一、Linux 下USB Hub热插拔处理
1.1 Linux下USB HUB的驱动的实现和分析:
USB设备是热插拔,因此在hub_probe函数中调用hub_configure函数来配置hub,在这个函数中主要是利用函数usb_alloc_urb函数来分配一个urb,利用usb_fill_int_urb来初始化这个urb结构,包括hub的中断服务程序hub_irq的,查询的周期等。
每当有设备连接到USB接口时,USB总线在查询hub状态信息的时候会触发hub的中断服务程序hub_irq, 在该函数中置位event_bits,运行工作队列。进入hub_event函数,该函数用来处理端口变化的事件。然后通过一个for循环来检测每个端口的状态信息。利用usb_port_status获取端口信息,如果发生变化就调用hub_port_connect_change函数来配置端口等。
在系统初始化的时候在usb_init函数中调用usb_hub_init函数,就进入了hub的初始化。
代码路径:drivers\usb\core\hub.c
在usb_hub_init函数中完成了注册hub驱动,并且利用函数alloc_workqueue创建一个工作队列。
1.2 初始化
这里我们主要分析khub的工作原理: 硬件层次是hub的工作,如何和host及其设备间通信及相应事件
hub的驱动定义为hub_driver,也是一个usb_driver结构体。hub驱动的匹配方式由hub_id_table定义,可以根据PID、VID、接口类型匹配。
[usb/core/hub.c ]
int usb_hub_init(void)
{
if (usb_register(&hub_driver) < 0) {
//注册usb HUB设备
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.
*/
/* 工作队列需要是可冻结的,以避免干扰usb持续的端口切换。
否则它可能会看到全速设备在EHCI控制器把端口交给全速控制器之前就消失了 */
hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0); //创建工作队列
if (hub_wq)
return 0;
}
[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_PRODUCT
| USB_DEVICE_ID_MATCH_INT_CLASS,
.idVendor = USB_VENDOR_SMSC,
.idProduct = USB_PRODUCT_USB5534B,
.bInterfaceClass = USB_CLASS_HUB,
.driver_info = HUB_QUIRK_DISABLE_AUTOSUSPEND},
{
.match_flags = USB_DEVICE_ID_MATCH_VENDOR
| USB_DEVICE_ID_MATCH_PRODUCT,
.idVendor = USB_VENDOR_CYPRESS,
.idProduct = USB_PRODUCT_CY7C65632,
.driver_info = HUB_QUIRK_DISABLE_AUTOSUSPEND},
{
.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 = {
.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,
};
hub驱动匹配和初始化过程如下图所示,主要的工作流程有:
当注册root hub时,会调用device_add注册设备。内核调用usb_hub_init函数初始化hub,此时会调用usb_register_driver注册hub驱动hub_driver。hub设备或驱动注册时都会调用usb_device_match匹配到对方,若匹配成功,则hub_probe函数被调用,进入hub的初始化流程。
hub_probe函数中做了两件事,第一件是初始化轮询hub所用的工作队列和定时器,hub_event比较重要,用于处理USB设备连接、断开、低功耗模式等,后续会介绍,第二件是配置hub,具体的工作如下:
获取hub描述符,hub有专用的描述。
初始化用于事务分割的工作队列。主要是将USB2.0事物转换成USB1.0或USB1.1。
通过请求获取hub的信息和状态。
分配hub中断传输的urb,用以查询hub状态。
填充hub中断传输的urb,回调函数为hub_irq。
给hub的每个port创建usb_port数据结构。
使能hub,主要是给port上电,检查port状态、提交之前设置的中断传输urb,用于查询hub的状态;最后调度处理hub事件的工作队列,当有事件发生时会调用hub_event处理。
然后进入hub的probe函数,主要是一些工作的初始化和hub的配置,我这里化简了
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id