【转】Linux那些事儿之我是U盘(18)冬天来了,春天还会远吗?(二)

时间:2021-04-14 14:34:31

我们打开unusual_devs.h,随便看一下,发现里边就是很多一个个UNUSUAL_DEV,每一行就是这么一个宏,毫无疑问它对应一种设备,我们从其中挑一个来看,比如挑一个三星的吧,过去在Intel的时候,前辈们会说,若不是当初我们对自己太自信了,这个世界上又怎么有三星的生存空间.说的是上世纪末,Intel决定提高flash产品的价格,Nokia这个大客户不干了,它想找别人,一个叫三星的小公司鬼魅般的出现了,没有人相信这样一个小公司能够满足Nokia,可是,韩国人,韩国人的韧劲不仅仅是体现在足球场上.于是,世界上有了三星,Nokia养活了三星,Intel,flash这一领域失去了一个重要的客户,CEO也为此引咎辞职了.而下面这个设备,正是来自三星的一个flash产品.

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

    712 UNUSUAL_DEV( 0x0839, 0x000a, 0x0001, 0x0001,

    713                 "Samsung",

    714                 "Digimax 410",

    715                 US_SC_DEVICE, US_PR_DEVICE, NULL,

716                 US_FL_FIX_INQUIRY),

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

UNUSUAL_DEV这个宏被定义过两次,当然,#define了之后再下一次#define之前有一个#undef,否则就重复定义了.storage_usb_ids之前,它的定义是

    116 #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, /

    117                     vendorName, productName,useProtocol, useTransport, /

    118                     initFunction, flags) /

    119 { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax) }

USB_DEVICE_VER的定义在include/linux/usb.h,

    448 /**

    449  * USB_DEVICE_VER - macro used to describe a specific usb device with a version range

    450  * @vend: the 16 bit USB Vendor ID

    451  * @prod: the 16 bit USB Product ID

    452  * @lo: the bcdDevice_lo value

    453  * @hi: the bcdDevice_hi value

    454  *

    455  * This macro is used to create a struct usb_device_id that matches a

    456  * specific device, with a version range.

    457  */

    458 #define USB_DEVICE_VER(vend,prod,lo,hi) /

    459         .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, .idVendor = (vend), .idProduct = (prod), .bcdDevice_lo         = (lo), .bcdDevice_hi = (hi)

所以这行最终出现在storage_usb_ids中的意思就是令match_flags USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,idVendor0x0839,idProduct0x000a,bcdDevice_lo0x0001,bcdDevice_hi0x0001.

而在us_unusal_dev_list这张表之前,UNUSUAL_DEV又被定义为:

    168 #undef UNUSUAL_DEV

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

    170                     vendor_name, product_name, use_protocol, use_transport, /

    171                     init_function, Flags) /

    172 { /

    173         .vendorName = vendor_name,      /

    174         .productName = product_name,    /

    175         .useProtocol = use_protocol,    /

    176         .useTransport = use_transport,  /

    177         .initFunction = init_function,  /

    178         .flags = Flags, /

    179 }

Ok.这样这个宏的意思又是令vendorName"Samsung",productName"Digimax 410",useProtocolUS_SC_DEVICE, useTransportUS_PR_DEVICE,initFunctionNULL,flagUS_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:

    121 static struct usb_device_id storage_usb_ids [] = {

    122

    123 #       include "unusual_devs.h"

    124 #undef UNUSUAL_DEV

us_unusual_dev_list:

    181 static struct us_unusual_dev us_unusual_dev_list[] = {

    182 #       include "unusual_devs.h"

    183 #       undef UNUSUAL_DEV

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

472         struct us_unusual_dev *unusual_dev = &us_unusual_dev_list[id_index];
    473         struct usb_device_id *id = &storage_usb_ids[id_index];

这样,unusual_devid就各取所需了.下面我们将会用到这两个指针.暂且不表.

总结陈词,最后具体解释一下这行为三星这款数码相机写的语句,

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_lobcdDevice_hi共同组成一个具体设备的编号(device release number),bcd就意味着这个编号是二进制的格式.

2.               vendorNameproductName不用再说了, "Samsung""Digimax 410".

3.               useProtocolUS_SC_DEVICE, useTransportUS_PR_DEVICE,这种情况就说明对于这种设备,它属于什么subclass,它使用什么通信协议,得从设备描述符里边去读取,它都写在那里边了.一会我们会看到我们的代码中会如何判断这个的.

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

     63 UNUSUAL_DEV(  0x03f0, 0x0207, 0x0001, 0x0001,

     64                 "HP",

     65                 "CD-Writer+ 8200e",

     66                 US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0),

     67

     68 UNUSUAL_DEV(  0x03f0, 0x0307, 0x0001, 0x0001,

     69                 "HP",

     70                 "CD-Writer+ CD-4e",

     71                 US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0),

5.               flag等于US_FL_FIX_INQUIRY,这个flag可以设为很多值,这个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这个文件的长度也是慢慢在增长.