USB驱动移植适配与设备树配置实战 - 第1节
1. USB协议基础
USB(Universal Serial Bus)采用分层星型拓扑结构,由主机控制器、根集线器、集线器和设备四类节点构成。整个拓扑中只有一个主机,所有通信均由主机发起,设备不能主动向主机发送数据。
关键概念:USB 2.0 支持最多 127 个设备地址(地址 0 用于枚举),设备通过集线器级联,最多允许 7 层(含根集线器)。
1.1.1 拓扑中的角色与职责
| 角色 | 职责 | 典型实现 |
|---|---|---|
| 主机(Host) | 调度总线事务、管理带宽、处理枚举 | EHCI / xHCI 控制器 |
| 根集线器(Root Hub) | 提供物理端口、检测设备连接/断开 | 集成在主机控制器内部 |
| 集线器(Hub) | 端口扩展、中继信号、电源管理 | 独立芯片或集成在设备中 |
| 设备(Device) | 实现具体功能(存储、HID、音视频等) | U盘、鼠标、摄像头 |
驱动移植要点:在设备树中,USB 主机控制器节点通常位于 SoC 总线(如 axi、ahb)下,集线器和设备由内核自动枚举,不需要在设备树中静态描述。
1.2 USB传输类型
USB 定义了四种传输类型,分别对应不同的带宽、延迟和可靠性需求。驱动开发中,选择正确的传输类型直接影响设备性能和稳定性。
1.2.1 控制传输
- 用途:设备枚举、获取描述符、设置配置、发送类特定命令。
- 特点:双向、可靠、带宽小,每个设备必须实现端点0。
- 阶段:Setup 阶段 → Data 阶段(可选)→ Status 阶段。
1.2.2 批量传输
- 用途:大块数据传输,如 U盘读写、打印机数据。
- 特点:可靠(硬件重试+CRC),无带宽保证,空闲时抢占带宽。
- 驱动注意:批量端点通常用于 BOT(Bulk-Only Transport)协议。
1.2.3 中断传输
- 用途:鼠标、键盘、游戏手柄等 HID 设备。
- 特点:保证最大延迟(轮询间隔),可靠传输。
- 驱动注意:中断传输实际上是主机轮询,并非硬件中断。
1.2.4 同步传输
- 用途:USB 摄像头、音频设备、实时数据采集。
- 特点:预留带宽、固定延迟、无重传、允许少量丢包。
- 驱动注意:同步端点需要预留带宽,驱动中需正确设置 wMaxPacketSize。
常见错误:在 USB 摄像头驱动中错误使用批量传输代替同步传输,导致帧率不稳定或丢帧严重。同步传输是实时流的唯一选择。
1.3 USB描述符体系
USB 描述符是设备向主机报告自身能力的结构化数据。主机通过枚举过程读取描述符,从而识别设备类型、配置端点、加载对应驱动。
1.3.1 核心描述符结构
| 描述符类型 | bDescriptorType | 主要字段 | 作用 |
|---|---|---|---|
| 设备描述符 | 0x01 | idVendor, idProduct, bcdUSB | 识别设备类型和协议版本 |
| 配置描述符 | 0x02 | bNumInterfaces, bMaxPower | 描述供电模式和接口数量 |
| 接口描述符 | 0x04 | bInterfaceClass, bInterfaceSubClass | 定义功能接口(如 HID、Mass Storage) |
| 端点描述符 | 0x05 | bEndpointAddress, bmAttributes, wMaxPacketSize | 定义数据传输端点属性 |
| 字符串描述符 | 0x03 | UNICODE 字符串 | 提供可读的厂商/产品名称 |
1.3.2 枚举流程中的描述符读取
- 复位:主机检测到设备连接,复位设备,分配地址 0。
- 获取设备描述符:主机发送控制传输读取前 8 字节,获取 wMaxPacketSize0。
- 设置地址:主机分配唯一地址(1~127)。
- 获取完整设备描述符:读取全部 18 字节。
- 获取配置描述符:读取配置描述符及所有接口、端点描述符。
- 选择配置:主机根据描述符信息加载驱动,发送 Set Configuration。
驱动开发关键:在 Linux 内核中,USB 核心层自动完成枚举和描述符解析。驱动开发者只需在 probe 回调中通过
struct usb_device 和 struct usb_interface 访问描述符信息,无需手动解析原始字节。
1.3.3 描述符在驱动中的访问示例
// 获取设备描述符中的 VID/PID
struct usb_device *udev = interface_to_usbdev(intf);
u16 vid = le16_to_cpu(udev->descriptor.idVendor);
u16 pid = le16_to_cpu(udev->descriptor.idProduct);
// 遍历接口的端点描述符
struct usb_host_interface *altsetting = intf->cur_altsetting;
for (int i = 0; i < altsetting->desc.bNumEndpoints; i++) {
struct usb_endpoint_descriptor *ep = &altsetting->endpoint[i].desc;
int ep_addr = ep->bEndpointAddress;
int ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
int max_pkt = le16_to_cpu(ep->wMaxPacketSize);
dev_info(&intf->dev, "EP 0x%02x, type %d, max %d\n",
ep_addr, ep_type, max_pkt);
}
调试技巧:使用
lsusb -v 可以查看完整描述符树,是驱动移植时验证设备描述符是否正确的重要工具。
1.4 本章小结
- USB 总线采用分层星型拓扑,主机唯一,设备通过集线器扩展。
- 四种传输类型各有适用场景:控制用于枚举配置,批量用于大块数据,中断用于低延迟输入,同步用于实时流。
- 描述符体系是设备自描述的基础,驱动通过描述符获取设备能力,完成匹配和初始化。
- 在设备树中,USB 主机控制器节点描述硬件寄存器、中断和时钟,设备枚举由内核自动完成。