Linux那些事儿之我是U盘(37)彼岸花的传说(五) – fudan

燕子去了,有再来的时候;杨柳枯了,有再青的时候;桃花谢了,有再开的时候;老婆离了,有再找的时候,孩子跑了,有回来的时候;煮熟的鸭子飞了,有飞回来的时候.一个函数没讲完就跳走了,有再回来的时候.其实,那些人,那些事,终究不曾远离.

于是,她再一次进入我们的视野.

她就是usb_stor_control_thread().唤醒她的是来自queuecommand的up(&(us->sema)), us->srb被赋值为srb,而srb是来自scsi核心层在调用queuecommand时候传递进来的参数.聚焦usb_stor_control_thread(),309行,前面说过,关于dev_semaphore这把锁我们必须在看完整个模块之后再来总较高的角度来看,所以这里自然先跳过.

312行,没啥好说的,如果传进来的是NULL,那么还是退出吧.别无选择.(为什么命令是空就说明要结束了?因为如果srb是空,那么scsi core是不会调用queuecommand的,道理不用说了吧,scsi core也不傻,都没有命令它还调用底层的函数干嘛,吃错药了?那么queuecommand没有调用的话是谁唤醒了这个守护进程?我们前面说了,唤醒它的有两个地方,一个是queuecommand,另一个就是模块要卸载了,准确的说是usb_stor_release_resources()函数,日后等我们遇到了这个函数再看吧,总之就是说当这个模块都要释放资源了,这时候srb肯定会被设为NULL,然后这种情况下唤醒了我们这个守护进程,于是才有了这里这个判断语句.)

318行,host也是一把锁,这把锁我们也到最后再来看.暂且不表.

321行至331行,又是判断两个flag有没有被设置,第二个不用多说了,看都看腻了,老是判断设备有没有被拔出,要是你的U盘插进去了永远不拔出来,那么你可以把这个flag相关的代码都删了,当然事实上是你不可能不拔出来,热插拔本来就是usb设备的一大特性.所以你就能理解写代码的哥们儿为什么这么煞费苦心的去判断设备是不是已经不在了.那么第一个flag呢,US_FLDX_TIMED_OUT,这个flag的含义也如其字面意义一样,超时了,超时的概念在计算机的世界里比比皆是.不过对于这个flag,设置它的函数是command_abort,这个函数也是咱们提供的,由scsi核心层去调用,由它那边负责计时,到了超时的时间它就调用command_abort.我们稍候会看,先不急.

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

