mirror of
https://github.com/fadden/ciderpress.git
synced 2024-12-22 05:29:43 +00:00
aa3145856c
Focusing on the diskimg library this time, which deals with a lot of filesystem structures that have specific widths. This is still a bit lax in places, e.g. using "long" for lengths. Should either specify a bit width or use di_off_t. Also, added "override" keyword where appropriate. Also, bumped library version to 5.0.0.
616 lines
20 KiB
C++
616 lines
20 KiB
C++
/*
|
|
* CiderPress
|
|
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
|
|
* See the file LICENSE for distribution terms.
|
|
*/
|
|
/*
|
|
* ASPI I/O functions.
|
|
*
|
|
* Some notes on ASPI stuff:
|
|
* - The Nero ASPI provides an interface for IDE hard drives. It also
|
|
* throws in a couple of mystery devices on a host adapter at the end.
|
|
* It has "unknown" device type and doesn't respond to SCSI device
|
|
* inquiries, so it's easy to ignore.
|
|
* - The Win98 generic ASPI only finds CD-ROM drives on the IDE bus.
|
|
*/
|
|
#include "StdAfx.h"
|
|
#if defined(_WIN32) && defined (WANT_ASPI)
|
|
|
|
#include "DiskImgPriv.h"
|
|
#include "SCSIDefs.h"
|
|
#include "CP_wnaspi32.h"
|
|
#include "ASPI.h"
|
|
|
|
|
|
/*
|
|
* Initialize ASPI.
|
|
*/
|
|
DIError ASPI::Init(void)
|
|
{
|
|
DWORD aspiStatus;
|
|
static const char* kASPIDllName = "wnaspi32.dll";
|
|
|
|
/*
|
|
* Try to load the DLL.
|
|
*/
|
|
fhASPI = ::LoadLibrary(kASPIDllName);
|
|
if (fhASPI == NULL) {
|
|
DWORD lastErr = ::GetLastError();
|
|
if (lastErr == ERROR_MOD_NOT_FOUND) {
|
|
LOGI("ASPI DLL '%s' not found", kASPIDllName);
|
|
} else {
|
|
LOGI("ASPI LoadLibrary(%s) failed (err=%ld)",
|
|
kASPIDllName, GetLastError());
|
|
}
|
|
return kDIErrGeneric;
|
|
}
|
|
|
|
GetASPI32SupportInfo = (DWORD(*)(void))::GetProcAddress(fhASPI, "GetASPI32SupportInfo");
|
|
SendASPI32Command = (DWORD(*)(LPSRB))::GetProcAddress(fhASPI, "SendASPI32Command");
|
|
GetASPI32DLLVersion = (DWORD(*)(void))::GetProcAddress(fhASPI, "GetASPI32DLLVersion");
|
|
if (GetASPI32SupportInfo == NULL || SendASPI32Command == NULL) {
|
|
LOGI("ASPI functions not found in dll");
|
|
::FreeLibrary(fhASPI);
|
|
fhASPI = NULL;
|
|
return kDIErrGeneric;
|
|
}
|
|
|
|
if (GetASPI32DLLVersion != NULL) {
|
|
fASPIVersion = GetASPI32DLLVersion();
|
|
LOGI(" ASPI version is %d.%d.%d.%d",
|
|
fASPIVersion & 0x0ff,
|
|
(fASPIVersion >> 8) & 0xff,
|
|
(fASPIVersion >> 16) & 0xff,
|
|
(fASPIVersion >> 24) & 0xff);
|
|
} else {
|
|
LOGI("ASPI WARNING: couldn't find GetASPI32DLLVersion interface");
|
|
}
|
|
|
|
/*
|
|
* Successfully loaded the library. Start it up and see if it works.
|
|
*/
|
|
aspiStatus = GetASPI32SupportInfo();
|
|
if (HIBYTE(LOWORD(aspiStatus)) != SS_COMP) {
|
|
LOGI("ASPI loaded but not working (status=%d)",
|
|
HIBYTE(LOWORD(aspiStatus)));
|
|
::FreeLibrary(fhASPI);
|
|
fhASPI = NULL;
|
|
return kDIErrASPIFailure;
|
|
}
|
|
|
|
fHostAdapterCount = LOBYTE(LOWORD(aspiStatus));
|
|
LOGI("ASPI loaded successfully, hostAdapterCount=%d",
|
|
fHostAdapterCount);
|
|
|
|
return kDIErrNone;
|
|
}
|
|
|
|
/*
|
|
* Destructor. Unload the ASPI DLL.
|
|
*/
|
|
ASPI::~ASPI(void)
|
|
{
|
|
if (fhASPI != NULL) {
|
|
LOGI("Unloading ASPI DLL");
|
|
::FreeLibrary(fhASPI);
|
|
fhASPI = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Issue an ASPI host adapter inquiry request for the specified adapter.
|
|
*
|
|
* Pass in a pointer to a struct that receives the result.
|
|
*/
|
|
DIError ASPI::HostAdapterInquiry(unsigned char adapter, AdapterInfo* pAdapterInfo)
|
|
{
|
|
SRB_HAInquiry req;
|
|
DWORD result;
|
|
|
|
assert(adapter >= 0 && adapter < kMaxAdapters);
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
req.SRB_Cmd = SC_HA_INQUIRY;
|
|
req.SRB_HaId = adapter;
|
|
|
|
result = SendASPI32Command(&req);
|
|
if (result != SS_COMP) {
|
|
LOGI("ASPI(SC_HA_INQUIRY on %d) failed with result=0x%lx",
|
|
adapter, result);
|
|
return kDIErrASPIFailure;
|
|
}
|
|
|
|
pAdapterInfo->adapterScsiID = req.HA_SCSI_ID;
|
|
memcpy(pAdapterInfo->managerID, req.HA_ManagerId,
|
|
sizeof(pAdapterInfo->managerID)-1);
|
|
pAdapterInfo->managerID[sizeof(pAdapterInfo->managerID)-1] = '\0';
|
|
memcpy(pAdapterInfo->identifier, req.HA_Identifier,
|
|
sizeof(pAdapterInfo->identifier)-1);
|
|
pAdapterInfo->identifier[sizeof(pAdapterInfo->identifier)-1] = '\0';
|
|
pAdapterInfo->maxTargets = req.HA_Unique[3];
|
|
pAdapterInfo->bufferAlignment =
|
|
(unsigned short) req.HA_Unique[1] << 8 | req.HA_Unique[0];
|
|
|
|
return kDIErrNone;
|
|
}
|
|
|
|
/*
|
|
* Issue an ASPI query on device type.
|
|
*/
|
|
DIError ASPI::GetDeviceType(unsigned char adapter, unsigned char target,
|
|
unsigned char lun, unsigned char* pType)
|
|
{
|
|
SRB_GDEVBlock req;
|
|
DWORD result;
|
|
|
|
assert(adapter >= 0 && adapter < kMaxAdapters);
|
|
assert(target >= 0 && target < kMaxTargets);
|
|
assert(lun >= 0 && lun < kMaxLuns);
|
|
assert(pType != NULL);
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
req.SRB_Cmd = SC_GET_DEV_TYPE;
|
|
req.SRB_HaId = adapter;
|
|
req.SRB_Target = target;
|
|
req.SRB_Lun = lun;
|
|
|
|
result = SendASPI32Command(&req);
|
|
if (result != SS_COMP)
|
|
return kDIErrASPIFailure;
|
|
|
|
*pType = req.SRB_DeviceType;
|
|
|
|
return kDIErrNone;
|
|
}
|
|
|
|
/*
|
|
* Return a printable string for the given device type.
|
|
*/
|
|
const char* ASPI::DeviceTypeToString(unsigned char deviceType)
|
|
{
|
|
switch (deviceType) {
|
|
case kScsiDevTypeDASD: return "Disk device";
|
|
case kScsiDevTypeSEQD: return "Tape device";
|
|
case kScsiDevTypePRNT: return "Printer";
|
|
case kScsiDevTypePROC: return "Processor";
|
|
case kScsiDevTypeWORM: return "Write-once read-multiple";
|
|
case kScsiDevTypeCDROM: return "CD-ROM device";
|
|
case kScsiDevTypeSCAN: return "Scanner device";
|
|
case kScsiDevTypeOPTI: return "Optical memory device";
|
|
case kScsiDevTypeJUKE: return "Medium changer device";
|
|
case kScsiDevTypeCOMM: return "Communications device";
|
|
case kScsiDevTypeUNKNOWN: return "Unknown or no device type";
|
|
default: return "Invalid type";
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Issue a SCSI device inquiry and return the interesting parts.
|
|
*/
|
|
DIError ASPI::DeviceInquiry(unsigned char adapter, unsigned char target,
|
|
unsigned char lun, Inquiry* pInquiry)
|
|
{
|
|
DIError dierr;
|
|
SRB_ExecSCSICmd srb;
|
|
CDB6Inquiry* pCDB;
|
|
unsigned char buf[96]; // enough to hold everything of interest, and more
|
|
CDB_InquiryData* pInqData = (CDB_InquiryData*) buf;
|
|
|
|
assert(sizeof(CDB6Inquiry) == 6);
|
|
|
|
memset(&srb, 0, sizeof(srb));
|
|
srb.SRB_Cmd = SC_EXEC_SCSI_CMD;
|
|
srb.SRB_HaId = adapter;
|
|
srb.SRB_Target = target;
|
|
srb.SRB_Lun = lun;
|
|
srb.SRB_Flags = SRB_DIR_IN;
|
|
srb.SRB_BufLen = sizeof(buf);
|
|
srb.SRB_BufPointer = buf;
|
|
srb.SRB_SenseLen = SENSE_LEN;
|
|
srb.SRB_CDBLen = sizeof(*pCDB);
|
|
|
|
pCDB = (CDB6Inquiry*) srb.CDBByte;
|
|
pCDB->operationCode = kScsiOpInquiry;
|
|
pCDB->allocationLength = sizeof(buf);
|
|
|
|
// Don't set pCDB->logicalUnitNumber. It's only there for SCSI-1
|
|
// devices. SCSI-2 uses an IDENTIFY command; I gather ASPI is doing
|
|
// this for us.
|
|
|
|
dierr = ExecSCSICommand(&srb);
|
|
if (dierr != kDIErrNone)
|
|
return dierr;
|
|
|
|
memcpy(pInquiry->vendorID, pInqData->vendorId,
|
|
sizeof(pInquiry->vendorID)-1);
|
|
pInquiry->vendorID[sizeof(pInquiry->vendorID)-1] = '\0';
|
|
memcpy(pInquiry->productID, pInqData->productId,
|
|
sizeof(pInquiry->productID)-1);
|
|
pInquiry->productID[sizeof(pInquiry->productID)-1] = '\0';
|
|
pInquiry->productRevision[0] = pInqData->productRevisionLevel[0];
|
|
pInquiry->productRevision[1] = pInqData->productRevisionLevel[1];
|
|
pInquiry->productRevision[2] = pInqData->productRevisionLevel[2];
|
|
pInquiry->productRevision[3] = pInqData->productRevisionLevel[3];
|
|
|
|
return kDIErrNone;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the capacity of a SCSI block device.
|
|
*/
|
|
DIError ASPI::GetDeviceCapacity(unsigned char adapter, unsigned char target,
|
|
unsigned char lun, unsigned long* pLastBlock, unsigned long* pBlockSize)
|
|
{
|
|
DIError dierr;
|
|
SRB_ExecSCSICmd srb;
|
|
CDB10* pCDB;
|
|
CDB_ReadCapacityData dataBuf;
|
|
|
|
assert(sizeof(dataBuf) == 8); // READ CAPACITY returns two longs
|
|
assert(sizeof(CDB10) == 10);
|
|
|
|
memset(&srb, 0, sizeof(srb));
|
|
srb.SRB_Cmd = SC_EXEC_SCSI_CMD;
|
|
srb.SRB_HaId = adapter;
|
|
srb.SRB_Target = target;
|
|
srb.SRB_Lun = lun;
|
|
srb.SRB_Flags = SRB_DIR_IN;
|
|
srb.SRB_BufLen = sizeof(dataBuf);
|
|
srb.SRB_BufPointer = (unsigned char*)&dataBuf;
|
|
srb.SRB_SenseLen = SENSE_LEN;
|
|
srb.SRB_CDBLen = sizeof(*pCDB);
|
|
|
|
pCDB = (CDB10*) srb.CDBByte;
|
|
pCDB->operationCode = kScsiOpReadCapacity;
|
|
// rest of CDB is zero
|
|
|
|
dierr = ExecSCSICommand(&srb);
|
|
if (dierr != kDIErrNone)
|
|
return dierr;
|
|
|
|
*pLastBlock =
|
|
(unsigned long) dataBuf.logicalBlockAddr0 << 24 |
|
|
(unsigned long) dataBuf.logicalBlockAddr1 << 16 |
|
|
(unsigned long) dataBuf.logicalBlockAddr2 << 8 |
|
|
(unsigned long) dataBuf.logicalBlockAddr3;
|
|
*pBlockSize =
|
|
(unsigned long) dataBuf.bytesPerBlock0 << 24 |
|
|
(unsigned long) dataBuf.bytesPerBlock1 << 16 |
|
|
(unsigned long) dataBuf.bytesPerBlock2 << 8 |
|
|
(unsigned long) dataBuf.bytesPerBlock3;
|
|
return kDIErrNone;
|
|
}
|
|
|
|
/*
|
|
* Test to see if a device is ready.
|
|
*
|
|
* Returns "true" if the device is ready, "false" if not.
|
|
*/
|
|
DIError ASPI::TestUnitReady(unsigned char adapter, unsigned char target,
|
|
unsigned char lun, bool* pReady)
|
|
{
|
|
DIError dierr;
|
|
SRB_ExecSCSICmd srb;
|
|
CDB6* pCDB;
|
|
|
|
assert(sizeof(CDB6) == 6);
|
|
|
|
memset(&srb, 0, sizeof(srb));
|
|
srb.SRB_Cmd = SC_EXEC_SCSI_CMD;
|
|
srb.SRB_HaId = adapter;
|
|
srb.SRB_Target = target;
|
|
srb.SRB_Lun = lun;
|
|
srb.SRB_Flags = 0; //SRB_DIR_IN;
|
|
srb.SRB_BufLen = 0;
|
|
srb.SRB_BufPointer = NULL;
|
|
srb.SRB_SenseLen = SENSE_LEN;
|
|
srb.SRB_CDBLen = sizeof(*pCDB);
|
|
|
|
pCDB = (CDB6*) srb.CDBByte;
|
|
pCDB->operationCode = kScsiOpTestUnitReady;
|
|
// rest of CDB is zero
|
|
|
|
dierr = ExecSCSICommand(&srb);
|
|
if (dierr != kDIErrNone) {
|
|
const CDB_SenseData* pSense = (const CDB_SenseData*) srb.SenseArea;
|
|
|
|
if (srb.SRB_TargStat == kScsiStatCheckCondition &&
|
|
pSense->senseKey == kScsiSenseNotReady)
|
|
{
|
|
// expect pSense->additionalSenseCode to be
|
|
// kScsiAdSenseNoMediaInDevice; no need to check it really.
|
|
LOGI(" ASPI TestUnitReady: drive %d:%d:%d is NOT ready",
|
|
adapter, target, lun);
|
|
} else {
|
|
LOGI(" ASPI TestUnitReady failed, status=0x%02x sense=0x%02x ASC=0x%02x",
|
|
srb.SRB_TargStat, pSense->senseKey,
|
|
pSense->additionalSenseCode);
|
|
}
|
|
*pReady = false;
|
|
} else {
|
|
const CDB_SenseData* pSense = (const CDB_SenseData*) srb.SenseArea;
|
|
LOGI(" ASPI TestUnitReady: drive %d:%d:%d is ready",
|
|
adapter, target, lun);
|
|
//LOGI(" status=0x%02x sense=0x%02x ASC=0x%02x",
|
|
// srb.SRB_TargStat, pSense->senseKey, pSense->additionalSenseCode);
|
|
*pReady = true;
|
|
}
|
|
|
|
return kDIErrNone;
|
|
}
|
|
|
|
/*
|
|
* Read one or more blocks from the device.
|
|
*
|
|
* The block size is going to be whatever the device's native size is
|
|
* (possibly modified by extents, but we'll ignore that). For a CD-ROM
|
|
* this means 2048-byte blocks.
|
|
*/
|
|
DIError ASPI::ReadBlocks(unsigned char adapter, unsigned char target,
|
|
unsigned char lun, long startBlock, short numBlocks, long blockSize,
|
|
void* buf)
|
|
{
|
|
SRB_ExecSCSICmd srb;
|
|
CDB10* pCDB;
|
|
|
|
//LOGI(" ASPI ReadBlocks start=%ld num=%d (size=%d)",
|
|
// startBlock, numBlocks, blockSize);
|
|
|
|
assert(sizeof(CDB10) == 10);
|
|
assert(startBlock >= 0);
|
|
assert(numBlocks > 0);
|
|
assert(buf != NULL);
|
|
|
|
memset(&srb, 0, sizeof(srb));
|
|
srb.SRB_Cmd = SC_EXEC_SCSI_CMD;
|
|
srb.SRB_HaId = adapter;
|
|
srb.SRB_Target = target;
|
|
srb.SRB_Lun = lun;
|
|
srb.SRB_Flags = SRB_DIR_IN;
|
|
srb.SRB_BufLen = numBlocks * blockSize;
|
|
srb.SRB_BufPointer = (unsigned char*)buf;
|
|
srb.SRB_SenseLen = SENSE_LEN;
|
|
srb.SRB_CDBLen = sizeof(*pCDB);
|
|
|
|
pCDB = (CDB10*) srb.CDBByte;
|
|
pCDB->operationCode = kScsiOpRead;
|
|
pCDB->logicalBlockAddr0 = (unsigned char) (startBlock >> 24); // MSB
|
|
pCDB->logicalBlockAddr1 = (unsigned char) (startBlock >> 16);
|
|
pCDB->logicalBlockAddr2 = (unsigned char) (startBlock >> 8);
|
|
pCDB->logicalBlockAddr3 = (unsigned char) startBlock; // LSB
|
|
pCDB->transferLength0 = (unsigned char) (numBlocks >> 8); // MSB
|
|
pCDB->transferLength1 = (unsigned char) numBlocks; // LSB
|
|
|
|
return ExecSCSICommand(&srb);
|
|
}
|
|
|
|
/*
|
|
* Write one or more blocks to the device.
|
|
*/
|
|
DIError ASPI::WriteBlocks(unsigned char adapter, unsigned char target,
|
|
unsigned char lun, long startBlock, short numBlocks, long blockSize,
|
|
const void* buf)
|
|
{
|
|
SRB_ExecSCSICmd srb;
|
|
CDB10* pCDB;
|
|
|
|
LOGI(" ASPI WriteBlocks start=%ld num=%d (size=%d)",
|
|
startBlock, numBlocks, blockSize);
|
|
|
|
assert(sizeof(CDB10) == 10);
|
|
assert(startBlock >= 0);
|
|
assert(numBlocks > 0);
|
|
assert(buf != NULL);
|
|
|
|
memset(&srb, 0, sizeof(srb));
|
|
srb.SRB_Cmd = SC_EXEC_SCSI_CMD;
|
|
srb.SRB_HaId = adapter;
|
|
srb.SRB_Target = target;
|
|
srb.SRB_Lun = lun;
|
|
srb.SRB_Flags = SRB_DIR_IN;
|
|
srb.SRB_BufLen = numBlocks * blockSize;
|
|
srb.SRB_BufPointer = (unsigned char*)buf;
|
|
srb.SRB_SenseLen = SENSE_LEN;
|
|
srb.SRB_CDBLen = sizeof(*pCDB);
|
|
|
|
pCDB = (CDB10*) srb.CDBByte;
|
|
pCDB->operationCode = kScsiOpWrite;
|
|
pCDB->logicalBlockAddr0 = (unsigned char) (startBlock >> 24); // MSB
|
|
pCDB->logicalBlockAddr1 = (unsigned char) (startBlock >> 16);
|
|
pCDB->logicalBlockAddr2 = (unsigned char) (startBlock >> 8);
|
|
pCDB->logicalBlockAddr3 = (unsigned char) startBlock; // LSB
|
|
pCDB->transferLength0 = (unsigned char) (numBlocks >> 8); // MSB
|
|
pCDB->transferLength1 = (unsigned char) numBlocks; // LSB
|
|
|
|
return ExecSCSICommand(&srb);
|
|
}
|
|
|
|
|
|
/*
|
|
* Execute a SCSI command.
|
|
*
|
|
* Returns an error if ASPI reports an error or the SCSI status isn't
|
|
* kScsiStatGood.
|
|
*
|
|
* The Nero ASPI layer typically returns immediately, and hands back an
|
|
* SS_ERR when something fails. Win98 ASPI does the SS_PENDING thang.
|
|
*/
|
|
DIError ASPI::ExecSCSICommand(SRB_ExecSCSICmd* pSRB)
|
|
{
|
|
HANDLE completionEvent = NULL;
|
|
DWORD eventStatus;
|
|
DWORD aspiStatus;
|
|
|
|
assert(pSRB->SRB_Cmd == SC_EXEC_SCSI_CMD);
|
|
assert(pSRB->SRB_Flags == SRB_DIR_IN ||
|
|
pSRB->SRB_Flags == SRB_DIR_OUT ||
|
|
pSRB->SRB_Flags == 0);
|
|
|
|
/*
|
|
* Set up event-waiting stuff, as described in the Adaptec ASPI docs.
|
|
*/
|
|
pSRB->SRB_Flags |= SRB_EVENT_NOTIFY;
|
|
|
|
completionEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (completionEvent == NULL) {
|
|
LOGI("Failed creating a completion event?");
|
|
return kDIErrGeneric;
|
|
}
|
|
|
|
pSRB->SRB_PostProc = completionEvent;
|
|
|
|
/*
|
|
* Send the request.
|
|
*/
|
|
(void)SendASPI32Command((LPSRB) pSRB);
|
|
aspiStatus = pSRB->SRB_Status;
|
|
if (aspiStatus == SS_PENDING) {
|
|
//LOGI(" (waiting for completion)");
|
|
eventStatus = ::WaitForSingleObject(completionEvent, kTimeout * 1000);
|
|
|
|
::CloseHandle(completionEvent);
|
|
|
|
if (eventStatus == WAIT_TIMEOUT) {
|
|
LOGI(" ASPI exec timed out!");
|
|
return kDIErrSCSIFailure;
|
|
} else if (eventStatus != WAIT_OBJECT_0) {
|
|
LOGI(" ASPI exec returned weird wait state %ld", eventStatus);
|
|
return kDIErrGeneric;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check the final status.
|
|
*/
|
|
aspiStatus = pSRB->SRB_Status;
|
|
|
|
if (aspiStatus == SS_COMP) {
|
|
/* success! */
|
|
} else if (aspiStatus == SS_ERR) {
|
|
const CDB_SenseData* pSense = (const CDB_SenseData*) pSRB->SenseArea;
|
|
|
|
LOGI(" ASPI SCSI command 0x%02x failed: scsiStatus=0x%02x"
|
|
" senseKey=0x%02x ASC=0x%02x\n",
|
|
pSRB->CDBByte[0], pSRB->SRB_TargStat,
|
|
pSense->senseKey, pSense->additionalSenseCode);
|
|
return kDIErrSCSIFailure;
|
|
} else {
|
|
// SS_ABORTED, SS_ABORT_FAIL, SS_NO_DEVICE, ...
|
|
LOGI(" ASPI failed on command 0x%02x: aspiStatus=%d scsiStatus=%d",
|
|
pSRB->CDBByte[0], aspiStatus, pSRB->SRB_TargStat);
|
|
return kDIErrASPIFailure;
|
|
}
|
|
|
|
return kDIErrNone;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return an array of accessible devices we found.
|
|
*
|
|
* Only return the devices matching device types in "deviceMask".
|
|
*/
|
|
DIError ASPI::GetAccessibleDevices(int deviceMask, ASPIDevice** ppDeviceArray,
|
|
int* pNumDevices)
|
|
{
|
|
DIError dierr;
|
|
ASPIDevice* deviceArray = NULL;
|
|
int idx = 0;
|
|
|
|
assert(deviceMask != 0);
|
|
assert((deviceMask & ~(kDevMaskCDROM | kDevMaskHardDrive)) == 0);
|
|
assert(ppDeviceArray != NULL);
|
|
assert(pNumDevices != NULL);
|
|
|
|
deviceArray = new ASPIDevice[kMaxAccessibleDrives];
|
|
if (deviceArray == NULL)
|
|
return kDIErrMalloc;
|
|
|
|
LOGI("ASPI scanning %d host adapters", fHostAdapterCount);
|
|
|
|
for (int ha = 0; ha < fHostAdapterCount; ha++) {
|
|
AdapterInfo adi;
|
|
|
|
dierr = HostAdapterInquiry(ha, &adi);
|
|
if (dierr != kDIErrNone) {
|
|
LOGI(" ASPI inquiry on %d failed", ha);
|
|
continue;
|
|
}
|
|
|
|
LOGI(" ASPI host adapter %d (SCSI ID=%d)", ha, adi.adapterScsiID);
|
|
LOGI(" identifier='%s' managerID='%s'",
|
|
adi.identifier, adi.managerID);
|
|
LOGI(" maxTargets=%d bufferAlignment=%d",
|
|
adi.maxTargets, adi.bufferAlignment);
|
|
|
|
int maxTargets = adi.maxTargets;
|
|
if (!maxTargets) {
|
|
/* Win98 ASPI reports zero here for ATAPI */
|
|
maxTargets = 8;
|
|
}
|
|
if (maxTargets > kMaxTargets)
|
|
maxTargets = kMaxTargets;
|
|
for (int targ = 0; targ < maxTargets; targ++) {
|
|
for (int lun = 0; lun < kMaxLuns; lun++) {
|
|
Inquiry inq;
|
|
unsigned char deviceType;
|
|
char addrString[48];
|
|
bool deviceReady;
|
|
|
|
dierr = GetDeviceType(ha, targ, lun, &deviceType);
|
|
if (dierr != kDIErrNone)
|
|
continue;
|
|
|
|
sprintf(addrString, "%d:%d:%d", ha, targ, lun);
|
|
|
|
dierr = DeviceInquiry(ha, targ, lun, &inq);
|
|
if (dierr != kDIErrNone) {
|
|
LOGI(" ASPI DeviceInquiry for '%s' (type=%d) failed",
|
|
addrString, deviceType);
|
|
continue;
|
|
}
|
|
|
|
LOGI(" Device %s is %s '%s' '%s'",
|
|
addrString, DeviceTypeToString(deviceType),
|
|
inq.vendorID, inq.productID);
|
|
|
|
if ((deviceMask & kDevMaskCDROM) != 0 &&
|
|
deviceType == kScsiDevTypeCDROM)
|
|
{
|
|
/* found CD-ROM */
|
|
} else if ((deviceMask & kDevMaskHardDrive) != 0 &&
|
|
deviceType == kScsiDevTypeDASD)
|
|
{
|
|
/* found hard drive */
|
|
} else
|
|
continue;
|
|
|
|
if (idx >= kMaxAccessibleDrives) {
|
|
LOGI("GLITCH: ran out of places to stuff CD-ROM drives");
|
|
assert(false);
|
|
goto done;
|
|
}
|
|
|
|
dierr = TestUnitReady(ha, targ, lun, &deviceReady);
|
|
if (dierr != kDIErrNone) {
|
|
LOGI(" ASPI TestUnitReady for '%s' failed", addrString);
|
|
continue;
|
|
}
|
|
|
|
deviceArray[idx].Init(ha, targ, lun, inq.vendorID,
|
|
inq.productID, deviceType, deviceReady);
|
|
idx++;
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
*ppDeviceArray = deviceArray;
|
|
*pNumDevices = idx;
|
|
return kDIErrNone;
|
|
}
|
|
|
|
#endif /*_WIN32*/
|