
做RK平台Android整机方案的朋友几乎没有不碰WiFi模组适配的。AIC8800这颗芯片近两年在商显、网关、工业平板上出现频率很高双频WiFi 6加BLE 5.0走SDIO接口性价比确实能打。但我见过太多团队从画板子到量产的周期里卡得最久的不是天线射频而是“系统跑起来了但wlan0死活不出来”这类系统集成问题。从DTS设备树描述到内核驱动probe、SDIO固件下载再到Android HAL把WiFi开关拉起来中间任何一环出错用户在设置里看到的结果都一样搜不到WiFi或者开关一直打不开。这篇文章就把这条启动链完整拆开讲透。AIC8800在RK Android系统上从设备树配置、驱动加载、固件下载、HAL启动到Framework能正常扫描SSID每个环节要做什么、为什么这么做、踩坑点在哪里一次性说清楚。适合正在做RK平台BSP、驱动、系统集成的工程师参考也适合刚接手WiFi适配任务、想快速建立全局观的同行。1. AIC8800在RK Android系统里的位置1.1 一颗WiFi 6 Combo芯片的基本盘AIC8800是爱科微AICSemi推出的WiFi 6 BLE 5.0 Combo芯片支持802.11a/b/g/n/ac/ax2.4GHz和5.8GHz双频WiFi部分走SDIO 3.0接口蓝牙部分走UART/PCM接口。相比瑞昱RTL8821、正基AP6256这些老方案AIC8800代际更新支持HE80理论吞吐更高在RK3568、RK3588这类中高端平台上跑WiFi 6速率没有瓶颈。很多模组厂做成邮票孔或LCC封装板上设计并不复杂。选这颗芯片做整机主要看中的是三点一是双频WiFi 6满足产品spec需求二是SDIO接口在RK Linux/Android生态里非常成熟三是成本比同规格大厂方案低一截。但便宜有便宜的代价它的驱动、固件、文档完善程度不如一线大厂遇到问题很多地方要自己趟。这也是我写这篇文章的原因——把这几年在AIC8800上踩过的坑集中整理出来。1.2 从芯片到系统一条跨越四层的启动链很多人以为WiFi模组适配就是“把驱动编进内核开机就有wlan0”实际完全不是这么回事。AIC8800从硬件上电到Android界面出现WiFi开关中间要经过至少四个层次设备树层DTS告诉内核WiFi模组挂在哪个SDIO控制器上、供电怎么控制、复位脚是哪个、唤醒脚是哪个。内核驱动层SDIO总线枚举到这颗芯片后驱动负责下电时序、加载固件、注册cfg80211和netdev最终生成wlan0。Android HAL层系统通过HAL服务管理WiFi的生命周期拉起wpa_supplicant转发Framework下发的扫描、连接指令。Framework/应用层WifiService负责策略和UI把“打开WiFi”这个动作翻译成上面几层的联动。这四层里任何一层出问题现象几乎都一样——WiFi不可用。所以排查的时候必须有全局视角不能只盯着驱动代码看。我以前带过几个刚入行的同事一上来就翻驱动源码翻了两天也定位不到问题最后发现是DTS里一个GPIO和别的模块冲突了。这就是典型的“把系统问题当成驱动问题来查”。2. DTS设备树硬件信息的第一道关卡2.1 电源、复位与唤醒GPIO怎么配才不翻车DTSDevice Tree Source本质上就是一块硬件的“体检表”内核启动时靠它知道板上接了什么东西、每个引脚怎么用。AIC8800这种外置SDIO WiFi芯片在DTS里最核心的就是三组GPIO使能脚enable/power、复位脚reset、唤醒脚host_wake。使能脚控制模组的供电开关一般接一个MOS管电路GPIO拉高后模组才上电。复位脚在初始化时要有一个完整的低电平→高电平过程确保芯片从复位状态正常启动。这两个脚经常放在sdio_pwrseq节点里由内核的mmc-pwrseq-simple框架统一管理。为什么要单独做pwrseq而不是在驱动里控制因为SDIO控制器在总线枚举之前就必须保证设备已经上电这个时序比驱动probe更早内核框架在sdhci初始化时会自动执行pwrseq的上电序列。唤醒脚是WiFi芯片通知SoC的“我有数据要处理”的中断脚一般接到SoC的GPIO配置为输入、上下拉根据模组手册来。这里有个最常见的坑如果唤醒脚的电平极性配反了或者这个GPIO刚好被其他模块复用那么WiFi驱动可以正常probe但系统进入休眠后就再也醒不过来了或者WiFi扫描时驱动等不到中断表现为扫描超时。我之前在一个项目上遇到过WiFi开起来能连上AP但只要系统深度休眠一次醒来之后WiFi必然是“已保存但无法连接”状态。查了三天最后发现是唤醒GPIO没有配置wakeup-source属性导致这个中断不能把CPU从深度睡眠中唤醒。加上属性、确认中断号没问题后休眠唤醒功能才算正常。2.2 SDIO控制器节点几个属性决定模组能否被枚举DTS里SDIO控制器的配置有四个属性几乎每个都会出问题我一个个说。第一个是bus-width 4WiFi模组一般用SDIO 4-bit模式4条数据线。有人图省事不写内核默认按1-bit枚举能识别但速度惨不忍睹WiFi跑起来也就几十Mbps。第二个是non-removableSDIO WiFi是焊死在板子上的不是可插拔的SD卡不加这个属性的话内核会把它当成可移动设备处理系统休眠时可能会触发移除逻辑醒来后设备直接消失。第三个是broken-cd这个属性特别容易被忽略。SD卡有卡检测引脚CDWiFi模组没有。如果控制器自带卡检测逻辑而板子上没有对应的检测电路内核就会一直认为“没有卡插入”SDIO总线上设备也不会被枚举。加上broken-cd相当于告诉内核不要检测卡直接尝试枚举。第四个是keep-power-in-suspend这个跟休眠相关。不加的话系统suspend时SDIO控制器会切断电源WiFi芯片直接掉电唤醒后内核再枚举一次。听起来没什么但重新枚举意味着WiFi要重新下载固件、重新连接AP用户体验就是“休眠一次WiFi断一次”连上还得等好几秒。加了keep-power-in-suspend休眠时保持供电WiFi连接不断体验好很多。还有一个sd-uhs-sdr104开启SDIO 3.0的高速模式。很多模组标称支持UHS-I SDR104但DTS里没开实际跑在默认的25MHz时钟下结果就是WiFi内网测速怎么都上不去跟百兆网口差不多。这个可以按模组支持的最高模式配一般RK平台配sdr104或者sdr50都没问题。2.3 RK3568上的一份DTS参考片段RK平台不同芯片的SDIO控制器名称略有差异RK3568上一般用sdmmc1挂SDIO WiFi。下面是一份可参考的节点配置覆盖了上面说的关键点sdio_pwrseq { status okay; pinctrl-names default; pinctrl-0 wifi_enable_h; reset-gpios gpio0 RK_PC6 GPIO_ACTIVE_LOW; post-power-on-delay-ms 50; }; sdmmc1 { status okay; bus-width 4; non-removable; broken-cd; cap-sdio-irq; keep-power-in-suspend; sd-uhs-sdr104; pinctrl-names default; pinctrl-0 sdmmc1_clk sdmmc1_cmd sdmmc1_bus4; vmmc-supply vcc_wifi; vqmmc-supply vcc_wifi_io; };注意vmmc-supply和vqmmc-supply一个管主电源一个管IO电平。AIC8800的IO电平一般是1.8V或3.3V具体看模组原理图。这两个电源没配好轻则WiFi掉卡重则无法枚举而且这种问题用示波器查电平很难发现因为有时候静态电平看着是对的一跑高速数据就出错。还有一个点pinctrl-0里的sdmmc1_clk、sdmmc1_cmd、sdmmc1_bus4这些引脚必须在SoC的pinctrl里确认没有被复用成其他功能。特别是RK3568这种引脚复用特别灵活的芯片一个bank的引脚可能同时支持SDIO、UART、I2C、PWM上层的其他驱动如果在DTS里先声明了这些引脚SDIO这边就会配置失败内核日志里会出现pin already requested之类的报错很容易排查。3. 内核驱动与固件下载wlan0从哪里来3.1 驱动probe流程从request_firmware到chip bootDTS配好之后系统启动时SDIO控制器会把总线上枚举到的设备信息和内核里的sdio_driver匹配。AIC8800的驱动一般由模组厂提供源码编译成aic_wifi模块或者直接编进内核。这个驱动的主要工作比很多人想象中要多得多。probe函数的第一步是确认芯片型号。驱动通过SDIO命令读取芯片内部寄存器拿到chip id然后和驱动支持的列表比对。对不上就直接返回失败常见原因是固件版本和驱动版本不匹配或者芯片被识别成另一个型号。第二步是请求电源域和GPIO这一部其实很多工作DTS里的pwrseq已经做掉了驱动在这里主要是拿到电源句柄和中断号注册中断处理函数。第三步是固件下载。AIC8800的固件不是烧录在芯片内部Flash里的而是在每次上电时由驱动从文件系统读取然后通过SDIO命令逐段写入芯片的SRAM里执行。这也是为什么固件放哪个目录特别重要放错地方驱动就加载不到芯片就一直停在boot状态后面所有流程都不会发生。固件下载完成后驱动会等芯片发出boot完成信号之后才能继续注册系统接口。这一步有个经典问题如果板上的电源纹波太大或者上电时序不稳定芯片经常会boot一半就死掉驱动一直等不到完成信号。表现就是dmesg里反复打印下载失败然后驱动给你来个probe failedwlan0自然就不存在。3.2 固件文件、路径与版本匹配AIC8800的固件一般由多个文件组成常见的有fw.bin、fw_data.bin、fw_patch.bin等。驱动通过request_firmware接口加载这个接口在内核里有一套标准的搜索路径。在Android系统上设备厂商的固件一般放在/vendor/etc/firmware/下面如果驱动代码里写的路径是相对路径aic8800/fw.bin那么完整路径就是/vendor/etc/firmware/aic8800/fw.bin。这个目录在编译vendor image时由PRODUCT_COPY_FILES机制打包进去。实际项目中我见过太多次“WiFi起不来”是因为固件根本不在系统里。有时候开发阶段改错了mk文件固件没有打包进vendor.img烧录后系统里找遍整个文件系统都找不到fw.bin。这种问题排查起来其实最快先看/vendor/etc/firmware/下有没有文件再看驱动日志里报的路径是什么。固件版本和驱动版本必须配套。芯片厂一般会给出驱动和固件的版本对应关系。如果只升级了驱动而固件没跟着换或者反过来chip boot阶段就会出现各种诡异问题有的直接拒绝启动有的能启动但WiFi扫描到AP后连接不上还有的会频繁断流。AIC8800的固件和驱动配套信息通常在模组厂发的release notes里升级固件前一定先确认匹配关系。3.3 cfg80211、netdev与rfkill的注册顺序固件下载完成、芯片正常启动后驱动开始注册上层的网络接口。这块做的事情包括注册cfg80211的wiphy设备、创建netdev接口wlan0、初始化rfkill设备。cfg80211是Linux内核的无线配置管理框架负责上层和驱动之间的命令通道比如扫描、连接、断开这些操作最终都是通过cfg80211的ops回调到AIC驱动里。wiphy注册成功之后系统里才真正出现“无线设备”这个概念。wlan0这个网络接口是由驱动创建的通常在注册wiphy之后调用alloc_netdev和register_netdev生成。rfkill是内核的无线开关机制。Android上层通过HAL调用rfkill接口来“拉开关”。如果rfkill初始化失败或者rfkill状态和实际状态不一致就会出现一个很经典的现象ip link set wlan0 up能执行成功但Android设置里的WiFi开关怎么都打不开。因为这个开关状态不对Framework认为WiFi被硬件禁用了。驱动加载成功、wlan0出现之后在内核层面整个WiFi设备就算ready了。你可以通过ifconfig -a或者ls /sys/class/net/看到wlan0通过iw dev看到无线设备信息。到这个阶段DTS和内核的部分就结束了后面是Android用户空间的事。4. Android HAL与Framework打通“最后一公里”4.1 WiFi HAL的架构演进与vendor侧实现Android系统的WiFi架构经历了多次演进。早期是直接由Framework通过socket连wpa_supplicant后来为了分离硬件厂商代码Google在Treble化之后把WiFi相关的硬件接口抽象成了HALHardware Abstraction Layer。Android 8到11这一段主要使用HIDL版本接口包名类似android.hardware.wifi1.0到了Android 12之后新平台开始迁移到AIDL版本android.hardware.wifi.aidl。RK平台的SDK里通常同时保留了HIDL和AIDL两套兼容代码具体启用哪套由manifest里的VINTF配置决定。对于AIC8800这种外置WiFi芯片HAL的核心任务其实并不复杂一是加载/确保内核驱动已加载二是管理wpa_supplicant进程的启停三是向Framework暴露WiFi相关的接口服务。很多厂商在实际做的时候直接把HAL实现得很薄甚至通过一个init.rc服务把wpa_supplicant拉起来HAL只是作为一个状态机去协调。这样做的优点是简单直接缺点是SELinux和权限问题比较多因为Framework对HAL的调用路径越长越容易出现权限拒绝。我在RK3568项目上就遇到过HAL服务起不来原因是SELinux策略没有给vendor进程对应的socket访问权限logcat里能看到avc denied报错。4.2 wpa_supplicant与HAL的分工协作搞清楚HAL和wpa_supplicant的关系是理解Android WiFi架构的关键。wpa_supplicant是一个用户空间的守护进程负责处理WPA/WPA2/WPA3认证、密钥协商、扫描结果缓存这些具体WiFi协议栈工作。它通过nl80211 socket和内核里的cfg80211通信nl80211再调用到AIC驱动。可以简单理解成wpa_supplicant是内核和Framework之间的协议翻译器。HAL在这里的角色是“服务总管”。以RK平台常见的实现为例WiFi HAL服务启动时会做这几件事检查wlan0是否存在加载wpa_supplicant配置文件然后fork并启动wpa_supplicant进程。Framework下发“打开WiFi”请求时HAL把命令通过wpa_supplicant的控制接口ctrl_iface转达过去。wpa_supplicant完成扫描或者连接状态变化时又通过事件回调通知HALHAL再上报给Framework。这套机制里最常出问题的是控制接口的socket路径和权限。wpa_supplicant的ctrl_interface默认指向/data/misc/wifi/sockets这个目录的owner必须是wifi用户权限要正确。如果目录权限不对HAL去连接socket就会失败现象是Framework上报WiFi已开启但实际扫描不到任何网络因为命令根本没到wpa_supplicant那里。4.3 MAC地址、射频校准与SELinux的隐藏坑除了启动流程还有三个隐藏问题值得单独拿出来说。第一个是MAC地址来源。WiFi模组本身可能没有烧录MAC地址或者MAC地址存在模组内部的OTP区域但驱动不一定去读。RK平台的常见做法包括从DTS的某个节点里读MAC地址从/persist分区读或者在系统启动时通过属性系统传递。如果MAC地址为空或者非法Android系统会拒绝启动WiFi或者每次启动都生成随机MAC。AIC8800的MAC读取逻辑要看驱动实现我曾经遇到过驱动把全零MAC当成有效值传给上层然后上层所有关于MAC的策略全部失灵。第二个是射频校准。WiFi芯片出厂时会有功率校准数据一般存放在模组内部Flash或者单独的分区里。驱动probe时会去读这个校准数据如果读不到或者数据损坏WiFi能扫描到AP但发射功率不对表现为距离近了信号很好隔一堵墙就基本没信号而且功耗明显偏高。AIC8800的校准数据管理方式需要问模组厂确认有的模组是把校准数据作为固件的一部分一起打包有的则需要单独烧录。第三个是SELinux。Android的SELinux enforcing模式下所有进程都被限制在白名单策略里。AIC8800的HAL服务、wpa_supplicant、wificond这几个进程都要有对应的te文件和allow规则。新增一个vendor服务时最容易忘记写SELinux策略结果服务能手动启动但系统开机时被SELinux拦下来。排查方法很直接dmesg和logcat里搜avc: denied关键字能看到是哪个进程访问了不该访问的资源。RK官方SDK里一般有wifi相关的SELinux模板直接改包名就行。5. 从按下电源键到连上WiFi全链路串联与排障手册5.1 完整启动时间线每个阶段该看到什么把前面几章的内容串起来AIC8800在RK Android系统上的完整启动链大概是这样一条时间线每一步都有对应的验证手段方便你确认到底卡在哪。电源键按下U-Boot阶段U-Boot初始化SDIO控制器部分方案会在这里就给WiFi模组上电以便支持网络启动。这个阶段可以看串口日志里有没有SDIO相关的初始化打印。Kernel启动早期DTS被解析SDIO控制器注册pwrseq执行上电序列GPIO拉高、延时、复位释放。这个阶段如果复位时序有问题后面SDIO枚举就会失败。Kernel驱动probeSDIO总线枚举到AIC8800驱动匹配成功开始请求固件并下载。dmesg里应该能看到驱动打印的chip id和固件下载信息。netdev注册wlan0出现。此时可以用ls /sys/class/net/验证。Android init阶段init进程根据init.rc启动wifi HAL服务和wpa_supplicant。用ps -A | grep wpa和ps -A | grep wifi验证进程是否存在。Framework启动WifiService初始化通过HAL查询WiFi能力设置WiFi状态为available。此时设置里的WiFi开关应该能打开。扫描与连接Framework下发扫描命令HAL转给wpa_supplicantwpa_supplicant通过nl80211下发给驱动。UI上能看到SSID列表点击连接后经过四次握手完成连接。每一步都有自己独立的日志输出。串口log主要看kernel部分logcat看Framework和HAL部分这两个日志要放一起看才能把问题定位完整。5.2 高频问题与排查速查表我在多个AIC8800项目上遇到过的问题大部分可以归结到下面这几类做成一张表方便查阅。现象可能原因排查手段开机后没有wlan0dmesg里没有AIC驱动probe信息DTS里GTXPIN冲突、SDIO控制器没enable。pwrseq上电时序不对查dmesg里sdhci和pinctrl报错检查DTS节点status量硬件供电波形有wlan0但无法扫描到AP或者扫描列表为空固件和驱动版本不匹配、天线没接好、cfg80211状态异常用iw dev wlan0 scan手动扫描看dmesg固件下载是否有报错WiFi能连接但速度极慢内网测速上不去SDIO没有跑高速模式DTS缺少sd-uhs-sdr104或vqmmc电平不对检查SDIO时序配置查看链路速率iw dev wlan0 link休眠唤醒后WiFi断开且无法重连缺少keep-power-in-suspendwakeup GPIO配置不正确固件掉电查DTS电源属性确认wakeup中断是否有触发Android设置里WiFi开关是灰色/打不开rfkill状态异常SELinux拦截HAL没起来看logcat里的avc denied手动ip link set wlan0 up检查rfkill状态wpa_supplicant进程反复重启config文件路径错误、socket目录权限不对看logcat中wpa_supplicant启动参数和报错信息检查/data/misc/wifi目录权限这个表看着简单实际排查的时候要按顺序来先硬件后软件先kernel后Android先Driver后HAL。我以前经验不足的时候遇到WiFi问题先去看Framework代码浪费了很多时间。后来养成一个习惯不管现象多复杂永远从dmesg看起确认内核这一层是干净的再往上查。这个习惯帮我少走了很多弯路。AIC8800在RK平台的启动链说到底就是“先让芯片活过来再让系统认识它最后让Android用得起来”。每一步都有明确的验证点只要按层拆解、逐层确认大多数问题都能在半小时内定位到具体环节。我现在做项目WiFi适配这块基本不会再被卡住了因为这条链路上每个阶段的日志特征、常见坑、排查入口都已经形成了一套固定的流程。希望这篇文章能把这套流程完整地传递给你下次遇到AIC8800或者其他外置SDIO WiFi芯片可以少踩几个坑。