Linux:主机USB设备驱动简析
Posted JiMoKuangXiangQu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux:主机USB设备驱动简析相关的知识,希望对你有一定的参考价值。
文章目录
- 1. 前言
- 2. 分析背景
- 3. USB 总线硬件拓扑
- 4. USB 协议栈概览
- 5. Linux USB 子系统初始化
- 6. Linux USB 主机控制器(HCD) 驱动
- 7. Linux USB 设备驱动加载过程
- 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(sizeofWIN10以上平台实现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主机控制端驱动)