接下来的时间里我们会接触两个变量,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的格式规定,如下图所示:
<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 387pt; HEIGHT: 269.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image001.emz"></imagedata></shape>
标准的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()中是如何应付这种情况的.
分享到:
相关推荐
根据给定的信息,“Linux那些事儿之我是U盘”这一标题及描述主要聚焦于USB技术在Linux环境下的工作原理和技术细节。接下来,我们将基于这个主题展开深入探讨,涵盖USB技术的基本概念、USB在Linux系统中的实现机制...
Linux那些事儿之我是U盘 Linux那些事儿之我是Hub Linux那些事儿之我是USB Core Linux那些事儿之我是UHCI Linux那些事儿之我是EHCI控制器 Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是...
读过《linux那些事儿之我是U盘》的人,都知道其风格,我就不多说了。 导读: linux那些事儿之我是U盘 linux那些事儿之我是HUB linux那些事儿之我是USB Core linux那些事儿之我是UHCI Linux那些事儿之我是EHCI主机控制...
### Linux那些事儿之我是USB(第2版)关键知识点概览 #### 一、书籍概述 - **核心主题**:本书主要围绕Linux内核中的USB子系统展开,深入剖析其工作原理和技术细节。 - **目标读者**:面向Linux初学者、驱动开发者...
本压缩包文件"linux那些事儿之我是USB.zip"包含了深入理解Linux USB驱动及内核相关知识的九个文档,包括Block层、EHCI主机控制器、HUB、PCI、SCSI硬盘、Sysfs、UHCI、USB core以及U盘。这些文档旨在提供一个系统性的...
3. **Linux那些事儿之我是U盘.pdf**: USB闪存驱动器,通常称为U盘,是常见的便携式存储设备。这部分可能讲述Linux如何识别、挂载和管理U盘,包括使用ums(USB Mass Storage)驱动程序,以及如何处理文件系统的读写...
导读.doc Linux那些事儿之我是Block层.pdf Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是Hub.pdf Linux那些事儿之我是USB_core.pdf Linux那些事儿之我是U盘.pdf等等 Linux那些事儿系列全在这里了
》包括《Linux那些事儿之我是Hub》、《Linux那些事儿之我是Sysfs》《Linux那些事儿之我是UHCI》、《Linux那些事儿之我是USB core》、《Linux那些事儿之我是U盘》,令人叹为观止的一个linux系列书籍。只能说,江山代...
“Linux那些事儿之我是U盘.pdf”将关注通用串行总线(USB)闪存驱动器。这部分会解释Linux如何识别和处理USB闪存设备,包括文件系统的挂载和数据交换过程。 “Linux那些事儿之我是USB Core.pdf”涉及Linux内核的USB...
Linux那些事儿之我是Block层 Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是Hub Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Sysfs ...Linux那些事儿之我是U盘
本文基于一篇幽默且深入的教程《Linux那些事儿之我是U盘》,该文章通过作者的亲身经历和深入浅出的讲解,介绍了Linux操作系统中USB存储设备的工作原理及相关技术细节。以下是对该文中涉及的重要知识点的梳理。 ####...