/* cdrdao - write audio CD-Rs in disc-at-once mode * * Copyright (C) 1998-2001 Andreas Mueller * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include "ScsiIf.h" #include "util.h" #include "ntddcdrm.h" #include "decodeSense.cc" // // SCSI Definitionen. // #define IOCTL_SCSI_BASE FILE_DEVICE_CONTROLLER #define IOCTL_SCSI_GET_CAPABILITIES CTL_CODE(IOCTL_SCSI_BASE, 0x0404, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_SCSI_PASS_THROUGH_DIRECT CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_SCSI_GET_ADDRESS CTL_CODE(IOCTL_SCSI_BASE, 0x0406, METHOD_BUFFERED, FILE_ANY_ACCESS) #define SCSI_IOCTL_DATA_OUT 0 #define SCSI_IOCTL_DATA_IN 1 #define SCSI_IOCTL_DATA_UNSPECIFIED 2 #pragma pack(4) typedef struct _IO_SCSI_CAPABILITIES { ULONG Length; ULONG MaximumTransferLength; ULONG MaximumPhysicalPages; ULONG SupportedAsynchronousEvents; ULONG AlignmentMask; BOOLEAN TaggedQueuing; BOOLEAN AdapterScansDown; BOOLEAN AdapterUsesPio; } IO_SCSI_CAPABILITIES, *PIO_SCSI_CAPABILITIES; typedef struct _SCSI_PASS_THROUGH_DIRECT { USHORT Length; UCHAR ScsiStatus; UCHAR PathId; UCHAR TargetId; UCHAR Lun; UCHAR CdbLength; UCHAR SenseInfoLength; UCHAR DataIn; ULONG DataTransferLength; ULONG TimeOutValue; PVOID DataBuffer; ULONG SenseInfoOffset; UCHAR Cdb[16]; } SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT; typedef struct _SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER { SCSI_PASS_THROUGH_DIRECT sptd; ULONG Filler; // realign buffer to double word boundary UCHAR ucSenseBuf[32]; } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, *PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER; typedef struct _SCSI_INQUIRY_DEVICE { UCHAR Type; UCHAR TypeModifier; UCHAR Version; UCHAR Format; UCHAR AddLength; // n-4 UCHAR Reserved[2]; UCHAR Flags; char VendorId[8]; char ProductId[16]; char ProductRevLevel[4]; char ProductRevDate[8]; } SCSI_INQUIRY_DEVICE, *PSCSI_INQUIRY_DEVICE; typedef struct _SCSI_ADDRESS { ULONG Length; UCHAR PortNumber; UCHAR PathId; UCHAR TargetId; UCHAR Lun; }SCSI_ADDRESS, *PSCSI_ADDRESS; #pragma pack(1) // // SCSI CDB operation codes // #define SCSIOP_INQUIRY 0x12 #define SCSIOP_MODE_SELECT 0x15 #define SCSIOP_MODE_SENSE 0x1A #define AUDIO_BLOCK_LEN 2352 #define BUF_SIZE (63*1024) class ScsiIfImpl { public: char *dev_; HANDLE hCD; SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb; unsigned char senseBuffer_[32]; char haid_; char lun_; char scsi_id_; }; ScsiIf::ScsiIf(const char *dev) { impl_ = new ScsiIfImpl; impl_->dev_ = strdup3CC("\\\\.\\", dev, NULL); impl_->hCD = INVALID_HANDLE_VALUE; vendor_[0] = 0; product_[0] = 0; revision_[0] = 0; } ScsiIf::~ScsiIf() { if (impl_->hCD != INVALID_HANDLE_VALUE) CloseHandle (impl_->hCD); delete impl_; } // opens scsi device // return: 0: OK // 1: device could not be opened // 2: inquiry failed int ScsiIf::init() { int i = 0; DWORD ol; SCSI_ADDRESS sa; IO_SCSI_CAPABILITIES ca; while (i++ < 3 && (impl_->hCD == INVALID_HANDLE_VALUE)) { impl_->hCD = CreateFile (impl_->dev_, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); } if (impl_->hCD == INVALID_HANDLE_VALUE) { return 1; } if (DeviceIoControl (impl_->hCD, IOCTL_SCSI_GET_ADDRESS, NULL, 0, &sa, sizeof(SCSI_ADDRESS), &ol, NULL)) { impl_->haid_ = sa.PortNumber; impl_->lun_ = sa.Lun; impl_->scsi_id_ = sa.TargetId; } else { CloseHandle (impl_->hCD); impl_->hCD = INVALID_HANDLE_VALUE; return 1; } if (DeviceIoControl (impl_->hCD, IOCTL_SCSI_GET_CAPABILITIES, NULL, 0, &ca, sizeof(IO_SCSI_CAPABILITIES), &ol, NULL)) { maxDataLen_ = ca.MaximumTransferLength; } else { CloseHandle (impl_->hCD); impl_->hCD = INVALID_HANDLE_VALUE; return 1; } if (inquiry() != 0) return 2; return 0; } // Sets given timeout value in seconds and returns old timeout. // return: old timeout int ScsiIf::timeout (int t) { return 0; } // sends a scsi command and receives data // return 0: OK // 1: scsi command failed (os level, no sense data available) // 2: scsi command failed (sense data available) int ScsiIf::sendCmd (unsigned char *cmd, int cmdLen, unsigned char *dataOut, int dataOutLen, unsigned char *dataIn, int dataInLen, int showMessage) { int i = 10; DWORD er, il, ol; ZeroMemory (&impl_->sb, sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER)); impl_->sb.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT); impl_->sb.sptd.PathId = 0; impl_->sb.sptd.TargetId = impl_->scsi_id_; impl_->sb.sptd.Lun = impl_->lun_; impl_->sb.sptd.CdbLength = cmdLen; impl_->sb.sptd.SenseInfoLength = 32; impl_->sb.sptd.TimeOutValue = 4; impl_->sb.sptd.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf); memcpy (impl_->sb.sptd.Cdb, cmd, cmdLen); if (dataOut && dataOutLen) { impl_->sb.sptd.DataIn = SCSI_IOCTL_DATA_OUT; impl_->sb.sptd.DataBuffer = dataOut; impl_->sb.sptd.DataTransferLength = dataOutLen; } else { impl_->sb.sptd.DataIn = SCSI_IOCTL_DATA_IN; impl_->sb.sptd.DataBuffer = dataIn; impl_->sb.sptd.DataTransferLength = dataInLen; } il = sizeof (SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER); // AM: what about sense data? if (DeviceIoControl (impl_->hCD, IOCTL_SCSI_PASS_THROUGH_DIRECT, &impl_->sb, il, &impl_->sb, il, &ol, NULL)) { er = impl_->sb.sptd.ScsiStatus ? impl_->sb.sptd.ScsiStatus | 0x20000000 : 0; if (!er) return (0); } else return (1); return 0; } const unsigned char *ScsiIf::getSense(int &len) const { len = 32; return impl_->sb.ucSenseBuf; } void ScsiIf::printError() { decodeSense(impl_->sb.ucSenseBuf, 32); } int ScsiIf::inquiry() { unsigned char cmd[6]; int i; SCSI_INQUIRY_DEVICE NTinqbuf; ZeroMemory (&NTinqbuf, sizeof(SCSI_INQUIRY_DEVICE)); cmd[0] = 0x12; // INQUIRY cmd[1] = cmd[2] = cmd[3] = 0; cmd[4] = sizeof (NTinqbuf); cmd[5] = 0; if (sendCmd (cmd, 6, NULL, 0, (unsigned char *) &NTinqbuf, sizeof (NTinqbuf), 1) != 0) { message (-2, "Inquiry command failed on '%s': ", impl_->dev_); return 1; } strncpy(vendor_, (char *)(NTinqbuf.VendorId), 8); vendor_[8] = 0; strncpy(product_, (char *)(NTinqbuf.ProductId), 16); product_[16] = 0; strncpy(revision_, (char *)(NTinqbuf.ProductRevLevel), 4); revision_[4] = 0; for (i = 7; i >= 0 && vendor_[i] == ' '; i--) { vendor_[i] = 0; } for (i = 15; i >= 0 && product_[i] == ' '; i--) { product_[i] = 0; } for (i = 3; i >= 0 && revision_[i] == ' '; i--) { revision_[i] = 0; } return 0; }