4. USB核心数据结构 - usb_bus_type:总线注册机制、总线属性、匹配规则

在Linux USB主机驱动框架中,usb_bus_type是USB子系统向驱动核心层注册的总线类型实例。它定义了USB总线上的设备与驱动如何注册、如何匹配、以及总线级别的属性文件。理解usb_bus_type是掌握USB热插拔、设备枚举和驱动绑定机制的关键。

4.1 usb_bus_type 的定义与注册

usb_bus_type是一个全局的struct bus_type实例,定义在drivers/usb/core/driver.c中。它通过bus_register()向内核注册,成为USB设备与驱动的“总线家园”。

/* drivers/usb/core/driver.c */
struct bus_type usb_bus_type = {
    .name       = "usb",
    .match      = usb_device_match,
    .uevent     = usb_uevent,
    .suspend    = usb_suspend,
    .resume     = usb_resume,
    .dev_groups = usb_dev_groups,
    .drv_groups = usb_drv_groups,
};
EXPORT_SYMBOL_GPL(usb_bus_type);

注册时机:USB核心初始化阶段(usb_init())调用bus_register(&usb_bus_type),将USB总线挂入内核设备模型的总线链表。此后,所有USB设备(struct usb_device)和USB接口(struct usb_interface)都会作为设备添加到该总线上,所有USB驱动(struct usb_driver)也会注册到该总线上。

关键点: USB总线上的“设备”包含两种实体:USB设备本身(代表整个设备)和USB接口(代表设备中的一个功能接口)。驱动则只绑定到接口上,而非整个设备。

4.2 总线注册机制与流程

USB总线的注册涉及设备模型层的多个步骤,下表总结了核心流程:

步骤 函数/操作 说明
1 bus_register(&usb_bus_type) 向内核设备模型注册USB总线类型
2 kset_create_and_add("usb", ...) 在sysfs中创建 /sys/bus/usb/ 目录
3 添加总线属性文件 创建 drivers_autoprobe 和 drivers_probe 等控制文件
4 子系统初始化完成 等待设备/驱动注册触发匹配

注册完成后,/sys/bus/usb/ 目录下会出现 devices/、drivers/ 子目录,以及总线级别的属性文件。

调试技巧: 查看 /sys/bus/usb/ 目录内容可以确认USB总线是否成功注册。如果该目录不存在,说明USB核心初始化失败或未编译进内核。

4.3 总线属性

usb_bus_type 通过 .dev_groups 和 .drv_groups 为USB设备和驱动提供默认属性组。这些属性在sysfs中呈现为只读或可写的文件,用于获取状态或控制行为。

4.3.1 设备属性组 (usb_dev_groups)

static const struct attribute_group usb_dev_groups[] = {
    { .attrs = usb_device_attrs },
    { },
};

static struct attribute *usb_device_attrs[] = {
    &dev_attr_authorized.attr,
    &dev_attr_avoid_reset_quirk.attr,
    &dev_attr_removable.attr,
    &dev_attr_serial.attr,
    &dev_attr_speed.attr,
    &dev_attr_version.attr,
    NULL,
};

常用设备属性说明:

属性名 类型 含义
authorized 读写 控制设备是否被授权使用(用于USB设备授权管理)
speed 只读 当前连接速度(如 480M、5000M)
serial 只读 设备序列号(如果支持)
removable 只读 设备是否可移除(unknown/removable/fixed)
version 只读 USB规范版本号(如 2.00、3.20)

4.3.2 驱动属性组 (usb_drv_groups)

static const struct attribute_group usb_drv_groups[] = {
    { .attrs = usb_driver_attrs },
    { },
};

static struct attribute *usb_driver_attrs[] = {
    &dev_attr_dyndbg.attr,
    NULL,
};

驱动属性通常较少,dyndbg 用于动态调试控制。

注意: 总线级别的属性(如 drivers_autoprobe)由 bus_register() 自动创建,不属于 dev_groups 或 drv_groups。这些属性控制整个总线的自动探测行为。

4.4 匹配规则:usb_device_match

usb_device_match 是 usb_bus_type 的匹配函数,当新设备或新驱动注册到USB总线时,内核调用该函数判断驱动是否能处理该设备。其实现位于 drivers/usb/core/driver.c:

