USB系列之三:从你的U盘里读出更多的内容

U盘是我们最常使用的一种USB设备,本文继续使用DOSUSB做驱动,试图以读取扇区的方式读取你的U盘。
    本文可能涉及的协议可能会比较多。
一、了解你的U盘
    首先我们用上一篇文章介绍的程序usbview.exe去看一下你的U盘,我在本文中用于测试的U盘情况如下:

    Device Descriptor: (设备描述符)
    USB Address:             1
    Length:                  18
    Descriptor Type:         1
    USB Specification nr.:   0x0110
    Calss Code:              Class code specified by interface
    Subclass Code:           0x00
    Protocol Code:           0x00
    MAX Packet Size:         0x08
    Vendor ID:               0x058f
    Product ID:              0x9321
    Device Code:             0x0100
    Manufacture Index:       1
    Product Index:           2
    Serial Number Index:     0
    Number of Configuration: 1

    String Descriptor: (字符串描述符)
    Manufacturer: Alcor Micro
    Product: Mass Storage Device

    Configuration Descriptor: (配置描述符)
    Length:               9
    Descriptor Type:      2
    Total Length:         32
    Number of Interfaces: 1
    Configuration Value:  1
    Configuration Index:  0
    Attributes:           Bus Powered
    Max Power:            50mA

    Interface Descriptor: (接口描述符)
    Length:               9
    Descriptor Type:      4
    Interface Number:     0
    Alternate Setting:    0
    Number of Endpoints:  2
    Interface Class:      Mass Storage Device
    Interface Sub Class:  6
    Interface Protocol:   80
    Interface Index:      0

    Endpoint Descriptor: (端点描述符)
    Length:               7
    Descriptor Type:      5
    Endpoint Address:     1 OUT endpoint
    Attributes:           Bulk
    Max Packet Size:      64
    Interval:             0

    Endpoint Descriptor: (端点描述符)
    Length:               7
    Descriptor Type:      5
    Endpoint Address:     2 IN endpoint
    Attributes:           Bulk
    Max Packet Size:      64
    Interval:             0

各种描述符的含义在以前的文章中介绍过了,或者去翻阅USB的specification,这里就不多说了,我们从接口描述符开始就一些关键点进行一下说明。
    首先看接口描述符,Interface Class = 8,表明是Mass Storage Device;Sub Class = 6,表明执行SCSI命令;Interface Protocol = 0x80,表明支持Bulk传输;另外,Number of Endpoints = 2,表明有两个端点。
    两个端点描述符要注意的是,Endpoint Address = 1的是OUT端点,Endpoint Address = 2的是IN端点,有些可能会不一样;有些U盘可能还会有第三个端点,比如支持中断传输的U盘还会有一个Interrupt端点,不过这都没有关系。
    我大概看了我手头有的5个U盘,都支持批量传输,且支持SCSI命令,所以,这可能是一个比较典型的例子,我们就以它为例。

二、CBW(Command Block Wrapper)和CSW(Command Status Wrapper)

在《USB系列之一》中,我们安装了一个DOSUSB,在《USB系列之二》中,我们利用USBDOS读取了所有的描述表,掌握这些内容需要了解USB协议1.1(USB Specification Revision 1.1)即可,当然还要了解USBDOS,不过这个比较简单。

在系列一和系列二中,我们已经对DOSUSB的一个数据结构URB有所了解,本文中还要大量用到,我们还接触了一个结构叫device_request,这个结构是在USB协议中定义的,用于向设备发送命令(Request),本文也会用到。

与前面不同的是,前面的两个系列可以针对任何USB设备,比如U盘、摄像头、打印机等,而本文将只针对我们经常使用的USB设备----U盘,如果你打算尝试本文所介绍的内容,请准备好一个U盘,什么样子的都行,或者是一个USB读卡器,不过要记得插一张卡进去,实际上本文所载范例就是使用一个USB的CF卡读卡器完成的,不用担心损害你的U盘中的数据,本文不会对U盘进行任何写操作,仅仅做一些读操作。

这个系列中我们需要针对U盘读更多的规范,如下:

不用为规范发愁,实际上,前两个规范都很短,其中第一个对实际编程没有什么作用,但最好看一下;第二个规范连目录一共22页,其中13页以前的内容可以跳过(很多和USB Specification中相同),第三个规范主要看第6章,第四个规范主要看第5章,后两个规范在编程时需要经常翻阅,以便了解你正在实现的SCSI命令的具体格式和参数。

本节我们主要介绍两个新的数据结构,这两个结构都是在第二个规范中定义的。

