《Linux那些事儿之我是USB》我是U盘(26)彼岸花的传说(五)

来源:岁月联盟 编辑:exp 时间:2011-11-14

 

下面讲一下usb_stor_control_thread()函数。唤醒它的是来自queuecommand的up(&(us->sema)),us->srb被赋值为srb,而srb是来自SCSI核心层在调用queuecommand时候传递进来的参数。聚焦usb_stor_control_thread()。314行,前面说过,关于dev_mutex这把锁我们必须在看完整个模块之后再来从较高的角度来看。

 

312行,如果设了US_FLIDX_DISCONNECTING,这个不用多说了,是判断设备有没有被拔出,要是你的U盘插进去了永远不拔出来,那么你可以把这个flag相关的代码都删了,当然事实上是你不可能不拔出来,热插拔本来就是USB设备的一大特性。

 

324行,host也是一把锁,这把锁我们也到最后再来看。

 

326行到330行,又是判断另一个flag有没有被设置,US_FLDX_TIMED_OUT这个flag的含义也如其字面意义一样,即超时了。超时的概念在计算机的世界里比比皆是。不过对于这个flag,设置它的函数是command_abort,这个函数也是咱们提供的,由SCSI核心层去调用,由它那边负责计时,到了超时的时间它就调用command_abort。我们稍后会看,先不急。

 

337行,判断srb的一个成员sc_data_direction,先看DMA_BIDIRECTIONAL这个宏。这个宏定义于include/linux/dma-mapping.h中:

 

7 /* These definitions mirror those in pci.h, sothey can be used

 

8  *interchangeably with their PCI_ counterparts */

 

9 enum dma_data_direction {

 

10    DMA_BIDIRECTIONAL= 0,

 

11    DMA_TO_DEVICE= 1,

 

12      DMA_FROM_DEVICE = 2,

 

13     DMA_NONE= 3,

 

14 };

 

这些代码被用来表示数据阶段数据传输的方向。DMA_TO_DEVICE表示从主存到设备,DMA_FROM_DEVICE表示从设备到主存。有传闻说,DMA_NONE则只被用于调试,一般不能使用否则将有可能导致内核崩溃。不过更准确一点的是,USB Mass Storage协议中边规定了双向传输是非法的,而一个命令传输零数据是合法的,比如TEST_UNIT_READY命令就不用传输数据。

 

DMA_BIDIRECTIONAL表示两个方向都有可能,换而言之也就是不知道究竟是哪个方向。同理,338行看到srb的sc_data_direction是DMA_BIDIRECTIONAL时,自然就当做出错了。因为不确定方向的话也就没法传输数据了。

 

