USB技术问题详解汇总
目录
- 核心概念辨析
- USB Class Driver 深度解析
- Host端 Gadget 机制 (Dummy HCD)
- 专题:Function Endpoint 详解
- 专题:OTG (On-The-Go) 详解
- 常见问题 (FAQ)
- 参考资料
1. 核心概念辨析
1.1 Host vs Device
USB 采用主从通信架构(Master-Slave):
- Host (主机): 通信的发起者和调度者。负责枚举设备、分配地址、调度带宽。
- 硬件: USB Host Controller (如 xHCI, EHCI)。
- 软件: Host Controller Driver (HCD) + USB Core + Class Drivers。
- Device (设备): 被动响应 Host 的请求。
- 硬件: USB Device Controller (UDC)。
- 软件: Gadget Driver (Linux 特有术语)。
1.2 DWC (DesignWare Core)
- 定义: Synopsys 公司提供的 USB IP Core(知识产权核),广泛用于 SoC 中(如 t41 平台)。
- 特点: 支持 Dual-Role (双角色),即同一控制器可配置为 Host 模式或 Device 模式。
- 架构地位:
- DWC 是底层硬件控制器的实现,处于 USB 协议栈的最底层。
- Host 模式: DWC 向上层提供 xHCI (或 EHCI/OHCI) 标准接口,操作系统通过标准的 HCD (Host Controller Driver) 如
xhci-hcd来驱动它。 - Device 模式: DWC 作为一个 UDC (USB Device Controller),需要专门的 UDC 驱动 (如
dwc3.ko) 来控制,并向上对接 Gadget Framework。
- 代码位置:
drivers/usb/dwc3/(DWC3 是 USB 3.0 版本,向下兼容 2.0)。 - 关键函数:
dwc3_set_mode(): 切换控制器工作模式 (Host/Device/DRD)。dwc3_gadget_init(): 初始化 Device 模式,注册 UDC。dwc3_host_init(): 初始化 Host 模式,注册 xHCI 平台设备。
1.3 Gadget
- 定义: Linux 内核中用于实现 USB Device 功能的子系统。
- 命名由来: “Gadget” 意为"小配件",形象地表示连接到 Host 的外设。
- 架构:
- UDC Driver: 驱动底层的 USB Device Controller (如 DWC3)。
- Gadget Function Driver: 实现具体的 USB 功能 (如 Mass Storage, CDC, HID)。
- Composite Framework: 允许一个物理设备通过组合多个 Function 来实现复合设备。
2. USB Class Driver 深度解析
USB Class Driver 是实现特定 USB 设备类标准协议的驱动程序。有趣的是,Linux 内核中存在两套Class Driver,分别对应 Host 端和 Gadget 端。
2.1 Host Side Class Driver
- 作用: 运行在 Host 上,驱动插入的 USB 设备。
- 位置:
drivers/usb/class/(及其他子系统如drivers/media/usb/uvc/)。 - 工作流:
- USB Core 枚举设备,获取设备描述符。
- 匹配
usb_device_id表。 - 加载对应的 Class Driver (如
usb-storage)。 - 向上层子系统注册设备 (如 SCSI 设备、网络接口)。
- 常见类型:
- MSC (Mass Storage): U盘、移动硬盘。
- UVC (Video): USB 摄像头。
- HID (Human Interface): 键盘、鼠标。
2.2 Gadget Side Class Driver (Function Driver)
- 作用: 运行在 Device (Gadget) 上,模拟特定的 USB 设备功能。
- 位置:
drivers/usb/gadget/function/。 - 工作流:
- 配置 Gadget 控制器 (UDC)。
- 使用
configfs或legacy模块加载 Function Driver (如g_mass_storage)。 - 响应 Host 的标准请求,模拟设备行为。
- 常见类型:
- f_mass_storage: 模拟 U盘 (底层对接文件或块设备)。
- f_uvc: 模拟摄像头 (底层对接 V4L2 输出设备)。
- f_hid: 模拟键盘/鼠标。
2.3 对比总结
| 特性 | Host Class Driver | Gadget Function Driver |
|---|---|---|
| 运行位置 | PC / 主机端 SoC | 嵌入式设备 / 从机端 SoC |
| 核心结构体 | struct usb_driver | struct usb_function |
| 数据流向 | 发起 URB (USB Request Block) | 处理 URB 请求 |
| 主要职责 | 控制设备,供应用层使用 | 模拟设备,供 Host 识别 |
3. Host端 Gadget 机制 (Dummy HCD)
用户常问:“Host端是否存在 Gadget?” 答案是:物理上不存在,但软件上可以模拟。
3.1 物理限制
- 标准 USB 接口有明确的主从之分 (Type-A vs Type-B/Micro-B)。
- Host 控制器硬件无法作为 Device 工作 (除非是 Dual-Role 控制器切换到了 Device 模式)。
3.2 Dummy HCD (虚拟测试驱动)
- 定义:
drivers/usb/gadget/udc/dummy_hcd.c是一个特殊的驱动。 - 原理:
- 它在内存中模拟了一个 Host Controller (HCD) 和一个 Device Controller (UDC)。
- 它将 HCD 发出的 URB 直接转发给 UDC,将 UDC 的响应直接回传给 HCD。
- 不需要物理 USB 线缆。
- 用途:
- 开发调试: 在 PC 上开发 Gadget 驱动,无需真实的开发板。
- 自动化测试: 测试 USB 协议栈的上层逻辑。
- 局限性: 无法测试物理层的电气特性,且时序与真实硬件不同。
4. 专题:Function Endpoint 详解
在 Gadget 开发中,“Function Endpoint” 是核心概念。它是 Gadget Function Driver 与底层 UDC 交互的桥梁。
4.1 什么是 Function Endpoint?
- Endpoint (端点): USB 通信的基本单元 (除 EP0 外)。
- Function Endpoint: 特指分配给某个特定 Function (如 CDC 串口) 使用的端点。
- 动态分配: 在 Linux Gadget 框架中,端点通常不是硬编码的,而是由 Function Driver 在
bind阶段向 UDC 申请的。
4.2 技术实现原理
关键数据结构
struct usb_ep: 代表一个物理端点。struct usb_function: 代表一个功能 (包含一组端点)。
自动配置流程 (Autoconfig)
Linux 提供了usb_ep_autoconfig()函数,用于根据描述符自动从 UDC 查找合适的端点。
代码示例 (基于drivers/usb/gadget/function/f_acm.c)
/* drivers/usb/gadget/function/f_acm.c *//* 1. 定义期望的端点描述符 */staticstructusb_endpoint_descriptoracm_fs_in_desc={.bLength=USB_DT_ENDPOINT_SIZE,.bDescriptorType=USB_DT_ENDPOINT,.bEndpointAddress=USB_DIR_IN,/* 期望方向:IN */.bmAttributes=USB_ENDPOINT_XFER_BULK,/* 期望类型:Bulk */};staticintacm_bind(structusb_configuration*c,structusb_function*f){structusb_composite_dev*cdev=c->cdev;structf_acm*acm=func_to_acm(f);structusb_ep*ep;/* ... *//* 2. 请求分配一个 Bulk IN 端点 *//* UDC 驱动会遍历所有可用端点,找到匹配的一个返回,并更新描述符中的地址 */ep=usb_ep_autoconfig(cdev->gadget,&acm_fs_in_desc);if(!ep)gotofail;acm->port.in=ep;/* 保存分配到的端点 *//* 3. 请求分配一个 Bulk OUT 端点 */ep=usb_ep_autoconfig(cdev->gadget,&acm_fs_out_desc);if(!ep)gotofail;acm->port.out=ep;/* ... */}4.3 性能优化建议
- Burst Mode: USB 3.0+ 支持 Burst 传输,确保 Endpoint 描述符中
bMaxBurst设置正确。 - Request 预分配: 不要为每个包动态分配
usb_request,应使用预分配的请求池。 - DMA 对齐: 确保数据缓冲区地址和长度满足 DMA 对齐要求 (通常是 Cache Line 对齐)。
5. 专题:OTG (On-The-Go) 详解
OTG 是 USB 2.0 引入的一项标准,允许设备在 Host 和 Device 角色之间动态切换。这对于移动设备(如手机、平板)至关重要。
5.1 核心概念
- ID Pin: USB Micro-AB 或 Type-C 接口中的关键引脚。
- 接地 (GND): 设备作为 Host (A-Device)。
- 悬空 (Float): 设备作为 Device (B-Device)。
- 角色定义:
- A-Device: 供电方 (VBUS Source),默认为 Host。
- B-Device: 受电方 (VBUS Sink),默认为 Device。
- 协议:
- SRP (Session Request Protocol): 允许 B-Device 请求 A-Device 开启 VBUS 供电,从而开始一个新的会话。
- HNP (Host Negotiation Protocol): 允许 A-Device 和 B-Device 在不交换物理线缆的情况下互换 Host/Device 角色。
5.2 Linux OTG 实现
在 Linux 内核中,OTG 的支持通常依赖于 PHY 驱动和控制器驱动的配合。
关键状态机 (OTG State Machine)
内核通过struct usb_otg和enum usb_otg_state维护 OTG 状态。
OTG_STATE_A_IDLE: A-Device 空闲。OTG_STATE_A_HOST: A-Device 作为 Host 工作中。OTG_STATE_B_PERIPHERAL: B-Device 作为 Device 工作中。OTG_STATE_B_HOST: B-Device 通过 HNP 切换为 Host。
硬件检测与切换 (Extcon)
现代内核常用extcon(External Connector) 子系统来处理 ID Pin 和 VBUS 的变化。
- PHY 驱动检测到 ID Pin 变化。
- Extcon通知 USB 控制器驱动 (如 DWC3)。
- DWC3调用
dwc3_set_mode()切换 Host/Device 逻辑。
5.3 OTG 工作流程图
5.4 关键疑点:为什么有了 VBUS 还需要 ID Pin?
这是初学者常问的问题:“既然 VBUS 可以表示有设备插入,为什么还需要一个额外的 ID 引脚来区分 Host/Device?”
核心原因在于“初始角色确定的二义性”和“无源启动需求”。
物理接口的统一化带来角色混淆
- 标准 USB: 通过物理形状区分。Type-A 插座永远是 Host(供电),Type-B 插座永远是 Device(受电)。物理连接决定了角色,不会插错。
- OTG (Micro-AB/Type-C): 接口形状完全一样。如果没有 ID Pin,当两个设备通过线缆连接时,谁该做 Host?谁该供电?这是一个“先有鸡还是先有蛋”的死锁。
ID Pin 模拟了物理接口的差异
- ID Pin 的作用就是电子化地模拟“物理形状”。
- ID 接地 (GND): 告诉控制器“我现在插的是 Micro-A 插头”,所以我必须扮演Host并开启VBUS 供电。
- ID 悬空 (Float): 告诉控制器“我现在插的是 Micro-B 插头”,所以我必须扮演Device并等待VBUS 上电。
VBUS 的局限性
- VBUS 只能表示“当前线上有电”。
- 在连接建立的瞬间(Cold Plug),线上是没电的。如果依靠 VBUS 探测,双方都处于“没电所以不工作”的状态,永远无法建立连接。
- 必须有一个机制在 VBUS 上电之前就决定谁来负责上电,这个机制就是 ID Pin。
总结: ID Pin 决定了初始角色 (Default Role)和供电方向,而 VBUS 用于会话检测 (Session Detect)。
6. 常见问题 (FAQ)
Q1: HOST 和 Device 都需要 DWC 吗?为什么 Device 有了 UDC 还需要 DWC?
- DWC 是硬件,UDC 是角色:DWC (DesignWare Core) 是 Synopsys 公司的 USB IP 核型号(硬件),而 UDC (USB Device Controller) 是 USB 协议中定义的一个角色(逻辑功能)。
- 关系:
- 当 DWC 硬件工作在Device 模式时,它就是一个UDC。
- 此时需要 Linux 内核中的
dwc3驱动来控制这个硬件,使其扮演 UDC 的角色,并向上传递数据给 Gadget 驱动。 - 类比: 就像 “Intel i9” (DWC) 是 CPU 硬件,当它运行 Web 服务时,它扮演 “Server” (UDC) 的角色。你不能说 “既然有了 Server 还需要 i9 吗?”,因为 i9 是实现 Server 功能的物理基础。
- Host 端: 同理,当 DWC 工作在 Host 模式时,它就是一个 xHCI Host Controller。
Q2: 为什么我的 Gadget 设备插入电脑没反应?
- 检查 UDC 驱动是否加载 (
lsmod | grep dwc3)。 - 检查 Gadget Function 是否绑定 (
ls /sys/class/udc). - 检查 DWC3 是否处于 Device 模式 (
cat /sys/kernel/debug/usb/dwc3.0/mode).
Q3: 可以同时运行 Host 和 Gadget 吗?
- 对于单口控制器:不能。同一时间只能是一种角色。
- 对于多口控制器或多个控制器:可以。例如 Port 0 作 Host 接鼠标,Port 1 作 Gadget 接 PC。
Q4:configfs和legacy有什么区别?
- Legacy: 旧方式,模块加载时即固定功能 (如
g_ether.ko),灵活性差。 - Configfs: 新方式 (
libcomposite),用户态通过文件系统动态创建 Function、配置参数并绑定到 UDC,极其灵活。推荐使用。
Q5: OTG 功能实现中有了 VBUS 为什么还需要一个 ID Pin 呢?
- VBUS只能反映"是否有电",无法在双方都没电的初始时刻区分谁该供电。
- ID Pin是物理层面的"角色标签"。
- 接地 (GND) = “我是 Host,我负责供电”。
- 悬空 (Float) = “我是 Device,我等待供电”。
- 如果没有 ID Pin,两个 OTG 设备连在一起时,都会因为等待对方供电而陷入死锁。
7. 参考资料
- Kernel Docs:
Documentation/usb/gadget_configfs.txt - Source Code:
drivers/usb/gadget/function/f_acm.c(CDC ACM 实现)drivers/usb/dwc3/core.c(DWC3 核心)drivers/usb/gadget/udc/dummy_hcd.c(虚拟驱动)