2. USB核心数据结构:usb_device、usb_interface、usb_host_endpoint 结构体详解

在USB主机侧驱动开发中,理解内核如何描述USB设备是编写正确URB(USB Request Block)的前提。Linux USB核心使用三个层级分明的结构体来抽象物理设备:usb_device(设备级)、usb_interface(接口级)和usb_host_endpoint(端点级)。本章将逐一拆解这三个结构体的核心字段、层级关系以及它们在批量传输配置中的实际作用。

学习提示: 这三个结构体在USB子系统中呈“设备→配置→接口→端点”的树形嵌套关系。驱动开发中最常打交道的是 usb_interface 和 usb_host_endpoint,因为批量传输URB的提交直接依赖于端点描述符的解析。

2.1 结构体层级关系总览

在深入每个结构体之前,先通过一张层级关系图建立整体认知。USB设备在枚举完成后,内核会构建如下数据结构链:

USB 核心数据结构层级关系 usb_device config (struct usb_host_config *) interface[] (struct usb_interface) endpoint[] (struct usb_host_endpoint) 说明:一个 usb_device 包含多个配置(config),每个配置包含多个接口(interface), 每个接口包含多个端点(endpoint)。驱动通常绑定到接口,并通过端点进行数据传输。

2.2 usb_device:设备级抽象

usb_device 是USB子系统中最顶层的结构体,代表一个物理USB设备。它在设备插入时由USB核心分配,并在设备断开时释放。驱动开发中通常通过 interface_to_usbdev() 宏从接口指针获取设备指针。

字段名 类型 说明
devnum int 设备地址(1~127),由USB核心在枚举时分配
speed enum usb_device_speed 设备速度:USB_SPEED_LOW/FULL/HIGH/SUPER
descriptor struct usb_device_descriptor 设备描述符,包含VID、PID、bcdUSB等关键信息
config struct usb_host_config * 当前激活的配置描述符指针
actconfig struct usb_host_config * 与config相同,指向当前激活配置
bus struct usb_bus * 所属USB总线(主机控制器)
ep0 struct usb_host_endpoint 默认控制端点(端点0),用于控制传输
state enum usb_device_state 设备状态:ATTACHED/POWERED/DEFAULT/ADDRESS/CONFIGURED/SUSPENDED
批量传输关注点: 在批量传输场景中,usb_device 的 speed 字段决定了批量传输的最大包长度(FS: 64字节,HS: 512字节,SS: 1024字节)。ep0 虽然不用于批量传输,但驱动常通过它发送类特定请求来初始化批量端点。

2.3 usb_interface:驱动绑定的核心单元

usb_interface 是USB驱动开发中最常接触的结构体。USB设备的一个接口对应一个逻辑功能(如键盘、鼠标、音频流),驱动通过 usb_driver 的 probe() 回调获得接口指针。批量传输端点通常属于某个接口的备用设置(alternate setting)。

字段名 类型 说明
altsetting struct usb_host_interface * 接口的所有备用设置数组,altsetting[0]为默认设置
cur_altsetting int 当前激活的备用设置索引
num_altsetting int 备用设置的总数量
dev struct device 内核通用设备结构,用于驱动模型绑定
usb_dev struct usb_device * 指向所属的usb_device,通过interface_to_usbdev()获取
minor int 接口的次设备号(用于USB设备节点)

获取当前接口的端点信息,通常通过 interface->cur_altsetting->endpoint 数组访问。下面的代码片段展示了如何在probe函数中遍历批量端点:

static int my_usb_probe(struct usb_interface *interface,
                        const struct usb_device_id *id)
{
    struct usb_host_interface *iface_desc;
    struct usb_endpoint_descriptor *endpoint;
    int i;

    // 获取当前激活的备用设置
    iface_desc = interface->cur_altsetting;

    // 遍历该接口下的所有端点
    for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
        endpoint = &iface_desc->endpoint[i].desc;

        // 检查是否为批量传输端点
        if (usb_endpoint_is_bulk_in(endpoint)) {
            dev_info(&interface->dev, "Found bulk IN endpoint\n");
            // 保存端点地址和最大包长度
            bulk_in_ep = usb_endpoint_num(endpoint);
            bulk_in_maxp = usb_endpoint_maxp(endpoint);
        }
        if (usb_endpoint_is_bulk_out(endpoint)) {
            dev_info(&interface->dev, "Found bulk OUT endpoint\n");
            bulk_out_ep = usb_endpoint_num(endpoint);
            bulk_out_maxp = usb_endpoint_maxp(endpoint);
        }
    }
    return 0;
}
常见陷阱: 不要直接使用 interface->altsetting[0] 来获取端点信息。如果设备当前激活的是备用设置1(如UVC摄像头的高带宽模式),altsetting[0] 可能不包含任何批量端点。始终使用 interface->cur_altsetting 来获取当前激活的接口描述符。

