/* * 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*/