从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 {
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;
}

设备那条路已经走过了,现在走走接口那条路。接口驱动的for_devices在usb_register _driver()里被初始化为0,所以这个把门儿的会痛痛快快的放行,继续往下走,遇到一对儿似曾相识的宏to_usb_interface和to_usb_driver,之所以说似曾相识,是因为早先已经遇到过一对儿to_usb_device和to_usb_device_driver。再往下走,就是两个函数usb_match_id和usb_match_dynamic_id,它们都是用来完成实际的匹配工作的,只不过前一个是从驱动的id_table里找,看接口是不是被驱动所支持,后一个是从驱动的动态id链表dynids里找。将id_table放在一个比较高的优先级的位置,从它里面找不到接口了才再从动态id链表里找。

当时讲到struct usb_driver结构的时候并没有详细讲它里面表示动态id的那个结构体struct usb_dynids,所以现在补充一下,这个结构的定义在include/linux/usb.h里

/* Stuff for dynamic usb ids */

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函数。

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->bDeviceClass || id->bInterfaceClass ||
id->driver_info; id++) {
if (usb_match_one_id(interface, id))
return id;
} return NULL;
}

参数id指向的是驱动的那个设备花名册,即id_table,如果它为空,那肯定就是不可能会匹配成功了,接下来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()究竟是怎么匹配的吧。

/* returns 0 if no match, 1 if match */
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; /* The interface class, subclass, and protocol 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)))
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; return 1;
}

此时函数中的这个id指向的就是驱动id_table里的某一项了。

intf获得接口采用的设置,设置里可是有接口描述符的,要匹配接口和驱动,接口描述符里的信息是必不可少的。

interface_to_usbdev从接口的struct usb_interface结构体获得usb设备的struct usb_device结构体。

#define interface_to_usbdev(intf) \

container_of(intf->dev.parent, struct usb_device, dev)

usb设备和它里面的接口是怎么关联起来的呢?就是上面的那个parent,接口的parent早在usb_generic_driver的generic_probe函数向设备模型提交设备里的每个接口的时候就被初始化好了,而且指定为接口所在的那个usb设备。这么一回顾,interface_to_usbdev的意思就很明显了。

这里又冒出来个usb_match_device(),这里虽说是在接口和接口驱动之间匹配,但是接口的parent也是必须要符合条件的,这即合情也合理啊,你好不容易鼓足了勇气向一个走在大街上一见钟情的mm表白,你觉得mm的第一反应是什么?依照行规,很可能就是:你爸是干吗的?是大款么?是当官的么?你要说不,那就别等第二反应了。所以说接口要想得到驱动的芳心,自己的parent符合驱动的条件也是很重要的,usb_match_device()就是专门来匹配接口parent的。同样在driver.c里定义

/* returns 0 if no match, 1 if match */
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的意思就很关键,这里再说一下这个match_flags。驱动的花名册里每个设备都对应了一个struct usb_device_id结构体,这个结构体里有很多字段,都是驱动设定好的条条框框,接口必须完全满足里面的条件才能够被驱动所接受,所以说匹配的过程就是检查接口是否满足这些条件的过程。当然你可以每次都按照id的内容一个一个的比较下去,但是经常来说,一个驱动往往只是想设定其中的某几项,并不要求struct usb_device_id结构里的所有那些条件都要满足,萝卜白菜各有所爱么,有的人觉得你有身体就够了,有的人觉得你有钱就够了,有得人觉得你不但得有身体有钱还要有权,如果你运气好,还可能碰到个只要你有时间的。match_flags就是为了方便各种各样的需求而生的,驱动可以将自己的条件组合起来,match_flags的每一位对应一个条件,驱动care哪个条件了,就将那一位置1,否则就置0。当然,内核里对每个驱动可能会care的条件都定义成了宏,供驱动去组合,它们都在include/linux/mod_devicetable.h里定义

/* Some useful macros to use to create struct usb_device_id */

#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函数usb_device_match就会返回1表示匹配成功,之后接着就会去调用驱动的probe函数做更深入的处理,什么样的处理?这是每个驱动才知道的事情,反正到此为止,core的任务是已经圆满完成了。

这个core的故事,从match开始,到match结束,它虽说不会遍及core的边边角角所有部分,但应该也有那么十之七八。在match的两端是设备和设备的驱动,是接口和接口的驱动,这个故事里遇到的人,遇到的事,早就安排在那里了,由不得我们去选择。

