3. Linux内核USB核心数据结构详解
USB子系统在Linux内核中通过四个核心数据结构来抽象物理设备、逻辑功能、传输端点和I/O请求。理解usb_device、usb_interface、usb_host_endpoint和urb是编写USB驱动的基础,也是分析枚举流程的关键。
学习提示: 这四个结构体分别对应USB规范中的设备层、配置/接口层、端点层和传输层。驱动开发中90%的USB操作都围绕它们展开。
3.1 usb_device —— USB设备的软件抽象
usb_device是内核中代表一个物理USB设备的核心结构体,定义在include/linux/usb.h中。每个连接到USB总线上的设备(包括Hub)都会对应一个usb_device实例。
struct usb_device {
int devnum; // 设备地址(1-127)
enum usb_device_state state; // 设备状态:ATTACHED/POWERED/DEFAULT/ADDRESS/CONFIGURED/SUSPENDED
enum usb_device_speed speed; // 速度:USB_SPEED_LOW/FULL/HIGH/SUPER/SUPER_PLUS
unsigned int route; // 拓扑路径位图(USB 3.0+)
struct usb_device *parent; // 父设备(Hub)
struct usb_device **children; // 子设备数组(Hub端口)
struct usb_device_descriptor descriptor; // 设备描述符
struct usb_host_config *config; // 当前激活的配置
struct usb_host_config *rawdescriptors; // 原始配置描述符
unsigned short bus_mA; // 总线供电电流(mA)
u8 portnum; // 父Hub上的端口号
u8 level; // 拓扑深度
struct device dev; // 内核通用设备结构
struct usb_bus *bus; // 所属USB总线
};
关键字段解读:
- devnum:枚举过程中由内核分配,范围1~127,0表示未分配地址。
- state:反映设备在枚举流程中的当前阶段,驱动可通过
usb_get_device_state()查询。 - speed:决定传输时序和带宽计算,直接影响端点最大包大小。
- config:指向当前激活的
usb_host_config,一个设备同一时刻只能有一个配置激活。
3.2 usb_interface —— 逻辑功能单元
usb_interface是USB设备中一个独立功能的抽象。一个USB配置(Configuration)可以包含多个接口(Interface),每个接口对应一个逻辑功能,例如一个复合设备可能包含键盘接口和音频接口。
struct usb_interface {
struct usb_host_interface *altsetting; // 当前激活的备用设置
struct usb_host_interface *cur_altsetting; // 当前备用设置指针
unsigned num_altsetting; // 备用设置数量
struct usb_interface_descriptor desc; // 接口描述符
struct device dev; // 内核通用设备结构
struct usb_device *usb_dev; // 所属usb_device
int minor; // USB major号(用于usbfs)
};
常见误区: 驱动绑定的是
usb_interface而非usb_device。一个设备可以有多个接口,每个接口由不同的驱动管理。例如USB摄像头通常包含视频接口和音频接口,分别由uvcvideo和snd-usb-audio驱动处理。
| 对比维度 | usb_device | usb_interface |
|---|---|---|
| 代表对象 | 物理USB设备 | 逻辑功能单元 |
| 驱动绑定 | 否(Hub驱动绑定) | 是(功能驱动绑定) |
| 数量关系 | 1个设备 | 1~多个接口 |
| 枚举时机 | 设备插入时创建 | 选择配置后创建 |
| 端点归属 | 设备级描述符 | 接口级端点集合 |
3.3 usb_host_endpoint —— 传输端点描述
usb_host_endpoint封装了USB端点的硬件属性和内核管理信息。每个端点对应设备端的一个数据缓冲区,驱动通过端点进行数据收发。
struct usb_host_endpoint {
struct usb_endpoint_descriptor desc; // 端点描述符(地址、属性、最大包大小)
struct list_head urb_list; // 挂起的URB链表
void *hcpriv; // Host Controller私有数据
struct ep_device *ep_dev; // sysfs端点设备
unsigned char *extra; // 额外描述符
int enabled; // 端点使能标志
};
| 传输类型 | 端点属性 | 典型应用 | 带宽保证 | 错误重传 |
|---|---|---|---|---|
| 控制传输 | 0x00 | 枚举、配置命令 | 10%保留 | 硬件自动重试 |
| 批量传输 | 0x02 | U盘、打印机 | 无 | 硬件自动重试 |
| 中断传输 | 0x03 | 键盘、鼠标 | 保证延迟 | 硬件自动重试 |
| 等时传输 | 0x01 | 音频、视频 | 保证带宽 | 无重传 |
3.4 urb —— USB请求块
urb(USB Request Block)是USB数据传输的基本载体,类似于网络协议栈中的sk_buff。驱动通过构造并提交URB来发起一次USB传输。
struct urb {
struct list_head urb_list; // 链表节点
struct usb_device *dev; // 目标设备
unsigned int pipe; // 管道信息(端点+方向+类型)
int status; // 传输状态
unsigned int transfer_flags; // 传输标志
void *transfer_buffer; // 数据缓冲区
dma_addr_t transfer_dma; // DMA地址
int transfer_buffer_length; // 缓冲区长度
int actual_length; // 实际传输长度
int interval; // 中断/等时传输间隔
int error_count; // 等时传输错误计数
void *context; // 完成回调上下文
usb_complete_t complete; // 完成回调函数
int number_of_packets; // 等时传输包数量
struct usb_iso_packet_descriptor iso_frame_desc[]; // 等时帧描述符
};
URB生命周期:
- 创建:
usb_alloc_urb()分配URB结构体 - 填充:设置设备、管道、缓冲区、回调函数
- 提交:
usb_submit_urb()将URB加入端点队列 - 执行:Host Controller处理URB,进行DMA传输
- 完成:回调函数被调用,驱动处理结果
- 释放:
usb_free_urb()回收资源
3.5 四者关系总结
四个核心数据结构在USB驱动栈中的层次关系如下:
| 层次 | 数据结构 | 对应USB规范 | 驱动操作 |
|---|---|---|---|
| 设备层 | usb_device | Device | usb_alloc_dev() / usb_new_device() |
| 接口层 | usb_interface | Interface | usb_set_interface() / usb_find_interface() |
| 端点层 | usb_host_endpoint | Endpoint | usb_endpoint_dir_in() / usb_endpoint_maxp() |
| 传输层 | urb | Transfer | usb_submit_urb() / usb_kill_urb() |
驱动开发实用技巧:
- 通过
interface_to_usbdev(interface)从usb_interface获取usb_device。 - 使用
usb_rcvbulkpipe()/usb_sndbulkpipe()等宏构造管道值(pipe)。 - 批量传输推荐使用
usb_bulk_msg()简化URB管理,但等时传输必须手动管理URB。 - URB回调函数在中断上下文执行,不能调用可能导致睡眠的函数。