全志T113串口RX"永久失聪"实录:一场DMA描述符池上的开局竞态
Contents
T113 音源播放器上跑着两路业务串口:uart4(ttyS4)接 OTG 音源切换芯片,uart5(ttyS5)接蓝牙模块,DTS 里都配了 use_dma = <3>(TX/RX 全走 DMA)。这周先后出现两起"串口收不到数":先是 OTG 芯片"未连接",修好之后隔了一次烧录,蓝牙又"聋"了——同一个病根,两次发作在不同的串口上。这篇完整复盘根因、修复和一个值得记住的竞争格局故事。
一、第一次发作:OTG 永远"未连接"
现象:面板 OTG 页面永远显示"未连接",发查询无响应。
分层排查下来芯片电源、使能脚都正常,dmesg 里躺着一行指纹:
|
|
每次开机必现。业务进程读 ttyS4 永远是 0 字节。
关键一步是外置串口旁路实测:USB 串口适配器并联到芯片的 TX 脚上(只听不打扰),主机发查询——芯片清清楚楚回了一帧 B6。硬件完好,数据确实在发,是内核这头的接收链聋了。
二、第二次发作:蓝牙只剩占位符
uart4 修好后(下面讲怎么修的)打的新固件上板,蓝牙页变成这样:
| 字段 | 显示 |
|---|---|
| 设备名 | Bluetoooth |
| 标题 | 蓝牙音乐 |
| 艺术家 | 蓝牙设备已连接 |
| 专辑 / 进度 / 格式 / 采样率 | 全空 |
全是面板的占位文案——说明主机一条蓝牙推送都没解析到。dmesg 这次轮到:
|
|
而同一个固件,上一次开机蓝牙还是好的。
三、病根:开局竞态,谁后开谁失聪
机制拆开是这样的:
- 这颗 SoC 的 UART 驱动(sun6i-dma)用一组公共的 RX 描述符池,
use_dma=3时 RX 也走 DMA,open 设备时就占用 RX 描述符; - 业务进程启动时几乎同时 open ttyS4 和 ttyS5,池子不大,先到的拿走,后到的分配失败;
- 分配失败的路径上没有重试也没有降级,这个 tty 的 RX 链路从此空转,close 再 open 也不恢复。
所以它是"开局竞态":每次开机看谁后 open,谁就聋。这就解释了两件事——为什么同一固件两次开机两种结局(不是硬件偶坏);以及为什么修好 uart4 之后 uart5 反而聋了:uart4 退出竞争后,竞争格局翻面,轮到 uart5 当"后开"的那个。修好 A、B 立刻坏,是资源竞争类问题的标准指纹。
四、修复:RX 让出 DMA,回到中断接收
两路都钉死 use_dma = <1>(TX 走 DMA,RX 中断/PIO):
|
|
RX 放弃 DMA 划不划算,看流量:
| 串口 | 实际流量 | 中断接收开销 |
|---|---|---|
| uart4(OTG 芯片) | 一次查询 7 条读、约 7×5 字节 / 3 秒 | 忽略不计 |
| uart5(蓝牙模块) | 文本状态行,峰值约 20 行/秒 | 115200 波特率下绰绰有余 |
RX DMA 的价值场景是高波特率大流量连续灌数据;业务侧这种小包文本状态流,PIO 完全够用。为用不上的性能引入一个开局竞态,纯亏。
五、方法论沉淀
- dmesg 指纹先行。
get rx dma descriptor failed一行顶半小时瞎猜——串口类问题先把 dmesg 里 dma/uart 相关的行捞干净。 - 外置串口旁路是分清"物理死"和"内核聋"的唯一可靠手段。并联在对方 TX 上只听不打扰,芯片到底有没有发数一目了然,不会被主机侧的解析 bug 带偏。
- “修好 A,B 立刻坏"指向资源竞争。不是你修坏了,是你改变了竞争格局——把两路外设的同类配置一起检查一遍。
- 不确定性本身就是线索。同一固件两次开机不同结局 = 开局竞态,别往"偶现硬件故障"的方向想。
六、遗留提醒
如果哪天确实需要 RX DMA(比如串口流量上去了),正确解法不是改回 3,而是先解决描述符池的分配时序(串行 open / 延迟第二路 / 加大池子),并且 dmesg 里必须盯着那行 descriptor failed 做开关机压力验证。在这个修改落地之前,use_dma=1 就是这对业务串口的最终答案。
Author 软件开发大郭
LastMod 2026-09-26