IOSCSIParallelDevice.cpp [plain text]
#include <libkern/c++/OSMetaClass.h>
#include <IOKit/IOSyncer.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/system.h>
#include "IOSCSIParallelDevice.h"
#include "IOSCSIParallelController.h"
#include "IOSCSIParallelCommand.h"
#include "SCSICommand.h"
#undef super
#define super IOSCSIDevice
#ifndef MIN
#define MIN(a,b) ((a <= b) ? a : b)
#endif
OSDefineMetaClassAndAbstractStructors( IOCDBDevice, IOService )
OSDefineMetaClassAndAbstractStructors( IOSCSIDevice, IOCDBDevice )
OSDefineMetaClassAndStructors( IOSCSIParallelDevice, IOSCSIDevice )
bool IOSCSIParallelDevice::init( IOSCSIParallelController *forController, SCSITargetLun forTargetLun )
{
SCSICDBInfo scsiCDB;
controller = forController;
targetLun = forTargetLun;
target = &controller->targets[targetLun.target];
queue_init( &deviceList );
queue_init( &bypassList );
queue_init( &activeList );
queue_init( &abortList );
queue_init( &cancelList );
clientSem = IORWLockAlloc();
if ( clientSem == 0 )
{
return false;
}
if ( super::init() == false )
{
return false;
}
if ( controller->controllerInfo.lunPrivateDataSize != 0 )
{
devicePrivateData = IOMallocContiguous( controller->controllerInfo.lunPrivateDataSize, 16, 0 );
if ( devicePrivateData == 0 )
{
return false;
}
}
bzero( &scsiCDB, sizeof(scsiCDB) );
abortCmd = allocCommand(kIOSCSIParallelDevice, 0);
if ( abortCmd == 0 )
{
return false;
}
abortCmd->setTimeout( kSCSIAbortTimeoutmS );
cancelCmd = allocCommand(kIOSCSIParallelDevice, 0);
if ( cancelCmd == 0 )
{
return false;
}
cancelCmd->setTimeout( 0 );
cancelCmd->cmdType = kSCSICommandCancel;
reqSenseCmd = allocCommand(kIOSCSIParallelDevice, 0);
if ( reqSenseCmd == 0 )
{
return false;
}
scsiCDB.cdbLength = 6;
scsiCDB.cdb[0] = kSCSICmdRequestSense;
scsiCDB.cdb[1] = targetLun.lun << 4;
scsiCDB.cdbTag = (UInt32) -1;
reqSenseCmd->setTimeout( kSCSIReqSenseTimeoutmS );
reqSenseCmd->cmdType = kSCSICommandReqSense;
reqSenseCmd->setCDB( &scsiCDB );
if ( controller->controllerInfo.tagAllocationMethod == kTagAllocationPerLun )
{
tagArray = (UInt32 *)IOMalloc( controller->tagArraySize );
bzero( tagArray, controller->tagArraySize );
}
deviceGate = IOCommandGate::commandGate( this, OSMemberFunctionCast(IOCommandGate::Action, this, &IOSCSIParallelDevice::receiveCommand ) );
if ( deviceGate == 0 )
{
return false;
}
if ( controller->workLoop->addEventSource( deviceGate ) != kIOReturnSuccess )
{
return false;
}
commandLimitSave = commandLimit = controller->controllerInfo.maxCommandsPerLun;
idleNotifyActive = false;
normalQHeld = false;
bypassQHeld = false;
return true;
}
IOReturn IOSCSIParallelDevice::probeTargetLun()
{
SCSICDBInfo cdb;
SCSIResults result;
IOReturn rc = kIOReturnSuccess;
IOMemoryDescriptor * desc = 0;
SCSIInquiry * inqData = 0;
UInt32 size = 0;
OSDictionary * propTable = NULL;
UInt32 attempts = 0;
probeCmd = allocCommand ( kIOSCSIParallelDevice, 0 );
if ( probeCmd == 0 )
{
rc = kIOReturnNoMemory;
goto probeError;
}
size = sizeof ( SCSIInquiry );
if ( !( inqData = ( SCSIInquiry * ) IOMalloc ( size ) ) )
{
rc = kIOReturnNoMemory;
goto probeError;
}
desc = IOMemoryDescriptor::withAddress (
( void * ) inqData,
size,
kIODirectionIn );
if ( desc == 0 )
{
rc = kIOReturnNoMemory;
goto probeError;
}
if ( open ( this ) == false )
{
rc = kIOReturnError;
goto probeError;
}
for ( attempts = 0; attempts < 8; attempts++ )
{
bzero ( ( void * ) &cdb, sizeof ( cdb ) );
cdb.cdbLength = 6;
cdb.cdb[0] = kSCSICmdInquiry;
cdb.cdb[4] = size;
probeCmd->setCDB ( &cdb );
probeCmd->setPointers ( desc, size, false );
probeCmd->setTimeout ( kSCSIProbeTimeoutmS );
probeCmd->setCallback ( );
probeCmd->execute ( );
rc = probeCmd->getResults ( &result );
if ( rc == kIOReturnBusy )
{
IOSleep ( 200 );
continue;
}
else if ( rc == kIOReturnUnderrun )
{
rc = kIOReturnSuccess;
break;
}
else if ( rc == kIOReturnSuccess )
{
break;
}
else
{
goto probeError;
}
}
if ( result.bytesTransferred <= ( UInt32 )( &inqData->flags - &inqData->devType ) )
{
rc = kIOReturnDeviceError;
goto probeError;
}
switch ( inqData->devType & kSCSIDevTypeQualifierMask )
{
case kSCSIDevTypeQualifierConnected:
case kSCSIDevTypeQualifierNotConnected:
break;
case kSCSIDevTypeQualifierReserved:
case kSCSIDevTypeQualifierMissing:
rc = kIOReturnNotAttached;
break;
default:
break;
}
if ( rc != kIOReturnSuccess )
{
goto probeError;
}
inquiryData = inqData;
inquiryDataSize = result.bytesTransferred;
propTable = createProperties ( );
if ( !propTable )
goto probeError;
setPropertyTable ( propTable );
propTable->release ( );
probeError: ;
if ( desc )
{
desc->release ( );
}
if ( inqData )
{
if ( rc != kIOReturnSuccess )
{
IOFree ( inqData, size );
}
}
return rc;
}
void IOSCSIParallelDevice::setupTarget()
{
SCSITargetParms targetParms;
UInt32 transferWidth;
if ( targetLun.lun != 0 )
{
close( this );
return;
}
getTargetParms( &targetParms );
if ( ((inquiryData->flags & kSCSIDevCapCmdQue) != 0) && (checkCmdQueEnabled() == true) )
{
targetParms.enableTagQueuing = true;
}
if ( inquiryData->flags & kSCSIDevCapSync )
{
targetParms.transferPeriodpS = controller->controllerInfo.minTransferPeriodpS;
targetParms.transferOffset = controller->controllerInfo.maxTransferOffset;
}
if ( inquiryData->flags & kSCSIDevCapWBus32 )
{
transferWidth = 4;
}
else if ( inquiryData->flags & kSCSIDevCapWBus16 )
{
transferWidth = 2;
}
else
{
transferWidth = 1;
}
targetParms.transferWidth = MIN( transferWidth, controller->controllerInfo.maxTransferWidth );
if ( ((inquiryData->version & 0x07) >= kSCSIInqVersionSCSI3)
&& (inquiryDataSize > (UInt32)(&inquiryData->scsi3Options - &inquiryData->devType)) )
{
if ( inquiryData->scsi3Options & kSCSI3InqOptionClockDT )
{
targetParms.transferOptions |= kSCSITransferOptionClockDT;
targetParms.transferOptions |= kSCSITransferOptionPPR;
if ( inquiryData->scsi3Options & kSCSI3InqOptionIUS )
{
targetParms.transferOptions |= kSCSITransferOptionIUS;
if ( inquiryData->scsi3Options & kSCSI3InqOptionQAS )
{
targetParms.transferOptions |= kSCSITransferOptionQAS;
}
}
}
}
setTargetParms( &targetParms );
close( this );
}
bool IOSCSIParallelDevice::checkCmdQueEnabled()
{
SCSICDBInfo scsiCDB;
SCSIResults scsiResult;
IOMemoryDescriptor *desc;
UInt32 size;
UInt8 controlModePage[32];
IOReturn cmdRc;
bool rc = false;
bzero( (void *)&scsiCDB, sizeof(scsiCDB) );
size = sizeof(controlModePage);
scsiCDB.cdbLength = 6;
scsiCDB.cdb[0] = kSCSICmdModeSense6;
scsiCDB.cdb[1] = 0x08;
scsiCDB.cdb[2] = 0x0a; scsiCDB.cdb[4] = size;
probeCmd->setCDB( &scsiCDB );
desc = IOMemoryDescriptor::withAddress( (void *)controlModePage, size, kIODirectionIn );
if ( desc == 0 )
{
return rc;
}
probeCmd->setPointers( desc, size, false );
probeCmd->setTimeout( kSCSIProbeTimeoutmS );
probeCmd->setCallback();
probeCmd->execute();
cmdRc = probeCmd->getResults( &scsiResult );
if ( (cmdRc == kIOReturnUnderrun) && (scsiResult.bytesTransferred > 7) )
{
cmdRc = kIOReturnSuccess;
}
if ( (cmdRc == kIOReturnSuccess) && ((controlModePage[7] & 0x01) == 0) )
{
rc = true;
}
desc->release();
return rc;
}
void IOSCSIParallelDevice::getInquiryData( void *clientBuf, UInt32 clientBufSize, UInt32 *clientDataSize )
{
UInt32 len;
bzero( clientBuf, clientBufSize );
len = MIN( clientBufSize, inquiryDataSize );
bcopy( inquiryData, clientBuf, len );
*clientDataSize = len;
}
void IOSCSIParallelDevice::abort()
{
submitCommand( kSCSICommandAbortAll, 0 );
}
void IOSCSIParallelDevice::reset()
{
submitCommand( kSCSICommandDeviceReset, 0 );
}
void IOSCSIParallelDevice::holdQueue( UInt32 queueType )
{
if ( getWorkLoop()->inGate() == false )
{
IOPanic( "IOSCSIParallelDevice::holdQueue() - must be called from workloop!!\n\r");
}
if ( queueType == kQTypeBypassQ )
{
bypassQHeld = true;
}
else if ( queueType == kQTypeNormalQ )
{
normalQHeld = true;
}
}
void IOSCSIParallelDevice::releaseQueue( UInt32 queueType )
{
if ( getWorkLoop()->inGate() == false )
{
IOPanic( "IOSCSIParallelDevice::releaseQueue() - must be called from workloop!!\n\r");
}
if ( queueType == kQTypeBypassQ )
{
bypassQHeld = false;
}
else if ( queueType == kQTypeNormalQ )
{
normalQHeld = false;
}
dispatchRequest();
}
void IOSCSIParallelDevice::notifyIdle( void *target, CallbackFn callback, void *refcon )
{
if ( getWorkLoop()->inGate() == false )
{
IOPanic( "IOSCSIParallelDevice:::notifyIdle() - must be called from workloop!!\n\r");
}
if ( callback == 0 )
{
idleNotifyActive = false;
return;
}
if ( idleNotifyActive == true )
{
IOPanic( "IOSCSIParallelDevice:::notifyIdle() - only one idle notify may be active\n\r");
}
idleNotifyActive = true;
idleNotifyTarget = target;
idleNotifyCallback = callback;
idleNotifyRefcon = refcon;
checkIdleNotify();
}
void IOSCSIParallelDevice::submitCommand( UInt32 cmdType, IOSCSIParallelCommand *scsiCmd, UInt32 cmdSequenceNumber )
{
deviceGate->runCommand( (void *)cmdType, (void *)scsiCmd, (void *) cmdSequenceNumber, (void *) 0 );
}
void IOSCSIParallelDevice::receiveCommand( UInt32 cmdType, IOSCSIParallelCommand *scsiCmd, UInt32 cmdSequenceNumber, void *p3 )
{
queue_head_t *queue;
switch ( cmdType )
{
case kSCSICommandExecute:
scsiCmd->cmdType = (SCSICommandType) cmdType;
scsiCmd->scsiCmd.cdbFlags &= (kCDBFNoDisconnect);
queue = (scsiCmd->queueType == kQTypeBypassQ) ? &bypassList : &deviceList;
if ( scsiCmd->queuePosition == kQPositionHead )
{
stackCommand( queue, scsiCmd );
}
else
{
addCommand( queue, scsiCmd );
}
dispatchRequest();
break;
case kSCSICommandAbortAll:
abortAllCommands( kSCSICommandAbortAll );
break;
case kSCSICommandAbort:
abortCommand( scsiCmd, cmdSequenceNumber );
break;
case kSCSICommandDeviceReset:
abortAllCommands( kSCSICommandDeviceReset );
break;
default:
break;
}
}
void IOSCSIParallelDevice::abortCommand( IOSCSIParallelCommand *scsiCmd, UInt32 sequenceNumber )
{
if ( scsiCmd->list == (queue_head_t *)deviceGate )
{
if ( scsiCmd->sequenceNumber != sequenceNumber )
{
return;
}
scsiCmd->results.returnCode = kIOReturnAborted;
}
else if ( scsiCmd->list == &deviceList )
{
if ( scsiCmd->sequenceNumber != sequenceNumber )
{
return;
}
deleteCommand( &deviceList, scsiCmd );
scsiCmd->results.returnCode = kIOReturnAborted;
finishCommand( scsiCmd );
}
else if ( scsiCmd->list == &activeList )
{
if ( scsiCmd->sequenceNumber != sequenceNumber )
{
return;
}
moveCommand( &activeList, &abortList, scsiCmd );
dispatchRequest();
}
}
void IOSCSIParallelDevice::abortAllCommands( SCSICommandType cmdType )
{
IOSCSIParallelDevice *abortDev;
abortCmdPending = cmdType;
if ( abortCmdPending == kSCSICommandAbortAll )
{
if ( client != 0 )
{
client->message( kSCSIClientMsgDeviceAbort, this );
}
}
else if ( abortCmdPending == kSCSICommandDeviceReset )
{
queue_iterate( &target->deviceList, abortDev, IOSCSIParallelDevice *, nextDevice )
{
if ( abortDev->client != 0 )
{
abortDev->client->message( kSCSIClientMsgDeviceReset, abortDev );
}
}
}
dispatchRequest();
}
void IOSCSIParallelDevice::resetOccurred( SCSIClientMessage clientMsg )
{
if ( client != 0 && clientMsg != kSCSIClientMsgNone )
{
client->message( clientMsg, this );
}
moveAllCommands( &activeList, &cancelList, kIOReturnAborted );
moveAllCommands( &abortList, &cancelList, kIOReturnAborted );
abortState = kStateIdle;
reqSenseState = kStateIdle;
commandLimit = commandLimitSave;
negotiateState = kStateIdle;
dispatchRequest();
}
void IOSCSIParallelDevice::resetComplete()
{
if ( client != 0 )
{
client->message( kSCSIClientMsgBusReset | kSCSIClientMsgDone, this );
}
}
bool IOSCSIParallelDevice::checkAbortQueue()
{
IOSCSIParallelCommand *origCmd;
if ( abortState == kStateActive )
{
return true;
}
if ( abortCmdPending != kSCSICommandNone )
{
abortCmd->origCommand = 0;
abortCmd->scsiCmd.cdbTagMsg = 0;
abortCmd->scsiCmd.cdbTag = (UInt32) -1;
abortCmd->cmdType = abortCmdPending;
abortCmd->scsiCmd.cdbAbortMsg = (abortCmdPending == kSCSICommandAbortAll)
? kSCSIMsgAbort : kSCSIMsgBusDeviceReset;
if ( disableDisconnect == true )
{
abortCmd->scsiCmd.cdbFlags |= kCDBFlagsNoDisconnect;
}
else
{
abortCmd->scsiCmd.cdbFlags &= ~kCDBFlagsNoDisconnect;
}
abortCmd->timer = ( abortCmd->timeout != 0 ) ?
abortCmd->timeout / kSCSITimerIntervalmS + 1 : 0;
bzero( &abortCmd->results, sizeof(SCSIResults) );
abortCmdPending = kSCSICommandNone;
abortState = kStateActive;
addCommand( &activeList, abortCmd );
controller->executeCommand( abortCmd );
}
else if ( queue_empty( &abortList ) == false )
{
origCmd = (IOSCSIParallelCommand *)queue_first( &abortList );
abortCmd->origCommand = origCmd;
abortCmd->cmdType = kSCSICommandAbort;
abortCmd->scsiCmd.cdbTagMsg = origCmd->scsiCmd.cdbTagMsg;
abortCmd->scsiCmd.cdbTag = origCmd->scsiCmd.cdbTag;
abortCmd->scsiCmd.cdbAbortMsg = (abortCmd->scsiCmd.cdbTagMsg != 0)
? kSCSIMsgAbortTag : kSCSIMsgAbort;
abortCmd->timer = ( abortCmd->timeout != 0 ) ?
abortCmd->timeout / kSCSITimerIntervalmS + 1 : 0;
bzero( &abortCmd->results, sizeof(SCSIResults) );
abortState = kStateActive;
addCommand( &activeList, abortCmd );
controller->executeCommand( abortCmd );
}
else
{
return false;
}
return true;
}
void IOSCSIParallelDevice::checkCancelQueue()
{
if ( cancelState != kStateIdle )
{
return;
}
if ( queue_empty( &cancelList ) == true )
{
return;
}
if ( controller->controllerInfo.disableCancelCommands == true )
{
return;
}
cancelCmd->origCommand = (IOSCSIParallelCommand *)queue_first( &cancelList );
bzero( &cancelCmd->results, sizeof(SCSIResults) );
cancelState = kStateActive;
controller->cancelCommand( cancelCmd );
}
bool IOSCSIParallelDevice::checkReqSense()
{
IOMemoryDescriptor *senseData;
UInt32 senseLength;
if ( target->reqSenseState == kStateActive )
{
return true;
}
if ( reqSenseState == kStateIssue )
{
reqSenseCmd->origCommand = reqSenseOrigCmd;
bzero( &reqSenseCmd->results, sizeof(SCSIResults) );
reqSenseOrigCmd->getPointers( &senseData, &senseLength, 0, true );
reqSenseCmd->setPointers( senseData, senseLength, false );
reqSenseCmd->scsiCmd.cdbFlags = 0;
if ( disableDisconnect == true )
{
reqSenseCmd->scsiCmd.cdbFlags |= kCDBFlagsNoDisconnect;
}
else
{
reqSenseCmd->scsiCmd.cdbFlags &= ~kCDBFlagsNoDisconnect;
}
#if 0
tpCur = &target->targetParmsCurrent;
if ( tpCur->transferWidth > 1 )
{
reqSenseCmd->scsiCmd.cdbFlags |= kCDBFlagsNegotiateWDTR;
if (tpCur->transferOptions & kSCSITransferOptionPPR) {
reqSenseCmd->scsiCmd.cdbFlags |= kCDBFlagsNegotiatePPR;
}
}
if ( tpCur->transferOffset != 0 )
{
reqSenseCmd->scsiCmd.cdbFlags |= kCDBFlagsNegotiateSDTR;
if (tpCur->transferOptions & kSCSITransferOptionPPR) {
reqSenseCmd->scsiCmd.cdbFlags |= kCDBFlagsNegotiatePPR;
}
}
#endif
reqSenseCmd->timer = ( reqSenseCmd->timeout != 0 ) ?
reqSenseCmd->timeout / kSCSITimerIntervalmS + 1 : 0;
reqSenseCmd->scsiCmd.cdb[3] = (senseLength >> 8) & 0xff;
reqSenseCmd->scsiCmd.cdb[4] = senseLength & 0xff;
reqSenseState = kStatePending;
}
if ( reqSenseState == kStatePending )
{
target->reqSenseState = reqSenseState = kStateActive;
addCommand( &activeList, reqSenseCmd );
commandCount++;
controller->commandCount++;
controller->executeCommand( reqSenseCmd );
}
return (target->reqSenseCount > 0);
}
bool IOSCSIParallelDevice::checkDeviceQueue( UInt32 *dispatchAction )
{
IOSCSIParallelCommand *scsiCmd = 0;
queue_head_t *queue;
UInt32 i;
bool rc = true;
bool queueHeld;
do
{
if ( controller->commandCount >= controller->commandLimit )
{
*dispatchAction = kDispatchStop;
break;
}
if ( target->commandCount >= target->commandLimit )
{
*dispatchAction = kDispatchNextTarget;
break;
}
*dispatchAction = kDispatchNextLun;
if ( commandCount >= commandLimit )
{
break;
}
for ( i=0; i < 2; i++ )
{
queueHeld = (i == 0) ? bypassQHeld : normalQHeld;
queue = (i == 0) ? &bypassList : &deviceList;
if ( queueHeld == true )
{
continue;
}
scsiCmd = checkCommand( queue );
if ( scsiCmd != 0 )
{
*dispatchAction = kDispatchNextCommand;
break;
}
}
if ( i == 2 )
{
rc = false;
break;
}
if ( disableDisconnect == true || (scsiCmd->scsiCmd.cdbFlags & kCDBFNoDisconnect) )
{
scsiCmd->scsiCmd.cdbFlags |= kCDBFlagsNoDisconnect;
if ( controller->commandCount != 0 )
{
*dispatchAction = kDispatchNextLun;
break;
}
controller->noDisconnectCmd = scsiCmd;
controller->commandLimitSave = controller->commandLimit;
controller->commandLimit = 1;
}
else if ( checkTag( scsiCmd ) == false )
{
switch ( controller->controllerInfo.tagAllocationMethod )
{
case kTagAllocationPerTarget:
*dispatchAction = kDispatchNextTarget;
break;
case kTagAllocationPerController:
*dispatchAction = kDispatchStop;
break;
case kTagAllocationPerLun:
;
default:
*dispatchAction = kDispatchNextLun;
}
break;
}
getCommand( queue );
checkNegotiate( scsiCmd );
scsiCmd->timer = ( scsiCmd->timeout != 0 ) ? scsiCmd->timeout / kSCSITimerIntervalmS + 1 : 0;
commandCount++;
target->commandCount++;
controller->commandCount++;
addCommand( &activeList, scsiCmd );
controller->executeCommand( scsiCmd );
} while ( 0 );
return rc;
}
void IOSCSIParallelDevice::rescheduleCommand( IOSCSIParallelCommand *scsiCmd )
{
if ( scsiCmd->list != &activeList )
{
IOLog( "IOSCSIParallelController::rescheduleCommand() - Command not active. Cmd = %08x\n\r", (int)scsiCmd );
return;
}
deleteCommand( &activeList, scsiCmd );
switch ( scsiCmd->cmdType )
{
case kSCSICommandExecute:
if ( scsiCmd->scsiCmd.cdbTagMsg != 0 )
{
freeTag( scsiCmd->scsiCmd.cdbTag );
scsiCmd->scsiCmd.cdbTag = (UInt32) -1;
}
stackCommand( &deviceList, scsiCmd );
if ( scsiCmd->scsiCmd.cdbFlags & kCDBFlagsNoDisconnect )
{
controller->commandLimit = controller->commandLimitSave;
controller->noDisconnectCmd = 0;
}
controller->commandCount--;
target->commandCount--;
commandCount--;
break;
case kSCSICommandReqSense:
reqSenseState = kStatePending;
target->reqSenseState = kStateIdle;
commandCount--;
controller->commandCount--;
break;
case kSCSICommandAbortAll:
case kSCSICommandDeviceReset:
abortCmdPending = scsiCmd->cmdType;
case kSCSICommandAbort:
abortState = kStateIdle;
break;
default:
;
}
dispatchRequest();
}
bool IOSCSIParallelDevice::setTargetParms( SCSITargetParms *targetParms )
{
IOSCSIParallelCommand *scsiCmd;
SCSICDBInfo scsiCDB;
bool fTagEnable;
bool rc = true;
IOMemoryDescriptor *senseDesc;
UInt8 senseBuffer[14];
if ( getWorkLoop()->inGate() == true )
{
IOPanic( "IOSCSIParallelDevice:::setTargetParms() - must not be called from workloop!!\n\r");
}
IOWriteLock( target->clientSem );
IOWriteLock( target->targetSem );
while ( target->negotiateState == kStateActive )
{
IOSleep( 100 );
}
target->targetParmsNew = *targetParms;
if ( targetParms->transferPeriodpS < controller->controllerInfo.minTransferPeriodpS )
{
target->targetParmsNew.transferPeriodpS = controller->controllerInfo.minTransferPeriodpS;
}
if ( target->targetParmsNew.transferPeriodpS == 0
|| target->targetParmsNew.transferOffset == 0
|| controller->controllerInfo.minTransferPeriodpS == 0 )
{
target->targetParmsNew.transferPeriodpS = 0;
target->targetParmsNew.transferOffset = 0;
}
target->commandLimit = 1;
fTagEnable = (targetParms->enableTagQueuing == true)
&& (controller->controllerInfo.tagAllocationMethod != kTagAllocationNone)
&& (controller->controllerInfo.maxTags != 0);
regObjCmdQueue->setValue( (UInt32)fTagEnable );
if ( fTagEnable == true )
{
target->commandLimitSave = controller->controllerInfo.maxCommandsPerTarget;
}
else
{
target->commandLimitSave = 1;
target->targetParmsNew.enableTagQueuing = false;
}
scsiCmd = allocCommand(kIOSCSIParallelDevice, 0);
bzero( &scsiCDB, sizeof( SCSICDBInfo ) );
scsiCDB.cdbLength = 6;
scsiCDB.cdb[0] = kSCSICmdTestUnitReady;
scsiCDB.cdb[1] = targetLun.lun << 4;
scsiCmd->setCDB( &scsiCDB );
senseDesc = IOMemoryDescriptor::withAddress(senseBuffer, sizeof(senseBuffer), kIODirectionIn);
if ( senseDesc == 0 ) return false;
scsiCmd->setPointers( senseDesc, sizeof(senseBuffer), false, true );
target->negotiateState = kStateIssue;
scsiCmd->execute();
IOWriteLock( target->targetSem );
IORWUnlock( target->targetSem );
scsiCmd->release();
senseDesc->release();
rc = (target->negotiationResult.returnCode == kIOReturnSuccess);
IORWUnlock( target->clientSem );
return rc;
}
void IOSCSIParallelDevice::getTargetParms( SCSITargetParms *targetParms )
{
*targetParms = target->targetParmsCurrent;
}
bool IOSCSIParallelDevice::setLunParms( SCSILunParms *lunParms )
{
IOSCSIParallelCommand *scsiCmd;
SCSICDBInfo scsiCDB;
IOMemoryDescriptor *senseDesc;
UInt8 senseBuffer[14];
if ( getWorkLoop()->inGate() == true )
{
IOPanic( "IOSCSIParallelDevice:::setLunParms() - must not be called from workloop!!\n\r");
}
IOWriteLock( clientSem );
lunParmsNew = *lunParms;
commandLimitSave = commandLimit;
commandLimit = 1;
scsiCmd = allocCommand(kIOSCSIParallelDevice, 0);
bzero( &scsiCDB, sizeof( SCSICDBInfo ) );
scsiCDB.cdbLength = 6;
scsiCDB.cdb[0] = kSCSICmdTestUnitReady;
scsiCDB.cdb[1] = targetLun.lun << 4;
scsiCmd->setCDB( &scsiCDB );
senseDesc = IOMemoryDescriptor::withAddress(senseBuffer, sizeof(senseBuffer), kIODirectionIn);
if ( senseDesc == 0 ) return false;
scsiCmd->setPointers( senseDesc, sizeof(senseBuffer), false, true );
negotiateState = kStateIssue;
scsiCmd->execute();
scsiCmd->release();
senseDesc->release();
while ( negotiateState != kStateIdle )
{
IOSleep( 100 );
}
IORWUnlock( clientSem );
return true;
}
void IOSCSIParallelDevice::getLunParms( SCSILunParms *lunParms )
{
lunParms->disableDisconnect = disableDisconnect;
}
void IOSCSIParallelDevice::checkNegotiate( IOSCSIParallelCommand *scsiCmd )
{
SCSITargetParms *tpCur, *tpNew;
if ( target->negotiateState == kStateIssue )
{
if ( target->commandCount == 0 )
{
target->negotiateState = kStateActive;
tpNew = &target->targetParmsNew;
tpCur = &target->targetParmsCurrent;
target->negotiationResult.returnCode = kIOReturnError;
if ((tpCur->transferPeriodpS != tpNew->transferPeriodpS) ||
(tpCur->transferOffset != tpNew->transferOffset) ||
((tpCur->transferOptions ^ tpNew->transferOptions) & kSCSITransferOptionsSCSI3) )
{
scsiCmd->scsiCmd.cdbFlags |= kCDBFlagsNegotiateSDTR;
if (tpNew->transferOptions & kSCSITransferOptionPPR) {
scsiCmd->scsiCmd.cdbFlags |= kCDBFlagsNegotiatePPR;
}
}
if ( tpCur->transferWidth != tpNew->transferWidth )
{
scsiCmd->scsiCmd.cdbFlags |= kCDBFlagsNegotiateWDTR;
}
if ( tpCur->enableTagQueuing != tpNew->enableTagQueuing )
{
scsiCmd->scsiCmd.cdbFlags |= kCDBFlagsEnableTagQueuing;
}
if ( (scsiCmd->scsiCmd.cdbFlags &
(kCDBFlagsNegotiateSDTR |
kCDBFlagsNegotiateWDTR |
kCDBFlagsNegotiatePPR |
kCDBFlagsEnableTagQueuing)) == 0 )
{
IORWUnlock( target->targetSem );
target->negotiateState = kStateIdle;
target->commandLimit = target->commandLimitSave;
}
*tpCur = *tpNew;
}
}
if ( negotiateState == kStateIssue )
{
if ( commandCount == 0 )
{
disableDisconnect = lunParmsNew.disableDisconnect;
negotiateState = kStateIdle;
}
}
}
void IOSCSIParallelDevice::negotiationComplete()
{
SCSITargetParms *tpCur, *tpNew;
tpNew = &target->targetParmsNew;
tpCur = &target->targetParmsCurrent;
if ( target->negotiationResult.returnCode == kIOReturnSuccess )
{
tpCur->transferPeriodpS = tpNew->transferPeriodpS = target->negotiationResult.transferPeriodpS;
tpCur->transferOffset = tpNew->transferOffset = target->negotiationResult.transferOffset;
tpCur->transferWidth = tpNew->transferWidth = target->negotiationResult.transferWidth;
tpCur->transferOptions = tpNew->transferOptions = target->negotiationResult.transferOptions;
target->commandLimit = target->commandLimitSave;
}
else
{
tpNew->transferPeriodpS = 0;
tpNew->transferOffset = 0;
tpNew->transferWidth = 1;
}
target->regObjTransferPeriod->setValue( tpNew->transferPeriodpS );
target->regObjTransferOffset->setValue( tpNew->transferOffset );
target->regObjTransferWidth->setValue( tpNew->transferWidth );
target->regObjTransferOptions->setValue( tpNew->transferOptions );
target->negotiateState = kStateIdle;
}
bool IOSCSIParallelDevice::checkTag( IOSCSIParallelCommand *scsiCmd )
{
SCSICDBInfo scsiCDB;
bool rc = true;
scsiCmd->getCDB( &scsiCDB );
scsiCDB.cdbTagMsg = 0;
scsiCDB.cdbTag = (UInt32)-1;
do
{
if ( scsiCmd->device->target->targetParmsCurrent.enableTagQueuing == false )
{
break;
}
if ( scsiCDB.cdbTagMsg == 0 )
{
break;
}
if ( allocTag( &scsiCDB.cdbTag ) == false )
{
rc = false;
break;
}
}
while ( 0 );
scsiCmd->setCDB( &scsiCDB );
return rc;
}
bool IOSCSIParallelDevice::allocTag( UInt32 *tagId )
{
UInt32 i;
UInt32 tagIndex;
UInt32 tagMask;
UInt32 *tags = 0;
switch ( controller->controllerInfo.tagAllocationMethod )
{
case kTagAllocationPerLun:
tags = tagArray;
break;
case kTagAllocationPerTarget:
tags = target->tagArray;
break;
case kTagAllocationPerController:
tags = controller->tagArray;
break;
default:
;
}
if ( tags == 0 ) return false;
for ( i = 0; i < controller->controllerInfo.maxTags; i++ )
{
tagIndex = i / 32;
tagMask = 1 << (i % 32);
if ( !(tags[tagIndex] & tagMask) )
{
tags[tagIndex] |= tagMask;
*tagId = i;
return true;
}
}
return false;
}
void IOSCSIParallelDevice::freeTag( UInt32 tagId )
{
UInt32 *tags = 0;
switch ( controller->controllerInfo.tagAllocationMethod )
{
case kTagAllocationPerLun:
tags = tagArray;
break;
case kTagAllocationPerTarget:
tags = target->tagArray;
break;
case kTagAllocationPerController:
tags = controller->tagArray;
break;
default:
;
}
if ( tags == 0 ) return;
tags[tagId/32] &= ~(1 << (tagId % 32));
}
IOSCSIParallelCommand *IOSCSIParallelDevice::findCommandWithNexus( UInt32 tagValue )
{
IOSCSIParallelCommand *scsiCmd;
queue_iterate( &activeList, scsiCmd, IOSCSIParallelCommand *, nextCommand )
{
switch ( scsiCmd->cmdType )
{
case kSCSICommandExecute:
case kSCSICommandReqSense:
if ( scsiCmd->scsiCmd.cdbTag == tagValue )
{
return scsiCmd;
}
break;
default:
;
}
}
queue_iterate( &abortList, scsiCmd, IOSCSIParallelCommand *, nextCommand )
{
switch ( scsiCmd->cmdType )
{
case kSCSICommandExecute:
case kSCSICommandReqSense:
if ( scsiCmd->scsiCmd.cdbTag == tagValue )
{
return scsiCmd;
}
break;
default:
;
}
}
return 0;
}
void IOSCSIParallelDevice::timer()
{
IOSCSIParallelCommand *scsiCmd, *tmp = 0;
SCSITargetLun scsiTargetLun;
queue_iterate( &activeList, scsiCmd, IOSCSIParallelCommand *, nextCommand )
{
tmp = (IOSCSIParallelCommand *)queue_prev( &scsiCmd->nextCommand );
if ( scsiCmd->timer )
{
if ( !--scsiCmd->timer )
{
scsiCmd->getTargetLun( &scsiTargetLun );
IOLog("Timeout: T/L = %d:%d Cmd = %08x Cmd Type = %d\n\r",
scsiTargetLun.target, scsiTargetLun.lun, (int)scsiCmd, scsiCmd->cmdType );
switch ( scsiCmd->cmdType )
{
case kSCSICommandExecute:
moveCommand( &activeList, &abortList, scsiCmd, kIOReturnTimeout );
scsiCmd = tmp;
break;
case kSCSICommandReqSense:
reqSenseState = kStateIdle;
moveCommand( &activeList, &abortList, scsiCmd, kIOReturnTimeout );
scsiCmd = tmp;
break;
case kSCSICommandAbort:
case kSCSICommandAbortAll:
case kSCSICommandDeviceReset:
controller->busResetState = kStateIssue;
break;
default:
;
}
dispatchRequest();
}
}
if ( queue_end( &activeList, (queue_head_t *)scsiCmd ) == true )
{
break;
}
}
}
void IOSCSIParallelDevice::dispatchRequest()
{
target->state = kStateActive;
controller->dispatchRequest();
}
bool IOSCSIParallelDevice::dispatch( UInt32 *dispatchAction )
{
bool rc;
checkCancelQueue();
if ( controller->checkBusReset() == true )
{
*dispatchAction = kDispatchStop;
return true;
}
if ( (rc = controller->commandDisable) == true )
{
*dispatchAction = kDispatchNextTarget;
return true;
}
if ( checkAbortQueue() == true )
{
*dispatchAction = kDispatchNextTarget;
return true;
}
do
{
if ( (rc = controller->commandDisable) == true )
{
*dispatchAction = kDispatchStop;
break;
}
if ( (rc = checkReqSense()) == true )
{
*dispatchAction = kDispatchNextTarget;
break;
}
rc = checkDeviceQueue( dispatchAction );
} while ( *dispatchAction == kDispatchNextCommand );
return rc;
}
void IOSCSIParallelDevice::completeCommand( IOSCSIParallelCommand *scsiCmd )
{
SCSICommandType cmdType;
cmdType = scsiCmd->cmdType;
switch ( cmdType )
{
case kSCSICommandExecute:
executeCommandDone( scsiCmd );
break;
case kSCSICommandReqSense:
executeReqSenseDone( scsiCmd );
break;
case kSCSICommandAbort:
case kSCSICommandAbortAll:
case kSCSICommandDeviceReset:
abortCommandDone( scsiCmd );
break;
case kSCSICommandCancel:
cancelCommandDone( scsiCmd );
break;
default:
;
}
checkIdleNotify();
dispatchRequest();
}
void IOSCSIParallelDevice::checkIdleNotify()
{
if ( idleNotifyActive == false )
{
return;
}
if ( (queue_empty( &activeList ) == true)
&& (queue_empty( &abortList ) == true)
&& (queue_empty( &cancelList ) == true)
&& (target->reqSenseCount == 0) )
{
idleNotifyActive = false;
(idleNotifyCallback)( idleNotifyTarget, idleNotifyRefcon );
}
}
void IOSCSIParallelDevice::flushQueue( UInt32 queueType, IOReturn rc )
{
queue_head_t *queue;
queue = (queueType == kQTypeBypassQ) ? &bypassList : &deviceList;
purgeAllCommands( queue, rc );
}
void IOSCSIParallelDevice::executeCommandDone( IOSCSIParallelCommand *scsiCmd )
{
deleteCommand( scsiCmd->list, scsiCmd );
commandCount--;
controller->commandCount--;
target->commandCount--;
if ( scsiCmd->scsiCmd.cdbTagMsg != 0 )
{
freeTag( scsiCmd->scsiCmd.cdbTag );
scsiCmd->scsiCmd.cdbTag = (UInt32) -1;
}
if ( scsiCmd->scsiCmd.cdbFlags & (kCDBFlagsNegotiateSDTR |
kCDBFlagsNegotiateWDTR |
kCDBFlagsNegotiatePPR |
kCDBFlagsEnableTagQueuing) )
{
if ( scsiCmd->scsiCmd.cdbFlags & (kCDBFlagsNegotiateSDTR |
kCDBFlagsNegotiateWDTR |
kCDBFlagsNegotiatePPR) )
{
negotiationComplete();
}
else
{
target->negotiationResult.returnCode = kIOReturnSuccess;
}
IORWUnlock( target->targetSem );
}
if ( scsiCmd->scsiCmd.cdbFlags & kCDBFlagsNoDisconnect )
{
controller->commandLimit = controller->commandLimitSave;
controller->noDisconnectCmd = 0;
}
if ( scsiCmd->results.scsiStatus == kSCSIStatusCheckCondition
&& scsiCmd->results.requestSenseDone == false
&& scsiCmd->senseData != 0 )
{
reqSenseOrigCmd = scsiCmd;
reqSenseState = kStateIssue;
target->reqSenseCount++;
return;
}
if ( scsiCmd->results.scsiStatus == kSCSIStatusQueueFull )
{
if ( commandCount > 4 )
{
commandLimit = commandCount;
}
stackCommand( &deviceList, scsiCmd );
return;
}
finishCommand( scsiCmd );
}
void IOSCSIParallelDevice::executeReqSenseDone( IOSCSIParallelCommand *scsiCmd )
{
IOSCSIParallelCommand *origCommand;
deleteCommand( scsiCmd->list, scsiCmd );
target->reqSenseState = reqSenseState = kStateIdle;
target->reqSenseCount--;
commandCount--;
controller->commandCount--;
reqSenseOrigCmd = 0;
origCommand = scsiCmd->origCommand;
if ( (scsiCmd->results.returnCode == kIOReturnSuccess) || (scsiCmd->results.returnCode == kIOReturnUnderrun) )
{
origCommand->results.requestSenseDone = true;
origCommand->results.requestSenseLength = scsiCmd->results.bytesTransferred;
}
else
{
origCommand->results.requestSenseDone = false;
origCommand->results.requestSenseLength = 0;
}
finishCommand( scsiCmd->origCommand );
}
void IOSCSIParallelDevice::abortCommandDone( IOSCSIParallelCommand *scsiCmd )
{
IOSCSIParallelCommand *origSCSICmd;
IOSCSIParallelDevice *abortDev;
deleteCommand( scsiCmd->list, scsiCmd );
abortState = kStateIdle;
if ( scsiCmd->cmdType == kSCSICommandAbortAll )
{
moveAllCommands( &activeList, &cancelList, kIOReturnAborted );
moveAllCommands( &abortList, &cancelList, kIOReturnAborted );
if ( client != 0 )
{
client->message( kSCSIClientMsgDeviceAbort | kSCSIClientMsgDone, this );
}
}
if ( scsiCmd->cmdType == kSCSICommandDeviceReset )
{
target->commandLimit = target->commandLimitSave;
target->reqSenseCount = 0;
target->reqSenseState = kStateIdle;
target->negotiateState = kStateIssue;
target->targetParmsCurrent.transferPeriodpS = 0;
target->targetParmsCurrent.transferOffset = 0;
target->targetParmsCurrent.transferWidth = 1;
queue_iterate( &target->deviceList, abortDev, IOSCSIParallelDevice *, nextDevice )
{
abortDev->resetOccurred( (SCSIClientMessage)(kSCSIClientMsgDeviceReset | kSCSIClientMsgDone) );
}
}
else if ( scsiCmd->cmdType == kSCSICommandAbort )
{
origSCSICmd = scsiCmd->origCommand;
if ( findCommand( &abortList, origSCSICmd ) == true )
{
moveCommand( &abortList, &cancelList, origSCSICmd, kIOReturnAborted );
}
}
return;
}
void IOSCSIParallelDevice::cancelCommandDone( IOSCSIParallelCommand *scsiCmd )
{
IOSCSIParallelCommand *origSCSICmd;
cancelState = kStateIdle;
origSCSICmd = scsiCmd->origCommand;
if ( findCommand( &cancelList, origSCSICmd ) == true )
{
IOLog( "IOSCSIParallelDevice::cancelCommandDone - Cancelled command not completed - scsiCmd = %08x\n\r", (int)origSCSICmd );
deleteCommand( &cancelList, origSCSICmd );
}
}
void IOSCSIParallelDevice::finishCommand( IOSCSIParallelCommand *scsiCmd )
{
if ( scsiCmd->completionInfo.async.callback )
{
(*scsiCmd->completionInfo.async.callback)( scsiCmd->completionInfo.async.target,
scsiCmd->completionInfo.async.refcon );
}
else
{
scsiCmd->completionInfo.sync.lock->signal();
}
}
OSDictionary *IOSCSIParallelDevice::createProperties()
{
OSDictionary *propTable = 0;
OSObject *regObj;
char tmpbuf[81];
char *d;
char unit[10];
propTable = OSDictionary::withCapacity(kSCSIMaxProperties);
if ( propTable == NULL )
{
return NULL;
}
regObj = (OSObject *)OSNumber::withNumber(targetLun.target,32);
if ( addToRegistry( propTable, regObj, kSCSIPropertyTarget ) != true )
{
goto createprop_error;
}
regObj = (OSObject *)OSNumber::withNumber(targetLun.target,32);
if ( addToRegistry( propTable, regObj, kSCSIPropertyIOUnit ) != true )
{
goto createprop_error;
}
sprintf(unit,"%x",targetLun.target);
setLocation(unit);
regObj = (OSObject *)OSNumber::withNumber(targetLun.lun,32);
if ( addToRegistry( propTable, regObj, kSCSIPropertyLun ) != true )
{
goto createprop_error;
}
d= tmpbuf;
stripBlanks( d, (char *)inquiryData->vendorName, sizeof(inquiryData->vendorName) );
regObj = (OSObject *)OSString::withCString( d );
if ( addToRegistry( propTable, regObj, kSCSIPropertyVendorName ) != true )
{
goto createprop_error;
}
stripBlanks( d, (char *)inquiryData->productName, sizeof(inquiryData->productName) );
regObj = (OSObject *)OSString::withCString( d );
if ( addToRegistry( propTable, regObj, kSCSIPropertyProductName ) != true )
{
goto createprop_error;
}
stripBlanks( d, (char *)inquiryData->productRevision, sizeof(inquiryData->productRevision) );
regObj = (OSObject *)OSString::withCString( d );
if ( addToRegistry( propTable, regObj, kSCSIPropertyProductRevision ) != true )
{
goto createprop_error;
}
regObj = (OSObject *)OSBoolean::withBoolean( (inquiryData->devTypeMod & kSCSIDevTypeModRemovable) != 0 );
if ( addToRegistry( propTable, regObj, kSCSIPropertyRemovableMedia ) != true )
{
goto createprop_error;
}
regObj = (OSObject *)OSNumber::withNumber( inquiryData->devType & kSCSIDevTypeMask, 32 );
if ( addToRegistry( propTable, regObj, kSCSIPropertyDeviceTypeID ) != true )
{
goto createprop_error;
}
regObj = (OSObject *)target->regObjTransferPeriod;
if ( addToRegistry( propTable, regObj, kSCSIPropertyTransferPeriod, false ) != true )
{
goto createprop_error;
}
regObjTransferPeriod = (OSNumber *)regObj;
regObj = (OSObject *)target->regObjTransferOffset;
if ( addToRegistry( propTable, regObj, kSCSIPropertyTransferOffset, false ) != true )
{
goto createprop_error;
}
regObjTransferOffset = (OSNumber *)regObj;
regObj = (OSObject *)target->regObjTransferWidth;
if ( addToRegistry( propTable, regObj, kSCSIPropertyTransferWidth, false ) != true )
{
goto createprop_error;
}
regObjTransferWidth = (OSNumber *)regObj;
regObj = (OSObject *)target->regObjTransferOptions;
if ( addToRegistry( propTable, regObj, kSCSIPropertyTransferOptions, false ) != true )
{
goto createprop_error;
}
regObjTransferOptions = (OSNumber *)regObj;
regObj = (OSObject *)target->regObjCmdQueue;
if ( addToRegistry( propTable, regObj, kSCSIPropertyCmdQueue, false ) != true )
{
goto createprop_error;
}
regObjCmdQueue = (OSNumber *)regObj;
return propTable;
createprop_error: ;
propTable->release();
return NULL;
}
bool IOSCSIParallelDevice::addToRegistry( OSDictionary *propTable, OSObject *regObj, char *key,
bool doRelease )
{
bool rc;
if ( regObj == NULL )
{
return false;
}
rc = propTable->setObject( key, regObj );
if ( doRelease )
{
regObj->release();
}
return rc;
}
bool IOSCSIParallelDevice::matchPropertyTable(OSDictionary * table)
{
bool match;
match = compareProperty( table, kSCSIPropertyIOUnit ) &&
compareProperty( table, kSCSIPropertyDeviceTypeID ) &&
compareProperty( table, kSCSIPropertyRemovableMedia ) &&
compareProperty( table, kSCSIPropertyVendorName ) &&
compareProperty( table, kSCSIPropertyProductName ) &&
compareProperty( table, kSCSIPropertyProductRevision );
if ( match == true )
{
match = super::matchPropertyTable(table);
}
return match;
}
IOService *IOSCSIParallelDevice::matchLocation(IOService * client)
{
return this;
}
void IOSCSIParallelDevice::stripBlanks( char *d, char *s, UInt32 l )
{
char *p, c;
for ( p = d, c = *s; l && c ; l--)
{
c = (*d++ = *s++);
if ( c != ' ' )
{
p = d;
}
}
*p = 0;
}
IOSCSICommand *IOSCSIParallelDevice::allocCommand( IOSCSIDevice *, UInt32 clientDataSize )
{
return (IOSCSICommand *) allocCommand( kIOSCSIParallelDevice, clientDataSize );
}
IOSCSIParallelCommand *IOSCSIParallelDevice::allocCommand( IOSCSIParallelDevice *, UInt32 clientDataSize )
{
IOSCSIParallelCommand *cmd;
if ( (cmd = controller->allocCommand( clientDataSize )) )
{
cmd->device = this;
}
return cmd;
}
IOCDBCommand *IOSCSIParallelDevice::allocCommand( IOCDBDevice *, UInt32 clientDataSize )
{
return (IOCDBCommand *) allocCommand( kIOSCSIDevice, clientDataSize );
}
IOWorkLoop *IOSCSIParallelDevice::getWorkLoop() const
{
return controller->workLoop;
}
bool IOSCSIParallelDevice::open( IOService *forClient, IOOptionBits options, void *arg )
{
if ( client != 0 ) return false;
client = forClient;
return super::open( forClient, options, arg );
}
void IOSCSIParallelDevice::close( IOService *forClient, IOOptionBits options )
{
client = 0;
return super::close( forClient, options );
}
IOReturn IOSCSIParallelDevice::message( UInt32 forMsg, IOService *forProvider, void *forArg )
{
IOReturn rc = kIOReturnSuccess;
SCSIClientMessage clientMsg;
clientMsg = (SCSIClientMessage) forMsg;
switch( clientMsg )
{
case kSCSIClientMsgBusReset:
holdQueue( kQTypeNormalQ );
break;
case kSCSIClientMsgBusReset | kSCSIClientMsgDone:
releaseQueue( kQTypeNormalQ );
break;
default:
rc = super::message( clientMsg, forProvider, forArg );
}
return rc;
}
void IOSCSIParallelDevice::free()
{
if ( deviceGate != 0 )
{
controller->workLoop->removeEventSource( deviceGate );
deviceGate->release();
}
if ( reqSenseCmd != 0 ) reqSenseCmd->release();
if ( abortCmd != 0 ) abortCmd->release();
if ( cancelCmd != 0 ) cancelCmd->release();
if ( probeCmd != 0 ) probeCmd->release();
if ( tagArray != 0 ) IOFree( tagArray, controller->tagArraySize );
if ( inquiryData != 0 ) IOFree( inquiryData, inquiryDataSize );
if ( devicePrivateData != 0 ) IOFreeContiguous( devicePrivateData, controller->controllerInfo.lunPrivateDataSize );
if ( clientSem != 0 ) IORWLockFree( clientSem );
super::free();
}