Windows D 中的 SCSI ReadCapacity16

Windows SCSI ReadCapacity16 in D

我正在尝试使用 D 将 scsi ReadCapacity16 (0x9E) 发送到 Windows 上的卷。CDB 是规范的,我的 ReadCapacity16 在 Linux 上工作,scsi 查询在 Windows。只有 Windows 上的非查询调用无法与来自 windows 内核的 "incorrect function" 一起工作。

既然只有查询有效,那么通过Windows内核发送非查询是否有技巧?让这个工作的任何提示?我已经研究了几个星期,但还没有解决这个问题。

这是 CDB 的示例:

\.\physicaldrive0 CDB buffer contents: 9e 10 00 00 00 00 00 00 - 00 00 00 00 00 20 00 00 sgio.exceptions.IoctlFailException@sgio\exceptions.d(13): ioctl error code is 1. Incorrect function.

此处是将 CDB 复制到 DeviceIoControl 调用缓冲区的位置,这与成功发送查询命令(但 readcap 失败)的代码路径相同。 Code in github 粘贴在下方:

void sgio_execute(ubyte[] cdb_buf, ubyte[] dataout_buf, ubyte[] datain_buf, ubyte[] sense_buf)
   version (Windows)
   {
      const uint SENSE_LENGTH = 196;
      ubyte[512] iobuffer = 0;
      DWORD amountTransferred = -1;
      SCSI_PASS_THROUGH_DIRECT scsiPassThrough = {0};
      scsiPassThrough.Cdb[] = 0;
      uint size = cast(uint)((cdb_buf.length <= scsiPassThrough.Cdb.length ?
                        cdb_buf.length : scsiPassThrough.Cdb.length));

      scsiPassThrough.Cdb[0..size] = cdb_buf[0..size];
      scsiPassThrough.Length             = SCSI_PASS_THROUGH_DIRECT.sizeof;
      scsiPassThrough.ScsiStatus         = 0x00;
      scsiPassThrough.TimeOutValue       = 0x40;
      scsiPassThrough.CdbLength          = cast(ubyte)(size);
      scsiPassThrough.SenseInfoOffset    = SCSI_PASS_THROUGH_DIRECT.sizeof;
      scsiPassThrough.SenseInfoLength    = SENSE_LENGTH;
      scsiPassThrough.DataIn             = SCSI_IOCTL_DATA_IN;
      scsiPassThrough.DataBuffer         = datain_buf.ptr;
      scsiPassThrough.DataTransferLength = bigEndianToNative!ushort(cast(ubyte[2]) cdb_buf[3..5]);

      int status = DeviceIoControl( m_device,
                                    IOCTL_SCSI_PASS_THROUGH_DIRECT,
                                    &scsiPassThrough,
                                    iobuffer.length, //scsiPassThrough.sizeof,
                                    &iobuffer,
                                    iobuffer.length,
                                    &amountTransferred,
                                    null);
      if (status == 0)
      {
         int errorCode = GetLastError();
         // build error message ...
         throw new IoctlFailException(exceptionMessage);
      }
   }
}

仔细阅读 Windows SCSI_PASS_THROUGH_DIRECT structure documentation 我注意到了这一点:

DataTransferLength: Indicates the size in bytes of the data buffer. Many devices transfer chunks of data of predefined length. The value in DataTransferLength must be an integral multiple of this predefined, minimum length that is specified by the device. If an underrun occurs, the miniport driver must update this member to the number of bytes actually transferred.

我通过增加 datain_buffer 的大小将代码更改为使用 512 字节的 DataTransferLength,现在代码工作正常。