知识屋:更实用的电脑技术知识网站
所在位置:首页 > 操作系统 > linux

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

发布时间:2014-09-05 16:47:21作者:知识屋

 

在整个usb-storage模块的代码中,其最灵魂的部分在一个叫做usb_stor_control_thread()的函数中,而那也自然是我们整个故事的高潮。这个函数的调用有一些特殊,是从usb_stor_acquire_resources()函数进入的,而后者我们即将遇到,它在整部戏中只出现过一次,即storage_probe中,行号为1005的地方。

 

然而在此之前,有四个函数挡在我们面前,它们就是get_device_info,get_transport,get_protocol,get_pipes。如我前面所说,两个人要走到一起,首先要了解彼此,这四个函数就是让驱动去认识设备的。这一点我们从名字上也能看出来。驱动需要知道设备的名字,所以有了get_device_info,驱动需要知道设备是哪一种类型,写代码的人把这些工作分配给了get_transport,get_protocol和get_pipes。

 

实际上,这四个函数,加上之前刚说过的associate_dev()函数,是整个故事中最平淡最枯燥的部分,第一次读这部分代码总让人困惑,怎么没看见一点USB数据通信?完全没有看到USB主机和USB设备是如何在交流的,这是USB吗?这几个函数应该说是给后面做铺垫,红花总要有绿叶配,没有这段代码的铺垫,到了后面USB设备恐怕也无法正常工作吧。不过,一个好消息是,这几个函数我们只会遇见这一次,它们在整个故事中就这么一次露脸的机会,像我们每个人的青春,只有一次,无法回头。所以,让我们享受这段平淡无奇的代码吧。

 

get_device_info,这个函数定义于drivers/usb/storage/usb.c中:

 

488 /* Get the unusual_devs entries and the stringdescriptors */

 

489 static int get_device_info(struct us_data *us,const struct usb_device_id *id)

 

490 {

 

491   struct usb_device *dev =us->pusb_dev;

 

492    structusb_interface_descriptor *idesc =

 

493                 &us->pusb_intf->cur_altsetting->desc;

 

494   struct us_unusual_dev *unusual_dev= find_unusual(id);

 

495

 

496     /*Store the entries */

 

497    us->unusual_dev= unusual_dev;

 

498    us->subclass= (unusual_dev->useProtocol == US_SC_DEVICE) ?

 

499                        idesc->bInterfaceSubClass:

 

500                        unusual_dev->useProtocol;

 

501    us->protocol= (unusual_dev->useTransport == US_PR_DEVICE) ?

 

502                        idesc->bInterfaceProtocol :

 

503                        unusual_dev->useTransport;

 

504   us->flags =USB_US_ORIG_FLAGS(id->driver_info);

 

505

 

506    if(us->flags & US_FL_IGNORE_DEVICE) {

 

507         printk(KERN_INFO USB_STORAGE "deviceignored/n");

 

508         return -ENODEV;

 

509   }

 

510

 

511    /*

 

512     * This flag is onlyneeded when we're in high-speed, so let's

 

513     * disable it if we're in full-speed

 

514     */

 

515    if(dev->speed != USB_SPEED_HIGH)

 

516      us->flags&= ~US_FL_GO_SLOW;

 

517

 

518   /* Log a message if a non-genericunusual_dev entry contains an

 

519    * unnecessary subclass or protocol override.  This may stimulate

 

520     * reports from users that will help usremove unneeded entries

 

521     * from the unusual_devs.h table.

 

522     */

 

523    if(id->idVendor || id->idProduct) {

 

524        static const char * msgs[3] = {

 

525                        "an unneeded SubClass entry",

 

526                        "an unneeded Protocol entry",

 

527                        "unneeded SubClass and Protocol entries"};

 

528         struct usb_device_descriptor *ddesc =&dev->descriptor;

 

529        int  msg = -1;

 

530

 

531        if (unusual_dev->useProtocol != US_SC_DEVICE &&

 

532                        us->subclass == idesc->bInterfaceSubClass)

 

533               msg += 1;

 

534           if(unusual_dev->useTransport != US_PR_DEVICE &&

 

535                        us->protocol == idesc->bInterfaceProtocol)

 

536               msg += 2;

 

537         if ( msg >= 0 && !(us->flags& US_FL_NEED_OVERRIDE))

 

538              printk(KERN_NOTICE USB_STORAGE"This device "

 

539                          "(%04x,%04x,%04x S %02x P%02x)"

 

540                          " has %s in unusual_devs.h(kernel"

 

541                           " %s)/n"

 

542                            "   Please send a copy of this message to "

 

543            "<linux-usb-devel@lists.sourceforge.net>/n",

 

544                         le16_to_cpu(ddesc->idVendor),

 

545                         le16_to_cpu(ddesc->idProduct),

 

546                         le16_to_cpu(ddesc->bcdDevice),

 

547                          idesc->bInterfaceSubClass,  

 

548                          idesc->bInterfaceProtocol,

 

549                         msgs[ msg],

 

550                           utsname()->release);

 

551   }

 

552

 

553     return0;

 

554 }

 

