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位.也就是说一共可以有64条USB总线.我想,对我们这些凡夫俗子来说,这么多条足够了吧.不够?你以为你在织十字绣?
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_init是Linux中整个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_host和usb_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
因为我这台机器有4个uhci主机控制器,所以我们可以看出,分别为每个主机控制器建立了一个目录.而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_device的class_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中不使用这种机制也能照样转.只不过Greg是Linux中USB掌门人,就算地球不转了大家还是要围着他转.
- Linux那些事儿之我是UHCI(6)来来,我是一条总线,线线线线线线
- Linux那些事儿之我是UHCI(2)PCI,我们来了!
- Linux那些事儿之我是UHCI-引子
- Linux那些事儿之我是UHCI(3)-物以类聚
- Linux那些事儿之我是UHCI(5)传说中的DMA
- Linux那些事儿之我是UHCI(16)寂寞在唱歌
- Linux那些事儿之我是UHCI(24)等时传输
- Linux那些事儿之我是UHCI(29)FSBR
- Linux那些事儿之我是UHCI(5)传说中…
- Linux那些事儿之我是UHCI(5)传说中…
- 线线相交 (Unity代码)
- 线线几何位置关系
- 多线线程资源共享
- Linux那些事儿之我是Hub(6)
- Linux那些事儿之我是UHCI(1)开户和销户
- Linux那些事儿之我是UHCI(4)IO内存和IO端口
- Linux那些事儿之我是UHCI(7)主机控制器的初始化(一)
- Linux那些事儿之我是UHCI(8)主机控制器的初始化(二)
- IBM to track mainframe power use
- ASP.NET中WEB用户控件和自定义控件
- 哥斯达黎加 百天快速移民、入藉
- 基 因 检 测 感 想
- 基因检测的市场价值
- Linux那些事儿之我是UHCI(6)来来,我是一条总线,线线线线线线
- 基因检测——二十一世纪最时尚的健康方式和财富通道
- 基因检测深远的意义
- 无盘Linux详解:硬盘启动Knoppix的前前后后
- grub引导硬盘上的knoppix4.0
- 使用自定义控件创建可编辑下拉框
- 被误解的C++——C++的缺陷和D的缺陷
- VC++.net 真的没有用吗?
- 分割字符串