7 /* These definitions mirror those in pci.h, so they 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 };在scsi_cmnd结构体里边,有这么两个成员,

75 enum dma_data_direction sc_data_direction; 76 enum dma_data_direction sc_old_data_direction;

这些被用来表征数据阶段数据传输的方向.DMA_TO_DEVICE表示从主存到设备,DMA_FROM_DEVICE表示从设备到主存.坊间有传闻说,DMA_NONE则只被用于调试,一般不能使用否则将有可能导致内核崩溃.不过更准确一点是,usb mass storage协议里边规定了双向传输是非法的,而一个命令传输零数据是合法的,比如TEST_UNIT_READY命令就不用传输数据.DMA_BIDIRECTIONAL表示两个方向都有可能,换言之也就是不知道究竟是哪个方向.就比如您找某位半仙算命,而当您想考考他,问他您是什么时候生的,他却对您说,您不是上半年生的就是下半年生的.这样只能说明他什么都不知道.同理,338行看到srb的sc_data_direction是DMA_BIDIRECTIONAL的时候,自然就当作出错了.因为不确定方向的话也就没法传输数据了嘛不是.

346行, 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 device,就像过去的struct usb_device用来描述usb device一样,这些写Linux代码的哥们儿也没别的技巧,就这几招.不用看也知道,struct scsi_device又是一个变态的数据结构,还是那句话,仿佛不写一些变态数据结构来不足以体现Linus这帮子人是腕儿.具体来看,她来自include/scsi/scsi_device.h中:

38 struct scsi_device { 39 struct Scsi_Host *host; 40 struct request_queue *request_queue; 41 42 /* the next two are protected by the host->host_lock */ 43 struct list_head siblings; /* list of all devices on this host */ 44 struct list_head same_target_siblings; /* just the devices sharing same target id */ 45 46 volatile unsigned short device_busy; /* commands actually active on low-level */ 47 spinlock_t sdev_lock; /* also the request queue_lock */ 48 spinlock_t list_lock; 49 struct list_head cmd_list; /* queue of in use SCSI Command structures */ 50 struct list_head starved_entry; 51 struct scsi_cmnd *current_cmnd; /* currently active command */ 52 unsigned short queue_depth; /* How deep of a queue we want */ 53 unsigned short last_queue_full_depth; /* These two are used by */ 54 unsigned short last_queue_full_count; /* scsi_track_queue_full() */ 55 unsigned long last_queue_full_time;/* don’t let QUEUE_FULLs on the same 56 jiffie count on our counter, they 57 could all be from the same event. */ 58 59 unsigned int id, lun, channel; 60 61 unsigned int manufacturer; /* Manufacturer of device, for using 62 * vendor-specific cmd’s */ 63 unsigned sector_size; /* size in bytes */ 64 65 void *hostdata; /* available to low-level driver */ 66 char devfs_name[256]; /* devfs junk */ 67 char type; 68 char scsi_level; 69 char inq_periph_qual; /* PQ from INQUIRY data */ 70 unsigned char inquiry_len; /* valid bytes in ‘inquiry’ */ 71 unsigned char * inquiry; /* INQUIRY response data */ 72 char * vendor; /* [back_compat] point into ‘inquiry’ … */ 73 char * model; /* … after scan; point to static string */ 74 char * rev; /* … "nullnullnullnull" before scan */ 75 unsigned char current_tag; /* current tag */ 76 struct scsi_target *sdev_target; /* used only for single_lun */ 77 78 unsigned int sdev_bflags; /* black/white flags as also found in 79 * scsi_devinfo.[hc]. For now used only to 80 * pass settings from slave_alloc to scsi 81 * core. */ 82 unsigned writeable:1; 83 unsigned removable:1; 84 unsigned changed:1; /* Data invalid due to media change */ 85 unsigned busy:1; /* Used to prevent races */ 86 unsigned lockable:1; /* Able to prevent media removal */ 87 unsigned locked:1; /* Media removal disabled */ 88 unsigned borken:1; /* Tell the Seagate driver to be 89 * painfully slow on this device */ 90 unsigned disconnect:1; /* can disconnect */ 91 unsigned soft_reset:1; /* Uses soft reset option */ 92 unsigned sdtr:1; /* Device supports SDTR messages */ 93 unsigned wdtr:1; /* Device supports WDTR messages */ 94 unsigned ppr:1; /* Device supports PPR messages */ 95 unsigned tagged_supported:1; /* Supports SCSI-II tagged queuing */ 96 unsigned simple_tags:1; /* simple queue tag messages are enabled */ 97 unsigned ordered_tags:1;/* ordered queue tag messages are enabled */ 98 unsigned single_lun:1; /* Indicates we should only allow I/O to 99 * one of the luns for the device at a 100 * time. */ 101 unsigned was_reset:1; /* There was a bus reset on the bus for 102 * this device */ 103 unsigned expecting_cc_ua:1; /* Expecting a CHECK_CONDITION/UNIT_ATTN 104 * because we did a bus reset. */ 105 unsigned use_10_for_rw:1; /* first try 10-byte read / write */ 106 unsigned use_10_for_ms:1; /* first try 10-byte mode sense/select */ 107 unsigned skip_ms_page_8:1; /* do not use MODE SENSE page 0x08 */ 108 unsigned skip_ms_page_3f:1; /* do not use MODE SENSE page 0x3f */ 109 unsigned use_192_bytes_for_3f:1; /* ask for 192 bytes from page 0x3f */ 110 unsigned no_start_on_add:1; /* do not issue start on add */ 111 unsigned allow_restart:1; /* issue START_UNIT in error handler */ 112 unsigned no_uld_attach:1; /* disable connecting to upper level drivers */ 113 unsigned select_no_atn:1; 114 unsigned fix_capacity:1; /* READ_CAPACITY is too high by 1 */ 115 116 unsigned int device_blocked; /* Device returned QUEUE_FULL. */ 117 118 unsigned int max_device_blocked; /* what device_blocked counts down from */ 119 #define SCSI_DEFAULT_DEVICE_BLOCKED 3 120 121 int timeout; 122 123 struct device sdev_gendev; 124 struct class_device sdev_classdev; 125 126 struct class_device transport_classdev; 127 128 enum scsi_device_state sdev_state; 129 unsigned long sdev_data[0]; 130 } __attribute__((aligned(sizeof(unsigned long))));

这个结构体将在未来的日子里被我们多次提到.当然,此刻,我们只需要注意到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中看到如下的定义:

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

"Shuttle",

"eUSCSI Bridge",

US_SC_DEVICE, US_PR_DEVICE, usb_stor_euscsi_init,

US_FL_SCM_MULT_TARG ),

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

然后就是359行了.看到这么一个flag-US_FL_FIX_INQUIRY,这又是us->flags中众多flag中的一个,一些定义于drivers/usb/storage/unusal_devs.h中的设备有这个flag,事实上,通常大多数设备的vendor name和product name是通过INQUIRY命令来获得的,而这个flag表明,这些设备的vendor name和product name不需要查询,或者根本就不支持查询,她们的vendor name和product name直接就定义好了,在unusal_devs.h中就设好了.那么359行这里这个cmnd[0]是什么?struct scsi_cmnd里边有这么一个成员,

79 #define MAX_COMMAND_SIZE 16 80 unsigned char cmnd[MAX_COMMAND_SIZE];

这个数组16个元素,她包含的就是scsi命令,要看懂这个条件判断,得先看下边那句fill_inquiry_response()函数调用.如果大家都没意见的话,我提议咱们下节再接着讲.

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

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

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

379 "Sony",

380 "PEG Mass Storage",

381 US_SC_DEVICE, US_PR_DEVICE, NULL,

382 US_FL_FIX_INQUIRY ),

383

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

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

386 "Sony",

387 "PEG Mass Storage",

388 US_SC_DEVICE, US_PR_DEVICE, NULL,

389 US_FL_FIX_INQUIRY ),

390

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

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

393 "Sony",

394 "PEG Mass Storage",

395 US_SC_DEVICE, US_PR_DEVICE, NULL,

396 US_FL_FIX_INQUIRY ),

常用掌上电脑的同志们不会对Sony的这些PEG产品陌生吧.

只需勇敢前行,梦想自会引路,有多远,走多远,把足迹连成生命线。

Linux那些事儿之我是U盘(37)彼岸花的传说(五) – fudan

相关文章:

你感兴趣的文章:

标签云: