2. Linux USB子系统架构:USB Core、HCD、UDC、Gadget API的层次关系与数据流路径分析
Linux USB子系统是嵌入式Linux设备实现USB通信的核心软件栈。对于USB Gadget(设备端)开发而言,理解USB Core、HCD(Host Controller Driver)、UDC(USB Device Controller)以及Gadget API之间的层次关系,是编写稳定、高效驱动程序的基础。本章将深入剖析这些组件的分层结构,并详细描述数据在USB子系统中的流动路径。
2.1 USB子系统整体层次架构
Linux USB子系统采用分层的模块化设计,从硬件到用户空间共分为四个主要层次。这种设计使得主机端(Host)和设备端(Device/Gadget)的驱动开发可以相对独立,同时共享核心的USB协议处理逻辑。
下表总结了USB子系统各层次的核心职责与关键组件:
| 层次 | 核心组件 | 主要职责 | 与Gadget开发的关系 |
|---|---|---|---|
| 用户空间 | 应用程序、libusb、configfs | 发起USB请求、配置Gadget功能、处理数据 | 通过Gadget API或configfs接口配置功能 |
| Gadget API层 | usb_gadget_driver、usb_gadget | 提供统一的设备端驱动接口,管理功能(Function)绑定 | 开发者直接交互的层,实现具体设备功能 |
| USB Core层 | usbcore.ko、协议处理、标准请求 | 实现USB协议规范,管理总线、设备和配置描述符 | 处理标准设备请求,路由数据到正确的Gadget驱动 |
| UDC层 | UDC驱动(如dwc2、dwc3、chipidea) | 直接控制USB设备控制器硬件,处理中断和DMA | 硬件抽象层,Gadget功能最终通过UDC与物理总线交互 |
| 硬件层 | USB Device Controller (UDC) IP核 | 物理层信号处理,包传输,端点管理 | 硬件平台相关,驱动需要适配具体IP核 |
2.2 层次关系与核心数据结构
下图展示了USB子系统设备侧的核心层次关系,以及各层之间的关键数据结构连接:
2.3 关键数据结构与接口
理解各层之间的交互,需要掌握几个核心数据结构。它们是连接不同层次的“信使”。
2.3.1 usb_gadget 结构体
代表一个USB设备控制器实例,是UDC层向上层暴露的抽象。包含设备速度、端点列表、电源管理等属性。
2.3.2 usb_gadget_driver 结构体
代表一个Gadget功能驱动。开发者需要实现其回调函数,如 bind()、unbind()、setup()(处理标准请求)、disconnect() 等。
2.3.3 usb_ep 与 usb_request
端点是USB通信的终点,usb_ep 结构体抽象了硬件端点。usb_request 则代表一次数据传输请求,包含数据缓冲区、长度、完成回调等。
usb_gadget_driver 和 usb_request。通过 usb_ep_queue() 将请求提交给UDC层,通过完成回调接收数据。
2.4 数据流路径分析
数据在USB子系统中的流动可以分为两个方向:下行(主机到设备) 和 上行(设备到主机)。下面以设备接收数据为例,详细分析数据路径。
2.4.1 下行数据流(主机 → 设备)
- 硬件接收: USB主机通过总线发送数据包,UDC硬件检测到包起始(SOP),将数据存入端点FIFO。
- 中断触发: UDC硬件产生接收中断,UDC驱动(如dwc3_gadget)的中断处理程序被调用。
- 请求匹配: UDC驱动从端点队列中取出一个预置的
usb_request,将硬件FIFO中的数据DMA拷贝到请求的缓冲区。 - 完成回调: UDC驱动调用
usb_request的完成回调函数(由Gadget功能驱动设置)。 - 协议处理: 如果数据是标准请求(如Set Address),USB Core层处理;如果是类特定数据,则传递给Gadget功能驱动。
- 用户空间交付: Gadget功能驱动将数据通过文件操作或网络协议栈交付给用户空间应用程序。
2.4.2 上行数据流(设备 → 主机)
- 请求提交: Gadget功能驱动填充
usb_request缓冲区,调用usb_ep_queue()将请求提交给UDC层。 - 硬件发送: UDC驱动将请求加入硬件发送队列,当主机发起IN令牌时,UDC硬件从FIFO发送数据。
- 发送完成: 数据发送完成后,UDC硬件产生完成中断,UDC驱动调用请求的完成回调。
- 状态通知: Gadget功能驱动在回调中得知数据已成功发送,可以释放或重用请求。
下图以设备接收数据为例,展示了完整的数据流路径:
2.5 关键交互流程:标准请求处理
当主机发送标准设备请求(如Get Descriptor、Set Configuration)时,USB Core层扮演了关键的路由角色。以下是典型的标准请求处理流程:
| 步骤 | 执行者 | 动作描述 |
|---|---|---|
| 1 | UDC硬件 | 接收Setup包,存入端点0的FIFO |
| 2 | UDC驱动 | 从端点0取出Setup包,调用 usb_gadget_driver->setup() 回调 |
| 3 | USB Core | 解析Setup包中的标准请求(bmRequestType, bRequest, wValue等) |
| 4 | USB Core | 如果是标准请求(如Get Descriptor),Core直接构造响应数据并通过端点0返回 |
| 5 | USB Core | 如果是类或厂商请求,Core将请求转发给Gadget功能驱动的 setup() 回调 |
| 6 | Gadget功能驱动 | 处理类/厂商请求,通过 usb_ep_queue() 返回响应数据 |
setup() 回调中,必须注意不能休眠或执行耗时操作,因为它运行在中断上下文。如果需要处理复杂逻辑,应使用工作队列(workqueue)或线程。
2.6 总结
Linux USB Gadget子系统通过清晰的层次划分,实现了硬件无关性与功能灵活性的统一。开发者主要工作在Gadget API层,通过实现 usb_gadget_driver 的回调函数来定义设备行为。理解USB Core的路由机制和UDC层的硬件抽象,有助于在遇到问题时快速定位是协议处理、功能逻辑还是硬件适配层面的故障。