Linux那些事儿之我是UHCI(6)来来,我是一条总线,线线线线线线

来源:互联网 发布:worldwind java 教程 编辑:程序博客网 时间:2024/04/28 09:51

下一个函数,1577,usb_register_bus.我们说过,一个USB主机控制器就意味着一条USB总线,因为主机控制器控制的正是一条总线.古人说,猫走不走直线,完全取决于耗子,而数据走不走总线,完全取决于主机控制器.

所以这里作为主机控制器的驱动,我们必须从软件的角度来说,注册一条总线.来自drivers/usb/core/hcd.c:

    712 /** 

    713  * usb_register_bus - registers the USB host controller with the usb core

    714  * @bus: pointer to the bus to register

    715  * Context: !in_interrupt()

    716  *

    717  * Assigns a bus number, and links the controller into usbcore data

    718  * structures so that it can be seen by scanning the bus list.

    719  */

    720 static int usb_register_bus(struct usb_bus *bus)

    721 {

    722         int busnum;

    723

    724         mutex_lock(&usb_bus_list_lock);

    725         busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);

    726         if (busnum < USB_MAXBUS) {

    727                 set_bit (busnum, busmap.busmap);

    728                 bus->busnum = busnum;

    729         } else {

    730                 printk (KERN_ERR "%s: too many buses/n", usbcore_name);

    731                 mutex_unlock(&usb_bus_list_lock);

    732                 return -E2BIG;

    733         }

    734

    735         bus->class_dev = class_device_create(usb_host_class, NULL, MKDEV(0,0),

    736                                              bus->controller, "usb_host%d", busnum);

    737         if (IS_ERR(bus->class_dev)) {

    738                 clear_bit(busnum, busmap.busmap);

    739                 mutex_unlock(&usb_bus_list_lock);

    740                 return PTR_ERR(bus->class_dev);

    741         }

    742

    743         class_set_devdata(bus->class_dev, bus);

    744

    745         /* Add it to the local list of buses */

    746         list_add (&bus->bus_list, &usb_bus_list);

    747         mutex_unlock(&usb_bus_list_lock);

    748

    749         usb_notify_add_bus(bus);

    750

    751         dev_info (bus->controller, "new USB bus registered, assigned bus number %d/n", bus->busnum);

    752         return 0;

    753 }

Linux中名字里带一个register的函数那是数不胜数,也许这是一种时尚,随着你对Linux内核渐渐的熟悉,你慢慢就会觉得其实叫register的函数都很简单,简单得就像90后的女生作人流一样.甚至你会发现,Linux内核中的模块没有不用register函数的,就像新闻联播里:开会没有不隆重的,闭幕没有不胜利的,讲话没有不重要的,决议没有不通过的,鼓掌没有不热烈的,人心没有不鼓舞的,领导没有不重视的,进展没有不顺利的,问题没有不解决的,完成没有不超额的,成就没有不巨大的,竣工没有不提前的,接见没有不亲切的,中日没有不友好的,中美没有不合作的,交涉没有不严正的,会谈没有不圆满的.

其实一路走来的兄弟们应该能够很容易的看懂这个函数,这个函数首先让我们想起了在hub驱动中讲的那个choose_address.当时我们有一个devicemap,而现在有一个busmap.很显然,原理是一样的.drivesr/usb/core/hcd.c中有定义:

     88 /* used when allocating bus numbers */

     89 #define USB_MAXBUS              64

     90 struct usb_busmap {

     91         unsigned long busmap [USB_MAXBUS / (8*sizeof (unsigned long))];

     92 };

     93 static struct usb_busmap busmap;

和当时我们在hub驱动中对devicemap的分析一样,当时的结论是该map一共有128,同理可知这里busmap则一共有64.也就是说一共可以有64USB总线.我想,对我们这些凡夫俗子来说,这么多条足够了吧.不够?你以为你在织十字绣?

