4.一样的精灵,不一样的API(1)
usb_register()这个函数是用来向USB核心层,即USB Core,注册一个USB设备驱动的,而这里我们注册的是Hub的驱动程序所对应的struct usb_driver结构体变量。定义于drivers/usb/ core/hub.c中:
- 2841 static struct usb_driver hub_driver = {
- 2842 .name = "hub",
- 2843 .probe = hub_probe,
- 2844 .disconnect = hub_disconnect,
- 2845 .suspend = hub_suspend,
- 2846 .resume = hub_resume,
- 2847 .pre_reset = hub_pre_reset,
- 2848 .post_reset = hub_post_reset,
- 2849 .ioctl = hub_ioctl,
- 2850 .id_table = hub_id_table,
- 2851 .supports_autosuspend = 1,
- 2852 };
这里面最重要的一个函数就是hub_probe,很多事情都在这期间发生了。每个USB设备(或者说所有设备)的驱动都会有一个probe函数,比如U盘的probe函数就是storage_probe(),不过storage_probe()被调用需要有两个前提:第一个前提是usb-storage被加载了,第二个前提是U盘等设备插入了被检测到了。
而Hub,说它特别,我可绝不是"忽悠"你。Hub本身就有两种:一种是普通的Hub,一种是Root Hub。对于普通Hub,它完全可能也和U盘一样,在某个时刻被插入,然后在这种情况下hub_probe被调用,但是对于Root Hub就不需要这么多废话了,Root Hub肯定是有的,只要你有USB主机控制器,就一定会有Root Hub,所以hub_probe()基本上很自然地就被调用了,不用说非得等待某个插入事件的发生,没这个必要。当然没有USB主机控制器就没有USB设备能工作。那么USB Core这整个模块你就没有必要分析了。所以,只要你有USB主机控制器,那么在USB主机控制器的驱动程序初始化的过程中,它就会调用hub_probe()来探测Root Hub,不管你的主机控制器是OHCI、UHCI还是EHCI的接口。
如果register一切顺利的话,那么返回值为0。如果返回值为负数,就说明出错了。现在假设这一步没有出错。
usb_hub_init()的2862行,这行代码其实是很有技术含量的,不过对于写驱动的人来说,其作用就和当年的kernel_thread()相当。不过kernel_thread()返回值是一个int型的,而kthread_run()返回的却是struct task_struct结构体指针。这里等号左边的khubd_task是我们自己定义的一个struct task_struct指针:
- 88 static struct task_struct *khubd_task;
struct task_struct不用多说,记录进程的数据结构。每一个进程都用一个struct task_struct结构体变量来表示。所以这里所做的就是记录下创建好的内核进程,以便日后要卸载模块时可以用另一个函数来结束这个内核进程(你也可以叫内核线程),到时我们会调用kthread_stop(khubd_task)函数来结束这个内核线程,这个函数的调用我们将会在usb_hub_cleanup()函数中看到。而usb_hub_cleanup()正是Hub里面和usb_hub_init()相对应的函数。
2863行,判断khubd_task,IS_ERR是一个宏,用来判断指针的。当你创建了一个进程,你当然想知道这个进程创建成功了没有。
以前我们注意到每次申请内存时都会做一次判断,你说创建进程是不是也要申请内存?不申请内存谁来记录struct task_struct?很显然,要进行判断。以前我们判断的是指针是否为空。以后接触代码多了你会发现,其实Linux内核中有很多种内存申请的方式,而这些方式所返回的内存地址也是不一样的,所以并不是每一次我们都只要判断指针是否为空就可以了。事实上,每一次调用kthread_run()之后,我们都会用一个IS_ERR()来判断指针是否有效。IS_ERR()为1就表示指针有错,或者准确一点说叫做指针无效。
什么叫指针无效?后面会专门解释,让我们继续往下看,只需要记得,如果你不希望发生缺页异常这样的错误的话,每次调用完kthread_run()之后要用IS_ERR()来检测返回的指针。如果IS_ERR()返回值是0,那么说明没有问题,于是返回值为 0,也就是说usb_hub_init()就这么结束了。反之,就会执行usb_deregister(),因为内核线程没有成功创建,hub就没法驱动起来了。
最后函数在2870行,返回值为-1。回到usb_init()函数中我们会知道,接下来usb_hub_ cleanup()就会被调用。usb_hub_cleanup()同样定义于drivers/usb/core/hub.c中:
- 2873 void usb_hub_cleanup(void)
- 2874 {
- 2875 kthread_stop(khubd_task);
- 2876
- 2877 /*
- 2878 * Hub resources are freed for us by usb_deregister. It calls
- 2879 * usb_driver_purge on every device which in turn calls that
- 2880 * devices disconnect function if it is using this driver.
- 2881 * The hub_disconnect function takes care of releasing the
- 2882 * individual hub resources. -greg
- 2883 */
- 2884 usb_deregister(&hub_driver);
- 2885 } /* usb_hub_cleanup() */
这个函数我想没有任何必要解释了吧。kthread_stop()和刚才的kthread_run()对应,usb_deregister()和usb_register()对应。
总之,如果创建子进程出了问题,那么一切都免谈。
反之,如果成功了,那么kthread_run()的三个参数就是我们要关注的了:第一个是hub_thread(),子进程将从这里开始执行。第二个是hub_thread(),传递的是NULL,第三个参数就是精灵进程的名字ps -el,如下所示:
- localhost:/usr/src/linux-2.6.22/drivers/usb/core # ps -el | grep khubd
- 1 S 0 1963 27 0 70 -5 - 0 hub_th ? 00:00:00 khubd
你就会发现有这么一个精灵进程运行着。所以,下一步,让我们进入hub_thread()来查看这个子进程吧。
以下是关于IS_ERR的介绍文字。如果你对内存管理没有任何兴趣,就不用往下看了。要想明白IS_ERR(),首先你得知道有一种空间叫做内核空间,不清楚也不要紧。结合IS_ERR()的代码来看,来自include/linux/err.h:
- 16 #define MAX_ERRNO 4095
- 17
- 18 #ifndef __ASSEMBLY__
- 19
- 20 #define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
- 21
- 22 static inline void *ERR_PTR(long error)
- 23 {
- 24 return (void *) error;
- 25 }
- 26
- 27 static inline long PTR_ERR(const void *ptr)
- 28 {
- 29 return (long) ptr;
- 30 }
- 31
- 32 static inline long IS_ERR(const void *ptr)
- 33 {
- 34 return IS_ERR_VALUE((unsigned long)ptr);
- 35 }
- 36
- 37 #endif