1. USB OTG Gadget 概述
1.1 什么是 USB OTG?
USB OTG,全称是 USB On-The-Go。说白了,就是让一个 USB 设备能随时切换身份——今天当主机(Host),明天当外设(Peripheral/Device)。
我刚开始接触 USB 时,总以为 USB 世界就是“电脑接鼠标键盘”这么简单。后来做嵌入式,发现设备之间直接通信的需求太多了。比如你拿着一个开发板,想直接插 U 盘读数据——传统方案得先接电脑,尴尬得很。OTG 就是解决这个问题的。
OTG 在硬件上依赖一根特殊的 ID 引脚。Micro-USB 或 Type-C 的插头里,第五脚就是 ID 线。ID 接地表示 A 侧(通常是 Host),ID 悬空表示 B 侧(通常是 Device)。芯片检测 ID 电平后,会自动配置自身的角色。嗯,这里要注意:早期很多方案只靠 ID 硬切换,后来出现了通过软件协商角色的 HNP(Host Negotiation Protocol) 和 SRP(Session Request Protocol),让切换更灵活了。
1.2 Gadget 模式与 Host 模式的区别
Gadget 模式又叫 Device 模式,它让嵌入式设备扮演一个“USB 外设”。比如开发板模拟成键盘、鼠标、U 盘、串口等等。Host 模式呢,就是让嵌入式设备扮演“主机”,能挂载 U 盘、接 4G 模块、连摄像头。
两者在软件栈上差异很大,我列个表你一看就明白。
| 对比维度 | Gadget 模式 (Device) | Host 模式 |
|---|---|---|
| 角色 | 作为外设,被电脑或其他主机枚举 | 作为主机,主动枚举外设 |
| 驱动栈 | 内核 gadget 子系统(如 configfs/g_serial.ko) | 标准 USB 主机控制器驱动 + 类驱动 |
| 电源控制 | 通常由主机供电(VBUS 来自主机) | 需对外提供 VBUS 电源(5V,至少 100mA) |
| 枚举过程 | 被主机复位 → 接收 Get Descriptor 请求 → 配置 | 主动复位总线 → 发送 Get Descriptor → 分配地址 |
| 常见应用 | 虚拟串口、ADB、MTP、RNDIS 网络 | 读 U 盘、接 HID 设备、控制 USB 摄像头 |
| 硬件要求 | 支持 Device 模式的 USB 控制器(内置 DMA/端点) | 支持 Host 模式的 EHCI/OHCI/xHCI 控制器 |
我在项目中遇到过一个坑:明明硬件支持 OTG,但加载的 gadget 驱动就是枚举失败。后来发现是 VBUS 检测引脚没接对,芯片一直以为自己被拔掉了。你想想看,Host 模式需要提供 VBUS,Device 模式却要检测 VBUS,一旦混淆就完蛋。
1.3 为什么你会关心枚举失败?
枚举是 USB 通信的第一步。不管是 Gadget 还是 Host,如果枚举通不过,后面的数据传输根本不会发生。我排查过几十起枚举失败的问题,最常见的原因就那么几个:
- 硬件层面:DP/DM 走线过长、缺少 ESD 保护、VBUS 供电不足、ID 引脚电平错误。
- 软件层面:驱动加载顺序错乱、描述符配置不对、端点数量或大小超过控制器限制。
- 协议层面:复位信号时序不满足规范、Setup 包无响应、配置值不匹配。
所以这门课我会带你从原理到实战,逐个击破这些坑。我之前带过一个新人,花了两天查不出问题,最后发现只是 g_ether 模块没加载完整——这种小错误其实很常见。
1.4 OTG 角色切换的典型场景
一张图帮你捋清 OTG 设备的状态机。我个人习惯把这个图贴在实验室白板上,每次定位问题先看当前角色对不对。
我的小习惯: 调试 OTG 枚举时,先用 lsusb -t(Host 侧)或 cat /sys/kernel/debug/usb/gadget/.../state(Gadget 侧)确认当前模式。如果状态不对,90% 是 ID 引脚或者 VBUS 检测的问题。
1.5 硬件上的“暗雷”
我曾经踩过一个经典的雷:某款 SOC 的 OTG 控制器只有一个共用 D+/D-,切换角色时 内部开关需要额外延时。我在驱动里没等这个延时,结果角色切换后总线乱掉,枚举直接 timeout。后来加上 10ms 的 mdelay 就好了。
另一个常见问题是 USB 电阻偏置。Host 模式下,D+ 和 D- 的下拉电阻要控制在 15kΩ 左右;Device 模式下,D+(高速)或 D-(全速)的上拉电阻是 1.5kΩ。我曾经见过一块山寨开发板,外部上拉焊成了 10kΩ,导致主机检测不到设备——折腾半天才查出来。
所以排查枚举问题,第一步就是 用示波器抓 DP/DM 波形。你看不到波形就猜,浪费时间还容易走弯路。我一般抓三个关键点:复位信号持续时间(至少 10ms)、Chirp 序列(高速握手)、Setup 包的响应。只要波形对,软件问题就缩小到描述符和端点配置了。
1.6 课程整体结构预览
既然这是第一章,我先说说后面的安排:
- 前 5 章:USB 协议基础 + 内核 Gadget 框架(configfs 用法、描述符构造)。
- 第 6-15 章:常见枚举失败案例(驱动未加载、端点冲突、速度协商失败等),含真实 dmesg 日志和解决方案。
- 第 16-20 章:Host 侧枚举问题(USB 集线器、电源管理、传输错误)。
- 第 21-25 章:硬件调测手段(示波器抓包、USB 协议分析仪、u-boot 中的 USB 初始)。
- 最后 5 章:复杂场景——复合设备(Composite Gadget)、HNP/SRP 切换、Type-C 配合 PD 的枚举。
注意: 本章不会涉及具体代码,但从下一章开始我会贴真实内核源码片段。建议你手边备一台 Linux 开发环境(或 WSL + 交叉编译工具链),跟着一起跑一遍。光看文章没用,动手踩坑才能记住。
1.7 写在最后
OK,这一章就这些。USB OTG 说白了就是“角色可变”的 USB 节点。Gadget 模式当小弟,Host 模式当大哥。枚举失败呢,往往出在角色认知错误或者物理层不通。记住这张状态图,后续排查时你就能快速定位是软件没配置对,还是硬件没准备好。
我当年刚开始调 Gadget 时,总觉得文档看懂了,可实际跑起来就是不通。后来养成了个习惯:每次遇到枚举失败,先问自己三个问题——ID 电平多少?VBUS 有没有?DP/DM 波形对不对? 回答完这三个,80% 的问题都能缩小范围。希望你也尽快养成这种直觉。