拿到RK3588平台、板载aic8800 WiFi模组的调试任务时,我原本以为这只是一次“改改DTS、编进内核、加载固件”的常规流程。结果从驱动适配到网络联调,前前后后踩了大大小小的坑,其中有三个问题差点让我怀疑人生。这个系列前面几篇都在写主控侧的外设调试,这一篇正好轮到无线部分,我把完整的调试过程、关键配置和排查思路都记下来。做同款方案、同平台开发的兄弟可以直接把这篇文章当参考手册用,能帮你省掉不少弯路。
先交代一下背景。板子主控是RK3588,WiFi模组用的aic8800,这是一颗支持WiFi 6的国产方案芯片,同时集成蓝牙,支持SDIO和USB两种host接口。我这次在板子上用的是SDIO接口方案,系统是标准的Linux BSP环境,内核版本基于5.10。aic8800这颗芯片在量产项目中挺常见,成本合适、性能也够用,但它的驱动适配和固件加载方式跟高通、博通这些老牌方案有不少差异,坑点主要集中在设备树配置、固件路径和SDIO枚举这几块。
适合看这篇内容的人,我大致分三类:一是刚拿到RK3588平台、需要把WiFi跑起来的BSP工程师;二是做嵌入式Linux产品、想把aic8800驱动移植到自家板子上的开发者;三是遇到WiFi识别不了、连不上AP或者吞吐量上不去的同学。下面内容我按实际调试的推进顺序来写,从准备阶段一路写到问题排查。
1. 调试前的准备工作
真正动手改代码之前,先把“家底”摸清楚。很多新手上来就急着编译,结果原理图没看、引脚接错、电源时序不对,折腾半天板子连SDIO设备都枚举不出来。我自己的习惯是:先花一个小时把资料理清楚,后面能省两天。
1.1 确认模组接口与供电关系
先看硬件原理图。我拿到板卡的原理图PDF后,第一件事就是找aic8800模组相关的网络标号。重点确认三件事:SDIO的数据线(CLK、CMD、DATA0-3)连到RK3588的哪一组SDIO控制器;模组的供电引脚是3.3V还是1.8V;WiFi的使能/复位脚接了哪个GPIO,是不是跟其他外设复用。
这个环节千万别嫌麻烦。我遇到过一块板子,WiFi的复位脚跟eMMC的复位脚挂在同一个网络标号上,结果每次初始化WiFi的时候,eMMC也跟着复位一次,系统直接崩溃。后来翻原理图才发现是硬件设计上的问题,软件层完全没法解,只能改版。原理图阶段多花十分钟,后面少加三天班。
aic8800的SDIO接口电平一般是3.3V或1.8V,具体看模组型号。RK3588的SDIO控制器可以配置不同的电压域,DTS里对应的mmc节点会有一个vqmmc-supply属性,需要和硬件实际供电匹配。如果这里配错了,SDIO通信会不稳定,轻则枚举失败,重则CRC错误刷屏。
1.2 拿到正确的SDK与固件版本
接下来是驱动源码和固件。aic8800的驱动通常由芯片原厂或模组厂商提供,不是内核主线自带,所以需要单独拿到驱动包。拿到手之后,先看目录结构和README,确认它支持的kernel版本范围。
这里有个关键点:驱动版本和固件版本必须配套。我一开始从某位同事那边拿到了驱动源码,但固件文件是从另一个渠道拷贝的,结果固件加载阶段一直报firmware magic mismatch,查了半天才发现是版本不匹配。后来找原厂重新要了配套的firmware包,问题当场解决。所以拿到源码的同时,一定要核对固件的版本号和编译时间,能放在同一个SDK包里的东西,就不要分开乱凑。
固件文件构成上,aic8800的SDK通常包含:
- bootloader相关的固件,一般是带boot字样的bin文件,负责芯片内部启动
- WiFi主固件,通常命名为aic8800_fw.bin或者类似的名字
- 蓝牙固件,如果模组含BT功能
- 射频校准参数文件,可能叫nvram或cfg参数文件
这些文件的去向,决定了你后续固件加载步骤的路径配置。我后面的第四部分会详细讲。
1.3 准备调试环境
最后是调试环境。RK3588的开发板或者自研板,至少要保证串口能正常输出,rootfs能登录,有网络通道用于拷贝文件。SDIO WiFi这种外设,一旦kernel崩溃或者SDIO总线报错,串口日志是唯一的救命稻草,所以串口工具、波特率、日志保存都要提前准备好。
另外,编译环境也建议提前确认。RK3588的BSP一般有独立的build.sh脚本,支持单独编译kernel、uboot、rootfs。我建议调试阶段直接编译内核和boot.img,不要每次都全量编译,那样太慢了。我自己的环境是一台16核的机器,单独编内核大概5到8分钟,全量编BSP可能要半小时到一小时,效率差距非常明显。
2. 驱动框架与内核配置要点
aic8800驱动并不复杂,但它的编译配置方式跟主线内核的网络驱动不太一样,需要单独适配。这一节我讲清楚驱动目录结构、Kconfig选择、编译方式三个层面。
2.1 驱动源码目录结构
把驱动包解压之后,先看一眼它的顶层结构。常用布局是:
aic8800/ ├── aic8800_fdrv/ # WiFi主驱动 ├── aic8800_btlpm/ # 蓝牙低功耗管理驱动 ├── aic_bluetooth/ # 蓝牙驱动 ├── Makefile ├── config.mk └── README.txtWiFi主驱动目录下会有多个子目录,对应SDIO、USB、PCIe等不同总线接口的适配层。我们这次用的SDIO接口,所以主要关注sdcard或者sdio相关的目录,具体要看厂家的命名习惯。
在这个阶段,我一般会先搜一下README里面的“SDIO”关键词,看它推荐的配置流程和编译开关。不同版本的SDK,编译选项差异很大,有的用Kconfig,有的用Makefile里的变量,还有的直接改config.mk。所以别太相信网上所谓的标准配置,一切以你手上的SDK为准。
2.2 内核Kconfig与Makefile配置
把驱动源码放进内核目录后,先别急着编译,先把内核配置打开。一般是把驱动源码放到:
kernel/drivers/net/wireless/aic8800/然后在对应的Kconfig和Makefile里加上路径引用。这个步骤说白了就是让内核编译系统认识这个驱动目录。以我手上的SDK为例,需要修改:
kernel/drivers/net/wireless/Kconfig kernel/drivers/net/wireless/Makefile然后通过menuconfig选中对应的配置项。注意,aic8800的配置项可能显示为“AIC8800 SDIO WiFi support”或者类似的名字。我用make menuconfig的效果如下:
Device Drivers -> Network device support -> Wireless LAN -> <*> AIC8800 SDIO WiFi support如果你用的是defconfig方式,也可以在kernel/arch/arm64/configs/rk3588_defconfig里加一行:
CONFIG_AIC8800=y调试阶段我建议编成模块(=m),这样更新驱动不用重新烧boot.img,直接insmod或者替换.ko文件就行。等量产版本再改为y编进内核,减少启动阶段的依赖顺序问题。
2.3 编译方式的坑点
aic8800的SDK,很多版本在编译时还会从config.mk里读取配置,例如选择SDIO还是USB模式。我在某个版本里就看到过类似这样的开关:
CONFIG_AIC8800_SDIO := y CONFIG_AIC8800_USB := n这里的坑在于:如果你只改了内核的Kconfig,但没改驱动自己的config.mk,编译出来的驱动可能默认走USB路径,导致SDIO接口完全没被初始化。所以编译之前,一定要确认两个层面都选择了正确的总线接口。
编译完成后,如果编成模块,会产生几个ko文件,其中WiFi主驱动模块的命名可能是aic8800_fdrv.ko。先用file命令确认它是ARM64架构,然后就可以推到板子上了。但先不急着加载,因为还需要DTS配置好,不然insmod的时候会发现SDIO设备根本不存在,直接报错。
3. DTS设备树配置详解
我第一次调aic8800时,以为设备树里只要配好SDIO控制器节点就能跑,后来证明太天真。这个芯片的驱动对复位引脚、供电引脚和时钟频率都很敏感,任何一个环节不对,表现出的故障现象都很有迷惑性。
3.1 SDIO控制器节点配置
RK3588内部有多组SDIO控制器,WiFi一般接到SDIO0或者SDIO1。到底用哪一组,由原理图决定。在DTS文件里找到对应的mmc节点,例如:
&sdio { status = "okay"; max-frequency = <150000000>; bus-width = <4>; cap-sdio-irq; disable-wp; non-removable; mmc-pwrseq = <&sdio_pwrseq>; vmmc-supply = <&vcc_3v3>; vqmmc-supply = <&vcc_1v8>; pinctrl-names = "default"; pinctrl-0 = <&sdio_pins>; };这些属性里,有几个很容易出问题:
- max-frequency。SDIO的时钟频率不是越高越好,如果板子布线质量一般,跑150MHz可能会出现CRC错误。我建议先保守一点,设成50MHz,确认能稳定识别和传数据之后,再逐步提高到100MHz、150MHz。
- non-removable。这个必须写,告诉内核这个SDIO设备不是可插拔的,不然内核会周期性扫描卡是否移除,导致WiFi频繁掉线。
- cap-sdio-irq。启用SDIO中断,aic8800的驱动会依赖这条中断线来通知host有事件需要处理,少了这个属性,可能会出现WiFi连接后完全不响应的情况。
- mmc-pwrseq。WiFi的电源时序控制节点,下一小节详细说。
3.2 pwrseq电源时序节点
aic8800这类芯片,上电时序很关键。必须先给供电,再拉高复位脚,然后等待一小段时间让芯片完成内部初始化,最后才能发起SDIO枚举。
RK3588的DTS里,一般用simple-pwrseq来实现这个时序。我板子上的配置是这样的:
sdio_pwrseq: sdio-pwrseq { compatible = "mmc-pwrseq-simple"; reset-gpios = <&gpio4 RK_PB1 GPIO_ACTIVE_LOW>; post-power-on-delay-ms = <80>; };注意这里的复位脚极性和延时。aic8800模组通常是复位脚拉低有效,所以reset-gpios用GPIO_ACTIVE_LOW。post-power-on-delay-ms这个值,我建议从50ms起步,如果发现板子冷启动时偶尔枚举不到WiFi设备,可以逐步加大到100ms甚至150ms,说明芯片需要的上电稳定时间更长。
跟我前面说的原理图阶段对应一下:这里的GPIO编号必须跟原理图上WiFi复位脚实际连接的GPIO完全一致。我之前调试的一块板子,GPIO编号差了一个bank,结果驱动看着正常加载了,但WiFi芯片根本没被复位释放,SDIO枚举出来的设备ID全是0xFF,日志里一堆timeout错误。
3.3 中断引脚与电平配置
SDIO WiFi一般还需要一个中断脚,比如WL_HOST_WAKE或者类似的名字,用来让WiFi芯片主动唤醒host。这个引脚通常需要在pinctrl里配置成GPIO功能,并且设置成合适的上下拉。
在pinctrl相关节点里,类似这样的配置:
wifi_host_wake: wifi-host-wake { rockchip,pins = <4 RK_PB2 RK_FUNC_GPIO &pcfg_pull_up>; };aic8800的中断线一般是高电平有效,所以host侧配上拉,确保默认状态下是确定电平。如果这个引脚的上下拉配置不对,芯片可能会在运行时不断产生虚假中断,表现为系统莫名其妙地频繁唤醒、WiFi功率异常、或者干脆驱动注册中断失败。
DTS的修改没有太多捷径,改完之后编译内核或dtb,烧录重启,然后看串口日志。接下来的第四部分,就是验证DTS配置是否生效的关键环节。
4. 固件加载流程与常见路径问题
DTS配好之后,系统启动或者insmod驱动时,驱动代码会去文件系统某个路径找固件文件,加载到aic8800芯片里。这一步如果出问题,现象非常统一:dmesg里有firmware load error、request_firmware failed之类的报错,或者卡在某个等待状态。但根因却可能五花八门。
4.1 固件文件放到哪里
Linux内核请求固件,默认路径一般是从几个固定位置找:/lib/firmware、/lib/firmware/updates、/lib/firmware/$(uname -r)等。aic8800的驱动内部会拼一个相对路径,比如请求“aic8800/aic8800_fw.bin”,那完整路径就是/lib/firmware/aic8800/aic8800_fw.bin。
我第一次踩坑就在这里。当时把固件直接放到了/lib/firmware/根目录,但驱动请求的是子目录路径,结果内核一直报找不到。后来我用strace去跟踪进程,才看到它请求的完整路径是/lib/firmware/aic8800/aic8800_fw.bin,把文件移到对应子目录后立刻正常。
所以,拿到固件文件后,先别急着一股脑拷贝。先去驱动源码里搜索request_firmware字符串,确认它请求的文件名和路径,然后在文件系统里建好同名目录。这一步花不了五分钟,但能帮你绕过最经典的问题。
4.2 确认dmesg加载日志
固件加载是否成功,dmesg里会有明确日志。正常情况你会看到类似:
aic8800_sdio: loading firmware aic8800_fw.bin aic8800_sdio: firmware version 1.1.x aic8800_sdio: chip id 0xXXX如果卡住了,我们要学会看日志的“层次”。第一种是无任何打印,说明驱动可能没有被调用到,或者是SDIO枚举阶段就失败了。第二种是打印了“loading firmware”之后长时间不往下走,那就要怀疑固件文件本身有问题,或者芯片与固件版本不匹配。第三种是显式报错,比如magic error、checksum error,那就直接找原厂要正确版本。
4.3 文件系统权限与安全机制
这个问题在纯Linux rootfs下比较少见,但在带SELinux的嵌入式系统里经常遇到。固件已经放到正确路径了,dmesg却显示permission denied,很可能是SELinux策略阻止了内核态读取文件。
本人之前在某平台的量产机上调过类似问题,内核加载固件的请求会被SELinux拦截,日志里会出现avc: denied字样。排查方式很简单:临时关闭SELinux,用setenforce 0,再做同样的操作。如果关了之后固件能正常加载,说明就是策略问题,需要在te规则里给对应的域加上读固件文件的权限。
Roothfs下也可以用ls -Z查看文件的SELinux标签,看看跟其他正常固件文件的标签是否一致。有时候直接给固件文件打上system_file类型的标签就能解决,具体按平台策略来。
4.4 固件加载与驱动的版本匹配
前面提到了版本匹配问题,这里再展开说。驱动代码在下载固件到芯片之后,通常会读取芯片回传的版本信息或者magic number,跟自己的预期做对比。如果对不上,就会拒绝继续初始化。
这个信息一般在dmesg里能看到类似:
aic8800: firmware magic mismatch, expected 0xXX, got 0xYY遇到这种报错,不要想着怎么绕过,一定要找到配套的固件版本。通常芯片原厂发布的SDK包,里面驱动版本和固件路径是配对好的。有些项目还会因为硬件版本不同,需要不同的nvram射频参数文件,这个也得跟硬件同事确认。
5. 编译烧录与SDIO枚举验证
软件和DTS都弄好了,接下来进入真刀真枪的编译烧录阶段。这一节我按照实际操作的顺序写,每一步都说明目的,方便你判断自己做对了没有。
5.1 单独编译内核
RK3588 BSP的构建脚本一般长这样:
source build/envsetup.sh lunch rk3588_evb-userdebug ./build.sh kernel如果只想编译内核和dtb,fastboot烧录时只烧boot.img对应的分区。单独编内核的好处是速度快,适合反复调试。
编译前再确认一次内核配置里AIC8800确实选中了。可以这样检查:
make ARCH=arm64 menuconfig然后搜索关键词“AIC8800”或者“aic”,确保相关配置项不是N。如果用的是defconfig,也可以直接在里面grep:
grep -i aic kernel/arch/arm64/configs/rk3588_defconfig5.2 烧录并抓取启动日志
编译完成后,一般来说要把boot.img(包含kernel和dtb)烧录进板子。烧录工具根据你的BSP版本来定,RK官方工具或者fastboot都可以。烧录完成后,串口中断启动过程,观察日志。
这个阶段我们要重点确认两件事:
第一,SDIO控制器是否正确所在节点被初始化。在dmesg中搜索mmc相关的打印,例如:
mmc0: new high speed SDIO card at address 0001注意这条日志,它表明内核在SDIO总线上真的发现了aic8800芯片。如果这里的进度条一直是卡的、或者报出timeout错误,请返回到第三部分的DTS检查。
第二,是否有aic8800设备树节点被内核读取。可以在板子上执行:
cat /proc/device-tree/sdio/status输出应该是“okay”。顺便用以下命令查看SDIO总线上注册的设备信息:
ls /sys/bus/sdio/devices/如果能看到一个类似“mmc0:0001”的目录,说明设备已经挂了上来。这个目录的存在,已经说明硬件、DTS、时序都没有大问题,剩下就是驱动和固件的事。
5.3 手动加载驱动模块
如果内核配置里编的是模块,在确认SDIO设备枚举成功后,就可以手动加载:
insmod /path/to/aic8800_fdrv.ko加载之后,立即观察dmesg。正常流程会看到驱动注册、固件下载、芯片初始化成功的日志。然后再执行:
ls /sys/class/net/确认多了一个wlan0或者wlan1接口。有wlanX接口了,说明WiFi驱动层面已经跑通,恭喜你,最麻烦的部分已经过去了。接下来的网络联调,相对简单得多。
6. 网络服务联调与吞吐量验证
驱动加载成功只是第一步,WiFi最终要能连上AP、拿到IP、跑得动流量。这部分的内容很多跟应用层相关,但作为BSP工程师,至少要能定位到问题出在哪一层。
6.1 配置wpa_supplicant
Linux下连接WiFi AP,通常会用到wpa_supplicant。先创建一个配置文件,比如/etc/wpa_supplicant.conf:
ctrl_interface=/var/run/wpa_supplicant network={ ssid="MyRouter" psk="password123" key_mgmt=WPA-PSK }然后启动wpa_supplicant:
wpa_supplicant -D nl80211 -i wlan0 -c /etc/wpa_supplicant.conf -B启动成功后,用以下命令看状态:
wpa_cli -i wlan0 status重点关注“wpa_state=COMPLETED”和“ip_address”两行。如果没有COMPLETED,说明认证失败,要检查SSID和密码,或者AP的信道、加密方式是否跟wpa_supplicant配置一致。
6.2 动态获取IP
连接成功后,配置IP地址。最省事的方式是用dhclient:
ifconfig wlan0 up dhclient wlan0或者用udhcpc:
udhcpc -i wlan0拿到IP之后,先ping一下网关,确认二层三层链路都通。
ping -I wlan0 192.168.1.1如果ping网关通了,但ping外网不通,大概率是DNS或者路由问题,跟WiFi驱动本身没关系。如果ping网关都不通,则需要检查AP的隔离设置、DHCP服务,以及WiFi芯片是否真的关联上了AP。
6.3 吞吐量验证方法
吞吐测试一般用iperf3。先在PC端开一个服务端:
iperf3 -s然后在板子上执行:
iperf3 -c 192.168.1.100 -i 1 -t 30正常SDIO接口的WiFi 6,近距离吞吐量能做到100-200Mbps以上,具体看芯片能力、天线环境、以及SDIO跑在什么时钟模式。如果吞吐量明显偏低,比如只有二三十Mbps,可以从几个方向排查:
- SDIO总线速率没跑上去。DTS里的max-frequency如果设置成50MHz,WiFi 6的速度肯定会被限制,试着提到100MHz或150MHz。
- 天线或者环境问题。WiFi信号弱、干扰大,速率会自动降级。用iw dev wlan0 link命令查看当前链路速率和信号强度。
- 驱动里默认开启了省电模式。省电模式会定期进入休眠,严重影响吞吐量。可以用iw dev wlan0 set power_save off先关闭省电,测一下是否明显改善。
吞吐量问题是BSP工程师和上层应用很容易互相甩锅的领域。作为调驱动的人,我建议你自己先用iperf3在近距离、无干扰环境下测出“该方案的理论值”,后续上层说速度不达标时,至少能有一份清晰的基线数据。
7. 常见问题排查与避坑记录
这个部分是我觉得最有实战价值的一块。我把整个调试过程中遇到过的、以及跟同行交流时高频出现的问题整理成了一份速查表,后面再单独讲几个印象特别深的坑。
7.1 问题速查表
| 故障现象 | 可能原因 | 排查手段与解决办法 |
|---|---|---|
| 系统启动后没有wlan0接口 | 驱动未加载、SDIO枚举失败、固件加载失败 | 先看dmesg,确认sdio设备枚举和fw加载日志,再查DTS和固件路径 |
| insmod报“Device or resource busy” | 内核中同名驱动已经内建,或驱动重复加载 | 检查内核是否把驱动编成了y,用lsmod和dmesg确认驱动状态 |
| SDIO枚举时CRC错误刷屏 | SDIO时钟太快、供电不足、信号完整性差 | 降低max-frequency,检查vmmc和vqmmc供电电压,改善地线连接 |
| 固件加载提示permission denied | 文件权限不对或者SELinux拦截 | chmod 644、检查SELinux avc日志,setenforce 0对照实验 |
| 固件加载提示magic mismatch | 驱动和固件版本不匹配 | 找回原厂配套SDK包,核对版本号,禁止混用 |
| WiFi连接后反复掉线 | 省电模式、AP不稳定、干扰严重 | 先关省电模式,再更换测试AP验证,观察掉线时的dmesg |
| 吞吐量远低于标称值 | SDIO时钟低、天线差、WiFi协商速率低 | 查看iw link协商速率,提高max-frequency,关闭省电 |
| 连不上5G频段AP | 固件频段限制或国家码设置不对 | 检查固件支持频段,用iw reg set调整国家码 |
| 蓝牙和WiFi互相干扰 | 天线共存、共用晶振、固件配置问题 | 检查模组天线的物理布局,确认板级共存方案是否有PTA信号 |
7.2 一个被“GPIO占用”坑了一晚上的案例
那次调的是另一块自研板。WiFi驱动加载时报GPIO请求失败,dmesg显示类似“gpio_request: gpio 146 already used”。我看到这个报错,第一反应是去改DTS里的GPIO编号,改成别的引脚。结果改了几次依然冲突。
后来我详细翻了driver的GPIO申请逻辑,发现它申请的不只是复位脚,还有host_wake和chip_enable等多路GPIO。其中某一路GPIO在DTS里被另一个外设节点占用了,但我只改了复位脚,没注意那一路。交叉搜索所有dts文件里对同一个GPIO的引用之后,才定位到问题。
这事的教训是:GPIO冲突排查,不要只盯报错行里的数字,要把驱动里申请的所有GPIO列出来,然后全局搜索每一个GPIO是否被其他节点引用。用grep搜索整个dts目录,是最笨但最有效的方式。
7.3 冷启动不识别、热重启正常的诡异问题
这个案例更具迷惑性。按复位键重启,WiFi一切正常;但完全断电再上电,有概率WiFi识别不到,卡在SDIO枚举。这种问题,十有八九是电源时序问题。
简单来说,WiFi芯片上电后,内部需要一段时间初始化。如果SDIO控制器在这段时间内就开始枚举,芯片还没准备好,自然无法应答。pwrseq里的post-power-on-delay-ms就是用来兜底的。把60ms改成120ms之后,冷启动概率明显下降。
但这种问题也可能跟外部电源轨的爬升时间有关。如果供电电路本身就很慢,比如电容太大导致3.3V爬升缓慢,那SDIO控制器侧看到的时间点跟WiFi芯片实际Ready的时间点会差很多。这种硬件特性导致的问题,软件只能靠增加延时和重试来缓解,根治还是要改电路。
7.4 省电模式带来的高延迟陷阱
Aic8800驱动默认可能开启WiFi省电模式,用来降低整机功耗。但代价是网络延迟会明显增加,尤其在交互类应用或者ping测试时,延迟经常跳到几十毫秒甚至几百毫秒。
我在联调阶段,客户反馈WiFi ping网关延迟不稳定,一开始怀疑是驱动中断处理问题。后来用iw dev wlan0 get power_save查看,发现power save是on。我用以下命令关掉再测:
iw dev wlan0 set power_save off延迟立刻稳定在个位数毫秒。量产阶段如果要保留省电特性,建议做成可配置项,按场景开关。在调试阶段,请一定先关掉省电模式再测,不然后面做的所有吞吐和延迟测试数据都不干净。
7.5 天线匹配问题被误判为驱动问题
另一个常见的甩锅现场是信号太弱。客户报障“WiFi连接距离近,隔一堵墙就掉线”,第一反应往往是驱动功率没配置好。但实际上,模组的天线引脚是否焊接正常、天线的净空区是否足够、天线阻抗是否匹配,这些硬件问题对信号的影响远比驱动配置大得多。
BSP工程师能做的软件侧检查是:用iw dev wlan0 station dump查看信号强度(signal),用iw dev wlan0 set txpower设置发射功率,看最大能到多少dBm。如果信号强度本身就很差,比如-70dBm以下,而且近距离也是这个水平,那就不是软件能救的,尽早拉硬件同事介入。
8. 几个能直接提升调试效率的小习惯
最后分享几个我自己长期调BSP外设总结出来的习惯,虽然不全是aic8800专属,但对同类调试很有帮助。
第一,dmesg日志永远加时间戳。在串口终端上,我习惯优先打开kernel的timestamp输出,这样排查问题时可以精确到毫秒级,判断各个节点初始化时序是否合理。
第二,固件和驱动的变化,一定用版本号管理。不要用“最终版”“新新版本”这类文件名,在量产项目里你会被绕晕。建议建一个文件夹,把每个SDK包按“日期_驱动版本_固件版本”命名,谁在什么时候用了哪一版,一目了然。我见过太多团队因为固件版本混乱,白白浪费一两天去复现一个根本不存在的bug。
第三,修改DTS前,先备份当时的版本,并随手记录修改点。设备树文件动辄几百行,全是大括号嵌套,改错了回滚起来非常痛苦。用git管理kernel目录,每次修改提交一个commit,注释写清楚改了什么、为什么改。这一点在多人协作时尤其重要,不然同事改了你不知道的节点,最后的责任很难分清楚。
第四,SDIO信号质量可以直接看CRC错误计数。在板子上执行:
cat /sys/kernel/debug/mmc0/err_stats如果这个统计在持续增长,说明SDIO链路本身存在数据错误,后面出现的WiFi卡死、吞吐骤降都可能跟这个有关。先修链路完整性,再谈WiFi性能。
调完aic8800这段时间,我最大的体感是:WiFi不像UART或者I2C那样只要寄存器配对了就能工作,它是一个完整的独立系统,有自己的固件、自己的电源管理、自己的射频前端。BSP工程师要做的,其实是把这个小系统跟主控平台无缝对接起来。对接的关键点无非就是设备树、供电时序、固件路径和总线配置这几件事。把这几件事捋顺了,大部分问题都能迎刃而解。后面如果时间允许,我准备再写一篇aic8800蓝牙部分的调试记录,蓝牙跟WiFi共存时的干扰排查也挺有意思的。