文章目录
- 1. 前言
- 2. 分析背景
- 3. USB 总线硬件拓扑
- 4. USB 协议栈概览
- 4.1 Linux USB 子系统概览
- 4.2 USB外设(如U盘)固件基础
- 5. Linux USB 子系统初始化
- 6. Linux USB 主机控制器(HCD) 驱动
- 6.1 USB 主机控制器驱动初始化
- 6.2 USB 主机控制器设备对象注册和驱动加载
- 7. Linux USB 设备驱动加载过程
- 7.1 `HUB 类设备` 驱动加载过程
- 7.2 `非 HUB 类设备` 驱动加载过程
- 7.2.1 按 VID & PID 匹配驱动
- 7.2.2 按 interface class 匹配驱动
- 7.2.2 其它情形
- 8. 参考资料
1. 前言
限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。
2. 分析背景
本文基于 linux-4.14.132
内核代码进行分析。
3. USB 总线硬件拓扑
其中:
1. Host 就是 USB 主机控制器(HCD),就是常说的 OHCI,UHCI,EHCI,xHCI , 内核用数据结构 struct usb_hcd 来描述。
2. RootHub 是集成到 USB 主机控制器(HCD) Host 的根集线器,根集线器上的端口用于连接USB设备:Hub或Func。
3. Func 表示USB设备,如U盘等。
一个 USB 主机控制器(HCD) 上最多可连接 127 个设备(HUB或Func),每个设备在枚举完成后会分配1个地址,地址区间为1~127,特殊地址0用于在设备枚举期间和主机控制器通信。
4. USB 协议栈概览
4.1 Linux USB 子系统概览
USB 主机(如带 Linux 系统的设备) USB Slave 设备
---------------------------------------- ------------------------------
| USB设备驱动 | | |
| ^ | | |
| | | | |
| v | | |
| USB Core | | |
| ^ | | |
| | | | 如U盘、USB鼠标键盘 驱动固件 |
| v | | |
| USB主机控制器驱动(OHCI/UHCI/EHCI/xHCI) | | |
| ^ | | |
| | | | |
| v | | |
| USB主机控制器 | | |
---------------------------------------- ------------------------------
^ ^
| |
| USB 总线 |
-------------------------------------------------------
4.2 USB外设(如U盘)固件基础
一个 USB 外设,它包含一系列的描述符,这些描述符用于定义 USB 外设的功能特性和行为逻辑。描述符包含以下类型:
设备描述符:每个设备有且仅有1个设备描述符。它定义设备的 PID & VID,包含的配置描述符的个数信息。
配置描述符:设备支持的接口数等信息。
接口描述符:接口包含的端点数,支持的协议类别、子类别,协议类型等信息。
端点描述符:端点是USB通信的基本单位。端点描述符包含端点类别、缓冲大小、地址等信息。
......
由于 USB 协议栈的内容过于庞大,也不是本文的重点,本篇将不做展开,感兴趣的童鞋可以参考 USB 官网 相关资料。
5. Linux USB 子系统初始化
/* drivers/usb/core/usb.c */
static int __init usb_init(void)
{
int retval;
...
/*
* USB 调试系统初始化:
* . 创建目录 /sys/kernel/debug/usb
* . 创建文件 /sys/kernel/debug/usb/devices
*/
retval = usb_debugfs_init();
if (retval)
goto out;
...
retval = bus_register(&usb_bus_type); /* 注册 USB 总线类型 */
if (retval)
goto bus_register_failed;
/*
* USB 总线 notifier 注册:
* 在 usb device 或 interface 注册、注销时,增加、移除相关的 sysfs 目录树。
*/
retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
if (retval)
goto bus_notifier_failed;
...
retval = usb_hub_init(); /* 注册 USB HUB 驱动 */
if (retval)
goto hub_init_failed;
/* 注册 USB device 通用驱动 */
retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
if (!retval)
goto out;
...
bus_notifier_failed:
bus_unregister(&usb_bus_type);
bus_register_failed:
...
usb_debugfs_cleanup()
out:
return retval;
}
subsys_initcall(usb_init);
/* drivers/usb/core/hub.c */
static const struct usb_device_id hub_id_table[] = {
{ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
| USB_DEVICE_ID_MATCH_INT_CLASS,
.idVendor = USB_VENDOR_GENESYS_LOGIC,
.bInterfaceClass = USB_CLASS_HUB,
.driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},
{ .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
.bDeviceClass = USB_CLASS_HUB},
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
.bInterfaceClass = USB_CLASS_HUB},
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, hub_id_table);
static struct usb_driver hub_driver = { /* USB HUB 驱动 */
.name = "hub",
.probe = hub_probe,
.disconnect = hub_disconnect,
.suspend = hub_suspend,
.resume = hub_resume,
.reset_resume = hub_reset_resume,
.pre_reset = hub_pre_reset,
.post_reset = hub_post_reset,
.unlocked_ioctl = hub_ioctl,
.id_table = hub_id_table,
.supports_autosuspend = 1,
};
int usb_hub_init(void)
{
/* 注册 USB HUB 驱动 */
if (usb_register(&hub_driver) < 0) {
printk(KERN_ERR "%s: can't register hub driver\n",
usbcore_name);
return -1;
}
/*
* The workqueue needs to be freezable to avoid interfering with
* USB-PERSIST port handover. Otherwise it might see that a full-speed
* device was gone before the EHCI controller had handed its port
* over to the companion full-speed controller.
*/
hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0);
if (hub_wq)
return 0;
...
}
USB 设备通用驱动 usb_generic_driver
注册:
/* drivers/usb/core/driver.c */
int usb_register_device_driver(struct usb_device_driver *new_udriver,
struct module *owner)
{
int retval = 0;
new_udriver->drvwrap.for_devices = 1; /* 标记为 device 级别的驱动 */
new_udriver->drvwrap.driver.name = new_udriver->name;
new_udriver->drvwrap.driver.bus = &usb_bus_type;
new_udriver->drvwrap.driver.probe = usb_probe_device;
new_udriver->drvwrap.driver.remove = usb_unbind_device;
new_udriver->drvwrap.driver.owner = owner;
retval = driver_register(&new_udriver->drvwrap.driver);
...
return retval;
}
/* include/linux/usb.h */
/* use a define to avoid include chaining to get THIS_MODULE & friends */
#define usb_register(driver) \
usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
/* drivers/usb/core/driver.c */
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
const char *mod_name)
{
int retval = 0;
...
new_driver->drvwrap.for_devices = 0; /* 标记为 interface 级别驱动 */
new_driver->drvwrap.driver.name = new_driver->name;
new_driver->drvwrap.driver.bus = &usb_bus_type;
new_driver->drvwrap.driver.probe = usb_probe_interface;
new_driver->drvwrap.driver.remove = usb_unbind_interface;
new_driver->drvwrap.driver.owner = owner;
new_driver->drvwrap.driver.mod_name = mod_name;
spin_lock_init(&new_driver->dynids.lock);
INIT_LIST_HEAD(&new_driver->dynids.list);
retval = driver_register(&new_driver->drvwrap.driver);
...
return retval;
}
上面的代码主要完成了以下3项工作:
1. USB 总线类型注册
2. USB HUB 驱动注册
3. USB 设备通用驱动注册
6. Linux USB 主机控制器(HCD) 驱动
我们以支持 USB 2.0 的 EHCI USB 主机控制器
驱动为例进行阐述,而其它 OHCI,UHCI,xHCI
的主机控制器驱动,读者可自行阅读相关代码进行分析。
6.1 USB 主机控制器驱动初始化
static int __init ehci_platform_init(void)
{
/* ehci-platform: EHCI generic platform driver */
pr_info("%s: " DRIVER_DESC "\n", hcd_name);
/* 初始化 EHCI 主机控制器驱动 */
ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides);
/* 注册 EHCI 主机控制器 platform 驱动 */
return platform_driver_register(&ehci_platform_driver);
}
/* drivers/usb/host/ehci-hcd.c */
/* EHCI 主机控制器驱动 */
static const struct hc_driver ehci_hc_driver = {
.description = hcd_name,
.product_desc = "EHCI Host Controller",
...
/*
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
.hcd_priv_size = sizeof(struct ehci_hcd),
/*
* basic lifecycle operations
*/
.reset = ehci_setup,
.start = ehci_run,
.stop = ehci_stop,
.shutdown = ehci_shutdown,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ehci_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue,
.endpoint_disable = ehci_endpoint_disable,
.endpoint_reset = ehci_endpoint_reset,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
/*
* scheduling support
*/
.get_frame_number = ehci_get_frame,
/*
* root hub support
*/
.hub_status_data = ehci_hub_status_data,
.hub_control = ehci_hub_control,
.bus_suspend = ehci_bus_suspend,
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
/*
* device support
*/
.free_dev = ehci_remove_device,
};
void ehci_init_driver(struct hc_driver *drv,
const struct ehci_driver_overrides *over)
{
/* Copy the generic table to drv and then apply the overrides */
*drv = ehci_hc_driver;
if (over) {
drv->hcd_priv_size += over->extra_priv_size;
if (over->reset)
drv->reset = over->reset;
if (over->port_power)
drv->port_power = over->port_power;
}
}
USB 主机控制器(HCD)驱动
用数据结构 struct hc_driver
抽象。
6.2 USB 主机控制器设备对象注册和驱动加载
看一下全志 H3 平台 EHCI
的 DTS 配置:
ehci0: usb@01c1a000 {
compatible = "allwinner,sun8i-h3-ehci", "generic-ehci";
reg = <0x01c1a000 0x100>;
interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_BUS_EHCI0>, <&ccu CLK_BUS_OHCI0>;
resets = <&ccu RST_BUS_EHCI0>, <&ccu RST_BUS_OHCI0>;
status = "okay";
};
在系统启动期间,会为该 EHCI 主机控制器
创建一个 platform_device
对象,然后在注册 EHCI 主机控制器的 platform_driver
驱动时,触发 EHCI 主机控制器
platform 设备驱动的加载:
/* drivers/usb/host/echi-platform.c */
static const struct of_device_id vt8500_ehci_ids[] = {
...
{ .compatible = "generic-ehci", },
...
{}
};
MODULE_DEVICE_TABLE(of, vt8500_ehci_ids);
static struct platform_driver ehci_platform_driver = {
.id_table = ehci_platform_table,
.probe = ehci_platform_probe,
.remove = ehci_platform_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "ehci-platform",
.pm = &ehci_platform_pm_ops,
.of_match_table = vt8500_ehci_ids,
.acpi_match_table = ACPI_PTR(ehci_acpi_match),
}
};
/* 加载 ECHI 的 platform_driver 驱动 */
static int ehci_platform_probe(struct platform_device *dev)
{
struct usb_hcd *hcd;
...
int err, irq, phy_num, clk = 0, rst;
...
/*
* 解析中断配置:
* interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
*/
irq = platform_get_irq(dev, 0);
...
/* 创建 USB ECHI 主机控制器(HCD)对象 */
hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
dev_name(&dev->dev));
...
...
/* 注册 USB ECHI 主机控制器对象 */
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
...
platform_set_drvdata(dev, hcd);
return err;
}
/* drivers/usb/core/hcd.c */
struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
struct device *dev, const char *bus_name)
{
return __usb_create_hcd(driver, dev, dev, bus_name, NULL);
}
struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver,
struct device *sysdev, struct device *dev, const char *bus_name,
struct usb_hcd *primary_hcd)
{
struct usb_hcd *hcd;
hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);
...
...
hcd->self.controller = dev; /* EHCI 的 platform_device */
...
hcd->self.bus_name = bus_name;
init_timer(&hcd->rh_timer);
hcd->rh_timer.function = rh_timer_func;
hcd->rh_timer.data = (unsigned long) hcd;
#ifdef CONFIG_PM
INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
#endif
hcd->driver = driver; /* 设定 EHCI 主机控制器的驱动: ehci_platform_hc_driver */
hcd->speed = driver->flags & HCD_MASK;
hcd->product_desc = (driver->product_desc) ? driver->product_desc :
"USB Host Controller";
return hcd;
}
int usb_add_hcd(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags)
{
int retval;
struct usb_device *rhdev;
...
/* ehci-platform 1c1a000.usb: EHCI Host Controller */
dev_info(hcd->self.controller, "%s\n", hcd->product_desc/*ehci_hc_driver.product_desc*/);
...
retval = hcd_buffer_create(hcd);
...
retval = usb_register_bus(&hcd->self);
...
...
/* 创建 ECHI 主机控制器 ROOT HUB 设备对象 */
rhdev = usb_alloc_dev(NULL, &hcd->self, 0);
...
hcd->self.root_hub = rhdev;
...
switch (hcd->speed) {
...
case HCD_USB2:
rhdev->speed = USB_SPEED_HIGH;
break;
...
}
...
if (hcd->driver->reset) {
/* 配置 EHCI 主机控制器 */
retval = hcd->driver->reset(hcd); /* ehci_setup() */
...
}
/* initialize tasklets */
init_giveback_urb_bh(&hcd->high_prio_bh);
init_giveback_urb_bh(&hcd->low_prio_bh);
...
if (usb_hcd_is_primary_hcd(hcd) && irqnum) {
/* 注册 ECHI 主机控制器中断处理接口 ehci_irq() */
retval = usb_hcd_request_irqs(hcd, irqnum, irqflags);
...
}
hcd->state = HC_STATE_RUNNING;
/* 启动 EHCI 主机控制器 */
retval = hcd->driver->start(hcd); /* ehci_run() */
/* starting here, usbcore will pay attention to this root hub */
/* ECHI 主机控制器 ROOT HUB 设备对象注册 和 驱动加载 */
retval = register_root_hub(hcd);
...
return retval;
}
static int usb_register_bus(struct usb_bus *bus)
{
...
busnum = idr_alloc(&usb_bus_idr, bus, 1, USB_MAXBUS, GFP_KERNEL);
bus->busnum = busnum;
...
usb_notify_add_bus(bus);
/* ehci-platform 1c1a000.usb: new USB bus registered, assigned bus number 1 */
dev_info (bus->controller, "new USB bus registered, assigned bus "
"number %d\n", bus->busnum);
return 0;
}
/* 创建 USB 设备对象 (usb_device) */
struct usb_device *usb_alloc_dev(struct usb_device *parent,
struct usb_bus *bus, unsigned port1) // drivers/usb/core/usb.c
{
struct usb_device *dev;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
...
device_initialize(&dev->dev);
dev->dev.bus = &usb_bus_type;
dev->dev.type = &usb_device_type;
dev->dev.groups = usb_device_groups;
...
dev->state = USB_STATE_ATTACHED;
...
dev->portnum = port1;
dev->bus = bus;
dev->parent = parent;
...
return dev;
}
static int usb_hcd_request_irqs(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags)
{
if (hcd->driver->irq) {
...
retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
hcd->irq_descr, hcd);
...
hcd->irq = irqnum;
...
} else {
...
}
return 0;
}
来看 EHCI
主机控制器 ROOT HUB
设备对象注册和驱动加载的细节:
/* drivers/usb/core/hcd.c */
static int register_root_hub(struct usb_hcd *hcd)
{
struct device *parent_dev = hcd->self.controller;
struct usb_device *usb_dev = hcd->self.root_hub;
const int devnum = 1;
int retval;
usb_dev->devnum = devnum;
usb_dev->bus->devnum_next = devnum + 1;
...
usb_set_device_state(usb_dev, USB_STATE_ADDRESS);
usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
...
retval = usb_new_device (usb_dev);
...
return retval;
}
/* drivers/usb/core/hub.c */
int usb_new_device(struct usb_device *udev)
{
...
err = usb_enumerate_device(udev); /* Read descriptors */
...
/* Tell the world! */
/*
* usb usb1: New USB device found, idVendor=1d6b, idProduct=0002
* usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
* usb usb1: Product: EHCI Host Controller
* usb usb1: Manufacturer: Linux 4.14.111 ehci_hcd
* usb usb1: SerialNumber: 1c1a000.usb
*/
announce_device(udev);
/*
* 注册 ECHI 控制器的 ROOT HUB 设备到 driver core ,
* 这将触发 HUB 驱动 hub_driver 加载: 即触发 hub_probe() .
*/
err = device_add(&udev->dev);
...
(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
...
return err;
}
/* ECHI 主机控制器的 ROOT HUB 驱动加载 */
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_hub *hub;
...
/* We found a hub */
dev_info(&intf->dev, "USB hub found\n"); /* hub 1-0:1.0: USB hub found */
/* 创建 USB HUB 对象 */
hub = kzalloc(sizeof(*hub), GFP_KERNEL);
...
...
hub->intfdev = &intf->dev;
hub->hdev = hdev;
INIT_DELAYED_WORK(&hub->leds, led_work);
INIT_DELAYED_WORK(&hub->init_work, NULL);
INIT_WORK(&hub->events, hub_event); /* 设置用来处理 HUB 上 USB 设备枚举过程的 work */
...
...
if (hub_configure(hub, &desc->endpoint[0].desc) >= 0) /* 配置 HUB */
return 0;
...
}
static int hub_configure(struct usb_hub *hub,
struct usb_endpoint_descriptor *endpoint)
{
hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
...
hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);
...
hub->descriptor = kzalloc(sizeof(*hub->descriptor), GFP_KERNEL);
...
/*
* 读取 HUB 设备 @hdev 的 【HUB 描述符】 到 @hub->descriptor ,
* 搞清楚 HUB 上有几个 USB 连接端口。
*/
ret = get_hub_descriptor(hdev, hub->descriptor);
...
maxchild = hub->descriptor->bNbrPorts;
/*
* 如: hub 1-0:1.0: 1 port detected
* 这日志表明,hub 1-0:1.0 上,物理上只有1个接口(1 port)
*
* hub 1-0:1.0: 6 ports detected
* hub 2-0:1.0: 2 ports detected
* hub 2-2:1.0: 7 ports detected
*/
dev_info(hub_dev, "%d port%s detected\n", maxchild,
(maxchild == 1) ? "" : "s");
/* 为 HUB 上的 USB 端口创建对象 */
hub->ports = kzalloc(maxchild * sizeof(struct usb_port *), GFP_KERNEL);
...
...
hub->urb = usb_alloc_urb(0, GFP_KERNEL); /* 分配用于 HUB 中断处理的 URB 对象 */
/* 初始化用于 HUB 中断处理的 URB 对象: 中断 URB complete 处理回调设为 hub_irq() */
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
hub, endpoint->bInterval);
...
for (i = 0; i < maxchild; i++) { /* 为 HUB 上的 usb 端口创建设备对象,并注册到 driver core */
ret = usb_hub_create_port_device(hub, i + 1);
...
}
hdev->maxchild = i;
...
hub_activate(hub, HUB_INIT);
return 0;
}
static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
{
...
/* Scan all ports that need attention */
kick_hub_wq(hub);
...
}
static void kick_hub_wq(struct usb_hub *hub)
{
if (hub->disconnected || work_pending(&hub->events))
return;
...
if (queue_work(hub_wq, &hub->events)) /* 触发 hub_event() */
return;
...
}
从上面的代码看到,USB 主机控制器
用数据结构 struct usb_hcd
描述;USB HUB 设备
用数据结构 struct usb_hub
描述;USB HUB 上的端口
用数据结构 struct usb_port
描述;USB 端口上挂接的设备
用数据结构 struct usb_device
描述。
7. Linux USB 设备驱动加载过程
当设备插入到 USB 端口时,会产生中断信号,然后内核调用 HUB 的中断处理接口 hub_irq()
来处理中断信号:
/* drivers/usb/core/hub.c */
/* completion function, fires on port status changes and various faults */
static void hub_irq(struct urb *urb)
{
hub->nerrors = 0;
/* Something happened, let hub_wq figure it out */
kick_hub_wq(hub); /* 调度 work: 触发 hub_event() 进一步处理设备插入事件 */
...
}
static void hub_event(struct work_struct *work)
{
struct usb_device *hdev;
struct usb_interface *intf;
struct usb_hub *hub;
struct device *hub_dev;
u16 hubstatus;
u16 hubchange;
int i, ret;
hub = container_of(work, struct usb_hub, events);
hdev = hub->hdev;
hub_dev = hub->intfdev;
intf = to_usb_interface(hub_dev);
...
/* deal with port status changes */
for (i = 1; i <= hdev->maxchild; i++) {
struct usb_port *port_dev = hub->ports[i - 1]; /* HUB 的第 @i 个端口 */
if (test_bit(i, hub->event_bits)
|| test_bit(i, hub->change_bits)/* 端口上发生状态变化: 设备插入、拔出 */
|| test_bit(i, hub->wakeup_bits)) {
...
port_event(hub, i); /* 处理端口上的事件 */
...
}
}
...
}
static void port_event(struct usb_hub *hub, int port1)
__must_hold(&port_dev->status_lock)
{
int connect_change;
connect_change = test_bit(port1, hub->change_bits);
clear_bit(port1, hub->event_bits);
clear_bit(port1, hub->wakeup_bits);
if (hub_port_status(hub, port1, &portstatus, &portchange) < 0)
return;
...
if (connect_change)
hub_port_connect_change(hub, port1, portstatus, portchange);
}
static void hub_port_connect_change(struct usb_hub *hub, int port1,
u16 portstatus, u16 portchange)
__must_hold(&port_dev->status_lock
{
...
clear_bit(port1, hub->change_bits);
...
usb_unlock_port(port_dev);
hub_port_connect(hub, port1, portstatus, portchange);
usb_lock_port(port_dev);
}
static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
u16 portchange)
{
...
status = 0;
for (i = 0; i < SET_CONFIG_TRIES; i++) {
udev = usb_alloc_dev(hdev, hdev->bus, port1);
...
/* 复位设备, 为设备分配地址, 获取设备描述符(usb_device_descriptor) */
...
status = hub_port_init(hub, udev, port1, i);
...
/* Run it through the hoops (find a driver, etc) */
if (!status) {
...
status = usb_new_device(udev); /* 匹配设备到驱动,然后加载设备驱动 */
...
}
}
}
int usb_new_device(struct usb_device *udev)
{
...
/* 读取设备 配置描述符、字符串描述符、OTG 描述符、 quirk 配置 */
err = usb_enumerate_device(udev); /* Read descriptors */
...
...
/* Tell the world! */
announce_device(udev); /* usb usb2: New USB device found, idVendor=1d6b, idProduct=0002 */
...
err = device_add(&udev->dev); /* 添加设备到 driver core,将触发 USB 设备和驱动配对 */
...
...
return err;
}
USB 设备驱动的匹配和加载概要流程如下:
device_add()
bus_add_device(dev)
bus_probe_device(dev)
device_initial_probe(dev)
__device_attach(dev, true)
bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver)
driver_match_device(drv, dev) /* 配对 USB 设备 和 驱动 */
driver_probe_device(drv, dev) /* 加载 USB 设备驱动 */
这里我们假定 USB 设备驱动
比 USB 设备
先注册。对于 USB 设备
比其 设备驱动
先注册的情形,匹配和加载过程是类似的,这里不再赘述。
USB 设备驱动匹配加载过程,对于不同类别的设备,过程上存在着一定差异,下面取几个典型的类别设备驱动匹配加载过程进行分析。
7.1 HUB 类设备
驱动加载过程
前面在讲述 EHCI
主机控制器集成的 ROOT HUB
设备驱动加载过程中,没有描述 HUB 驱动和设备的匹配细节,我们将在本小节展开。对于非 ROOT HUB
和 ROOT HUB
设备的驱动加载过程,它们同样是通过 usb_new_device()
接口来加载驱动:
usb_new_device(udev)
device_add(&udev->dev)
...
driver_match_device(drv, dev) /* 配对 USB 设备 和 驱动 */
driver_probe_device(drv, dev) /* 加载 USB 设备驱动 */
下面就来看看 HUB 类设备驱动加载的细节 :
/* drivers/base/base.h */
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
/* 此处 drv->bus->match = usb_bus_type.match = usb_device_match() */
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
/* drivers/usb/core/driver.c */
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
/* devices and interfaces are handled separately */
/* devices and interfaces are handled separately */
if (is_usb_device(dev)) {
/* interface drivers never match devices */
if (!is_usb_device_driver(drv)) /* 不是 device 级别的驱动,表示不匹配 */
return 0;
/* TODO: Add real matching code */
return 1; /* 新插入的设备总是走这里: 先匹配到 usb_generic_driver */
} else if (is_usb_interface(dev)) {
/* interface 设备驱动匹配,在后面详述 */
...
}
return 0;
}
所有新发现的 USB 设备(HUB 也是 USB 设备),首先都会先匹配到 USB 设备通用驱动 usb_generic_driver
,然后进入该驱动的 usb_probe_device()
接口:
driver_probe_device(drv, dev)
really_probe(dev, drv)
drv->probe(dev) = usb_probe_device()
/* drivers/usb/core/driver.c */
static int usb_probe_device(struct device *dev)
{
int error = 0;
...
if (!error)
error = udriver->probe(udev); /* generic_probe() */
return error;
}
/* drivers/usb/core/generic.c */
static int generic_probe(struct usb_device *udev)
{
int err, c;
...
if (udev->authorized == 0)
...
else {
c = usb_choose_configuration(udev);
if (c >= 0) {
err = usb_set_configuration(udev, c);
...
}
}
...
return 0;
}
/* drivers/usb/core/message.c */
int usb_set_configuration(struct usb_device *dev, int configuration)
{
...
/* Allocate memory for new interfaces before doing anything else,
* so that if we run out then nothing will have changed. */
n = nintf = 0;
if (cp) {
/* 为 USB 设备 的 所有 interface 创建对象 */
nintf = cp->desc.bNumInterfaces;
new_interfaces = kmalloc(nintf * sizeof(*new_interfaces), GFP_NOIO);
...
for (; n < nintf; ++n) {
new_interfaces[n] = kzalloc(sizeof(struct usb_interface), GFP_NOIO);
...
}
...
}
...
/* 初始化 USB 设备的 所有 interface 设备 */
for (i = 0; i < nintf; ++i) {
struct usb_interface_cache *intfc;
struct usb_interface *intf;
struct usb_host_interface *alt;
cp->interface[i] = intf = new_interfaces[i];
...
alt = usb_altnum_to_altsetting(intf, 0);
...
intf->cur_altsetting = alt;
usb_enable_interface(dev, intf, true);
intf->dev.parent = &dev->dev;
intf->dev.driver = NULL;
intf->dev.bus = &usb_bus_type;
intf->dev.type = &usb_if_device_type;
...
device_initialize(&intf->dev);
...
}
kfree(new_interfaces);
/* 发送 SET_CONFIGURATION 请求 */
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
NULL, 0, USB_CTRL_SET_TIMEOUT);
...
dev->actconfig = cp;
...
usb_set_device_state(dev, USB_STATE_CONFIGURED);
...
for (i = 0; i < nintf; ++i) {
struct usb_interface *intf = cp->interface[i];
...
device_enable_async_suspend(&intf->dev);
/*
* 添加 USB interface 设备到 driver core ,
* 触发 USB interface 设备驱动匹配流程 。
*/
ret = device_add(&intf->dev);
...
create_intf_ep_devs(intf);
}
usb_autosuspend_device(dev);
return 0;
}
device_add(&udev->dev)
...
driver_match_device(drv, dev) /* 配对 USB 设备 和 驱动 */
driver_probe_device(drv, dev) /* 加载 USB 设备驱动 */
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
/* 此处 drv->bus->match = usb_bus_type.match = usb_device_match() */
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
/* devices and interfaces are handled separately */
if (is_usb_device(dev)) {
/* USB interface 设备驱动匹配不走这里 */
...
} else if (is_usb_interface(dev)) { /* USB interface 设备驱动匹配走这里 */
struct usb_interface *intf;
struct usb_driver *usb_drv;
const struct usb_device_id *id;
/* device drivers never match interfaces */
if (is_usb_device_driver(drv)) /* hub_driver 在这里成立 */
return 0;
intf = to_usb_interface(dev);
usb_drv = to_usb_driver(drv); /* hub_driver */
id = usb_match_id(intf, usb_drv->id_table);
if (id)
return 1;
id = usb_match_dynamic_id(intf, usb_drv);
if (id)
return 1;
}
return 0;
}
按从 USB 外设(如 U盘、USB 键鼠)读取到的 设备描述符
、interface 描述符
信息,匹配到 hub_driver
的 usb_device_id
表后,将加载 HUB 类设备的驱动:
driver_probe_device(drv, dev) /* 加载 USB 设备驱动 */
really_probe(dev, drv)
drv->probe(dev) = usb_probe_interface()
/* drivers/usb/core/driver.c */
/* called from driver core with dev locked */
static int usb_probe_interface(struct device *dev)
{
struct usb_driver *driver = to_usb_driver(dev->driver);
struct usb_interface *intf = to_usb_interface(dev);
struct usb_device *udev = interface_to_usbdev(intf);
const struct usb_device_id *id;
int error = -ENODEV;
...
...
error = driver->probe(intf, id); /* 进入 HUB 驱动入口: hub_probe() */
...
....
return error;
}
到此,HUB 类设备(包括 ROOT HUB
和 非 ROOT HUB
)驱动的加载过程完成。
7.2 非 HUB 类设备
驱动加载过程
7.2.1 按 VID & PID 匹配驱动
如 RealTek 的 R8152 USB 网卡,看它的驱动定义:
/* drivers/net/usb/r8152.c */
#define REALTEK_USB_DEVICE(vend, prod) \
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
USB_DEVICE_ID_MATCH_INT_CLASS, \
.idVendor = (vend), \
.idProduct = (prod), \
.bInterfaceClass = USB_CLASS_VENDOR_SPEC \
}, \
{ \
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO | \
USB_DEVICE_ID_MATCH_DEVICE, \
.idVendor = (vend), \
.idProduct = (prod), \
.bInterfaceClass = USB_CLASS_COMM, \
.bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, \
.bInterfaceProtocol = USB_CDC_PROTO_NONE
/* table of devices that work with this driver */
static const struct usb_device_id rtl8152_table[] = {
...
{REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8152)},
...
{}
};
MODULE_DEVICE_TABLE(usb, rtl8152_table);
static struct usb_driver rtl8152_driver = {
.name = MODULENAME,
.id_table = rtl8152_table,
.probe = rtl8152_probe,
.disconnect = rtl8152_disconnect,
.suspend = rtl8152_suspend,
.resume = rtl8152_resume,
.reset_resume = rtl8152_reset_resume,
.pre_reset = rtl8152_pre_reset,
.post_reset = rtl8152_post_reset,
.supports_autosuspend = 1,
.disable_hub_initiated_lpm = 1,
};
module_usb_driver(rtl8152_driver);
当 usb_device_match()
匹配过程中,读取到 R8152 网卡设备描述符的 VID & PID 信息
,匹配到驱动的 rtl8152_driver
的 ID 匹配表 rtl8152_table[]
,则加载 R8152 驱动 rtl8152_driver
,进入 rtl8152_probe()
执行。
7.2.2 按 interface class 匹配驱动
还有的 USB 设备,它们驱动的匹配表不指定 VID & PID,而是指定 interface class 信息。像这种情形,驱动的匹配是通过读取设备的 interface 描述符,然后提取其中的 interface class 信息进行匹配。
/* drivers/usb/class/usblp.c */
static const struct usb_device_id usblp_ids[] = {
{ USB_DEVICE_INFO(USB_CLASS_PRINTER, 1, 1) },
...
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, usblp_ids);
static struct usb_driver usblp_driver = {
.name = "usblp",
.probe = usblp_probe,
.disconnect = usblp_disconnect,
.suspend = usblp_suspend,
.resume = usblp_resume,
.id_table = usblp_ids,
.supports_autosuspend = 1,
};
module_usb_driver(usblp_driver);
这是一个 USB 打印机类别设备的通用驱动,当从打印机的 interface 描述符
提取到的 class 信息,与驱动匹配表的 class 信息 USB_CLASS_PRINTER
匹配时,将加载驱动,进入驱动接口 usblp_probe()
执行。
7.2.2 其它情形
还有更多其它驱动匹配的情形,细节呈现在 usb_device_match()
中,函数的逻辑不复杂,不在此处展开,感兴趣的读者可以自行阅读代码研究。
8. 参考资料
《Universal Serial Bus Specification》