3

生の SCSI コマンドを CD-ROM ドライブに発行する必要があるアプリケーションに取り組んでいます。現在、ドライブに READ CD ( 0xBE) コマンドを送信し、CD の特定のセクターからデータを取得するのに苦労しています。

次のコードを検討してください。

#include <windows.h>
#include <winioctl.h>
#include <ntddcdrm.h>
#include <ntddscsi.h>
#include <stddef.h>

int main(void)
{
  HANDLE fh;
  DWORD ioctl_bytes;
  BOOL ioctl_rv;
  const UCHAR cdb[] = { 0xBE, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0 };
  UCHAR buf[2352];
  struct sptd_with_sense
  {
    SCSI_PASS_THROUGH_DIRECT s;
    UCHAR sense[128];
  } sptd;

  fh = CreateFile("\\\\.\\E:", GENERIC_READ | GENERIC_WRITE,
    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL, NULL);

  memset(&sptd, 0, sizeof(sptd));
  sptd.s.Length = sizeof(sptd.s);
  sptd.s.CdbLength = sizeof(cdb);
  sptd.s.DataIn = SCSI_IOCTL_DATA_IN;
  sptd.s.TimeOutValue = 30;
  sptd.s.DataBuffer = buf;
  sptd.s.DataTransferLength = sizeof(buf);
  sptd.s.SenseInfoLength = sizeof(sptd.sense);
  sptd.s.SenseInfoOffset = offsetof(struct sptd_with_sense, sense);
  memcpy(sptd.s.Cdb, cdb, sizeof(cdb));

  ioctl_rv = DeviceIoControl(fh, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sptd,
    sizeof(sptd), &sptd, sizeof(sptd), &ioctl_bytes, NULL);

  CloseHandle(fh);

  return 0;
}

CDB はMMC-6 Revision 2gに従って組み立てられ、LBA 1 から 1 セクターを転送する必要があります。私は CD-DA ディスクのみを使用しているため、各セクターは 2352 バイトであり、2352 である理由sizeof(buf)が説明されています。

簡潔にするために、エラーチェックは省略されています。デバッガーは、呼び出しDeviceIoControlが正常に返され、内部の値が次のようになっていることを示しています。ioctl_bytes0x2csptd.s

Length              0x002c      unsigned short
ScsiStatus          0x00        unsigned char
PathId              0x00        unsigned char
TargetId            0x00        unsigned char
Lun                 0x00        unsigned char
CdbLength           0x0c        unsigned char
SenseInfoLength     0x00        unsigned char
DataIn              0x01        unsigned char
DataTransferLength  0x00000930  unsigned long
TimeOutValue        0x0000001e  unsigned long
DataBuffer          0x0012f5f8  void *
SenseInfoOffset     0x0000002c  unsigned long

これは、コマンドがドライブによって正常に実行されたことを示しており、ScsiStatus0 ( SCSI_STATUS_GOOD) であり、センス データは返されませんでした。0xccただし、アプリケーションがデバッグ モードでコンパイルされているため、データのバッファは書き込まれません。

ただし、CDB を次のような標準の INQUIRY コマンドに変更すると、次のようになります。

const UCHAR cdb[] = { 0x12, 0, 0, 0, 36, 0 };

バッファは問い合わせデータで適切に満たされ、ドライブの名前、ベンダー、その他すべてを読み取ることができます。

Microsoft の SCSI_PASS_THROUGH_DIRECT に関するドキュメントよると、ターゲット バッファーのアライメントを既に試みました。実験的にバッファーを 64 バイトにアラインしてもうまくいきませんでしIOCTL_SCSI_GET_CAPABILITIESた。必要なアラインメントを返すはずの を発行すると、次の情報が得られました。

Length                      0x00000018  unsigned long
MaximumTransferLength       0x00020000  unsigned long
MaximumPhysicalPages        0x00000020  unsigned long
SupportedAsynchronousEvents 0x00000000  unsigned long
AlignmentMask               0x00000001  unsigned long
TaggedQueuing               0x00        unsigned char
AdapterScansDown            0x00        unsigned char
AdapterUsesPio              0x01        unsigned char

これにより、1であるためアライメントは不要でAlignmentMaskあり、これが問題の原因であるとは思えません。興味深いことに、AdapterUsesPioデバイス マネージャーはそうではないと言っていますが、1 です。

記録のために、以下のコードは Linux で適切に動作し、ターゲット バッファーは CD からのデータで満たされます。Windows と同様に、返される SCSI ステータスは 0 であり、センス データは返されません。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <scsi/sg.h>
#include <scsi/scsi.h>
#include <linux/cdrom.h>
#include <sys/ioctl.h>

int main(void)
{
  int fd = open("/dev/sr0", O_RDONLY | O_NONBLOCK);
  if(fd == -1) { perror("open"); return 1; }

  {
    struct sg_io_hdr sgio;
    unsigned char cdb[] = { 0xBE, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0 };
    unsigned char buf[2352];
    unsigned char sense[128];
    int rv;

    sgio.interface_id = 'S';
    sgio.dxfer_direction = SG_DXFER_FROM_DEV;
    sgio.cmd_len = sizeof(cdb);
    sgio.cmdp = cdb;
    sgio.dxferp = buf;
    sgio.dxfer_len = sizeof(buf);
    sgio.sbp = sense;
    sgio.mx_sb_len = sizeof(sense);
    sgio.timeout = 30000;

    rv = ioctl(fd, SG_IO, &sgio);
    if(rv == -1) { perror("ioctl"); return 1; }
  }
  close(fd);
  return 0;
}

Windows コードは、Windows XP 上の Visual Studio C++ 2010 Express および WinDDK 7600.16385.1 でコンパイルされています。Windows XP でも動作します。

4

2 に答える 2

2

この問題は、構文に関しては有効ですが、不適切に形成された CDB 内にあります。MMC仕様で見落としていたのはこれでした:

ここに画像の説明を入力

9番目のバイトには、ドライブが返すデータの種類を選択するために使用されるビットが含まれていると想定されています。質問のコードでは、0 に設定しました。これは、ドライブから「フィールドなし」を要求したことを意味します。このバイトを0x10(ユーザー データ) に変更すると、Linux と Windows の両方のバージョンで、特定のセクターに対して同じデータが返されます。元の形式の CDB を使用しても、Linux がバッファにデータを返した理由はまだわかりません。

したがって、LBA 1 で 1 つの CD-DA セクターを読み取るときの READ CD コマンドの適切な CDB は、次のようになります。

const unsigned char cdb[] = { 0xBE, 0, 0, 0, 0, 1, 0, 0, 1, 0x10, 0, 0 };
于 2015-04-27T18:25:37.930 に答える