Linux那些事儿之我是U盘(48)迷雾重重的Bulk传输(六) – fudan

接下来咱们该看看如何处理CSW了.1018行,usb_stor_bulk_transfer_buf()函数再一次被调用,这次是获得CSW,期望长度是US_BULK_CS_WRAP_LEN,这个宏来自drivers/usb/storage/transport.h中:

109 #define US_BULK_CS_WRAP_LEN 13

13对应CSW的长度,13个bytes.而cswlen记录了实际传输的长度.1025行,如果返回值是USB_STOR_XFER_SHORT,表明数据传少了,没有达到我们期望的那么多,而假如cswlen又等于0,那么说明没有获得真正的CSW,正如注释所说,有些变态的设备会在数据阶段末尾多加一些0长度的包进来,这就意味着咱们并没有获得CSW,于是重新执行一次usb_stor_bulk_transfer_buf(),再获得一次.(旁白:数据传输失败了可以重来一次,你我失去的青春能重来一次么?唉,人生没有彩排,天天都是现场直播.)

1032行,如果result等于USB_STOR_XFER_STALLED,在interpret_urb_result中查找一下,USB_STOR_XFER_STALLED对应于usb core传回来的是-EPIPE,这种情况说明管道不通,就相当于您家里的下水管道堵塞,当然这也说明get CSW再次失败了…,这种情况很简单,直接retry,为什么要retry?我们看一下interpret_urb_result()函数,最重要的就是310到314行,这里判断了,因为我们曾经讲过,bulk端点可能会设置了halt条件,设置了这种条件的端点必然会堵塞管道,所以这里就不管如何,试一试看,看清掉这个flag是否会有好转.所以对于这种情况,我们可以重试一次.我们抱着试一试的心态去retry,应该说这种心态是正确的,我再重申一次,这里实际上反映的就是Linux代码背后的哲学,反映的是一种勇敢面对挫折的人生态度,一枚贝壳要用一生的时间才能将无数的沙粒转化成一粒并不规则的珍珠,雨后的彩虹绽放刹那的美丽却要积聚无数的水汽.如果把这些都看成是一次又一次挫折,那么是挫折成就了光彩夺目的珍珠和美丽的彩虹.我们要相信,失败并不可怕,失败是通往成功的道路.

如果您不是像李白一样近视的话,(床前明月光都能看成地上霜的人,还不是近视吗?)您应该会看见这次传递给usb_stor_bulk_transfer_buf()函数的最后一个参数不是像之前那样,这次是NULL,这是因为实际上cswlen作为一个临时变量,表征的是状态阶段的实际传输长度,但是在眼下这种情况我们已经不需要使用这个临时变量了.

1042行,好家伙,如果都这么重新获取了还不成功的话,不用再瞎耽误工夫了,直接返回吧,向领导汇报这设备无药可救了.没办法,返回USB_STOR_TRANSPORT_ERROR,到这里还不成功那真的就是让人绝望了.什么?你说失败是成功之母?对于这句话我没有异议,问题是失败在遇上我之前已经结扎了…

而从1046行开始,正式分析CSW了.结合我们从usb mass storage bulk only协议中抓出来的那幅图,那幅介绍CSW的格式的图,bcs->Residue对应于CSW的dCSWDataResidue,她表示的是实际传输的数据和期望传输的数据的差值.bcs->Signature对应于CSW中的dCSWSignature,bcs->Tag对应于CSW中的dCSWTag,而bcs->Status对应于bCSWStatus.我们有些事情没道理的,有人很抢手,有人没资格,路是人走的.在bcs中成员的存储格式居然还有区别,有的是little endian的,有些却不是.对于那些little endian的,咱们需要调用像cpu_to_le32这样的宏来转换,而其她的却不需要转换,对于bcs来说,其成员Residue和Signature就需要这样转换.这些规矩仿佛是没有道理的.

1050行,和之前bcb中使用US_BULK_CB_SIGN一样,US_BULK_CS_SIGN这个宏用来标志这个数据包是一个CSW包.而US_BULK_CS_OLYMPUS_SIGN也是一个宏,不过她是专为某种变态设备专门准备的.这两个宏和接下来将提到的一些宏依然来自drivers/usb/storage/transport.h中,

