hub_event()的前世今生
缘起 usb_hub_init()
主要是注册hub驱动和创建工作队列
注册hub驱动:
if (usb_register(&hub_driver) < 0) {
printk(KERN_ERR "%s: can't register hub driver\n",
usbcore_name);
return -1;
}
这里引入usb_driver 结构体,先看看hub是如何初始化的
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工作队列
hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0);
if (hub_wq)
return 0;
probe函数促使其修成正果
hub_probe函数:简单来说如果是支持的hub,则对其进行分配内存、初始化和初始化。对于hub_event部分,就一句,但对以后有着深远的影响,因为这里是hub接入其他设备的入口。
INIT_WORK(&hub->events, hub_event);
而hub_irq()函数则是决定是否能进入到该入口。且看下面一 一道来
if (hub_configure(hub, &desc->endpoint[0].desc) >= 0)
return 0;
配置整个hub的同时,也在向host注册一些信息,比如,发生中断传输就找他——hub_irq()
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
hub, endpoint->bInterval);
Hub 也疯狂
经过以上的注册、porb、config,hub算是能正常的工作了。如果一个设备接入hub,又该如何run起来呢?刚才也说明了irq是入口,看看为啥这么认为
参数是urb,中断哪里的urb?肯定是host过来的,将中断信息保存到urb的buffer中传递过来,irq整合数据后唤醒hub workqueue
for (i = 0; i < urb->actual_length; ++i)
bits |= ((unsigned long) ((*hub->buffer)[i]))
<< (i*8);
hub->event_bits[0] = bits;
/* Something happened, let hub_wq figure it out */
kick_hub_wq(hub);
kick_hub_wq唤醒hub_event
if (queue_work(hub_wq, &hub->events))
return;
踏平坎坷成大道
斗罢艰险又出发,又出发!hub_event() 函数,首先是一系列的判断,作为运行在各大平台的操作系统,必须严谨,先判断是否是自身hub引发的中断,是不是电压不稳引起的异常,当然还判断该hub是否是正常工作的。如果都不是其他因素引起的异常,那就遍历hub端口,找到发生状态改变的端口,进行处理。在hub_irq中就将状态都装入event_bits中,所以这里遍历找到对应的事件。
/* deal with port status changes */
for (i = 1; i <= hdev->maxchild; i++) {
struct usb_port *port_dev = hub->ports[i - 1];
if (test_bit(i, hub->event_bits)
|| test_bit(i, hub->change_bits)
|| test_bit(i, hub->wakeup_bits)) {
/*
* The get_noresume and barrier ensure that if
* the port was in the process of resuming, we
* flush that work and keep the port active for
* the duration of the port_event(). However,
* if the port is runtime pm suspended
* (powered-off), we leave it in that state, run
* an abbreviated port_event(), and move on.
*/
pm_runtime_get_noresume(&port_dev->dev);
pm_runtime_barrier(&port_dev->dev);
usb_lock_port(port_dev);
port_event(hub, i);
usb_unlock_port(port_dev);
pm_runtime_put_sync(&port_dev->dev);
}
}
从这个for循环可以得出:
1.event_bits 、change_bits、wakeup_bits中的每一个bits对应一个port的状态,针对这点,hub的spec中有描述
2. hub最多支持8个port
找到有变化的端口后,进行电源设置,然后进入port_event()函数,下一节继续梳理port_event。