/* * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * IOSCSIParallelDevice.cpp * */ #include #include #include #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, (IOCommandGate::Action) &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; /* If it's a SCSI-3 target that handles DT clocking, * assume the HBA can try using the PPR message. */ 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; // Control Mode Page 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; } /* Check DQue bit on ControlMode Page (0x0A) */ 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; //SCSITargetParms *tpCur; 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 the command is untagged, then don't allocate a tag nor * send the untagged command as a simple-tagged command. */ 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 ) { // IOLog( "IOSCSI: Q-full - commandCount = %d commandLimit = %d\n\r", commandCount, commandLimit ); 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 ) { // If 'doRelease' is true, then a reference count is consumed. 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; // IOLog( "IOSCSIParallelDevice::message() - clientMsg = %08x\n\r", clientMsg ); 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(); }