Je travaille sur une application qui doit émettre des commandes SCSI brutes sur un lecteur de CD-ROM. Actuellement, je n'arrive pas à envoyer une commande READ CD (0xBE
) au lecteur et à récupérer les données d'un secteur donné du CD.Comment émettre une commande READ CD sur un lecteur de CD-ROM sous Windows?
Consultez le code suivant:
#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;
}
La PEH a été assemblé selon MMC-6 Revision 2g, et devrait transférer 1 secteur de 1. Depuis que je LBA je travaille avec des disques CD-DA seulement, chaque secteur est 2352 octets , ce qui explique pourquoi sizeof(buf)
est 2352.
La vérification d'erreur a été omise par souci de brièveté. Le débogueur montre que le tandis que les valeurs à l'intérieur de sptd.s
appel retourne avec succès DeviceIoControl
et ioctl_bytes
est 0x2c
, sont les suivantes:
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
Cela montre que la commande a été exécutée avec succès par le lecteur, comme ScsiStatus
est égal à 0 (SCSI_STATUS_GOOD
) et aucune donnée de détection n'a été renvoyée. Toutefois, le tampon pour les données n'est pas écrit, car le débogueur montre qu'il est rempli avec 0xcc
, que l'application est compilée en mode débogage.
Cependant, quand je change la PEH à la commande standard ENQUETE comme ceci:
const UCHAR cdb[] = { 0x12, 0, 0, 0, 36, 0 };
Le tampon est correctement rempli avec des données d'enquête, et je suis capable de lire le nom du lecteur, le vendeur, et tout le reste.
je l'ai déjà essayé aligner le tampon de cible, selon la Microsoft's documentation for SCSI_PASS_THROUGH_DIRECT, qui dit que Le membre DataBuffer de SCSI_PASS_THROUGH_DIRECT est un pointeur sur ce dispositif adaptateur tampon aligné. l'alignement Expérimentalement le tampon à 64 octets ne fonctionnait pas, et l'émission d'un IOCTL_SCSI_GET_CAPABILITIES
, qui est censé revenir l'alignement nécessaire, m'a donné les informations suivantes:
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
Ce qui me porte à croire que l'alignement n'est pas nécessaire puisque AlignmentMask
est 1, et donc il ne semble pas que ce soit la cause du problème. Fait intéressant, AdapterUsesPio
est 1, bien que le Gestionnaire de périphériques dise le contraire. Pour l'anecdote, le code ci-dessous fonctionne correctement sous Linux et le tampon cible est rempli avec les données du CD. Identique à Windows, l'état SCSI renvoyé est 0 et aucune donnée de détection n'est renvoyée.
#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;
}
Le code Windows est compilé avec Visual Studio C++ 2010 Express et winddk 7600.16385.1, sous Windows XP. Il est également exécuté sous Windows XP.
Je n'en ai aucune idée, mais vous pourriez vouloir regarder comment le pilote libscg du projet [cdrecord] (http://cdrecord.org/) le fait. Le gars qui développe cdrecord aime aussi répondre aux questions liées à SCSI. – fuz