接下来的时间里我们会接触两个变量,fake_sense和need_auto_sense,sense顾名思义,感觉.所以就让我们跟着感觉走.我们前面提到过,如果设备想发送比期望值更多的数据,那么我们前面就设了fake_sense为1.这里就来看看设为1之后怎么办.这里咱们看到了这个一个冬冬,usb_stor_sense_invalidCDB,她是谁?
让我们把镜头对准drivers/usb/storage/scsiglue.c,
479 /* To Report "Illegal Request: Invalid Field in CDB */
480 unsigned char usb_stor_sense_invalidCDB[18] = {
481 [0] = 0x70, /* current error */
482 [2] = ILLEGAL_REQUEST, /* Illegal Request = 0x05 */
483 [7] = 0x0a, /* additional length */
484 [12] = 0x24 /* Invalid Field in CDB */
485 };
486
这是一个字符数组,共18个元素,初始化的时候其中4个元素被赋了值,为了说明这个数组,下面不得不插播一段scsi广告,广告过后立刻回来.
我们知道SCSI通过命令通信,有一个命令是Request Sense.她是用来获取错误信息的,不知道为什么,那些有文化的人把错误信息唤作sense data.可能老外取名字都喜欢取得很优雅吧,相比之下,我们国内很多东西取名字就有些土,比如某所高校,中文名是沈阳理工,而英文名居然就是Shenyang Ligong University.这样没文化的名字实在让人笑死了.如果一个设备接收到了一个Request Sense命令,那么她将按游戏规则返回一个sense data,我们可以参考scsi协议,找到sense data的格式规定,如下图所示:
标准的sense data是18个bytes的.所以这里准备了一个18个元素的数组,第0个byte的低七位称为error code,0x70表明是出问题的是当前这个命令,第二个byte的低四位成为sense key,0x5h称为Illegal Request,表明命令本身有问题,比如命令的参数不合法.而第七个byte称为additional sense length表明在这个18个元素之后还会有additional sense bytes,而她的长度就在这里被标注了,这些additional sense bytes通常指的是一些命令特有的数据,或者是一些外围设备特有的数据,这里为她赋值为0x0a.而第十二个byte,称为additional sense code,这部分针对sense key提供一些信息,也就是说比如sense key如果是Illegal Request,那么我们知道了是命令有问题,那么究竟有什么问题呢?additional sense code提供更详细的一些信息,scsi规范中对24h的描述是Invalid Field in CDB,正是我们这里注释所说.
所以,这样我们明白了,1073行,就是将usb_stor_sense_invalidCDB数组里边的冬冬copy至srb->sense_buffer里边,然后返回USB_STOR_TRANSPORT_NO_SENSE.struct scsi_cmnd结构体里面是这样定义sense_buffer的,
115 #define SCSI_SENSE_BUFFERSIZE 96
116 unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE]; /* obtained by REQUEST SENSE
117 * when CHECK CONDITION is
118 * received on original command
119 * (auto-sense) */
关于sense_buffer就得从scsi协议以及Linux中的scsi核心层来讲了.scsi协议里边有这么一码子事,当一个scsi命令执行出了错,你可以发送一个REQUEST_SENSE命令给目标设备,然后它会返回给你一些信息,即sense data.不过呢,scsi核心层偷懒,把这一艰巨的任务抛给了底层驱动,即我们作为底层驱动不得不自己发送REQUEST SENSE命令给目标设备.当然了,所谓的scsi core偷懒并不是没有它的道理,因为有些scsi host卡会自动发送这个命令,就是说当设备汇报说命令执行有误,那这时scsi host卡会自动发送REQUEST SENSE命令去了解详情.所以scsi core就干脆把权力下放,让底层驱动自己去处理吧.因此稍后我们会看到一个变量名字叫做need_auto_sense.就是说,REQUEST SENSE这个命令要么就是硬件你自动发出去,要么就让软件自动发出去,总之scsi core这一层是不管你了.只要你最终返回scsi核心层的时候把相关的sense data保存在srb->sense_buffer里,scsi核心层自然就知道该如何处理了.
再回到我们具体的问题中来,我们说了,有些设备就是贱,你明明只期望它返回n个字节,它偏偏要给你捣乱,它想返回n+m个字节,对于这种情况我们怎么处理?老实说,它想多返回的几个字节我们完全可以抛弃,因为我们只关心我们提供的buffer是否装满了,是否达到了我们要求的length个字节,如果达到了,那么剩下的不管也罢,不过写代码的同志们在这个问题上考虑得比我们要周到,他们对这个细节也是体贴入微的.或者说他们对这种傻X的设备也是很关心的.对于这种情况,写代码的同志们考虑,还是应该向上层汇报一下,说明这个命令对这个设备来说,执行起来总有些问题.因为这种情况完全可能就是,比如说,一个命令可以带有一些参数,而可能某个设备并不支持其中的某个参数,而你执行命令的时候去设了这个参数,那么设备的返回值可能就不正常,所谓的比预期的值要多就是一种不正常的表现,所以呢,对于这种情况,我们干脆就告诉上层这个命令有问题,在Linux中,我们就可以通过sense data来向上层汇报.而之所以这里称作fake sense,说的是这个sense data里面的东西是我们自己设置好的,因为我们已经很清楚我们应该在设备里放置什么,不需要向设备发送一个REQUEST SENSE的命令,而更确切的说,我们这个命令的返回结果是US_BULK_STAT_OK,也就是说,从设备那方返回的状态来看,设备认为命令没有问题,但是你说设备的话你能相信吗?老实说,从孙志刚案件开始,我不相信警察!从刘涌案件开始,我不相信法律!从苏秀文案件开始,我不相信政府!从打工受户籍歧视开始,我不相信有公平!从CCTV每天报喜不报忧开始,我不相信有真话!而从我进入复旦微电子系开始学习计算机硬件,我他妈的就没相信过硬件设备.不是这有毛病就是那有毛病,硬件bug到处都是.算了,别跑题了,继续说,因为设备认为传输是成功的,所以你发送REQUEST SENSE根本就没用,因为设备根本就不会为你准备sense data,因为sense data本来就是为了提供错误信息的.因此我们需要自己设一个sense data,放进sense_buffer里去.从而让scsi core那一层知道有这么一回事,别被设备瞒天过海给忽悠了.
讲到这里,usb_stor_Bulk_transport()这个函数就算结束了.返回值一共就是四种情况,USB_STOR_TRANSPORT_GOOD,USB_STOR_TRANSPORT_FAILED,USB_STOR_TRANSPORT_ERROR,以及USB_STOR_TRANSPORT_NO_SENSE.然后上层会去分析这些返回值.让我们结束这个函数,回到调用她的函数中来,即usb_stor_invoke_transport().在回去之前,我们需要记住的就是,对于刚才说的这种情况,即fake_sense为1的情况,我们返回的就是USB_STOR_TRANSPORT_NO_SENSE.一会我们会看到usb_stor_invoke_transport()中是如何应付这种情况的.