IOSCSIProtocolServices.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOCommandGate.h>
#include <IOKit/scsi-commands/IOSCSIProtocolServices.h>
#if ( SCSI_PROTOCOL_SERVICES_DEBUGGING_LEVEL >= 1 )
#define PANIC_NOW(x) IOPanic x
#else
#define PANIC_NOW(x)
#endif
#if ( SCSI_PROTOCOL_SERVICES_DEBUGGING_LEVEL >= 2 )
#define ERROR_LOG(x) IOLog x
#else
#define ERROR_LOG(x)
#endif
#if ( SCSI_PROTOCOL_SERVICES_DEBUGGING_LEVEL >= 3 )
#define STATUS_LOG(x) IOLog x
#else
#define STATUS_LOG(x)
#endif
#define super IOSCSIProtocolInterface
OSDefineMetaClass ( IOSCSIProtocolServices, IOSCSIProtocolInterface );
OSDefineAbstractStructors ( IOSCSIProtocolServices, IOSCSIProtocolInterface );
OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 1 );
OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 2 );
OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 3 );
OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 4 );
OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 5 );
OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 6 );
OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 7 );
OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 8 );
OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 9 );
OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 10 );
OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 11 );
OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 12 );
OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 13 );
OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 14 );
OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 15 );
OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 16 );
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 }
};
UInt32
IOSCSIProtocolServices::GetInitialPowerState ( void )
{
STATUS_LOG ( ( "%s%s::%s called%s\n", "\033[33m", getName ( ), __FUNCTION__, "\033[0m" ) );
return fCurrentPowerState;
}
#pragma mark -
#pragma mark Public Methods
bool
IOSCSIProtocolServices::init ( OSDictionary * propTable )
{
if ( super::init ( propTable ) == false )
{
return false;
}
return true;
}
bool
IOSCSIProtocolServices::start ( IOService * provider )
{
if ( !super::start ( provider ) )
{
return false;
}
fAllowServiceRequests = true;
fSCSITaskQueueHead = NULL;
fQueueLock = IOSimpleLockAlloc ( );
if ( fQueueLock == NULL )
{
PANIC_NOW ( ( "IOSCSIProtocolServices::start Allocate fQueueLock failed." ) );
}
return true;
}
void
IOSCSIProtocolServices::stop ( IOService * provider )
{
if ( fQueueLock != NULL )
{
IOSimpleLockFree ( fQueueLock );
fQueueLock = NULL;
}
super::stop ( provider );
}
#pragma mark -
#pragma mark Power Management Methods
void
IOSCSIProtocolServices::InitializePowerManagement ( IOService * provider )
{
STATUS_LOG ( ( "%s%s::%s called%s\n", "\033[33m", getName ( ), __FUNCTION__, "\033[0m" ) );
fCurrentPowerState = kSCSIProtocolLayerPowerStateOn;
super::InitializePowerManagement ( provider );
registerPowerDriver ( this, sPowerStates, kSCSIProtocolLayerNumDefaultStates );
changePowerStateTo ( kSCSIProtocolLayerPowerStateOn );
}
void
IOSCSIProtocolServices::HandlePowerChange ( void )
{
IOReturn status;
STATUS_LOG ( ( "%s%s::%s called%s\n", "\033[33m",
getName ( ), __FUNCTION__, "\033[0m" ) );
STATUS_LOG ( ( "fProposedPowerState = %ld, fCurrentPowerState = %ld\n",
fProposedPowerState, fCurrentPowerState ) );
while ( fProposedPowerState != fCurrentPowerState )
{
STATUS_LOG ( ( "Looping because power states differ\n" ) );
switch ( fProposedPowerState )
{
case kSCSIProtocolLayerPowerStateOff:
status = HandlePowerOff ( );
STATUS_LOG ( ( "HandlePowerOff returned status = %d\n", status ) );
if ( status == kIOReturnSuccess )
{
fCurrentPowerState = kSCSIProtocolLayerPowerStateOff;
}
break;
case kSCSIProtocolLayerPowerStateOn:
status = HandlePowerOn ( );
STATUS_LOG ( ( "HandlePowerOn returned status = %d\n", status ) );
if ( status == kIOReturnSuccess )
{
fCurrentPowerState = kSCSIProtocolLayerPowerStateOn;
}
break;
default:
PANIC_NOW ( ( "HandlePowerChange: bad proposed power state\n" ) );
break;
}
}
}
void
IOSCSIProtocolServices::HandleCheckPowerState ( void )
{
super::HandleCheckPowerState ( kSCSIProtocolLayerPowerStateOn );
}
void
IOSCSIProtocolServices::TicklePowerManager ( void )
{
super::TicklePowerManager ( kSCSIProtocolLayerPowerStateOn );
}
IOReturn
IOSCSIProtocolServices::HandlePowerOff ( void )
{
return kIOReturnSuccess;
}
IOReturn
IOSCSIProtocolServices::HandlePowerOn ( void )
{
return kIOReturnSuccess;
}
#pragma mark -
#pragma mark Status Notification Senders
void
IOSCSIProtocolServices::SendNotification_DeviceRemoved ( void )
{
STATUS_LOG ( ( "%s: SendNotification_DeviceRemoved called\n", getName ( ) ) );
fAllowServiceRequests = false;
STATUS_LOG ( ( "%s: SendNotification_DeviceRemoved Reject queued tasks\n", getName ( ) ) );
RejectSCSITasksCurrentlyQueued ( );
}
void
IOSCSIProtocolServices::SendNotification_VerifyDeviceState( void )
{
STATUS_LOG ( ("%s: SendNotification_VerifyDeviceState called\n", getName ( ) ) );
messageClients ( kSCSIProtocolNotification_VerifyDeviceState, 0 , 0 );
}
#pragma mark -
#pragma mark SCSI Task Field Accessors
SCSITaskAttribute
IOSCSIProtocolServices::GetTaskAttribute ( SCSITaskIdentifier request )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast ( SCSITask, request );
return scsiRequest->GetTaskAttribute ( );
}
bool
IOSCSIProtocolServices::SetTaskState ( SCSITaskIdentifier request,
SCSITaskState newTaskState )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast ( SCSITask, request );
return scsiRequest->SetTaskState ( newTaskState );
}
SCSITaskState
IOSCSIProtocolServices::GetTaskState ( SCSITaskIdentifier request )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast ( SCSITask, request );
return scsiRequest->GetTaskState ( );
}
UInt8
IOSCSIProtocolServices::GetLogicalUnitNumber( SCSITaskIdentifier request )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast( SCSITask, request );
return scsiRequest->GetLogicalUnitNumber();
}
UInt8
IOSCSIProtocolServices::GetCommandDescriptorBlockSize ( SCSITaskIdentifier request )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast ( SCSITask, request );
if ( scsiRequest->GetTaskExecutionMode() == kSCSITaskMode_CommandExecution )
{
return scsiRequest->GetCommandDescriptorBlockSize ( );
}
else
{
return scsiRequest->GetAutosenseCommandDescriptorBlockSize ( );
}
}
bool
IOSCSIProtocolServices::GetCommandDescriptorBlock (
SCSITaskIdentifier request,
SCSICommandDescriptorBlock * cdbData )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast ( SCSITask, request );
if ( scsiRequest->GetTaskExecutionMode ( ) == kSCSITaskMode_CommandExecution )
{
return scsiRequest->GetCommandDescriptorBlock ( cdbData );
}
else
{
return scsiRequest->GetAutosenseCommandDescriptorBlock ( cdbData );
}
}
UInt8
IOSCSIProtocolServices::GetDataTransferDirection ( SCSITaskIdentifier request )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast ( SCSITask, request );
if ( scsiRequest->GetTaskExecutionMode ( ) == kSCSITaskMode_CommandExecution )
{
return scsiRequest->GetDataTransferDirection( );
}
else
{
return scsiRequest->GetAutosenseDataTransferDirection ( );
}
}
UInt64
IOSCSIProtocolServices::GetRequestedDataTransferCount ( SCSITaskIdentifier request )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast ( SCSITask, request );
if ( scsiRequest->GetTaskExecutionMode ( ) == kSCSITaskMode_CommandExecution )
{
return scsiRequest->GetRequestedDataTransferCount ( );
}
else
{
return scsiRequest->GetAutosenseRequestedDataTransferCount ( );
}
}
bool
IOSCSIProtocolServices::SetRealizedDataTransferCount ( SCSITaskIdentifier request,
UInt64 newRealizedDataCount )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast ( SCSITask, request );
if ( scsiRequest->GetTaskExecutionMode ( ) == kSCSITaskMode_CommandExecution )
{
return scsiRequest->SetRealizedDataTransferCount ( newRealizedDataCount );
}
else
{
return scsiRequest->SetAutosenseRealizedDataCount ( newRealizedDataCount );
}
}
UInt64
IOSCSIProtocolServices::GetRealizedDataTransferCount ( SCSITaskIdentifier request )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast ( SCSITask, request );
if ( scsiRequest->GetTaskExecutionMode ( ) == kSCSITaskMode_CommandExecution )
{
return scsiRequest->GetRealizedDataTransferCount ( );
}
else
{
return scsiRequest->GetAutosenseRealizedDataCount ( );
}
}
IOMemoryDescriptor *
IOSCSIProtocolServices::GetDataBuffer ( SCSITaskIdentifier request )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast ( SCSITask, request );
if ( scsiRequest->GetTaskExecutionMode ( ) == kSCSITaskMode_CommandExecution )
{
return scsiRequest->GetDataBuffer ( );
}
else
{
return scsiRequest->GetAutosenseDataBuffer ( );
}
}
UInt64
IOSCSIProtocolServices::GetDataBufferOffset ( SCSITaskIdentifier request )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast ( SCSITask, request );
return scsiRequest->GetDataBufferOffset ( );
}
UInt32
IOSCSIProtocolServices::GetTimeoutDuration ( SCSITaskIdentifier request )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast ( SCSITask, request );
return scsiRequest->GetTimeoutDuration ( );
}
bool
IOSCSIProtocolServices::SetAutoSenseData ( SCSITaskIdentifier request,
SCSI_Sense_Data * senseData )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast ( SCSITask, request );
return scsiRequest->SetAutoSenseData ( senseData );
}
bool
IOSCSIProtocolServices::SetProtocolLayerReference ( SCSITaskIdentifier request,
void * newReferenceValue )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast ( SCSITask, request );
return scsiRequest->SetProtocolLayerReference ( newReferenceValue );
}
void *
IOSCSIProtocolServices::GetProtocolLayerReference ( SCSITaskIdentifier request )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast ( SCSITask, request );
return scsiRequest->GetProtocolLayerReference ( );
}
bool
IOSCSIProtocolServices::SetTaskExecutionMode ( SCSITaskIdentifier request, SCSITaskMode newTaskMode )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast ( SCSITask, request );
return scsiRequest->SetTaskExecutionMode ( newTaskMode );
}
SCSITaskMode
IOSCSIProtocolServices::GetTaskExecutionMode ( SCSITaskIdentifier request )
{
SCSITask * scsiRequest;
scsiRequest = OSDynamicCast ( SCSITask, request );
return scsiRequest->GetTaskExecutionMode ( );
}
#pragma mark -
#pragma mark SCSI Task Queue Management
void
IOSCSIProtocolServices::AddSCSITaskToQueue ( SCSITaskIdentifier request )
{
SCSITask * scsiRequest;
STATUS_LOG ( ( "%s: AddSCSITaskToQueue called.\n", getName ( ) ) );
IOSimpleLockLock ( fQueueLock );
scsiRequest = OSDynamicCast ( SCSITask, request );
scsiRequest->EnqueueFollowingSCSITask ( NULL );
if ( fSCSITaskQueueHead == NULL )
{
fSCSITaskQueueHead = scsiRequest;
}
else
{
SCSITask * currentElement;
currentElement = fSCSITaskQueueHead;
while ( currentElement->GetFollowingSCSITask ( ) != NULL )
{
currentElement = currentElement->GetFollowingSCSITask ( );
}
currentElement->EnqueueFollowingSCSITask ( scsiRequest );
}
IOSimpleLockUnlock ( fQueueLock );
}
void
IOSCSIProtocolServices::AddSCSITaskToHeadOfQueue ( SCSITask * request )
{
IOSimpleLockLock ( fQueueLock );
request->EnqueueFollowingSCSITask ( fSCSITaskQueueHead );
fSCSITaskQueueHead = request;
IOSimpleLockUnlock ( fQueueLock );
}
SCSITask *
IOSCSIProtocolServices::RetrieveNextSCSITaskFromQueue ( void )
{
SCSITask * selectedTask;
IOSimpleLockLock ( fQueueLock );
if ( fSCSITaskQueueHead == NULL )
{
selectedTask = NULL;
}
else
{
selectedTask = fSCSITaskQueueHead;
fSCSITaskQueueHead = selectedTask->GetFollowingSCSITask ( );
selectedTask->EnqueueFollowingSCSITask ( NULL );
}
IOSimpleLockUnlock ( fQueueLock );
return selectedTask;
}
bool
IOSCSIProtocolServices::AbortSCSITaskFromQueue ( SCSITask *request )
{
return false;
}
void
IOSCSIProtocolServices::SendSCSITasksFromQueue ( void )
{
bool cmdAccepted = false;
SCSITask * nextVictim;
do
{
SCSIServiceResponse serviceResponse;
SCSITaskStatus taskStatus;
nextVictim = RetrieveNextSCSITaskFromQueue ( );
if ( nextVictim != NULL )
{
cmdAccepted = SendSCSICommand ( nextVictim, &serviceResponse, &taskStatus );
if ( cmdAccepted == false )
{
AddSCSITaskToHeadOfQueue ( nextVictim );
}
else if ( serviceResponse != kSCSIServiceResponse_Request_In_Process )
{
nextVictim->SetServiceResponse ( serviceResponse );
nextVictim->SetTaskStatus ( taskStatus );
nextVictim->SetTaskState ( kSCSITaskState_ENDED );
}
}
} while ( ( cmdAccepted == true ) && ( nextVictim != NULL ) );
}
void
IOSCSIProtocolServices::RejectSCSITasksCurrentlyQueued ( void )
{
SCSITask * nextVictim;
STATUS_LOG ( ( "%s: RejectSCSITasksCurrentlyQueued called.\n", getName ( ) ) );
do
{
nextVictim = RetrieveNextSCSITaskFromQueue ( );
if ( nextVictim != NULL )
{
RejectTask ( nextVictim );
}
} while ( nextVictim != NULL );
}
void
IOSCSIProtocolServices::ProcessCompletedTask ( SCSITaskIdentifier request,
SCSIServiceResponse serviceResponse,
SCSITaskStatus taskStatus )
{
SCSITask * scsiRequest;
STATUS_LOG ( ( "%s: ProcessCompletedTask called.\n", getName ( ) ) );
scsiRequest = OSDynamicCast ( SCSITask, request );
if ( scsiRequest->GetTaskExecutionMode ( ) == kSCSITaskMode_CommandExecution )
{
scsiRequest->SetServiceResponse ( serviceResponse );
if ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE )
{
scsiRequest->SetTaskStatus ( taskStatus );
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( taskStatus == kSCSITaskStatus_CHECK_CONDITION ) )
{
if ( scsiRequest->IsAutosenseRequested ( ) == true )
{
if ( scsiRequest->GetAutoSenseData ( NULL ) == false )
{
scsiRequest->SetTaskExecutionMode ( kSCSITaskMode_Autosense );
AddSCSITaskToHeadOfQueue ( scsiRequest );
return;
}
}
}
}
}
else
{
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( taskStatus == kSCSITaskStatus_GOOD ) )
{
scsiRequest->SetAutosenseIsValid ( true );
}
}
scsiRequest->SetTaskState ( kSCSITaskState_ENDED );
release ( );
scsiRequest->TaskCompletedNotification ( );
}
void
IOSCSIProtocolServices::RejectTask ( SCSITaskIdentifier request )
{
SCSITask * scsiRequest;
STATUS_LOG ( ( "%s: RejectTask called.\n", getName ( ) ) );
scsiRequest = OSDynamicCast ( SCSITask, request );
scsiRequest->SetTaskState ( kSCSITaskState_ENDED );
scsiRequest->SetServiceResponse ( kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE );
scsiRequest->SetTaskStatus ( kSCSITaskStatus_No_Status );
release ( );
scsiRequest->TaskCompletedNotification ( );
}
#pragma mark -
#pragma mark Provided Services to the SCSI Protocol Layer Subclasses
void
IOSCSIProtocolServices::CommandCompleted ( SCSITaskIdentifier request,
SCSIServiceResponse serviceResponse,
SCSITaskStatus taskStatus )
{
STATUS_LOG ( ( "%s: CommandCompleted called.\n", getName ( ) ) );
if ( fAllowServiceRequests == false )
{
RejectTask ( request );
return;
}
ProcessCompletedTask ( request, serviceResponse, taskStatus );
SendSCSITasksFromQueue ( );
}
#pragma mark -
#pragma mark Provided Services to the SCSI Application Layer
void
IOSCSIProtocolServices::ExecuteCommand ( SCSITaskIdentifier request )
{
STATUS_LOG ( ( "%s::%s called.\n", getName ( ), __FUNCTION__ ) );
retain ( );
if ( fAllowServiceRequests == false )
{
RejectTask ( request );
return;
}
SetTaskState ( request, kSCSITaskState_ENABLED );
SetTaskExecutionMode ( request, kSCSITaskMode_CommandExecution );
AddSCSITaskToQueue ( request );
SendSCSITasksFromQueue ( );
}
SCSIServiceResponse
IOSCSIProtocolServices::AbortCommand ( SCSITaskIdentifier request )
{
return kSCSIServiceResponse_FUNCTION_REJECTED;