IOSCSITargetDevice.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/IOMemoryDescriptor.h>
#include "SCSITaskDefinition.h"
#include "SCSIPrimaryCommands.h"
#include <IOKit/scsi-commands/IOSCSITargetDevice.h>
#define DEBUG 0
#define DEBUG_ASSERT_COMPONENT_NAME_STRING "SCSI Target Device"
#if DEBUG
#define SCSI_TARGET_DEVICE_DEBUGGING_LEVEL 3
#endif
#include "IOSCSIArchitectureModelFamilyDebugging.h"
#if ( SCSI_TARGET_DEVICE_DEBUGGING_LEVEL >= 1 )
#define PANIC_NOW(x) IOPanic x
#else
#define PANIC_NOW(x)
#endif
#if ( SCSI_TARGET_DEVICE_DEBUGGING_LEVEL >= 2 )
#define ERROR_LOG(x) IOLog x
#else
#define ERROR_LOG(x)
#endif
#if ( SCSI_TARGET_DEVICE_DEBUGGING_LEVEL >= 3 )
#define STATUS_LOG(x) IOLog x
#else
#define STATUS_LOG(x)
#endif
#define super IOSCSIPrimaryCommandsDevice
OSDefineMetaClassAndStructors ( IOSCSITargetDevice, IOSCSIPrimaryCommandsDevice );
#define kTURMaxRetries 1
#define kMaxInquiryAttempts 2
#if 0
#pragma mark -
#pragma mark ₯ Public Methods
#pragma mark -
#endif
bool
IOSCSITargetDevice::handleOpen( IOService * client,
IOOptionBits options,
void * arg )
{
bool result = false;
require_nonzero ( fClients, ErrorExit );
require_nonzero ( OSDynamicCast ( IOSCSILogicalUnitNub, client ), ErrorExit );
result = fClients->setObject ( client );
ErrorExit:
return result;
}
void
IOSCSITargetDevice::handleClose( IOService * client,
IOOptionBits options )
{
require_nonzero ( fClients, Exit );
if ( fClients->containsObject( client ) )
{
fClients->removeObject( client );
if ( ( fClients->getCount( ) == 0 ) && isInactive( ) )
{
message( kIOMessageServiceIsRequestingClose, getProvider( ), 0 );
}
}
Exit:
return;
}
bool
IOSCSITargetDevice::handleIsOpen( const IOService * client ) const
{
bool result = false;
require_nonzero ( fClients, CallSuperClassError );
if ( ( client == NULL ) && ( fClients->getCount ( ) != 0 ) )
{
result = true;
}
else
{
result = fClients->containsObject ( client );
}
return result;
CallSuperClassError:
result = super::handleIsOpen ( client );
return result;
}
bool
IOSCSITargetDevice::InitializeDeviceSupport ( void )
{
bool result = false;
STATUS_LOG ( ( "%s: InitializeDeviceSupport started\n", getName ( ) ) );
STATUS_LOG ( ( "%s: RetrieveCharacteristicsFromProvider\n", getName ( ) ) );
RetrieveCharacteristicsFromProvider();
STATUS_LOG ( ( "%s: DetermineTargetCharacteristics\n", getName ( ) ) );
result = DetermineTargetCharacteristics();
STATUS_LOG ( ( "%s: InitializeDeviceSupport completed\n", getName ( ) ) );
return result;
}
void
IOSCSITargetDevice::StartDeviceSupport ( void )
{
UInt64 countLU, loopLU;
bool supportsREPORTLUNS = false;
STATUS_LOG ( ( "%s: StartDeviceSupport started\n", getName ( ) ) );
countLU = DetermineMaximumLogicalUnitNumber();
fClients = OSSet::withCapacity( countLU + 1 );
#if 0
if( fTargetANSIVersion >= kINQUIRY_ANSI_VERSION_SCSI_SPC_Compliant )
{
UInt32 luCount[2];
if( RetrieveReportLUNsData( 0, (UInt8 *) luCount, 8 ) == true )
{
}
}
#endif
if( supportsREPORTLUNS == false )
{
for( loopLU = 0; loopLU <= countLU; loopLU++)
{
CreateLogicalUnit( loopLU );
}
}
STATUS_LOG ( ( "%s: StartDeviceSupport is complete\n", getName ( ) ) );
}
void
IOSCSITargetDevice::SuspendDeviceSupport ( void )
{
}
void
IOSCSITargetDevice::ResumeDeviceSupport ( void )
{
}
void
IOSCSITargetDevice::StopDeviceSupport ( void )
{
}
void
IOSCSITargetDevice::TerminateDeviceSupport ( void )
{
}
UInt32
IOSCSITargetDevice::GetNumberOfPowerStateTransitions ( void )
{
return 0;
}
bool
IOSCSITargetDevice::ClearNotReadyStatus ( void )
{
return true;
}
#if 0
#pragma mark -
#pragma mark ₯ Power Management Utility Methods
#pragma mark -
#endif
UInt32
IOSCSITargetDevice::GetInitialPowerState ( void )
{
return 0;
}
void
IOSCSITargetDevice::HandlePowerChange ( void )
{
}
void
IOSCSITargetDevice::HandleCheckPowerState ( void )
{
}
void
IOSCSITargetDevice::TicklePowerManager ( void )
{
}
void
IOSCSITargetDevice::RetrieveCharacteristicsFromProvider( void )
{
OSDictionary * characterDict = NULL;
OSObject * obj = NULL;
STATUS_LOG ( ( "%s: Check for the SCSI Device Characteristics property from provider.\n", getName ( ) ) );
if ( ( GetProtocolDriver()->getProperty ( kIOPropertySCSIDeviceCharacteristicsKey ) ) == NULL )
{
STATUS_LOG ( ( "%s: No SCSI Device Characteristics property, set defaults.\n", getName ( ) ) );
fDefaultInquiryCount = 0;
}
else
{
STATUS_LOG ( ( "%s: Get the SCSI Device Characteristics property from provider.\n", getName ( ) ) );
characterDict = OSDynamicCast ( OSDictionary, ( GetProtocolDriver()->getProperty ( kIOPropertySCSIDeviceCharacteristicsKey ) ) );
STATUS_LOG ( ( "%s: set this SCSI Device Characteristics property.\n", getName ( ) ) );
setProperty ( kIOPropertySCSIDeviceCharacteristicsKey, characterDict );
STATUS_LOG ( ( "%s: check for the Inquiry Length property.\n", getName ( ) ) );
if ( characterDict->getObject ( kIOPropertySCSIInquiryLengthKey ) == NULL )
{
STATUS_LOG ( ( "%s: No Inquiry Length property, use default.\n", getName ( ) ) );
fDefaultInquiryCount = 0;
}
else
{
OSNumber * defaultInquiry;
STATUS_LOG ( ( "%s: Get Inquiry Length property.\n", getName ( ) ) );
defaultInquiry = OSDynamicCast ( OSNumber, characterDict->getObject ( kIOPropertySCSIInquiryLengthKey ) );
fDefaultInquiryCount = defaultInquiry->unsigned32BitValue ( );
}
}
STATUS_LOG ( ( "%s: default inquiry count is: %d\n", getName ( ), fDefaultInquiryCount ) );
characterDict = OSDynamicCast ( OSDictionary, GetProtocolDriver()->getProperty ( kIOPropertyProtocolCharacteristicsKey ) );
if ( characterDict == NULL )
{
characterDict = OSDictionary::withCapacity ( 1 );
}
else
{
characterDict->retain ( );
}
obj = GetProtocolDriver()->getProperty ( kIOPropertyPhysicalInterconnectTypeKey );
if ( obj != NULL )
{
characterDict->setObject ( kIOPropertyPhysicalInterconnectTypeKey, obj );
}
obj = GetProtocolDriver()->getProperty ( kIOPropertyPhysicalInterconnectLocationKey );
if ( obj != NULL )
{
characterDict->setObject ( kIOPropertyPhysicalInterconnectLocationKey, obj );
}
setProperty ( kIOPropertyProtocolCharacteristicsKey, characterDict );
#if 0
obj = GetProtocolDriver()->getProperty ( kIOPropertySCSIProtocolMultiInitKey );
if ( obj != NULL )
{
}
#endif
characterDict->release ( );
}
#if 0
#pragma mark -
#pragma mark ₯ Target Related Member Routines
#pragma mark -
#endif
bool
IOSCSITargetDevice::DetermineTargetCharacteristics( void )
{
UInt8 inqData[255] = {0};
UInt8 inqDataCount = 0;
bool result = false;
STATUS_LOG ( ( "%s: DetermineTargetCharacteristics started \n", getName ( ) ) );
if( VerifyTargetPresence() == false )
{
STATUS_LOG ( ( "%s: Target presence could not be verified \n", getName ( ) ) );
return false;
}
if( fDefaultInquiryCount != 0 )
{
inqDataCount = fDefaultInquiryCount;
}
else
{
result = RetrieveDefaultINQUIRYData( 0, inqData, 6 );
if ( result == false )
{
STATUS_LOG ( ( "%s: Target INQUIRY data could not be retrieved \n", getName ( ) ) );
return false;
}
inqDataCount = ((SCSICmd_INQUIRY_StandardDataAll *) inqData)->ADDITIONAL_LENGTH + 5;
}
result = RetrieveDefaultINQUIRYData( 0, inqData, inqDataCount );
if ( result == false )
{
STATUS_LOG ( ( "%s: Target INQUIRY data could not be retrieved \n", getName ( ) ) );
return false;
}
if ( IsProtocolServiceSupported ( kSCSIProtocolFeature_SubmitDefaultInquiryData, NULL ) == true )
{
HandleProtocolServiceFeature ( kSCSIProtocolFeature_SubmitDefaultInquiryData, (void *) inqData );
}
SetCharacteristicsFromINQUIRY( (SCSICmd_INQUIRY_StandardDataAll *) inqData );
if( RetrieveINQUIRYDataPage( 0, inqData, 0, 4 ) == true )
{
UInt8 loop;
bool pagefound = false;
RetrieveINQUIRYDataPage( 0, inqData, 0, inqData[3] + 4 );
for( loop= 4; loop < (inqData[3] + 4); loop++)
{
if( inqData[loop] == 0x83 )
{
pagefound = true;
}
}
if( pagefound == true )
{
PublishDeviceIdentification();
}
}
return true;
}
bool
IOSCSITargetDevice::VerifyTargetPresence( void )
{
SCSITaskIdentifier request;
bool presenceVerified = false;
UInt8 TURCount = 0;
request = GetSCSITask();
if( request == NULL )
{
return false;
}
do
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
ResetForNewTask( request );
TEST_UNIT_READY( request, 0x00 );
serviceResponse = SendCommand( request, 0 );
if( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE )
{
if( GetTaskStatus( request ) == kSCSITaskStatus_CHECK_CONDITION )
{
bool validSense = false;
SCSI_Sense_Data senseBuffer = { 0 };
IOMemoryDescriptor * bufferDesc = NULL;
validSense = GetAutoSenseData( request, &senseBuffer, sizeof( senseBuffer ) );
if( validSense == false )
{
ResetForNewTask(request );
REQUEST_SENSE( request,
bufferDesc,
kSenseDefaultSize,
0 );
serviceResponse = SendCommand( request, 0 );
}
}
presenceVerified = true;
}
TURCount++;
} while(( presenceVerified == false ) && ( TURCount < kTURMaxRetries ));
ReleaseSCSITask( request );
return presenceVerified;
}
bool
IOSCSITargetDevice::SetCharacteristicsFromINQUIRY( SCSICmd_INQUIRY_StandardDataAll * inquiryBuffer )
{
OSString * string = NULL;
char tempString[17]; int index = 0;
fTargetPeripheralDeviceType = ( inquiryBuffer->PERIPHERAL_DEVICE_TYPE & kINQUIRY_PERIPHERAL_TYPE_Mask );
fTargetANSIVersion = ( inquiryBuffer->VERSION & kINQUIRY_ANSI_VERSION_Mask );
#if 0
fTargetHasHiSup = ( inquiryBuffer->RESPONSE_DATA_FORMAT & ;
fTargetHasSCCS;
fTargetHasEncServs;
fTargetHasMultiPorts;
fTargetHasMChanger;
#endif
setProperty ( kIOPropertySCSIPeripheralDeviceType,
( UInt64 ) fTargetPeripheralDeviceType,
kIOPropertySCSIPeripheralDeviceTypeSize );
for ( index = 0; index < kINQUIRY_VENDOR_IDENTIFICATION_Length; index++ )
{
tempString[index] = inquiryBuffer->VENDOR_IDENTIFICATION[index];
}
tempString[index] = 0;
for ( index = kINQUIRY_VENDOR_IDENTIFICATION_Length - 1; index != 0; index-- )
{
if ( tempString[index] != ' ' )
{
tempString[index + 1] = '\0';
break;
}
}
string = OSString::withCString ( tempString );
if ( string != NULL )
{
setProperty ( kIOPropertySCSIVendorIdentification, string );
string->release ( );
}
for ( index = 0; index < kINQUIRY_PRODUCT_IDENTIFICATION_Length; index++ )
{
tempString[index] = inquiryBuffer->PRODUCT_INDENTIFICATION[index];
}
tempString[index] = 0;
for ( index = kINQUIRY_PRODUCT_IDENTIFICATION_Length - 1; index != 0; index-- )
{
if ( tempString[index] != ' ' )
{
tempString[index+1] = '\0';
break;
}
}
string = OSString::withCString ( tempString );
if ( string != NULL )
{
setProperty ( kIOPropertySCSIProductIdentification, string );
string->release ( );
}
for ( index = 0; index < kINQUIRY_PRODUCT_REVISION_LEVEL_Length; index++ )
{
tempString[index] = inquiryBuffer->PRODUCT_REVISION_LEVEL[index];
}
tempString[index] = 0;
for ( index = kINQUIRY_PRODUCT_REVISION_LEVEL_Length - 1; index != 0; index-- )
{
if ( tempString[index] != ' ' )
{
tempString[index+1] = '\0';
break;
}
}
string = OSString::withCString ( tempString );
if ( string != NULL )
{
setProperty ( kIOPropertySCSIProductRevisionLevel, string );
string->release ( );
}
return true;
}
#if 0
#pragma mark -
#pragma mark ₯ Logical Unit Related Member Routines
#pragma mark -
#endif
bool
IOSCSITargetDevice::RetrieveReportLUNsData(
SCSILogicalUnitNumber logicalUnit,
UInt8 * dataBuffer,
UInt8 dataSize )
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
IOMemoryDescriptor * bufferDesc = NULL;
SCSITaskIdentifier request = NULL;
bool result = false;
request = GetSCSITask();
if( request == NULL )
{
return false;
}
bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) dataBuffer,
dataSize,
kIODirectionIn );
if( REPORT_LUNS (
request,
bufferDesc,
dataSize,
0 ) == true )
{
serviceResponse = SendCommand ( request, 0 );
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
result = true;
}
}
else
{
result = false;
}
bufferDesc->release();
ReleaseSCSITask( request );
return result;
}
UInt64
IOSCSITargetDevice::DetermineMaximumLogicalUnitNumber( void )
{
UInt32 protocolLUNCount;
if( IsProtocolServiceSupported( kSCSIProtocolFeature_GetMaximumLogicalUnitNumber, (void *) &protocolLUNCount ) == false )
{
return 0;
}
return protocolLUNCount;
}
bool
IOSCSITargetDevice::VerifyLogicalUnitPresence( SCSILogicalUnitNumber theLogicalUnit )
{
return false;
}
bool
IOSCSITargetDevice::CreateLogicalUnit( SCSILogicalUnitNumber theLogicalUnit )
{
STATUS_LOG ( ( "IOSCSITargetDevice::CreatePeripheralDeviceNubForLUN entering.\n" ) );
IOSCSILogicalUnitNub * nub = new IOSCSILogicalUnitNub;
if ( nub == NULL )
{
PANIC_NOW(( "IOSCSITargetDevice::CreatePeripheralDeviceNubForLUN failed\n" ));
return false;
}
nub->init( 0 );
nub->SetLogicalUnitNumber( (UInt8) theLogicalUnit );
if ( nub->attach( this ) == false )
{
PANIC_NOW(( "IOSCSITargetDevice::CreatePeripheralDeviceNubForLUN unable to attach nub" ));
return false;
}
if( nub->start( this ) == false )
{
nub->detach( this );
}
else
{
nub->registerService( kIOServiceSynchronous );
}
nub->release();
return false;
}
#if 0
#pragma mark -
#pragma mark ₯ INQUIRY Utility Member Routines
#pragma mark -
#endif
bool
IOSCSITargetDevice::RetrieveDefaultINQUIRYData(
SCSILogicalUnitNumber logicalUnit,
UInt8 * inquiryBuffer,
UInt8 inquirySize )
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
IOMemoryDescriptor * bufferDesc = NULL;
SCSITaskIdentifier request = NULL;
int index;
bool result = false;
request = GetSCSITask();
if( request == NULL )
{
return false;
}
bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) inquiryBuffer,
inquirySize,
kIODirectionIn );
require_nonzero ( bufferDesc, RELEASE_TASK );
IOSleep ( 100 );
for ( index = 0; ( index < kMaxInquiryAttempts ); index++ )
{
if (INQUIRY (
request,
bufferDesc,
0,
0,
0,
inquirySize,
0 ) == false )
{
result = false;
break;
}
serviceResponse = SendCommand ( request, 0 );
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
result = true;
break;
}
else
{
IOSleep ( 1000 );
}
}
bufferDesc->release();
RELEASE_TASK:
ReleaseSCSITask( request );
return true;
}
bool
IOSCSITargetDevice::RetrieveINQUIRYDataPage(
SCSILogicalUnitNumber logicalUnit,
UInt8 * inquiryBuffer,
UInt8 inquiryPage,
UInt8 inquirySize )
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
IOMemoryDescriptor * bufferDesc = NULL;
SCSITaskIdentifier request = NULL;
bool result = false;
request = GetSCSITask();
if( request == NULL )
{
return false;
}
bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) inquiryBuffer,
inquirySize,
kIODirectionIn );
if( INQUIRY (
request,
bufferDesc,
0,
1,
inquiryPage,
inquirySize,
0 ) == true )
{
serviceResponse = SendCommand ( request, 0 );
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
result = true;
}
}
else
{
result = false;
}
bufferDesc->release();
ReleaseSCSITask( request );
return result;
}
void
IOSCSITargetDevice::PublishDeviceIdentification( void )
{
UInt8 inqData[255] = {0};
UInt8 inqDataCount = 0;
OSArray * deviceIDs = NULL;
UInt8 deviceIDCount = 0;
if( RetrieveINQUIRYDataPage( 0, inqData, kINQUIRY_Page83_PageCode, 4 ) == false )
{
return;
}
if( inqData[3] == 0 )
{
return;
}
if( RetrieveINQUIRYDataPage( 0, inqData, kINQUIRY_Page83_PageCode, inqData[3] + 4 ) == false )
{
return;
}
deviceIDs = OSArray::withCapacity ( 1 );
if( deviceIDs == NULL )
{
return;
}
inqDataCount = 4;
while ( inqDataCount < (inqData[3] + 4))
{
UInt8 idSize;
OSDictionary * idDictionary;
OSNumber * numString = NULL;
UInt8 codeSet = 0;
deviceIDCount++;
idDictionary = OSDictionary::withCapacity ( 1 );
codeSet = inqData[inqDataCount] & 0x0F;
numString = OSNumber::withNumber( codeSet , 8 );
if( numString != NULL )
{
idDictionary->setObject( kIOPropertySCSIINQUIRYDeviceIdCodeSet, numString);
numString->release();
}
numString = OSNumber::withNumber( inqData[inqDataCount + 1] & kINQUIRY_Page83_CodeSetMask, 8 );
if( numString != NULL )
{
idDictionary->setObject( kIOPropertySCSIINQUIRYDeviceIdType, numString);
numString->release();
}
numString = OSNumber::withNumber( (inqData[inqDataCount + 1] >> 4) & 0x03, 8 );
if( numString != NULL )
{
idDictionary->setObject( kIOPropertySCSIINQUIRYDeviceIdAssociation, numString);
numString->release();
}
inqDataCount +=3;
idSize = inqData[inqDataCount];
inqDataCount++;
if( codeSet == kINQUIRY_Page83_CodeSetASCIIData )
{
OSString * charString = NULL;
char idString[255] = {0};
for( UInt8 i = 0; i< idSize; i++ )
{
idString[i] = inqData[inqDataCount+i];
}
charString = OSString::withCString( idString );
if( charString != NULL )
{
idDictionary->setObject( kIOPropertySCSIINQUIRYDeviceIdentifier, charString);
charString->release();
}
}
else
{
OSData * idData = NULL;
idData = OSData::withBytes( &inqData[inqDataCount], idSize );
if( idData != NULL )
{
idDictionary->setObject( kIOPropertySCSIINQUIRYDeviceIdentifier, idData);
idData->release();
}
}
inqDataCount +=idSize;
if( deviceIDs->ensureCapacity( deviceIDCount ) == deviceIDCount )
{
deviceIDs->setObject( idDictionary );
}
idDictionary->release();
}
setProperty ( kIOPropertySCSIINQUIRYDeviceIdentification, deviceIDs );
deviceIDs->release();
}
#if 0
#pragma mark -
#pragma mark ₯ VTable Padding
#pragma mark -
#endif
OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 1 );
OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 2 );
OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 3 );
OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 4 );
OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 5 );
OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 6 );
OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 7 );
OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 8 );
OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 9 );
OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 10 );
OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 11 );
OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 12 );
OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 13 );
OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 14 );
OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 15 );
OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 16 );