第一个数据结构叫CBW(Command Block Wrapper)

    这个结构将承载具体的与设备有关的命令发送到设备上去,这个结构分成两部分,第一部分从byte[0]--byte[14]共15个字节,第而部分从byte[15]--byte[30]共16个字节,整个数据结构为31个字节。规范中并没有定义第二部分的内容,这是因为第二部分承载的具体的命令,既与命令集(SCSI命令集)有关,也与具体的命令有关,我们使用SCSI命令集,所以后16个字节的内容在前面提到的后面两个规范中有定义。

比如我们要向设备发出一个SCSI命令INQUIRY(我们姑且先不要管命令的含义),那么这个命令的结构在SPC-3的第142页有定义,如下:


    对于SCSI INQUIRY这条命令而言,CBW的第二部分的定义就是上面的这六个字节,不同的命令,定义也会不同。

好,我们回到CSW的结构上来,根据规范,dCBWSignature的值必须是0X43425355,其实就是USBC这几个字母倒过来,这是因为CBW的字符顺序是little endian(这个东东在以前有关网络编程的文章中介绍过),而我们PC机中的字符顺序是big endian,所以要颠倒一下,总之写dCBWSignature = 0X43425355就OK了;dCBWTag仅仅是一个标志,你可以填任何值,这里要先说一下CSW(Command Status Wrapper),我们每发出一个命令,设备都会返回一个CSW(这个东东下面很快就要介绍了),以说明命令的执行状态,这个结构中也有Signature和Tag这两个字段,其中Tag字段和发出命令时CBW中的Tag字段相同,这样就可以区分这个CSW是和那个CBW对应的了,至于Signature,下面再说。

下一个字段是dCBWDataTransferLength,表示的是当这个命令发出后,我们希望设备返回数据的字符数或者我们要向设备传输的字符数,本文仅涉及从设备返回数据,不涉及向设备传输数据;举例来说:我们发送INQIURY命令到设备,按照SPC-3第144页的说明,该命令返回的数据至少为36个字节,所以,此时这个字节应该填36;再如:我们读取U盘的一个扇区,如果扇区的长度是512个字节,那么这个字段就要填512。

再下来是bmCBWFlags字段,这个字段只有bit 7有意义,为0表示要向设备传输数据,为1表示要从设备获得数据。

bCBWLUN字段总是填0,因为绝大多数的U盘都不支持多LUN(Logical Unit Number),只有一个逻辑单元自然好吗就是0了。

bCBWCBLength字段是只CBW第二部分的长度,像前面举例的INQUIRY命令,长度为6个字节,则这个字段就应该填6,再如:READ(10)命令的长度是10个字节(SBC-2第42页有定义),这个字段当然要填10了。

第二个要说的数据结构是CSW,当host向device发送一个CBW后,接着就可以从device收到数据(或者发数据到device),当接受完所需的的数据后,就可以从device获得一个CSW(Command Status Wrapper),CSW的结构如下:

前面说过,在CBW中的dCBWSignature的值恒为:0x43425355,得到的CSW中的dCSWSignature的值为:0x53425355,dCSWTag与dCBWTag中的一致。

在得到的CSW中,恒定有13个字节,bCSWStatus的定义如下:

三、发送命令和接收数据

我们知道USB协议中定义了三种传输方式,控制传输、批量传输、中断传输和实时传输,在《USB系列二》中我们一直都在使用控制传输,我们应该比较熟悉了,本文中将涉及批量传输。

我们在使用控制传输时,我们设置好URB启动传输事务,相应的结果将返回到制定得buffer中,批量传输没有那么简单,批量传输分为输出事务和输入事务,我们应该注意到,前面在看U盘的描述表时,在端点这一级有两个端点,一个叫OUT端点,一个叫IN端点,当我们启动一个输出事务时,一定要发送给OUT端点,当我们启动一个输入事务时,一定要发送到输入端点。下面我们简单描述一下如何启动批量传输事务。

在使用控制传输时,我们应该阅读过DOSUSB的说明,并且对URB结构比较熟悉,URB中有一个字段叫transation_type,当这个值为0x2d时为控制传输;当为0x69时为批量传输的IN事务;当为0xe1时为批量传输的OUT事务;当我们启动一个传输时,一定要正确地设置这个值。