345行,US_FL_SCM_MULT_TARG这个flag,表示设备支持多个target,这里的意思很明显,对于那些不支持多个target的设备,其us->srb->device->id必须为0,否则就有问题了。struct us_data结构体中的成员struct scsi_cmnd * srb,struct scsi_cmnd结构体中有一成员struct scsi_device * device,而struct scsi_device顾名思义,描述一个SCSI设备,就像过去的struct usb_device用来描述USB设备一样。struct scsi_device来自include/scsi/scsi_device.h中:

 

 49struct scsi_device {

 

 50    struct Scsi_Host *host;

 

 51    struct request_queue *request_queue;

 

 52

 

 53    /* the next two are protected by thehost->host_lock */

 

 54    struct list_head  siblings;/* list of all devices on this host*/

 

 55    struct list_head  same_target_siblings;

 

 56

 

 57    /* this is now protected by therequest_queue->queue_lock */

 

 58     unsigned int device_busy;  /* commands actually active on

 

 59                                       *low-level. protected by queue_lock.*/

 

 60    spinlock_t list_lock;

 

 61    struct list_head cmd_list; /* queue of in useSCSI Command structures*/

 

 62   struct list_head starved_entry;

 

 63    struct scsi_cmnd *current_cmnd; /* currentlyactive command */

 

 64    unsigned short queue_depth;     /* How deep of aqueue we want */

 

 65    unsigned short last_queue_full_depth; /* Thesetwo are used by */

 

 66    unsigned short last_queue_full_count; /*scsi_track_queue_full() */

 

 67   unsigned long last_queue_full_time;/*don't let QUEUE_FULLs on the same

 

 68                                           jiffie count on our counter, they

 

 69                                           could all be from the same event.*/

 

 70

 

 71   unsigned int id, lun, channel;

 

 72

 

 73   unsigned int manufacturer;      /* Manufacturerof device, for using

 

 74                                         *vendor-specific cmd's */

 

 75    unsigned sector_size;   /* size in Bytes */

 

 76

 

 77     void *hostdata;        /* available to low-level driver */

 

 78   char type;

 

 79   char scsi_level;

 

 80     char inq_periph_qual;   /* PQ from INQUIRY data */

 

 81    unsigned char inquiry_len;      /* valid Bytesin 'inquiry' */

 

 82    unsigned char * inquiry;        /*INQUIRY response data */

 

 83    const char * vendor;    /* [back_compat] point into 'inquiry' ... */

 

 84    const char * model;     /* ... after scan; point to static string*/

 

 85   const char * rev;          /* ..."nullnullnullnull" before scan */

 

 86    unsigned char current_tag;      /* current tag*/

 

 87    struct scsi_target      *sdev_target;/* used only forsingle_lun*/

 

 88

 

 89    unsigned int    sdev_bflags; /* black/white flags as also foundin

 

 90                                 * scsi_devinfo.[hc]. For now used only to

 

 91                                 * pass settings from slave_alloc to scsi

 

 92                                 *core. */

 

 93    unsigned writeable:1;

 

 94    unsigned removable:1;

 

 95   unsigned changed:1;     /* Data invalid due to media change */

 

 96    unsigned busy:1;        /* Used to prevent races*/

 

 97    unsigned lockable:1;    /* Able to prevent media removal */

 

 98  unsigned locked:1;      /* Media removal disabled */

 

 99  unsigned borken:1;      /* Tell the Seagate driver to be

 

100                                 * painfully slow on this device */

 

101  unsigned disconnect:1;  /* can disconnect */

 

102     unsignedsoft_reset:1;  /* Uses soft resetoption */

 

103   unsigned sdtr:1;        /*Device supports SDTR messages */

 

104    unsignedwdtr:1;        /* Device supports WDTRmessages */

 

105   unsigned ppr:1;        /* Device supports PPR messages */

 

106    unsignedtagged_supported:1;/* Supports SCSI-II tagged queuing */

 

107     unsignedsimple_tags:1; /* simple queue tag messages are enabled*/

 

108     unsignedordered_tags:1;/* ordered queue tag messages are enabled */

 

109      unsigned single_lun:1; /* Indicates we should only allow I/O to

 

110                                 * one of the luns for the device at a

 

111                                 * time. */

 

112     unsignedwas_reset:1;   /* There was abus reset on the bus for

 

113                                 * this device */

 

114  unsigned expecting_cc_ua:1; /* Expecting aCHECK_CONDITION/UNIT_ATTN

 

115                                     * because we did a bus reset. */

 

116    unsigneduse_10_for_rw:1; /* first try 10-Byte read / write */

 

117    unsigneduse_10_for_ ms:1; /* first try 10-Byte mode sense/select*/

 

118     unsignedskip_ ms_page_8:1;      /* do not use MODE SENSE page 0x08*/

 

119     unsignedskip_ ms_page_3f:1; /* do not use MODE SENSE page 0x3f */

 

120    unsigneduse_192_Bytes_for_3f:1;/* ask for 192 Bytes from page 0x3f*/

 

121   unsigned no_start_on_add:1;     /* do not issue starton add */

 

122    unsignedallow_restart:1; /* issue START_UNIT in error handler */

 

123    unsignedmanage_start_stop:1;/* Let HLD (sd) manage start/stop */

 

124    unsigned no_uld_attach:1;/*disable connecting toupper level drivers*/

 

125   unsigned select_no_atn:1;

 

126    unsignedfix_capacity:1;     /*READ_CAPACITY is too high by 1 */

 

127    unsignedguess_capacity:1;/* READ_CAPACITY might be too high by 1 */

 

128    unsignedretry_hwerror:1;       /* Retry HARDWARE_ERROR */

 

129

 

130   unsigned int device_blocked;    /* Device returnedQUEUE_FULL. */

 

131

 

132 unsigned int max_device_blocked;

 

133 #define SCSI_DEFAULT_DEVICE_BLOCKED 3

 

134

 

135   atomic_t iorequest_cnt;

 

136   atomic_t iodone_cnt;

 

137    atomic_tioerr_cnt;

 

138

 

139    inttimeout;

 

140

 

141   struct device          sdev_gendev;

 

142    structclass_device    sdev_classdev;

 

143

 

144   struct execute_work     ew; /* used to getprocess context on put*/

 

145

 

146    enumscsi_device_state sdev_state;

 

147   unsigned long          sdev_data[0];

 

148 } __attribute__((aligned(sizeof(unsignedlong))));

 

