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

下面讲一下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() */

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

68jiffie count on our counter, they

69could 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 devicesdev_gendev;

142 structclass_devicesdev_classdev;

143

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

145

146 enumscsi_device_state sdev_state;

147 unsigned longsdev_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",

419US_SC_DEVICE, US_PR_DEVICE, usb_stor_euscsi_init,

420US_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",

639US_SC_DEVICE, US_PR_DEVICE, NULL,

640US_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",

646US_SC_DEVICE, US_PR_DEVICE, NULL,

647US_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",

653US_SC_DEVICE, US_PR_DEVICE, NULL,

654US_FL_FIX_INQUIRY ),

655


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值