Linux:主机USB设备驱动简析

这篇具有很好参考价值的文章主要介绍了Linux:主机USB设备驱动简析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 前言

限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。

2. 分析背景

本文基于 linux-4.14.132 内核代码进行分析。

3. USB 总线硬件拓扑

Linux:主机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); /* 匹配设备到驱动,然后加载设备驱动 */
			...
		}
	}
}

static int
hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
		int retry_counter)
{
	...

	retval = hub_port_reset(hub, port1, udev, delay, false);

	...

	if (udev->speed == USB_SPEED_WIRELESS) /* USB 无线网卡 */
		speed = "variable speed Wireless";
	else
		speed = usb_speed_string(udev->speed); /* 速度类型转字串描述 */

	/* 
	 * 打印 USB 设备的 速度、编号、驱动名。如:
	 * [    1.928574] usb 2-1: new full-speed USB device number 2 using uhci_hcd
	 * [    2.261085] usb 2-2: new full-speed USB device number 3 using uhci_hcd
	 */
	if (udev->speed < USB_SPEED_SUPER) /* 非 USB 3.0: USB 2.5, USB 2.0, USB 1.0 */
		dev_info(&udev->dev,
			"%s %s USB device number %d using %s\n", 
			(udev->config) ? "reset" : "new", speed,
			devnum, driver_name);
		
	...

	/* 获取 设备配置描述符 (USB_REQ_GET_DESCRIPTOR) 到 @udev->descriptor */
	retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);

	...
}

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 HUBROOT 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_driverusb_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() 中,函数的逻辑不复杂,不在此处展开,感兴趣的读者可以自行阅读代码研究。文章来源地址https://www.toymoban.com/news/detail-420577.html

8. 参考资料

《Universal Serial Bus Specification》

到了这里,关于Linux:主机USB设备驱动简析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA-(Win11)USB-Blaster无法在此设备上加载驱动解决

    在电脑上连接FPGA的USB-Blaster下载器的时候,可能会出现 无法在此设备上加载驱动 问题,可以通过以下方式解决: 首先这里如果驱动没有安装好,会有感叹号标志! 可以在桌面底部搜索 windows 安全中心 选择 设备安全性 单击顶部的 核心隔离 详细信息选项。 只需要关闭 内存完

    2024年02月09日
    浏览(41)
  • Linux设备驱动程序(一)——设备驱动简介

    这一部分主要是用来介绍 Linux 设备驱动程序的一些基本概念,包括:Linux 设备驱动程序的作用、内核功能的划分、设备和模块的分类以及版本编号。 设备驱动程序就像一个个的“黑盒子”,使某个特定硬件响应一个定义良好的内部编程接口,这些操作完全隐藏了设备的工作

    2024年02月05日
    浏览(88)
  • linux设备驱动(5)--设备树

    代码学习资料来源于: 第6.1讲 Linux设备树详解-什么是设备树?_哔哩哔哩_bilibili 仅用于个人学习/复习,侵联删 在linux内核3.x版本之后,linux内核开始使用设备树, 设备树描述开发板上的硬件信息 。 如上图所示,树的主干就是系统总线,IIC控制器,GPIO控制器,SPI控制器等都

    2024年02月11日
    浏览(37)
  • Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

    若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/134533533 红胖子网络科技博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中… 上一篇:《Linux驱动开发笔记(三

    2024年02月05日
    浏览(53)
  • 4、Linux驱动开发:设备-设备号&设备号注册

    🍅点击这里查看所有博文   随着自己工作的进行,接触到的技术栈也越来越多。给我一个很直观的感受就是,某一项技术/经验在刚开始接触的时候都记得很清楚。往往过了几个月都会忘记的差不多了,只有经常会用到的东西才有可能真正记下来。存在很多在特殊情况下有

    2024年02月15日
    浏览(57)
  • Linux驱动之INPUT设备驱动

    目录 一、开发环境 二、编写按键input设备的注册与事件上报         2.1 修改设备树文件                 1 添加 pinctrl 节点                 2、添加 KEY 设备节点                 3、检查 PIN 是否被其他外设使用         2.2 驱动程序编写      

    2024年02月07日
    浏览(50)
  • Linux设备驱动之SPI驱动

    Linux下SPI驱动分成两部分:主机驱动和设备驱动。 主机驱动:         主机侧SPI控制器使用 struct spi_master 描述,该结构体中包含了SPI控制器的序号(很多SoC中存在多个SPI控制器),片选数量,SPI信息传输的速率,配置SPI模式的函数指针(4种模式),实现数据传输的函数指针

    2023年04月11日
    浏览(55)
  • Linux 驱动学习笔记 ——(1)字符设备驱动

    《【正点原子】I.MX6U嵌入式Linux驱动开发指南》学习笔记 字符设备是 Linux 驱动中最基本的一类设备驱动,字节设备就是按照字节流来读写的设备,常见的字符设备包括:LED、蜂鸣器、按键、I2C 以及 SPI 等。 Linux 中一切皆文件,字符设备驱动加载成功后会在 /dev 目录下生成相

    2024年02月08日
    浏览(56)
  • Linux设备驱动——第三章字符驱动

    当对幸福的憧憬过于急切,那痛苦就在人的心灵深处升起。——加缪 本章的目的是编写一个完整的字符设备驱动。我们开发一个字符驱动是因为这一类适合大部分简单的硬件设备。字符驱动也比块驱动易于理解。本章的最终目的是编写一个模块化的字符驱动,但是我们不会在

    2024年02月08日
    浏览(83)
  • Linux驱动开发实战(一)——设备驱动模型

    在早期的Linux内核中并没有为设备驱动提供统一的设备模型。随着内核的不断扩大及系统更加复杂,编写一个驱动程序越来越困难,所以在Linux2.6内核中添加了一个统一的设备模型。这样,写设备驱动程序就稍微容易一些了。本章将对设备模型进行详细的介绍。 设备驱动模型

    2024年02月16日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包