你的直觉非常敏锐!ADB 确实“用起来像串口”,但它在底层“绝对不是串口”!
它们之所以会“抢”,是因为它们在争夺 USB 芯片内部有限的硬件资源(插槽与管道)。
下面为你拆解:为什么 ADB 用起来像串口?以及它们到底在抢什么?
一、 ADB 是串口吗?(为什么用起来那么像?)
答案:不是串口。
- 为什么用起来像?
因为你用adb shell敲命令时,界面和串口终端(如 SecureCRT / Putty)一模一样,都是打字、回车、返回结果,数据也是流式传输的。 - 底层的本质区别:
ttyGS(CDC-ACM):是符合国际标准的硬件级虚拟串口(遵循 USB CDC-ACM 协议)。它在内核里会生成标准的串口设备节点(如/dev/ttyGS2),任何 C 语言/Java 串口库都能直接 open/read/write 它。- ADB(FunctionFS):是 Google自定义的一套私有网络协议(类似于一个运行在 USB 上的 Mini 版 TCP/IP 协议)。它在内核里使用的是
ffs.adb(FunctionFS),必须运行一个名为adbd的守护进程才能解析数据。
二、 既然 ADB 不是串口,为什么会和ttyGS抢呢?
它们在底层并不是“业务上打架”,而是在抢USB 芯片的 3 种底层硬件资源:
1. 抢 ConfigFS 的“乐高插槽” (导致File exists报错)
在 Linux USB 复合设备(Composite Device)中,插槽编号是有限的(f1,f2,f3):
- 单 ADB 脚本说:“我要把 ADB 放在
f1插槽!” - 串口脚本说:“我也要把
ttyGS2串口放在f1插槽!” - 结果:后执行的脚本发现
f1已经被占了,直接抛出symlink() failed: File exists崩溃!
2. 抢芯片内部的“硬件传输管道”(Endpoints / 端点,导致Device busy报错)
联发科 MT8189 芯片内部的物理传输管道(Hardware Endpoints)数量是有限的(比如只有 8 个 IN/OUT 管道):
ttyGS2串口需要占用3 个管道(数据输入、数据输出、中断响应);ffs.adb需要占用2 个管道(数据输入、数据输出);mass_storage(U盘)需要占用2 个管道。- 冲突原因:当旧脚本试图把 U盘 + ADB + 串口 + 网卡全部挂载进去时,芯片的物理管道不够用了!内核直接拒绝写入,抛出
Device or resource busy!
3. 抢物理主机的“USB 设备描述符”
当 USB 线插进电脑/母 设备 时,母 设备 必须知道这条线里包含了什么功能:
- 如果子 设备 告诉母 设备:“我只有 ADB”;
- 后来子 设备 又想加上串口,但没有先断开线(
pullup D-)重新汇报; - 母 设备 的驱动就会错乱,不知道该把数据发给 ADB 还是发给串口,导致整个 USB 接口假死。
💡 总结:我们改的代码是如何让它们“和平共处”的?
你修改后的acm,adb组合脚本,做到了以下三点和谐共处:
- 明确分工(不抢插槽):强制规定
f1专门给acm.gs2(串口),f2专门给ffs.adb(ADB)。 - 剔除多余功耗(不抢管道):把没用的
mass_storage(U盘)剔除掉,省下硬件管道给串口和 ADB 享用,再也不会触发Device busy。 - 规范汇报(打包成复合设备):向母 设备 汇报:“我是一个复合设备(Composite Device),内部包含了 1 个串口 + 1 个 ADB”,母 设备 识别后,就会同时给子 设备 分配独立的串口通道和 ADB 通道!