static int usb_device_match(struct device *dev, struct device_driver *drv)
{
    struct usb_interface *intf;
    struct usb_driver *usb_drv;
    struct usb_device *usb_dev;

    /* 1. 判断设备类型:是 usb_device 还是 usb_interface */
    if (is_usb_device(dev)) {
        /* 设备是 USB 设备本身,只匹配 usb_device_driver */
        usb_dev = to_usb_device(dev);
        if (drv->probe == usb_device_probe)
            return 1; /* 匹配 usb_generic_driver */
        else
            return 0;
    }

    /* 2. 设备是 USB 接口,匹配 usb_driver */
    intf = to_usb_interface(dev);
    usb_drv = to_usb_driver(drv);

    /* 3. 检查 usb_driver 的 id_table */
    if (usb_drv->id_table)
        return usb_match_device(intf->altsetting->desc.bcdDevice,
                                usb_drv->id_table);

    /* 4. 检查 usb_driver 的 match 回调(动态匹配) */
    if (usb_drv->match)
        return usb_drv->match(dev, drv);

    return 0;
}

匹配逻辑分为两条路径:

  • 设备路径(usb_device): 只匹配 usb_device_driver(即 usb_generic_driver),用于管理整个USB设备的生命周期。
  • 接口路径(usb_interface): 匹配普通的 usb_driver,通过 id_table 或 match 回调进行匹配。

4.4.1 匹配优先级

优先级 匹配方式 说明
1 设备类型判断 区分是 usb_device 还是 usb_interface
2 id_table 匹配 通过 VID/PID/class/subclass/protocol 精确匹配
3 match 回调 驱动自定义匹配逻辑(动态匹配)

4.4.2 usb_match_device 详解

usb_match_device 是实际的ID匹配函数,它遍历驱动的 id_table,比较设备描述符中的字段:

int usb_match_device(struct usb_device *dev,
                     const struct usb_device_id *id)
{
    /* 匹配 VID */
    if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
        id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
        return 0;

    /* 匹配 PID */
    if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
        id->idProduct != le16_to_cpu(dev->descriptor.idProduct))
        return 0;

    /* 匹配 bcdDevice 范围 */
    if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
        (id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI))
        if (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice) ||
            id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice))
            return 0;

    /* 匹配 class/subclass/protocol */
    if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
        id->bDeviceClass != dev->descriptor.bDeviceClass)
        return 0;
    /* ... 类似匹配 subclass 和 protocol */

    return 1; /* 全部匹配成功 */
}
匹配示例: 一个USB鼠标驱动可能使用 USB_DEVICE(0x046d, 0xc077) 精确匹配罗技特定型号,而一个HID通用驱动可能只匹配 bInterfaceClass == 0x03(HID类)。

4.5 总线匹配流程SVG图

下图展示了当USB接口设备注册时,usb_bus_type 如何通过 usb_device_match 完成驱动匹配的完整流程:

usb_bus_type 匹配流程 usb_device_match 匹配流程 新设备/驱动注册 is_usb_device(dev) ? 判断设备是 usb_device 还是 usb_interface 是 匹配 usb_device_driver 调用 probe 绑定 否 匹配 usb_driver id_table 存在? 检查 usb_drv->id_table 有 usb_match_device() 匹配成功 → 调用 probe 无 检查 match 回调 回调返回 true → probe 匹配结果 匹配成功:驱动 probe 被调用;匹配失败:继续尝试下一个驱动

4.6 总线注册与匹配的完整流程

结合设备模型层,USB总线注册与匹配的完整时序如下:

  1. USB核心初始化: 调用 bus_register(&usb_bus_type),创建 /sys/bus/usb/ 目录及属性文件。
  2. 设备注册: 当USB设备插入时,hub_port_connect_change() 创建 usb_device 并添加到总线;枚举完成后为每个接口创建 usb_interface 并添加到总线。
  3. 驱动注册: 模块加载时调用 usb_register(),将 usb_driver 注册到 usb_bus_type。
  4. 匹配触发: 内核设备模型在设备/驱动注册时自动调用 usb_device_match。
  5. 绑定: 匹配成功则调用驱动的 probe 函数,完成设备初始化。
面试考点: 为什么USB设备本身(usb_device)不直接匹配普通的usb_driver?因为USB设备是一个复合实体,其功能由接口体现。usb_generic_driver 负责管理设备级别的操作(如配置选择),而具体的功能驱动绑定到接口上。这种设计实现了设备与功能的解耦。

4.7 总结

usb_bus_type 是USB子系统与内核设备模型之间的桥梁。它通过 usb_device_match 实现了USB特有的两级匹配机制:设备级匹配(usb_device ↔ usb_device_driver)和接口级匹配(usb_interface ↔ usb_driver)。总线属性通过sysfs暴露设备状态和控制接口,为用户空间提供了设备管理的入口。理解这一结构是后续分析USB驱动绑定、电源管理和热插拔的基础。