2. Linux USB Gadget框架:内核中USB Gadget的架构层次、UDC驱动、Gadget Function驱动、ConfigFS接口

Linux USB Gadget框架是构建多功能复合设备的软件基石。它采用分层的模块化设计,将USB设备角色(Device Role)的实现从硬件细节中抽象出来,使得开发者能够像搭积木一样组合不同的功能(Function),最终通过ConfigFS接口在用户空间动态装配和配置复合设备。本章将深入剖析这一框架的四个核心层次。

2.1 架构层次总览

Linux USB Gadget框架自下而上分为四个清晰的层次,每一层都封装了特定的职责,并通过标准API与相邻层交互。这种分层设计极大地提升了代码的可移植性和复用性。

核心分层(自底向上):
UDC层(底层硬件抽象) → Gadget层(通用设备模型) → Function层(具体功能实现) → ConfigFS层(用户空间配置接口)
Linux USB Gadget 框架架构层次 ConfigFS 层 用户空间配置接口 (configfs) Gadget Function 层 功能驱动 (Ethernet, Mass Storage, Serial, HID...) Gadget API 层 通用设备模型与核心API (usb_gadget_probe_driver) UDC 层 底层硬件驱动 (DWC2, DWC3, Chipidea, MUSB...) 数据流向:用户空间 (ConfigFS) → 功能层 → Gadget API → UDC → USB硬件

2.2 UDC驱动:硬件与内核的桥梁

UDC(USB Device Controller)驱动是框架中最底层的软件实体,直接与SoC或外置USB控制器硬件交互。它负责处理USB协议层面的细节,包括:端点(Endpoint)管理、传输请求(Request)调度、中断处理、以及USB总线状态(如复位、挂起、恢复)的响应。

特性 描述
核心职责 初始化硬件控制器、配置端点、处理USB标准请求(如Get Descriptor、Set Address)
关键数据结构 struct usb_gadget(代表一个UDC实例)、struct usb_ep(代表端点)、struct usb_request(代表一次数据传输)
注册接口 usb_add_gadget_udc() 将UDC驱动注册到内核,并绑定到对应的平台设备
常见实现 DWC2(广泛用于STM32、Raspberry Pi)、DWC3(用于高通、TI平台)、Chipidea(用于i.MX系列)、MUSB(用于OMAP平台)
开发提示: 在调试UDC驱动时,可以通过 /sys/kernel/debug/usb/udc 目录下的调试接口查看当前UDC的状态、已注册的端点以及待处理的请求队列。

2.3 Gadget Function驱动:功能的原子单元

Gadget Function驱动是框架中实现具体USB功能的模块。每个Function驱动封装了一种USB设备类的行为,例如:Mass Storage Function(模拟U盘)、Ethernet Function(模拟USB网卡,如ECM/RNDIS)、Serial Function(模拟USB串口,如ACM)、HID Function(模拟键盘、鼠标等HID设备)。

Function驱动通过Gadget API层提供的回调函数与上层交互。一个复合设备可以同时加载多个Function,每个Function占用一组端点,并通过接口(Interface)和端点描述符向主机声明自身能力。

Gadget Function 驱动结构 USB 复合设备 (Composite Device) Mass Storage 端点: BULK IN/OUT 接口: 1 Ethernet (ECM) 端点: INT + BULK 接口: 1 Serial (ACM) 端点: INT + BULK 接口: 2 Gadget API (bind/unbind/setup)

2.4 ConfigFS接口:用户空间的动态装配

ConfigFS(Configuration File System)是Linux内核提供的一种基于文件系统的配置机制,它允许用户空间程序通过创建目录、写入文件来动态地构建和配置USB Gadget设备。这是实现多功能复合设备最灵活、最推荐的方式。

ConfigFS将Gadget的配置过程映射为文件系统操作,主要涉及以下目录结构:

/sys/kernel/config/usb_gadget/
└── g1/                          # 一个Gadget实例
    ├── UDC                      # 绑定到哪个UDC控制器(写入UDC名称)
    ├── bcdUSB                   # USB版本号
    ├── idVendor                 # 厂商ID
    ├── idProduct                # 产品ID
    ├── strings/                 # 字符串描述符
    │   └── 0x409/
    │       ├── manufacturer     # 厂商字符串
    │       ├── product          # 产品字符串
    │       └── serialnumber     # 序列号
    ├── configs/                 # 配置描述符
    │   └── c.1/                 # 配置1
    │       ├── strings/0x409/
    │       │   └── configuration # 配置描述字符串
    │       └── f1/              # 链接到Function实例
    └── functions/               # Function实例目录
        ├── mass_storage.usb0/   # Mass Storage Function
        │   └── lun.0/
        │       └── file         # 指定 backing file
        ├── ecm.usb0/            # Ethernet ECM Function
        └── acm.usb0/            # Serial ACM Function
关键操作顺序: 使用ConfigFS配置Gadget时,必须遵循严格的顺序:
1. 创建Gadget目录并设置VID/PID。
2. 创建字符串描述符。
3. 创建配置目录。
4. 创建Function实例并配置参数(如Mass Storage的backing file)。
5. 将Function符号链接到配置目录下。
6. 最后将UDC名称写入UDC文件以激活Gadget。

以下是一个典型的ConfigFS配置脚本片段,用于创建一个同时包含Mass Storage和Ethernet功能的复合设备:

#!/bin/sh
# 创建Gadget实例
mkdir /sys/kernel/config/usb_gadget/g1
cd /sys/kernel/config/usb_gadget/g1

# 设置USB标识
echo 0x1d6b > idVendor   # Linux Foundation
echo 0x0104 > idProduct  # Multifunction Composite Gadget

# 创建字符串描述符
mkdir strings/0x409
echo "MyCompany" > strings/0x409/manufacturer
echo "MultiFunction Device" > strings/0x409/product
echo "1234567890" > strings/0x409/serialnumber

# 创建配置
mkdir configs/c.1
mkdir configs/c.1/strings/0x409
echo "Config 1: MS+ECM" > configs/c.1/strings/0x409/configuration

# 创建Mass Storage Function
mkdir functions/mass_storage.usb0
echo /dev/mmcblk0p1 > functions/mass_storage.usb0/lun.0/file

# 创建Ethernet Function
mkdir functions/ecm.usb0

# 将Function链接到配置
ln -s functions/mass_storage.usb0 configs/c.1
ln -s functions/ecm.usb0 configs/c.1

# 激活Gadget(绑定到UDC)
echo ci_hdrc.0 > UDC
调试技巧: 如果Gadget无法被主机识别,首先检查 /sys/kernel/config/usb_gadget/g1/UDC 文件是否已成功写入UDC名称。其次,通过 lsusb -v 查看主机侧是否枚举出正确的厂商ID、产品ID以及配置描述符中的接口数量。

2.5 层次间协作流程

当主机通过USB线缆连接设备时,框架各层按照以下流程协同工作:

  1. 硬件检测: UDC驱动检测到VBUS插入,触发中断。
  2. Gadget绑定: UDC驱动调用Gadget API层的 usb_gadget_probe_driver(),找到已注册的Gadget驱动(由ConfigFS创建)。
  3. Function绑定: Gadget驱动遍历配置中的所有Function,调用每个Function的 bind() 回调,分配端点并准备描述符。
  4. 主机枚举: 主机发送标准请求(Get Descriptor、Set Configuration),Gadget驱动通过UDC驱动返回组合后的描述符。
  5. 数据传输: 主机选择配置后,各Function驱动通过 usb_ep_queue() 提交请求,UDC驱动负责实际的硬件传输。
Gadget框架层次间协作流程 1. VBUS插入 UDC检测中断 2. Gadget绑定 usb_gadget_probe_driver 3. Function绑定 bind()回调 4. 枚举 描述符交互 5. 数据传输 usb_ep_queue() 关键设计原则 • 每一层只通过明确定义的API与相邻层通信 • Function驱动无需关心底层UDC硬件细节 • ConfigFS将配置过程从内核空间解耦到用户空间

2.6 本章小结