492行,struct usb_interface_descriptor *idesc,这个也无需再说,在之前的associate_dev函数中已经介绍过这个结构体,而且整个故事就是针对一个接口的,一个接口就对应一个接口描述符。

 

494行,struct us_unusual_dev,这个结构体是第一次出现,它定义于drivers/usb/storage/usb.h中:

 

61 struct us_unusual_dev {

 

62    constchar* vendorName;

 

63    const char*productName;

 

64     __u8  useProtocol;

 

65     __u8  useTransport;

 

66      int(*initFunction)(struct us_data *);

 

67 };

 

而“=”右边的find_unusual()函数定义于drivers/usb/storage/usb.c中:

 

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 }

 

us_unusual_dev_list是一个数组,定义于drivers/usb/storage/usb.c:

 

178 static struct us_unusual_devus_unusual_dev_list[] = {

 

179 #       include "unusual_devs.h"

 

180 #       undef UNUSUAL_DEV

 

181 #       undef USUAL_DEV

 

182

 

183   /* Terminating entry */

 

184     {NULL }

 

185 };

 

Linux代码中有很多奇怪的地方,可是像us_unusual_dev_list这个数组这么奇怪还真没见过。为了了解这个数组以及find_unusual()函数,我们先来看一看这个storage_usb_ids。它不是别人,正是我们曾经赋给usb_storage_driver的成员id_table的值。忘记了id_table的可以回去看。它实际上就是一张表格,告诉全世界driver支持怎样的一些设备。storage_usb_ids同样来自drivers/usb/storage/usb.c中:

 

138 static struct usb_device_id storage_usb_ids []= {

 

139

 

140 #       include"unusual_devs.h"

 

141 #undef UNUSUAL_DEV

 

142 #undef USUAL_DEV

 

143    /*Terminating entry */

 

144   { }

 

145 };

 

这么一看,us_unusual_dev_list和storage_usb_ids俨然是双胞胎啊!唯一的区别只是前者多了一个NULL。这里最莫名其妙的就是包含了一个文件,于是让我们先来查看这个unusual_devs.h文件到底是干什么的?先看一下这个文件最下面的一些行:

 

1476 /* Control/Bulk transport for all SubClassvalues */

 

1477 USUAL_DEV(US_SC_RBC, US_PR_CB,USB_US_TYPE_STOR),

 

1478 USUAL_DEV(US_SC_8020, US_PR_CB,USB_US_TYPE_STOR),

 

1479 USUAL_DEV(US_SC_QIC, US_PR_CB,USB_US_TYPE_STOR),

 

1480 USUAL_DEV(US_SC_UFI, US_PR_CB,USB_US_TYPE_STOR),

 

1481 USUAL_DEV(US_SC_8070, US_PR_CB, USB_US_TYPE_STOR),

 

1482 USUAL_DEV(US_SC_SCSI, US_PR_CB,USB_US_TYPE_STOR),

 

1483

 

