我手里有一块 Yuzuki Chameleon 开发板,咸鱼看到便宜板子忍不住的性子就买了回来,上面用的是全志 H616 芯片,板载了一颗 XR829 WiFi/BT combo 芯片.出厂自带的是 Tina Linux(4.9内核),WiFi蓝牙都好好的,可是USB不行,还大量噪音,我这洁癖怎么受得了,我想跑主线内核,具体来说是 6.18.29 (LTS),这就意味着得把 XR829 的驱动移植上去.
这条路走了大概一周,踩了无数坑.写下这篇记录,一方面给自己留个档案,另一方面如果有人也在折腾类似的国产 WiFi 芯片移植,希望能有点参考价值.
一开始是scan complete 但看不见任何 AP.
移植的第一步是基于 6.18 主线里已有的 CW1200 驱动改.XR829 和 CW1200 用的是同一套 WSM(Wireless Subsystem Macro)协议框架,所以有人已经做了一版初步移植.但这版移植能 scan,scan 也能 complete,就是结果里一个 BSS 都没有--连旁边十几个路由器一个都扫不到.
这就很诡异了.scan 完成了说明固件起来了,SDIO 通信正常,命令通道通了.但扫不到 AP,问题可能出在 RF 初始化,固件配置,或者更底层的东西上.
我一开始在 5.15 BSP 上做baseline调试,因为 5.15 也有一版 XR829 驱动.但很快发现 5.15 自己也是从 4.9 移植过来的,不确定正确性,不能当金标准.真正的金标准是 Tina 4.9 SDK 里的原厂驱动.
在这段时间里我试了一大堆东西,很多后来被证明是无效的:
- 把 SDIO 电压从 1.8V 改到 3.3V
- PG11 WLAN_REG_ON 的 GPIO 归属和 mmc rescan
- 加上 PG16/PG17 的 BT wake GPIO
- PG10 的 32K 时钟拉高/RTC 时钟源切换
- 换成 Tina 出厂 MAC 地址
- 关掉 powersave
- pre-scan 的时候关掉 RX/BSSID/beacon filter
- 改 regdomain 为 CN/JP/US...
- DPLL 覆盖成 40000 KHz 或 26000 KHz
是的,每一个"没用"背后都是一次编译,上传,重启,等开机,跑 scan,看结果的循环.板子只能通过串口操作,上传速度大约 10KB/s,一个模块 600K 要传一分钟.加上重启等待,每次实验的周期至少三四分钟.
后来证明这些单变量都没能解决 scan no-BSS 的根本问题.
接着发现 PG12 中断风暴.
在调试过程中发现了一个很有意思的问题.PG12 是 XR829 的 WIRQ(WiFi中断请求)脚,接在 H616 的 PG12 上.初始配置用了 low-trigger 的中断触发方式,结果触发了中断风暴,/proc/interrupts 里 IRQ 209 的计数以百万级别疯涨.
改成 active-high 之后中断风暴消失了,但 scan 仍然没有结果.这说明中断极性确实有问题,但不是 scan 失败的根因.
XR829 需要 32.768KHz 的低频时钟.板子上没有外挂 32K 晶振,按照原理图这个时钟应该由 H616 的 PG10/X32KFOUT 提供.我在这上面花了不少时间:改设备树的时钟源描述,改 pinctrl 配置让 PG10 输出 32K,换不同的 RTC clock provider,甚至试过用内部振荡器.最后确定的方案是通过 H616 的 HOSC32K fanout 功能,让 PG10 输出 32K 给 XR829.寄存器操作是 0x00000001 改成 0x00010005.但是你可能会说,32K没有不是明显的吗,真不是,一开始实在被板子上错误观感误导了,因为他两个晶振一大一小,结果其实是一个是SOC一个是WIFI.
这个时钟对 BT 固件下载的可靠性有明显影响.修好之前 BT 固件下载大约只有 50% 的成功率,修好之后变成 100%.
最大的坑:模块上传路径
我在 6.18.29 上调试的时候,每次编译好新的 xr829.ko 模块都上传到 /lib/modules/6.18.29/updates/xr829.ko.上传完 modprobe,觉得新功能应该生效了,跑测试,发现结果完全没变化.反复好几次之后我怀疑模块没被加载.跑了一下 modinfo -n cw1200_core(当时驱动还叫 cw1200 名字),发现系统实际加载的是 /lib/modules/6.18.29/kernel/drivers/net/wireless/st/cw1200/cw1200_core.ko.modules.dep 里 kernel/ 路径优先于 updates/ 路径.我一直上传到了 updates/,而 modprobe 根本不看那个路径.也就是说之前好几次所谓的"测试"实际上跑的还是最初的旧模块.发现这个问题之后我把上传路径改对了.这是一个典型的"你以为改了但没改"的坑,在这种只能串口操作的嵌入式环境下特别容易中招,因为你没法直接看到文件系统,只能远程操作.
经过大量的折腾后WiFi STA 终于通了,6.18 上的 WiFi STA 模式在某个时间点终于打通了.能连上家里的Xiaomi_Demo路由器,拿到 IP 地址 192.168.31.19,ping 网关也没问题.但高兴没太久就发现新的问题.因为HT/11n 模式下的数据面死亡
这是最折磨人的一个 bug.现象是这样的:开启 802.11n HT 模式后,用 iperf3 做吞吐测试.反向测试(板子发包到主机)大约在 20-30 秒的时候必然断流.不只是 iperf3 断,整个网络都死了,ping 网关 100% 丢包,SSH 也连不上.但 wpa_state 显示 COMPLETED,说明 WiFi 连接本身没有断.ARP 表里网关的条目还是 REACHABLE 状态.检查 XR829 驱动内部状态:TX 队列空,WSM 空闲,数据面锁没卡住.RxDecryptionFailures,RxMicFailures,RxNoKeyFailures 全是 0.所有指标看起来都正常,但数据就是过不去.唯一的恢复办法是 wpa_cli disconnect 再 reconnect,重新协商密钥.
排查过程非常漫长,一开始怀疑是 power save 的问题,因为关掉 power save 之后现象似乎有变化.但后来证明 ps_disable=1 加上固件 power save active,在 HT 模式下 iperf3 依然会在 20 秒左右失败.不是 power save 的问题.然后怀疑 SDIO 时钟太快.把 mmc1 的 max-frequency 从 50MHz 降到 25MHz.也没用.怀疑 TX BA(Block ACK).关掉 STA 的 TX BA,让 AMPDU 计数为 0.还是失败.怀疑 HT40,Short GI,Greenfield.全部禁用,只用 HT20 + long GI.还是失败.直到有一天我试了 runtime 设置 MCS mask:iw dev wlan0 set bitrates ht-mcs-2.4 0,把 HT 限制到只支持 MCS0(最低速率).重新连接后跑反向 iperf3 120 秒,没事,我又连跑1小时,没事.
然后在驱动层面做同样的事情,把 2.4GHz band 的 HT capability 改成只 advertise MCS0:ht_cap.mcs.rx_mask[0] = 0x01.这样 AP 端就不会尝试用 MCS1 及以上的速率给板子发数据了.这个 workaround 验证通过:反向 iperf3 PASS,4.20 Mbit/s;正向 iperf3 PASS,20.4 Mbit/s.测试完 ping 网关很久都OK.
代价是吞吐量受限.MCS0 的理论速率只有 6.5 Mbps,实际反向大约 4 Mbps.如果 MCS1-7 能用,反向应该能到 20-30 Mbps.但至少稳定了.根本原因目前定位在 MCS1+ 的 TX 速率策略/固件行为上.4.9 BSP 和 6.18 在 HT capability 广告,MCS/rate retry 策略构造,固件 rate entry 编码,TX status/rate feedback 处理这些地方可能有差异.这还是个开放问题.
PTK 损坏,WiFi 连上之后偶尔会遇到一种情况:wpa_state=COMPLETED,IP 地址也有,ARP 能解析网关的 MAC 地址,但 ping 网关 100% 丢包.更诡异的是广播/多播 IP 包能通,只有单播 IP 包不通.这说明 PTK装错了或者损坏了.广播/多播用的是 GTK,GTK 没问题所以广播能通.单播用 PTK,PTK 不对所以单播全丢.
这个问题有两种触发方式:
第一种是 BT 压力测试时.开 BLE 广播加上 GATT 连接,WiFi 就容易进 bad state.后来发现是 WPA tailroom 的问题--6.18 的 mac80211 不再自动为加密 IV 预留 tailroom 空间,但 XR829 驱动还在往 SKB 里注入 IV/ICV,空间不够就破坏了加密数据.加上 IEEE80211_KEY_FLAG_RESERVE_TAILROOM 之后 BT 压力触发的大概率死亡被修复了.
第二种是开机后直接出现,不需要任何 BT 活动.这个更隐蔽--根因是 xradio_set_key() 往固件下发密钥的时候,mac80211 可能已经开始在另一个线程发加密数据帧了.固件还没完成密钥编程,加密数据帧就过来了,结果前面几帧用了错误的密钥.
修复方案是在 SET_KEY 和 DISABLE_KEY 操作期间持有 wsm_lock_tx().这个锁会先 flush 所有在途的 TX 帧,然后阻止新的数据帧发送.等固件确认密钥安装完毕后再解锁.修复后连续 3 次重启,每次开机后 ping 网关都OK.
在无线驱动的密钥安装路径上,必须考虑和 TX 路径的并发竞争,原来umac可能有处理,只能说,KAPI也是大坑.mac80211 框架不会帮你做这件事,驱动得自己保证.
SoftAP 的坑比STA还多,XR829 在 AP 模式下工作花了很多尝试.
第一轮:发现 hostapd 启动后 Arduino 能发 AUTH request,板子也回 AUTH response,但每一帧的 TX confirm 都是 status=1(RETRY_EXCEEDED),意味着固件发了但没收到 ACK.更糟糕的是 pr_info 的 debug 日志形成了风暴,把串口堵死了,连 sbctl 命令都执行不了.
第二轮:加了 wsm_set_inactivity,这是 4.9 里有的一个命令,让固件知道 AP 模式下多久没数据就认为 STA 不活跃.部署验证成功了,但 auth response 依然 TX FAIL.也是在这轮发现了上面提到的模块上传路径问题--之前所有的改动根本没生效.
第三轮:试了用 raw_link_id=0 发管理帧,这样 AUTH response 用广播 link 而不是分配的 unicast link.没用.wsm_map_link 还是被固件 NACK.
第四轮:深入分析 RX 路径,发现固件上报的 incoming AUTH frame 的 link_id 是 0.也就是说固件不会自动为管理帧分配 link_id.但驱动在 TX 方向给分配了 link_id=1,两边不匹配.不过即使知道了这一点,也没法直接解决 TX 失败的问题.
第五轮:干脆跳过 wsm_map_link,假设固件会在第一次 TX 的时候自动映射 STA.结果 TX 还是 fail.这轮我做了 WSM TX header 的逐字节 hexdump 验证,确认 WSM 层和 802.11 层的帧格式都是对的.帧格式完美无缺,固件就是不发.
第六轮:启用了之前被 if (hw_type != HIF_XRADIO) 屏蔽的 EDCA 参数配置.EDCA 命令成功下发,没报错.但 TX 依然 fail.
六轮下来,能排除的都排除了.最后发现真正的根因不在这些地方.后面在改用 XR829 原生驱动(而不是基于 cw1200 的移植版)之后,AP 模式自然就通了.
真正让 AP 工作的是三个补丁:
第一个:AP VIF 的 if_id 从 2 改成 1.固件用 if_id=2 来返回 scan 结果,原来代码把 AP 也分到了 if_id=2,导致 scan 和 AP 的帧混淆.
第二个:禁用 PROBE_RESP_EXTRA_IE 宏.6.18 的 ieee80211_proberesp_get() 返回 NULL 了,之前代码没处理这个情况.
第三个:取消注释 AP 模式下的 xradio_update_filtering().原来这行被注释掉了,AP 模式的 RX filter 从来没配置过,auth/association 帧进不来.
三个补丁打上之后,AP 完美工作.STA+AP 并发也通过了.
还有一个关键发现:STA+AP 并发模式下,AP 必须先于 STA 启动.先 hostapd 再 wpa_supplicant.顺序反了会出问题.
蓝牙:固件版本是关键,BT 的情况比较简单但也有坑.
硬件上 XR829 的蓝牙走 UART1 四线串口(TX/RX/RTS/CTS),硬件流控.GPIO 方面板子只连了 BT_RSTN(PG13)到 H616,BT_WAKE 和 HOST_WAKE 都没接.驱动里 bsp_stubs.c 有个 bug:xr829_board_init() 在找不到 allwinner,sunxi-wlan 设备树节点的时候直接 return 了,导致后面 BT GPIO 的 fallback 赋值变成死代码.修掉这个之后 BT 固件下载从 50% 成功率变成 100%.但 HCI Reset 一直超时.试了各种波特率,流控开关,delay 组合,全部失败.
直到换了一个 BT 固件:出厂固件HCI Reset 必然超时;换成一个 40M 版本的 BT 固件,从 ROM sync,baud 切换,固件下载,SETPC,HCI Reset,HCI baud 更新,全部一次通过.hciattach /dev/ttyS1 xradio 1500000 flow 这一条命令搞定.
WiFi 和 BT 都通了之后,共存是个大问题.XR829 是 combo 芯片,WiFi 和 BT 共享 RF 和天线.早期的 PTK 损坏问题修好之后,共存的主要症状变成了一种瞬态中断:BLE 活跃期间偶尔出现 ping 全丢,但 wpa_state 正常,ARP 正常,驱动内部状态全部正常,没有解密错误,然后过一会儿自己恢复.这跟早期需要 reconnect 才能恢复的持久性死亡不一样.这个瞬态问题可能跟 EPTA有关,但还没深入调查.目前优先级是先解决 HT MCS1+ 的稳定性问题.
功能基本通了之后做了一轮大清理,模块从 574K 缩到 156K,通过一个 XR829_DEBUG Kconfig 开关控制是否编译 debug/ETF 代码.
源码自取 https://github.com/nickfox-taterli/yuzuki-chameleon-linux-6.18.29