三、usb设备的识别过程
在这里梳理一下上一篇博客中的内容:(这张图来自https://blog.csdn.net/lizuobin2/article/details/51931161)
上一篇博客刚好从平台设备、驱动的匹配分析到hub_irq这个函数这里。
static void hub_irq(struct urb *urb)
{struct usb_hub *hub = urb->context;int status = urb->status;
unsigned i;
unsignedlongbits;switch(status) {
....case 0: //端口的状态发生了变化
bits = 0;for (i = 0; i < urb->actual_length; ++i)
bits|= ((unsigned long) ((*hub->buffer)[i]))<< (i*8);
hub->event_bits[0] = bits; //保存状态改变的信息
break;
}
hub->nerrors = 0;/*Something happened, let khubd figure it out*/kick_khubd(hub);//重要函数 唤醒hub线程resubmit:if (hub->quiescing)return;if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
&& status != -ENODEV && status != -EPERM)
dev_err (hub->intfdev, "resubmit --> %d\n", status);
}
在kick_khubd(hub)中会唤醒执行wake_up(&khubd_wait)来唤醒一个程序。那么唤醒的到底是哪个程序呢?
int usb_hub_init(void)
{//这里注册了hub_driver 这个在上一篇博客中提到过
if (usb_register(&hub_driver) < 0) {
}//这里创建了一个hub_thread 名字叫"khubd"
khubd_task = kthread_run(hub_thread, NULL, "khubd");/*Fall through if kernel_thread failed*/usb_deregister(&hub_driver);
printk(KERN_ERR"%s: can't start khubd\n", usbcore_name);return -1;
}
现在可以看看在hub_thread这个线程中做一些什么事?
static int hub_thread(void *__unused)
{
set_freezable();do{
hub_events(); wait_event_freezable(khubd_wait,!list_empty(&hub_event_list) ||kthread_should_stop());
}while (!kthread_should_stop() || !list_empty(&hub_event_list));return 0;
}
在这个内核线程中主要执行hub_events(),但是平时应该是休眠状态,直到有事情发生,其他程序中将其唤醒。因此可以说是在hub_irq 函数中唤醒了hub_thread,使得hub_thread能够执行hub_events()
函数。
当有usb设备插入时,主机控制器检测到hub端口状态的变化,会执行hub_irq函数,然后按照下图的函数调用顺序一路往下执行,实现usb设备的识别过程:
(1)给新设备分配地址
(2)并将该地址告诉usb设备即设置usb地址
(3)发出命令读取描述符
(4)执行usb_new_device和device_add(向总线上添加usb设备)
(5)device_add 之后又会执行usb_device_match函数和generic_probe函数,在generic_probe 函数中 选择和设置 配置 之后又会执行device_add(向usb总线上添加接口设备)
(6)device_add 之后又会执行usb_device_match函数(这个时候应该就根据id_table和自己写的设备驱动进行匹配操作了)和xx_probe函数(自己驱动的probe函数)。