usb驱动开发之大结局

时间:2021-12-28 09:43:57

从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()相遇,看看它怎么在接口和驱动之间搭起那座桥。

usb驱动开发之大结局usb驱动开发之大结局
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函数。

usb驱动开发之大结局usb驱动开发之大结局
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()究竟是怎么匹配的吧。

usb驱动开发之大结局usb驱动开发之大结局
/* 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里定义

usb驱动开发之大结局usb驱动开发之大结局
/* 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. 庖丁解牛&colon;USB 驱动开发技术彻底解密

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

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

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

  3. usb驱动开发10之usb&lowbar;device&lowbar;match

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

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

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

  5. 转&colon; 嵌入式linux下usb驱动开发方法--看完少走弯路【转】

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

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

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

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

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

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

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

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

    函数usb_control_msg完成一些初始化后调用了usb_internal_control_msg之后就free urb.剩下的活,全部留给usb_internal_control_msg去做了 ...

随机推荐

  1. 记录那些我不清楚的知识点(HTML)

    <div class="link"><a href="http://www.baidu.com/" target="iframeHt ...

  2. java四种内部类

    内部类有成员内部类,局部内部类,匿名内部类,静态内部类. 1,成员内部类package innerClass; public class InnerClassTest { String s1=&quo ...

  3. MySQL基础之第2章 Windows平台下安装与配置MySQL

    2.1.msi安装包 2.1.1.安装 特别要注意的是,安装前要删除原来的my.ini和原来的data目录,改名也行,不然在最后一步会“apply security settings”报个1045错误 ...

  4. oracle是数据库的学习第一节:数据库的安装

    一.本地oracle服务器 1.安装oracle服务器,可以到oracle官方网站上下载与自己电脑匹配的服务器,一般用10g,或者11g; 2.打开cmd,打开sql*plus,之后可以写SQL语句了 ...

  5. Linux中&lowbar;ALIGN宏背后的原理——内存对齐

    转载自: http://englishman2008.blog.163.com/blog/static/2801290720114210254690/ 1. 原理    int a;     int ...

  6. Android系统源代码下载

    最近,我在研究android,所以想下载android源代码看看.按照http://source.android.com/source/downloading.html 这个页面所提示的步骤在下载源代 ...

  7. sqlserver数据库类型对应Java中的数据类型

    SQL Server 类型 JDBC 类型 (java.sql.Types) Java 语言类型 bigint BIGINT long timestamp binary BINARY byte[] b ...

  8. iOS之内存分析

    静态内存分析(Product->Analyze) 静态内存分析是不运行程序,直接对代码进行分析. 但是没有真正分配内存,根据代码的上下文的语法结构,来分析是否有内存泄露 缺点:不一定准确,但是如 ...

  9. JDBC事务的处理-----模拟银行转账业务

    定义: 数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成. 概要: 一个数据库事务通常包含了一个序列的对数据库的读/写操作.它的存在包含有以下两个目的: ...

  10. Thread&period;sleep&lpar;1000&rpar;

    public class Wait { public static void main(String[] args) { System.out.println(System.currentTimeMi ...