IOSCSIParallelInterfaceProtocolTransport.cpp [plain text]
#include <IOKit/IOMemoryDescriptor.h>
#include <IOKit/IOCommandGate.h>
#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOSyncer.h>
#include <IOKit/IORegistryEntry.h>
#include <IOKit/scsi/SCSICmds_INQUIRY_Definitions.h>
#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
#include "IOSCSIDevice.h"
#include "IOSCSIParallelInterfaceProtocolTransport.h"
#if ( SCSI_PARALLEL_DEBUGGING_LEVEL >= 1 )
#define PANIC_NOW(x) IOPanic x
#else
#define PANIC_NOW(x)
#endif
#if ( SCSI_PARALLEL_DEBUGGING_LEVEL >= 2 )
#define ERROR_LOG(x) IOLog x
#else
#define ERROR_LOG(x)
#endif
#if ( SCSI_PARALLEL_DEBUGGING_LEVEL >= 3 )
#define STATUS_LOG(x) IOLog x
#else
#define STATUS_LOG(x)
#endif
enum
{
kNoTimeout = 0, k1SecondTimeout = 1000, k10SecondTimeout = 10 * k1SecondTimeout,
k30SecondTimeout = 3 * k10SecondTimeout,
k45SecondTimeout = 45000,
k100Milliseconds = 100
};
enum
{
kSCSICommandPoolSize = 1
};
enum
{
kMaxSenseDataSize = 255
};
#define super IOSCSIProtocolServices
OSDefineMetaClassAndStructors ( IOSCSIParallelInterfaceProtocolTransport, IOSCSIProtocolServices );
#pragma mark -
#pragma mark Public Methods
bool
IOSCSIParallelInterfaceProtocolTransport::start ( IOService * provider )
{
IOReturn theErr = kIOReturnSuccess;
IOWorkLoop * workLoop = NULL;
STATUS_LOG ( ( "IOSCSIParallelInterfaceProtocolTransport::start called\n" ) );
fSCSIDevice = NULL;
if ( super::start ( provider ) == false )
return false;
fSCSIDevice = OSDynamicCast ( IOSCSIDevice, provider );
if ( fSCSIDevice == NULL )
{
ERROR_LOG ( ( "Error in dynamic cast\n" ) );
return false;
}
if ( fSCSIDevice->open ( this ) == false )
{
ERROR_LOG ( ( "device wouldn't open\n" ) );
return false;
}
fCommandGate = IOCommandGate::commandGate ( this );
if ( fCommandGate == NULL )
{
ERROR_LOG ( ( "fCommandGate == NULL.\n" ) );
goto CLOSE_DEVICE_ERROR;
}
workLoop = getWorkLoop ( );
if ( workLoop == NULL )
{
ERROR_LOG ( ( "workLoop == NULL.\n" ) );
goto RELEASE_COMMAND_GATE_ERROR;
}
theErr = workLoop->addEventSource ( fCommandGate );
if ( theErr != kIOReturnSuccess )
{
ERROR_LOG ( ( "Error = %d adding event source.\n", theErr ) );
goto RELEASE_COMMAND_GATE_ERROR;
}
fCommandPool = IOCommandPool::withWorkLoop ( workLoop );
if ( fCommandPool == NULL )
{
ERROR_LOG ( ( "fCommandPool == NULL.\n" ) );
goto REMOVE_EVENT_SOURCE_ERROR;
}
AllocateSCSICommandObjects ( );
if ( InspectDevice ( fSCSIDevice ) == false )
{
ERROR_LOG ( ( "InspectDevice returned false.\n" ) );
goto DEALLOCATE_COMMANDS_ERROR;
}
InitializePowerManagement ( provider );
STATUS_LOG ( ( "IOSCSIParallelInterfaceProtocolTransport::start complete\n" ) );
registerService ( );
return true;
DEALLOCATE_COMMANDS_ERROR:
DeallocateSCSICommandObjects ( );
if ( fCommandPool != NULL )
fCommandPool->release ( );
REMOVE_EVENT_SOURCE_ERROR:
if ( workLoop != NULL )
workLoop->removeEventSource ( fCommandGate );
RELEASE_COMMAND_GATE_ERROR:
if ( fCommandGate != NULL )
fCommandGate->release ( );
CLOSE_DEVICE_ERROR:
fSCSIDevice->close ( this );
return false;
}
void
IOSCSIParallelInterfaceProtocolTransport::stop ( IOService * provider )
{
STATUS_LOG ( ( "IOSCSIParallelInterfaceProtocolTransport::stop called\n" ) );
DeallocateSCSICommandObjects ( );
super::stop ( provider );
}
#pragma mark -
#pragma mark Protected Methods
bool
IOSCSIParallelInterfaceProtocolTransport::SendSCSICommand (
SCSITaskIdentifier request,
SCSIServiceResponse * serviceResponse,
SCSITaskStatus * taskStatus )
{
SCSICommandDescriptorBlock cdb;
UInt16 commandLength = 0;
IOSCSICommand * cmd = NULL;
SCSIClientData * clientData = NULL;
SCSICDBInfo cdbInfo;
UInt8 requestType;
bzero ( &cdbInfo, sizeof ( cdbInfo ) );
cmd = GetSCSICommandObject ( );
clientData = ( SCSIClientData * ) cmd->getClientData ( );
*serviceResponse = kSCSIServiceResponse_Request_In_Process;
STATUS_LOG ( ( "IOSCSIParallelInterfaceProtocolTransport::SendSCSICommand called\n" ) );
GetCommandDescriptorBlock ( request, &cdb );
commandLength = GetCommandDescriptorBlockSize ( request );
if ( commandLength == kSCSICDBSize_6Byte )
{
STATUS_LOG( ( "cdb = %02x:%02x:%02x:%02x:%02x:%02x\n", cdb[0], cdb[1],
cdb[2], cdb[3], cdb[4], cdb[5] ) );
}
else if ( commandLength == kSCSICDBSize_10Byte )
{
STATUS_LOG ( ( "cdb = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", cdb[0],
cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8],
cdb[9] ) );
}
else if ( commandLength == kSCSICDBSize_12Byte )
{
STATUS_LOG ( ( "cdb = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", cdb[0],
cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8],
cdb[9], cdb[10], cdb[11] ) );
}
else if ( commandLength == kSCSICDBSize_16Byte )
{
STATUS_LOG ( ( "cdb = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", cdb[0],
cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8],
cdb[9], cdb[10], cdb[11], cdb[12], cdb[13], cdb[14], cdb[15] ) );
}
for ( UInt32 index = 0; index < commandLength; index++ )
{
STATUS_LOG ( ( "copying the cdb\n" ) );
cdbInfo.cdb[index] = cdb[index];
}
STATUS_LOG ( ( "setting the cdb length\n" ) );
cdbInfo.cdbLength = commandLength;
STATUS_LOG ( ( "zeroing the scsi command\n" ) );
cmd->zeroCommand ( );
STATUS_LOG ( ( "Setting the data pointers\n" ) );
requestType = GetDataTransferDirection ( request );
if ( requestType == kSCSIDataTransfer_NoDataTransfer )
{
STATUS_LOG ( ( "Setting for no data transfer\n" ) );
cmd->setPointers ( 0, 0, false, false );
}
else if ( requestType == kSCSIDataTransfer_FromTargetToInitiator )
{
STATUS_LOG ( ( "Setting for DataTransferFromTargetToInitiator\n" ) );
cmd->setPointers ( GetDataBuffer ( request ),
GetRequestedDataTransferCount ( request ),
false,
false );
}
else
{
STATUS_LOG ( ( "Setting for DataTransferFromInitiatorToTarget\n" ) );
cmd->setPointers ( GetDataBuffer ( request ),
GetRequestedDataTransferCount ( request ),
true,
false );
}
STATUS_LOG ( ( "Setting the auto-sense pointers\n" ) );
cmd->setPointers ( clientData->senseBuffer,
clientData->senseBuffer->getLength ( ),
false,
true );
STATUS_LOG ( ( "Setting the command timeout\n" ) );
cmd->setTimeout ( GetTimeoutDuration ( request ) );
STATUS_LOG ( ( "Setting the cdb\n" ) );
cmd->setCDB ( &cdbInfo );
STATUS_LOG ( ( "Setting up the context\n" ) );
clientData->scsiTask = request;
clientData->cmd = cmd;
STATUS_LOG ( ( "Setting the callback proc\n" ) );
cmd->setCallback ( this, &sSCSICallbackProc, ( void * ) clientData );
STATUS_LOG ( ( "Executing the command\n" ) );
cmd->execute ( );
STATUS_LOG ( ( "Exiting...\n" ) );
return true;
}
void
IOSCSIParallelInterfaceProtocolTransport::SCSICallbackFunction (
IOSCSICommand * cmd,
SCSIClientData * clientData )
{
SCSIResults results;
SCSIServiceResponse serviceResponse;
SCSITaskStatus taskStatus;
STATUS_LOG ( ( "%s::%s entering\n", getName ( ), __FUNCTION__ ) );
cmd->getResults ( &results );
STATUS_LOG ( ( "Returning the scsi command to pool\n" ) );
ReturnSCSICommandObject ( cmd );
if ( results.returnCode == kIOReturnUnderrun )
results.returnCode = kIOReturnSuccess;
ERROR_LOG ( ( "Error code = 0x%08x\n", results.returnCode ) );
if ( results.returnCode != kIOReturnSuccess )
{
if ( results.scsiStatus == kSCSIStatusCheckCondition )
{
ERROR_LOG ( ( "%s::%s result = %d\n", getName ( ), __FUNCTION__, results.scsiStatus ) );
if ( results.requestSenseDone )
{
ERROR_LOG ( ( "Pulling out sense data\n" ) );
SetAutoSenseData ( clientData->scsiTask,
( SCSI_Sense_Data * ) clientData->senseBuffer->getBytesNoCopy ( ),
clientData->senseBuffer->getLength ( ) );
ERROR_LOG ( ( "Finished setting sense data\n" ) );
}
SetRealizedDataTransferCount ( clientData->scsiTask, results.bytesTransferred );
taskStatus = kSCSITaskStatus_CHECK_CONDITION;
serviceResponse = kSCSIServiceResponse_TASK_COMPLETE;
}
else
{
serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
taskStatus = kSCSITaskStatus_No_Status;
}
}
else
{
switch ( results.scsiStatus )
{
case kSCSIStatusGood:
STATUS_LOG ( ( "%s::%s result = noErr\n", getName ( ), __FUNCTION__ ) );
SetRealizedDataTransferCount ( clientData->scsiTask, results.bytesTransferred );
taskStatus = kSCSITaskStatus_GOOD;
serviceResponse = kSCSIServiceResponse_TASK_COMPLETE;
break;
case kSCSIStatusCheckCondition:
ERROR_LOG ( ( "%s::%s result = %d\n", getName ( ), __FUNCTION__, results.scsiStatus ) );
if ( results.requestSenseDone )
{
ERROR_LOG ( ( "Pulling out sense data\n" ) );
SetAutoSenseData ( clientData->scsiTask,
( SCSI_Sense_Data * ) clientData->senseBuffer->getBytesNoCopy ( ),
clientData->senseBuffer->getLength ( ) );
ERROR_LOG ( ( "Finished setting sense data\n" ) );
}
SetRealizedDataTransferCount ( clientData->scsiTask, results.bytesTransferred );
taskStatus = kSCSITaskStatus_CHECK_CONDITION;
serviceResponse = kSCSIServiceResponse_TASK_COMPLETE;
break;
case kSCSIStatusConditionMet:
taskStatus = kSCSITaskStatus_CONDITION_MET;
serviceResponse = kSCSIServiceResponse_TASK_COMPLETE;
break;
case kSCSIStatusBusy:
taskStatus = kSCSITaskStatus_BUSY;
serviceResponse = kSCSIServiceResponse_TASK_COMPLETE;
break;
case kSCSIStatusIntermediate:
taskStatus = kSCSITaskStatus_INTERMEDIATE;
serviceResponse = kSCSIServiceResponse_TASK_COMPLETE;
break;
case kSCSIStatusIntermediateMet:
taskStatus = kSCSITaskStatus_INTERMEDIATE_CONDITION_MET;
serviceResponse = kSCSIServiceResponse_TASK_COMPLETE;
break;
case kSCSIStatusReservationConfict:
taskStatus = kSCSITaskStatus_RESERVATION_CONFLICT;
serviceResponse = kSCSIServiceResponse_TASK_COMPLETE;
break;
case kSCSIStatusQueueFull:
taskStatus = kSCSITaskStatus_TASK_SET_FULL;
serviceResponse = kSCSIServiceResponse_TASK_COMPLETE;
break;
default:
taskStatus = kSCSITaskStatus_No_Status;
serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
break;
}
}
CompleteSCSITask ( clientData->scsiTask,
serviceResponse,
taskStatus );
STATUS_LOG ( ( "Exiting virtual callback\n" ) );
}
void
IOSCSIParallelInterfaceProtocolTransport::CompleteSCSITask (
SCSITaskIdentifier scsiTask,
SCSIServiceResponse serviceResponse,
SCSITaskStatus taskStatus )
{
STATUS_LOG ( ( "IOSCSIParallelInterfaceProtocolTransport::CompleteSCSITask called\n" ) );
CommandCompleted ( scsiTask, serviceResponse, taskStatus );
}
SCSIServiceResponse
IOSCSIParallelInterfaceProtocolTransport::AbortSCSICommand ( SCSITaskIdentifier request )
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_FUNCTION_REJECTED;
ERROR_LOG ( ( "IOSCSIParallelInterfaceProtocolTransport::AbortSCSICommand called\n" ) );
return serviceResponse;
}
bool
IOSCSIParallelInterfaceProtocolTransport::IsProtocolServiceSupported (
SCSIProtocolFeature feature,
void * value )
{
bool isSupported = false;
switch ( feature )
{
case kSCSIProtocolFeature_GetMaximumLogicalUnitNumber:
isSupported = true;
*( UInt32 * ) value = 0;
break;
case kSCSIProtocolFeature_MaximumReadTransferByteCount:
isSupported = true;
*( UInt64 * ) value = 131072; break;
case kSCSIProtocolFeature_MaximumWriteTransferByteCount:
isSupported = true;
*( UInt64 * ) value = 131072; break;
default:
break;
}
return isSupported;
}
bool
IOSCSIParallelInterfaceProtocolTransport::HandleProtocolServiceFeature (
SCSIProtocolFeature feature,
void * serviceValue )
{
return false;
}
#pragma mark -
#pragma mark Private Methods
void
IOSCSIParallelInterfaceProtocolTransport::sSCSICallbackProc ( void * target, void * refCon )
{
SCSIClientData clientData;
SCSIClientData * clientDataPtr = NULL;
IOSCSIParallelInterfaceProtocolTransport * xpt = NULL;
IOSCSICommand * cmd = NULL;
STATUS_LOG ( ( "IOSCSIParallelInterfaceProtocolTransport::sSCSICallbackProc entering.\n" ) );
xpt = ( IOSCSIParallelInterfaceProtocolTransport * ) target;
clientDataPtr = ( SCSIClientData * ) refCon;
assert ( clientDataPtr != NULL );
cmd = clientDataPtr->cmd;
bcopy ( clientDataPtr, &clientData, sizeof ( SCSIClientData ) );
STATUS_LOG ( ( "IOSCSIParallelInterfaceProtocolTransport::sSCSICallbackProc calling virtual callback...\n" ) );
xpt->SCSICallbackFunction ( cmd, &clientData );
}
bool
IOSCSIParallelInterfaceProtocolTransport::InspectDevice ( IOSCSIDevice * scsiDevice )
{
OSObject * obj = NULL;
OSDictionary * dict = NULL;
bool created = false;
obj = getProperty ( kIOPropertyProtocolCharacteristicsKey, gIOServicePlane );
if ( obj != NULL )
{
dict = OSDynamicCast ( OSDictionary, obj );
if ( dict != NULL )
{
dict = OSDictionary::withDictionary ( dict );
if ( dict != NULL )
{
setProperty ( kIOPropertyProtocolCharacteristicsKey, dict );
dict->release ( );
dict = NULL;
}
}
}
obj = getProperty ( kIOPropertyProtocolCharacteristicsKey );
if ( obj == NULL )
{
dict = OSDictionary::withCapacity ( 3 );
created = true;
}
else
{
dict = OSDynamicCast ( OSDictionary, obj );
}
if ( dict != NULL )
{
OSNumber * domainID = NULL;
OSNumber * targetID = NULL;
OSNumber * LUN = NULL;
domainID = OSDynamicCast ( OSNumber, scsiDevice->getProperty ( kIOPropertySCSIDomainIdentifierKey, gIOServicePlane ) );
if ( domainID != NULL )
{
dict->setObject ( kIOPropertySCSIDomainIdentifierKey, domainID );
}
targetID = OSDynamicCast ( OSNumber, scsiDevice->getProperty ( kSCSIPropertyTarget ) );
if ( targetID != NULL )
{
dict->setObject ( kIOPropertySCSITargetIdentifierKey, targetID );
}
LUN = OSDynamicCast ( OSNumber, scsiDevice->getProperty ( kSCSIPropertyLun ) );
if ( LUN != NULL )
{
dict->setObject ( kIOPropertySCSILogicalUnitNumberKey, LUN );
}
}
if ( created == true )
{
setProperty ( kIOPropertyProtocolCharacteristicsKey, dict );
dict->release ( );
created = false;
}
return true;
}
void
IOSCSIParallelInterfaceProtocolTransport::AllocateSCSICommandObjects ( void )
{
STATUS_LOG ( ( "IOSCSIParallelInterfaceProtocolTransport::AllocateSCSICommandObjects entering\n" ) );
IOSCSICommand * cmd = NULL;
SCSIClientData * clientData = NULL;
for ( UInt32 index = 0; index < kSCSICommandPoolSize; index++ )
{
cmd = fSCSIDevice->allocCommand ( fSCSIDevice, sizeof ( SCSIClientData ) );
assert ( cmd != NULL );
clientData = ( SCSIClientData * ) cmd->getClientData ( );
assert ( clientData != NULL );
clientData->senseBuffer = IOBufferMemoryDescriptor::withCapacity ( kMaxSenseDataSize,
kIODirectionIn,
true );
STATUS_LOG ( ( "adding command to pool\n" ) );
fCommandPool->returnCommand ( cmd );
}
STATUS_LOG ( ( "IOSCSIParallelInterfaceProtocolTransport::AllocateSCSICommandObjects exiting\n" ) );
}
void
IOSCSIParallelInterfaceProtocolTransport::DeallocateSCSICommandObjects ( void )
{
STATUS_LOG ( ( "IOSCSIParallelInterfaceProtocolTransport::DeallocateSCSICommandObjects entering\n" ) );
IOSCSICommand * cmd = NULL;
SCSIClientData * clientData = NULL;
cmd = ( IOSCSICommand * ) fCommandPool->getCommand ( false );
assert ( cmd != NULL );
while ( cmd != NULL )
{
clientData = ( SCSIClientData * ) cmd->getClientData ( );
assert ( clientData != NULL );
clientData->senseBuffer->release ( );
cmd->release ( );
cmd = ( IOSCSICommand * ) fCommandPool->getCommand ( false );
}
STATUS_LOG ( ( "IOSCSIParallelInterfaceProtocolTransport::DeallocateSCSICommandObjects exiting\n" ) );
}
IOSCSICommand *
IOSCSIParallelInterfaceProtocolTransport::GetSCSICommandObject ( bool blockForCommand )
{
IOSCSICommand * cmd = NULL;
STATUS_LOG ( ( "IOSCSIParallelInterfaceProtocolTransport::GetSCSICommandObject entering.\n" ) );
cmd = ( IOSCSICommand * ) fCommandPool->getCommand ( blockForCommand );
assert ( cmd != NULL );
return cmd;
}
void
IOSCSIParallelInterfaceProtocolTransport::ReturnSCSICommandObject ( IOSCSICommand * cmd )
{
STATUS_LOG ( ( "IOSCSIParallelInterfaceProtocolTransport::ReturnSCSICommandObject entering.\n" ) );
assert ( cmd != NULL );
fCommandPool->returnCommand ( cmd );
}
IOReturn
IOSCSIParallelInterfaceProtocolTransport::message ( UInt32 type, IOService * provider, void * argument )
{
IOReturn ret = kIOReturnSuccess;
ERROR_LOG ( ( "IOSCSIParallelInterfaceProtocolTransport::message %p %lx\n", this, type ) );
switch ( type )
{
case kSCSIClientMsgBusReset:
if ( !fBusResetInProgress )
{
ERROR_LOG ( ( "kSCSIClientMsgBusReset\n" ) );
fBusResetInProgress = true;
fSCSIDevice->holdQueue ( kQTypeNormalQ );
}
break;
case ( kSCSIClientMsgBusReset | kSCSIClientMsgDone ):
ERROR_LOG ( ( "kSCSIClientMsgBusResetDone\n" ) );
fSCSIDevice->releaseQueue ( kQTypeNormalQ );
fBusResetInProgress = false;
break;
default:
ret = super::message ( type, provider, argument );
break;
}
return ret;
}