//** Defines taken from ntddscsi.h in MS Windows DDK CD
#define SCSI_IOCTL_DATA_OUT 0 //Give data to SCSI device (e.g. for writing)
#define SCSI_IOCTL_DATA_IN 1 //Get data from SCSI device (e.g. for reading)
#define SCSI_IOCTL_DATA_UNSPECIFIED 2 //No data (e.g. for ejecting)
#define MAX_SENSE_LEN 18 //Sense data max length
#define IOCTL_SCSI_PASS_THROUGH_DIRECT 0x4D014
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_AND_SENSE_BUFFER {
SCSI_PASS_THROUGH_DIRECT sptd;
UCHAR SenseBuf[MAX_SENSE_LEN];
}T_SPDT_SBUF;
int GetDriveStatus(const CStdString &strPath, bool bStatusEx)
{
HANDLE hDevice; // handle to the drive to be examined
int iResult; // results flag
ULONG ulChanges=0;
DWORD dwBytesReturned;
T_SPDT_SBUF sptd_sb; //SCSI Pass Through Direct variable.
byte DataBuf[8]; //Buffer for holding data to/from drive.
CLog::Log(LOGDEBUG, __FUNCTION__": Requesting status for drive %s.", strPath.c_str());
hDevice = CreateFile( strPath.c_str(), // drive
0, // no access to the drive
FILE_SHARE_READ, // share mode
NULL, // default security attributes
OPEN_EXISTING, // disposition
FILE_ATTRIBUTE_READONLY, // file attributes
NULL);
if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive
{
CLog::Log(LOGERROR, __FUNCTION__": Failed to CreateFile for %s.", strPath.c_str());
return -1;
}
CLog::Log(LOGDEBUG, __FUNCTION__": Requesting media status for drive %s.", strPath.c_str());
iResult = DeviceIoControl((HANDLE) hDevice, // handle to device
IOCTL_STORAGE_CHECK_VERIFY2, // dwIoControlCode
NULL, // lpInBuffer
0, // nInBufferSize
&ulChanges, // lpOutBuffer
sizeof(ULONG), // nOutBufferSize
&dwBytesReturned , // number of bytes returned
NULL ); // OVERLAPPED structure
CloseHandle(hDevice);
if(iResult == 1)
return 2;
// don't request the tray status as we often doesn't need it
if(!bStatusEx)
return 0;
hDevice = CreateFile( strPath.c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,
NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
CLog::Log(LOGERROR, __FUNCTION__": Failed to CreateFile2 for %s.", strPath.c_str());
return -1;
}
sptd_sb.sptd.Length=sizeof(SCSI_PASS_THROUGH_DIRECT);
sptd_sb.sptd.PathId=0;
sptd_sb.sptd.TargetId=0;
sptd_sb.sptd.Lun=0;
sptd_sb.sptd.CdbLength=10;
sptd_sb.sptd.SenseInfoLength=MAX_SENSE_LEN;
sptd_sb.sptd.DataIn=SCSI_IOCTL_DATA_IN;
sptd_sb.sptd.DataTransferLength=sizeof(DataBuf);
sptd_sb.sptd.TimeOutValue=2;
sptd_sb.sptd.DataBuffer=(PVOID)&(DataBuf);
sptd_sb.sptd.SenseInfoOffset=sizeof(SCSI_PASS_THROUGH_DIRECT);
sptd_sb.sptd.Cdb[0]=0x4a;
sptd_sb.sptd.Cdb[1]=1;
sptd_sb.sptd.Cdb[2]=0;
sptd_sb.sptd.Cdb[3]=0;
sptd_sb.sptd.Cdb[4]=0x10;
sptd_sb.sptd.Cdb[5]=0;
sptd_sb.sptd.Cdb[6]=0;
sptd_sb.sptd.Cdb[7]=0;
sptd_sb.sptd.Cdb[8]=8;
sptd_sb.sptd.Cdb[9]=0;
sptd_sb.sptd.Cdb[10]=0;
sptd_sb.sptd.Cdb[11]=0;
sptd_sb.sptd.Cdb[12]=0;
sptd_sb.sptd.Cdb[13]=0;
sptd_sb.sptd.Cdb[14]=0;
sptd_sb.sptd.Cdb[15]=0;
ZeroMemory(DataBuf, 8);
ZeroMemory(sptd_sb.SenseBuf, MAX_SENSE_LEN);
//Send the command to drive
CLog::Log(LOGDEBUG, __FUNCTION__": Requesting tray status for drive %s.", strPath.c_str());
iResult = DeviceIoControl((HANDLE) hDevice,
IOCTL_SCSI_PASS_THROUGH_DIRECT,
(PVOID)&sptd_sb, (DWORD)sizeof(sptd_sb),
(PVOID)&sptd_sb, (DWORD)sizeof(sptd_sb),
&dwBytesReturned,
NULL);
CloseHandle(hDevice);
if(iResult)
{
if(DataBuf[5] == 0) // tray close
return 0;
else if(DataBuf[5] == 1) // tray open
return 1;
else
return 2; // tray closed, media present
}
CLog::Log(LOGERROR, __FUNCTION__": Could not determine tray status %d", GetLastError());
return -1;
}