1484 /* Control/Bulk/Interrupt transport for allSubClass values */

 

1485 USUAL_DEV(US_SC_RBC, US_PR_CBI,USB_US_TYPE_STOR),

 

1486 USUAL_DEV(US_SC_8020, US_PR_CBI,USB_US_TYPE_STOR),

 

1487 USUAL_DEV(US_SC_QIC, US_PR_CBI,USB_US_TYPE_STOR),

 

1488 USUAL_DEV(US_SC_UFI, US_PR_CBI,USB_US_TYPE_STOR),

 

1489 USUAL_DEV(US_SC_8070, US_PR_CBI,USB_US_TYPE_STOR),

 

1490 USUAL_DEV(US_SC_SCSI, US_PR_CBI,USB_US_TYPE_STOR),

 

1491

 

1492 /* Bulk-only transport for all SubClassvalues */

 

1493 USUAL_DEV(US_SC_RBC, US_PR_BULK,USB_US_TYPE_STOR),

 

1494 USUAL_DEV(US_SC_8020, US_PR_BULK,USB_US_TYPE_STOR),

 

1495 USUAL_DEV(US_SC_QIC, US_PR_BULK,USB_US_TYPE_STOR),

 

1496 USUAL_DEV(US_SC_UFI, US_PR_BULK,USB_US_TYPE_STOR),

 

1497 USUAL_DEV(US_SC_8070, US_PR_BULK,USB_US_TYPE_STOR),

 

1498 USUAL_DEV(US_SC_SCSI, US_PR_BULK, 0),

 

USUAL_DEV以及UNUSUAL_DEV均定义于drivers/usb/storage/usb.c中:

 

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

 

129                    vendorName, productName,useProtocol, useTransport, /

 

130                    initFunction, flags) /

 

131 { USB_DEVICE_VER(id_vendor, id_product,bcdDeviceMin,bcdDeviceMax), /

 

132  .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }

 

133

 

134 #define USUAL_DEV(useProto, useTrans, useType)/

 

135 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE,useProto, useTrans), /

 

136  .driver_info = (USB_US_TYPE_STOR<<24) }

 

注意到我们看的是structusb_device_id结构体的数组,其中每一项必然是一个struct usb_device_id的结构体变量。我们先来看USB_DEVICE_VER和USB_INTERFACE_INFO,很显然这两个都是宏,来自include/linux/usb.h:

 

715 /**

 

716  *USB_DEVICE_VER - macro used to describe a specific usb device with a

 

717 *             version range

 

718  *@vend: the 16 bit USB Vendor ID

 

719  *@prod: the 16 bit USB Product ID

 

720  *@lo: the bcdDevice_lo value

 

721  *@hi: the bcdDevice_hi value

 

722  *

 

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

 

724  *specific device, with a version range.

 

725 */

 

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

 

727   .match_flags =USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, /

 

728   .idVendor = (vend), .idProduct =(prod), /

 

729   .bcdDevice_lo = (lo),.bcdDevice_hi = (hi)

 

744 /**

 

745  *USB_INTERFACE_INFO - macro used to describe a class of usb interfaces

 

746  *@cl: bInterfaceClass value

 

747  *@sc: bInterfaceSubClass value

 

748  *@pr: bInterfaceProtocol value

 

749  *

 

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

 

751  *specific class of interfaces.

 

752 */

 

753 #define USB_INTERFACE_INFO(cl,sc,pr) /

 

754  .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl),/

 

755  .bInterfaceSubClass = (sc), .bInterfaceProtocol= (pr)

 

756

 

每一个USB_DEVICE_VER或者USB_INTERFACE_INFO就是构造一个struct usb_device_id的结构体变量,回顾一下struct usb_device_id的定义,这里实际上就是为其中的四个元素赋了值,它们是match_flags、bInterfaceClass、bInterfaceSubClass和bInterfaceProtocol。

 

