《Linux那些事儿之我是USB》我是U盘(16)冬天来了,春天还会远吗?(二)

来源:岁月联盟 编辑:exp 时间:2011-10-23

 

打开unusual_devs.h吧,之前我们看了它的最后几行,但是如果你仔细看的话会发现最后几行和前面的一些行有着明显的不同。最后几行都是USUAL_DEV的宏,而前面则全是UNUSUAL_DEV的宏,随便看一下,发现每一行就是这么一个宏,毫无疑问它对应一种设备,我们从其中挑一个来看,比如挑一个三星的吧。

 

下面这个设备,正是来自三星的一个Flash产品。

 

1092 /* Submitted by Hartmut Wahl <hwahl@hwahl.de>*/

 

1093 UNUSUAL_DEV( 0x0839, 0x000a, 0x0001, 0x0001,

 

1094                "Sa msung",

 

1095                "Digimax 410",

 

1096                US_SC_DEVICE, US_PR_DEVICE, NULL,

 

1097                US_FL_FIX_INQUIRY),

 

Digimax 410,熟悉数码照相机的人大概对三星的这款410万像素3倍光学变焦的产品不会陌生,不过数码照相机更新得很快,这款在2002年推出的相机如今当然也算是很一般了,市场上卖的话也就在1500元以下,不过当时刚推出时也是3000元到4000元的。我们来看这一行是什么意思。

 

UNUSUAL_DEV这个宏的定义以及它所利用的USB_DEVICE_VER的定义我们前面都已经看到了。

 

所以这段对三星设备的描述的UNUSUAL_DEV最终出现在storage_usb_ids中的意思就是令match_flags为USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,idVendor为0x0839,idProduct为0x000a,bcdDevice_lo为0x0001,bcdDevice_hi为0x0001。可是你会问了,这里我们看到的这个UNUSUAL_DEV里面有10个参数,可是刚才我们看到的USB_DEVICE_VER却只能处理其中的前几个,后面那些怎么办呢?呵呵,你可曾注意到,在数组storage_usb_ids里面,有如下两行:

 

141 #undef UNUSUAL_DEV

 

142 #undef USUAL_DEV

 

明明定义了这两个宏,为什么又undef掉呢?事实上,在storage_usb_ids和us_unusual_dev_list这两个数组之间,我们又看到了下面这一段:

 

161 #define UNUSUAL_DEV(idVendor, idProduct,bcdDeviceMin, bcdDeviceMax, /

 

162               vendor_name, product_name,use_protocol, use_transport, /

 

163              init_function, Flags) /

 

164 { /

 

165    .vendorName= vendor_name,     /

 

166    .productName= product_name,    /

 

167    .useProtocol= use_protocol,    /

 

168    .useTransport= use_transport,  /

 

169    .initFunction= init_function,  /

 

170 }

 

171

 

172 #define USUAL_DEV(use_protocol, use_transport,use_type) /

 

173 { /

 

174   .useProtocol = use_protocol,    /

 

175     .useTransport= use_transport,  /

 

176 }

 

UNUSUAL_DEV和USUAL_DEV竟然被定义了两次。这样对于三星产品来说,这个宏的意思又是令vendorName为“Sa msung”,令productName为“Digimax410”,而useProtocol为US_SC_DEVICE,useTransport为US_PR_DEVICE,initFunction为NULL,flag为US_FL_FIX_INQUIRY。

 

看明白了吗?首先不去管各项的具体含义,至少我们看出来,针对同一个文件,我们使用两次定义UNUSUAL_DEV这个宏的方法,两次利用了它的不同元素。换而言之,UNUSUAL_DEV这个宏本来可以设定10个参数,而storage_usb_ids中需要使用其中的前4个参数,同时us_unusual_dev_list中需要使用其中的后6个参数,所以在unusual_devs.h中定义的一行起了两个作用。这就是为什么不管是storage_usb_ids数组还是us_unusual_dev_list数组,其中都把unusual_devs.h文件给包含了进来。

 

而我们之所以使用两个数组的原因是,storage_usb_ids是提供给USB Core的,它需要比较驱动和设备从而确定设备是被这个驱动所支持的,我们只需要比较四项就可以了,因为这四项已经足以确定一个设备的厂商、产品、序列号。比较这些就够了,而us_unusual_dev_list这个数组中的元素是我们接下来的代码要用的,比如它用什么协议,它有什么初始化函数,所以我们使用了两个数组。而我们需要注意的是,这两个数组中元素的顺序是一样的,所以我们从storage_usb_ids中得到的id_index用于us_unusual_dev_list也是可以的,表示的还是同一个设备。而这也就是我们刚才在get_device_info()->find_unusual()中看到的。

 

