1. USB子系统概述:主机控制器、设备端与总线拓扑结构,URB在USB通信中的核心地位

USB(Universal Serial Bus)子系统是Linux内核中最为复杂且成熟的设备驱动框架之一。它采用树形拓扑结构,由USB主机控制器、USB设备以及集线器(Hub)三层实体构成。在软件层面,URB(USB Request Block,USB请求块)是贯穿整个通信过程的核心数据载体,所有USB事务最终都通过URB进行封装与传递。

核心要点:理解USB子系统的分层架构与URB的枢纽作用,是掌握中断端点与URB协同工作的前提。

1.1 USB总线拓扑结构

USB总线采用主从式星形拓扑,主机端(Host)作为唯一的主控节点,设备端(Device)作为从节点。集线器(Hub)负责扩展端口,形成层级化的树状网络。

  • 根集线器(Root Hub):集成在主机控制器内部,是总线拓扑的起点。
  • 外部集线器(External Hub):可级联,最多支持7层深度(包括根集线器)。
  • 功能设备(Function Device):位于叶子节点,提供具体功能(如键盘、鼠标、摄像头)。
USB总线拓扑结构图 USB主机(Host) 根集线器(Root Hub) 集线器1(Hub) 集线器2(Hub) 设备1(键盘) 设备2(鼠标) 设备3(摄像头) 拓扑说明 主机控制器通过根集线器管理总线,集线器可级联扩展端口。 每个功能设备必须通过集线器与主机通信,最大层级深度为7层。

1.2 USB主机控制器

USB主机控制器(Host Controller)是硬件层面的核心,负责管理总线上的数据传输。Linux内核支持多种主机控制器标准,每种标准对应不同的驱动框架。

控制器标准 总线类型 速度支持 Linux驱动
UHCI PCI USB 1.1(全速/低速) uhci-hcd
OHCI PCI / 嵌入式 USB 1.1(全速/低速) ohci-hcd
EHCI PCI USB 2.0(高速) ehci-hcd
xHCI PCI / ACPI USB 3.x(超高速) xhci-hcd
经验提示:在嵌入式系统中,OHCI和xHCI较为常见。xHCI是唯一向后兼容USB 2.0/1.1的控制器标准,支持所有速度等级。

1.3 USB设备端与端点

每个USB设备在逻辑上由一组端点(Endpoint)构成。端点是USB通信的最小逻辑单元,每个端点具有唯一的地址和传输方向。

  • 端点0(默认控制端点):每个设备必须包含,用于设备枚举和控制传输。
  • 中断端点(Interrupt Endpoint):用于周期性、低延迟的数据传输,如键盘、鼠标、游戏手柄。
  • 批量端点(Bulk Endpoint):用于大块数据传输,无实时性要求,如U盘、打印机。
  • 等时端点(Isochronous Endpoint):用于实时流数据,允许少量丢包,如音频、视频。
USB设备端点结构图 USB设备 端点0(控制) 中断端点 批量端点 等时端点 端点特性 每个端点由端点地址(Endpoint Address)和传输方向(IN/OUT)唯一标识。 中断端点支持轮询间隔(bInterval),范围从1ms到255ms。 批量端点无固定带宽保证,等时端点预留固定带宽但无重传机制。

1.4 URB在USB通信中的核心地位

URB(USB Request Block)是Linux USB子系统中最核心的数据结构。它封装了一次USB事务所需的所有信息:目标设备、目标端点、传输方向、数据缓冲区、回调函数等。无论是内核中的USB设备驱动,还是主机控制器驱动,都通过URB进行交互。

URB的本质:URB是USB通信的“工作订单”。驱动提交URB,主机控制器执行URB,完成后通过回调通知驱动。这种异步模型使得USB子系统能够高效处理并发事务。