735, class_device_create,这个函数是Linux设备模型中一个很基础的函数,我家Intel的企业文化中有六大价值观,Intel面试的时候我特逗的一点就是把那六大价值观给背了下来,然后面试的时候跟面试官一条一条说,把人家逗乐了.这六大价值观中有一个叫做Result Orientation,用中文说就是以结果为导向.那现在我想是该使用这一价值观的时候了.Linux 2.6设备模型中提供了大把大把的基础函数,它们都在drivers/base目录下面,这下面的函数你如果有兴趣当然可以看一看,不过我不推荐你这么做,除非你的目的就是要彻底研究这个设备模型是如何实现的.对这些基础函数,我觉得比较好的认识方法就是以结果为导向,看看它们执行之后具体有什么效果,用直观的效果来体现它们的作用.

那好,那么这里class_device_create的效果是什么?我们知道设备模型和sysfs结合相当紧密,最能反映设备模型的效果的就是sysfs.所以凭一种男人的直觉,我们应该到/sysfs下面去看效果.不过我现在更愿意给你提供更多的背景,

首先,什么是class?C++的高手们一定不会不知道class,虽说哥们儿从未写过C++程序,可是好歹也看过两遍那本Thinking in C++的第一卷,所以class还是知道的.class就是类,设备模型中引入类的意义在于让一些模糊的东西变得更加清晰,更加直观,比如同样是scsi设备,可能你是磁盘她是磁带,但你们都属于一类,这一类就是scsi_device.于是就可以在/sys/class下面建立一个文件夹,从这里来体现同一个类别的各种设备.比如,偶的某台机器里/sys/class下面可以看到这些类,此时此刻还没有加载usbcore.

localhost:~ # ls /sys/class/

backlight  graphics  mem   net      spi_master  vc

dma        input     misc  pci_bus  tty         vtconsole

而咱们这里的class_device_create的第一个参数是usb_host_class,它是什么东西呢?

让我们把镜头切给usbcore的初始化函数,usb_init().我们在hub driver中已经说过,usb_initLinux中整个usb子系统的起点.一切的一切都从这里开始.而这个函数中有这么一段:

    877         retval = usb_host_init();

    878         if (retval)

    879                 goto host_init_failed;

我们来看它具体做了什么事情,usb_host_init来自drivers/usb/core/hcd.c:

    671 static struct class *usb_host_class;

    672

    673 int usb_host_init(void)

    674 {

    675         int retval = 0;

    676

    677         usb_host_class = class_create(THIS_MODULE, "usb_host");

    678         if (IS_ERR(usb_host_class))

    679                 retval = PTR_ERR(usb_host_class);

    680         return retval;

    681 }

让我们在上面提到的那台机器机器中加载usbcore,看看在/sys/class/下面会发生点什么.

localhost:~ # modprobe usbcore

localhost:~ # ls /sys/class/

backlight  dma  graphics  input  mem  misc  net  pci_bus  spi_master  tty  usb_device  usb_host  vc  vtconsole

看出区别了么?多了两个目录,usb_hostusb_device,换言之,多了两个类.所以我们不难知道,这里class_create函数的效果就是在/sys/class/下面创建一个叫做usb_host的目录.usb_device是在另一个函数中创建的,usb_devio_init.方法是一样的,也是调用class_create函数.关于usb_device我们就不去多说了,继续看我们这个usb_host.我们这里调用class_create然后返回值赋给了usb_host_class,而这正是我们传递给class_device_create的第一个参数,所以你不看代码也应该知道我们的目标是在/sys/class/usb_host/下面建立一个文件或者是一个目录,那么结合代码来看,你就不难发现我们要建立的是一个叫做usb_hostn的文件或者是目录,具体是什么,让我们用结果来说话, 首先我们没有加载uhci-hcd,这时候可以看出这个目录是空的.

localhost:~ # ls /sys/class/usb_host/

然后我们把这个模块加载,再来看看效果.

localhost:~ # modprobe uhci-hcd

localhost:~ # ls -l /sys/class/usb_host/

total 0

drwxr-xr-x 2 root root 0 Oct  4 22:26 usb_host1

drwxr-xr-x 2 root root 0 Oct  4 22:26 usb_host2

drwxr-xr-x 2 root root 0 Oct  4 22:26 usb_host3

drwxr-xr-x 2 root root 0 Oct  4 22:26 usb_host4

