三、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;
    unsigned long bits;
    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函数)。