usb驱动开发之大结局的更多相关文章

  1. HarmonyOS USB DDK助你轻松实现USB驱动开发

    HDF(Hardware Driver Foundation)驱动框架是HarmonyOS硬件生态开放的基础,为开发者提供了驱动加载.驱动服务管理和驱动消息机制等驱动能力,让开发者能精准且高效地开发驱 ...

  2. 庖丁解牛:USB 驱动开发技术彻底解密

    我们知道如果开发工程师不懂RS232 肯定会让人笑话可以想象面向未来USB 接口无处不在因此掌握USB 的原理固件编程及其驱动开发技术势必成为当务之急USB 即插即用的优点和灵活性运用于各种电子产品现 ...

  3. usb驱动开发14之设备生命线

    直接看代码吧. /*-------------------------------------------------------------------*/ /** * usb_submit_urb ...

  4. usb驱动开发10之usb_device_match

    在第五节我们说过会专门分析函数usb_device_match,以体现模型的重要性.同时,我们还是要守信用的. 再贴一遍代码,看代码就要不厌其烦. static int usb_device_matc ...

  5. usb驱动开发1之学习准备

    此系列是http://blog.csdn.net/fudan_abc/博文的整理,同时加入了自己的理解.很敬佩fudan_abc的文章,仔细学习和分析受益很多.注:fundan_abc所分析linux ...

  6. 转: 嵌入式linux下usb驱动开发方法--看完少走弯路【转】

    转自:http://blog.csdn.net/jimmy_1986/article/details/5838297 嵌入式linux下的usb属于所有驱动中相当复杂的一个子系统,要想将她彻底征服,至 ...

  7. usb驱动开发24之接口驱动

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

  8. USB驱动开发大全【转】

    本文转载自:http://www.360doc.com/content/12/0504/19/8363527_208666082.shtml 编写USB驱动程序步骤:1所有usb驱动都必须创建主要结构 ...

  9. usb驱动开发15之设备生命线

    总算是进入了HCD的片儿区,既然来到一个片区,怎么都要去拜会一下山头几个大哥吧.,先回忆一些我们怎么到这里的?给你列举一个调用函数过程usb_control_msg->usb_internal_ ...

随机推荐

  1. python基础之条件控制与循环

    Python3 条件控制 Python条件语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块. 计算机之所以能做很多自动化的任务,因为它可以自己做条件判断. 比如,输入用户 ...

  2. 国内IT软件开发人员现状

             首先在这里讨论的是国内的大陆地区.在今天这个中国IT环境下,开发人员出路何在?一个优秀开发人,应该有致力于编写优雅代码,让别人读得懂,具有可读性,可测试性的代码,不仅仅是可以运行的代 ...

  3. Linux Ctrl+c与ctrl+z的区别

    提问:CTRL-Z和CTRL-C区别?回答:CTRL-Z和CTRL-C都是中断命令,但是他们的作用却不一样.CTRL-C是强制中断程序的执行,而CTRL-Z的是将任务中断,但是此任务并没有结束,他仍然 ...

  4. 4、ASP.NET MVC入门到精通——NHibernate构建一个ASP.NET MVC应用程序

    下周就去办理离职手续了,之前没有使用过NHibernate,只知道NHibernate是一种ORM框架,但是听说新公司是使用NHibernate在做项目,所以,我就网上找资料学习一下NHibernat ...

  5. 把普通对象转换成json格式的对象

    1.什么叫做JSON?JSON只是一种数据格式(它不是一种新的数据类型) var obj = {name: "中国", age: 5000};//->普通格式的对象 var ...

  6. Sass-也许你想和CSS玩耍起来(上篇)

    我们努力,我们坚持,共勉! 众所周知,css其实不是一门编程语言,熟悉的人都知道css全称Cascading Style Sheets(层叠样式表)是一种用来表现HTML(标准通用标记语言的一个应用) ...

  7. 一些有用的SAP技术TCODE

    Background Processing RZ01 Job Scheduling Monitor SM36 Schedule Background Job SM36WIZ Job definitio ...

  8. MyEclipse 2016 CI 3发布

    JSjet 语法高亮 受够了Eclipse中的JavaScript编码体验?那就来试试MyEclipse 2016 CI 3.JSjet改进了JavaScript编码的语法高亮功能,大大提升了代码的可 ...

  9. 环信SDK报错处理方法obtain an updated library from the vendor, or disable bitcode for this target. for archit

    ld: '/Users/momo/Desktop/ThreeFingers/Pods/EaseMobSDKFull/EaseMobSDKFull/lib/libEaseMobClientSDK_arm ...

  10. C语言基于GTK+Libvlc实现的简易视频播放器

    小编心语:现下,各种视频播放软件层出不穷,竞争也越演越烈,不知道大家有木有这个想法,小编有时在想能不能做一款属于自己的视频播放器呢~小编特意去实验楼,整理出了这篇关于如何实现简易视频播放器的博文.简易 ...