Linux:主机USB设备驱动简析

Posted JiMoKuangXiangQu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux:主机USB设备驱动简析相关的知识,希望对你有一定的参考价值。

文章目录

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

WIN10以上平台实现UCX框架的USB虚拟总线驱动(USB主机控制端驱动)

By fanxiushu 2022-06-01 转载或引用请注明原始作者。
CSDN上前面多篇文章都讲述过USB虚拟总线驱动,也可以叫做USB主机控制端驱动。
包括linux平台和windows平台的。
linux平台的USB主机控制器驱动相对而言比较容易开发。
因为linux内核设计了一个很容易入门的总体框架,使得我们不用花太多精力去处理与linux内核衔接的问题。
以下连接阐述的就是linux平台下的USB主机控制器驱动的开发过程。
linux平台实现USB虚拟总线驱动一(原理以及开发流程)_雨中风华的博客-CSDN博客_linux虚拟usb驱动
而windows平台就很糟糕了。而要达到所有的windows通用的虚拟USB总线驱动就更加糟糕了。
尤其要支持 WINXP,WIN7,WIN8平台,因为这些平台的内核并没有提供一个总体框架来方便的开发USB虚拟总线驱动。
因此我们必须要处理各种细节,需要仔细处理与windows操作系统衔接的各种问题。
CSDN上以前的文章也阐述过通用的windows平台的USB虚拟总线驱动的开发过程,有兴趣可以自行去查阅。

而到了WIN10以上的内核中,微软似乎发现了这样的问题,终于实现了一个UCX基础框架,方便USB虚拟总线驱动的开发。
当然,也只能支持WIN10平台,因此下面的内容都是针对win10平台的阐述(当然也同样适用WIN11及以后的升级版本)

UCX是 USB Con

以上是关于Linux:主机USB设备驱动简析的主要内容,如果未能解决你的问题,请参考以下文章

WIN10以上平台实现UCX框架的USB虚拟总线驱动(USB主机控制端驱动)

WIN10以上平台实现UCX框架的USB虚拟总线驱动(USB主机控制端驱动)

linux usb 设备重新挂载

一文搞懂 USB 设备端驱动框架

怎样写linux下的USB设备驱动程序

Linux USB 驱动开发—— USB设备基础概念