ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AIC8800DC SDIO WiFi6驱动移植排错指南:从设备树到休眠唤醒

AIC8800DC SDIO WiFi6驱动移植排错指南:从设备树到休眠唤醒 先说一个很常见的场景我手头这块基于瑞芯微方案的板子要把WLAN模块从老的WiFi5 SDIO网卡换成AIC8800DC这颗国产WiFi6BLE Combo模组。芯片是新的驱动是厂商SDK里拿的照着文档编进去开机一看SDIO卡都没枚举出来。后面又是一连串的固件下载失败、wlan0不出现、休眠唤醒后网络假死。整个过程下来最深的感受是这类模组移植真正难的不是“把代码编进去”而是从硬件识别、总线通信、固件加载到电源管理这一整条链路上任何一个环节踩坑都会让项目卡上好几天。这篇就把我这次AIC8800DC驱动移植的完整排错过程写出来包括设备树怎么配、驱动代码目录怎么找、固件下载失败和CMD53超时怎么定位、wlan0起来之后怎么联网验证以及最容易被骂“烂尾”的休眠唤醒部分。写给正在做嵌入式Linux、BSP、智能硬件和网关类产品的工程师目标是让你拿到同款方案后少走几个弯路。1. AIC8800DC是什么为什么SDIO驱动移植要从这里开始1.1 芯片、模组和厂商驱动的基本盘AIC8800系列是AICSemi推出的一颗WiFi6 BLE Combo芯片模组厂做成邮票孔模组后市面上常见的就有AIC8800DC、AIC8800M2这些型号。它的定位很明确双频2.4G/5G1T1R80MHz带宽支持802.11ax也就是WiFi6同时挂了一个BLE 5.x的射频通路。接口方面主控和芯片之间的数据通路走SDIO 3.0也支持USB接口版本但SDIO版本在嵌入式Linux设备里用得更普遍因为很多SoC本来就闲置了一个SDIO控制器。这类国产Combo方案为什么这几年在IoT、门锁、网关、手持设备里越来越多两个原因一个是成本确实比同规格的进口芯片低一截另一个是供应链更稳。但代价也很明显驱动和文档质量参差不齐有些问题厂商FAE自己也未必第一时间能说清。所以做移植的人必须先建立一个概念这不是简单编一个ko就能完事的东西你要同时面对Linux的mmc子系统、SDIO协议、cfg80211无线子系统、电源管理框架四个大块里哪一块出问题现象都可能一样。1.2 WiFi4、WiFi5、WiFi6之间的差别哪些会真正影响移植很多新人把WiFi4/5/6当成“速度越来越快”的线性关系实际上不是这样。从移植驱动角度看我比较关心下面这几点WiFi4对应802.11n最大20/40MHz带宽调制到64QAMMCS0~7单流理论150Mbps。WiFi5对应802.11ac频段限定5G带宽加到80/160MHz调制到256QAM引入MU-MIMO单流理论433Mbps80MHz。WiFi6对应802.11ax2.4G和5G都有调制到1024QAM加入OFDMA和TWT160MHz下理论速率能到近1Gbps1T1R也能到600Mbps级别。对驱动移植来说真正影响工作的并不是这些物理速率数字而是三个衍生问题。第一速率高了之后SDIO总线带宽必须跟得上SDIO 3.0的SDR104模式下时钟可以跑到208MHz这时候对PCB走线和信号质量的要求比WiFi4时代高得多总线跑不稳驱动层就会看到大量CMD53传输失败。第二WiFi6默认涉及的WPA3、OWE这些加密方式依赖内核的crypto配置编译内核时如果没把相关模块编进去后面连接会遇到一堆诡异问题。第三WiFi6的TWT机制是省电的核心这个东西和系统休眠唤醒强相关稍后专门讲。1.3 SDIO协议在驱动里扮演的角色先分清mmc core和function driverSDIO这个词本身是SD协议的IO扩展物理上还是CLK、CMD、DATA0~DATA3四根数据线加一根时钟一根命令。和SD卡不一样的是SDIO设备可以包含多个I/O Function比如WiFi是一个FunctionBLE是另一个Function。访问SDIO设备主要靠两条命令CMD52做单字节寄存器读写速度慢通常用来初始化、中断使能这些控制面操作CMD53做块传输或字节传输WiFi的数据包走的就是这条通道速度快但对时序敏感。在Linux里SDIO设备识别和驱动加载是分层的。mmc核心层和具体平台的host controller驱动负责最底层的卡检测、时钟配置、电压切换真正的WiFi功能驱动是上层的SDIO function driver它注册自己的vendor/device ID或者compatible等mmc core枚举出SDIO卡之后自动匹配。所以排查问题的时候顺序很重要先确认“卡有没有被识别到”再查“厂商驱动有没有probe”最后才谈固件和网络。很多人一上来就怀疑厂商驱动代码有bug结果查了半天发现是设备树里电源没给上这个亏我吃过不止一次。2. 移植前的地基设备树配置与硬件识别2.1 别急着改代码先把硬件上的四件事确认清楚拿到一块新板子不管驱动文档写得多详细我都会先做一轮硬件确认这步省不掉否则后面所有问题都会变成“薛定谔的bug”。四件事电源、IO电压、复位/使能脚、SDIO信号线。电源方面AIC8800这类模组通常有VDDIO、VDDPA、VDD Core等几路电源板级原理图上一般会标注。对应的就是设备树里vmmc和vqmmc这两个supply。vmmc是卡主电源vqmmc是IO信号电压SDIO 3.0标准下通信电压需要1.8V所以vqmmc通常给1.8Vvmmc给3.3V。如果板上的IO domain配置不对内核切换电压时会把整个总线搞挂表现就是卡枚举时好时坏。复位和使能脚经常混在一起但作用不同。复位脚一般是模组的hardware reset使能脚控制电源LDO或电平转换芯片。设备树里通常用mmc-pwrseq-simple来描述这个上电时序reset-gpios对应复位脚post-power-on-delay-ms给一个掉电后重新上电的等待时间。我曾经因为把复位脚接反了极性导致每次开机SDIO卡都在复位状态dmesg里只有干干净净的卡检测失败查了大半天才发现是GPIO_ACTIVE_LOW写反了。2.2 设备树里必须写清楚的几个属性给SDIO WiFi模组配设备树不是简单在某个mmc节点加statusokay就完事。我拿这次调试用的节点做例子/ { sdio_pwrseq: sdio-pwrseq { compatible mmc-pwrseq-simple; pinctrl-names default; pinctrl-0 wifi_enable_h; reset-gpios gpio3 RK_PA5 GPIO_ACTIVE_LOW; post-power-on-delay-ms 80; }; }; sdmmc2 { status okay; bus-width 4; non-removable; cap-sdio-irq; cap-sdio-irq; keep-power-in-suspend; mmc-pwrseq sdio_pwrseq; max-frequency 50000000; pinctrl-names default; pinctrl-0 sdmmc2_clk sdmmc2_cmd sdmmc2_bus4; vmmc-supply vcc3v3_wifi; vqmmc-supply vcc1v8_wifi; wakeup-source; };逐个解释为什么这么写。bus-width 4SDIO WiFi几乎都是四线模式双线模式跑WiFi6带宽不够。non-removable这个模组焊死在板子上让内核别去做热插拔检测。cap-sdio-irq意思是这个host支持SDIO带内中断SDIO设备没有单独的中断脚靠DATA1线上的中断信号通知主机这个属性不开WiFi中断根本收不到。keep-power-in-suspend和wakeup-source是为后面休眠唤醒准备的先写上但后面如果做深睡方案keep-power-in-suspend可能又得去掉这个矛盾点后面专门讲。2.3 从“不识卡”到“枚举成功”的排错流程开机后在串口里看到什么算正常应该能看到类似这样的日志mmc2: new SDIO card at address 0001 mmc2: new SDIO card at address 0001在这个日志之前卡住那就是卡都没认到。最常见的一种错误长这样mmc2: error -110 whilst initialising SDIO card-110是ETIMEDOUT意思是mmc core发送CMD或者等待卡响应超时了。这个错误信息给不了更多线索按我这个顺序排查基本能定位先看设备树里mmc节点有没有加non-removable没有的话内核会等card detect中断而板子上根本没接卡检测引脚等于无限等待。再看reset脚极性确认模组没有被一直按在复位状态。然后看vmmc电压有没有真正起来用示波器量一下模组供电管脚很多是从PMIC拉的3.3Vbootloader阶段没配好内核里也没配就一直是0V。最后是降频大法把max-frequency从默认的150MHz或者100MHz降到50MHz甚至25MHz再试如果是信号质量问题降频后立刻就能枚举成功。实测下来很多“卡都认不到”的问题都是电源和复位真正的信号问题反而在枚举阶段少一些更多是固件下载阶段暴露。3. 驱动编译、装载与固件下载排错3.1 先摸清厂商驱动代码放在哪几个目录经常有人在群里问“驱动代码在哪个目录”这个问题其实得分情况。AIC8800这类厂商SDK给的驱动一般不会是Linux内核主线里的代码而是单独的一个压缩包解压之后通常有这几个目录一个是bus驱动比如aic_sdio负责SDIO总线层的注册和CMD53读写一个是fullmac/fdrv目录负责802.11协议、cfg80211操作、网络设备注册还有firmware目录放的是需要烧到模组里的固件bin文件和nvram参数文件。如果你用的是某个BSP板级SDK厂商可能会把这个驱动已经放到了内核源码树里常见的位置是drivers/net/wireless/aic8800或者drivers/net/wireless/aicsemi这类目录。还有一种情况是驱动作为外部模块提供编译时用make -C $(KDIR) M$(DRV_PATH) modules编出一堆ko文件。先搞清驱动是以哪种形式存在再谈编译否则后面加载顺序错了都找不到原因。我的习惯是先解压SDK看README和Makefile很多坑在注释里其实写过只是没人仔细看。3.2 Kconfig、Makefile和内核配置项怎么搭起来如果驱动是打进内核编译需要确认两个层面。第一是内核本身的配置CONFIG_MMC、CONFIG_MMC_SDIO、CONFIG_WIRELESS、CONFIG_CFG80211、CONFIG_RFKILL这些是必须开的。WiFi6的WPA3支持还要求内核crypto相关配置否则后面wpa_supplicant连WPA3路由会遇到“unsupported cipher”这类错误。第二是驱动自身的Kconfig里有没有依赖别的模块有的厂商驱动还依赖一个独立的sprdbt或bt driver蓝牙部分不开WiFi部分也会有奇怪依赖。外部模块的方式相对简单核心是做好交叉编译环境。Makefile里如果自带ARCH和CROSS_COMPILE覆盖要改成本平台的值如果没带就手动传进去。编完的ko文件要放到rootfs里位置决定了modprobe能不能找到一般放在/lib/modules/$(uname -r)/extra/或者直接放/lib/firmware/相关路径。这里有个很隐蔽的坑编译模块用的内核源码版本必须和你板子上跑的内核版本一致否则会出现modules_disabled或者vermagic不匹配ko根本加载不进去。3.3 固件下载失败的三种典型形态驱动probe成功之后下一步就是给模组下载固件。这一步问题最多我碰到过用户贴的日志五花八门归纳下来大概三种第一种是找不到固件文件dmesg里的直接报错长这样[ 3.123456] aic8800: Direct firmware load for aic8800_fw.bin failed with error -2 [ 3.123500] aic8800: load firmware failed, err-2-2是ENOENT纯粹是文件不存在或者路径不对。解决方法是查驱动代码里request_firmware用的文件名再去/lib/firmware下建一个对应名字的软链或者把文件直接拷进去。别想当然改文件名一定以代码里用的字符串为准。第二种是固件文件找到了但MD5或者版本不匹配日志里可能有mismatch、version check failed之类的关键词。这时候把厂商SDK里不同版本固件挨个试一般能解决。有些出厂的固件是给评估板用的放到量产板上因为晶振频率不同也会出现下载后行为异常。第三种就麻烦了CMD53传输错误mmc2: error -84 whilst transferring data. sdhci: REGISTER DUMP (mmc2)-84是EILSEQ数据线CRC校验失败说明总线层面数据传错了。这种大概率是信号完整性或者时钟频率过高先把max-frequency往下降到50MHz如果好了再慢慢往上调。还要检查vqmmc电压SDIO跑在3.3V电平下高频信号很容易出错1.8V下才能跑SDR104这种高速模式。3.4 一个比较有效的排错表格把固件下载阶段常见问题整理成下面这个表现场排查时比翻代码快得多。现象直接原因处理方式firmware load failed with error -2固件文件缺失或路径不对按驱动代码里的文件名放到/lib/firmware固件版本mismatchbin文件与驱动不匹配更换SDK对应版本固件mmc error -84 in CMD53总线CRC错误降低max-frequency检查vqmmc电压mmc error -110传输超时检查中断、时钟确认SDIO主机没有挂死反复重启/死机模组供电不足或顺序不对查pwrseq时序示波器量上电波形4. wlan0生成、接口配置与联网验证4.1 从SDIO func到net_device正常日志长什么样驱动下载固件成功后在内核日志里应该能看到wlan0这个网络接口创建出来的完整链路。大致是这样[ 4.123456] aic8800_sdio: mmc2:0001:1, sdio device initialized [ 4.123500] aic8800: cfg80211 init done [ 4.124000] IPv6: ADDRCONF(NETDEV_UP): wlan0: link is not ready看到cfg80211 init done和wlan0出现说明SDIO function driver和cfg80211无线子系统已经对接上了。这时候用ip link show wlan0应该能看到这个接口。如果wlan0没出现但SDIO设备已经枚举成功重点看厂商驱动的cfg80211注册那一步常见问题是内核Kconfig没开全或者驱动在注册net_device时依赖的某个子系统没起来比如rfkill注册失败可能导致整个probe中断。4.2 用wpa_supplicant联网实测wlan0出现了不等于能上网我习惯先用wpa_supplicant手工连一次排除系统集成问题。配置文件极其简单ctrl_interface/var/run/wpa_supplicant network{ ssidtest_ap psk12345678 }启动命令wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf udhcpc -i wlan0 ping 192.168.1.1这里如果遇到连不上AP先看wpa_supplicant日志里报的是什么。比较常见的是协商能力问题比如AP开启了WPA3而内核crypto配置不全导致连接失败。另外注意wpa_supplicant的版本新版本默认对802.11w管理帧保护有要求老驱动固件不支持的话也得在network段里加相应配置。4.3 三个容易让人误判的坑MAC地址、5G频段和速率第一个坑是MAC地址。有些模组的固件里没有出厂MAC驱动会每次随机生成一个导致路由器里设备名一直在变。解决方法是看驱动支不支持从设备树读local-mac-address属性支持的话在根节点或mmc节点里指定一个。不支持的话只能改驱动代码在注册net_device之前用eth_platform_get_mac_address或直接写死一个存在OTP里的MAC。第二个坑是5G频段搜不到。这不一定说明驱动有问题很多是全 regulatory domain 配置的问题Linux里默认world regdom会禁用5G某些频段用iw reg set CN或者wpa_supplicant配置countryCN能解决。第三个坑是协商速率上不去。先别急着怪驱动用iw dev wlan0 link看协商的带宽和速率如果只协商到20MHz检查AP配置里有没有固定带宽如果正好卡在40MHz多半是干扰或距离问题和驱动无关。5. 休眠唤醒驱动移植最容易被骂烂尾的最后一公里5.1 WiFi6里的TWT和WoWLAN为什么和休眠唤醒强相关系统休眠唤醒这关很多人以为把suspend/resume回调写了就完事但WiFi6时代多了两个绕不开的概念TWT和WoWLAN。TWT是802.11ax引入的机制AP和STA协商各自的唤醒周期没轮到自己的时候射频可以关掉用极低功耗维持协议在线。WoWLAN则是让网卡在主机睡眠时继续监听空中报文检测到特定的唤醒条件比如魔术包、ARP请求、网络唤醒模式的UDP包之后通过一个唤醒GPIO把SoC叫醒。这两个机制叠加起来产品就能做到“系统睡着但WiFi还活着”。对门锁、传感器这类电池供电设备来说价值非常高。但也正因为如此SSDIO WiFi的休眠唤醒排查比普通外设复杂得多——你不仅要保证SoC侧的suspend/resume流程正确还要保证WiFi固件在睡眠期间自己活得下来并且唤醒之后SoC和WiFi双方都能正确恢复到工作状态。5.2 设备树、SDIO PM能力和驱动回调的搭配先从设备树说起。如果希望睡眠时WiFi还能保持连接并具备唤醒能力keep-power-in-suspend和wakeup-source基本是必加的前者让mmc控制器在睡眠时别把卡电断掉后者告诉内核这个设备要参与系统唤醒。如果做的是深度睡眠方案、睡眠时直接断开WiFi电源那keep-power-in-suspend就得去掉同时要在resume路径里完整重新下载固件逻辑更复杂。驱动侧的PM回调核心是注册sdio_func_driver的pm成员。Linux里的电源管理回调不是只有suspend/resume一对完整的还有suspend_late/resume_early、freeze/thaw这些WiFi驱动一般至少要实现suspend和resume。suspend里需要做几件事调用cfg80211的暂停接口把wowlan相关配置同步给固件mask掉不必要的中断最后通知SDIO子系统进入PM状态。resume则是反向操作等总线恢复后重新使能中断恢复固件状态然后给cfg80211发一个恢复事件让上层的wpa_supplicant重新感知到连接状态。另外SDIO在suspend之前内核mmc层会检查host的pm_caps如果host不支持MMC_PM_KEEP_POWER即便设备树写了keep-power-in-suspend也不生效。这个可以通过cat /sys/class/mmc_host/mmc2/mmc2:0001/pm_caps之类的节点来确认不同平台导出路径不太一样。5.3 唤醒后网络假死一个典型的日志和排查思路我这次遇到的最头疼的问题就是系统从休眠唤醒后wlan0还健在iw dev wlan0 link也显示已连接但ping路由器第一个包就卡住网络完全不通重启wpa_supplicant也没用。dmesg里干干净净没有任何报错看起来像“一切正常”。这种假死状态本质上是固件已经乱了但驱动不知道。排查思路一先试ip link set wlan0 down ip link set wlan0 up如果这样操作后能恢复说明问题出在驱动resume后没有重新初始化但SDIO总线和固件下载链路还是好的。如果down/up都不能恢复那大概率是SDIO总线在睡眠期间挂掉了要么是host controller没有正确恢复时钟要么是模组掉电但驱动以为还活着。排查思路二在驱动resume回调里加打印在恢复后先读一个模组内部寄存器确认返回值是否合理不合理就主动重新初始化。很多厂商驱动其实带了异常检测逻辑但默认没开需要编译时打开对应的debug选项。还有一个经常被忽略的点休眠唤醒问题要先确认平台本身的suspend/resume是好的再排查WiFi。我在调试中吃过亏一开始就在WiFi驱动里找问题后来发现整个系统从睡眠唤醒后触摸屏和网络都异常真正原因是SoC的DDR自刷新配置有问题。所以正确顺序是先用最简单的GPIO按键唤醒把平台基础功能调通再挂WiFi设备这才谈得上定位WiFi相关问题。5.4 深浅睡眠方案的取舍和我的经验最后聊一下方案选型。做产品的时候在休眠唤醒上要根据功耗指标决定WiFi要不要保留连接。如果产品要求睡眠电流极低通常选择深睡方案WiFi模组直接断电唤醒后重新连接网络这种情况驱动移植更简单但用户体验会差一点比如收到消息推送会有延迟。如果产品要求睡眠状态下还能实时收消息、远程唤醒那就只能用浅睡方案必须把TWT和WoWLAN调通驱动和平台的配合要很仔细。根据我自己的经验硬件唤醒脚的连接非常关键深睡方案无所谓浅睡方案里模组的wake_host GPIO必须接到SoC能唤醒睡眠的引脚上而且Bootloader阶段这个GPIO的复用要提前配好否则系统sleep之后WiFi固件把唤醒信号拉起来了但SoC压根不理会。另外一个建议是调试时先用一个外部独立GPIO触发唤醒排除WiFi模块本身的干扰等平台唤醒链路稳定后再把WoWLAN的唤醒源加回来。我这样一步步剥离开来每次都能准确定位问题出在平台、总线还是固件。6. 把这次踩坑留下的工具和经验留下来最后分享一个我自己的习惯也算给还没踩过坑的人提个醒。我调这类SDIO WiFi驱动时会准备一小段固定的排查命令集合随时把日志拉出来看dmesg | grep -E aic|mmc|sdio|wlan|cfg80211|firmware|wowlan cat /sys/bus/sdio/devices/*/device cat /sys/bus/sdio/devices/*/vendor cat /sys/kernel/debug/ieee80211/phy0/wowlan iw dev wlan0 info这套命令能快速回答三个问题卡有没有识别到、驱动有没有加载、无线子系统状态是否正常。每次有人找我问移植问题我都会先让他贴这三行输出比盲目猜现象高效得多。另外就是坚持把每一版设备树、每一份驱动配置存进git哪怕只是改了max-frequency也要记录当时的场景否则调完一个bug过两周回头找原因你会后悔当初没记。AIC8800DC这个方案本身并不差踩坑多主要是因为SDIO WiFi的链路层层嵌套任何一层挂了都会让整体看起来“没反应”。但只要把链路拆开一块一块验证从卡识别、固件下载、网络注册到休眠唤醒每一步都有明确的现象和日志可以对照问题定位就不难。希望这篇记录能帮你少加两个通宵的班。
返回列表