110 #define US_BULK_CS_SIGN 0x53425355 /* spells out ‘USBS’ */ 111 /* This is for Olympus Camedia digital cameras */ 112 #define US_BULK_CS_OLYMPUS_SIGN 0x55425355 /* spells out ‘USBU’ */ 113 #define US_BULK_STAT_OK 0 114 #define US_BULK_STAT_FAIL 1 115 #define US_BULK_STAT_PHASE 2

对大多数普通的设备来说,如果要标志一个CSW包,其Signature会是53425355h.但是Olympus Camedia这种数码相机偏偏要标新立异,她愣是跟您换个数字,那咱也没办法.敢情人家设计者是穿美特斯邦威长大的,就是不走寻常路.

Tag就是和CBW相对应的,两个Tag应该相同.要不然也就不叫接头暗号了.前面为Tag赋值为srb->serial_number,这回自然也应该等于这个值.

而bcs->Status,标志命令执行是成功还是失败,当她是0表明命令是成功的,当她是非0,嘿嘿,肯定有问题.目前的spec规定,她只能是00h,01h,02h,而03h到FFh都是保留的,不能用,所以这里会判断她是否是大于US_BULK_STAT_PHASE,也就是说是否会大于02h,大于了当然就不行.好,这样子,就是说这些条件如果不满足的话,那么一定是有问题的.返回错误值吧.

1060行至1066行,如果residue不为0,那么说明数据没传完,或者说和预期的不一样,那么来细看一下,首先该设备应该没有设置US_FL_IGNORE_RESIDUE这个flag,老规矩,让我们看一下什么样的设备设置了这个flag,

269 /* Yakumo Mega Image 37

270 * Submitted by Stephan Fuhrmann <atomenergie@t-online.de> */

271 UNUSUAL_DEV( 0x052b, 0x1801, 0x0100, 0x0100,

272 "Tekom Technologies, Inc",

273 "300_CAMERA",

274 US_SC_DEVICE, US_PR_DEVICE, NULL,

275 US_FL_IGNORE_RESIDUE ),

276

277 /* Another Yakumo camera.

278 * Reported by Michele Alzetta <michele.alzetta@aliceposta.it> */

279 UNUSUAL_DEV( 0x052b, 0x1804, 0x0100, 0x0100,

280 "Tekom Technologies, Inc",

281 "300_CAMERA",

282 US_SC_DEVICE, US_PR_DEVICE, NULL,

283 US_FL_IGNORE_RESIDUE ),

284

285 /* Reported by Iacopo Spalletti <avvisi@spalletti.it> */

286 UNUSUAL_DEV( 0x052b, 0x1807, 0x0100, 0x0100,

287 "Tekom Technologies, Inc",

288 "300_CAMERA",

289 US_SC_DEVICE, US_PR_DEVICE, NULL,

290 US_FL_IGNORE_RESIDUE ),

291

292 /* Yakumo Mega Image 47

293 * Reported by Bjoern Paetzel <kolrabi@kolrabi.de> */

294 UNUSUAL_DEV( 0x052b, 0x1905, 0x0100, 0x0100,

295 "Tekom Technologies, Inc",

296 "400_CAMERA",

297 US_SC_DEVICE, US_PR_DEVICE, NULL,

298 US_FL_IGNORE_RESIDUE ),

299

300 /* Reported by Paul Ortyl <ortylp@3miasto.net>

301 * Note that it’s similar to the device above, only different prodID */

302 UNUSUAL_DEV( 0x052b, 0x1911, 0x0100, 0x0100,

303 "Tekom Technologies, Inc",

304 "400_CAMERA",

305 US_SC_DEVICE, US_PR_DEVICE, NULL,

306 US_FL_IGNORE_RESIDUE ),

