IOSCSITargetDevice.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <libkern/c++/OSArray.h>
#include <libkern/c++/OSData.h>
#include <libkern/c++/OSDictionary.h>
#include <libkern/c++/OSString.h>
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/IOMemoryDescriptor.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IORegistryEntry.h>
#include <IOKit/IOService.h>
#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
#include "SCSITaskDefinition.h"
#include "SCSIPrimaryCommands.h"
#include "SCSICmds_INQUIRY_Definitions.h"
#include "SCSICmds_REPORT_LUNS_Definitions.h"
#include "SCSICmds_REQUEST_SENSE_Defs.h"
#include "IOSCSITargetDevice.h"
#include "IOSCSITargetDeviceHashTable.h"
#include "SCSITargetDevicePathManager.h"
#include "SCSIPathManagers.h"
#include <IOKit/scsi/spi/IOSCSIParallelInterfaceController.h>
#define DEBUG 0
#define DEBUG_ASSERT_COMPONENT_NAME_STRING "SCSI Target Device"
#if DEBUG
#define SCSI_TARGET_DEVICE_DEBUGGING_LEVEL 0
#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; IOSleep (1)
#else
#define ERROR_LOG(x)
#endif
#if ( SCSI_TARGET_DEVICE_DEBUGGING_LEVEL >= 3 )
#define STATUS_LOG(x) IOLog x; IOSleep (1)
#else
#define STATUS_LOG(x)
#endif
#define super IOSCSIPrimaryCommandsDevice
OSDefineMetaClassAndStructors ( IOSCSITargetDevice, IOSCSIPrimaryCommandsDevice );
#define kTURMaxRetries 1
#define kMaxInquiryAttempts 2
#define kStandardInquiryDataHeaderSize 5
#define kMaxInquiryDataBytes 255
#define kSCSILogicalUnitDefaultLUN 0
#define kSCSICmd_INQUIRY_StandardDataAllString "SCSICmd_INQUIRY_StandardDataAll"
#if 0
#pragma mark -
#pragma mark ₯ Public Methods
#pragma mark -
#endif
bool
IOSCSITargetDevice::Create (
IOSCSIProtocolServices * provider )
{
bool result = false;
IOSCSITargetDevice * newDevice = NULL;
newDevice = OSTypeAlloc ( IOSCSITargetDevice );
require_nonzero ( newDevice, ErrorExit );
result = newDevice->init ( 0 );
require ( result, ReleaseTargetExit );
result = newDevice->attach ( provider );
require ( result, DetachTargetExit );
result = newDevice->start ( provider );
require ( result, DetachTargetExit );
result = newDevice->CreateTargetDeviceOrPath ( provider );
require ( result, DetachTargetExit );
newDevice->release ( );
newDevice = NULL;
return result;
DetachTargetExit:
newDevice->stop ( provider );
newDevice->detach ( provider );
ReleaseTargetExit:
require_nonzero_quiet ( newDevice, ErrorExit );
newDevice->release ( );
newDevice = NULL;
ErrorExit:
return result;
}
bool
IOSCSITargetDevice::CreateTargetDeviceOrPath (
IOSCSIProtocolServices * provider )
{
bool result = true;
result = VerifyTargetPresence ( );
require ( result, ErrorExit );
RetrieveCharacteristicsFromProvider ( );
result = DetermineTargetCharacteristics ( );
require ( result, ErrorExit );
result = IsProviderAnotherPathToTarget ( provider );
if ( result == false )
{
fPathManager = SCSIRoundRobinPathManager::Create ( this, provider );
check ( fPathManager );
ScanForLogicalUnits ( );
}
else
{
detach ( provider );
}
result = true;
ErrorExit:
return result;
}
bool
IOSCSITargetDevice::IsProviderAnotherPathToTarget (
IOSCSIProtocolServices * provider )
{
bool result = false;
bool checkSerialNumber = true;
OSDictionary * dict = NULL;
OSArray * array = NULL;
OSData * nodeName = NULL;
UInt32 index = 0;
UInt32 count = 0;
IOSCSITargetDeviceHashTable * ht = NULL;
STATUS_LOG ( ( "+IOSCSITargetDevice::IsProviderAnotherPathToTarget\n" ) );
ht = IOSCSITargetDeviceHashTable::GetSharedInstance ( );
array = OSDynamicCast ( OSArray, getProperty ( kIOPropertySCSIINQUIRYDeviceIdentification ) );
if ( array != NULL )
{
STATUS_LOG ( ( "Inspecting page 83h identifiers\n" ) );
count = array->getCount ( );
for ( index = 0; index < count; index++ )
{
STATUS_LOG ( ( "Inspecting identifier = %ld\n", index ) );
dict = OSDynamicCast ( OSDictionary, array->getObject ( index ) );
if ( dict != NULL )
{
OSNumber * idType = NULL;
OSNumber * codeSet = NULL;
OSNumber * association = NULL;
idType = OSDynamicCast ( OSNumber, dict->getObject ( kIOPropertySCSIINQUIRYDeviceIdType ) );
codeSet = OSDynamicCast ( OSNumber, dict->getObject ( kIOPropertySCSIINQUIRYDeviceIdCodeSet ) );
association = OSDynamicCast ( OSNumber, dict->getObject ( kIOPropertySCSIINQUIRYDeviceIdAssociation ) );
if ( ( idType == NULL ) || ( codeSet == NULL ) || ( association == NULL ) )
{
ERROR_LOG ( ( "Found NULL for idType, codeSet, or association\n" ) );
continue;
}
if ( ( idType->unsigned8BitValue ( ) == kINQUIRY_Page83_IdentifierTypeIEEE_EUI64 ) &&
( codeSet->unsigned8BitValue ( ) == kINQUIRY_Page83_CodeSetBinaryData ) &&
( association->unsigned8BitValue ( ) == ( kINQUIRY_Page83_AssociationDevice >> 4 ) ) )
{
nodeName = OSDynamicCast ( OSData, dict->getObject ( kIOPropertySCSIINQUIRYDeviceIdentifier ) );
if ( nodeName == NULL )
panic ( "nodeName can't be NULL\n" );
SetNodeUniqueIdentifier ( nodeName );
STATUS_LOG ( ( "Verifying identifier is same as WWNN\n" ) );
dict = OSDynamicCast ( OSDictionary, provider->getProperty ( kIOPropertyProtocolCharacteristicsKey, gIOServicePlane ) );
if ( ( dict != NULL ) && ( nodeName != NULL ) )
{
check ( nodeName->isEqualTo ( dict->getObject ( kIOPropertyFibreChannelNodeWorldWideNameKey ) ) );
}
else
{
check ( dict );
check ( nodeName );
}
STATUS_LOG ( ( "Updating hash table\n" ) );
result = ht->IsProviderPathToExistingTarget ( this, provider, ht->Hash ( nodeName ) );
checkSerialNumber = false;
break;
}
}
}
}
if ( checkSerialNumber == true )
{
OSString * string = NULL;
STATUS_LOG ( ( "Inspecting unit serial number\n" ) );
string = OSDynamicCast ( OSString, getProperty ( kIOPropertySCSIINQUIRYUnitSerialNumber ) );
if ( string != NULL )
{
SetNodeUniqueIdentifier ( string );
result = ht->IsProviderPathToExistingTarget ( this, provider, ht->Hash ( string ) );
}
}
STATUS_LOG ( ( "-IOSCSITargetDevice::IsProviderAnotherPathToTarget\n" ) );
return result;
}
void
IOSCSITargetDevice::SetHashEntry ( void * newEntry )
{
fTargetHashEntry = newEntry;
}
OSObject *
IOSCSITargetDevice::GetNodeUniqueIdentifier ( void )
{
return fNodeUniqueIdentifier;
}
void
IOSCSITargetDevice::SetNodeUniqueIdentifier ( OSObject * uniqueID )
{
require_nonzero ( uniqueID, ErrorExit );
uniqueID->retain ( );
if ( fNodeUniqueIdentifier != NULL )
fNodeUniqueIdentifier->release ( );
fNodeUniqueIdentifier = uniqueID;
ErrorExit:
return;
}
void
IOSCSITargetDevice::AddPath ( IOSCSIProtocolServices * provider )
{
STATUS_LOG ( ( "+IOSCSITargetDevice::AddPath\n" ) );
attach ( provider );
provider->open ( this );
if ( fPathManager != NULL )
{
fPathManager->AddPath ( provider );
messageClients ( kIOMessageServicePropertyChange );
}
STATUS_LOG ( ( "-IOSCSITargetDevice::AddPath\n" ) );
}
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;
}
IOReturn
IOSCSITargetDevice::message ( UInt32 type, IOService * nub, void * arg )
{
IOReturn result = kIOReturnSuccess;
switch ( type )
{
case kSCSIControllerNotificationPortStatus:
{
IOSCSIProtocolServices * path = NULL;
path = OSDynamicCast ( IOSCSIProtocolServices, nub );
if ( ( fPathManager != NULL ) && ( path != NULL ) )
{
fPathManager->PathStatusChanged ( path, ( UInt32 ) arg );
messageClients ( kIOMessageServicePropertyChange );
}
}
break;
default:
{
STATUS_LOG ( ( "IOSCSITargetDevice::Unknown message type = 0x%08x\n", type ) );
result = super::message ( type, nub, arg );
}
break;
}
return result;
}
void
IOSCSITargetDevice::detach ( IOService * provider )
{
STATUS_LOG ( ( "+IOSCSITargetDevice::detach\n" ) );
if ( fPathManager != NULL )
{
fPathManager->RemovePath ( ( IOSCSIProtocolServices * ) provider );
messageClients ( kIOMessageServicePropertyChange );
}
super::detach ( provider );
STATUS_LOG ( ( "-IOSCSITargetDevice::detach\n" ) );
}
void
IOSCSITargetDevice::free ( void )
{
STATUS_LOG ( ( "+IOSCSITargetDevice::free\n" ) );
if ( fTargetHashEntry != NULL )
{
IOSCSITargetDeviceHashTable * ht = NULL;
ht = IOSCSITargetDeviceHashTable::GetSharedInstance ( );
ht->DestroyHashReference ( fTargetHashEntry );
fTargetHashEntry = NULL;
}
if ( fPathManager != NULL )
{
fPathManager->release ( );
fPathManager = NULL;
}
if ( fNodeUniqueIdentifier != NULL )
{
fNodeUniqueIdentifier->release ( );
fNodeUniqueIdentifier = NULL;
}
super::free ( );
STATUS_LOG ( ( "-IOSCSITargetDevice::free\n" ) );
}
#if 0
#pragma mark -
#pragma mark ₯ Protected Methods - Methods used by this class and subclasses
#pragma mark -
#endif
bool
IOSCSITargetDevice::InitializeDeviceSupport ( void )
{
bool result = true;
return result;
}
void
IOSCSITargetDevice::StartDeviceSupport ( void )
{
return;
}
void
IOSCSITargetDevice::ScanForLogicalUnits ( void )
{
UInt64 countLU = 0;
UInt64 loopLU = 0;
OSData * data = NULL;
bool supportsREPORTLUNS = false;
bool result = false;
STATUS_LOG ( ( "+IOSCSITargetDevice::ScanForLogicalUnits\n" ) );
countLU = DetermineMaximumLogicalUnitNumber ( );
fClients = OSSet::withCapacity ( countLU + 1 );
if ( countLU > 0 )
{
if ( fTargetANSIVersion >= kINQUIRY_ANSI_VERSION_SCSI_SPC_Compliant )
{
STATUS_LOG ( ( "fTargetANSIVersion >= kINQUIRY_ANSI_VERSION_SCSI_SPC_Compliant\n" ) );
SCSICmd_REPORT_LUNS_Header * header = NULL;
header = IONew ( SCSICmd_REPORT_LUNS_Header, 1 );
if ( header != NULL )
{
STATUS_LOG ( ( "header != NULL\n" ) );
bzero ( header, sizeof ( SCSICmd_REPORT_LUNS_Header ) );
result = RetrieveReportLUNsData ( kSCSILogicalUnitDefaultLUN, ( UInt8 * ) header, sizeof ( SCSICmd_REPORT_LUNS_Header ) );
if ( result == true )
{
UInt32 length = 0;
length = OSSwapBigToHostInt32 ( header->LUN_LIST_LENGTH ) + kREPORT_LUNS_HeaderSize;
IODelete ( header, SCSICmd_REPORT_LUNS_Header, 1 );
STATUS_LOG ( ( "length = %ld\n", length ) );
if ( length >= sizeof ( SCSICmd_REPORT_LUNS_Header ) )
{
header = ( SCSICmd_REPORT_LUNS_Header * ) IOMalloc ( length );
if ( header != NULL )
{
result = RetrieveReportLUNsData ( kSCSILogicalUnitDefaultLUN, ( UInt8 * ) header, length );
if ( result == true )
{
UInt32 count = 0;
UInt32 index = 0;
SCSICmd_REPORT_LUNS_LUN_ENTRY * LUN = NULL;
supportsREPORTLUNS = true;
count = OSSwapBigToHostInt32 ( header->LUN_LIST_LENGTH ) / ( sizeof ( SCSICmd_REPORT_LUNS_LUN_ENTRY ) );
STATUS_LOG ( ( "count = %ld\n", count ) );
for ( index = 0; index < count; index++ )
{
UInt8 addressMethod = 0;
UInt8 logicalUnitNumber = 0;
LUN = &header->LUN[index];
LUN->FIRST_LEVEL_ADDRESSING = OSSwapBigToHostInt16 ( LUN->FIRST_LEVEL_ADDRESSING );
addressMethod = LUN->FIRST_LEVEL_ADDRESSING >> kREPORT_LUNS_ADDRESS_METHOD_OFFSET;
STATUS_LOG ( ( "addressMethod = %d\n", addressMethod ) );
if ( addressMethod == kREPORT_LUNS_ADDRESS_METHOD_PERIPHERAL_DEVICE )
{
bool LUNObjectExists = false;
check ( ( LUN->FIRST_LEVEL_ADDRESSING & 0xFF00 ) == 0 );
logicalUnitNumber = LUN->FIRST_LEVEL_ADDRESSING & 0x00FF;
STATUS_LOG ( ( "logicalUnitNumber = %d\n", logicalUnitNumber ) );
LUNObjectExists = DoesLUNObjectExist ( logicalUnitNumber );
if ( ( logicalUnitNumber < countLU ) && ( LUNObjectExists == false ) )
{
bool LUNPresent = false;
LUNPresent = VerifyLogicalUnitPresence ( logicalUnitNumber );
if ( LUNPresent == true )
{
CreateLogicalUnit ( logicalUnitNumber );
}
}
}
#if DEBUG
else
{
ERROR_LOG ( ( "Not kREPORT_LUNS_ADDRESS_METHOD_PERIPHERAL_DEVICE, not creating LUN\n" ) );
}
#endif
}
}
IOFree ( header, length );
header = NULL;
}
}
}
else
{
IODelete ( header, SCSICmd_REPORT_LUNS_Header, 1 );
header = NULL;
}
}
}
}
if ( supportsREPORTLUNS == false )
{
ERROR_LOG ( ( "Device does not support REPORT_LUNS command, creating LUNs by brute-force\n" ) );
for ( loopLU = 0; loopLU <= countLU; loopLU++ )
{
bool LUNPresent = false;
LUNPresent = VerifyLogicalUnitPresence ( loopLU );
if ( LUNPresent == true )
{
CreateLogicalUnit ( loopLU );
}
}
}
data = OSDynamicCast ( OSData, getProperty ( kSCSICmd_INQUIRY_StandardDataAllString ) );
result = IsProtocolServiceSupported ( kSCSIProtocolFeature_SubmitDefaultInquiryData, NULL );
if ( ( result == true ) && ( data != NULL ) )
{
HandleProtocolServiceFeature ( kSCSIProtocolFeature_SubmitDefaultInquiryData,
( void * ) data->getBytesNoCopy ( ) );
#if (DEBUG == 0)
removeProperty ( kSCSICmd_INQUIRY_StandardDataAllString );
#endif
data = NULL;
}
registerService ( );
STATUS_LOG ( ( "-IOSCSITargetDevice::ScanForLogicalUnits\n" ) );
}
void
IOSCSITargetDevice::ExecuteCommand ( SCSITaskIdentifier request )
{
SetTargetLayerReference ( request, ( void * ) this );
if ( fPathManager != NULL )
{
fPathManager->ExecuteCommand ( request );
}
else
{
super::ExecuteCommand ( request );
}
}
SCSIServiceResponse
IOSCSITargetDevice::AbortTask ( UInt8 logicalUnit,
SCSITaggedTaskIdentifier theTag )
{
if ( fPathManager != NULL )
{
return fPathManager->AbortTask ( logicalUnit, theTag );
}
else
{
return super::AbortTask ( logicalUnit, theTag );
}
}
SCSIServiceResponse
IOSCSITargetDevice::AbortTaskSet ( UInt8 logicalUnit )
{
if ( fPathManager != NULL )
{
return fPathManager->AbortTaskSet ( logicalUnit );
}
else
{
return super::AbortTaskSet ( logicalUnit );
}
}
SCSIServiceResponse
IOSCSITargetDevice::ClearACA ( UInt8 logicalUnit )
{
if ( fPathManager != NULL )
{
return fPathManager->ClearACA ( logicalUnit );
}
else
{
return super::ClearACA ( logicalUnit );
}
}
SCSIServiceResponse
IOSCSITargetDevice::ClearTaskSet ( UInt8 logicalUnit )
{
if ( fPathManager != NULL )
{
return fPathManager->ClearTaskSet ( logicalUnit );
}
else
{
return super::ClearTaskSet ( logicalUnit );
}
}
SCSIServiceResponse
IOSCSITargetDevice::LogicalUnitReset ( UInt8 logicalUnit )
{
if ( fPathManager != NULL )
{
return fPathManager->LogicalUnitReset ( logicalUnit );
}
else
{
return super::LogicalUnitReset ( logicalUnit );
}
}
SCSIServiceResponse
IOSCSITargetDevice::TargetReset ( void )
{
if ( fPathManager != NULL )
{
return fPathManager->TargetReset ( );
}
else
{
return super::TargetReset ( );
}
}
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;
}
bool
IOSCSITargetDevice::SetTargetLayerReference ( SCSITaskIdentifier request,
void * value )
{
SCSITask * scsiRequest = NULL;
bool result = false;
scsiRequest = OSDynamicCast ( SCSITask, request );
if ( scsiRequest != NULL )
{
result = scsiRequest->SetTargetLayerReference ( value );
}
return result;
}
void *
IOSCSITargetDevice::GetTargetLayerReference ( SCSITaskIdentifier request )
{
SCSITask * scsiRequest = NULL;
void * result = NULL;
scsiRequest = OSDynamicCast ( SCSITask, request );
if ( scsiRequest != NULL )
{
result = scsiRequest->GetTargetLayerReference ( );
}
return result;
}
#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;
fDefaultInquiryCount = 0;
obj = GetProtocolDriver ( )->getProperty ( kIOPropertySCSIDeviceCharacteristicsKey );
if ( obj != NULL )
{
characterDict = OSDynamicCast ( OSDictionary, obj );
if ( characterDict != NULL )
{
setProperty ( kIOPropertySCSIDeviceCharacteristicsKey, characterDict );
if ( characterDict->getObject ( kIOPropertySCSIInquiryLengthKey ) != NULL )
{
OSNumber * defaultInquiry = NULL;
STATUS_LOG ( ( "%s: Get Inquiry Length property.\n", getName ( ) ) );
obj = characterDict->getObject ( kIOPropertySCSIInquiryLengthKey );
defaultInquiry = OSDynamicCast ( OSNumber, obj );
if ( defaultInquiry != NULL )
{
fDefaultInquiryCount = defaultInquiry->unsigned32BitValue ( );
}
}
}
}
STATUS_LOG ( ( "%s: default inquiry count is: %d\n", getName ( ), fDefaultInquiryCount ) );
obj = GetProtocolDriver ( )->getProperty ( kIOPropertyProtocolCharacteristicsKey );
characterDict = OSDynamicCast ( OSDictionary, obj );
if ( characterDict == NULL )
{
characterDict = OSDictionary::withCapacity ( 1 );
}
else
{
characterDict->retain ( );
}
if ( characterDict != NULL )
{
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 )
{
bool result = false;
result = PublishDefaultINQUIRYInformation ( );
require ( result, ErrorExit );
PublishINQUIRYVitalProductDataInformation ( this, kSCSILogicalUnitDefaultLUN );
ErrorExit:
return result;
}
bool
IOSCSITargetDevice::PublishDefaultINQUIRYInformation ( void )
{
bool result = false;
UInt8 length = 0;
OSData * data = NULL;
SCSICmd_INQUIRY_StandardDataAll * inqData = NULL;
inqData = IONew ( SCSICmd_INQUIRY_StandardDataAll, 1 );
require_nonzero ( inqData, ErrorExit );
if ( fDefaultInquiryCount != 0 )
{
length = fDefaultInquiryCount;
}
else
{
length = kStandardInquiryDataHeaderSize + 1;
result = RetrieveDefaultINQUIRYData ( kSCSILogicalUnitDefaultLUN, ( UInt8 * ) inqData, length );
require ( result, ReleaseBuffer );
length = inqData->ADDITIONAL_LENGTH + kStandardInquiryDataHeaderSize;
}
result = RetrieveDefaultINQUIRYData ( kSCSILogicalUnitDefaultLUN, ( UInt8 * ) inqData, length );
require ( result, ReleaseBuffer );
SetCharacteristicsFromINQUIRY ( inqData );
data = OSData::withBytes ( ( const void * ) inqData, sizeof ( SCSICmd_INQUIRY_StandardDataAll ) );
require_nonzero ( data, ReleaseBuffer );
setProperty ( kSCSICmd_INQUIRY_StandardDataAllString, data );
data->release ( );
data = NULL;
IODelete ( inqData, SCSICmd_INQUIRY_StandardDataAll, 1 );
inqData = NULL;
result = true;
return result;
ReleaseBuffer:
require_nonzero_quiet ( inqData, ErrorExit );
IODelete ( inqData, SCSICmd_INQUIRY_StandardDataAll, 1 );
inqData = NULL;
ErrorExit:
result = false;
return result;
}
void
IOSCSITargetDevice::PublishINQUIRYVitalProductDataInformation (
IOService * object,
SCSILogicalUnitNumber logicalUnit )
{
bool result = false;
UInt8 length = 0;
UInt8 index = 0;
UInt8 pageLength = 0;
SCSICmd_INQUIRY_Page00_Header * data = NULL;
UInt8 * bytes = NULL;
IOBufferMemoryDescriptor * buffer = NULL;
STATUS_LOG ( ( "+IOSCSITargetDevice::PublishINQUIRYVitalProductDataInformation\n" ) );
buffer = IOBufferMemoryDescriptor::withCapacity ( kMaxInquiryDataBytes, kIODirectionIn );
require_nonzero ( buffer, ErrorExit );
length = sizeof ( SCSICmd_INQUIRY_Page00_Header );
data = ( SCSICmd_INQUIRY_Page00_Header * ) buffer->getBytesNoCopy ( );
bytes = ( UInt8 * ) data;
require_nonzero ( data, ReleaseBuffer );
bzero ( data, kMaxInquiryDataBytes );
result = RetrieveINQUIRYDataPage ( logicalUnit,
bytes,
kINQUIRY_Page00_PageCode,
length );
require ( result, ReleaseBuffer );
STATUS_LOG ( ( "PERIPHERAL_DEVICE_TYPE = 0x%02x\n", data->PERIPHERAL_DEVICE_TYPE ) );
STATUS_LOG ( ( "PAGE_CODE = 0x%02x\n", data->PAGE_CODE ) );
STATUS_LOG ( ( "PAGE_LENGTH = %d\n", data->PAGE_LENGTH ) );
pageLength = data->PAGE_LENGTH;
length = data->PAGE_LENGTH + sizeof ( SCSICmd_INQUIRY_Page00_Header );
STATUS_LOG ( ( "length = %d\n", length ) );
result = RetrieveINQUIRYDataPage ( logicalUnit,
bytes,
kINQUIRY_Page00_PageCode,
length );
require ( result, ReleaseBuffer );
require ( ( data->PAGE_CODE == kINQUIRY_Page00_PageCode ), ReleaseBuffer );
require ( ( data->PAGE_LENGTH == pageLength ), ReleaseBuffer );
for ( index = sizeof ( SCSICmd_INQUIRY_Page00_Header ); index < length; index++ )
{
STATUS_LOG ( ( "PAGE_CODE_ENTRY[%d] = 0x%02x\n", ( int ) ( index - sizeof ( SCSICmd_INQUIRY_Page00_Header ) ), bytes[index] ) );
if ( bytes[index] == kINQUIRY_Page80_PageCode )
{
PublishUnitSerialNumber ( object, logicalUnit );
}
if ( bytes[index] == kINQUIRY_Page83_PageCode )
{
PublishDeviceIdentification ( object, logicalUnit );
}
}
ReleaseBuffer:
require_nonzero_quiet ( buffer, ErrorExit );
buffer->release ( );
buffer = NULL;
ErrorExit:
STATUS_LOG ( ( "-IOSCSITargetDevice::PublishINQUIRYVitalProductDataInformation\n" ) );
return;
}
bool
IOSCSITargetDevice::VerifyTargetPresence ( void )
{
SCSITaskIdentifier request = NULL;
bool presenceVerified = false;
UInt8 TURCount = 0;
STATUS_LOG ( ( "+IOSCSITargetDevice::VerifyTargetPresence\n" ) );
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
do
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
TEST_UNIT_READY ( request, 0x00 );
STATUS_LOG ( ( "Sending TEST_UNIT_READY %d to target\n", TURCount + 1 ) );
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
if ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE )
{
if ( GetTaskStatus ( request ) == kSCSITaskStatus_CHECK_CONDITION )
{
bool validSense = false;
SCSI_Sense_Data senseBuffer = { 0 };
validSense = GetAutoSenseData ( request, &senseBuffer, sizeof ( senseBuffer ) );
if ( validSense == false )
{
IOMemoryDescriptor * bufferDesc = NULL;
bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) &senseBuffer,
sizeof ( SCSI_Sense_Data ),
kIODirectionIn );
if ( bufferDesc != NULL )
{
REQUEST_SENSE ( request, bufferDesc, kSenseDefaultSize, 0 );
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
bufferDesc->release ( );
}
}
}
presenceVerified = true;
}
TURCount++;
} while ( ( presenceVerified == false ) && ( TURCount < kTURMaxRetries ) );
ReleaseSCSITask ( request );
ErrorExit:
STATUS_LOG ( ( "+IOSCSITargetDevice::VerifyTargetPresence, target present = %s\n", presenceVerified ? "yes" : "no" ) );
return presenceVerified;
}
bool
IOSCSITargetDevice::SetCharacteristicsFromINQUIRY (
SCSICmd_INQUIRY_StandardDataAll * inquiryBuffer )
{
OSString * string = NULL;
int index = 0;
char tempString[17] = { 0 };
fTargetPeripheralDeviceType = ( inquiryBuffer->PERIPHERAL_DEVICE_TYPE & kINQUIRY_PERIPHERAL_TYPE_Mask );
fTargetANSIVersion = ( inquiryBuffer->VERSION & kINQUIRY_ANSI_VERSION_Mask );
#if DEBUG
setProperty ( "ANSI Version", fTargetANSIVersion, 8 );
#endif
fTargetHasHiSup = ( inquiryBuffer->RESPONSE_DATA_FORMAT & kINQUIRY_Byte3_HISUP_Mask );
fTargetHasSCCS = ( inquiryBuffer->SCCSReserved & kINQUIRY_Byte5_SCCS_Mask );
fTargetHasEncServs = ( inquiryBuffer->flags1 & kINQUIRY_Byte6_ENCSERV_Mask );
fTargetHasMultiPorts = ( inquiryBuffer->flags1 & kINQUIRY_Byte6_MULTIP_Mask );
fTargetHasMChanger = ( inquiryBuffer->flags1 & kINQUIRY_Byte6_MCHNGR_Mask );
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 ( );
string = NULL;
}
for ( index = 0; index < kINQUIRY_PRODUCT_IDENTIFICATION_Length; index++ )
{
tempString[index] = inquiryBuffer->PRODUCT_IDENTIFICATION[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 ( );
string = NULL;
}
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 ( );
string = NULL;
}
return true;
}
void
IOSCSITargetDevice::TargetTaskCompletion ( SCSITaskIdentifier request )
{
if ( ( GetServiceResponse ( request ) == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_CHECK_CONDITION ) )
{
bool validSense = false;
SCSI_Sense_Data senseBuffer = { 0 };
validSense = GetAutoSenseData ( request, &senseBuffer, sizeof ( senseBuffer ) );
if ( validSense )
{
ERROR_LOG ( ( "IOSCSITargetDevice::TargetTaskCompletion - SENSE_KEY = %d, ASC = 0x%02x, ASCQ = 0x%02x\n",
senseBuffer.SENSE_KEY & kSENSE_KEY_Mask,
senseBuffer.ADDITIONAL_SENSE_CODE,
senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ) );
if ( ( senseBuffer.SENSE_KEY & kSENSE_KEY_Mask ) == kSENSE_KEY_UNIT_ATTENTION )
{
if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x3F ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x0E ) )
{
IOLog ( "REPORT_LUNS DATA HAS CHANGED\n" );
}
else if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x3F ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x03 ) )
{
IOLog ( "INQUIRY DATA HAS CHANGED\n" );
}
}
}
}
TaskCompletedNotification ( request );
}
#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;
bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) dataBuffer,
dataSize,
kIODirectionIn );
require_nonzero ( bufferDesc, ErrorExit );
request = GetSCSITask ( );
require_nonzero ( request, ReleaseBuffer );
SetLogicalUnitNumber ( request, logicalUnit );
if ( REPORT_LUNS ( request, bufferDesc, dataSize, 0 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
result = true;
}
else
{
bool validSense = false;
SCSI_Sense_Data senseBuffer = { 0 };
validSense = GetAutoSenseData ( request, &senseBuffer, sizeof ( senseBuffer ) );
if ( validSense )
{
ERROR_LOG ( ( "REPORT_LUNS failed, serviceResponse = %d, taskStatus = %d\n",
serviceResponse, GetTaskStatus ( request ) ) );
ERROR_LOG ( ( "SENSE_KEY = %d, ASC = 0x%02x, ASCQ = 0x%02x\n",
senseBuffer.SENSE_KEY & kSENSE_KEY_Mask,
senseBuffer.ADDITIONAL_SENSE_CODE,
senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ) );
}
}
}
ReleaseSCSITask ( request );
request = NULL;
ReleaseBuffer:
require_nonzero_quiet ( bufferDesc, ErrorExit );
bufferDesc->release ( );
bufferDesc = NULL;
ErrorExit:
return result;
}
UInt64
IOSCSITargetDevice::DetermineMaximumLogicalUnitNumber ( void )
{
UInt32 protocolLUNCount = 0;
bool supported = false;
supported = IsProtocolServiceSupported (
kSCSIProtocolFeature_GetMaximumLogicalUnitNumber,
( void * ) &protocolLUNCount );
if ( supported == false )
{
protocolLUNCount = 0;
}
return protocolLUNCount;
}
bool
IOSCSITargetDevice::VerifyLogicalUnitPresence (
SCSILogicalUnitNumber logicalUnit )
{
bool presenceVerified = false;
SCSITaskIdentifier request = NULL;
UInt8 TURCount = 0;
STATUS_LOG ( ( "+IOSCSITargetDevice::VerifyLogicalUnitPresence\n" ) );
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
do
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
TEST_UNIT_READY ( request, 0x00 );
STATUS_LOG ( ( "Sending TEST_UNIT_READY %d to LUN %d\n", TURCount + 1, ( int ) logicalUnit ) );
SetLogicalUnitNumber ( request, logicalUnit );
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
if ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE )
{
if ( GetTaskStatus ( request ) == kSCSITaskStatus_CHECK_CONDITION )
{
bool validSense = false;
SCSI_Sense_Data senseBuffer = { 0 };
validSense = GetAutoSenseData ( request, &senseBuffer, sizeof ( senseBuffer ) );
if ( validSense == false )
{
IOMemoryDescriptor * bufferDesc = NULL;
bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) &senseBuffer,
sizeof ( SCSI_Sense_Data ),
kIODirectionIn );
if ( bufferDesc != NULL )
{
REQUEST_SENSE ( request, bufferDesc, kSenseDefaultSize, 0 );
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
bufferDesc->release ( );
}
}
if ( ( ( senseBuffer.SENSE_KEY & kSENSE_KEY_Mask ) == kSENSE_KEY_ILLEGAL_REQUEST ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE == 0x25 ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x00 ) )
{
ERROR_LOG ( ( "Logical unit = %lld not valid\n", logicalUnit ) );
break;
}
}
presenceVerified = true;
}
TURCount++;
} while ( ( presenceVerified == false ) && ( TURCount < kTURMaxRetries ) );
ReleaseSCSITask ( request );
ErrorExit:
STATUS_LOG ( ( "+IOSCSITargetDevice::VerifyLogicalUnitPresence, LUN present = %s\n", presenceVerified ? "yes" : "no" ) );
return presenceVerified;
}
bool
IOSCSITargetDevice::CreateLogicalUnit ( SCSILogicalUnitNumber logicalUnit )
{
bool result = false;
IOSCSILogicalUnitNub * nub = NULL;
nub = OSTypeAlloc ( IOSCSILogicalUnitNub );
require_nonzero ( nub, ErrorExit );
result = nub->init ( 0 );
require ( result, ReleaseNub );
nub->SetLogicalUnitNumber ( ( UInt8 ) logicalUnit );
result = nub->attach ( this );
require ( result, ReleaseNub );
PublishINQUIRYVitalProductDataInformation ( nub, logicalUnit );
result = nub->start ( this );
require_action_quiet ( result, ReleaseNub, nub->detach ( this ) );
ReleaseNub:
require_nonzero_quiet ( nub, ErrorExit );
nub->release ( );
nub = NULL;
ErrorExit:
return result;
}
void
IOSCSITargetDevice::SetLogicalUnitNumber (
SCSITaskIdentifier request,
SCSILogicalUnitNumber logicalUnit )
{
if ( logicalUnit != 0 )
{
SCSITask * scsiRequest = NULL;
scsiRequest = OSDynamicCast ( SCSITask, request );
if ( scsiRequest != NULL )
{
scsiRequest->SetLogicalUnitNumber ( logicalUnit );
}
}
}
bool
IOSCSITargetDevice::DoesLUNObjectExist ( SCSILogicalUnitNumber logicalUnit )
{
bool result = false;
OSCollectionIterator * iter = NULL;
IOSCSILogicalUnitNub * lun = NULL;
OSObject * obj = NULL;
iter = OSCollectionIterator::withCollection ( fClients );
if ( iter != NULL )
{
BeginLoop:
obj = iter->getNextObject ( );
while ( obj != NULL )
{
lun = OSDynamicCast ( IOSCSILogicalUnitNub, obj );
if ( ( lun != NULL ) && ( lun->GetLogicalUnitNumber ( ) == logicalUnit ) )
{
result = true;
break;
}
if ( iter->isValid ( ) )
{
obj = iter->getNextObject ( );
}
else
{
iter->reset ( );
ERROR_LOG ( ( "IOSCSITargetDevice::DoesLUNObjectExist collection changed from underneath iterator\n" ) );
goto BeginLoop;
}
}
iter->release ( );
}
return result;
}
#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 = 0;
bool result = false;
bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) inquiryBuffer,
inquirySize,
kIODirectionIn );
require_nonzero ( bufferDesc, ErrorExit );
request = GetSCSITask ( );
require_nonzero ( request, ReleaseBuffer );
SetLogicalUnitNumber ( request, logicalUnit );
for ( index = 0; index < kMaxInquiryAttempts; index++ )
{
result = INQUIRY ( request, bufferDesc, 0, 0, 0, inquirySize, 0 );
require ( result, ReleaseTask );
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
result = true;
break;
}
else
{
result = false;
IOSleep ( 1000 );
}
}
ReleaseTask:
require_nonzero_quiet ( request, ReleaseBuffer );
ReleaseSCSITask ( request );
request = NULL;
ReleaseBuffer:
require_nonzero_quiet ( bufferDesc, ReleaseBuffer );
bufferDesc->release ( );
bufferDesc = NULL;
ErrorExit:
return result;
}
bool
IOSCSITargetDevice::RetrieveINQUIRYDataPage (
SCSILogicalUnitNumber logicalUnit,
UInt8 * inquiryBuffer,
UInt8 inquiryPage,
UInt8 inquirySize )
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
SCSITaskIdentifier request = NULL;
IOMemoryDescriptor * bufferDesc = NULL;
bool result = false;
bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) inquiryBuffer,
inquirySize,
kIODirectionIn );
require_nonzero ( bufferDesc, ErrorExit );
request = GetSCSITask ( );
require_nonzero ( request, ReleaseBuffer );
SetLogicalUnitNumber ( request, logicalUnit );
if ( INQUIRY (
request,
bufferDesc,
0,
1,
inquiryPage,
inquirySize,
0 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
result = true;
}
else
{
bool validSense = false;
SCSI_Sense_Data senseBuffer = { 0 };
ERROR_LOG ( ( "RetrieveINQUIRYDataPage failed, page = %d, serviceResponse = %d, taskStatus = %d\n",
inquiryPage, serviceResponse, GetTaskStatus ( request ) ) );
validSense = GetAutoSenseData ( request, &senseBuffer, sizeof ( senseBuffer ) );
if ( validSense )
{
ERROR_LOG ( ( "SENSE_KEY = %d, ASC = 0x%02x, ASCQ = 0x%02x\n",
senseBuffer.SENSE_KEY & kSENSE_KEY_Mask,
senseBuffer.ADDITIONAL_SENSE_CODE,
senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ) );
}
}
}
ReleaseSCSITask ( request );
ReleaseBuffer:
require_nonzero_quiet ( bufferDesc, ErrorExit );
bufferDesc->release ( );
bufferDesc = NULL;
ErrorExit:
return result;
}
void
IOSCSITargetDevice::PublishUnitSerialNumber ( IOService * object,
SCSILogicalUnitNumber logicalUnit )
{
bool result = false;
SCSICmd_INQUIRY_Page80_Header * data = NULL;
IOBufferMemoryDescriptor * buffer = NULL;
OSString * string = NULL;
char serialNumber[kMaxInquiryDataBytes] = { 0 };
UInt8 length = 0;
UInt8 pageLength = 0;
STATUS_LOG ( ( "+IOSCSITargetDevice::PublishUnitSerialNumber\n" ) );
buffer = IOBufferMemoryDescriptor::withCapacity ( kMaxInquiryDataBytes, kIODirectionIn );
require_nonzero ( buffer, ErrorExit );
length = sizeof ( SCSICmd_INQUIRY_Page80_Header ) - sizeof ( UInt8 );
data = ( SCSICmd_INQUIRY_Page80_Header * ) buffer->getBytesNoCopy ( );
require_nonzero ( data, ReleaseBuffer );
bzero ( data, kMaxInquiryDataBytes );
result = RetrieveINQUIRYDataPage ( logicalUnit,
( UInt8 * ) data,
kINQUIRY_Page80_PageCode,
length );
require ( result, ReleaseBuffer );
require_nonzero ( data->PAGE_LENGTH, ReleaseBuffer );
STATUS_LOG ( ( "PERIPHERAL_DEVICE_TYPE = 0x%02x\n", data->PERIPHERAL_DEVICE_TYPE ) );
STATUS_LOG ( ( "PAGE_CODE = 0x%02x\n", data->PAGE_CODE ) );
STATUS_LOG ( ( "PAGE_LENGTH = %d\n", data->PAGE_LENGTH ) );
pageLength = data->PAGE_LENGTH;
length = sizeof ( SCSICmd_INQUIRY_Page80_Header ) - sizeof ( UInt8 ) + data->PAGE_LENGTH;
STATUS_LOG ( ( "length = %d\n", length ) );
result = RetrieveINQUIRYDataPage ( logicalUnit,
( UInt8 * ) data,
kINQUIRY_Page80_PageCode,
length );
require ( result, ReleaseBuffer );
require ( ( data->PAGE_CODE == kINQUIRY_Page80_PageCode ), ReleaseBuffer );
require ( ( data->PAGE_LENGTH == pageLength ), ReleaseBuffer );
bcopy ( &data->PRODUCT_SERIAL_NUMBER, serialNumber, data->PAGE_LENGTH );
string = OSString::withCString ( serialNumber );
if ( string != NULL )
{
object->setProperty ( kIOPropertySCSIINQUIRYUnitSerialNumber, string );
string->release ( );
string = NULL;
}
ReleaseBuffer:
require_nonzero_quiet ( buffer, ErrorExit );
buffer->release ( );
buffer = NULL;
ErrorExit:
STATUS_LOG ( ( "-IOSCSITargetDevice::PublishUnitSerialNumber\n" ) );
return;
}
void
IOSCSITargetDevice::PublishDeviceIdentification (
IOService * object,
SCSILogicalUnitNumber logicalUnit )
{
OSArray * deviceIDs = NULL;
SCSICmd_INQUIRY_Page83_Identification_Descriptor * descriptor = NULL;
SCSICmd_INQUIRY_Page83_Header * data = NULL;
IOBufferMemoryDescriptor * buffer = NULL;
UInt8 * bytes = NULL;
UInt8 length = 0;
UInt8 offset = 0;
UInt8 pageLength = 0;
bool result = false;
STATUS_LOG ( ( "+IOSCSITargetDevice::PublishDeviceIdentification\n" ) );
buffer = IOBufferMemoryDescriptor::withCapacity ( kMaxInquiryDataBytes, kIODirectionIn );
require_nonzero ( buffer, ErrorExit );
length = sizeof ( SCSICmd_INQUIRY_Page83_Header );
data = ( SCSICmd_INQUIRY_Page83_Header * ) buffer->getBytesNoCopy ( );
bytes = ( UInt8 * ) data;
require_nonzero ( data, ReleaseBuffer );
bzero ( data, kMaxInquiryDataBytes );
result = RetrieveINQUIRYDataPage ( logicalUnit,
bytes,
kINQUIRY_Page83_PageCode,
length );
require ( result, ReleaseBuffer );
require_nonzero ( data->PAGE_LENGTH, ReleaseBuffer );
STATUS_LOG ( ( "PAGE_LENGTH = %d\n", data->PAGE_LENGTH ) );
pageLength = data->PAGE_LENGTH;
length = data->PAGE_LENGTH + sizeof ( SCSICmd_INQUIRY_Page83_Header );
result = RetrieveINQUIRYDataPage ( logicalUnit,
bytes,
kINQUIRY_Page83_PageCode,
length );
require ( result, ReleaseBuffer );
require ( ( data->PAGE_CODE == kINQUIRY_Page83_PageCode ), ReleaseBuffer );
require ( ( pageLength == data->PAGE_LENGTH ), ReleaseBuffer );
deviceIDs = OSArray::withCapacity ( 1 );
require_nonzero ( deviceIDs, ReleaseBuffer );
offset = sizeof ( SCSICmd_INQUIRY_Page83_Header );
STATUS_LOG ( ( "PERIPHERAL_DEVICE_TYPE = 0x%02x\n", data->PERIPHERAL_DEVICE_TYPE ) );
STATUS_LOG ( ( "PAGE_CODE = 0x%02x\n", data->PAGE_CODE ) );
STATUS_LOG ( ( "PAGE_LENGTH = %d\n", data->PAGE_LENGTH ) );
STATUS_LOG ( ( "length = %d\n", length ) );
STATUS_LOG ( ( "offset = %d\n", offset ) );
while ( offset < length )
{
UInt8 codeSet = 0;
UInt8 idType = 0;
UInt8 association = 0;
OSDictionary * idDictionary = NULL;
OSNumber * number = NULL;
STATUS_LOG ( ( "Processing SCSICmd_INQUIRY_Page83_Identification_Descriptor\n" ) );
idDictionary = OSDictionary::withCapacity ( 1 );
require_nonzero ( idDictionary, ReleaseDeviceIDs );
descriptor = ( SCSICmd_INQUIRY_Page83_Identification_Descriptor * ) &bytes[offset];
STATUS_LOG ( ( "CODE_SET = 0x%02x\n", descriptor->CODE_SET ) );
STATUS_LOG ( ( "IDENTIFIER_TYPE = 0x%02x\n", descriptor->IDENTIFIER_TYPE ) );
STATUS_LOG ( ( "IDENTIFIER_LENGTH = %d\n", descriptor->IDENTIFIER_LENGTH ) );
offset += descriptor->IDENTIFIER_LENGTH +
offsetof ( SCSICmd_INQUIRY_Page83_Identification_Descriptor, IDENTIFIER );
require_action ( ( offset <= length ), ReleaseDeviceIDs, idDictionary->release ( ) );
#if DEBUG
UInt8 index = 0;
for ( index = 0; index < descriptor->IDENTIFIER_LENGTH; index++ )
{
UInt8 * identifier = ( UInt8 * ) &descriptor->IDENTIFIER;
STATUS_LOG ( ( "identifier[%d] = 0x%02x : ", index, identifier[index] ) );
}
STATUS_LOG ( ( "\n" ) );
#endif
codeSet = descriptor->CODE_SET & kINQUIRY_Page83_CodeSetMask;
number = OSNumber::withNumber ( codeSet, 8 );
if ( number != NULL )
{
idDictionary->setObject ( kIOPropertySCSIINQUIRYDeviceIdCodeSet, number );
number->release ( );
number = NULL;
}
idType = descriptor->IDENTIFIER_TYPE & kINQUIRY_Page83_IdentifierTypeMask;
number = OSNumber::withNumber ( idType, 8 );
if ( number != NULL )
{
idDictionary->setObject ( kIOPropertySCSIINQUIRYDeviceIdType, number );
number->release ( );
number = NULL;
}
association = ( ( descriptor->IDENTIFIER_TYPE & kINQUIRY_Page83_AssociationMask ) >> 4 );
number = OSNumber::withNumber ( association, 8 );
if ( number != NULL )
{
idDictionary->setObject ( kIOPropertySCSIINQUIRYDeviceIdAssociation, number );
number->release ( );
number = NULL;
}
if ( codeSet == kINQUIRY_Page83_CodeSetASCIIData )
{
OSString * string = NULL;
char identifier[kMaxInquiryDataBytes] = { 0 };
bcopy ( &descriptor->IDENTIFIER, identifier, descriptor->IDENTIFIER_LENGTH );
string = OSString::withCString ( identifier );
if ( string != NULL )
{
idDictionary->setObject ( kIOPropertySCSIINQUIRYDeviceIdentifier, string );
string->release ( );
string = NULL;
}
}
else
{
OSData * idData = NULL;
idData = OSData::withBytes ( &descriptor->IDENTIFIER, descriptor->IDENTIFIER_LENGTH );
if ( idData != NULL )
{
idDictionary->setObject ( kIOPropertySCSIINQUIRYDeviceIdentifier, idData );
idData->release ( );
idData = NULL;
}
}
STATUS_LOG ( ( "offset = %d\n", offset ) );
deviceIDs->setObject ( idDictionary );
idDictionary->release ( );
idDictionary = NULL;
}
object->setProperty ( kIOPropertySCSIINQUIRYDeviceIdentification, deviceIDs );
ReleaseDeviceIDs:
require_nonzero_quiet ( deviceIDs, ErrorExit );
deviceIDs->release ( );
deviceIDs = NULL;
ReleaseBuffer:
require_nonzero_quiet ( buffer, ErrorExit );
buffer->release ( );
buffer = NULL;
ErrorExit:
STATUS_LOG ( ( "-IOSCSITargetDevice::PublishDeviceIdentification\n" ) );
return;
}
#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 );