4. USB设备驱动框架

USB设备驱动是Linux内核中最为典型的“主机-外设”驱动模型之一。理解usb_driver结构体、probe/disconnect回调机制以及URB(USB Request Block)的生命周期,是进行USB驱动移植与开发的基础。本章将拆解这三个核心要素,并展示它们如何协同工作。

4.1 usb_driver结构体:驱动的“身份证”

在Linux内核中,每个USB设备驱动都需要注册一个struct usb_driver实例。这个结构体是驱动与USB核心之间的契约,它定义了驱动支持的设备、初始化/退出函数以及电源管理回调。

成员 类型 说明
name const char * 驱动名称,在/sys/bus/usb/drivers/中显示
probe int (*probe)(struct usb_interface *, const struct usb_device_id *) 设备匹配成功时调用,初始化接口
disconnect void (*disconnect)(struct usb_interface *) 设备拔出或驱动卸载时调用,释放资源
id_table const struct usb_device_id * 驱动支持的设备ID列表(VID/PID/类)
suspend int (*suspend)(struct usb_interface *, pm_message_t) 系统挂起时调用(可选)
resume int (*resume)(struct usb_interface *) 系统唤醒时调用(可选)
移植要点:在设备树适配场景中,id_table通常用于匹配USB设备的VID和PID。如果设备是复合设备(多个接口),驱动需要针对每个usb_interface分别注册usb_driver。

以下是一个典型的usb_driver注册示例:

static struct usb_device_id my_usb_table[] = {
    { USB_DEVICE(0x1234, 0x5678) },  // VID=0x1234, PID=0x5678
    { }                               // 终止条目
};
MODULE_DEVICE_TABLE(usb, my_usb_table);

static int my_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    // 初始化端点、分配URB、注册字符设备等
    return 0;
}

static void my_disconnect(struct usb_interface *intf)
{
    // 释放URB、注销设备、清理数据
}

static struct usb_driver my_usb_driver = {
    .name       = "my_usb_driver",
    .probe      = my_probe,
    .disconnect = my_disconnect,
    .id_table   = my_usb_table,
};

module_usb_driver(my_usb_driver);

4.2 probe/disconnect回调:驱动的“生命周期钩子”

probe和disconnect是USB驱动中最重要的两个回调函数,它们分别在设备接入和移除时被USB核心自动调用。理解这两个函数的执行时机和职责,是避免资源泄漏和驱动崩溃的关键。

4.2.1 probe回调流程

  1. 匹配阶段:USB核心遍历已注册的usb_driver,将新插入设备的usb_interface与驱动的id_table进行匹配。
  2. 调用probe:匹配成功后,核心调用驱动的probe函数,传入usb_interface指针和匹配到的usb_device_id。
  3. 资源初始化:在probe内部,驱动需要完成以下工作:
    • 获取接口描述符,解析端点信息。
    • 分配URB并初始化。
    • 注册字符设备或输入设备等。
    • 设置接口私有数据(usb_set_intfdata)。
  4. 返回状态:成功返回0,失败返回负错误码(如-ENOMEM、-EIO)。

4.2.2 disconnect回调流程

  1. 触发条件:设备物理拔出、驱动卸载或系统进入低功耗模式。
  2. 资源清理:在disconnect内部,驱动必须:
    • 停止所有正在进行的URB传输(usb_kill_urb)。
    • 释放URB内存(usb_free_urb)。
    • 注销字符设备或输入设备。
    • 清理接口私有数据。
  3. 返回:disconnect无返回值,但必须确保所有资源被释放,否则会导致内存泄漏。
常见陷阱:在disconnect中,不能假设设备仍然处于可访问状态。调用usb_control_msg等函数可能会失败,应优先使用usb_kill_urb而非usb_unlink_urb来确保URB完全停止。
probe/disconnect 回调生命周期 设备插入 USB核心匹配 probe() 调用 解析端点 分配URB 注册设备 设备拔出 disconnect() 调用 停止URB 释放资源

4.3 URB(USB请求块)生命周期

URB是USB数据传输的核心载体。所有USB通信(控制、批量、中断、等时)都通过URB进行。理解URB的完整生命周期——从创建、填充、提交到完成回调——是编写可靠USB驱动的关键。

4.3.1 URB的创建与初始化

URB使用usb_alloc_urb分配,使用usb_fill_*_urb系列函数初始化。不同类型的传输使用不同的填充函数:

传输类型 填充函数 典型用途
控制传输 usb_fill_control_urb 设备配置、命令发送
批量传输 usb_fill_bulk_urb 大块数据(U盘、摄像头)
中断传输 usb_fill_int_urb 键盘、鼠标、HID设备
等时传输 usb_fill_isoc_urb 音频、视频流

初始化示例(批量传输):

struct urb *urb;
unsigned char *buffer;

urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
    return -ENOMEM;

buffer = kmalloc(MAX_PACKET_SIZE, GFP_KERNEL);
if (!buffer) {
    usb_free_urb(urb);
    return -ENOMEM;
}

usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, 0x81),
                  buffer, MAX_PACKET_SIZE,
                  my_complete_callback,  // 完成回调函数
                  my_private_data);      // 回调上下文

4.3.2 URB的提交与完成

