这个世界上不需要努力就能得到的东西只有一样,那就是年龄。所以要不怕苦不怕累,回到struct usb_hcd,继续努力的往下看。
64行,又见kref,usb主机控制器的引用计数。struct usb_hcd也有自己专用的引用计数函数,看hcd.c文件
和struct urb的那几个长的也忒像了,像的俺都不好意思多介绍它们了,如果不明白就回去看看聊struct urb的时候怎么说的吧。
66行,product_desc,主机控制器的产品描述字符串,对于UHCI,它为“UHCI Host Controller”,对于EHCI,它为“EHCI Host Controller”。
67行,irq_descr[24],这里边儿保存的是“ehci-hcd:usb<chmetcnv tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="1" unitname="”" w:st="on">1<span lang="EN-US" style="FONT-FAMILY: 宋体; mso-hansi-font-family: Verdana; mso-ascii-font-family: Verdana"><span lang="EN-US">”</span></span></chmetcnv>之类的字符串,也就是驱动的大名再加上总线编号。
71~73行,电源管理的,飘过。
78行,driver,每个男人心中都有一个狐狸精,每个女人心里都有一个洛丽塔,每个主机控制器驱动都有一个struct hc_driver结构体。看看它在hcd.h里的定义
和usb_driver一样,和pci_driver也一样,所有的xxx_driver都有一堆函数指针,具体的主机控制器驱动就靠它们来活的,不多说了,太深入了,做人要懂得适可而止,这里只说下函数指针之外的东西,也就是开头儿的那三个。description直白点说就是驱动的大名,比如对于UHCI,它是“uhci_hcd”,对于EHCI,它就是“ehci_hcd”。product_desc和struct usb_hcd里的那个是一个样儿。hcd_priv_size还是有点儿意思的,前面喝那杯茶的时候提到过,每个主机控制器驱动都会有一个私有结构体,藏在struct usb_hcd最后的那个变长数组里,这个变也是相对的,在创建usb_hcd的时候也得知道它能变多长,不然谁知道要申请多少内存啊,这个长度就hcd_priv_size。
81行,flags,属于HCD的一些标志,可用值就在82,83行。它们什么意思?书到用时方恨少,flags到用时才可知。
69,70,85~91,这几行都是专为root hub服务的。佛说,一花一世界,一叶一如来。一个host controller对应一个root hub,即使某些嵌入式系统里,硬件上host controller没有集成root hub,软件上也需要虚拟一个出来,也就是所谓的virtual root hub。它位置是特殊的,但需要提供的功能和其它hub是没有什么差别的,仅仅是在和host controller的软硬件接口上有一些特别的规定。那个网络红人ayawawa再怎么觉得自己有多特殊,觉得“漂亮者皆不如我聪明,聪明者皆不如我漂亮”,她也始终是个需要男人去关怀的女人,root hub再怎么特别也始终是一个hub,是一个usb设备,也不能脱离usb这个大家庭,也要向组织注册,也要有自己的设备生命线,既然这里遇到了,就说说它的这条线。
/*------------------------------------------root hub的生命线-------------------------------------------------
还是要先声明一下,root hub的生命线只是咱们的主线里的一个岔路口,不会沿着它去仔细的走,只会尽量的使用快镜头去展示一下。如果有哪里不明白,等走完了那条主线,你再回过头来看看,很多事情都是在我们蓦然回首的时候才明白的。
基于root hub和host controller这种极为暧昧极为特殊的关系,UHCI、EHCI等主机控制器驱动程序在自己的初始化代码里都有大量的篇幅大量的心思花在root hub上面。不管是UHCI,还是EHCI,一般来说都是PCI设备,是PCI设备都应该有个struct pci_driver结构体,都应该有一个属于自己的probe。在这个probe里,也都会调用usb_create_hcd()来创建一个属于自己的usb_hcd,也都会调用usb_add_hcd()将这个刚刚创建的usb_hcd注册到usb组织里。于是root hub便披着皇帝的新装,走着猫步登场了。这里只贴与root hub相关的一些主要代码
在usb_add_hcd函数里,首先会调用咱们已经亲密接触过的usb_alloc_dev()来为root hub准备一个struct usb_device结构体,我唯一想再提一下的是这时root hub的设备类型被赋值为usb_device_type,所属总线类型赋值为usb_bus_type。然后根据各个host controller的实际情况,设定它的速度,现在你应该知道struct hc_driver里的那个漏过的flags是干吗的了吧,它等于HCD_USB2时表示这个host controller绑定的root hub是高速hub,等于HCD_USB1时表示是低速/全速hub。struct usb_device结构体准备妥当之后,就要赋给struct usb_bus的root_hub元素。这些都只不过是准备工作,重头戏都在usb_add_hcd()最后调用的register_root_hub()里面。
register_root_hub()非常肯定的将root hub的设备号devnum设置为1,于是就少了选择地址设置地址的过程,root hub直接跳跃式发展进入了Address状态,咱们耗费几代人,期待数十年才能完成的壮举,root hub轻而易举的就实现了。当然,是个人都知道在Address状态是好处多多便利多多的,不然房地产就支柱不起来了。起码这时可以很方便的获得root hub的设备描述符,可以调用usb_new_device将root hub送给无所不能的设备模型,去寻找它命中的驱动。
如果这些都一切顺利的话,register_root_hub()在最后就会将struct usb_hcd的rh_registered设置为1,表示root hub已经找到组织并加入到革命队伍里了。
Linux设备模型根据root hub所属的总线类型usb_bus_type将它添加到usb总线的那条有名的设备链表里,然后会轮询usb总线的另外一条有名的驱动链表,针对每个找到的驱动去调用usb总线的match函数,也就是咱们以前一再遇到以后不断遇到的usb_device_match(),为root hub牵线搭桥寻找另一个匹配的半圆。要注意,root hub的设备类型是usb_device_type,所以在usb_device_match()里走的设备那条路,匹配成功的是那个对所有usb_device_type类型的设备都来者不拒的花心大萝卜,usb世界里唯一的那个usb设备驱动(不是usb接口驱动)struct device_driver结构体对象usb_generic_driver。所以接下来要调用的就是usb_generic_driver的probe函数generic_probe(),去配置root hub,然后root hub就大步迈进了Configured状态。因为root hub只有一个配置,所以generic_probe()配置root hub时并没有多少选择的烦恼,如果有所疑问,可以look下hcd.c的开头就已经设定好的root hub设备描述符
这张表是const的,也就是说在整个usb的世界里它都是只能去读不能去修改的,要用严谨科学的态度去对待root hub。明眼人一眼就能看到躲在它最后的那个0x01,这就是root hub支持的配置数量。在它下边儿还有针对usb 1.1的root hub设备描述符,大同小异就不贴了。
既然进入了Configured状态,就可以无拘无束的使用root hub提供的所有功能了,不过,对于咱们使用usb的劳苦大众来说,实际在起作用的还是设备里的接口,所以generic_probe()接下来就会根据root hub使用的配置,为它所有的接口准备struct usb_interface结构体对象,这时接口所属的总线类型仍然为usb_bus_type,设备类型就不一样了,为usb_if_device_type,早先说过的那句总线有总线的类型,设备有设备的类型到这里就应该再加上一句,接口有接口的类型。usb_if_device_type在message.c里定义
为root hub的接口准备struct usb_interface结构体也没费太大功夫,因为spec里规定了hub上除了端点0,就只有一个中断的IN端点,并不用准备太多的东西。root hub的配置描述符也已经在hcd.c的开头儿指定好了,确实只有一个接口,一个设置,一个端点,去look一下
174行的0x01,199行0x00,200行的0x01,分别指定了接口数量,使用的设置,端点的数目。也就是说,如果加上端点0,root hub只有两个端点,而另外一个端点为IN端点,端点号为1,中断类型,每次可以处理的最大字节数为2,期望host controller访问自己的时间间隔为256ms。这里打个岔问个问题,为什么说这个中断端点的wMaxPacketSize为2,211行不是明确写着0x02,0x00么?呵呵,协议里说了,usb设备的各种描述符都是按照little-endian方式的字节序存储的,先是低字节然后是高字节。
为root hub的那个独苗儿接口准备好struct usb_interface结构体之后应该怎么做?佛又说了,一个接口一个驱动,接下来显然是应该将它送给设备模型去寻找它命中注定的驱动了。然后此处省略2008字,又到了usb_device_match()。不过你说这个时候设备和接口两条路它应该走哪条?它的类型已经设置成usb_if_device_type了,设备那条路把门儿的根本就不会让它进,所以它必须得去走接口那条路。
有关在接口这条路上它怎么去寻找另一半儿,咱现在不多说,日后在主线里说,现在只天马行空的扯一下。首先回头瞧下root hub的设备描述符,它的bDeviceClass 被指定为0x09,在include/linux/usb/ch9.h里,它对应的是USB_CLASS_HUB,再回头看看root hub配置描述符的201行,bInterfaceClass也被指定成0x09,也就是说同样为USB_CLASS_HUB。你再去hub驱动对应的hub.c文件里看看那里的hub_id_table,是不是应该明白点什么了?对头,root hub里的那个接口所对应的驱动就是hub驱动,虽然它很特殊,可它也是个hub啊。root hub一边和host controller相生相依,一边和hub驱动眉来眼去花前月下,多么巨大的讽刺。
接下来要做的就是调用hub驱动里的hub_probe()。不管是root hub还是一般的hub,走到这一步都是必然的,不同的是对于root hub在host controller初始化的过程中就会去调用hub_probe()。再省略2008字后,hub_probe()通过hub_configure()创建并初始化了一个中断的urb,然后在hub_activate()里调用usb_submit_urb()将它提交给core,接着就又回到了HCD。如果觉得省略了这么多让你很不爽,那就再去看看《我是hub》吧。
每个hub都要需要进行中断传输来获得hub的状态变化,不然hub驱动里那个著名的hub_events就活不下去了,你连在hub上的usb设备系统也就察觉不到了,你也就不能用usb摄像头来泡mm了,……,多米诺骨牌的一连串反应。都走到了21世纪,离奥运都不到300天了,都应该知道中断传输不会提交一次就完事儿了,这么一次就完事儿hub不会满意,每个男人女人都不会满意。你需要在你的结束处理函数离提交再提交,host controller都不嫌烦,你烦什么啊。中断传输都是有个间隔时间的,这个时间不由你决定,也不由我决定,咱们只能期待,决定权在host controller那里。host controller就决定了,对于root hub要每隔250ms去访问一次,你再看上面root hub的描述符,里面显示它期待的时间为0xff,也就是256ms,250与256也差不了多少,root hub也应该满意了。这个固定的访问频率host controller是怎么实现的?就是通过struct usb_hcd里的那个定时器rh_timer,定时器就是专门干这种事儿的,root hub每提交一次urb,在HCD里都会对它初始化一次,指定250ms之后去获得root hub的状态,然后就让urb返回给root hub,于是root hub再提交,再250ms后返回,……。
但是这种对root hub的轮询机制是早先版本里的,现在就不一样了,世道变了,队伍不好带了。struct usb_hcd里出现了uses_new_polling,看名字就知道是一种新机制出现了。这也是Alan Stern在2005年的阳春三月春心萌动孕育出来的,三月真是一年里的好时候。旧社会主要靠剥削,新社会主要靠奉献,旧机制主要靠轮询,新机制主要靠中断。在新的机制里,root hub仍然是要提交一个中断urb的,不同的是这时它可以不用再请定时器来帮忙,在有设备插入时,host controller会在自己的中断处理函数里调用一个名叫usb_hcd_poll_rh_status的函数去获得root hub的状态并将urb的所有权归还给hub驱动。
那是不是就可以不需要定时器rh_timer了?事情没这么简单,也不能就这么过河拆桥,由于某些不可言状的原因,原来的机制还必须得保留着,给不同的host controller选择使用。为了让新旧机制和谐的运作,usb_hcd_poll_rh_status多了一个职务,就是作为rh_timer的定时器函数,而且struct usb_hcd里除了uses_new_polling之外就又多了poll_rh等几个元素。如果uses_new_polling没有被设置,则一定会采用旧的轮询机制,如果uses_new_polling已经被设置了,但同时又设置了poll_rh,也是要采用旧机制,否则采用的就是新机制。至于status_urb,其实就是root hub提交的那个urb。
root hub的生命线就还是说到这里吧,适可而止适可而止。
-------------------------------------------------------------------------------------------------------------*/
回到struct usb_hcd,看92行,wireless,是不是无线usb。
94~97这几行都是与主机控制器的娘家PCI有关的,说到PCI就不得不说到那张著名的表。PCI spec说了,要想在我这儿混,谁都必须不能少了那张表。就好像木子美对一男八卦记者说的:要采访我,必须先和我上床;在床上能用多长时间,我就给你多长时间的采访。
不过强制的也不一定都是坏事儿,起码这张表不是,中断号啊等很多有用的东西都在里面准备好了,有了这张表儿,写PCI驱动的身体甭儿好,吃饭甭儿香。94行的irq就躲在上面表儿里的倒数第四个byte,HCD可以直接拿来用,根本就不用再去申请,一提到申请个什么,谁都知道那是多么一个艰辛的过程。接下来的regs,rsrc_start,rsrc_len就与中间的那几个Base Address0~5脱不开关系了,牵涉到所谓的I/O内存和I/O端口,简单说一下。
大家都知道CPU牛X,是众人瞩目的焦点,但是它再牛也不可能一个人战斗,电脑运转是个集体项目,只有一个卡卡是不够的,还有很多千奇百怪的外设。卡卡再厉害也不能从自己门口带球带到对方门口,他需要跟小皮,加加配合,交流,CPU也需要跟各种外设配合交流,它需要访问外设里的那些寄存器或者内存。现在差别就出来了,主要是空间的差别,一些CPU芯片有两个空间,即I/O空间和内存空间,提供有专门访问外设I/O端口的指令,而另外一些只有一个空间,即内存空间。外设的I/O端口可以映射在I/O空间也可以映射到内存空间,CPU通过访问这两个空间来访问外设,I/O空间有I/O空间访问的接口,内存空间有内存空间访问的接口。当然某些外设不但有寄存器,还有内存,也就是I/O内存,比如EHCI/OHCI,它们需要映射到内存空间。但是不管映射到哪个空间,访问I/O端口还是I/O内存,CPU必须知道它们映射后的地址,不然没有办法配合交流。
上面表里中间的那些Base Address保存的就是PCI设备里I/O内存或I/O端口的首尾位置还有长度,驱动里使用时要首先把它们给读出来,如果要映射到I/O空间,则要根据读到的值向系统申请I/O端口资源,如果要映射到内存空间,除了要申请内存资源,还要使用ioremap等进行映射。
96行的rsrc_start和97行的rsrc_len保存的就是从表里读出来的host controller的I/O端口或内存的首地址和长度,95行的regs保存的是调用ioremap_nocache映射后的内存地址。
98行,power_budget,能够提供的电流。
101行,*pool [HCD_BUFFER_POOLS],几个dma池。因为HCD_BUFFER_POOLS在100行定义为4,所以这里就表示每个主机控制器可以有4个dma池。
我们知道主机控制器是可以进行DMA传输的,镜头再回到前面的struct urb,那里有两个成员transfer_dma和setup_dma,当时只是说你可以使用usb_buffer_alloc分配好dma缓冲区给它们,然后再告诉HCD你的urb已经有了,HCD就可以不用再进行复杂的DMA映射了,并没有提到你这个获取的dma缓冲区是从哪里来的。俺都暗示到这种地步了,你用屁股也能猜出来是从这里所说的dma池子里来的了。
混了这么久,俺对池子还是有比较美好的回忆的,还亲手搭建过线程池、数据库连接池等等池子,像线程啊数据库连接啊什么的创建销毁的时候不是比较的耗资源么,就先建个池子预先创建好一批线程什么的放里边儿,用的时候就从里面取出来,不用的时候就再放里面,用的越多就越划的来,号称将负担均分了。就像皇马那种俱乐部的会员制,你小贝大罗不是贵么,咱不怕,有千千万万个会员顶着,分到每个人头上就只相当于掉几根头发而已,人多力量大在哪儿都是适用的,哪像俺们米兰只有一个吹牛皮的老贝,小罗、埃托奥谁都想要谁又谁都没来。所以说,池子是多么的重要啊。
当然,dma池没这么幼稚,它还有其它的内涵。一般来说DMA映射获得的都是以页为单位的内存,urb需要不了这么大,如果需要比较小的DMA缓冲区,就离不开DMA池了。还是看看主机控制器的这几个池子是怎么创建的,在buffer.c文件里
这里首先要判断下这个主机控制器支持不支持DAM,如果不支持的话再创建什么DMA池就是纯粹无稽之谈了。如果支持DMA,就逐个适用dma_pool_alloc来创建DMA池,如果创建失败了,就调用同一个文件里的hcd_buffer_destroy来将已经创建成功的池子给销毁掉。
这里调用的dma_pool_destroy和上面的dma_pool_alloc也是相映成趣,都是DMA池子领域的小白领。带翅膀的丘比特说了,每个人的人生都要找到四个人,第一个是自己,第二个是你最爱的人,第三个是最爱你的人,第四个是共度一生的人。他还说了,每个DMA池子都要找到四个函数,一个用来创建,一个用来销毁,一个用来取内存,一个用来放内存。上边儿只遇到了创建和销毁的,还少两个取和放的,再去同一个文件里找找
又可以找到两个dma_pool_alloc和dma_pool_free,现在可以凑齐四个了。不过咱们不用管它们四个到底什么长相,只要它们能够干活儿就成了,这里要注意的是几个问题。第一个是即使你的主机控制器不支持DMA,这几个函数也是可以用的,只不过创建的不是DMA池子,取的也不是DMA缓冲区,此时,DMA池子不存在,hcd_buffer_alloc获取的只是适用kmalloc申请的普通内存,当然相应的,你必须在它没有利用价值的时候使用hcd_buffer_free将它释放掉。
第二个问题是size的问题,也是每个男人女人的问题。看到它们里面都有个pool_max吧,这是个同一个文件里定义的数组
这个数组里定义的就是四个池子中每个池子里保存的DMA缓冲区的size。注意这里虽说只定义了四种size,但是并不说明你使用hcd_buffer_alloc获取DMA缓冲区的时候不能指定更大的size,如果谁谁太贪心了,欲望太强烈了,这几个池子都满足不了她的要求,那就会使用dma_alloc_coherent为她建立一个新的DMA映射。还有,每个人的情况都不一样,不可能都会完全恰好和上面定义的四种size一致,那也不用怕,这不是病,使用这个size获取DMA缓冲区的时候,池子会选择一个略大一些的回馈过去。
还是让咱们抛开size,回到struct usb_hcd中来,103行,state,主机控制器的状态,紧挨着它的下面那些行就是相关的可用值和宏定义。咱们自己的状态还都稀里糊涂的,就先不说它们了。
126行,如果不是有短暂失忆症或选择性失忆症的话,是会知道这是什么意思用来干吗的。