Linux USB Gadget设备开发工程 - 第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 拓扑结构示意图

USB 总线拓扑结构(主机-集线器-设备) USB 主机(Host) 根集线器(Root Hub) 集线器 1(Hub) 集线器 2(Hub) 集线器 3(Hub) U 盘(Bulk) 键盘(Int) 摄像头(Iso) Gadget 设备 鼠标(Int) 说明 USB 2.0 最多支持 7 层拓扑(含根集线器);每个集线器最多连接 4 个下游端口。

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 传输类型对比图

USB 四种传输类型对比 控制传输 双向 · 可靠 端点 0 强制使用 枚举/配置/命令 批量传输 可靠 · 非实时 带宽尽力而为 U盘/网络/打印 中断传输 可靠 · 低延迟 轮询机制 键盘/鼠标/游戏杆 同步传输 实时 · 无重传 固定带宽 摄像头/音频 传输类型选择建议 • 控制传输:所有 USB 设备必须支持,用于枚举和标准请求。 • 批量传输:大数据量、可靠性要求高,但无实时性保证。 • 中断传输:小数据量、周期性查询,适合人机交互设备。 • 同步传输:实时流数据,允许少量丢包,不可重传。

1.3 USB 描述符体系

USB 描述符是设备向主机报告自身能力的结构化数据。主机通过枚举过程读取描述符,从而识别设备类型、配置功能和加载对应驱动。描述符体系呈树形层级:设备 → 配置 → 接口 → 端点。

1.3.1 描述符层级结构

USB 描述符树形层级结构 设备描述符(Device Descriptor) 配置描述符(Configuration Descriptor) 接口描述符(Interface 0) 接口描述符(Interface 1) 端点描述符(EP 1 IN) 端点描述符(EP 2 OUT) 端点描述符(EP 3 IN) 端点描述符(EP 4 OUT) 描述符枚举顺序 主机先读取设备描述符(18 字节),再读取配置描述符(9 字节),然后递归读取接口和端点描述符。 每个描述符的 bLength 字段指明自身长度,主机据此解析后续数据。

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 层。