从上次在几米的向左走向右走遇到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里的定义

#define  to_usb_interface(d) container_of(d, struct usb_interface, dev)
#define  to_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 nonzeroid->driver_info is the way to create an entry thatindicates that the driver want to examine everydevice 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里

#define  to_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 nevergreater 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_VENDOR       0x0001
#define USB_DEVICE_ID_MATCH_PRODUCT     0x0002
#define USB_DEVICE_ID_MATCH_DEV_LO      0x0004
#define USB_DEVICE_ID_MATCH_DEV_HI      0x0008
#define USB_DEVICE_ID_MATCH_DEV_CLASS       0x0010
#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS    0x0020
#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL    0x0040
#define USB_DEVICE_ID_MATCH_INT_CLASS       0x0080
#define USB_DEVICE_ID_MATCH_INT_SUBCLASS    0x0100
#define USB_DEVICE_ID_MATCH_INT_PROTOCOL    0x0200

你用自己的火眼金睛很容易的就能看出来这些数字分别表示了一个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的两端是设备和设备的驱动,是接口和接口的驱动,这个故事里遇到的人,遇到的事,早就安排在那里了,由不得我们去选择。在人生的路口上,早已经安排了那些人,那些事,决定你向左走还是向右走。既然如此,那就随便走好了,想那么多干什么呢?

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

  1. Linux那些事儿 之 戏说USB(19)设备

    转载地址:http://blog.csdn.net/fudan_abc/article/details/1807800 第一眼看到struct usb_device这个结构,我仿佛置身于衡山路的酒吧里 ...

  2. 【转】Linux那些事儿 之 戏说USB(19)设备

    第一眼看到struct usb_device这个结构,我仿佛置身于衡山路的酒吧里,盯着舞池里扭动的符号,眼神迷离. 交大里苟了几年,毕业了又是住在学校附近的徐虹北路上,沿着虹桥路走过去,到徐家汇不过1 ...

  3. Linux那些事儿 之 戏说USB(33)字符串描述符

    关于字符串描述符,前面的前面已经简单描述过了,地位仅次于设备/配置/接口/端点四大描述符,那四大设备必须得支持,而字符串描述符对设备来说则是可选的. 这并不是就说字符串描述符不重要,对咱们来说,字符串 ...

  4. Linux那些事儿 之 戏说USB(28)设备的生命线(十一)

    现在已经使用GET_DESCRIPTOR请求取到了包含一个配置里所有相关描述符内容的一堆数据,这些数据是raw的,即原始的,所有数据不管是配置描述符.接口描述符还是端点描述符都彼此的挤在一起,所以得想 ...

  5. Linux那些事儿 之 戏说USB(27)设备的生命线(十)

    跟着设备的生命线走到现在,我算是明白了,什么东西的发展都是越往后越高级越复杂.再给张小表,看看现在和上次那张表出现的时候有什么变化. state        USB_STATE_ADDRESS sp ...

  6. Linux那些事儿 之 戏说USB(25)设备的生命线(八)

    回到struct usb_hcd,继续努力的往下看. 7行,又见kref,usb主机控制器的引用计数.struct usb_hcd也有自己专用的引用计数函数,看drivers/usb/core/hcd ...

  7. Linux那些事儿 之 戏说USB(15)设备

    struct usb_device结构冗长而又杂乱 include/linux/usb.h struct usb_device {int devnum;char devpath[16];u32 rou ...

  8. Linux那些事儿 之 戏说USB(25)设备的生命线(四)

    转载地址:http://blog.csdn.net/fudan_abc/article/details/1819919 洗澡是屁股享福,脑袋吃苦:看电影是脑袋享福,屁股吃苦:看内核代码是脑袋.屁股都吃 ...

  9. Linux那些事儿 之 戏说USB(34)接口的驱动

    从上节的上节我们已经知道,usb_generic_driver在自己的生命线里,以一己之力将设备的各个接口送给了linux的设备模型,让usb总线的match函数,也就是usb_device_matc ...

最新文章

  1. 1032 Sharing
  2. 启动mysqld报 mysql the server quit without updating pid file
  3. 如何使用robots禁止各大搜索引擎爬虫爬取网站
  4. 二、“究恒常之宇宙,成一家之学说”
  5. SerialPort comstat is being used without defining
  6. echarts 弹出放大_Echarts图标增加全屏/放大功能
  7. MySQL保存或更新 saveOrUpdate
  8. Codeforces Round #470 Div. 1
  9. Android thumbnail 图片的获得及与原始图片的映射
  10. mysql 5.5加服务器_mysql 5.5 安装配置方法图文教程
  11. 2022版首发,阿里Java开发手册(黄山版).PDF
  12. python常见题_Python常见面试题汇总(根据面试总结)
  13. 青春(2010-05-28 04:30:39)韩寒
  14. Android安装步骤
  15. HTML5前端期末大作业 HTML+CSS+JavaScript防锤子手机商城官网 web前端网页设计实例 企业网站制作
  16. 计算机网路基础课后习题答案 主编刘建友
  17. C语言 查找书籍(结构体)
  18. win10安装PyPESQ库
  19. [CF538H]Summer Dichotomy
  20. 955.WLB 不加班公司名单新增 5 家公司!2021 最新版!

热门文章

  1. python list 的乘法
  2. debug.keystore not found for signing config 'debug'.
  3. web渗透测试基本步骤
  4. asp.net[web.config] httphandlers 与实现自由定义访问地址
  5. 「日常训练」Bad Luck Island(Codeforces Round 301 Div.2 D)
  6. spec 2016使用
  7. 机器学习模型质量评价标准 — 精准率、召回率
  8. 搭建Git服务器教程转载
  9. 電子商務新紀元-WebService With BizSnap
  10. php 5.4 iis6,WIN2003+IIS6+FastCGI+PHP5.4的安装配置