Linux那些事儿 之 戏说USB(大结局)还是那个match

来源:互联网 发布:精点数据 编辑:程序博客网 时间:2024/04/30 13:10
从上次在几米的向左走向右走遇到usb总线的那个match函数usb_device_match()开始到现在,遇到了设备,遇到了设备驱动,遇到了接口,也遇到了接口驱动,期间还多次遇到usb_device_match()。
每个设备也都有一条共同之路,与hub初恋,失身于usb_generic_driver,嫁给了接口驱动,被usb总线保养。设备没有真正自由过,刚开始时在Default状态动弹不得,稍后步入Address,无论外头风光多好,都得与usb_generic_driver长厢厮守,没得选择,终于达到了Configured,又必须为自己的接口殚精竭虑,以便usb_device_match()能够为它们找一个好人家。
不管怎么说,在这里我们会再次与usb_device_match()相遇,看看它怎么在接口和驱动之间搭起那座桥。
static int usb_device_match(struct device *dev, struct device_driver *drv){/* devices and interfaces are handled separately */if (is_usb_device(dev)) {/* interface drivers never match devices */if (!is_usb_device_driver(drv))return 0;/* TODO: Add real matching code */return 1;} else if (is_usb_interface(dev)) {struct usb_interface *intf;struct usb_driver *usb_drv;const struct usb_device_id *id;/* device drivers never match interfaces */if (is_usb_device_driver(drv))return 0;intf = to_usb_interface(dev);usb_drv = to_usb_driver(drv);id = usb_match_id(intf, usb_drv->id_table);if (id)return 1;id = usb_match_dynamic_id(intf, usb_drv);if (id)return 1;}return 0;}
设备那条路已经走过了,现在走走13行接口那条路。19行,接口驱动的for_devices在usb_register _driver()里被初始化为0,所以这个把门儿的会痛痛快快的放行,继续往下走,22行,遇到一对儿似曾相识的宏to_usb_interface和to_usb_driver,之所以说似曾相识,是因为早先已经遇到过一对儿to_usb_device和to_usb_device_driver。这两对儿一对儿用于接口和接口驱动,一对儿用于设备和设备驱动,意思都很直白,还是看看include/linux/usb.h里的定义
#defineto_usb_interface(d) container_of(d, struct usb_interface, dev)
#defineto_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)
再往下走,就是两个函数usb_match_id和usb_match_dynamic_id,它们都是用来完成实际的匹配工作的,只不过前一个是从驱动的id_table里找,看接口是不是被驱动所支持,后一个是从驱动的动态id链表dynids里找。驱动的id表分id_table和dynids两种。显然25~31这几行的意思就是将id_table放在一个比较高的优先级的位置,从它里面找不到接口了才再从动态id链表里找。
当时讲到struct usb_driver结构的时候并没有详细讲它里面表示动态id的那个结构体struct usb_dynids,但是做人要厚道,不能太CCTV,所以现在补充一下,这个结构的定义在include/linux/usb.h里
struct usb_dynids {spinlock_t lock;struct list_head list;};
它只有两个字段,一把锁,一个链表,都是在usb_register _driver()里面初始化的,这个list是驱动动态id链表的头儿,它里面的每个节点是用另外一个结构struct usb_dynid来表示
struct usb_dynid {struct list_head node;struct usb_device_id id;};
这里面就出现了一个struct usb_device_id结构体,也就是设备的id,每次添加一个动态id,就会向驱动的动态id链表里添加一个struct usb_dynid结构体。你现在应该可以想像到usb_match_id和usb_match_dynamic_id这两个函数除了查找的地方不一样,其它应该是没什么差别的。所以接下来咱们只深入探讨一下usb_match_id函数,至于usb_match_dynamic_id(),如果你实在无聊暂时找不到人生目标也可以去看看。它们都在driver.c里定义
const struct usb_device_id *usb_match_id(struct usb_interface *interface, const struct usb_device_id *id){/* proc_connectinfo in devio.c may call us with id == NULL. */if (id == NULL)return NULL;/* It is important to check that id->driver_info is nonzero,   since an entry that is all zeroes except for a nonzero   id->driver_info is the way to create an entry that   indicates that the driver want to examine every   device and interface. */for (; id->idVendor || id->idProduct || id->bDeviceClass ||       id->bInterfaceClass || id->driver_info; id++) {if (usb_match_one_id(interface, id))return id;}return NULL;}
5行,参数id指向的是驱动的那个设备花名册,即id_table,如果它为空,那肯定就是不可能会匹配成功了。
13行,你可能会问为什么这里不详细介绍一下struct usb_device_id结构,主要是《我是U盘里》已经说得非常之详细和有趣了,俺这里实在没必要耗费时间和口舌去说它,另一方面,它里面的那些元素都相当的暴露和直白,我相信依你的智商一眼就能明白个八九不离十。
那么这个for循环就是轮询设备花名册里的每个设备,如果符合了条件id->idVendor || id->bDeviceClass || id->bInterfaceClass || id->driver_info,就调用函数usb_match_one_id做深层次的匹配。本来,在动态id出现之前这个地方是没有usb_match_one_id这么一个函数的,所有的匹配都在这个for循环里直接做了,但是动态id出现之后,同时出现了前面提到的usb_match_dynamic_id函数,要在动态id链表里做同样的匹配,这就要避免代码重复,于是就将那些重复的代码提出来,组成了usb_match_one_id函数。
for循环的条件里可能出现的一种情况是,id的其它字段都为空,只有driver_info字段有实实在在的内容,这种情况下匹配是肯定成功的,不信的话等会儿你可以看usb_match_one_id(),这种驱动对usb接口来说是比较随便的那种,不管啥接口都能和她对得上眼,为什么会出现这种情况?咱们已经知道,匹配成功后,接着就会调用驱动自己的probe函数,驱动在它里面还会对接口做进一步的检查,如果真出现了这里所说的情况,意思也就是驱动将所有的检查接口,和接口培养感情的步骤都揽在自己的probe函数里了,它会在那个时候将driver_info的内容取出来,然后想怎么处理就怎么处理,本来么,id里边儿的driver_info就是给驱动保存数据用的。
还是看看usb_match_one_id()究竟是怎么匹配的吧,定义也在driver.c里
int usb_match_one_id(struct usb_interface *interface,     const struct usb_device_id *id){struct usb_host_interface *intf;struct usb_device *dev;/* proc_connectinfo in devio.c may call us with id == NULL. */if (id == NULL)return 0;intf = interface->cur_altsetting;dev = interface_to_usbdev(interface);if (!usb_match_device(dev, id))return 0;return usb_match_one_id_intf(dev, intf, id);}
8行,这个id指向的就是驱动id_table里的某一项了。
11行,获得接口采用的设置,设置里可是有接口描述符的,要匹配接口和驱动,接口描述符里的信息是必不可少的。
12行,从接口的struct usb_interface结构体获得usb设备的struct usb_device结构体,interface_to_usbdev的定义在include/linux/usb.h里
#defineto_usb_device(d) container_of(d, struct usb_device, dev)static inline struct usb_device *interface_to_usbdev(struct usb_interface *intf){return to_usb_device(intf->dev.parent);}
usb设备和它里面的接口是怎么关联起来的呢?就是上面的那个parent,接口的parent早在usb_generic_driver的generic_probe函数向设备模型提交设备里的每个接口的时候就被初始化好了,而且指定为接口所在的那个usb设备。这么一回顾,interface_to_usbdev的意思就很明显了。
14行,这里又冒出来个usb_match_device(),接口和驱动之间的感情还真不是那么好培养的,一层一层的。不过既然存在就是有来头的,它也不会毫无根据的出现,这里虽说是在接口和接口驱动之间匹配,但是接口的parent也是必须要符合条件的,这即合情也合理啊,你好不容易鼓足了勇气向一个走在大街上一见钟情的mm表白,你觉得mm的第一反应是什么?依照行规,很可能就是:你爸是干吗的?是大款么?是当官的么?你要说不,那就别等第二反应了。所以说接口要想得到驱动的芳心,自己的parent符合驱动的条件也是很重要的,usb_match_device()就是专门来匹配接口parent的。同样在driver.c里定义
int usb_match_device(struct usb_device *dev, const struct usb_device_id *id){if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&    id->idVendor != le16_to_cpu(dev->descriptor.idVendor))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&    id->idProduct != le16_to_cpu(dev->descriptor.idProduct))return 0;/* No need to test id->bcdDevice_lo != 0, since 0 is never   greater than any unsigned number. */if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&    (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice)))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&    (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice)))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&    (id->bDeviceClass != dev->descriptor.bDeviceClass))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&    (id->bDeviceSubClass != dev->descriptor.bDeviceSubClass))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&    (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))return 0;return 1;}
这个函数采用了排比的修辞手法,美观的同时也增加了可读性。这一个个的if条件里都有一部分是将id 的match_flags和一个宏相与,所以弄明白match_flags的意思就很关键。罢了罢了,本来说不再浪费口舌在id里的那些字段上了,不过为了减少你蓦然回首的次数,这里再说一下这个match_flags。
驱动的花名册里每个设备都对应了一个struct usb_device_id结构体,这个结构体里有很多字段,都是驱动设定好的条条框框,接口必须完全满足里面的条件才能够被驱动所接受,所以说匹配的过程就是检查接口是否满足这些条件的过程。
当然你可以每次都按照id的内容一个一个的比较下去,但是经常来说,一个驱动往往只是想设定其中的某几项,并不要求struct usb_device_id结构里的所有那些条件都要满足。match_flags就是为了方便各种各样的需求而生的,驱动可以将自己的条件组合起来,match_flags的每一位对应一个条件,驱动care哪个条件了,就将那一位置1,否则就置0。当然,内核里对每个驱动可能会care的条件都定义成了宏,供驱动去组合,它们都在include/linux/mod_devicetable.h里定义
#define USB_DEVICE_ID_MATCH_VENDOR0x0001#define USB_DEVICE_ID_MATCH_PRODUCT0x0002#define USB_DEVICE_ID_MATCH_DEV_LO0x0004#define USB_DEVICE_ID_MATCH_DEV_HI0x0008#define USB_DEVICE_ID_MATCH_DEV_CLASS0x0010#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS0x0020#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL0x0040#define USB_DEVICE_ID_MATCH_INT_CLASS0x0080#define USB_DEVICE_ID_MATCH_INT_SUBCLASS0x0100#define USB_DEVICE_ID_MATCH_INT_PROTOCOL0x0200
你用自己的火眼金睛很容易的就能看出来这些数字分别表示了一个u16整数,也就是match_flags中的某一位。驱动比较在意哪个方面,就可以将match_flags里对应的位置1,在和接口匹配的时候自动就会去比较驱动设置的那个条件是否满足。那整个usb_match_device()函数就没什么说的了,就是从match_flags那里得到驱动都在意哪些条件,然后将设备保存在自己描述符里的自身信息与id里的相应条件进行比较,有一项比较不成功就说明匹配失败,如果一项符合了就接着看下一项,接口parent都满足条件了,就返回1,表示匹配成功了。

