1. SCSI 协议定义
小型计算机系统接口(SCSI,Small Computer System Interface):是一种用于计算机及其周边设备之间(硬盘、软驱、光驱、打印机、扫描仪等)系统级接口的独立处理器标准。SCSI 标准定义命令、通信协议以及实体的电气特性(换成 OSI 的说法,就是占据物理层、链接层、套接层、应用层),最大部分的应用是在存储设备上(例如硬盘、磁带机),除外,SCSI 可以连接的设备包括有扫描仪、光学设备(像 CD、DVD)、打印机等等,SCSI 命令中有条列出支持的设备 SCSI 周边设备。理论上,SCSI 不可能连接所有的设备,所以有 “1Fh - unknown or no device type” 这个参数存在。
SCSI 协议的主要功能是在主机和存储设备之间传送命令、状态和块数据。
2. SCSI 协议特点
(1)SCSI 可以划分为 SCSI-1、SCSI-2、SCSI-3,最新的为 SCSI-3,也是目前应用最广泛的 SCSI 版本。
3. SCSI 协议
Scsi 协议是U盘,读卡器这些大容量存储所使用的协议,整个协议是工作在批量传输,分为输入跟输出两个方向。到两个概念:CBW;CSW。当这些大批量设备传输完成了后,主机会识别到你当前的设备是大容量设备,根据 SCSI 的协议来发送 CBW 包下来,CBW 包可理解为主机的一个命令包,我们要去解释这个命令包去进行相应的处理,当数据处理完后,就要回复 CSW 包,这相当于 SCSI 协议的一个应答包,是对刚才 CBW 包的处理情况的一个回应。
设备插入到 USB 后,USB 即对设备进行搜索,并要求设备提供相应的描述符。在USB Host 得到描述符后,即完成设备的配置,识别出为 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 端发送的数据必须符合 CBW 格式(31byte,小端模式),而 Mass Storage 设备的应答,其格式必须符合 CSW 格式(13byte,小端模式)。中间过程传输的数据,根据不同的命令,格式也有不同地要求。
3.1 CBW 包结构及解释:
名称 | 解释 |
---|---|
dCBWSignature | CBW 数据包标识,为常数 0x43425355(小端) |
dCBWTag | 主机发送的一个命令块标志,设备应该回复一个包含同样信息的 dCSWTag 来相应 CBW |
dCBWDataTransferLength | 在执行本条命令的时候,主机期待通过 Bulk 端点传输的数据长度,如果此值为 0,表示主机和设备之间没有数据传输,设备应该应该忽略 bmCBWFlags 的值 |
bmCBWFlags | 如果 dCBWDataTransferLength = 0,此值忽略,否则表示如下:Bit7:0 — Data out (主机——>设备);1— Data in(设备——>主机);Bit6:不使用,主机应该将其设置为 0;Bit 5…0:保留,主机应该将其设置为0 |
bCBWLUN | 表示设备正在发送的逻辑单元,对于支持多个逻辑单元的设备,主机应根据对应的地址设置数值,否则,主机应该将数值设置为 0 |
bCBWCBLength | CBW 命令块数据中的有效字节,合法的数值为 1~16,其他值被保留 |
CBWCB | 设备执行的命令块,符合bInterfaceSubClass.中定义的命令规范,此处是SCSI指令集 |
结构体定义:
typedef struct _CBW
{
unsigned long dwCBWSignature; // dwCBWSignature: the constant 0x55 0x53 0x42 0x43(LSB)
unsigned long dwCBWtag; // dwCBWtag:
unsigned long dwCBWXferLength; // dwCBWXferLength:number of bytes to transfer
unsigned char bCBWFlags; // bmCBWFlags:
// Bit 7: direction - the device shall ignore this bit if the
// dCBWDataTransferLength field is zero, otherwise:
// 0 = Data-Out from host to the device,
// 1 = Data-In from the device to the host.
// Bit 6: obsolete. The host shall set this bit to zero.
// Bits 5..0: reserved - the host shall set these bits to zero.
unsigned char bCBWlun; // bmCBWlun:
unsigned char bCBWCBLength; // bCBWLength: 0x01..0x10
CDB cdb; // CBWCB: the command descriptor block
} CBW;
3.2 CSW 包结构及解释:
名称 | 解释 |
---|---|
dCSWSignature | CSW 命令块标识,值为 0x53425355(小端) |
dCSWTag | 值为收到的 dCBWTag ,同样返回给 主机 |
dCSWDataResidue | 仍需要传送的数据,等于 dCBWDataTransferLength-本次已经传送的数据 |
bCSWStatus | 指示命名是否成功传输(00H—成功;01H—失败;02H—传输过程错误;03~FFH:保留) |
结构体定义:
typedef struct _CSW
{
unsigned long dwCSWSignature; // dwCSWSignature: the constant 0x53 0x42 0x53 0x55
unsigned long dwCSWtag; // dwCSWtag:
unsigned long dwCSWResidue; // dwCBWXferLength: number of bytes not transferred
unsigned char bCSWStatus; // bCSWStatus:
// 00h command Passed ("good status")
// 01h command failed
// 02h phase error
// 03h to FFh reserved
}CSW;
3.3 SCSI命令集——Mass Storage 设备
指令 | 标识 | 解释 |
---|---|---|
0x00 | Test Unit Ready | 查询设备是否ready |
0x03 | Request Sense | 主机请求设备返回执行结果,及获取状态信息 |
0x04 | Format Unit | 格式化存储设备 |
0x12 | Inquiry | 获取设备信息 |
0x1A | Mode Sense(6) | 向 host 传输参数 |
0x5A | Mode Sense(10) | 向 host 传输参数 |
0x25 | Read Capacity(10) | 读取设备容量 |
0x28 | Read(10) | Host 从设备读取数据 |
0x2A | Write(10) | Host 写数据到存储设备 |
0x23 | Read Format Capacity | 查询当前容量及可用空间 |
0x15 | Mode Select(6) | 允许 Host 对外部设备设置参数 |
0x55 | Mode Select(10) | 允许 Host 对外部设备设置参数 |
0x1E | Prevent/Allow Medium Removal | 禁止/允许存储介质移动 |
0x1B | Start/Stop Uint | 加载/卸载可移动设备 |
0x2B | Seek(10) | 为设备分配地址 |
0xA0 | Report LUNs | 索取设备的 LUN 数和 LUN 清单 |
0x2F | Verify | 在存储中验证数据 |
命令举例:
READ(10) :0x28
3.4 SCSI 指令流程
USB MSC设备中的固件(firmware)或者硬件(hardware),必须要实现下面这些功能:
(1)检测和响应通用的 USB Request 和 USB 总线上的事件;
(2)检测和响应来自 USB 设备的关于信息或者动作的 USB Mass Storage Request;
(3)检测和响应,从 USB Transfer 中获得的 SCSI Command。这些业界标准的命令,是用来获得状态信息,控制设备操作,向存储介质块中读取(read block)和写入(write block)数据的。
指令收发简述:
a. 主机首先发出 Inquiry 命令,设备响应了 Inquiry 之后就可以看到盘符;
b. Inquiry 之后会发出 ReadFormatCapacity 命令,这个命令在 SCSI 规范中是“厂家自定义命令”,可参考 UFI 命令集文档(实际上,U 盘所使用的所有 SCSI 命令集都可以参考 UFI 文档,它比 SCSI 标准文档更简洁明了)。注意:这个命令在BusHound 里是没有描述的,必须在 “Device” 选项页里勾选上这个 U 盘所对应的USB Mass Storage Device 这个节点,才能看到这个命令的数据流;
c. ReadFormatCapacity 之后会发出 ReadCapacity 命令,ReadCapacity 完成后就会发送 Read(10) 读取U盘的第一个扇区;
d. U盘读数据(读扇区)时会发送 Read(10);
e. U 盘写数据时(写扇区)会发送 Write(10);
f. Test Unit Ready 会在无其他数据传输时会定时发送,如果设备没有回应成功的 CSW 给主机,则主机认为设备已不存在。此时如果再双击磁盘图标,Windows 会提示“请插入磁盘”;
g. Verify 在写数据时有用,表示核实数据,一般直接返回成功的 CSW 就可以了。一般来说,数据校验的工作在接收和向介质写数据时就已经顺带做了,如果发现错误,则直接告诉主机那次的数据传输有误,不会等到主机 Verify 时;
h. RequestSense:如果 CSW 指示此次传输不成功,那么主机会发出此请求;
i. StartStop 暂时未发现大用处,一般直接返回成功的CSW;
j. MediumRemoval 在 U 盘被 Eject 的时候有用,处理不正确会 Windows 会弹出错误信息;
k. ModeSense6/ModeSense10 这两个命令可以不支持(不支持不代表不反应,任何一个命令你都要做出反应,对于不支持的命令,可以通过 STALL 握手来向主机表明),暂时也未遇到过什么异常情况,而且我查看过一些 U 盘,有相当一部分就是随便回了几个数据给主机。这两个命令只会在 U 盘插入后发送一次,此后不再发送。
3.5 数据交换流程
通过 bulk 端点进行的数据传输,都遵循这样一个过程,即三个阶段:
CBW->DATA->CSW
CBW 是一个数据块,携带主机发给设备的 SCSI 命令。接收了 CBW 后,设备就可以从中知道在接下来的 DATA 阶段中该干什么。DATA阶段(无数据需要传输;
IN 传输(设备到主机)或 OUT 传输(主机到设备))。
4. 数据实例分析
下图是通过 bus hound 抓取 U 盘插入的数据,可以看到 SCSI 命令及 CBW 、CSW 的指令包交互过程: