笔记写的比较杂乱,仅供自己以后复习参考。
命令层 块命令和流命令等
协议层 xxoo
物理层
第二章 SCSI基础
协议的重要性
总线空闲条件:SCSI总线上没有活动
选择阶段
SCSI ID表示总线控制权优先级
消息阶段 协议使用消息来报告错误 命令状态和其他信息 也可以使用消息发送控制信息
命令阶段
启动器发送一个带有命令指令和参数数据的数据块给目标器
如果目标器要报告命令块格式或者参数错误 那么事务处理又机虐了消息阶段
数据阶段 是否产生数据阶段取决于发送的命令 命令决定了数据传输的方向
状态阶段 标志着SCSI命令的完成
最后的事务处理的还是消息阶段
启动器 SCSI设备
第三章 SCSI阶段
在一个简单的SCSI事务处理中 启动器为取得总线的控制权而进行仲裁
一旦获得了对总线的控制权它就选择一个目标器进行连接
目标器做出反应
SCSI设备可以分为启动器(initiator)或目标器(target),例如SCSI主机适配器是启动器,硬盘驱动器是目标器.一个S最小的可行配置是一个启动器和一个目标器.
计算机通过主机适配器连接到SCSI总线上,主机适配器一般在主板上,外围设备通过一个控制器连接到SCSI总线上,控制器一般在设备的电路板上.有时一个控制器也可以连接多个设备,叫做桥式连接.
启动器把一个命令发送到目标器,目标器执行命令并把结果通知给启动器,例如格式化硬盘就只需发送FORMAT UNIT命令然后就可以将控制权完全交给驱动器,格式化完成后,启动器仅仅得到命令执行成功或失败的信息.
总线状态
BSY SEL C/D I/O MSG ATN
第五章 SCSI命令
SCSI的命令和参数被封装在称为CDB命令描述块的结构中
不同的命令组对应于不同的长度命令描述符
0号组CDB是6字节
1号组和2号组是10字节长
5号组的CDB占用12个字节
其他由厂商自定义
CDB的第一个字节描述命令的操作码
高三位表示命令组 0-7
低5位表示命令码
CDB的第二个字节的高三位表示一个LUN(逻辑单元号)
低五位可能是保留的 也可能是以后字段的一部分 取决于命令组
命令参数 LUN字段后就是命令参数字段
对于直接存取设备来说 它包含的是逻辑块地址
对于传输数据的命令来说 包含的是传输长度
也可以包含和特定命令或者设备类型相关的值
控制字段
每个命令描述符的最后一个字段是控制字段
参数列表
多字节字段是按大端法来存储的
和X86相反
▲Scsi Pass through requests go to the miniport as
SRB_FUNCTION_EXECUTE_SCSI
///
硬盘驱动器模型
IDE接口前身——ST506
ATAPI使用IDE作为其物理接口
但命令使用的是SCSI命令
数据通过逻辑块号来定位(LBA?)
格式化整个磁盘则是由某个命令直接触发的
SCSI-2总线可以对8个设备进行编址
SCSI-3可以更多
设备的SCSI总线地址称为SCSI ID
这些设备扮演了启动器或者目标器的角色
启动器和目标器
启动器是一个在SCSI总线上触发任务的设备
而目标器则是执行该任务的设备
逻辑单元LUN
LUN的概念
LUN的全称是Logical Unit Number,也就是逻辑单元号。我们知道SCSI总线上可挂接的设备数量是有限的,一般为6个或者15个,我们可以用Target ID(也有称为SCSI ID的)来描述这些设备,设备只要一加入系统,就有一个代号,我们在区别设备的时候,只要说几号几号就ok了。
而实际上我们需要用来描述的对象,是远远超过该数字的,于是我们引进了LUN的概念,也就是说LUN ID的作用就是扩充了Target ID。每个Target下都可以有多个LUN Device,我们通常简称LUN Device为LUN,这样就可以说每个设备的描述就有原来的Target x变成Target x LUN y了,那么显而易见的,我们描述设备的能力增强了。就好比,以前你给别人邮寄东西,写地址的时候,可以写:
xx市人民大街54号 xxx(收)
但是自从高楼大厦越来越多,你不得不这么写:
xx市人民大街54号xx大厦518室 xxx (收)
所以我们可以总结一下,LUN就是我们为了使用和描述更多设备及对象而引进的一个方法而已,一点也没什么特别的地方。
这里提到一点,由于TARGET Id的数量是有限的 那么我们为了表示更多的SCSI设备,就引入了LUN的概念
把设备一维的标示改成二维的标示TargetId = X, LUN = Y
客户机-服务器模型是实现SCSI系统很好的途径
从这个角度来说,PC是客户端 它自己的SCSI硬盘是服务器
PC向硬盘发送一个请求 硬盘执行请求并把数据传送回来
LBA->CHS
SCSI命令模型:
SCSI命令可以看作是对远程过程的调用:
Service Response = Execute Command(Task Identifier, Command, Descriptor Block, [Task Attribute], [Data Output Buffer], [Data Input Buffer], [Command Buffer], [Command Length], [Autosense Request], [Sense Data], Status)
SCSI命令由一个启动器发送到目标器的LUN,LUN的设备服务器执行命令,并返回一个状态.命令的输入输出方向是以启动器为参照的.
★在SCSI命令中必须实现:
1.任务标识符:只有SCSI-3中有,由一套64位的数字组成,分成启动器标识符,目标器标识符和LUN标识符.排序后的任务还有一个附加的标签.
2.命令描述块(CDC):包含完整的SCSI命令.
3.状态字节:命令结束后,状态字节给出一个命令是否被正确执行的信息,而且它还提供了一些关于命令结束的附加信息.
DISK.SYS-类驱动
SCSIPORT.SYS-端口驱动
ATAPI.SYS-微端口驱动
IOCTL_SCSI_GET_INQUIRY_DATA
获取每个SCSI总线和相应的驱动器所控制的设备的信息
在这些miniport驱动上面的是称为SCSIPORT的驱动
SCSIPORT驱动对于系统中的所有SCSI请求提供了唯一的入口点
~~~~~~~~~~~~
把系统特有的SCSI IO请求转化成标准的SCSI 命令描述块COMMAND DESCRIPTOR BLOCK
也就是传说中的CDB
~~~~~~~~~~~
并把这些请求发送给适当的MINIPORT驱动
由于其硬件细节被隐藏在了miniport驱动中,所以高层驱动可以调用
scsiport驱动来执行所有SCSI IO操作 而不用管硬件接口
一个限制
如果某驱动对一个特殊设备发出请求
我们要做的是向该类驱动发送和SCSI PASS-THOUGH命令
~~~~~~~~~~~~
而不是发送给SCSIPORT
~~~~~~~~~~~~~~~~~~~~
避免应用程序在某有通知类驱动的情况下发送命令
而且还可以允许类驱动保持对设备的控制权
这里难道是为了防止绕过类驱动直接向SCSIPORT发送SCSI命令?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SCSI_PASS_THROUGH 和SCSI_REQUEST_BLOCK这两个结构很像啊~
The values of the DataIn member correspond to the SCSI_IOCTL_DATA_IN, SCSI_IOCTL_DATA_OUT, and SCSI_IOCTL_DATA_UNSPECIFIED
SCSI_PASS_THOUGH是可以让RING3通过DEVICE_IO_CONTROL来直接发CDB
SCSI_REQUEST_BLOCK是在驱动下 自己构造IRP包来发CDB
■如果是读取ATA设备 那么我们就不用填PATHID TARGRT ID LUN?
是的 这三个参数填0就行
[WIN32 SCSI支持模型]
win32应用程序 WIN32
↓
文件系统驱动 FSD
↓
磁盘类驱动(DISK.SYS) CD-ROM类驱动...
↓
SCSIPORT驱动(SCSIPORT.SYS) 端口驱动
↓
ATAPI(EIDE) Miniport驱动(atapi.sys atapi ide miniport driver) 微端口驱动
↓
标准IDE接口
详细的图参考 SCSI程序员指南p134
----------------------------------------分割线---------------------------------
[走aspi路线]
win32应用程序
↓
ASPI管理器
↓
ASPI驱动
↓
SCSIPORT驱动 剩下的同上图
atapi直接就可以接收SCSI指令。。。DISK.SYS也是通过CLASSPNP把SCSI转发到atapi的
scsi设备和scsi指令是两回事,DISK。SYS通过CLASSPNP把SCSI命令发到你的设备,如你是atapi的就发往他,如你是scsi设备就发往SCSI端口驱动
CDB命令描述块结构
操作码
命令特定参数
控制字节
每个命令的第0字节就是操作码 他定义了命令的类型和长度
高三位代表命令所属的命令组 低五位表示命令本身
每个命令组都有一个命令长度
因为对命令的第一个字节也就是操作码进行解析后
目标器就知道这个命令还剩下多少字节
对于不同类型的设备来说同样的操作码可以被解释为不同的命令
尽管通常它们之间还是有一点相似之处的
例如操作码0AH在磁盘和磁带设备表示WRITE 写命令 然后对于处理器设备
它代表SEND命令 而且命令的结构也可能是不同的。
因此,你不能仅仅从操作码来推断命令,必须知道这个命令是用在哪个设备上的
命令组
代表命令组的高三位可以有2^3=8个不同组合
所以可以代表8个命令组
除了保留组绝对不可以使用外 剩下的都可以使用
我们知道,Windows管理驱动设备栈,是使用的DeviceObject 中的AttachedDevice域,例如在我的虚拟机上,一个磁盘请求IRP发送到Disk.sys的磁盘设备后,会被接着转发到总线上的Atapi.sys的端口设备,实际是存在这样的关系:Atapi.sys的设备(例如\Deivce\Ide\IdeDevicePOTOLO-3)的AttachedDevice域 = Disk.sys的设备(例如\Device\Harddisk0\DR0) 。
\Device\HarddiskX\DRX ---------> \Deivce\Ide\IdeDevicePOTOLO-X
SENSE DATA
当一个SCSI设备(通常是一个LUN)发现它自己处于异常状态时,它就拒绝执行下面的命令
并返回一个CHECK CONDITION状态
它在这种状态下产生18个字节的数据 这个数据包括经过编码的关于错误的信息
就被称作 SENSE DATA
typedef struct _SENSE_DATA {
BYTE Valid;
BYTE SegmentNumber;
BYTE FileMark;
BYTE Information[4];
BYTE AdditionalSenseLength;
BYTE CommandSpecificInformation[4];
BYTE AdditionalSenseCode;
BYTE AdditionalSenseCodeQualifier;
BYTE FieldReplaceableUnitCode;
BYTE SenseKeySpecific[3];
} SENSE_DATA, *PSENSE_DATA; SRB(SCSI_REQUEST_BLOCK结构)
typedef struct _SCSI_REQUEST_BLOCK {
USHORT Length;
UCHAR Function;
UCHAR SrbStatus;
UCHAR ScsiStatus;
UCHAR PathId;
UCHAR TargetId;
UCHAR Lun;
UCHAR QueueTag;
UCHAR QueueAction;
UCHAR CdbLength;
UCHAR SenseInfoBufferLength;
ULONG SrbFlags;
ULONG DataTransferLength;
ULONG TimeOutValue;
PVOID DataBuffer;
PVOID SenseInfoBuffer;
struct _SCSI_REQUEST_BLOCK *NextSrb;
PVOID OriginalRequest;
PVOID SrbExtension;
union {
ULONG InternalStatus;
ULONG QueueSortKey;
};
UCHAR Cdb[16];
} SCSI_REQUEST_BLOCK, *PSCSI_REQUEST_BLOCK;
我的猜测:
由于ATA设备不是SCSI设备 那么其中的PathId(总线ID),TargetId(目标器ID)和LUN都没有意义,这样向ATA发SRB的时候,这些区域填0就OK(不知正确与否)
参考资料:
《SCSI程序员指南》
《SCSI总线和IDE接口:协议、应用和编程》