这里不得不说的是,这个世界上有许许多多的USB设备,它们各有各的特点,为了区分它们,USB规范或者说USB协议,把USB设备分成了很多类,然而每个类又分成子类。这很好理解,我们的大学也是如此,先是分成很多个学院,然后每个学院又被分为很多个系,比如信息学院,下面分了电子工程系、微电子系、计算机系、通信工程系,然后可能每个系下边又分了各个专业。USB协议也是,首先每个接口属于一个Class,(为什么是把接口分类,而不把设备分类?前面讲过了,在USB设备驱动中,不用再提设备,因为每个设备驱动对应的是一种接口,而不是一种设备),然后Class下面分了SubClass,接着SubClass下面又按各种设备所遵循的不同的通信协议继续细分。

 

USB协议中为每一种Class、每一种SubClass和每一种Protocol定义一个数值,比如Mass Storage的Class就是0x08,而这里USB_CLASS_MASS_STORAGE这个宏在include/linux/usb/ch9.h中定义,其值正是8。

 

我们拿第1477行来举例。

 

1477 USUAL_DEV(US_SC_RBC, US_PR_CB,USB_US_TYPE_STOR),

 

把这个宏展开,就是说定义了这么一个usb_device_id结构体变量,其match_flags=USB_DEVICE_ID_MATCH_INT_INFO,而bInterfaceClass=USB_CLASS_MASS_STORAGE,bInterfaceSubClass=US_SC_RBC,以及bInterfaceProtocol=US_PR_CB。

 

USB_CLASS_MASS_STORAGE就不用再说了,这个驱动程序所支持的每一种设备都是属于这个类,或者说这个Class。但是这个Class里面包含不同的SubClass,比如SubClass 02为CD-ROM设备,04为软盘驱动器,06为通用SCSI类设备。而通信协议则主要有CBI协议和Bulk-Only协议。

 

像US_SC_RBC这些关于SubClass的宏的定义是在文件include/linux/usb_usual.h中:

 

74 #define US_SC_RBC       0x01           /* Typically, flash devices */

 

75 #define US_SC_8020      0x02           /* CD-ROM */

 

76 #define US_SC_QIC       0x03            /* QIC-157 Tapes */

 

77 #define US_SC_UFI       0x04           /* Floppy */

 

78 #define US_SC_8070      0x05           /* Removable media */

 

79 #define US_SC_SCSI      0x06           /* Transparent */

 

80 #define US_SC_ISD200    0x07           /* ISD200 ATA */

 

81 #define US_SC_MIN       US_SC_RBC

 

82 #define US_SC_MAX       US_SC_ISD200

 

83

 

84 #define US_SC_DEVICE    0xff           /* Use device's value */

 

而像US_PR_CB这些关于传输协议的宏也定义于同一个文件中:

 

88 #define US_PR_CBI       0x00           /* Control/Bulk/Interrupt */

 

89 #define US_PR_CB        0x01           /* Control/Bulk w/o interrupt */

 

90 #define US_PR_BULK      0x50           /* bulk only */

 

91 #ifdef CONFIG_USB_STORAGE_USBAT

 

92 #define US_PR_USBAT     0x80           /* SCM-ATAPI bridge */

 

93 #endif

 

94 #ifdef CONFIG_USB_STORAGE_SDDR09

 

95 #define US_PR_EUSB_SDDR09       0x81    /* SCM-SCSI bridge for SDDR-09 */

 

96 #endif

 

97 #ifdef CONFIG_USB_STORAGE_SDDR55

 

98 #define US_PR_SDDR55    0x82           /* SDDR-55 (made up) */

 

99 #endif

 

100 #define US_PR_DPCM_USB  0xf0           /* Combination CB/SDDR09 */

 

101 #ifdef CONFIG_USB_STORAGE_FREECOM

 

102 #define US_PR_FREECOM   0xf1           /* Freecom */

 

103 #endif

 

104 #ifdef CONFIG_USB_STORAGE_DATAFAB

 

105 #define US_PR_DATAFAB   0xf2           /* Datafab chipsets */

 

106 #endif

 

107 #ifdef CONFIG_USB_STORAGE_JUMPSHOT

 

108 #define US_PR_JUMPSHOT  0xf3           /* Lexar Jumpshot */

 

