ciderpress/diskimg/SPTI.cpp

138 lines
4.7 KiB
C++

/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Implementation of some SPTI functions.
*/
#include "StdAfx.h"
#ifdef _WIN32
#include "DiskImgPriv.h"
#include "SCSIDefs.h"
#include "CP_ntddscsi.h"
#include "SPTI.h"
/*
* Get the capacity of the device.
*
* Returns the LBA of the last valid block and the device's block size.
*/
/*static*/ DIError SPTI::GetDeviceCapacity(HANDLE handle, uint32_t* pLastBlock,
uint32_t* pBlockSize)
{
SCSI_PASS_THROUGH_DIRECT sptd;
uint32_t lba, blockLen;
CDB_ReadCapacityData dataBuf;
DWORD cb;
BOOL status;
assert(sizeof(dataBuf) == 8); // READ CAPACITY returns two longs
memset(&sptd, 0, sizeof(sptd));
sptd.Length = sizeof(sptd);
sptd.PathId = 0; // SCSI card ID filled in by ioctl
sptd.TargetId = 0; // SCSI target ID filled in by ioctl
sptd.Lun = 0; // SCSI lun ID filled in by ioctl
sptd.CdbLength = 10; // CDB size is 10 for READ CAPACITY
sptd.SenseInfoLength = 0; // don't return any sense data
sptd.DataIn = SCSI_IOCTL_DATA_IN; // will be data from drive
sptd.DataTransferLength = sizeof(dataBuf);
sptd.TimeOutValue = 10; // SCSI timeout value, in seconds
sptd.DataBuffer = (PVOID) &dataBuf;
sptd.SenseInfoOffset = 0; // offset to request-sense buffer
CDB10* pCdb = (CDB10*) &sptd.Cdb;
pCdb->operationCode = kScsiOpReadCapacity;
// rest of CDB is zero
status = ::DeviceIoControl(handle, IOCTL_SCSI_PASS_THROUGH_DIRECT,
&sptd, sizeof(sptd), NULL, 0, &cb, NULL);
if (!status) {
DWORD lastError = ::GetLastError();
LOGE("DeviceIoControl(SCSI READ CAPACITY) failed, err=%ld",
::GetLastError());
if (lastError == ERROR_IO_DEVICE) // no disc in drive
return kDIErrDeviceNotReady;
else
return kDIErrSPTIFailure;
}
lba = (uint32_t) dataBuf.logicalBlockAddr0 << 24 |
(uint32_t) dataBuf.logicalBlockAddr1 << 16 |
(uint32_t) dataBuf.logicalBlockAddr2 << 8 |
(uint32_t) dataBuf.logicalBlockAddr3;
blockLen = (uint32_t) dataBuf.bytesPerBlock0 << 24 |
(uint32_t) dataBuf.bytesPerBlock1 << 16 |
(uint32_t) dataBuf.bytesPerBlock2 << 8 |
(uint32_t) dataBuf.bytesPerBlock3;
*pLastBlock = lba;
*pBlockSize = blockLen;
return kDIErrNone;
}
/*
* Read one or more blocks from the specified SCSI device.
*
* "buf" must be able to hold (numBlocks * blockSize) bytes.
*/
/*static*/ DIError SPTI::ReadBlocks(HANDLE handle, long startBlock,
short numBlocks, long blockSize, void* buf)
{
SCSI_PASS_THROUGH_DIRECT sptd;
DWORD cb;
BOOL status;
assert(startBlock >= 0);
assert(numBlocks > 0);
assert(buf != NULL);
LOGD(" SPTI phys read block (%ld) %d", startBlock, numBlocks);
memset(&sptd, 0, sizeof(sptd));
sptd.Length = sizeof(sptd); // size of struct (+ request-sense buffer)
sptd.ScsiStatus = 0;
sptd.PathId = 0; // SCSI card ID filled in by ioctl
sptd.TargetId = 0; // SCSI target ID filled in by ioctl
sptd.Lun = 0; // SCSI lun ID filled in by ioctl
sptd.CdbLength = 10; // CDB size is 10 for READ CAPACITY
sptd.SenseInfoLength = 0; // don't return any sense data
sptd.DataIn = SCSI_IOCTL_DATA_IN; // will be data from drive
sptd.DataTransferLength = blockSize * numBlocks;
sptd.TimeOutValue = 10; // SCSI timeout value (in seconds)
sptd.DataBuffer = (PVOID) buf;
sptd.SenseInfoOffset = 0; // offset from start of struct to request-sense
CDB10* pCdb = (CDB10*) &sptd.Cdb;
pCdb->operationCode = kScsiOpRead;
pCdb->logicalBlockAddr0 = (uint8_t) (startBlock >> 24); // MSB
pCdb->logicalBlockAddr1 = (uint8_t) (startBlock >> 16);
pCdb->logicalBlockAddr2 = (uint8_t) (startBlock >> 8);
pCdb->logicalBlockAddr3 = (uint8_t) startBlock; // LSB
pCdb->transferLength0 = (uint8_t) (numBlocks >> 8); // MSB
pCdb->transferLength1 = (uint8_t) numBlocks; // LSB
status = ::DeviceIoControl(handle, IOCTL_SCSI_PASS_THROUGH_DIRECT,
&sptd, sizeof(sptd), NULL, 0, &cb, NULL);
if (!status) {
LOGE("DeviceIoControl(SCSI READ(10)) failed, err=%ld",
::GetLastError());
return kDIErrReadFailed; // close enough
}
if (sptd.ScsiStatus != 0) {
LOGE("SCSI READ(10) failed, status=%d", sptd.ScsiStatus);
return kDIErrReadFailed;
}
return kDIErrNone;
}
#endif /*_WIN32*/