我们以一个具体的例子来说明如何启动一个传输,我们以SCSI INQUIRY命令为了,关于这个命令的定义在SPC-3的第142页--157页有说明,篇幅很长,但绝大多数篇幅用来解释返回数据的含义,我们可以暂时不去理会。首先我们要填写CBW结构,CBW结构的第一部分的填写前面已经说的很明白了,第二部分的定义在SPC-3的第142页,共有6个字节,我们要按照定义填写好,实际上只要填两个字段,一个是OPERATION CODE = 0X12,第二个就是ALLOCATION CODE = 36,表示需要返回36个字节的内容;CBW填好后,我们开始填写URB,首先把CBW的偏移和段地址放到URB的buffer_off和buffer_seg中,把transation_type=0xe1,表示一个输出事务,注意把end_point字段一定要放OUT endpoint的地址,从前面的描述符表中看,应该是1(2是IN endpoint的地址,你的机器可能不同),其它字段的填法在《USB系列二》中已经介绍过了,填完以后调用DOSUSB,这样,一个承载着INQUIRY命令的输出事务就发送到由URB中dev_add和end_point两个字段指定的端点上去了。

接下来我们要接收device返回的执行INQUIRY命令的结果,这要启动一个输入事务,相对容易一些,只要填写URB就可以了,把transation_type=0x69,把end_point填上OUT endpoint的地址,本例中为2,buffer_off和buffer_seg指向缓冲区buffer,把buffer_length和actual_length均填为64,因为前面端点描述符表中写明包的最大长度为64,其它字段按常规填写,调用DOSUSB,在buffer中就可以得到返回的内容,按照SPC-3中对返回内容的解释即可了解设备的一些情况。

接收晚数据后,不要忘了接收CSW,方法也是启动一个输入事务,与接收数据完全相同,然后根据CSW的结构解释其含义。至此一个命令执行完毕。

四、范例

在本文的范例中,我们实现了如下内容:

  • 实现了Bulk-Only Mass Storage Reset
  • 实现了Get Max LUN
  • 实现了SCSI INQUIRY Command
  • 实现了SCSI READ CAPACITY (10) Command
  • 实现了SCSI REQUEST SENSE Command
  • 实现了SCSI TEST UNIT READY Command
  • 实现了SCSI READ (10) Command

最后的一个命令,我将从你的U盘上读出一个扇区。

最前面的两个命令,请翻阅《Universal Serial Bus Mass Storage Class - Bulk-Only Transport》第7页;INQUIRY、REQUEST SENSE、TEST UNIT READY三个命令请翻阅SPC-3的第142、221和232页;READ CAPACITY(10)和READ(10)命令,请翻阅SBC-2的第42和44页。

源代码请在下面网址下载:

http://blog.hengch.com/source/reader.rar

各种概念在前面已经介绍过了,程序无非就是实现这些概念,几乎所有的代码都是围绕着填写数据结构和显示返回结果的,所以代码本身并不难,更重要的是理解数据结构中个字段的含义,这可能不得不阅读一些规范,我想我不可能比规范说的更严谨更完整。要注意的是,你使用的U盘不可能和我的完全一致,一般情况下有可能有变化的是:设备地址devAddr、输出端点地址outEndpoint和输入端点地址inEndpoint,所以在编译程序之前一定要使用《USB系列之二》中的方法仔细查看一下你的U盘的各种描述符表,如果这些值和我的U盘不同,请在主程序开始的地方,更改这几个变量;另外,在主程序6th step中,scsiRead10(0),传递给scsiRead10的参数为0,含义是从LBA(Logical Block Address)为0的地方读取一个扇区,如果你向读取其它扇区,可以更改这个值,其最大值我们在实现 READ CAPACITY时已经读出了,可以参考;此外,注意CBW的字符顺序是little endian,所以我们在填写LBA和读取最大LBA时都做了相应的转换。

好了,应该没有什么了!

Enjoy it.

时间: 2024-10-08 12:51:22

USB系列之三:从你的U盘里读出更多的内容的相关文章

USB系列之六:基于DOSUSB的简单U盘驱动程序

首先要说明的是,该驱动程序仅实现了部分块设备的功能,如果作为成品软件使用,会感觉性能比较差,而且有些功能(比如FORMAT)是不能完成的,发表此驱动程序的目的旨在说明USB的编程原理以及DOS下驱动程序的工作原理:同时要说明的是,此驱动程序仅支持32M(包括32M)以下的U盘,当然这个问题解决起来并不困难,有兴趣的读者可以在阅读本文并理解的基础上加以改进使其支持32M以上2G以下的U盘.    前面的博文中提到由于DOSUSB是在命令行加载的,如果从config.sys中加载这个基于DOSUSB

USB系列之八:透过ASPI执行SCSI命令