因为我这台机器有4uhci主机控制器,所以我们可以看出,分别为每个主机控制器建立了一个目录.usb_host后面的这个1,2,3,4就是刚才说的busnum,即总线编号,因为一个主机控制器控制着一条总线.同时我们把class_device_create的返回值赋给了bus->class_dev.struct usb_bus中有一个成员struct class_device *class_dev,这个成员被称作class device,这个结构体对写驱动的人来说意义不大,但是从设备模型的角度来说是必要的,实际上对写驱动的人来说,你完全可以不理睬设备模型中class这个部分,你可以我行我素,你可以尽可能少的支持设备模型,因为这对你访问设备没有太多影响.你甚至可以让你的设备根本就不在/sysfs下面体现出来,你有权这么做,因为你是叛逆的80.但是一个聪明的人,你应该知道,有些东西是相互的,你用代码去支持设备模型,将来你使用设备的时候就能享受到设备模型为你提供的方便.相反,如果你为了省事不去支持设备模型,那么将来你会遭报应的,因为你使用设备的时候会发现有很多不便.这道理就像我们中华民族广大妇女的传统美德善待婆婆一样!虽然是今非昔比,已没有了主仆之分,但亦应该在互相尊重人格平等的基础上,更加地善待婆婆,才是最聪明的选择.因为每一个女人都有可能成为将来的婆婆.如果世上的女人都能为后人作出榜样,这个社会就会变的和谐起来,请做一个善良的聪明女人吧!

所以这里有743行这么一个举动,调用class_set_devdata,这就算是写代码的人对设备模型的支持,因为struct class_device中也有一个成员void *class_data,被称为class-specific data,而在include/linux/device.h中定义了class_set_devdata和一个与之对应的函数class_get_devdata.

    279 static inline void *

    280 class_get_devdata (struct class_device *dev)

    281 {

    282         return dev->class_data;

    283 }

    284

    285 static inline void

    286 class_set_devdata (struct class_device *dev, void *data)

    287 {

    288         dev->class_data = data;

    289 }

结合我们这里具体对这个函数调用的代码可知,最终咱们这个host对应的class_deviceclass_data被赋值为bus.这样有朝一日我们要通过class_device找到对应的bus的时候我们只要调用class_get_devdata即可.设备模型的精髓就在于把一个设备相关联的种种元素都给联系起来,设备模型提供了大量建立这种纽带的函数,我们要做的就是调用这些函数.

,继续,746,很显然又是队列操作. usb_bus_list是一个全局队列,drivers/usb/core/hcd.c中定义:

     84 /* host controllers we manage */

     85 LIST_HEAD (usb_bus_list);

     86 EXPORT_SYMBOL_GPL (usb_bus_list);

每次注册一条总线就是往这个队列里添加一个元素.struct usb_bus中有一个成员struct list_head bus_list.所以这里直接调用list_add即可.

749,usb_notify_add_bus,看到这个函数我几乎晕阙.因为细讲这个函数意味着我得少打两盘麻将而你却未必会更能理解usb子系统.真的,时间有限,生命有限,你别天真的以为挤时间就真的像挤乳沟一样容易.我只想用一句话来解释这个函数,按我们Intel以结果为导向的理论来说,这个函数在此情此景执行的结果是/proc/bus/usb下面会多一些文件,比如,

localhost:~ # ls /proc/bus/usb/

001  002  003  004  devices

这几个文件都是刚才这个函数执行之后的效果.

好了,usb_register_bus算是看完了,再一次回到usb_add_hcd中来.1580,usb_alloc_dev被调用,这个函数我们可不陌生.不过我们下节再看吧.这节剩下的时间我们为usb_notify_add_bus再多说两句话,这个函数牵涉到Linux中的notify机制,这是Linux内核中一种常用的事件回调处理机制.传说中的那个神奇的内核调试工具kdb中就是利用了这种机制进入kdb.这种机制在网络设备驱动中的应用,那就像成都小吃在北京的分布一样,满大街都是.而在usb子系统中,以前并没有使用这种机制,只是Greg同学在2005年底提出来要加进来的.其实我个人觉得,usb中不使用这种机制也能照样转.只不过GregLinuxUSB掌门人,就算地球不转了大家还是要围着他转.

原创粉丝点击