Я работаю над приложением, которое должно выдавать необработанные команды SCSI на CD-ROM. В настоящее время я борюсь с отправкой команды READ CD (0xBE
) на диск и возвратом данных из данного сектора компакт-диска.Как выполнить команду READ CD на CD-ROM в Windows?
Рассмотрим следующий код:
#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;
}
ЦКБ был собран в соответствии с MMC-6 Revision 2g, и должен передать 1 сектор из LBA 1. Так как я работаю только с CD-DA дисков, каждый сектор 2352 байтов , что объясняет, почему sizeof(buf)
является 2352.
Проверка ошибок была опущена для краткости. Отладчик показывает, что DeviceIoControl
вызов завершается успешно и ioctl_bytes
является 0x2c
, в то время как значения внутри sptd.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
Это показывает, что команда была успешно выполнена приводом, а ScsiStatus
является 0 (SCSI_STATUS_GOOD
) , и никакие данные чувств не были возвращены. Однако буфер для данных не записывается, поскольку отладчик показывает, что он заполнен 0xcc
, поскольку приложение скомпилировано в режиме отладки.
Однако, когда я изменить CDB к стандартной команде ЗАПРОСА как это:
const UCHAR cdb[] = { 0x12, 0, 0, 0, 36, 0 };
Буфер правильно заполнен данными дознания, и я могу прочитать имя диска, поставщик и все остальное.
Я уже пытался совместив целевой буфер, в соответствии с Microsoft's documentation for SCSI_PASS_THROUGH_DIRECT, в котором говорится, что член DataBuffer из 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
Который приводит меня к мысли, что выравнивание не требуется, так как AlignmentMask
равен 1, и, похоже, это не является причиной проблемы. Интересно, что AdapterUsesPio
- 1, хотя диспетчер устройств говорит иначе.
Для записи приведенный ниже код работает правильно в Linux, а целевой буфер заполняется данными с компакт-диска. То же, что и в 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 компилируется с Visual Studio C++ 2010 Express и WinDDK 7600.16385.1, на Windows XP. Он также запускается в Windows XP.
Я понятия не имею, но вы можете посмотреть, как это делает драйвер libscg из проекта [cdrecord] (http://cdrecord.org/). Парень, который разрабатывает cdrecord, также любит отвечать на вопросы, связанные с SCSI. – fuz