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)也会注册到该总线上。
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_DEVICE(0x046d, 0xc077) 精确匹配罗技特定型号,而一个HID通用驱动可能只匹配 bInterfaceClass == 0x03(HID类)。
4.5 总线匹配流程SVG图
下图展示了当USB接口设备注册时,usb_bus_type 如何通过 usb_device_match 完成驱动匹配的完整流程:
4.6 总线注册与匹配的完整流程
结合设备模型层,USB总线注册与匹配的完整时序如下:
- USB核心初始化: 调用
bus_register(&usb_bus_type),创建/sys/bus/usb/目录及属性文件。 - 设备注册: 当USB设备插入时,
hub_port_connect_change()创建usb_device并添加到总线;枚举完成后为每个接口创建usb_interface并添加到总线。 - 驱动注册: 模块加载时调用
usb_register(),将usb_driver注册到usb_bus_type。 - 匹配触发: 内核设备模型在设备/驱动注册时自动调用
usb_device_match。 - 绑定: 匹配成功则调用驱动的
probe函数,完成设备初始化。
4.7 总结
usb_bus_type 是USB子系统与内核设备模型之间的桥梁。它通过 usb_device_match 实现了USB特有的两级匹配机制:设备级匹配(usb_device ↔ usb_device_driver)和接口级匹配(usb_interface ↔ usb_driver)。总线属性通过sysfs暴露设备状态和控制接口,为用户空间提供了设备管理的入口。理解这一结构是后续分析USB驱动绑定、电源管理和热插拔的基础。