IOSCSIParallelInterfaceDevice.cpp [plain text]
#include <libkern/c++/OSData.h>
#include <libkern/c++/OSDictionary.h>
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
#include <IOKit/scsi/SCSICommandOperationCodes.h>
#include <IOKit/scsi/SCSICmds_INQUIRY_Definitions.h>
#include "IOSCSIParallelInterfaceDevice.h"
#include "DriverKitKernelSupport/IOUserSCSIParallelInterfaceController.h"
#define DEBUG 0
#define DEBUG_ASSERT_COMPONENT_NAME_STRING "SPI Device"
#if DEBUG
#define SCSI_PARALLEL_DEVICE_DEBUGGING_LEVEL 0
#endif
#include "IOSCSIParallelFamilyDebugging.h"
#if ( SCSI_PARALLEL_DEVICE_DEBUGGING_LEVEL >= 1 )
#define PANIC_NOW(x) panic x
#else
#define PANIC_NOW(x)
#endif
#if ( SCSI_PARALLEL_DEVICE_DEBUGGING_LEVEL >= 2 )
#define ERROR_LOG(x) IOLog x
#else
#define ERROR_LOG(x)
#endif
#if ( SCSI_PARALLEL_DEVICE_DEBUGGING_LEVEL >= 3 )
#define STATUS_LOG(x) IOLog x
#else
#define STATUS_LOG(x)
#endif
#define super IOSCSIProtocolServices
OSDefineMetaClassAndStructors ( IOSCSIParallelInterfaceDevice, IOSCSIProtocolServices );
#define kIOPropertyIOUnitKey "IOUnit"
#define kIODeviceLocationKey "io-device-location"
#define kMaxTaskRetryCount 3
enum
{
kWorldWideNameDataSize = 8,
kAddressIdentifierDataSize = 3,
kALPADataSize = 1,
kSASAddressDataSize = 8,
kSCSIPortIdentifierDataSize = 8
};
static IOPMPowerState sPowerStates[kSCSIProtocolLayerNumDefaultStates] =
{
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, (IOPMDeviceUsable | IOPMMaxPerformance), IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 }
};
#if 0
#pragma mark -
#pragma mark IOKit Member Routines
#pragma mark -
#endif
bool
IOSCSIParallelInterfaceDevice::SetInitialTargetProperties (
OSDictionary * properties )
{
OSDictionary * protocolDict = NULL;
OSObject * value = NULL;
bool result = false;
protocolDict = OSDictionary::withCapacity ( properties->getCount ( ) );
require_nonzero ( protocolDict, INIT_FAILURE );
setProperty ( kIOPropertyProtocolCharacteristicsKey, protocolDict );
protocolDict->release ( );
protocolDict = NULL;
value = properties->getObject ( kIOPropertyFibreChannelNodeWorldWideNameKey );
SetTargetProperty ( kIOPropertyFibreChannelNodeWorldWideNameKey, value );
value = properties->getObject ( kIOPropertyFibreChannelPortWorldWideNameKey );
SetTargetProperty ( kIOPropertyFibreChannelPortWorldWideNameKey, value );
value = properties->getObject ( kIOPropertyFibreChannelAddressIdentifierKey );
SetTargetProperty ( kIOPropertyFibreChannelAddressIdentifierKey, value );
value = properties->getObject ( kIOPropertyFibreChannelALPAKey );
SetTargetProperty ( kIOPropertyFibreChannelALPAKey, value );
value = properties->getObject ( kIOPropertySASAddressKey );
SetTargetProperty ( kIOPropertySASAddressKey, value );
value = properties->getObject ( kIOPropertyRetryCountKey );
SetTargetProperty ( kIOPropertyRetryCountKey, value );
result = true;
INIT_FAILURE:
return result;
}
bool
IOSCSIParallelInterfaceDevice::start ( IOService * provider )
{
OSDictionary * protocolDict = NULL;
OSDictionary * copyDict = NULL;
bool result = false;
char unit[10];
fController = OSDynamicCast ( IOSCSIParallelInterfaceController, provider );
require_nonzero ( fController, PROVIDER_CAST_FAILURE );
fController->retain ( );
result = super::start ( provider );
require ( result, PROVIDER_START_FAILURE );
result = fController->open ( this );
require ( result, CONTROLLER_OPEN_FAILURE );
result = fController->InitializeTargetForID ( fTargetIdentifier );
require ( result, CONTROLLER_INIT_FAILURE );
fMultiPathSupport = fController->DoesHBASupportMultiPathing ( );
InitializePowerManagement ( provider );
copyDict = OSDynamicCast ( OSDictionary, copyProperty ( kIOPropertyProtocolCharacteristicsKey ) );
if ( copyDict != NULL )
{
protocolDict = ( OSDictionary * ) copyDict->copyCollection ( );
copyDict->release ( );
copyDict = NULL;
}
if ( protocolDict != NULL )
{
OSNumber * targetID = NULL;
targetID = OSNumber::withNumber ( fTargetIdentifier, 64 );
if ( targetID != NULL )
{
protocolDict->setObject ( kIOPropertySCSITargetIdentifierKey, targetID );
setProperty ( kIOPropertyIOUnitKey, targetID );
targetID->release ( );
}
setProperty ( kIOPropertyProtocolCharacteristicsKey, protocolDict );
protocolDict->release ( );
protocolDict = NULL;
}
snprintf ( unit, 10, "%x", ( int ) fTargetIdentifier );
setLocation ( unit );
CreateSCSITargetDevice ( );
return true;
CONTROLLER_INIT_FAILURE:
CONTROLLER_OPEN_FAILURE:
PROVIDER_START_FAILURE:
PROVIDER_CAST_FAILURE:
return false;
}
void
IOSCSIParallelInterfaceDevice::stop ( IOService * provider )
{
super::stop ( provider );
}
bool
IOSCSIParallelInterfaceDevice::finalize ( IOOptionBits options )
{
if ( ( fController != NULL ) && ( fController->isOpen ( this ) == true ) )
{
fController->close ( this );
}
return super::finalize ( options );
}
void
IOSCSIParallelInterfaceDevice::free ( void )
{
if ( fHBAData != NULL )
{
IOFree ( fHBAData, fHBADataSize );
fHBAData = NULL;
fHBADataSize = 0;
}
if ( fQueueLock != NULL )
{
IOSimpleLockFree ( fQueueLock );
fQueueLock = NULL;
}
if ( fController != NULL )
{
fController->release ( );
fController = NULL;
}
super::free ( );
}
IOReturn
IOSCSIParallelInterfaceDevice::message (
UInt32 type,
IOService * provider,
void * argument )
{
IOReturn result = kIOReturnSuccess;
switch ( type )
{
case kSCSIControllerNotificationBusReset:
{
for ( int index = 0; index < kSCSIParallelFeature_TotalFeatureCount; index++ )
{
fFeatureIsNegotiated[index] = false;
}
SendNotification_VerifyDeviceState ( );
}
break;
case kSCSIPort_NotificationStatusChange:
{
messageClients ( kSCSIPort_NotificationStatusChange, argument );
}
default:
{
result = super::message ( type, provider, argument );
}
break;
}
return result;
}
IOReturn
IOSCSIParallelInterfaceDevice::requestProbe ( IOOptionBits options )
{
if ( isOpen ( ) == false )
{
CreateSCSITargetDevice ( );
return kIOReturnSuccess;
}
else
{
return kIOReturnNotPermitted;
}
}
SCSIParallelTask *
IOSCSIParallelInterfaceDevice::GetNextOutstandingTask ( )
{
SCSIParallelTask * task = NULL;
IOSimpleLockLock ( fQueueLock );
if ( !queue_empty ( &fOutstandingTaskList ) )
{
task = ( SCSIParallelTask * ) queue_first ( &fOutstandingTaskList );
}
IOSimpleLockUnlock ( fQueueLock );
return task;
}
void
IOSCSIParallelInterfaceDevice::SignalTaskSubmittedForTask ( SCSIParallelTaskIdentifier task )
{
SCSIParallelTask * parallelTask = NULL;
parallelTask = ( SCSIParallelTask * ) task;
if ( parallelTask != NULL )
{
parallelTask->fTaskSubmitted = true;
GetCommandGate ( )->commandWakeup ( ( void * ) ¶llelTask->fTaskSubmitted );
}
}
void
IOSCSIParallelInterfaceDevice::InitializePowerManagement ( IOService * provider )
{
PMinit ( );
temporaryPowerClampOn ( );
provider->joinPMtree ( this );
makeUsable ( );
fPowerManagementInitialized = true;
fCurrentPowerState = kSCSIProtocolLayerPowerStateOn;
registerPowerDriver ( this, sPowerStates, kSCSIProtocolLayerNumDefaultStates );
changePowerStateTo ( kSCSIProtocolLayerPowerStateOn );
fCurrentPowerState = kSCSIProtocolLayerPowerStateOn;
fProposedPowerState = kSCSIProtocolLayerPowerStateOn;
}
#if 0
#pragma mark -
#pragma mark Device Object Management Member routines
#pragma mark -
#endif
IOSCSIParallelInterfaceDevice *
IOSCSIParallelInterfaceDevice::CreateTarget (
SCSITargetIdentifier targetID,
UInt32 sizeOfHBAData,
IORegistryEntry * entry )
{
IOSCSIParallelInterfaceDevice * newDevice = NULL;
bool result = false;
STATUS_LOG ( ( "+CreateTarget \n" ) );
newDevice = OSTypeAlloc ( IOSCSIParallelInterfaceDevice );
require_nonzero ( newDevice, DEVICE_CREATION_FAILURE );
result = newDevice->InitTarget ( targetID, sizeOfHBAData, entry );
require ( result, RELEASE_DEVICE );
STATUS_LOG ( ( "-CreateTarget \n" ) );
return newDevice;
RELEASE_DEVICE:
require_nonzero_quiet ( newDevice, DEVICE_CREATION_FAILURE );
newDevice->release ( );
newDevice = NULL;
DEVICE_CREATION_FAILURE:
return NULL;
}
bool
IOSCSIParallelInterfaceDevice::InitTarget (
SCSITargetIdentifier targetID,
UInt32 sizeOfHBAData,
IORegistryEntry * entry )
{
bool result = false;
result = super::init ( 0 );
require ( result, ERROR_EXIT );
queue_init ( &fOutstandingTaskList );
queue_init ( &fResendTaskList );
fQueueLock = IOSimpleLockAlloc ( );
require_nonzero ( fQueueLock, ERROR_EXIT );
if ( entry != NULL )
{
OSObject * value = NULL;
lockForArbitration ( );
result = attachToParent ( entry, gIODTPlane );
unlockForArbitration ( );
require ( result, ATTACH_TO_PARENT_FAILURE );
value = entry->copyProperty ( kIODeviceLocationKey );
if ( value != NULL )
{
setProperty ( kIODeviceLocationKey, value );
}
}
fHBADataSize = sizeOfHBAData;
fTargetIdentifier = targetID;
fAllowResends = true;
fMultiPathSupport = true;
if ( sizeOfHBAData != 0 )
{
fHBAData = IOMalloc ( sizeOfHBAData );
require_nonzero ( fHBAData, HBA_DATA_ALLOC_FAILURE );
bzero ( fHBAData, sizeOfHBAData );
}
return true;
HBA_DATA_ALLOC_FAILURE:
ATTACH_TO_PARENT_FAILURE:
require_nonzero_quiet ( fQueueLock, ERROR_EXIT );
IOSimpleLockFree ( fQueueLock );
fQueueLock = NULL;
ERROR_EXIT:
return false;
}
void
IOSCSIParallelInterfaceDevice::DestroyTarget ( void )
{
IORegistryEntry * parent = NULL;
SendNotification_DeviceRemoved ( );
removeProperty ( kIODeviceLocationKey );
IOSimpleLockLock ( fQueueLock );
fAllowResends = false;
IOSimpleLockUnlock ( fQueueLock );
lockForArbitration ( );
parent = getParentEntry ( gIODTPlane );
if ( parent != NULL )
{
detachFromParent ( parent, gIODTPlane );
}
unlockForArbitration ( );
}
IOSCSIParallelInterfaceDevice *
IOSCSIParallelInterfaceDevice::GetPreviousDeviceInList ( void )
{
return fPreviousParallelDevice;
}
void
IOSCSIParallelInterfaceDevice::SetPreviousDeviceInList (
IOSCSIParallelInterfaceDevice * newPrev )
{
fPreviousParallelDevice = newPrev;
}
IOSCSIParallelInterfaceDevice *
IOSCSIParallelInterfaceDevice::GetNextDeviceInList ( void )
{
return fNextParallelDevice;
}
void
IOSCSIParallelInterfaceDevice::SetNextDeviceInList (
IOSCSIParallelInterfaceDevice * newNext )
{
fNextParallelDevice = newNext;
}
void
IOSCSIParallelInterfaceDevice::DetermineParallelFeatures ( UInt8 * inqData )
{
OSDictionary * dict = NULL;
OSDictionary * copyDict = NULL;
OSNumber * features = NULL;
UInt64 deviceFeatures = 0;
UInt64 ITNexusFeatures = 0;
bool supported = false;
UInt8 inqSCSIVersion = 0;
UInt8 inqDataLength = 0;
inqSCSIVersion = ( ( SCSICmd_INQUIRY_StandardData * ) inqData )->VERSION & kINQUIRY_ANSI_VERSION_Mask;
inqDataLength = ( ( SCSICmd_INQUIRY_StandardData * ) inqData )->ADDITIONAL_LENGTH + 5;
if ( ( inqSCSIVersion >= kINQUIRY_ANSI_VERSION_SCSI_2_Compliant ) &&
( inqDataLength > kINQUIRY_Byte7_Offset ) )
{
if ( inqData[kINQUIRY_Byte7_Offset] & kINQUIRY_Byte7_SYNC_Mask )
{
deviceFeatures |= (1 << kSCSIParallelFeature_SynchronousDataTransfer);
supported = DoesHBASupportSCSIParallelFeature ( kSCSIParallelFeature_SynchronousDataTransfer );
if ( supported == true )
{
fITNexusSupportsFeature[kSCSIParallelFeature_SynchronousDataTransfer] = true;
ITNexusFeatures |= (1 << kSCSIParallelFeature_SynchronousDataTransfer);
}
}
if ( inqData[kINQUIRY_Byte7_Offset] & kINQUIRY_Byte7_WBUS16_Mask )
{
deviceFeatures |= (1 << kSCSIParallelFeature_WideDataTransfer);
supported = DoesHBASupportSCSIParallelFeature ( kSCSIParallelFeature_WideDataTransfer );
if ( supported == true )
{
fITNexusSupportsFeature[kSCSIParallelFeature_WideDataTransfer] = true;
ITNexusFeatures |= (1 << kSCSIParallelFeature_WideDataTransfer);
}
}
}
if ( ( inqSCSIVersion >= kINQUIRY_ANSI_VERSION_SCSI_SPC_Compliant ) &&
( inqDataLength > kINQUIRY_Byte56_Offset ) )
{
if ( inqData[kINQUIRY_Byte56_Offset] & kINQUIRY_Byte56_IUS_Mask )
{
deviceFeatures |= (1 << kSCSIParallelFeature_InformationUnitTransfers);
supported = DoesHBASupportSCSIParallelFeature ( kSCSIParallelFeature_InformationUnitTransfers );
if ( supported == true )
{
fITNexusSupportsFeature[kSCSIParallelFeature_InformationUnitTransfers] = true;
ITNexusFeatures |= (1 << kSCSIParallelFeature_InformationUnitTransfers);
}
}
if ( inqData[kINQUIRY_Byte56_Offset] & kINQUIRY_Byte56_QAS_Mask )
{
deviceFeatures |= (1 << kSCSIParallelFeature_QuickArbitrationAndSelection);
supported = DoesHBASupportSCSIParallelFeature( kSCSIParallelFeature_QuickArbitrationAndSelection );
if ( supported == true )
{
fITNexusSupportsFeature[kSCSIParallelFeature_QuickArbitrationAndSelection] = true;
ITNexusFeatures |= (1 << kSCSIParallelFeature_QuickArbitrationAndSelection);
}
}
if ( ( ( inqData[kINQUIRY_Byte56_Offset] & kINQUIRY_Byte56_CLOCKING_Mask ) == kINQUIRY_Byte56_CLOCKING_ONLY_DT ) ||
( ( inqData[kINQUIRY_Byte56_Offset] & kINQUIRY_Byte56_CLOCKING_Mask ) == kINQUIRY_Byte56_CLOCKING_ST_AND_DT ) )
{
deviceFeatures |= (1 << kSCSIParallelFeature_DoubleTransitionDataTransfers);
supported = DoesHBASupportSCSIParallelFeature ( kSCSIParallelFeature_DoubleTransitionDataTransfers );
if ( supported == true )
{
fITNexusSupportsFeature[kSCSIParallelFeature_DoubleTransitionDataTransfers] = true;
ITNexusFeatures |= (1 << kSCSIParallelFeature_DoubleTransitionDataTransfers);
}
}
}
copyDict = ( OSDictionary * ) copyProperty ( kIOPropertyProtocolCharacteristicsKey );
if ( copyDict != NULL )
{
dict = ( OSDictionary * ) copyDict->copyCollection ( );
copyDict->release ( );
}
if ( dict != NULL )
{
features = OSNumber::withNumber ( deviceFeatures, 64 );
if ( features != NULL )
{
dict->setObject ( kIOPropertySCSIDeviceFeaturesKey, features );
features->release ( );
features = NULL;
}
features = OSNumber::withNumber ( ITNexusFeatures, 64 );
if ( features != NULL )
{
dict->setObject ( kIOPropertySCSI_I_T_NexusFeaturesKey, features );
features->release ( );
features = NULL;
}
setProperty ( kIOPropertyProtocolCharacteristicsKey, dict );
dict->release ( );
dict = NULL;
}
}
SCSITargetIdentifier
IOSCSIParallelInterfaceDevice::GetTargetIdentifier ( void )
{
return fTargetIdentifier;
}
void *
IOSCSIParallelInterfaceDevice::GetHBADataPointer ( void )
{
return fHBAData;
}
UInt32
IOSCSIParallelInterfaceDevice::GetHBADataSize ( void )
{
return fHBADataSize;
}
bool
IOSCSIParallelInterfaceDevice::IsFeatureNegotiationNecessary (
SCSIParallelFeature feature )
{
if ( feature >= kSCSIParallelFeature_TotalFeatureCount )
{
return false;
}
return ( fITNexusSupportsFeature[feature] &&
( fFeatureIsNegotiated[feature] == false ) );
}
SCSIParallelTaskIdentifier
IOSCSIParallelInterfaceDevice::FindTaskForAddress (
SCSILogicalUnitNumber theL,
SCSITaggedTaskIdentifier theQ )
{
SCSIParallelTask * task = NULL;
bool found = false;
IOSimpleLockLock ( fQueueLock );
queue_iterate ( &fOutstandingTaskList, task, SCSIParallelTask *, fCommandChain )
{
if ( ( GetLogicalUnitNumber ( task ) == theL ) && ( GetTaggedTaskIdentifier ( task ) == theQ ) )
{
found = true;
break;
}
}
IOSimpleLockUnlock ( fQueueLock );
if ( found == false )
{
task = NULL;
}
return task;
}
SCSIParallelTaskIdentifier
IOSCSIParallelInterfaceDevice::FindTaskForControllerIdentifier (
UInt64 theIdentifier )
{
SCSIParallelTask * task = NULL;
bool found = false;
IOSimpleLockLock ( fQueueLock );
queue_iterate ( &fOutstandingTaskList, task, SCSIParallelTask *, fCommandChain )
{
if ( theIdentifier == kSCSIParallelTaskControllerIDQueueHead )
{
found = true;
break;
}
if ( GetControllerTaskIdentifier ( task ) == theIdentifier )
{
found = true;
break;
}
}
IOSimpleLockUnlock ( fQueueLock );
if ( found == false )
{
task = NULL;
}
return task;
}
bool
IOSCSIParallelInterfaceDevice::SetTargetProperty (
const char * key,
OSObject * value )
{
bool result = false;
OSDictionary * protocolDict = NULL;
OSDictionary * copyDict = NULL;
require_nonzero ( key, ErrorExit );
require_nonzero ( value, ErrorExit );
copyDict = OSDynamicCast ( OSDictionary, copyProperty ( kIOPropertyProtocolCharacteristicsKey ) );
require_nonzero ( copyDict, ErrorExit );
protocolDict = ( OSDictionary * ) copyDict->copyCollection ( );
copyDict->release ( );
require_nonzero ( protocolDict, ErrorExit );
if ( strcmp ( key, kIOPropertyFibreChannelPortWorldWideNameKey ) == 0 )
{
OSData * data = OSDynamicCast ( OSData, value );
require_nonzero ( data, ErrorExit );
require ( ( data->getLength ( ) == kWorldWideNameDataSize ), ErrorExit );
result = protocolDict->setObject ( key, value );
result = protocolDict->setObject ( kIOPropertySCSIPortIdentifierKey, value );
}
else if ( strcmp ( key, kIOPropertyFibreChannelNodeWorldWideNameKey ) == 0 )
{
OSData * data = OSDynamicCast ( OSData, value );
char name[27] = { 0 };
require_nonzero ( data, ErrorExit );
require ( ( data->getLength ( ) == kWorldWideNameDataSize ), ErrorExit );
result = protocolDict->setObject ( key, value );
snprintf ( name, sizeof ( name ), "FC Target %016qX", OSSwapHostToBigInt64 ( *( UInt64 * ) data->getBytesNoCopy ( ) ) );
setName ( name, gIOServicePlane );
}
else if ( strcmp ( key, kIOPropertyFibreChannelAddressIdentifierKey ) == 0 )
{
OSData * data = OSDynamicCast ( OSData, value );
require_nonzero ( data, ErrorExit );
require ( ( data->getLength ( ) == kAddressIdentifierDataSize ), ErrorExit );
result = protocolDict->setObject ( key, value );
}
else if ( strcmp ( key, kIOPropertyFibreChannelALPAKey ) == 0 )
{
OSData * data = OSDynamicCast ( OSData, value );
require_nonzero ( data, ErrorExit );
require ( ( data->getLength ( ) == kALPADataSize ), ErrorExit );
result = protocolDict->setObject ( key, value );
}
else if ( strcmp ( key, kIOPropertySASAddressKey ) == 0 )
{
OSData * data = OSDynamicCast ( OSData, value );
char name[28] = { 0 };
require_nonzero ( data, ErrorExit );
require ( ( data->getLength ( ) == kSASAddressDataSize ), ErrorExit );
result = protocolDict->setObject ( key, value );
result = protocolDict->setObject ( kIOPropertySCSIPortIdentifierKey, value );
snprintf ( name, sizeof ( name ), "SAS Target %016qX", OSSwapHostToBigInt64 ( *( UInt64 * ) data->getBytesNoCopy ( ) ) );
setName ( name, gIOServicePlane );
}
else if ( strcmp ( key, kIOPropertyRetryCountKey ) == 0 )
{
result = protocolDict->setObject ( key, value );
}
setProperty ( kIOPropertyProtocolCharacteristicsKey, protocolDict );
protocolDict->release ( );
protocolDict = NULL;
ErrorExit:
return result;
}
void
IOSCSIParallelInterfaceDevice::RemoveTargetProperty ( const char * key )
{
OSDictionary * protocolDict = NULL;
OSDictionary * copyDict = NULL;
require_nonzero ( key, ErrorExit );
copyDict = OSDynamicCast ( OSDictionary, copyProperty ( kIOPropertyProtocolCharacteristicsKey ) );
require_nonzero ( copyDict, ErrorExit );
protocolDict = ( OSDictionary * ) copyDict->copyCollection ( );
copyDict->release ( );
require_nonzero ( protocolDict, ErrorExit );
if ( protocolDict->getObject ( key ) != NULL )
{
protocolDict->removeObject ( key );
}
setProperty ( kIOPropertyProtocolCharacteristicsKey, protocolDict );
protocolDict->release ( );
protocolDict = NULL;
ErrorExit:
return;
}
#if 0
#pragma mark -
#pragma mark SCSI Protocol Services Member Routines
#pragma mark -
#endif
bool
IOSCSIParallelInterfaceDevice::SendSCSICommand (
SCSITaskIdentifier request,
SCSIServiceResponse * serviceResponse,
SCSITaskStatus * taskStatus )
{
SCSIParallelTaskIdentifier parallelTask = NULL;
SCSIParallelTask * parallelTaskPtr = NULL;
IOMemoryDescriptor * buffer = NULL;
IOReturn status = kIOReturnBadArgument;
IOWorkLoop * workLoop = NULL;
IOUserSCSIParallelInterfaceController * userController = NULL;
bool block = true;
bool ret = false;
*taskStatus = kSCSITaskStatus_No_Status;
*serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
userController = OSDynamicCast ( IOUserSCSIParallelInterfaceController, fController );
if ( isInactive ( ) == true )
{
return false;
}
workLoop = getWorkLoop ( );
if ( workLoop != NULL )
{
if ( workLoop->onThread ( ) )
{
block = false;
}
}
parallelTask = GetSCSIParallelTask ( block );
if ( parallelTask == NULL )
{
return false;
}
parallelTaskPtr = ( SCSIParallelTask * ) parallelTask;
SetTargetIdentifier ( parallelTask, fTargetIdentifier );
SetDevice ( parallelTask, this );
SetSCSITaskIdentifier ( parallelTask, request );
SetProtocolLayerReference ( request, parallelTask );
for ( UInt32 index = 0; index < kSCSIParallelFeature_TotalFeatureCount; index++ )
{
if ( IsFeatureNegotiationNecessary ( ( SCSIParallelFeature ) index ) == true )
{
SetSCSIParallelFeatureNegotiation (
parallelTask,
( SCSIParallelFeature ) index,
kSCSIParallelFeature_AttemptNegotiation );
}
}
AddToOutstandingTaskList ( parallelTask );
buffer = GetDataBuffer ( parallelTask );
if ( buffer != NULL )
{
status = SetDMABuffer ( parallelTask, buffer );
if ( status != kIOReturnSuccess )
{
ERROR_LOG ( ( "SetDMABuffer failed, status = 0x%08x\n", status ) );
RemoveFromOutstandingTaskList ( parallelTask );
FreeSCSIParallelTask ( parallelTask );
CommandCompleted ( request, *serviceResponse, *taskStatus );
ret = true;
goto Exit;
}
}
*serviceResponse = ExecuteParallelTask ( parallelTask );
if ( *serviceResponse != kSCSIServiceResponse_Request_In_Process )
{
RemoveFromOutstandingTaskList ( parallelTask );
FreeSCSIParallelTask ( parallelTask );
*serviceResponse = kSCSIServiceResponse_Request_In_Process;
if ( isInactive ( ) == true )
{
*taskStatus = kSCSITaskStatus_DeviceNotPresent;
}
CommandCompleted ( request, kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE, *taskStatus );
}
ret = true;
Exit:
if ( userController != NULL )
{
GetCommandGate ( )->runAction ( OSMemberFunctionCast (
IOCommandGate::Action,
this,
&IOSCSIParallelInterfaceDevice::SignalTaskSubmittedForTask ),
( void * ) parallelTask );
}
return ret;
}
void
IOSCSIParallelInterfaceDevice::CompleteSCSITask (
SCSIParallelTaskIdentifier completedTask,
SCSIServiceResponse serviceResponse,
SCSITaskStatus completionStatus )
{
SCSITaskIdentifier clientRequest = NULL;
SCSIParallelTask * task = ( SCSIParallelTask * ) completedTask;
UInt8 retryCount = task->fTaskRetryCount;
if ( completedTask == NULL )
{
return;
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( completionStatus == kSCSITaskStatus_TASK_SET_FULL ) &&
( fAllowResends == true ) &&
( retryCount < kMaxTaskRetryCount ) )
{
AddToResendTaskList ( completedTask );
return;
}
RemoveFromOutstandingTaskList ( completedTask );
clientRequest = GetSCSITaskIdentifier ( completedTask );
if ( clientRequest == NULL )
{
panic ( "IOSCSIParallelInterfaceDevice::CompleteSCSITask: clientRequest is NULL, completedTask = %p\n", completedTask );
}
IOSCSIProtocolServices::SetRealizedDataTransferCount ( clientRequest, GetRealizedDataTransferCount ( completedTask ) );
for ( UInt32 index = 0; index < kSCSIParallelFeature_TotalFeatureCount; index++ )
{
if ( IsFeatureNegotiationNecessary ( ( SCSIParallelFeature ) index ) == true )
{
if ( GetSCSIParallelFeatureNegotiationResult ( completedTask, ( SCSIParallelFeature ) index ) ==
kSCSIParallelFeature_NegotitiationSuccess )
{
fFeatureIsNegotiated[index] = true;
}
}
}
FreeSCSIParallelTask ( completedTask );
IOSimpleLockLock ( fQueueLock );
while ( !queue_empty ( &fResendTaskList ) )
{
SCSIParallelTaskIdentifier parallelTask;
SCSIParallelTask * task = NULL;
parallelTask = ( SCSIParallelTaskIdentifier ) queue_first ( &fResendTaskList );
task = ( SCSIParallelTask * ) parallelTask ;
queue_remove ( &fResendTaskList, task, SCSIParallelTask *, fResendTaskChain );
IOSimpleLockUnlock ( fQueueLock );
if ( ExecuteParallelTask ( parallelTask ) != kSCSIServiceResponse_Request_In_Process )
{
SCSITaskIdentifier nextRequest = NULL;
RemoveFromOutstandingTaskList ( parallelTask );
nextRequest = GetSCSITaskIdentifier ( parallelTask );
FreeSCSIParallelTask ( parallelTask );
CommandCompleted ( nextRequest, kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE, kSCSITaskStatus_No_Status );
IOSimpleLockLock ( fQueueLock );
continue;
}
else
{
IOSimpleLockLock ( fQueueLock );
break;
}
}
IOSimpleLockUnlock ( fQueueLock );
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( completionStatus == kSCSITaskStatus_TASK_SET_FULL ) &&
( retryCount >= kMaxTaskRetryCount ) )
{
CommandCompleted ( clientRequest, kSCSIServiceResponse_TASK_COMPLETE, kSCSITaskStatus_BUSY );
}
else
{
CommandCompleted ( clientRequest, serviceResponse, completionStatus );
}
}
#if 0
#pragma mark -
#pragma mark SCSI Protocol Service Feature routines
#pragma mark -
#endif
bool
IOSCSIParallelInterfaceDevice::IsProtocolServiceSupported (
SCSIProtocolFeature feature,
void * value )
{
bool isSupported = false;
require ( ( isInactive ( ) == false ), ErrorExit );
require_nonzero ( fController, ErrorExit );
switch ( feature )
{
case kSCSIProtocolFeature_GetMaximumLogicalUnitNumber:
{
isSupported = true;
*( UInt32 * ) value = fController->ReportHBAHighestLogicalUnitNumber ( );
}
break;
case kSCSIProtocolFeature_SubmitDefaultInquiryData:
{
isSupported = true;
}
break;
case kSCSIProtocolFeature_ProtocolAlwaysReportsAutosenseData:
{
isSupported = fController->DoesHBAPerformAutoSense ( );
}
break;
case kSCSIProtocolFeature_HierarchicalLogicalUnits:
{
OSBoolean * obj = NULL;
obj = OSDynamicCast ( OSBoolean, fController->getProperty ( kIOHierarchicalLogicalUnitSupportKey ) );
if ( ( obj != NULL ) && ( obj->isTrue ( ) ) )
{
isSupported = true;
}
}
break;
case kSCSIProtocolFeature_MultiPathing:
{
isSupported = fMultiPathSupport;
}
break;
default:
{
}
break;
}
ErrorExit:
return isSupported;
}
bool
IOSCSIParallelInterfaceDevice::HandleProtocolServiceFeature (
SCSIProtocolFeature feature,
void * serviceValue )
{
bool wasHandled = false;
switch ( feature )
{
case kSCSIProtocolFeature_SubmitDefaultInquiryData:
{
DetermineParallelFeatures ( ( UInt8 * ) serviceValue );
wasHandled = true;
registerService ( );
}
break;
default:
{
break;
}
}
return wasHandled;
}
#if 0
#pragma mark -
#pragma mark SCSI Task Management Functions
#pragma mark -
#endif
SCSIServiceResponse
IOSCSIParallelInterfaceDevice::AbortSCSICommand (
SCSITaskIdentifier request )
{
return kSCSIServiceResponse_FUNCTION_REJECTED;
}
SCSIServiceResponse
IOSCSIParallelInterfaceDevice::HandleAbortTask (
UInt8 theLogicalUnit,
SCSITaggedTaskIdentifier theTag )
{
return fController->AbortTaskRequest ( fTargetIdentifier, theLogicalUnit, theTag );
}
SCSIServiceResponse
IOSCSIParallelInterfaceDevice::HandleAbortTaskSet (
UInt8 theLogicalUnit )
{
return fController->AbortTaskSetRequest ( fTargetIdentifier, theLogicalUnit );
}
SCSIServiceResponse
IOSCSIParallelInterfaceDevice::HandleClearACA (
UInt8 theLogicalUnit )
{
return fController->ClearACARequest ( fTargetIdentifier, theLogicalUnit );
}
SCSIServiceResponse
IOSCSIParallelInterfaceDevice::HandleClearTaskSet (
UInt8 theLogicalUnit )
{
return fController->ClearTaskSetRequest ( fTargetIdentifier, theLogicalUnit );
}
SCSIServiceResponse
IOSCSIParallelInterfaceDevice::HandleLogicalUnitReset (
UInt8 theLogicalUnit )
{
return fController->LogicalUnitResetRequest ( fTargetIdentifier, theLogicalUnit );
}
SCSIServiceResponse
IOSCSIParallelInterfaceDevice::HandleTargetReset ( void )
{
return fController->TargetResetRequest ( fTargetIdentifier );
}
#if 0
#pragma mark -
#pragma mark Controller Object Accessors
#pragma mark -
#endif
SCSIServiceResponse
IOSCSIParallelInterfaceDevice::ExecuteParallelTask (
SCSIParallelTaskIdentifier parallelRequest )
{
return fController->ExecuteParallelTask ( parallelRequest );
}
SCSIParallelTaskIdentifier
IOSCSIParallelInterfaceDevice::GetSCSIParallelTask ( bool blockForCommand )
{
return fController->GetSCSIParallelTask ( blockForCommand );
}
void
IOSCSIParallelInterfaceDevice::FreeSCSIParallelTask (
SCSIParallelTaskIdentifier returnTask )
{
return fController->FreeSCSIParallelTask ( returnTask );
}
bool
IOSCSIParallelInterfaceDevice::DoesHBASupportSCSIParallelFeature (
SCSIParallelFeature theFeature )
{
return fController->DoesHBASupportSCSIParallelFeature ( theFeature );
}
#if 0
#pragma mark -
#pragma mark SCSI Parallel Task Object Accessors
#pragma mark -
#endif
bool
IOSCSIParallelInterfaceDevice::AddToOutstandingTaskList (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return false;
}
IOSimpleLockLock ( fQueueLock );
queue_enter ( &fOutstandingTaskList, task, SCSIParallelTask *, fCommandChain );
IOSimpleLockUnlock ( fQueueLock );
return true;
}
void
IOSCSIParallelInterfaceDevice::RemoveFromOutstandingTaskList (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
require_nonzero ( ( task->fCommandChain.next ), Exit );
require_nonzero ( ( task->fCommandChain.prev ), Exit );
IOSimpleLockLock ( fQueueLock );
require ( ( queue_empty ( &fOutstandingTaskList ) == false ), ExitLocked );
queue_remove ( &fOutstandingTaskList, task, SCSIParallelTask *, fCommandChain );
ExitLocked:
IOSimpleLockUnlock ( fQueueLock );
Exit:
return;
}
bool
IOSCSIParallelInterfaceDevice::AddToResendTaskList (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
thread_t thread = THREAD_NULL;
if ( task == NULL )
{
return false;
}
IOSimpleLockLock ( fQueueLock );
task->fTaskRetryCount++;
queue_enter ( &fResendTaskList, task, SCSIParallelTask *, fResendTaskChain );
if ( fResendThreadScheduled == false )
{
fResendThreadScheduled = true;
IOSimpleLockUnlock ( fQueueLock );
retain ( );
kernel_thread_start (
OSMemberFunctionCast (
thread_continue_t,
this,
&IOSCSIParallelInterfaceDevice::SendFromResendTaskList ),
this,
&thread );
}
else
{
IOSimpleLockUnlock ( fQueueLock );
}
return true;
}
void
IOSCSIParallelInterfaceDevice::SendFromResendTaskList ( void )
{
thread_t thread = THREAD_NULL;
SCSIParallelTaskIdentifier parallelTask = NULL;
SCSIParallelTask * task = NULL;
if ( fAllowResends == true )
{
IOSleep ( 10000 );
}
IOSimpleLockLock ( fQueueLock );
while ( !queue_empty ( &fResendTaskList ) )
{
parallelTask = ( SCSIParallelTaskIdentifier ) queue_first ( &fResendTaskList );
task = ( SCSIParallelTask * ) parallelTask;
queue_remove ( &fResendTaskList, task, SCSIParallelTask *, fResendTaskChain );
IOSimpleLockUnlock ( fQueueLock );
if ( fAllowResends == true )
{
if ( ExecuteParallelTask ( parallelTask ) == kSCSIServiceResponse_Request_In_Process )
{
IOSimpleLockLock ( fQueueLock );
break;
}
}
SCSITaskIdentifier nextRequest = NULL;
RemoveFromOutstandingTaskList ( parallelTask );
nextRequest = GetSCSITaskIdentifier ( parallelTask );
FreeSCSIParallelTask ( parallelTask );
CommandCompleted ( nextRequest, kSCSIServiceResponse_TASK_COMPLETE, kSCSITaskStatus_BUSY );
IOSimpleLockLock ( fQueueLock );
continue;
}
fResendThreadScheduled = false;
IOSimpleLockUnlock ( fQueueLock );
release ( );
thread = current_thread ( );
thread_deallocate ( thread );
thread_terminate ( thread );
}
void
IOSCSIParallelInterfaceDevice::RemoveFromResendTaskList (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
require_nonzero ( ( task->fResendTaskChain.next ), Exit );
require_nonzero ( ( task->fResendTaskChain.prev ), Exit );
IOSimpleLockLock ( fQueueLock );
require ( ( queue_empty ( &fResendTaskList ) == false ), ExitLocked );
queue_remove ( &fResendTaskList, task, SCSIParallelTask *, fResendTaskChain );
ExitLocked:
IOSimpleLockUnlock ( fQueueLock );
Exit:
return;
}
bool
IOSCSIParallelInterfaceDevice::SetSCSITaskIdentifier (
SCSIParallelTaskIdentifier parallelTask,
SCSITaskIdentifier scsiRequest )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return false;
}
return task->SetSCSITaskIdentifier ( scsiRequest );
}
SCSITaskIdentifier
IOSCSIParallelInterfaceDevice::GetSCSITaskIdentifier (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return NULL;
}
return task->GetSCSITaskIdentifier ( );
}
bool
IOSCSIParallelInterfaceDevice::SetDevice (
SCSIParallelTaskIdentifier parallelTask,
IOSCSIParallelInterfaceDevice * device )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return false;
}
return task->SetDevice ( device );
}
bool
IOSCSIParallelInterfaceDevice::SetTargetIdentifier (
SCSIParallelTaskIdentifier parallelTask,
SCSITargetIdentifier theTargetID )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return false;
}
return task->SetTargetIdentifier ( theTargetID );
}
SCSITargetIdentifier
IOSCSIParallelInterfaceDevice::GetTargetIdentifier (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return NULL;
}
return task->GetTargetIdentifier ( );
}
IOReturn
IOSCSIParallelInterfaceDevice::SetDMABuffer (
SCSIParallelTaskIdentifier parallelTask,
IOMemoryDescriptor * buffer )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return NULL;
}
return task->SetBuffer ( buffer );
}
SCSILogicalUnitNumber
IOSCSIParallelInterfaceDevice::GetLogicalUnitNumber (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return 0;
}
return task->GetLogicalUnitNumber ( );
}
SCSITaggedTaskIdentifier
IOSCSIParallelInterfaceDevice::GetTaggedTaskIdentifier (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return kSCSIUntaggedTaskIdentifier;
}
return task->GetTaggedTaskIdentifier ( );
}
SCSITaskAttribute
IOSCSIParallelInterfaceDevice::GetTaskAttribute (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return kSCSITask_SIMPLE;
}
return task->GetTaskAttribute ( );
}
UInt8
IOSCSIParallelInterfaceDevice::GetCommandDescriptorBlockSize (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return 0;
}
return task->GetCommandDescriptorBlockSize ( );
}
bool
IOSCSIParallelInterfaceDevice::GetCommandDescriptorBlock (
SCSIParallelTaskIdentifier parallelTask,
SCSICommandDescriptorBlock * cdbData )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return false;
}
return task->GetCommandDescriptorBlock ( cdbData );
}
UInt8
IOSCSIParallelInterfaceDevice::GetDataTransferDirection (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return kSCSIDataTransfer_NoDataTransfer;
}
return task->GetDataTransferDirection ( );
}
UInt64
IOSCSIParallelInterfaceDevice::GetRequestedDataTransferCount (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return 0;
}
return task->GetRequestedDataTransferCount ( );
}
UInt64
IOSCSIParallelInterfaceDevice::GetRealizedDataTransferCount (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return 0;
}
return task->GetRealizedDataTransferCount ( );
}
bool
IOSCSIParallelInterfaceDevice::SetRealizedDataTransferCount (
SCSIParallelTaskIdentifier parallelTask,
UInt64 realizedTransferCountInBytes )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return false;
}
return task->SetRealizedDataTransferCount ( realizedTransferCountInBytes );
}
void
IOSCSIParallelInterfaceDevice::IncrementRealizedDataTransferCount (
SCSIParallelTaskIdentifier parallelTask,
UInt64 realizedTransferCountInBytes )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return;
}
return task->IncrementRealizedDataTransferCount ( realizedTransferCountInBytes );
}
IOMemoryDescriptor *
IOSCSIParallelInterfaceDevice::GetDataBuffer (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return NULL;
}
return task->GetDataBuffer ( );
}
UInt64
IOSCSIParallelInterfaceDevice::GetDataBufferOffset (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return 0;
}
return task->GetDataBufferOffset ( );
}
UInt32
IOSCSIParallelInterfaceDevice::GetTimeoutDuration (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return 0;
}
return task->GetTimeoutDuration ( );
}
void
IOSCSIParallelInterfaceDevice::SetSCSIParallelFeatureNegotiation (
SCSIParallelTaskIdentifier parallelTask,
SCSIParallelFeature requestedFeature,
SCSIParallelFeatureRequest newRequest )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return;
}
return task->SetSCSIParallelFeatureNegotiation ( requestedFeature, newRequest );
}
SCSIParallelFeatureRequest
IOSCSIParallelInterfaceDevice::GetSCSIParallelFeatureNegotiation (
SCSIParallelTaskIdentifier parallelTask,
SCSIParallelFeature requestedFeature )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return kSCSIParallelFeature_NoNegotiation;
}
return task->GetSCSIParallelFeatureNegotiation ( requestedFeature );
}
SCSIParallelFeatureResult
IOSCSIParallelInterfaceDevice::GetSCSIParallelFeatureNegotiationResult (
SCSIParallelTaskIdentifier parallelTask,
SCSIParallelFeature requestedFeature )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return kSCSIParallelFeature_NegotitiationUnchanged;
}
return task->GetSCSIParallelFeatureNegotiationResult ( requestedFeature );
}
UInt64
IOSCSIParallelInterfaceDevice::GetControllerTaskIdentifier (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return 0;
}
return task->GetControllerTaskIdentifier ( );
}
UInt32
IOSCSIParallelInterfaceDevice::GetHBADataSize (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return 0;
}
return task->GetHBADataSize ( );
}
void *
IOSCSIParallelInterfaceDevice::GetHBADataPointer (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return NULL;
}
return task->GetHBADataPointer ( );
}
IOMemoryDescriptor *
IOSCSIParallelInterfaceDevice::GetHBADataDescriptor (
SCSIParallelTaskIdentifier parallelTask )
{
SCSIParallelTask * task = ( SCSIParallelTask * ) parallelTask;
if ( task == NULL )
{
return NULL;
}
return task->GetHBADataDescriptor ( );
}