在<USB系列之七>里我们介绍了ASPI的规范,并对一系列ASPI的命令做了测试,其中的02号命令是执行SCSI命令,我们专门在这篇文章中介绍,在<USB系列七>中,我们已经了解了调用ASPI的方法,主要是要填一个SRB(SCSI Request Block)的表,在以前的<USB系列之三:从你的U盘里读出更多的内容>文章中我们通过DOSUSB已经实现了许多SCSI命令,这些命令包括: SCSI INQUIRY Command SCSI READ CAPACITY (1

USB系列之七:ASPI介绍及命令测试

在以前的一篇博文<关于构建DOS下编程平台的总结>中曾经介绍了一种在DOS下驱动U盘的方法,我们大致回顾一下.在config.sys中加入两个驱动程序,就可以驱动U盘:    device = aspiohci.sys    device=di1000dd.sys    这两个驱动程序在上述博文中有下载.    如果大家仔细地阅读过我关于USB的文章的话,应该对OHCI这个东西不会陌生,在USB系列文章中,用大量的篇幅介绍了OHCI,这是因为OHCI这种符合USB1.1的控制器在使用上比UHC

USB系列之五:用汇编实现的一些USB功能

前面的USB系列一至四,实现了我们需要的一些USB功能,但都是用C语言的32位代码,之后我们插进了三篇关于DOS下设备驱动程序的文章,我们现在应该清楚,当我们要在DOS下写一个U盘的驱动时,最好使用汇编语言,而且不得不在实模式下编程. 基于这样一个原因,本文计划把<USB系列二>到<USB系列四>中的三段程序代码,用汇编语言再重新实现一遍,而且使用16位的8086模式编程,在下载下面的源代码之前,希望读者能够认真阅读USB系列以前所有的文章,最好能把其中的代码都看明白并亲自试一试,

USB系列之九:基于ASPI的U盘驱动程序

USB系列之七和之八介绍了ASPI,并通过一些实例说明了基于ASPI的编程方法,本文使用前两篇文章介绍的知识以及以前介绍的有关DOS驱动程序下驱动程序的内容实际完成一个简单的基于ASPI的U盘驱动程序,算是对ASPI应用的一个总结.    在<USB系列之六>中,我们完成了一个简单的基于DOSUSB的U盘驱动程序,实际上我们今天的程序是在那个程序的基础上改的,基本结构完全相同,思路也完全一样,只是由于有ASPI的支持,无需再读取各种描述符表,读盘.写盘的操作也显得简洁了很多,希望对本文有兴趣的

SCCM 2012 R2实战系列之三:独立主站点部署

3.1 SCCM 2012 R2主站点的安装 SCCM 2012 R2跟以前的SCCM 2007不同的是多了一个管理中心站点的角色, 管理中心站点主要负责SCCM管理控制和报表查看. 主站点跟以往的SCCM 2007主站点功能一样,具备软件分发.系统部署等主要功能,一个管理中心站点下可以有多个主站点,主站点的关系可以是并列的. 在SCCM服务器中放入SCCM2012的安装光盘,以域管理员身份登录.如下图所示,在安装光盘的smssetup\bin\X64目录下找到extadsch.exe,双击运行

【iOS与EV3混合机器人编程系列之三】编写EV3 Port Viewer 应用监测EV3端口数据

在前两篇文章中,我们对iOS与EV3混合机器人编程做了一个基本的设想,并且介绍了要完成项目所需的软硬件准备和知识准备. 那么在今天这一篇文章中,我们将直接真正开始项目实践. ==第一个项目: EV3 Port Viewer== 项目目的:在iOS设备上通过WiFi连接EV3并且读取EV3每个端口的数据. 大家可以一周之后在App Store上搜索EV3 Port Viewer,那么我已经做了一个范例App发布了,正在审核中 应用的基本使用要求:将EV3和iPhone同时连接到同一个WiFi网络中

[基础知识]Linux新手系列之三

给Linux新手 [系列之三] Linux相关资料由兄弟连分享 OK,从哪里得到Linux呢?为了讨论方便,省去不表要的枝节问题,我只讨论光盘安装.所以第一步是得到一张RedHat 6.2的光盘.得到光盘有两种方式,一种是去买光盘,这个比较简单,外面盗版盘就有,不过需要指出的是,RedHat 6.2的光盘不可能是盗-版盘,因为RedHat 6.2遵循GPL协议,它是不收取版权费用的.所以任何人拷贝安装光盘都是合法的.所以不必为买到一张"RedHat 6.2的盗版盘"而沾沾自喜或有了偷盗

SCCM 2012 R2 实战系列之三: 独立主站点部署

4.1 SCCM 2012 R2主站点的安装      SCCM 2012 R2跟以前的SCCM 2007不同的是多了一个管理中心站点的角色,     管理中心站点主要负责SCCM管理控制和报表查看.     主站点跟以往的SCCM 2007主站点功能一样,具备软件分发.系统部署等主要功能,一个管理中心站点下可以有多个主站点,主站点的关系可以是并列的.       在SCCM服务器中放入SCCM2012的安装光盘,以域管理员身份登录.如下图所示,在安装光盘的smssetupinX64目录下找到e