109 #endif

 

110 #ifdef CONFIG_USB_STORAGE_ALAUDA

 

111 #define US_PR_ALAUDA    0xf4           /* Alauda chipsets */

 

112 #endif

 

113 #ifdef CONFIG_USB_STORAGE_KARMA

 

114 #define US_PR_KARMA     0xf5           /* Rio Karma */

 

115 #endif

 

116

 

117 #define US_PR_DEVICE    0xff           /* Use device's value */

 

说了这么多,U盘属于其中的哪一种呢?USB协议中规定,U盘的SubClass是属于US_SC_SCSI的,而其通信协议使用的是Bulk-Only的,即这里看到的US_PR_BULK。显然这些东西我们后来都会用得上。

 

那么这里还有一个match_flag,它又是表示什么意思?USB_INTERFACE_INFO这个宏好像把所有的设备的match_flag都给设成了USB_DEVICE_ID_MATCH_INT_INFO,这是为什么?这个宏来自include/linux/usb.h:

 

699 #define USB_DEVICE_ID_MATCH_INT_INFO /

 

700                (USB_DEVICE_ID_MATCH_INT_CLASS | /

 

701                USB_DEVICE_ID_MATCH_INT_SUBCLASS | /

 

702                USB_DEVICE_ID_MATCH_INT_PROTOCOL)

 

match_flag这个东西是给USB Core去用的,USB Core负责给设备寻找适合它的驱动,负责给驱动寻找适合它的设备,它所比较的就是struct usb_device_id的变量,而struct usb_device_id结构体中有许多成员,那么是不是一定要把每一个成员都给比较一下呢?其实就是告诉USB Core,你只要比较bInterfaceClass,bInterfaceSubClass和bInterfaceProtocol即可。include/linux/mod_devicetable.h中针对struct usb_device_id中的每一个要比较的项定义了一个宏:

 

122 /* Some useful macros to use to create structusb_device_id */

 

123 #define USB_DEVICE_ID_MATCH_VENDOR             0x0001

 

124 #define USB_DEVICE_ID_MATCH_PRODUCT            0x0002

 

125 #define USB_DEVICE_ID_MATCH_DEV_LO             0x0004

 

126 #define USB_DEVICE_ID_MATCH_DEV_HI             0x0008

 

127 #define USB_DEVICE_ID_MATCH_DEV_CLASS           0x0010

 

128 #define USB_DEVICE_ID_MATCH_DEV_SUBCLASS       0x0020

 

129 #define USB_DEVICE_ID_MATCH_DEV_PROTOCOL       0x0040

 

130 #define USB_DEVICE_ID_MATCH_INT_CLASS           0x0080

 

131 #define USB_DEVICE_ID_MATCH_INT_SUBCLASS       0x0100

 

132 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL       0x0200

 

回去对比一下structusb_device_id就知道这些宏是什么意思了。

 

但是你一定有一个疑问,那就是为什么会有一个USUAL_DEV和一个UNUSUAL_DEV这样两个宏的存在,它们之间有什么区别呢?顾名思义,有些设备属于普通设备,而有些设备却并不是普通设备,它们或者是有一些别的设备不具备的特性,或者是他们遵循的通信协议有些与众不同,比如,它既不是Bulk-Only也不是CBI,像这些不按常理出牌的设备,写代码的人把它们单独给列了出来。当然,从大的分类来看,它们依然是属于USB Mass Storage这个类别的,否则也没必要放在这个目录下面了。

 

为了包容这些另类的设备,伟大的Linux内核开发人员们为它们准备了一个文件,它就是让诸多USB Mass Storage设备厂家欢欣鼓舞的unusual_devs.h,有了它,厂家们不用再为自己的设备不被Linux内核支持而烦恼了。

(免责声明:文章内容如涉及作品内容、版权和其它问题,请及时与我们联系,我们将在第一时间删除内容,文章内容仅供参考)
收藏
  • 人气文章
  • 最新文章
  • 下载排行榜
  • 热门排行榜