usb_register()和usb_deregister()这两个函数我们当初分析usb storage的时候就已经见到过了.当时我们就说过了,这个函数是用来向usb核心层,即usb core,注册一个usb设备驱动的.那年我们注册了一个struct usb_driver usb_storage_driver.而这里我们注册的是hub的驱动程序所对应的struct usb_dr" />
现在的位置: 首页 > 综合 > 正文

Linux那些事儿之我是Hub(3)一样的精灵不一样的API

2013年10月24日 ⁄ 综合 ⁄ 共 4954字 ⁄ 字号 评论关闭

usb_register()usb_deregister()这两个函数我们当初分析usb storage的时候就已经见到过了.当时我们就说过了,这个函数是用来向usb核心层,usb core,注册一个usb设备驱动的.那年我们注册了一个struct usb_driver usb_storage_driver.而这里我们注册的是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 };

和咱们storage那个情况一样,最重要的一个函数就是hub_probe,对应咱们那里的storage_probe().很多事情都在这期间发生了.不过有一点你必须明白,当初storage_probe()被调用是发生在usb-storage模块被加载了并且检测到了有设备插入之后的情况下,也就是说有两个前提,第一个usb-storage被加载了,第二个设备插入了被检测到了,于是storage_probe()被调用.hub,说她特别,我可绝不是忽悠你.hub本身就是两种,一种是普通的hub,一种是root hub.对于普通hub,它完全可能也是和U盘一样,在某个时刻被你插入,然后这种情况下hub_probe被调用,但是对于root hub就不需要这么多废话了,root hub肯定是有的,只要你有host controller,就一定会有root hub,所以hub_probe()基本上是很自然的就被调用了,不用说非得等待某个插入事件的发生,没这个必要.当然如果你要抬杠,你说那如果没有usb host controller那就不用初始化root hub,这简直是废话,没有usb host controller就没有usb设备能工作.那么usb core这整个模块你就没有必要分析了.所以,只要你有usb host controller,那么在usb host controller的驱动程序初始化的过程中,它就会调用hub_probe()来探测root hub,不管你的host controllerohci,uhci,还是ehci的接口.别急,慢慢来.

如果register一切顺利的话,那么返回0.要不返回负数,返回负数就说明出错了.如果这一步都能出错,那我只能说你运气真的很好,中国队要有你这样的运气,早就拿世界杯冠军了,哪能像现在这样和缅甸进行生死战.Ok,假设这一步没有fail,如果真的fail你也不用急,Linus肯定比你急,Greg更急,恐怕这位Linuxpci/usb的维护者一世英名就被这样你毁了.

Ok,2862,这行代码其实是很有技术含量的.不过对写驱动的人来说,其作用就和我们当年那个kernel_thread()是的,或者如果你忘记了我们曾经讲过的创建usb-storage精灵进程的kernel_thread(),那么你就把这个当作fork.世界在变,Linux内核代码当然也在变,小时候我们看周润发赵雅芝演的上海滩,长大了我们看孙俪黄晓明演的上海滩.都是上海滩,只是版本不同,只是内容不同.内核kthread_run()就扮演着2.6.10内核那个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()正是usb hub里面和usb_hub_init()相对应的函数.

2863,判断一下khubd_task,IS_ERR是一个宏,用来判断指针的.当你创建了一个进程,你当然想知道这个进程创建成功了没有.以前我们注意到每次申请内存的时候都会做一次判断,你说创建进程是不是也要申请内存?不申请内存谁来记录struct task_struct?很显然,要判断.以前我们判断的是指针是否为空,但是Linux内核不是上海滩,越老越经典.以后接触代码多了你会发现,我们以前在usb storage申请内存的时候用的都是kmalloc(),但是其实Linux内核中有很多种内存申请的方式,而这些方式所返回的内存地址也是不一样的,所以并不是每一次我们都只要判断指针是否为空就可以了.事实上,每一次调用kthread_run()之后,我们都会用一个IS_ERR()来判断指针是否有效.IS_ERR()1就表示指针有错,或者准确一点说叫做指针无效.什么叫指针无效?如果你和我一样,觉得生活很无聊,那么本节最后一段会专门给你个解释,其他人就不用去管IS_ERR(),让我们继续往下看,只需要记得,如果你不希望发生缺页异常这样的错误的话,那么请记住,每次调用完kthread_run()之后要用IS_ERR()来检测一下返回的指针.如果IS_ERR()返回值是0,那么说明没有问题,于是return 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.1/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(),首先你得知道有一种空间叫做内核空间,不清楚也不要紧,我也不是很清楚,曾经,在复旦,上操作系统这门课的时候,我一度以为我已经成为天使了,因为我天天上课都在听天书.后来,确切地说是去年,我去微软全球技术中心(GSTC)面试的时候,那个manager就要我解释这个名词,要我谈一谈对内核空间和用户空间的理解,其实我也挺纳闷的,我只不过是希望能成为微软的一名技术支持工程师,居然还要懂内核,你说这是什么世道?中学时候,老师不是跟我说只要学好数理化,走遍天下都不怕吗?算了,不去想这些伤心往事了.结合IS_ERR()的代码来看,来自include/linux/err.h:

      8 /*

      9  * Kernel pointers have redundant information, so we can use a

     10  * scheme where we can return either an error code or a dentry

     11  * pointer with the same return value.

     12  *

     13  * This should be a per-architecture thing, to allow different

     14  * error and pointer decisions.

     15  */

     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 {

  

抱歉!评论已关闭.