URB初始化后,通过usb_submit_urb提交给USB核心。核心将URB放入主机控制器驱动(HCD)的队列中,等待硬件处理。传输完成后,核心调用驱动注册的完成回调函数。

  1. 提交阶段:usb_submit_urb(urb, GFP_ATOMIC)——在中断上下文或持有自旋锁时使用GFP_ATOMIC。
  2. 传输阶段:URB在HCD内部排队,等待USB总线空闲时进行传输。
  3. 完成回调:传输成功或失败后,核心调用complete回调。回调中检查urb->status:
    • 0:传输成功。
    • -ENOENT:URB被usb_kill_urb终止。
    • -ECONNRESET:URB被usb_unlink_urb取消。
    • -ESHUTDOWN:设备已断开。
  4. 重新提交或释放:在完成回调中,驱动可以选择重新提交URB(如周期性中断传输)或释放URB。
性能建议:对于批量传输,尽量使用usb_fill_bulk_urb并配合usb_submit_urb的GFP_KERNEL标志(在进程上下文)。避免在中断上下文中提交大量URB,以免阻塞中断处理。

4.3.3 URB的取消与释放

在设备断开或驱动卸载时,必须取消所有正在进行的URB。Linux提供了两个取消函数:

函数 行为 适用场景
usb_kill_urb 同步等待URB完全停止,直到完成回调被调用 设备断开、驱动卸载(推荐)
usb_unlink_urb 异步取消URB,立即返回,不等待完成 在中断上下文中取消URB

释放URB必须使用usb_free_urb,且必须在URB完全停止后调用。

URB 完整生命周期 usb_alloc_urb usb_fill_*_urb usb_submit_urb HCD队列 传输完成 complete 回调 检查 status 重新提交/处理数据 status == 0 错误处理/释放 status != 0 usb_kill_urb 设备断开/卸载 usb_free_urb

4.4 综合示例:一个简单的USB批量传输驱动

以下代码片段展示了usb_driver、probe、disconnect和URB生命周期的完整协作:

static struct usb_device_id my_table[] = {
    { USB_DEVICE(0x1234, 0x5678) },
    { }
};
MODULE_DEVICE_TABLE(usb, my_table);

struct my_usb_dev {
    struct usb_device *udev;
    struct usb_interface *intf;
    struct urb *rx_urb;
    unsigned char *rx_buf;
    dma_addr_t rx_dma;
};

static void rx_complete(struct urb *urb)
{
    struct my_usb_dev *dev = urb->context;
    if (urb->status == 0) {
        // 处理接收到的数据
        dev_info(&dev->intf->dev, "Received %d bytes\n", urb->actual_length);
        // 重新提交URB以持续接收
        usb_submit_urb(urb, GFP_ATOMIC);
    } else if (urb->status == -ENOENT || urb->status == -ECONNRESET) {
        // URB被正常取消,无需处理
    } else {
        dev_err(&dev->intf->dev, "URB error: %d\n", urb->status);
    }
}

static int my_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    struct usb_device *udev = interface_to_usbdev(intf);
    struct my_usb_dev *dev;
    int ret;

    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    if (!dev)
        return -ENOMEM;

    dev->udev = udev;
    dev->intf = intf;

    // 分配URB和DMA缓冲区
    dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
    if (!dev->rx_urb) {
        kfree(dev);
        return -ENOMEM;
    }

    dev->rx_buf = usb_alloc_coherent(udev, MAX_PACKET_SIZE, GFP_KERNEL, &dev->rx_dma);
    if (!dev->rx_buf) {
        usb_free_urb(dev->rx_urb);
        kfree(dev);
        return -ENOMEM;
    }

    // 初始化批量输入URB
    usb_fill_bulk_urb(dev->rx_urb, udev,
                      usb_rcvbulkpipe(udev, 0x81),
                      dev->rx_buf, MAX_PACKET_SIZE,
                      rx_complete, dev);
    dev->rx_urb->transfer_dma = dev->rx_dma;
    dev->rx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

    // 提交URB
    ret = usb_submit_urb(dev->rx_urb, GFP_KERNEL);
    if (ret) {
        usb_free_coherent(udev, MAX_PACKET_SIZE, dev->rx_buf, dev->rx_dma);
        usb_free_urb(dev->rx_urb);
        kfree(dev);
        return ret;
    }

    usb_set_intfdata(intf, dev);
    return 0;
}

static void my_disconnect(struct usb_interface *intf)
{
    struct my_usb_dev *dev = usb_get_intfdata(intf);

    usb_set_intfdata(intf, NULL);
    if (dev) {
        usb_kill_urb(dev->rx_urb);  // 同步停止URB
        usb_free_coherent(dev->udev, MAX_PACKET_SIZE, dev->rx_buf, dev->rx_dma);
        usb_free_urb(dev->rx_urb);
        kfree(dev);
    }
}

static struct usb_driver my_driver = {
    .name       = "my_bulk_driver",
    .probe      = my_probe,
    .disconnect = my_disconnect,
    .id_table   = my_table,
};

module_usb_driver(my_driver);
移植检查清单:
  • 确认id_table中的VID/PID与目标设备一致。
  • 检查端点地址(0x81)是否与设备描述符匹配。
  • 确保MAX_PACKET_SIZE不超过端点支持的最大包长。
  • 在disconnect中优先使用usb_kill_urb而非usb_unlink_urb。