Linux USB Gadget驱动从入门到工程部署 - 第1节
1.1 USB 总线拓扑结构
USB(Universal Serial Bus)采用主从式星形拓扑,由一个主机(Host)控制整个总线上的所有通信。在嵌入式 Linux 系统中,主机角色通常由USB 主机控制器(如 EHCI、XHCI)承担;而 Gadget 子系统则让设备端模拟主机行为或实现特定功能。
1.1.1 拓扑层级与角色
- 主机(Host):唯一的总线主控者,负责调度所有传输事务。
- 集线器(Hub):扩展端口,实现层级级联,最多支持 7 层(含根集线器)。
- 设备(Device):功能节点,可以是 U 盘、键盘、摄像头,也可以是运行 Gadget 驱动的嵌入式设备。
💡 关键点:在 Gadget 模式下,嵌入式设备通过 UDC(USB Device Controller) 连接到主机,逻辑上它就是一个标准的 USB 设备,但内部运行着可动态配置的功能驱动。
1.1.2 拓扑结构示意图
1.2 USB 传输类型
USB 协议定义了 4 种传输类型,分别对应不同的带宽、延迟和可靠性需求。Gadget 驱动开发中,选择正确的传输类型直接影响功能性能和兼容性。
| 传输类型 | 特点 | 典型应用 | Gadget 场景 |
|---|---|---|---|
| 控制传输 | 双向、可靠、用于枚举和配置 | 设备枚举、描述符请求 | 所有 Gadget 必须支持端点 0 |
| 批量传输 | 可靠、非实时、带宽尽力而为 | U 盘、打印机 | Mass Storage、ECM/RNDIS |
| 中断传输 | 可靠、低延迟、轮询机制 | 键盘、鼠标 | HID Gadget |
| 同步传输 | 实时、无重传、固定带宽 | 摄像头、音频 | UVC、UAC Gadget |
🔧 工程提示:在 Gadget 驱动中,控制传输由 USB 核心自动处理(端点 0),开发者只需关注功能端点(批量/中断/同步)的配置。同步传输需要硬件支持 isochronous 调度,部分低端 UDC 可能不支持。
1.2.1 传输类型对比图
1.3 USB 描述符体系
USB 描述符是设备向主机报告自身能力的结构化数据。主机通过枚举过程读取描述符,从而识别设备类型、配置功能和加载对应驱动。描述符体系呈树形层级:设备 → 配置 → 接口 → 端点。
1.3.1 描述符层级结构
1.3.2 关键描述符字段解析
| 描述符类型 | 关键字段 | 说明 | Gadget 配置要点 |
|---|---|---|---|
| 设备描述符 | bcdUSB、idVendor、idProduct、bNumConfigurations | 标识设备版本、厂商、产品、配置数量 | 必须正确设置 VID/PID,否则主机无法识别 |
| 配置描述符 | bConfigurationValue、bmAttributes、bMaxPower | 配置编号、供电属性、最大电流 | 自供电/总线供电选择影响电源管理 |
| 接口描述符 | bInterfaceClass、bInterfaceSubClass、bInterfaceProtocol | 接口功能分类(HID、Mass Storage、CDC 等) | 决定主机加载哪个类驱动 |
| 端点描述符 | bEndpointAddress、bmAttributes、wMaxPacketSize、bInterval | 端点方向/编号、传输类型、最大包长、轮询间隔 | 必须匹配 UDC 硬件能力,否则枚举失败 |
⚠️ 常见陷阱:
- 端点地址冲突:同一配置下,IN 端点和 OUT 端点地址不能重复(方向不同算不同端点)。
- wMaxPacketSize 越界:USB 2.0 批量端点最大 512 字节,USB 3.0 最大 1024 字节,超出会导致枚举失败。
- bInterval 设置错误:中断端点的轮询间隔单位是毫秒(USB 2.0)或 125µs(USB 3.0),设置过小会浪费总线带宽。
1.3.3 描述符在 Gadget 驱动中的代码体现
在 Linux Gadget 驱动中,描述符通常以 静态结构体数组 的形式定义,并通过 usb_assign_descriptors() 注册到 UDC 层。以下是一个典型的 Mass Storage Gadget 描述符片段:
/* 设备描述符 */
static struct usb_device_descriptor msg_device_desc = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.idVendor = cpu_to_le16(0x0525),
.idProduct = cpu_to_le16(0xA4A5),
.iManufacturer = 1,
.iProduct = 2,
.bNumConfigurations = 1,
};
/* 配置描述符 */
static struct usb_config_descriptor msg_config_desc = {
.bLength = USB_DT_CONFIG_SIZE,
.bDescriptorType = USB_DT_CONFIG,
.wTotalLength = cpu_to_le16(USB_DT_CONFIG_SIZE +
USB_DT_INTERFACE_SIZE +
USB_DT_ENDPOINT_SIZE * 2),
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
.bMaxPower = 0x32, /* 100 mA */
};
/* 接口描述符 */
static struct usb_interface_descriptor msg_intf_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_MASS_STORAGE,
.bInterfaceSubClass = US_SC_SCSI,
.bInterfaceProtocol = US_PR_BULK,
};
/* 端点描述符:批量 IN */
static struct usb_endpoint_descriptor msg_fs_bulk_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN | 1,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(64),
};
/* 端点描述符:批量 OUT */
static struct usb_endpoint_descriptor msg_fs_bulk_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT | 2,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(64),
};
🔧 工程提示:在编写 Gadget 驱动时,建议使用 USB 描述符宏(如
USB_DT_DEVICE_SIZE)来保证结构体长度正确。对于高速/全速双模设备,需要分别提供对应的端点描述符,并通过 usb_assign_descriptors() 的 fs/hs/ss 参数区分。
1.4 本章小结
- USB 拓扑:主机-集线器-设备的星形结构,Gadget 设备作为功能节点接入。
- 四种传输类型:控制(枚举)、批量(数据)、中断(交互)、同步(流媒体),各有适用场景。
- 描述符体系:设备 → 配置 → 接口 → 端点的树形结构,是主机识别和驱动加载的依据。
- 工程要点:描述符字段必须精确匹配硬件能力,否则枚举失败;Gadget 驱动通过静态结构体定义描述符,并注册到 UDC 层。