插入usb之后发生了什么

源码赏析
小实验
/**
    这个函数做了两件事情:
            1 : 注册hub驱动
            2 : 创建名为"khubd"的守护线程,它的作用是检测hub的状态变化。
*/
int usb_hub_init(void)
{
    /**
        usb hub也是usb设备。驱动的注册和usb设备驱动的注册方式是一样的。

        该函数最终是 :如果usb总线提供了probe就调用总线提供的probe,如果没有提供
        那么就调用驱动提供的probe,由于usb总线没有提供probe,那么驱动提供的 hub_probe()
        就会被调用了
        (注册流程请见我以前博文的赏析)

        看看这个hub_driver.
        static struct usb_driver hub_driver = {
            .name =     "hub",
            这个hub_probe第一次调用,是在usb主机控制器的驱动初始化的时候用来探测Root Hub的。
            在系统启动后的log中,会打印有:USB hub found
            .probe =    hub_probe,
            .disconnect =   hub_disconnect,
            这是休眠、唤醒hub的函数,以后在说。
            .suspend =  hub_suspend,
            .resume =   hub_resume, 
            ...
    };
    */
    if (usb_register(&hub_driver) < 0) 
    {
        printk(KERN_ERR "%s: can't register hub driver\n",usbcore_name);
        return -1;
    }
    /**
        创建了一个守护进程,用于监视hub的状态。

        这个守护进程 在后面我们会看到 它会在urb的完成函数hub_irq()->kick_khubd()中被唤醒。
        它在什么时候被睡眠?
        怎么被唤醒的?
        我们往下面看
        kthread_run()这个函数底层调用了kthread_create()函数,kthread_create()会唤醒睡眠的2号
                    进程kthreadd 来创建内核线程,请见我以前的博文的赏析
    */
    khubd_task = kthread_run(hub_thread,NULL,"khubd");
    /**
        如果khubd成功创建,那么就返回了。
    */
    if(!IS_ERR(khubd_task))
    {
        return 0;
    }
    /**
        以下代码是 内核线程"khubd"创建不成功过的时候执行的
        卸载hub驱动
    */
    usb_deregister(&hub_driver);
    printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);

    return -1;

}

static int hub_thread(void * __unnsed)
{
    /**
        内核线程默认是不可以被冷冻的,清除它的PF_NOFREEZE位,让进程可以冷冻。
    */
    set_freezable();
    /**
        循环退出的条件:
                1 : hub_event_list队列为空
                2 : 线程接收到了stop信号。
    */
    do{
        /**
            这个函数很庞大、很重要,它里面有这样一段代码:
            while(1)
            {
                spin_lock_irq(&hub_event_lock);
                if (list_empty(&hub_event_list)) {
                    spin_unlock_irq(&hub_event_lock);
                    break;
                }
                .....
            }
            由上面代码可以知道,
            如果hub_event_list为空,那么就会执行下面的wait_event_freezable()进行睡眠。
                hub_event_list的初始值就是空,那么就直接break了,返回hub_thread,然后就睡眠,等待事件的到来。
                修改了内核代码,这里实验验证了一下,内核打印的log:
                    usbcore: registered new interface driver usbfs
                    usbcore: registered new interface driver hub
                    list_empty----**: 1   添加的code
                    usbcore: registered new device driver usb
            如果hub_event_list不为空,就会进行进行usb的枚举,这一部分 我们放在下面欣赏
        */
        hub_events();
        /**
            看看它的condition : 
            !list_empty(&hub_event_list) ||kthread_should_stop()
            只要接收到stop停止信号或者     hub_event_list为空,那么就会睡眠

            在下面的函数kick_khubd()中,是通过list_add_tail(&hub->event_list, &hub_event_list)
            来唤醒的。

            它的底层调用到了 __wait_event_interruptible()函数,
                这个函数会调用set_current_state(TASK_INTERRUPTIBLE);将线程的状态设置成TASK_INTERRUPTIBLE
                然后如果没有信号需要处理,就调用schedule()让进程一直休眠直到条件满足为止。
            这个函数的分析请见我以前的博文。
        */
        wait_event_freezable(khubd_wait,!list_empty(&hub_event_list) ||kthread_should_stop());
    }while (!kthread_should_stop() || !list_empty(&hub_event_list));
}

/**
    Hub正常工作后,usb主机控制器就会定时的询问hub,
    如果hub端口上有usb设备的插入、拔出的时候,hub就会把端口的状态变化告诉usb主机控制器
    这是通过向usb主机控制器发送urb请求(当然是中断urb了)来实现的。当usb主机控制器处理完成这个urb后,
    那么urb的完成函数hub_irq()就被调用了,
*/

static void hub_irq(struct urb *urb)
{
    struct usb_hub * hub = urb->context;
    /*
        上面获取的urb是这样来的 : 
        static int hub_configure()
        {
                分配一个urb
                hub->urb = usb_alloc_urb(0, GFP_KERNEL);
                if (!hub->urb) 
                {
                    ret = -ENOMEM;
                    goto fail;
                }
                填充中断类型的urb,完成函数:hub_irq().
                usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
                hub, endpoint->bInterval);  
                .....
        }
    */
    int status = urb->status;
    unsigned i;
    unsigned long bits;
    .....

    /*
        调用了这个函数。它会唤醒前面睡眠的线程的
    */
    kick_khubd(hub);
}

static void kick_khubd(struct usb_hub *hub)
{
    unsigned long   flags;
    /**
        hub_irq()是运行在中断上下文的,
        这里如果不关闭中断的话,可能会导致死锁的发生
        详情请见我以前的博文: http://blog.csdn.net/leesagacious/article/details/48595203
    */
    spin_lock_irqsave(&hub_event_lock, flags);
    if (!hub->disconnected && list_empty(&hub->event_list))
     {
         /**
             这行代码是为了满足“khubd”守护进程被唤醒的条件的
             将hub的event_list添加到hub_event_list尾部
         */
        list_add_tail(&hub->event_list, &hub_event_list);

        usb_autopm_get_interface_no_resume(to_usb_interface(hub->intfdev));

        /**
            终于要唤醒了......
        */
        wake_up(&khubd_wait);
    }
    spin_unlock_irqrestore(&hub_event_lock, flags);
}

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值