这个结构体将在后面多次被提到。当然,此刻,我们只需要注意到unsigned int id,lun,channel这三个成员,这正是定位一个SCSI设备必要的三个成员,一个SCSI卡所控制的设备被划分为几层,先是若干个channel,然后每个channel上有若干个target,每个target用一个target id来表示,然后一个target可以有若干个lun,而这里判断的是target id。对于不支持多个target的设备,必须为0。对于绝大多数USB Mass Storage设备来说,它们的target id肯定为0。有些设备厂家就是要标新立异,它就是要让设备支持多个target,于是它就可以设置US_FL_SCM_MULT_TARG这么一个flag,比如我们可以在drivers/usb/storage/unusual_devs.h中看到如下的定义:

 

416 UNUSUAL_DEV(  0x04e6, 0x0002, 0x0100, 0x0100,

 

417                "Shuttle",

 

418                "eUSCSI Bridge",

 

419                US_SC_DEVICE, US_PR_DEVICE, usb_stor_euscsi_init,

 

420                US_FL_SCM_MULT_TARG ),

 

然后352行,us->srb->device->lun不应该大于us->max_lun,这两个东西是什么区别?us->max_lun是咱们早期在usb_stor_scan_thread()中调用usb_stor_Bulk_max_lun()函数来向usb mass storage设备获得的最大LUN,比如MAX LUN等于3,那么这个设备支持的就是4个LUN,即0,1,2,3。而us->srb->device->lun则可以是这四个值中的任意一个,看传递进来的命令是要访问谁了。但它显然不可能超过MAX LUN。

 

然后就是358行了。看到这么一个flag-US_FL_FIX_INQUIRY,这又是us->flags中众多flag中的一个,我们前面已经介绍过这个flag,一些定义于drivers/usb/storage/unusal_devs.h中的设备有这个flag。事实上,通常大多数设备的厂商名(Vendor Name)和产品名(Product Name)是通过INQUIRY命令来获得的,而这个flag表明,这些设备的厂商名和产品名不需要查询,或者根本就不支持查询,它们的厂商名和产品名直接就定义好了,在unusal_devs.h中就设好了。那么358行这里这个cmnd[0]是什么?struct scsi_cmnd里边有这么一个成员,

 

65 #define MAX_COMMAND_SIZE 16

 

66         unsigned charcmnd[MAX_COMMAND_SIZE];

 

这个数组16个元素,它包含的就是SCSI命令,要看懂这个条件判断,得先看下边那句fill_inquiry_response()函数调用。

 

最后“贴”几个设了US_FL_FIX_INQUIRY这个flag的设备,这几个都是Sony的PEG记忆棒,或者叫记忆卡,可以用在PDA里边。drivers/usb/storage/unusual_devs.h中:

 

635 /* Submitted by Nathan Babb<nathan@lexi.com> */

 

636 UNUSUAL_DEV(  0x054c, 0x006d, 0x0000, 0x9999,

 

637                "Sony",

 

638                 "PEGMass Storage",

 

639                US_SC_DEVICE, US_PR_DEVICE, NULL,

 

640                US_FL_FIX_INQUIRY ),

 

641

 

642 /* Submitted by Mike Alborn<malborn@deandra.homeip.net> */

 

643 UNUSUAL_DEV(  0x054c, 0x016a, 0x0000, 0x9999,

 

644                 "Sony",

 

645                "PEG Mass Storage",

 

646                US_SC_DEVICE, US_PR_DEVICE, NULL,

 

647                US_FL_FIX_INQUIRY ),

 

648

 

649 /* Submitted by Frank Engel<frankie@cse.unsw.edu.au> */

 

650 UNUSUAL_DEV(  0x054c, 0x0099, 0x0000, 0x9999,

 

651                "Sony",

 

652                "PEG Mass Storage",

 

653                US_SC_DEVICE, US_PR_DEVICE, NULL,

 

654                US_FL_FIX_INQUIRY ),

 

655