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

时间:2022-12-30 03:16:52

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

  • SCSI INQUIRY Command
  • SCSI READ CAPACITY (10) Command
  • SCSI REQUEST SENSE Command
  • SCSI TEST UNIT READY Command
  • SCSI READ (10) Command

  在《USB系列之四:向U盘上写数据》一文中,我们又实现了SCSI WRITE(10) Command,所以我们已经实现过6个SCSI命令了,先让我们回顾一下以前的SCSI命令我们是如何实现的。
  按照《USB Mass Storage Class -- Bulk Only Transport》(这个文档在USB系列之三中有过介绍并提供了下载)的说明,我们首先要填写一个CBW(Command Block Wrapper)表,在这个表中中的最后有一个不定长的部分叫做CBWCB,这部分的长度及字段含义视发出的SCSI命令不同而不同,实际上,这个部分就是描述SCSI命令的,在SCSI的规范中,这个部分叫做CDB(Command Descriptor Block),它和CBWCB完全是一个东西,在本文中,我们沿用SCSI规范中的叫法,把这个表叫做CDB。
  了解SCSI命令,需要了解两个规范SPC-3和SBC-2,这两个文档在USB系列之三中都有介绍并提供了下载,鉴于以前我们已经实现过许多SCSI命令,在本文中我们仅就最重要的三个SCSI命令进行实现,它们是:

  • SCSI TEST UNIT READY Command
  • SCSI READ (10) Command
  • SCSI WRITE (10) Command

  相信读者可以自行实现其它的命令。
  为了清晰地表现读、写扇区的操作,我们用两个源程序来分别测试读、写扇区,第一个程序叫aspiread,专门测试读扇区,通过这个程序,我们有把握使用其中的SCSI READ(10)命令把指定的扇区内容读出;第二个程序叫aspiwrit,专门测试写扇区操作,因为写扇区本身是看不见什么的,要靠读扇区命令把写入的内容读出来才能验证,这也就是我们为什么单独测试读扇区命令的原因。
  这两个程序的下载地址如下:
  aspiread下载:http://blog.hengch.com/source/aspiread.zip
  aspiwrit下载:http://blog.hengch.com/source/aspiwrit.zip
  我们需要对这两个程序分别做一下说明。

  先说aspiread程序,这个程序在我的环境下执行后的结果如下:

    ASPI Reading test program v1..

    Open SCSIMRG success!    ASPI entry : :6a5c

    Test unit ready......
    Command Status:     Host Adapter Status:
    Target Status:
    Press any key when ready......

    Reading.........
    Command Status:     Host Adapter Status:
    Target Status:     Residual Byte:
    Data  c0 8e d0 bc  7c   bb   8d  8a fb ba   b9 1d f5  cd   e4  cb af  b3   3c   0e  3c   1c  c6  fe cb  ef cd  8b  8b 4c  8b ee  c6  fe cb  1a  3c   f4 be 8b  ac 3c   0b  bb   b4 0e cd  5e eb f0 eb fe bf   bb  7c b8    cd  5f  0c  c0 cd  4f  ed be a3  eb d3 be c2  bf fe 7d  3d  aa  c7 8b f5 ea  7c    6e   6c           6f 6e     6c      6f   6c 6f    6e   6f       6e        6d  4d     6e   6f       6e        6d                                                                                                                                                                                                                                            f4      3d                                                    aa 

  这个程序我们先发出SCSI命令Test Unit Ready,用以观察设备是否处于Ready状态,然后我们读取LBA = 0的扇区,因为我们可以保证这个扇区是有内容的,肯定不会是全0,如果希望读取其它扇区,把相关语句该一下就可以了,在执行SCSI READ(10)命令时,有下面几点需要注意一下:

  1、我们在USB系列之七中的测试已经表明,在我的环境下,只有Host Adapter Number = 0和Target ID = 0是合法的,所以,程序中对这两项均没有填写,因为在初始化SRB后,本身所有项就都是0,如果在执行USB系列之七中的程序发现这个数据有变,请自行在程序中填写。
  2、注意一定要填写SCSI Request Flags字段,在程序中我们填为0ch,即bit2和bit3为1,其它为0,从ASPI的规范中可以看到,bit2为1表明剩余字符报告使能,bit3为1表明数据传输方向是从SCSI Target到Host,就是从U盘到主机的意思,这一位是一定要设的,否则无法成功完成读扇区的动作;bit2这一位如果不设,那么在完成读扇区的动作时,Data Allocation Length这个字段仍然是512(和我们调用ASPI前设置的一样),如果设置,这个字段在调用后一般会为0,表明我们所要求的512个字节都读出来了,没有剩余字符。
  3、执行完读操作后,Command Status = 01,表明动作已经完成,如果不是01,则buffer中的内容无效,可以从返回的Command Status以及Host Status和Target Status返回的状态上分析原因。

  下面说说aspiwrit这个程序
  这个程序首先要初始化一下buffer,把内容变成00--0ffh的两次循环,然后把buffer中的数据写到LBA = 50h的扇区中(注意:这里可能会破坏已有的文件数据,所以请使用一个空的U盘,或者没有存放有用数据的U盘),写完成后我们把buffer清为全0,然后把LBA = 50h的扇区读出,并显示读出的内容。下面是aspiwrit的执行结果:

   ASPI Reading test program v1..

    Open SCSIMRG success!    ASPI entry : :6a5c

    Test unit ready......
    Command Status:     Host Adapter Status:
    Target Status:
    Press any key when ready......

    Writing.........
    Command Status:     Host Adapter Status:
    Data     Residual Byte:
    Press any key when ready......

    Reading.........
    Command Status:     Host Adapter Status:
    Target Status:     Residual Byte:
    Data           0a 0b 0c 0d 0e 0f           1a 1b 1c 1d 1e 1f           2a 2b 2c 2d 2e 2f           3a 3b 3c 3d 3e 3f           4a 4b 4c 4d 4e 4f           5a 5b 5c 5d 5e 5f           6a 6b 6c 6d 6e 6f           7a 7b 7c 7d 7e 7f           8a 8b 8c 8d 8e 8f           9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff           0a 0b 0c 0d 0e 0f           1a 1b 1c 1d 1e 1f           2a 2b 2c 2d 2e 2f           3a 3b 3c 3d 3e 3f           4a 4b 4c 4d 4e 4f           5a 5b 5c 5d 5e 5f           6a 6b 6c 6d 6e 6f           7a 7b 7c 7d 7e 7f           8a 8b 8c 8d 8e 8f           9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 

  在前面说明aspiread时已经说明的要点在这里就不再重复了。
  1、在执行SCSI WRITE(10)命令时,SRB中SCSI Request Flags字段在程序中我们填为14h,即bit2和bit4为1,其它为0,bit3和bit4组成二进制的10,从ASPI的规范中可以看出表明数据传输方向是从Host到SCSI Target,就是从主机到U盘的意思。
  2、在USB系列之三中,我们介绍过SCSI的一个重要规范SPC-3,并提供了下载,在这份规范中,有一个SCSI命令叫做REQUEST SENSE,当执行命令出错时,可以使用这个命令获得Device的Sense Data,进而判断错误情况,当然我们也可以透过ASPI执行这个SCSI命令,但似乎ASPI已经帮我们想到了这一点,这就是ASPI For DOS这个规范中SRB表中的Sense Allocation Area这个字段的意义,当执行SCSI命令出错时,你会在这里得到Sense Data,得到的数据长度小于等于Sense Allocation Length字段设置的值。

  好了,掌握这些知识后,我们已经具备了使用ASPI编写一个U盘的设备驱动程序的能力,我们会在下一篇文章中涉及这个问题。