2.4 usb_host_endpoint:URB提交的目标

usb_host_endpoint 是USB端点在内核中的完整表示,包含了描述符信息、URB队列和调度状态。批量传输URB最终被提交到某个端点,由主机控制器调度执行。

字段名 类型 说明
desc struct usb_endpoint_descriptor 端点描述符,包含bEndpointAddress、bmAttributes、wMaxPacketSize
urb_list struct list_head 挂载到该端点的URB链表,由USB核心管理
hcpriv void * 主机控制器私有数据,驱动不应直接访问
ep_dev struct device 端点对应的设备节点(sysfs接口)
extra unsigned char * 端点描述符的额外数据(如类特定扩展描述符)
extralen int 额外数据的长度

端点描述符 usb_endpoint_descriptor 是硬件层面的描述,其关键字段解析如下:

字段 位域/值 说明
bEndpointAddress Bit 0-3: 端点号
Bit 7: 方向(0=OUT, 1=IN)
例如 0x81 表示端点1 IN;0x02 表示端点2 OUT
bmAttributes Bit 0-1: 传输类型
00=控制, 01=等时, 10=批量, 11=中断
批量传输必须为 0x02
wMaxPacketSize Bit 0-10: 最大包大小
Bit 11-12: 额外事务数(HS等时)
批量传输中FS=64, HS=512, SS=1024
bInterval 1-255 批量传输通常忽略此字段,中断/等时传输使用

内核提供了便捷的宏来解析端点描述符,避免手动位操作:

// 判断端点类型和方向
usb_endpoint_is_bulk_in(epd)    // 是否为批量IN端点
usb_endpoint_is_bulk_out(epd)   // 是否为批量OUT端点
usb_endpoint_is_int_in(epd)     // 是否为中断IN端点
usb_endpoint_xfer_bulk(epd)     // 是否为批量传输(不分方向)

// 获取端点号和最大包长度
usb_endpoint_num(epd)           // 返回端点号(0-15)
usb_endpoint_maxp(epd)          // 返回wMaxPacketSize值
usb_endpoint_type(epd)          // 返回传输类型枚举值

2.5 批量传输端点配置实战流程

结合上述三个结构体,配置批量传输端点的完整流程如下:

  1. 获取接口指针: 在驱动的 probe() 回调中,参数 struct usb_interface *interface 即为当前接口。
  2. 获取当前备用设置: 通过 interface->cur_altsetting 获取当前激活的接口描述符。
  3. 遍历端点数组: 从 cur_altsetting->endpoint 数组(大小为 desc.bNumEndpoints)中逐个检查。
  4. 识别批量端点: 使用 usb_endpoint_is_bulk_in/out() 宏判断端点类型。
  5. 保存端点信息: 记录端点地址(用于URB的 pipe 构造)和最大包长度(用于缓冲区对齐)。
  6. 构造URB并提交: 使用 usb_fill_bulk_urb() 填充URB,指定目标端点pipe。
关键公式: 构造批量传输pipe时,使用 usb_rcvbulkpipe(udev, ep_num) 或 usb_sndbulkpipe(udev, ep_num),其中 udev 通过 interface_to_usbdev(interface) 获取。

下面的SVG图展示了从设备枚举到URB提交的完整数据流:

批量传输端点配置数据流 1. 设备枚举 2. 解析描述符 3. probe()回调 4. 遍历端点 5. 保存配置 6. 提交URB 数据流说明:USB核心枚举设备 → 解析usb_device/interface/endpoint → 驱动probe获取接口指针 → 遍历cur_altsetting->endpoint数组 → 保存批量端点地址和最大包长 → 构造URB并提交 关键数据结构 usb_device → usb_interface → usb_host_endpoint 常用宏/函数 interface_to_usbdev() usb_endpoint_is_bulk_in/out() URB构造 usb_fill_bulk_urb() usb_rcv/sndbulkpipe()

2.6 结构体关系总结

下表总结了三个核心结构体在批量传输配置中的角色分工:

结构体 批量传输中的角色 驱动开发者操作
usb_device 提供设备速度、地址、默认控制端点 通过 interface_to_usbdev() 获取,用于构造pipe
usb_interface 提供当前备用设置和端点数组 在probe中遍历 cur_altsetting->endpoint[]
usb_host_endpoint 包含端点描述符和URB队列 解析 desc 字段获取地址和最大包长
最佳实践: 在驱动初始化阶段,将批量端点的 bEndpointAddress 和 wMaxPacketSize 缓存到驱动私有结构体中。这样在后续URB提交时无需再次遍历端点数组,提升性能并减少代码耦合。