1. USB OTG概念与驱动模型概览
做嵌入式Linux驱动开发的,几乎都绕不过USB OTG。我记得刚入行那会儿,领导扔给我一块板子让我把USB Host和Device功能跑起来,我心想这不就是插个U盘的事嘛。结果折腾了整整一周,发现不是驱动加载失败就是角色切换崩了。后来我才明白,要搞懂这些问题,得先弄清楚USB OTG到底是个什么东西,以及它的驱动模型长什么样。
1.1 什么是USB OTG?
USB OTG的全称是USB On-The-Go。说白了,它允许一个USB设备既能当Host(主机),也能当Device(从设备)。普通的USB设备角色是固定的——电脑永远是Host,鼠标永远是Device。但嵌入式设备不一样,它今天可能当个U盘给别人拷数据,明天又要插个游戏手柄自己玩。
我遇到过这样的项目:一款智能电视盒,用户插U盘时电视盒是Host,当通过USB线连电脑刷机时,电视盒又得变成Device。这就是典型的OTG场景。嗯,这里要注意,OTG不仅仅是切换角色那么简单,它还涉及硬件检测、电源管理、驱动绑定等一堆底层逻辑。
| 模式 | 角色 | 典型场景 |
|---|---|---|
| Host模式 | 主机,供电方 | 插U盘、鼠标、键盘 |
| Device模式 | 从设备,受电方 | 连电脑做存储/UVC摄像头 |
| OTG模式 | 动态切换 | ID引脚电平决定角色 |
1.2 USB OTG的驱动模型分层
Linux内核里的USB驱动不是一个大块,而是分层的。我习惯把这套模型理解为三层结构,每一层干自己那摊活,层与层之间通过回调函数和数据结构交互。你看这个图,基本就清楚了:
1.3 各层具体是干什么的?
先说说最底层的HCD(Host Controller Driver)。这一层直接跟硬件寄存器打交道,负责处理中断、DMA传输、端点调度。我调过一款某厂的USB控制器,它的中断处理函数里有个竞态条件,导致双核CPU上偶尔丢包——查了三天才定位到。HCD层出问题,通常表现为设备插上去没反应,或者传输数据老是CRC错误。
再往上是USB核心层。这一层不直接操作硬件,而是负责解析USB协议、设备枚举、分配地址、管理配置描述符。它还负责OTG的角色协商——比如ID引脚变化了,核心层要通知上层的Gadget驱动切换模式。核心层如果出毛病,最常见的现象就是设备能识别但枚举失败,或者角色切换卡住。
最上层是UDC(USB Device Controller)驱动。这一层实现具体的设备功能,比如模拟U盘、模拟串口、模拟摄像头。我习惯叫它Gadget驱动。写Gadget驱动其实就是用Linux提供的Gadget API,实现USB描述符和请求处理。如果你写了个虚拟串口驱动,插上电脑发现电脑不识别,十有八九是描述符写错了。
1.4 与驱动加载相关的核心器件
在实际的嵌入式板子上,USB OTG还依赖一些外围器件。我列个表格,你们看看每个器件在驱动加载中扮演什么角色:
| 器件/概念 | 作用 | 驱动失败的表现 |
|---|---|---|
| USB PHY | 物理层收发器,处理电气信号 | 驱动probe失败,dmesg报phy相关错误 |
| ID检测引脚 | GPIO中断触发的角色切换 | 角色无法自动切换 |
| VBUS供电 | Host模式下给外设供电 | Host模式外设无法上电 |
| DMA控制器 | 高速数据传输 | 大包传输时数据错乱 |
| 时钟/电源域 | USB模块的时钟与供电 | 驱动超时或无响应 |
我曾经因为一个PHY供电没配好,折腾了整整两天。dmesg里只看到一条“usb_phy_generic_probe: failed to get phy”的错误,反复检查设备树才发现是regulator节点名字写错了。你看看,很多时候OTG驱动加载问题其实不是驱动本身有bug,而是硬件依赖没打通。
1.5 OTG驱动加载的典型流程
我现在带你过一遍OTG驱动在系统启动时的加载流程,这样后续排查问题时你心里就有谱了:
- PHY初始化:内核先检测USB PHY的设备树节点,调用phy驱动probe,配置时钟和供电。
- UDC控制器注册:底层控制器的驱动(如dwc3、musb等)注册到USB核心子系统。
- 核心层启动:USB核心层检测到新注册的控制器,启动它。
- 角色检测:读取ID引脚电平或软件配置,确定初始角色是Host还是Device。
- Gadget驱动绑定:如果是Device模式,UDC驱动绑定Gadget功能驱动(如g_serial、g_mass_storage)。
- VBUS供电(Host):如果是Host模式,打开VBUS供电,等待外设插入。
depends-on属性强行设置依赖顺序。这一点很多新手容易忽略。
1.6 本章小结
USB OTG的驱动模型并不复杂,难的是每一层之间依赖关系的梳理,以及硬件配置的精准。记住一个原则:排查OTG驱动加载故障时,从底层硬件往上查,而不是从上往下跳。先确认PHY有电有时钟,再确认控制器能注册,最后才去翻Gadget驱动的代码。