还是回到usb_match_one_id()继续往下看,假设你运气还不错,parent满足了驱动的所有条件,那就调用函数usb_match_one_id_intf

int usb_match_one_id_intf(struct usb_device *dev,  struct usb_host_interface *intf,  const struct usb_device_id *id){/* The interface class, subclass, protocol and number should never be * checked for a match if the device class is Vendor Specific, * unless the match record specifies the Vendor ID. */if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC &&!(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&(id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS |USB_DEVICE_ID_MATCH_INT_SUBCLASS |USB_DEVICE_ID_MATCH_INT_PROTOCOL |USB_DEVICE_ID_MATCH_INT_NUMBER)))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&    (id->bInterfaceClass != intf->desc.bInterfaceClass))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&    (id->bInterfaceSubClass != intf->desc.bInterfaceSubClass))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&    (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) &&    (id->bInterfaceNumber != intf->desc.bInterfaceNumber))return 0;return 1;}
8行。这行的意思是,如果接口的parent,usb设备是属于厂商定义的class,也就是不属于storage等等标准的class,就不再检查接口的class,subclass和protocol了,除非match_flags里指定了条件USB_DEVICE_ID_MATCH_VENDOR。16行之后的三个if也不用多说,前面是检查接口parent的,这里就是检查接口本身是不是满足驱动的条件的。

当上面各个函数进行的所有检查都完全匹配时,usb总线的match函数usb_device_match就会返回1表示匹配成功,之后接着就会去调用驱动的probe函数做更深入的处理,什么样的处理?这是每个驱动才知道的事情,反正到此为止,core的任务是已经圆满完成了,咱们的故事也就该结束了。

这个core的故事,从match开始,到match结束,它虽说不会遍及core的边边角角所有部分,但应该也有那么十之七八。在match的两端是设备和设备的驱动,是接口和接口的驱动,这个故事里遇到的人,遇到的事,早就安排在那里了,由不得我们去选择。在人生的路口上,早已经安排了那些人,那些事,决定你向左走还是向右走。既然如此,那就随便走好了,想那么多干什么呢?
0 0
原创粉丝点击