1.4.1 URB的生命周期

  1. 创建:驱动通过 usb_alloc_urb() 分配URB结构体。
  2. 初始化:填充URB字段,包括目标端点、传输类型、数据缓冲区、回调函数等。
  3. 提交:调用 usb_submit_urb() 将URB提交给主机控制器。
  4. 执行:主机控制器根据URB描述执行USB事务(Token、Data、Handshake阶段)。
  5. 完成:事务完成后,主机控制器调用URB的回调函数,驱动处理结果。
  6. 释放:驱动调用 usb_free_urb() 释放URB资源。
URB生命周期流程图 1. 创建URB 2. 初始化URB 3. 提交URB 4. 执行事务 5. 完成回调 6. 释放URB 关键API usb_alloc_urb() usb_submit_urb() usb_free_urb()

1.4.2 URB数据结构核心字段

URB结构体定义在 <linux/usb.h> 中,以下是最常用的字段:

struct urb {
    /* 私有字段:由USB核心使用,驱动不应直接修改 */
    struct kref kref;             // 引用计数
    void *hcpriv;                 // 主机控制器私有数据
    int unlinked;                 // 取消链接标志

    /* 公共字段:驱动需要填充 */
    struct usb_device *dev;       // 目标USB设备
    unsigned int pipe;            // 管道:包含端点地址、方向、类型
    int status;                   // 传输状态(-EINPROGRESS表示进行中)
    unsigned int transfer_flags;  // 传输标志(如URB_NO_TRANSFER_DMA_MAP)
    void *transfer_buffer;        // 数据缓冲区指针
    dma_addr_t transfer_dma;      // DMA缓冲区地址(如果使用DMA)
    int transfer_buffer_length;   // 缓冲区长度
    int actual_length;            // 实际传输的数据长度
    unsigned char *setup_packet;  // 控制传输的设置包(仅控制端点)
    int interval;                 // 中断端点的轮询间隔(单位:ms)
    int error_count;              // 等时传输的错误计数
    void *context;                // 回调上下文指针
    usb_complete_t complete;      // 完成回调函数
};
注意事项:驱动在提交URB后,不应修改URB的公共字段,直到回调函数被调用。回调函数在中断上下文中执行,必须快速返回,不能睡眠。

1.5 URB与中断端点的协同关系

中断端点(Interrupt Endpoint)是USB规范中为低延迟、周期性传输设计的端点类型。在Linux USB子系统中,中断端点的数据传输完全依赖URB机制。驱动通过提交URB到中断端点,实现数据的轮询接收或发送。

  • 轮询间隔(bInterval):中断端点在描述符中声明轮询间隔,主机控制器按此间隔调度事务。
  • URB提交:驱动调用 usb_submit_urb() 将URB提交到中断端点,主机控制器在每次轮询间隔执行一次事务。
  • 自动重提交:在回调函数中,驱动可以重新提交相同的URB,实现连续的数据流接收。
典型场景:USB鼠标驱动在探测阶段分配并提交一个中断URB,每次鼠标移动或按键时,主机控制器自动将数据填入URB缓冲区并调用回调,驱动解析数据后重新提交URB,形成循环。
URB与中断端点协同流程图 USB设备驱动 URB请求块 主机控制器 中断端点 提交URB 调度执行 轮询间隔 数据返回 完成回调 通知驱动 中断URB的典型流程:驱动提交URB → 主机控制器按间隔轮询端点 → 数据到达后回调驱动 → 驱动重新提交URB形成循环

1.6 本章小结

本章从USB子系统的硬件拓扑出发,介绍了主机控制器、设备端点以及URB在通信中的核心地位。关键知识点总结如下:

知识点 核心内容
USB拓扑结构 主机-集线器-设备的三层树形结构,最大7层深度
主机控制器 UHCI/OHCI/EHCI/xHCI四种标准,xHCI兼容所有速度
设备端点 控制、中断、批量、等时四种类型,端点0为默认控制端点
URB核心地位 封装USB事务所有信息,驱动与主机控制器通过URB交互
中断端点协同 驱动提交中断URB,主机控制器按间隔轮询,回调驱动处理数据