Linux那些事儿 之 戏说USB(大结局)还是那个match
从上次在几米的向左走向右走遇到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相关推荐
- Linux那些事儿 之 戏说USB(19)设备
转载地址:http://blog.csdn.net/fudan_abc/article/details/1807800 第一眼看到struct usb_device这个结构,我仿佛置身于衡山路的酒吧里 ...
- 【转】Linux那些事儿 之 戏说USB(19)设备
第一眼看到struct usb_device这个结构,我仿佛置身于衡山路的酒吧里,盯着舞池里扭动的符号,眼神迷离. 交大里苟了几年,毕业了又是住在学校附近的徐虹北路上,沿着虹桥路走过去,到徐家汇不过1 ...
- Linux那些事儿 之 戏说USB(33)字符串描述符
关于字符串描述符,前面的前面已经简单描述过了,地位仅次于设备/配置/接口/端点四大描述符,那四大设备必须得支持,而字符串描述符对设备来说则是可选的. 这并不是就说字符串描述符不重要,对咱们来说,字符串 ...
- Linux那些事儿 之 戏说USB(28)设备的生命线(十一)
现在已经使用GET_DESCRIPTOR请求取到了包含一个配置里所有相关描述符内容的一堆数据,这些数据是raw的,即原始的,所有数据不管是配置描述符.接口描述符还是端点描述符都彼此的挤在一起,所以得想 ...
- Linux那些事儿 之 戏说USB(27)设备的生命线(十)
跟着设备的生命线走到现在,我算是明白了,什么东西的发展都是越往后越高级越复杂.再给张小表,看看现在和上次那张表出现的时候有什么变化. state USB_STATE_ADDRESS sp ...
- Linux那些事儿 之 戏说USB(25)设备的生命线(八)
回到struct usb_hcd,继续努力的往下看. 7行,又见kref,usb主机控制器的引用计数.struct usb_hcd也有自己专用的引用计数函数,看drivers/usb/core/hcd ...
- Linux那些事儿 之 戏说USB(15)设备
struct usb_device结构冗长而又杂乱 include/linux/usb.h struct usb_device {int devnum;char devpath[16];u32 rou ...
- Linux那些事儿 之 戏说USB(25)设备的生命线(四)
转载地址:http://blog.csdn.net/fudan_abc/article/details/1819919 洗澡是屁股享福,脑袋吃苦:看电影是脑袋享福,屁股吃苦:看内核代码是脑袋.屁股都吃 ...
- Linux那些事儿 之 戏说USB(34)接口的驱动
从上节的上节我们已经知道,usb_generic_driver在自己的生命线里,以一己之力将设备的各个接口送给了linux的设备模型,让usb总线的match函数,也就是usb_device_matc ...
最新文章
- 1032 Sharing
- 启动mysqld报 mysql the server quit without updating pid file
- 如何使用robots禁止各大搜索引擎爬虫爬取网站
- 二、“究恒常之宇宙,成一家之学说”
- SerialPort comstat is being used without defining
- echarts 弹出放大_Echarts图标增加全屏/放大功能
- MySQL保存或更新 saveOrUpdate
- Codeforces Round #470 Div. 1
- Android thumbnail 图片的获得及与原始图片的映射
- mysql 5.5加服务器_mysql 5.5 安装配置方法图文教程
- 2022版首发,阿里Java开发手册(黄山版).PDF
- python常见题_Python常见面试题汇总(根据面试总结)
- 青春(2010-05-28 04:30:39)韩寒
- Android安装步骤
- HTML5前端期末大作业 HTML+CSS+JavaScript防锤子手机商城官网 web前端网页设计实例 企业网站制作
- 计算机网路基础课后习题答案 主编刘建友
- C语言 查找书籍(结构体)
- win10安装PyPESQ库
- [CF538H]Summer Dichotomy
- 955.WLB 不加班公司名单新增 5 家公司!2021 最新版!
热门文章
- python list 的乘法
- debug.keystore not found for signing config 'debug'.
- web渗透测试基本步骤
- asp.net[web.config] httphandlers 与实现自由定义访问地址
- 「日常训练」Bad Luck Island(Codeforces Round 301 Div.2 D)
- spec 2016使用
- 机器学习模型质量评价标准 — 精准率、召回率
- 搭建Git服务器教程转载
- 電子商務新紀元-WebService With BizSnap
- php 5.4 iis6,WIN2003+IIS6+FastCGI+PHP5.4的安装配置