一般的设备是不会设置这个flag的,但是确实有那么一些设备是设了这个flag的,查一查drivers/usb/storage/unusual_devs.h,发现Tekom公司的数码相机全都有这么一个问题.这个flag的意思很明确,对于这类设备不需要管在乎CSW中的那个dCSWDataResidue,因为十有八九这个字节汇报的东西是错的,是不准的,当然这也就是有一个硬件bug的例子了.所以这里判断的就是这个flag没有设,或者srb->sc_data_direction等于DMA_TO_DEVICE,这种情况下,发送给设备的数据长度不应该超过transfer_length.而1064行srb->resid本来是我们传递给usb_stor_bulk_transfer_sg的参数,记录的就是剩下的数据长度,(比如期待值是10,传递了8,那么剩余就是2.白痴都知道.),而residue刚刚被再次赋值了,不是原来的residue就是transfer_length,要知道原来的residue等于dCSWDataResidue,这是设备传递过来的,换言之,是硬件传来的数据,它未必就和我们软件得来的相同.所以srb->resid这时候就等于residue了,以硬件的为准呗.不过我想说的是,这几行代码实际上涉及到一个鲜为人知的花絮,如今这个世界每天都有人爆料,每天都有人炮轰别人,以至于我们常说生活就像宋祖德的嘴,你永远都不知道下一个倒霉的会是谁.不过这里我只爆料不炮轰.首先你看这段代码的时候,一定不明白为什么要判断传输方向是不是DMA_TO_DEVICE对吧,其实这里又是一个硬件的bug,开发设备驱动这东西,最讲究的就是实战,尤其是像usb-storage这么一个通用的模块,它要支持各种各样的设备,不管你是三星家的还是索尼家的,只要你生产的是usb mass storage设备,而且你又不准备自己专门写一个设备驱动,那么我们这个usb-storage就应该支持你这个设备.而,在实战中,我们发现,有些设备,他们在执行读操作的时候,经常会在状态阶段汇报一个错误的dCSWDataResidue,就是说比如本来没有传输完全顺利,该传几个字节就传了几个字节,按理说这种情况,dCSWDataResidue应该是记录着0,可是实际上这些设备却把这个值设成了某个正数,你说这不是胡来吗?而如果我们发现这个值是正数,那么当我们向scsi核心层反映我们我们这个命令的结果的时候就会说这个命令执行失败了.但事实上这些设备执行读操作并没有问题,这种情况属于谎报军情,罪该论斩.所以我们这里就加入这么一个标志,对于读操作,即方向为DMA_FROM_DEVICE的情况,咱们就忽略这个dCSWDataResidue,换言之,也就是忽略residue,我们直接返回给scsi那边srb->resid就可以了.反之,对于写操作,即方向为DMA_TO_DEVICE的操作,我们当然不愿意无缘无故的抛弃有效的dCSWDataResidue啦.所以对于DMA_TO_DEVICE的情况,我们最终返回给scsi核心层的srb->resid是以srb->resid和residue中那个大一点的为准.这就是为什么我们这里要判断或者我们设置了US_FL_IGNORE_RESIDUE这么一个flag,或者我们执行的是读操作,对于这两种情况我们要忽略掉residue,反之我们就不忽略.不过有趣的是,三年前,某个老外去开源社区抱怨,说他买了一个中国厂商生产的MP3,该设备在写操作的时候老是莫名其妙的报错,但是在Windows下却用得好好的.最后大家一分析,发现问题就是在这里,即这个设备在写的时候会误报dCSWDataResidue,用社区里面那些伙计的话说就是,这种设备在执行写命令的时候,会往dCSWDataResidue填写垃圾信息.本来写操作是正确的执行了,可是偏偏要让scsi那边以为操作没有执行成功.所以,某位帅哥就提交了一个patch,把这个判断方向的代码去掉了,因为反正读写都有可能出问题,那么干脆甭判断了,都给忽略掉得了,也因此,对于这种有问题的设备,就必须设置US_FL_IGNORE_RESIDUE这个flag了.当时那个patch是这样的:

===== drivers/usb/storage/transport.c 1.151 vs edited =====
--- 1.151/drivers/usb/storage/transport.c 2004-10-20 12:38:15 -04:00
+++ edited/drivers/usb/storage/transport.c 2004-10-28 10:50:42 -04:00
@@ -1058,8 +1058,7 @@
  /* try to compute the actual residue, based on how much data
  * was really transferred and what the device tells us */
  if (residue) {
- if (!(us->flags & US_FL_IGNORE_RESIDUE) ||
- srb->sc_data_direction == DMA_TO_DEVICE) {
+ if (!(us->flags & US_FL_IGNORE_RESIDUE)) {
  residue = min(residue, transfer_length);
  srb->resid = max(srb->resid, (int) residue);
  }
同时我们也把当时去开源社区抱怨的那位哥们的调试信息贴出来:
 usb-storage: Command WRITE_10 (10 bytes)
 usb-storage: 2a 00 00 00 01 37 00 00 08 00
 usb-storage: Bulk Command S 0x43425355 T 0x82 L 4096 F 0 Trg 0 LUN 0 CL 10
 usb-storage: usb_stor_bulk_transfer_buf: xfer 31 bytes
 usb-storage: Status code 0; transferred 31/31
 usb-storage: -- transfer complete
 usb-storage: Bulk command transfer result=0
 usb-storage: usb_stor_bulk_transfer_sglist: xfer 4096 bytes, 2 entries
 usb-storage: Status code 0; transferred 4096/4096
 usb-storage: -- transfer complete
 usb-storage: Bulk data transfer result 0x0
 usb-storage: Attempting to get CSW...
 usb-storage: usb_stor_bulk_transfer_buf: xfer 13 bytes
 usb-storage: Status code 0; transferred 13/13
 usb-storage: -- transfer complete
 usb-storage: Bulk status result = 0
 usb-storage: Bulk Status S 0x53425355 T 0x82 R 3072 Stat 0x0
 usb-storage: -- unexpectedly short transfer
 usb-storage: scsi cmd done, result=0x10070000
 SCSI error : <0 0 0 0> return code = 0x10070000
 end_request: I/O error, dev sda, sector 311
 应该说这段信息清晰的打印出来整个Bulk传输是怎么进行的.一共三个阶段,Command/Data/Status,这里执行的命令就是WRITE_10,本来这是一次成功的传输,但是最后返回值result却不为0,而是0x10070000,关于这个0x10070000如何出来的,我们稍候会知道.最后两行是scsi core那边的代码打印出来的,我们不用管,只是需要知道我们最终返回给scsi核心层的一个有用信息就是srb->result.所以我们看到scsi那边打印了一个reture code,和我们这里的result是一样的.其实打印的都是srb->result.很显然,srb这个东西相当于usb-storage和scsi那边的桥梁,连接了两个模块.
Ok,继续往下走.1068行开始基于CSW返回的状态,来判断结果了.判断的值就是bcs->Status.如果是0,那么表明命令执行成功了.关于bcs->Status的取值,usb mass storage bulk only spec里面规定的很清楚,参考下面这幅图
                              
我们前面看到,我们定义了三个宏US_BULK_STAT_OK / US_BULK_STAT_FAIL / US_BULK_STAT_PHASE ,分别对应00h,01h,02h.所以这里我们就用这三个宏来进行判断.先来看后两个,如果是US_BULK_STAT_FAIL,那么返回USB_STOR_TRANSPORT_FAILED,如果是US_BULK_STAT_PHASE,那么返回USB_STOR_TRANSPORT_ERROR.这俩没啥好说的,我们在drivers/usb/storage/transport.h中一共定义了四个这样的宏:
 131 /*
 132 * Transport return codes
 133 */
 134
 135 #define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */
 136 #define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */
 137 #define USB_STOR_TRANSPORT_NO_SENSE 2 /* Command failed, no auto-sense */
 138 #define USB_STOR_TRANSPORT_ERROR 3 /* Transport bad (i.e. device dead) */
其意思都很明显,从字面上就能看出来.相信受过九年制义务教育的你不会看不明白.至于这里返回这些值以后上面如何处理那我们稍后从这个函数返回了就知道了.另一个问题,FAILED和ERROR的区别也很明显,一个是说传输没问题,但是命令执行的时候有错误,另一个是传输本身就有错.
现在我们来看看US_BULK_STAT_OK的情况了,这种情况说明,传输成功了.但是这里需要判断fake_sense.什么是fake_sense?我们下一节再来专门讨论这个问题,需要知道的是,这里这三个return就意味着usb_stor_Bulk_transport()函数将结束了(当然,还有一个return,1094行,就是说如果以上情况都不属于,那当然更加是出错了,所以直接返回USB_STOR_TRANSPORT_ERROR).我们将返回usb_stor_invoke_transport(),而这更加意味着一次Bulk传输的结束.至此我们就算是把这个迷雾重重的Bulk传输给从头到尾讲了一遍.而回到usb_stor_invoke_transport()之后所需要做的就是一些错误处理了,或者说秋后算账.

如若今生再相见,哪怕流离百世,迷途千年,也愿。

Linux那些事儿之我是U盘(48)迷雾重重的Bulk传输(六) – fudan

相关文章:

你感兴趣的文章:

标签云: