amoisonicUSBmassstorage协议
主要把在实现“模拟U盘功能”过程中的⼀些调试过程记录下来,并加以解析。
⼀、背景知识
USB 组织在universal Serial Bus Mass Storage Class Spaceification 1.1版本中定义了海量存储设备类(Mass Storage Class)的规范,这个类规范包括四个 独⽴的⼦类规范,即:
1. USB Mass Storage Class Control/Bulk/Interrupt (CBI) Transport北京出租车业垄断黑幕
2.USB Mass Storage Class Bulk-Only Transport
3.USB Mass Storage Class ATA Command Block
4.USB Mass Storage Class UFI Command Specification
前两个⼦规范定义了数据/命令/状态在USB 上的传输⽅法。Bulk- Only 传输规范仅仅使⽤Bulk 端点传送数据/命令/状态,CBI 传输规范则使⽤Control/Bulk/Interrupt 三种类型的端点进⾏数据/命令/状态传送。后两个⼦规范则定义了存储介质的操作命令。ATA 命令规范⽤于硬盘,UFI 命令规范是针对USB 移动存储。 Microsoft Windows 中提供对Mass Storage 协议的⽀持,因此USB 移动设备只需要遵循 Mass Storage 协议来组织数据和处理命令,即可实现与PC 机交换数据。⽽Flash 的存储单元组织形式采⽤FAT16 ⽂件系统,这样,就可以直接在Windows的浏览器中通过可移动磁盘来交换数据了,Windows 负责对FAT16 ⽂件系统的管理,USB 设备不需要⼲预FAT16 ⽂件系统操作的具体细节。
USB(Host)唯⼀通过描述符了解设备的有关信息,根据这些信息,建⽴起通信,在这些描述符中,规定了设备所使⽤的协议、端点情况等。因此,正确地提供描述符,是USB 设备正常⼯作的先决条件。 -2.6.26内核中在利⽤USB gadget驱动实现模拟U盘时主要涉及到file_storage.c、s3c2410_udc.c等驱动⽂件(这些⽂件的具体结构,将在下⼀篇⽂章中描述)。此时我们想先从这些代码中到USB描述描述符,从中确定使⽤的存储类规范,从⽽确定协议。确定通讯协议是我们调试的基础。
存储类规范是由接⼝描述符决定的。接⼝描述符各项的定义义如下:
其中,bInterfaceClass、bInterfaceSubClass、bInterfaceProtocol可以判断出设备是否是存储类,以及属于哪种存储⼦类和存储介质的操作命令。
在file_storage.c⽂件中,
/* USB protocol value = the transport method */
#define USB_PR_CBI 0x00 // Control/Bulk/Interrupt
#define USB_PR_CB 0x01 // Control/Bulk w/o interrupt佳能960
#define USB_PR_BULK 0x50 // Bulk-only
/* USB subclass value = the protocol encapsulation */
#define USB_SC_RBC 0x01 // Reduced Block Commands (flash)
#define USB_SC_8020 0x02 // SFF-8020i, MMC-2, ATAPI (CD-ROM)
#define USB_SC_QIC 0x03 // QIC-157 (tape)
#define USB_SC_UFI 0x04 // UFI (floppy)
#define USB_SC_8070 0x05 // SFF-8070i (removable)
#define USB_SC_SCSI 0x06 // Transparent SCSI
默认的情况是:
mod_data = { // Default values
.transport_parm = "BBB",
.protocol_parm = "SCSI",
……
默认的赋值如下:
bInterfaceClass=08 表⽰:存储类
bInterfaceSubClass=0x06 表⽰:透明的SCSI指令
bInterfaceProtocol=0x50 表⽰:bulk-only 传输
2、Bulk-Only 传输协议
下⾯看看Bulk-Only 传输协议:(详细的规范请阅读《Universal Serial BusMass Storage ClassBulk-Only Transport》)
设备插⼊到USB 后,USB 即对设备进⾏搜索,并要求设备提供相应的描述符。在USBHost 得到上述描述符后,即完成了设备的配置,识别出为Bulk-Only 的Mass Storage 设备,然后即进⼊Bulk-Only 传输⽅式。在此⽅式下,USB 与设备间的所有数据均通过Bulk-In和Bulk-Out 来进⾏传输,不再通过控制端点传输任何数据。
在这种传输⽅式下,有三种类型的数据在USB 和设备之间传送,CBW、CSW 和普通数据。CBW(Command Block Wrapper,即命令块包)是从USB Host 发送到设备的命令,命令格式遵从接⼝中的bInterfaceSubClass 所指定的命令块,这⾥为SCSI 传输命令集。USB设备需要将SCSI 命令从CBW 中提取出来,执⾏相应的命令,完成以后,向Host 发出反映当前命令执⾏状态的CSW(Command Status Wrapper),Host 根据CSW 来决定是否继续发送下⼀个CBW 或是数据。Host 要求USB 设备执⾏的命令可能为发送数据,则此时需要将特定数据传送出去,完毕后发出CSW,以使Host 进⾏下⼀步的操作。USB 设备所执⾏的操
作可⽤下图描述:
CBW的格式如下:
dCBWSignature:
CBW的标识,固定值:43425355h (little endian)。
dCBWTag:
主机发送的⼀个命令块标识,设备需要原样作为dCSWTag(CSW中的⼀部分)再发送给Host;主要⽤于关联CSW到对应的CBW。
dCBWDataTransferLength:
本次CBW命令要求在命令与回应之间传输的字节数。如果为0,则不传输数据。
bmCBWFlags:
反映数据传输的⽅向,0 表⽰来⾃Host,1 表⽰发⾄Host;
bCBWLUN:
对于有多个LUN逻辑单元的设备,⽤来选择具体⽬标。如果没有多个LUN,则写0。
bCBWCBLength:
命令的长度,范围在0~16.
CBWCB:
传输的具体命令,符合bInterfaceSubClass.中定义的命令规范,此处是SCSI
CSW命令格式如下:
dCSWSignature:
CSW的标识,固定值:53425355h (little endian)
dCSWTag:
设置这个标识和CBW中的dCBWTag⼀致,参照上⾯关于dCBWTag的解释
dCSWDataResidue:
还需要传送的数据,此数据根据dCBWDataTransferLength-本次已经传送的数据得到
bCSWStatus:
指⽰命令的执⾏状态。如果命令正确执⾏,bCSWStatus 返回0 即可。
3、SCSI指令集
Bulk-Only 的CBW 中的CBWCB 中的内容即为如下格式的命令块描述符(Command Block Descriptor)。SCSI-2 有三种字长的命令,6 字节、10字节和12字节,Microsoft Windows 环境下⽀持12 字节长的命令。
对于不同的命令,其命令块描述符略有不同,其要求的返回内容也有所不同,根据相应的⽂档,可以对每种请求作出适当的回应。⽐如,下⾯是INQUIRY 请求的命令块描述符和其返回内容的数据格式:如:INQUIRY
命令描述符:
返回数据格式
Host 会依次发出INQUIRY、Read Capacity、UFI Mode Sense 请求,如果上述请求的返回结果都正确,则Host 会发出READ 命令,读取⽂件系统0 簇0 扇区的MBR 数据,进⼊⽂件系统识别阶段。
4、利⽤USB View观察结果
可通过USB View软件查看到USB设置阶段获取到的信息。
⼆、出现的主要问题
在调试过程中遇到了⼀个问题。现象是:在⽬标板加载完驱动后,即执⾏完:
# insmod g_file_storage.ko file=/dev/mtdblock2 stall=0 removable=1
后,接好USB线。此时在windows端设备出有usb storage设备加⼊,但出现不了盘符。
aidma下⾯记录下调试过程。
三、调试过程
根据规范,当完成SCSI指令集中Inquiry 命令时,可以出现盘符。所以可以通过bushound软件查看通讯过程,出原因。
下⾯是利⽤bushound⼯具在出现问题时采集到的数据。
Dev Phase Data Info Time Cmd.Phase. Ofs
--- ----- --------------------------------- ---------- ----- -----------
26 CTL 80 06 00 01 - 00 00 12 00 GET DESCRIPTR 0us 1.1.0
26 DI 12 01 10 01 - 00 00 00 10 - 25 05 a5 a4 - 12 03 01 02 ........%....... 4.8ms 1.2.0
03 01 .. 1.2.16
26 CTL 80 06 00 02 - 00 00 09 00 GET DESCRIPTR 14us 2.1.0
26 DI 09 02 20 00 - 01 01 04 c0 - 01 .. ...... 3.9ms 2.2.0海尔t628拆机
26 CTL 80 06 00 02 - 00 00 20 00 GET DESCRIPTR 16us 3.1.0
26 DI 09 02 20 00 - 01 01 04 c0 - 01 09 04 00 - 00 02 08 06 .. ............. 4.9ms 3.2.0
50 05 07 05 - 81 02 40 00 - 00 07 05 02 - 02 40 00 @......@.. 3.2.16
26 CTL 80 06 00 03 - 00 00 02 00 GET DESCRIPTR 60us 4.1.0
26 DI 09 02 20 00 - 01 01 04 c0 - 01 .. ...... 3.9ms 2.2.0
26 DI 04 03 .. 3.9ms 3.1.0
26 CTL 80 06 00 03 - 00 00 04 00 GET DESCRIPTR 15us 5.1.0
26 DI 04 03 09 04 .... 3.9ms 6.1.0
26 CTL 80 06 03 03 - 09 04 02 00 GET DESCRIPTR 10us 1.2.16
26 DI 1a 03 .... 4.0ms 6.2.0
26 CTL 80 06 03 03 - 09 04 1a 00 GET DESCRIPTR 18us 7.1.0
26 DI 1a 03 33 00 - 37 00 32 00 - 30 00 34 00 - 31 00 37 00 ..3.7.2.0.4.1.7. 4.9ms 7.2.0
35 00 36 00 - 37 00 37 00 - 35 00 5.6.7.7.5. 7.2.16
26 CTL 00 09 01 00 - 00 00 00 00 SET CONFIG 16us 8.1.0
26 CTL 01 0b 00 00 - 00 00 00 00 SET INTERFACE 60ms 9.1.0
26 CTL a1 fe 00 00 - 00 00 01 00 CLASS 62ms 10.1.0
26 DI 00 . 3.9ms 10.2.0
26 DO 55 53 42 43 - 08 60 e0 86 - 24 00 00 00 - 80 00 06 12 USBC.`..$....... 985us 11.1.0
00 00 00 24 - 00 00 00 00 - 00 00 00 00 - 00 00 00 ...$........... 11.1.16
26 DI 00 80 02 02 - 1f 00 00 00 - 4c 69 6e 75 - 78 20 20 20 ........Linux 1.0ms 12.1.0
46 69 6c 65 - 2d 53 74 6f - 72 20 47 61 - 64 67 65 74 File-Stor Gadget 12.1.16
30 33 31 32 0312 12.1.32
26 CTL 80 06 00 02 - 00 00 20 00 GET DESCRIPTR 893ms 13.1.0
26 DI 09 02 20 00 - 01 01 04 c0 - 01 09 04 00 - 00 02 08 06 .. ............. 4.1ms 13.2.0
50 05 07 05 - 81 02 40 00 - 00 07 05 02 - 02 40 00 @......@.. 13.2.16
26 CTL 80 06 00 02 - 00 00 20 00 GET DESCRIPTR 2.7sc 14.1.0
26 DI 09 02 20 00 - 01 01 04 c0 - 01 09 04 00 - 00 02 08 06 .. ............. 4.4ms 14.2.0
50 05 07 05 - 81 02 40 00 - 00 07 05 02 - 02 40 00 @......@.. 14.2.16
26 USTS 05 00 00 c0 no response 2.8sc 15.1.0
注意上⾯红⾊部分的代码,DO发出了55 53 42 43开始的CBW命令块,命令码是12,即Inquiry命令。要求⽬标返回Inquiry命令要求的数据,长度是0x24。接下来设备端通过DI返回了设备信息。按照规范,
在返回完了数据后,设备端还应该通过DI向系统返回CSW的值。但实际的捕获内容并没有。所以导致不能正确出现盘符。
在file_storage.c中,发送数据时都会调⽤到start_transfer()函数。在此函数中加⼊printk调试语句,观察现象。发现只要加⼊的调试语句,windows端就能够正常设别设备了。于是,可以猜测是因为需要在连续两次发送之间加上⼀些延时。在函数中加⼊udelay(800)后,windows系统可以正常发现设备了。具体的代码,将在下⼀遍⽂章中解析。
下⾯是程序正常后,⽤bushound捕获到的数据。
红⾊部分,可以看出设备正确的按照规范在发送完数据后,返回CSW信息。
四、总结做好USB gadget驱动、或者USB host驱动调试需要:
灭火器的维修与报废·掌握⼀定的知识基础
包括:USB协议、具体的类设备规范、USB驱动程序架构、USB设备端控制器操作等。
·合理利⽤调试⼯具。
包括:USB view 、bushound 、及⼀些硬件USB信号分析仪。