源码赏析
小实验
/**
这个函数做了两件事情:
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;
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);
}