482 static struct us_unusual_dev*find_unusual(const struct usb_device_id *id)

 

483 {

 

484   const int id_index = id -storage_usb_ids;

 

485     return&us_unusual_dev_list[id_index];

 

486 }

 

看得出来,id_index可以脚踏两只船,在storage_usb_ids和us_unusual_dev_list之间游刃有余。而find_unusual()这个函数的真实作用就是返回一个us_unusual_dev的指针。

 

494    structus_unusual_dev *unusual_dev = find_unusual(id);

 

这个指针之后就将被用到。

 

最后具体解释一下这行为三星这款数码相机写的语句。

 

(1)关于match_flags,它的值是USB_DEVICE_ID_MATCH_DEVICE_ AND_VERSION。这是一个宏,它就告诉USB Core,要比较这样几个方面,idVendor,idProduct,bcdDevice_lo,bcdDevice_hi,其中idVendor和下面的vendorName是对应的,而idProduct和下面的productName是对应的,业内为每家公司编一个号,这样便于管理,比如三星的编号就是0x0839,那么三星的产品中就会在其设备描述符中idVendor的烙上0x0839。而三星自己的每种产品也会有一个编号,和Digimax 410对应的编号就是0x000a,而bcdDevice_lo和bcdDevice_hi共同组成一个具体设备的编号(Device Release Number),bcd就意味着这个编号是二进制的格式。

 

(2).vendorName和productName为“Sa msung”和“Digimax410”。

 

(3).useProtocol为US_SC_DEVICE,useTransport为US_PR_DEVICE,这种情况就说明对于这种设备,它属于什么SubClass,它使用什么通信协议,得从设备描述符里面去读取,它都写在那里面了。一会儿会看到我们的代码中会如何判断这个的。

 

(4).initFunction等于NULL,这个很有意义的,这个函数就是设备的初始化函数,一般的设备都不需要这个函数,但是有些设备偏要标新立异,它就告诉你,要用我的设备必须先做一些初始化,于是它提供了一个函数,initFunction,当然这是一个函数指针,这里如果不为NULL的话,到时候就会被调用,以后我们会看到代码中对这个指针进行了判断。如果为空不理睬,否则就会执行。比如我们看下面两款设备,它们就分别提供了一个叫做usb_stor_sddr09_init()和usb_stor_sddr09_dpcm_init()的初始化函数,

 

422 #ifdef CONFIG_USB_STORAGE_SDDR09

 

423 UNUSUAL_DEV(  0x04e6, 0x0003, 0x0000, 0x9999,

 

424                "Sandisk",

 

425                "ImageMate SDDR09",

 

426                US_SC_SCSI, US_PR_EUSB_SDDR09, usb_stor_sddr09_init,

 

427                0),

 

428

 

429 /* This entry is from Andries.Brouwer@cwi.nl*/

 

430 UNUSUAL_DEV(  0x04e6, 0x0005, 0x0100, 0x0208,

 

431                "SCM Microsyste ms",

 

432                "eUSB SmartMedia / CompactFlash Adapter",

 

433                US_SC_SCSI, US_PR_DPCM_USB, usb_stor_sddr09_dpcm_init,

 

434                0),

 

435 #endif

 

(5)flag等于US_FL_FIX_INQUIRY,这个flag可以设为很多值,它的存在本身就表示这个设备有一些与众不同,因为一般的设备是不用这个flag的,有这个flag就表明这个设备可能在某些地方需要进行特殊的处理,所以今后在代码中我们会看到突然跳出一句,判断us->flag等于某个数值,如果等于,就执行一些代码;如果不等于,那就不做任何事情。这个flag的存在也使得我们可以方便处理一些设备的Bug,比如正常的设备你问它:“吃了吗?”它就回答:“吃了”。可是不正常的设备可能就会根本不回答,或者回答:“北京房价真高!”于是对于这种设备,可能我们就需要一些专门的代码来对付。具体到这个US_FL_FIX_INQUIRY,这个flag这么一设置,就表明这个设备在接收到INQUIRY命令时会有一些异常的特征,所以以后我们会在代码里看到我们是如何处理它的。设置了这个flag的当然不止是三星的这款相机,别的设备也有可能设置的。

 

(6)既然明白了unusual_devs.h的作用,那么要做的是很显然的一件事情,如果一个厂家推出了一个新的设备,它有一些新的特征,而目前的设备驱动不足以完全支持它,那么厂家首先需要做的事情就是在unusual_devs.h中添加一个UNUSUAL_DEV来定义自己的设备,然后再看是否需要给内核打补丁以及如何打。因此这几年unusual_devs.h这个文件的长度也是慢慢在增长。