IOSCSIBlockCommandsDevice.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <IOKit/scsi-commands/SCSICommandDefinitions.h>
#include "IOBlockStorageServices.h"
#include "IOSCSIBlockCommandsDevice.h"
#define DEBUG 0
#define DEBUG_ASSERT_COMPONENT_NAME_STRING "SBC"
#if DEBUG
#define SCSI_SBC_DEVICE_DEBUGGING_LEVEL 0
#endif
#include "IOSCSIArchitectureModelFamilyDebugging.h"
#if ( SCSI_SBC_DEVICE_DEBUGGING_LEVEL >= 1 )
#define PANIC_NOW(x) IOPanic x
#else
#define PANIC_NOW(x)
#endif
#if ( SCSI_SBC_DEVICE_DEBUGGING_LEVEL >= 2 )
#define ERROR_LOG(x) IOLog x
#else
#define ERROR_LOG(x)
#endif
#if ( SCSI_SBC_DEVICE_DEBUGGING_LEVEL >= 3 )
#define STATUS_LOG(x) IOLog x
#else
#define STATUS_LOG(x)
#endif
#define super IOSCSIPrimaryCommandsDevice
OSDefineMetaClass ( IOSCSIBlockCommandsDevice, IOSCSIPrimaryCommandsDevice );
OSDefineAbstractStructors ( IOSCSIBlockCommandsDevice, IOSCSIPrimaryCommandsDevice );
#define kMaxRetryCount 8
#define kCapacityDataBufferSize 8
#define kWriteProtectMask 0x80
#define kAppleKeySwitchProperty "AppleKeyswitch"
#define kFireWireHDIconKey "FireWireHD.icns"
#define kUSBHDIconKey "USBHD.icns"
#define kModeSense6ParameterHeaderSize 4
#if 0
#pragma mark -
#pragma mark ₯ Public Methods - API Exported to layers above
#pragma mark -
#endif
IOReturn
IOSCSIBlockCommandsDevice::SyncReadWrite (
IOMemoryDescriptor * buffer,
UInt64 startBlock,
UInt64 blockCount,
UInt64 blockSize )
{
IODirection direction;
IOReturn status = kIOReturnBadArgument;
require_action ( IsProtocolAccessEnabled ( ),
ErrorExit,
status = kIOReturnNotAttached );
require_action ( IsDeviceAccessEnabled ( ),
ErrorExit,
status = kIOReturnOffline );
direction = buffer->getDirection ( );
if ( direction == kIODirectionIn )
{
status = IssueRead ( buffer, startBlock, blockCount );
}
else if ( direction == kIODirectionOut )
{
status = IssueWrite ( buffer, startBlock, blockCount );
}
ErrorExit:
return status;
}
IOReturn
IOSCSIBlockCommandsDevice::AsyncReadWrite ( IOMemoryDescriptor * buffer,
UInt64 startBlock,
UInt64 blockCount,
UInt64 blockSize,
void * clientData )
{
IODirection direction;
IOReturn status = kIOReturnBadArgument;
require_action ( IsProtocolAccessEnabled ( ),
ErrorExit,
status = kIOReturnNotAttached );
require_action ( IsDeviceAccessEnabled ( ),
ErrorExit,
status = kIOReturnOffline );
direction = buffer->getDirection ( );
if ( direction == kIODirectionIn )
{
status = IssueRead ( buffer, startBlock, blockCount, clientData );
}
else if ( direction == kIODirectionOut )
{
status = IssueWrite ( buffer, startBlock, blockCount, clientData );
}
ErrorExit:
return status;
}
IOReturn
IOSCSIBlockCommandsDevice::EjectTheMedium ( void )
{
IOReturn status = kIOReturnNoResources;
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
SCSITaskIdentifier request = NULL;
bool doPollForRemoval = false;
STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) );
require_action ( IsProtocolAccessEnabled ( ),
ErrorExit,
status = kIOReturnNotAttached );
require_action ( IsDeviceAccessEnabled ( ),
ErrorExit,
status = kIOReturnOffline );
if ( fMediaIsRemovable == false )
{
status = SynchronizeCache ( );
changePowerStateToPriv ( kSBCPowerStateSleep );
}
else
{
if ( fKnownManualEject == false )
{
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
if ( PREVENT_ALLOW_MEDIUM_REMOVAL ( request, kMediaStateUnlocked, 0 ) == true )
{
( void ) SendCommand ( request, kTenSecondTimeoutInMS );
}
if ( START_STOP_UNIT ( request, 0, 0, 1, 0, 0 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) != kSCSITaskStatus_GOOD ) )
{
doPollForRemoval = true;
}
}
ReleaseSCSITask ( request );
request = NULL;
}
else
{
doPollForRemoval = true;
}
ResetMediumCharacteristics ( );
fMediumIsWriteProtected = true;
if ( ( doPollForRemoval == true ) || ( fMediumRemovalPrevented == false ) )
{
fPollingMode = kPollingMode_MediaRemoval;
}
else
{
fPollingMode = kPollingMode_NewMedia;
}
EnablePolling ( );
}
status = kIOReturnSuccess;
ErrorExit:
return status;
}
IOReturn
IOSCSIBlockCommandsDevice::FormatMedium ( UInt64 blockCount, UInt64 blockSize )
{
IOReturn status = kIOReturnUnsupported;
require_action ( IsProtocolAccessEnabled ( ),
ErrorExit,
status = kIOReturnNotAttached );
require_action ( IsDeviceAccessEnabled ( ),
ErrorExit,
status = kIOReturnOffline );
ErrorExit:
return status;
}
UInt32
IOSCSIBlockCommandsDevice::GetFormatCapacities (
UInt64 * capacities,
UInt32 capacitiesMaxCount ) const
{
return 0;
}
IOReturn
IOSCSIBlockCommandsDevice::LockUnlockMedium ( bool doLock )
{
IOReturn status = kIOReturnSuccess;
require_action ( IsProtocolAccessEnabled ( ), ErrorExit, status = kIOReturnNotAttached );
require_action ( IsProtocolAccessEnabled ( ), ErrorExit, status = kIOReturnOffline );
ErrorExit:
return status;
}
IOReturn
IOSCSIBlockCommandsDevice::SynchronizeCache ( void )
{
IOReturn status = kIOReturnSuccess;
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
SCSITaskIdentifier request = NULL;
STATUS_LOG ( ( "IOSCSIBlockCommandsDevice::SynchronizeCache called\n" ) );
require_action ( IsProtocolAccessEnabled ( ),
ErrorExit,
status = kIOReturnNotAttached );
require_action ( IsDeviceAccessEnabled ( ),
ErrorExit,
status = kIOReturnOffline );
require ( fWriteCacheEnabled, ErrorExit );
request = GetSCSITask ( );
require_nonzero_action ( request,
ErrorExit,
status = kIOReturnNoResources );
if ( SYNCHRONIZE_CACHE ( request, 0, 0, 0, 0, 0 ) == true )
{
serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
status = kIOReturnSuccess;
}
else
{
status = kIOReturnError;
}
ReleaseSCSITask ( request );
request = NULL;
ErrorExit:
return status;
}
UInt64
IOSCSIBlockCommandsDevice::ReportMediumBlockSize ( void )
{
return fMediumBlockSize;
}
UInt64
IOSCSIBlockCommandsDevice::ReportMediumTotalBlockCount ( void )
{
return fMediumBlockCount;
}
bool
IOSCSIBlockCommandsDevice::ReportMediumWriteProtection ( void )
{
return fMediumIsWriteProtected;
}
UInt64
IOSCSIBlockCommandsDevice::ReportDeviceMaxBlocksReadTransfer ( void )
{
UInt64 maxBlockCount = 256; bool supported = false;
STATUS_LOG ( ( "IOSCSIBlockCommandsDevice::ReportDeviceMaxBlocksReadTransfer\n" ) );
supported = GetProtocolDriver ( )->IsProtocolServiceSupported (
kSCSIProtocolFeature_MaximumReadBlockTransferCount,
&maxBlockCount );
if ( supported == false )
{
UInt64 maxByteCount = 0;
supported = GetProtocolDriver ( )->IsProtocolServiceSupported (
kSCSIProtocolFeature_MaximumReadTransferByteCount,
&maxByteCount );
if ( ( supported == true ) &&
( maxByteCount > 0 ) &&
( fMediumBlockSize > 0 ) )
{
maxBlockCount = maxByteCount / fMediumBlockSize;
}
}
return maxBlockCount;
}
UInt64
IOSCSIBlockCommandsDevice::ReportDeviceMaxBlocksWriteTransfer ( void )
{
UInt64 maxBlockCount = 256;
bool supported = false;
STATUS_LOG ( ( "IOSCSIBlockCommandsDevice::ReportDeviceMaxBlocksWriteTransfer.\n" ) );
supported = GetProtocolDriver ( )->IsProtocolServiceSupported (
kSCSIProtocolFeature_MaximumWriteBlockTransferCount,
&maxBlockCount );
if ( supported == false )
{
UInt64 maxByteCount = 0;
supported = GetProtocolDriver ( )->IsProtocolServiceSupported (
kSCSIProtocolFeature_MaximumWriteTransferByteCount,
&maxByteCount );
if ( ( supported == true ) &&
( maxByteCount > 0 ) &&
( fMediumBlockSize > 0 ) )
{
maxBlockCount = maxByteCount / fMediumBlockSize;
}
}
return maxBlockCount;
}
bool
IOSCSIBlockCommandsDevice::ReportDeviceMediaRemovability ( void )
{
STATUS_LOG ( ( "IOSCSIBlockCommandsDevice::ReportMediaRemovability fMediaIsRemovable = %d\n",
( int ) fMediaIsRemovable ) );
return fMediaIsRemovable;
}
#if 0
#pragma mark -
#pragma mark ₯ Protected Methods - Methods used by this class and subclasses
#pragma mark -
#endif
bool
IOSCSIBlockCommandsDevice::InitializeDeviceSupport ( void )
{
bool setupSuccessful = false;
fMediaIsRemovable = false;
fMediumPresent = false;
fMediumIsWriteProtected = true;
fMediumRemovalPrevented = false;
fKnownManualEject = false;
STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) );
fIOSCSIBlockCommandsDeviceReserved = ( IOSCSIBlockCommandsDeviceExpansionData * )
IOMalloc ( sizeof ( IOSCSIBlockCommandsDeviceExpansionData ) );
require_nonzero ( fIOSCSIBlockCommandsDeviceReserved, ErrorExit );
bzero ( fIOSCSIBlockCommandsDeviceReserved,
sizeof ( IOSCSIBlockCommandsDeviceExpansionData ) );
if ( getProperty ( kIOPropertySCSIDeviceCharacteristicsKey ) != NULL )
{
OSDictionary * characterDict = NULL;
characterDict = OSDynamicCast (
OSDictionary,
getProperty ( kIOPropertySCSIDeviceCharacteristicsKey ) );
if ( characterDict->getObject ( kIOPropertySCSIManualEjectKey ) != NULL )
{
STATUS_LOG ( ( "%s: found a Manual Eject property.\n", getName ( ) ) );
fKnownManualEject = true;
}
}
require ( ClearNotReadyStatus ( ), ReleaseReservedMemory );
setupSuccessful = DetermineDeviceCharacteristics ( );
if ( setupSuccessful == true )
{
fPollingMode = kPollingMode_NewMedia;
fPollingThread = thread_call_allocate (
( thread_call_func_t ) IOSCSIBlockCommandsDevice::sProcessPoll,
( thread_call_param_t ) this );
require_nonzero_action_string ( fPollingThread,
ErrorExit,
setupSuccessful = false,
"fPollingThread allocation failed.\n" );
InitializePowerManagement ( GetProtocolDriver ( ) );
}
STATUS_LOG ( ( "%s::%s setupSuccessful = %d\n", getName ( ),
__FUNCTION__, setupSuccessful ) );
return setupSuccessful;
ReleaseReservedMemory:
require_nonzero_quiet ( fIOSCSIBlockCommandsDeviceReserved, ErrorExit );
IOFree ( fIOSCSIBlockCommandsDeviceReserved,
sizeof ( IOSCSIBlockCommandsDeviceExpansionData ) );
fIOSCSIBlockCommandsDeviceReserved = NULL;
ErrorExit:
return setupSuccessful;
}
void
IOSCSIBlockCommandsDevice::StartDeviceSupport ( void )
{
OSBoolean * shouldNotPoll = NULL;
shouldNotPoll = OSDynamicCast (
OSBoolean,
getProperty ( kAppleKeySwitchProperty ) );
if ( shouldNotPoll != NULL )
{
require ( shouldNotPoll->isFalse ( ), Exit );
}
EnablePolling ( );
Exit:
CreateStorageServiceNub ( );
}
void
IOSCSIBlockCommandsDevice::SuspendDeviceSupport ( void )
{
if ( fPollingMode != kPollingMode_Suspended )
{
DisablePolling ( );
}
}
void
IOSCSIBlockCommandsDevice::ResumeDeviceSupport ( void )
{
if ( fMediumPresent == false )
{
fPollingMode = kPollingMode_NewMedia;
EnablePolling ( );
}
}
void
IOSCSIBlockCommandsDevice::StopDeviceSupport ( void )
{
DisablePolling ( );
}
void
IOSCSIBlockCommandsDevice::TerminateDeviceSupport ( void )
{
if ( fPollingThread != NULL )
{
thread_call_free ( fPollingThread );
fPollingThread = NULL;
}
if ( fPowerDownNotifier != NULL )
{
fPowerDownNotifier->remove ( );
fPowerDownNotifier = NULL;
}
}
bool
IOSCSIBlockCommandsDevice::CreateCommandSetObjects ( void )
{
bool result = false;
fSCSIBlockCommandObject =
SCSIBlockCommands::CreateSCSIBlockCommandObject ( );
require_nonzero ( fSCSIBlockCommandObject, ErrorExit );
result = true;
ErrorExit:
return result;
}
void
IOSCSIBlockCommandsDevice::FreeCommandSetObjects ( void )
{
if ( fSCSIBlockCommandObject != NULL )
{
fSCSIBlockCommandObject->release ( );
fSCSIBlockCommandObject = NULL;
}
if ( fIOSCSIBlockCommandsDeviceReserved != NULL )
{
IOFree ( fIOSCSIBlockCommandsDeviceReserved,
sizeof ( IOSCSIBlockCommandsDeviceExpansionData ) );
fIOSCSIBlockCommandsDeviceReserved = NULL;
}
}
SCSIBlockCommands *
IOSCSIBlockCommandsDevice::GetSCSIBlockCommandObject ( void )
{
check ( fSCSIBlockCommandObject );
return fSCSIBlockCommandObject;
}
SCSIPrimaryCommands *
IOSCSIBlockCommandsDevice::GetSCSIPrimaryCommandObject ( void )
{
check ( fSCSIBlockCommandObject );
return OSDynamicCast ( SCSIPrimaryCommands,
GetSCSIBlockCommandObject ( ) );
}
bool
IOSCSIBlockCommandsDevice::ClearNotReadyStatus ( void )
{
SCSI_Sense_Data senseBuffer = { 0 };
IOMemoryDescriptor * bufferDesc = NULL;
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
SCSITaskIdentifier request = NULL;
bool driveReady = false;
bool result = true;
STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) );
bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) &senseBuffer,
kSenseDefaultSize,
kIODirectionIn );
check ( bufferDesc );
request = GetSCSITask ( );
check ( request );
do
{
if ( TEST_UNIT_READY ( request, 0 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
if ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE )
{
bool validSense = false;
if ( GetTaskStatus ( request ) == kSCSITaskStatus_CHECK_CONDITION )
{
validSense = GetAutoSenseData ( request, &senseBuffer );
if ( validSense == false )
{
if ( REQUEST_SENSE ( request, bufferDesc, kSenseDefaultSize, 0 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
if ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE )
{
validSense = true;
}
}
if ( validSense == true )
{
if ( ( ( senseBuffer.SENSE_KEY & kSENSE_KEY_Mask ) == kSENSE_KEY_NOT_READY ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE == 0x04 ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x01 ) )
{
STATUS_LOG ( ( "%s::drive not ready\n", getName ( ) ) );
driveReady = false;
IOSleep ( 200 );
}
else if ( ( ( senseBuffer.SENSE_KEY & kSENSE_KEY_Mask ) == kSENSE_KEY_NOT_READY ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE == 0x04 ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x02 ) )
{
if ( START_STOP_UNIT ( request, 0x00, 0x00, 0x00, 0x01, 0x00 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
}
else
{
driveReady = true;
STATUS_LOG ( ( "%s::drive READY\n", getName ( ) ) );
}
STATUS_LOG ( ( "sense data: %01x, %02x, %02x\n",
( senseBuffer.SENSE_KEY & kSENSE_KEY_Mask ),
senseBuffer.ADDITIONAL_SENSE_CODE,
senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ) );
}
}
else
{
driveReady = true;
}
}
else
{
IOSleep ( 200 );
}
} while ( ( driveReady == false ) && ( isInactive ( ) == false ) );
bufferDesc->release ( );
bufferDesc = NULL;
ReleaseSCSITask ( request );
result = isInactive ( ) ? false : true;
return result;
}
void
IOSCSIBlockCommandsDevice::EnablePolling ( void )
{
AbsoluteTime time;
require ( ( isInactive ( ) == false ), Exit );
require ( fPollingThread, Exit );
require ( ( fPollingMode != kPollingMode_Suspended ), Exit );
retain ( );
clock_interval_to_deadline ( 1000, kMillisecondScale, &time );
thread_call_enter_delayed ( fPollingThread, time );
Exit:
return;
}
void
IOSCSIBlockCommandsDevice::DisablePolling ( void )
{
fPollingMode = kPollingMode_Suspended;
require ( thread_call_cancel ( fPollingThread ), Exit );
release ( );
Exit:
return;
}
bool
IOSCSIBlockCommandsDevice::DetermineDeviceCharacteristics ( void )
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
SCSITaskIdentifier request = NULL;
IOBufferMemoryDescriptor * buffer = NULL;
SCSICmd_INQUIRY_StandardData * inquiryBuffer = NULL;
UInt8 inquiryBufferSize = 0;
UInt8 loop = 0;
bool succeeded = false;
STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) );
if ( fDefaultInquiryCount == 0 )
{
STATUS_LOG ( ( "%s: use sizeof(SCSICmd_INQUIRY_StandardData) for Inquiry.\n", getName ( ) ) );
inquiryBufferSize = sizeof ( SCSICmd_INQUIRY_StandardData );
}
else
{
STATUS_LOG ( ( "%s: use fDefaultInquiryCount for Inquiry.\n", getName ( ) ) );
inquiryBufferSize = fDefaultInquiryCount;
}
buffer = IOBufferMemoryDescriptor::withCapacity ( inquiryBufferSize, kIODirectionIn );
require_nonzero_string ( buffer, ErrorExit,
"Couldn't allocate INQUIRY memory descriptor" );
inquiryBuffer = ( SCSICmd_INQUIRY_StandardData * ) buffer->getBytesNoCopy ( );
require_nonzero_string ( inquiryBuffer, ReleaseDescriptor,
"Couldn't allocate INQUIRY buffer" );
request = GetSCSITask ( );
require_nonzero ( request, ReleaseDescriptor );
for ( loop = 0; ( ( loop < kMaxRetryCount ) && ( isInactive ( ) == false ) ); loop++ )
{
if ( INQUIRY ( request,
buffer,
0,
0,
0x00,
inquiryBufferSize,
0 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
succeeded = true;
break;
}
}
require ( succeeded, ReleaseTask );
SetANSIVersion ( inquiryBuffer->VERSION & kINQUIRY_ANSI_VERSION_Mask );
fANSIVersion = GetANSIVersion ( );
if ( ( inquiryBuffer->RMB & kINQUIRY_PERIPHERAL_RMB_BitMask ) ==
kINQUIRY_PERIPHERAL_RMB_MediumRemovable )
{
STATUS_LOG ( ( "Media is removable\n" ) );
fMediaIsRemovable = true;
}
else
{
STATUS_LOG ( ( "Media is NOT removable\n" ) );
fMediaIsRemovable = false;
}
buffer->release ( );
buffer = NULL;
require ( ( fMediaIsRemovable == false ), ReleaseTask );
buffer = IOBufferMemoryDescriptor::withCapacity ( kCachingModePageSize,
kIODirectionOutIn );
require_nonzero ( buffer, ReleaseTask );
if ( MODE_SENSE_6 ( request,
buffer,
0x00,
kModePageControlSavedValues,
kCachingModePageCode,
kCachingModePageSize,
0x00 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
UInt8 * cachePage = ( UInt8 * ) buffer->getBytesNoCopy ( );
UInt8 blockDescriptorLength = 0;
bool WCEBit = false;
blockDescriptorLength = cachePage[3];
cachePage[blockDescriptorLength + kModeSense6ParameterHeaderSize] = kCachingModePageCode;
WCEBit = cachePage[blockDescriptorLength + 6] & kWriteCacheEnabledMask;
if ( MODE_SELECT_6 ( request,
buffer,
0x01, 0x00, cachePage[0] + sizeof ( UInt8 ),
0x00 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
if ( WCEBit == true )
{
fWriteCacheEnabled = true;
}
}
}
}
ReleaseTask:
require_nonzero_quiet ( request, ReleaseDescriptor );
ReleaseSCSITask ( request );
request = NULL;
ReleaseDescriptor:
require_nonzero_quiet ( buffer, ErrorExit );
buffer->release ( );
buffer = NULL;
ErrorExit:
STATUS_LOG ( ( "%s::%s succeeded = %d\n", getName ( ), __FUNCTION__, succeeded ) );
return succeeded;
}
void
IOSCSIBlockCommandsDevice::SetMediumCharacteristics (
UInt32 blockSize,
UInt32 blockCount )
{
STATUS_LOG ( ( "mediumBlockSize = %ld, blockCount = %ld\n",
blockSize, blockCount ) );
fMediumBlockSize = blockSize;
fMediumBlockCount = blockCount;
}
void
IOSCSIBlockCommandsDevice::ResetMediumCharacteristics ( void )
{
fMediumBlockSize = 0;
fMediumBlockCount = 0;
fMediumPresent = false;
}
void
IOSCSIBlockCommandsDevice::CreateStorageServiceNub ( void )
{
IOService * nub = NULL;
nub = new IOBlockStorageServices;
require_nonzero ( nub, ErrorExit );
nub->init ( );
require ( nub->attach ( this ), ErrorExit );
nub->registerService ( );
nub->release ( );
return;
ErrorExit:
PANIC_NOW ( ( "IOSCSIBlockCommandsDevice::CreateStorageServiceNub failed" ) );
return;
}
void
IOSCSIBlockCommandsDevice::ProcessPoll ( void )
{
switch ( fPollingMode )
{
case kPollingMode_NewMedia:
{
PollForNewMedia ( );
}
break;
case kPollingMode_MediaRemoval:
{
PollForMediaRemoval ( );
}
break;
default:
{
ERROR_LOG ( ( "%s:ProcessPoll Unknown polling mode.\n", getName ( ) ) );
}
break;
}
}
void
IOSCSIBlockCommandsDevice::PollForNewMedia ( void )
{
bool mediaFound = false;
UInt64 blockCount = 0;
UInt64 blockSize = 0;
fMediumPresent = false;
mediaFound = DetermineMediaPresence ( );
require_quiet ( mediaFound, Exit );
if ( fMediaIsRemovable == true )
{
fMediumRemovalPrevented = PreventMediumRemoval ( );
}
else
{
fMediumRemovalPrevented = true;
}
require ( DetermineMediumCapacity ( &blockSize, &blockCount ), Exit );
SetMediumCharacteristics ( blockSize, blockCount );
fMediumIsWriteProtected = DetermineMediumWriteProtectState ( );
fMediumPresent = true;
fPollingMode = kPollingMode_Suspended;
SetMediumIcon ( );
messageClients ( kIOMessageMediaStateHasChanged,
( void * ) kIOMediaStateOnline,
0 );
if ( fMediumRemovalPrevented == false )
{
fPollingMode = kPollingMode_MediaRemoval;
}
Exit:
return;
}
bool
IOSCSIBlockCommandsDevice::DetermineMediaPresence ( void )
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
SCSITaskIdentifier request = NULL;
bool mediaFound = false;
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
if ( TEST_UNIT_READY ( request, 0 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
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 );
if( validSense == false )
{
bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) &senseBuffer,
kSenseDefaultSize,
kIODirectionIn );
require_nonzero ( bufferDesc, ReleaseTask );
if ( REQUEST_SENSE ( request, bufferDesc, kSenseDefaultSize, 0 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
bufferDesc->release ( );
bufferDesc = NULL;
if ( ( serviceResponse != kSCSIServiceResponse_TASK_COMPLETE ) ||
( GetTaskStatus ( request ) != kSCSITaskStatus_GOOD ) )
{
ERROR_LOG ( ( "%s: REQUEST_SENSE failed\n", getName ( ) ) );
goto ReleaseTask;
}
}
if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x00 ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x00 ) )
{
STATUS_LOG ( ( "Media found\n" ) );
mediaFound = true;
}
else
{
ERROR_LOG ( ( "ASC = 0x%02x, ASCQ = 0x%02x\n",
senseBuffer.ADDITIONAL_SENSE_CODE,
senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ) );
}
}
else
{
STATUS_LOG ( ( "Media found\n" ) );
mediaFound = true;
}
}
ReleaseTask:
ReleaseSCSITask ( request );
ErrorExit:
return mediaFound;
}
bool
IOSCSIBlockCommandsDevice::PreventMediumRemoval ( void )
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
SCSITaskIdentifier request = NULL;
bool mediumLocked = false;
require ( ( fKnownManualEject == false ), Exit );
request = GetSCSITask ( );
require_nonzero ( request, Exit );
if ( PREVENT_ALLOW_MEDIUM_REMOVAL ( request, kMediaStateLocked, 0 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
mediumLocked = true;
}
ReleaseSCSITask ( request );
Exit:
return mediumLocked;
}
bool
IOSCSIBlockCommandsDevice::DetermineMediumCapacity (
UInt64 * blockSize,
UInt64 * blockCount )
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
UInt32 capacityData[2] = { 0 };
IOMemoryDescriptor * bufferDesc = NULL;
SCSITaskIdentifier request = NULL;
bool result = false;
*blockSize = 0;
*blockCount = 0;
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
bufferDesc = IOMemoryDescriptor::withAddress ( capacityData,
kCapacityDataBufferSize,
kIODirectionIn );
require_nonzero ( bufferDesc, ReleaseTask );
if ( READ_CAPACITY ( request, bufferDesc, 0, 0x00, 0, 0 ) == true )
{
serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
*blockSize = ( UInt64 ) OSSwapBigToHostInt32 ( capacityData[1] );
*blockCount = ( UInt64 ) ( OSSwapBigToHostInt32 ( capacityData[0] ) + 1 );
STATUS_LOG ( ( "%s: Media capacity: %x and block size: %x\n",
getName ( ), ( UInt32 ) *blockCount, ( UInt32 ) *blockSize ) );
result = true;
}
bufferDesc->release ( );
bufferDesc = NULL;
ReleaseTask:
require_nonzero_quiet ( request, ErrorExit );
ReleaseSCSITask ( request );
request = NULL;
ErrorExit:
return result;
}
bool
IOSCSIBlockCommandsDevice::DetermineMediumWriteProtectState ( void )
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
IOMemoryDescriptor * bufferDesc = NULL;
SCSITaskIdentifier request = NULL;
bool writeProtectDetermined = false;
bool mediumIsProtected = true;
SCSI_Sense_Data senseBuffer = { 0 };
UInt8 modeBuffer[16] = { 0 };
require_action_quiet ( fMediaIsRemovable, Exit, mediumIsProtected = false );
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) &senseBuffer,
kSenseDefaultSize,
kIODirectionIn );
check ( bufferDesc );
if ( bufferDesc != NULL )
{
if ( REQUEST_SENSE ( request, bufferDesc, kSenseDefaultSize, 0 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
bufferDesc->release ( );
bufferDesc = NULL;
}
bufferDesc = IOMemoryDescriptor::withAddress ( modeBuffer,
8,
kIODirectionIn );
require_nonzero ( bufferDesc, ReleaseTask );
if ( GetANSIVersion ( ) == kINQUIRY_ANSI_VERSION_NoClaimedConformance )
{
if ( MODE_SENSE_10 ( request,
bufferDesc,
0x00,
0x00,
0x00, 0x3F, 8, 0 ) == true )
{
serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
STATUS_LOG ( ( "%s: Returned Mode sense data: ", getName ( ) ) );
#if SCSI_SBC_DEVICE_DEBUGGING_LEVEL >= 3
for ( UInt32 i = 0; i < 8; i++ )
{
STATUS_LOG ( ( "%x: ", modeBuffer[i] ) );
}
STATUS_LOG ( ( "\n" ) );
#endif // SCSI_SBC_DEVICE_DEBUGGING_LEVEL >= 3
if ( ( modeBuffer[3] & kWriteProtectMask ) == 0 )
{
mediumIsProtected = false;
}
writeProtectDetermined = true;
}
}
if ( writeProtectDetermined == false )
{
if ( MODE_SENSE_6 ( request,
bufferDesc,
0x00,
0x00, 0x3F, 8, 0 ) == true )
{
serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
STATUS_LOG ( ( "%s: Returned Mode sense data: ", getName ( ) ) );
#if SCSI_SBC_DEVICE_DEBUGGING_LEVEL
for ( UInt32 i = 0;i < 8; i++ )
{
STATUS_LOG ( ( "%x: ", modeBuffer[i] ) );
}
STATUS_LOG ( ( "\n" ) );
#endif
if ( ( modeBuffer[2] & kWriteProtectMask ) == 0 )
{
mediumIsProtected = false;
}
}
else
{
ERROR_LOG ( ( "%s: Mode Sense failed\n", getName ( ) ) );
mediumIsProtected = true;
}
}
bufferDesc->release ( );
bufferDesc = NULL;
ReleaseTask:
require_nonzero_quiet ( request, ErrorExit );
ReleaseSCSITask ( request );
request = NULL;
ErrorExit:
Exit:
return mediumIsProtected;
}
void
IOSCSIBlockCommandsDevice::SetMediumIcon ( void )
{
STATUS_LOG ( ( "IOSCSIBlockCommandsDevice::SetMediumIcon called\n" ) );
if ( getProperty ( kIOMediaIconKey, gIOServicePlane ) == NULL )
{
OSDictionary * dict = NULL;
STATUS_LOG ( ( "No current icon key\n" ) );
dict = GetProtocolCharacteristicsDictionary ( );
if ( dict != NULL )
{
OSString * protocolString = NULL;
STATUS_LOG ( ( "Got Protocol Characteristics Dictionary\n" ) );
protocolString = OSDynamicCast ( OSString, dict->getObject ( kIOPropertyPhysicalInterconnectTypeKey ) );
if ( protocolString != NULL )
{
const char * protocol = NULL;
STATUS_LOG ( ( "Got Protocol string\n" ) );
protocol = protocolString->getCStringNoCopy ( );
if ( protocol != NULL )
{
OSString * identifier = NULL;
OSString * resourceFile = NULL;
STATUS_LOG ( ( "Protocol = %s\n", protocol ) );
identifier = OSString::withCString ( kIOSCSIArchitectureBundleIdentifierKey );
dict = OSDictionary::withCapacity ( 2 );
if ( fMediaIsRemovable == false )
{
if ( strcmp ( protocol, "FireWire" ) == 0 )
{
resourceFile = OSString::withCString ( kFireWireHDIconKey );
}
if ( strcmp ( protocol, "USB" ) == 0 )
{
resourceFile = OSString::withCString ( kUSBHDIconKey );
}
}
if ( resourceFile != NULL )
{
STATUS_LOG ( ( "Resource file is non-NULL\n" ) );
if ( ( dict != NULL ) && ( identifier != NULL ) )
{
STATUS_LOG ( ( "Setting keys\n" ) );
dict->setObject ( kCFBundleIdentifierKey, identifier );
dict->setObject ( kIOBundleResourceFileKey, resourceFile );
setProperty ( kIOMediaIconKey, dict );
}
resourceFile->release ( );
}
if ( dict != NULL )
{
dict->release ( );
dict = NULL;
}
if ( identifier != NULL )
{
identifier->release ( );
identifier = NULL;
}
}
}
}
}
}
void
IOSCSIBlockCommandsDevice::PollForMediaRemoval ( void )
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
SCSITaskIdentifier request = NULL;
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
if ( TEST_UNIT_READY ( request, 0 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
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 );
if ( validSense == false )
{
bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) &senseBuffer,
kSenseDefaultSize,
kIODirectionIn );
require_nonzero ( bufferDesc, ReleaseTask );
if ( REQUEST_SENSE ( request, bufferDesc, kSenseDefaultSize, 0 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
bufferDesc->release ( );
bufferDesc = NULL;
require ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ), ReleaseTask );
require ( ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ), ReleaseTask );
}
if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x3A ) ||
( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x28 ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x00 ) ) )
{
ERROR_LOG ( ( "Media was removed. Tearing down the media object." ) );
fPollingMode = kPollingMode_NewMedia;
messageClients ( kIOMessageMediaStateHasChanged,
( void * ) kIOMediaStateOffline );
ResetMediumCharacteristics ( );
EnablePolling ( );
}
}
}
ReleaseTask:
require_nonzero_quiet ( request, ErrorExit );
ReleaseSCSITask ( request );
request = NULL;
ErrorExit:
return;
}
IOReturn
IOSCSIBlockCommandsDevice::IssueRead (
IOMemoryDescriptor * buffer,
UInt64 startBlock,
UInt64 blockCount )
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
SCSITaskIdentifier request = NULL;
IOReturn status = kIOReturnNoResources;
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
if ( READ_10 ( request,
buffer,
fMediumBlockSize,
0,
0,
0,
( SCSICmdField4Byte ) startBlock,
( SCSICmdField2Byte ) blockCount,
0 ) == true )
{
serviceResponse = SendCommand ( request, 0 );
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
status = kIOReturnSuccess;
}
else
{
status = kIOReturnIOError;
}
}
else
{
status = kIOReturnBadArgument;
}
ReleaseSCSITask ( request );
ErrorExit:
return status;
}
IOReturn
IOSCSIBlockCommandsDevice::IssueRead (
IOMemoryDescriptor * buffer,
UInt64 startBlock,
UInt64 blockCount,
void * clientData )
{
IOReturn status = kIOReturnNoResources;
SCSITaskIdentifier request = NULL;
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
if ( READ_10 ( request,
buffer,
fMediumBlockSize,
0,
0,
0,
( SCSICmdField4Byte ) startBlock,
( SCSICmdField2Byte ) blockCount,
0 ) == true )
{
SetApplicationLayerReference ( request, clientData );
STATUS_LOG ( ( "IOSCSIBlockCommandsDevice::IssueRead send command.\n" ) );
SendCommand ( request,
0,
&IOSCSIBlockCommandsDevice::AsyncReadWriteComplete );
status = kIOReturnSuccess;
}
else
{
status = kIOReturnBadArgument;
}
ErrorExit:
return status;
}
IOReturn
IOSCSIBlockCommandsDevice::IssueWrite ( IOMemoryDescriptor * buffer,
UInt64 startBlock,
UInt64 blockCount )
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
SCSITaskIdentifier request = NULL;
IOReturn status = kIOReturnNoResources;
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
if ( WRITE_10 ( request,
buffer,
fMediumBlockSize,
0,
0,
0,
0,
( SCSICmdField4Byte ) startBlock,
( SCSICmdField2Byte ) blockCount,
0 ) == true )
{
serviceResponse = SendCommand ( request, 0 );
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
status = kIOReturnSuccess;
}
else
{
status = kIOReturnIOError;
}
}
else
{
status = kIOReturnBadArgument;
}
ReleaseSCSITask ( request );
ErrorExit:
return status;
}
IOReturn
IOSCSIBlockCommandsDevice::IssueWrite (
IOMemoryDescriptor * buffer,
UInt64 startBlock,
UInt64 blockCount,
void * clientData )
{
IOReturn status = kIOReturnNoResources;
SCSITaskIdentifier request = NULL;
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
if ( WRITE_10 ( request,
buffer,
fMediumBlockSize,
0,
0,
0,
0,
( SCSICmdField4Byte ) startBlock,
( SCSICmdField2Byte ) blockCount,
0 ) == true )
{
SetApplicationLayerReference ( request, clientData );
SendCommand ( request,
0,
&IOSCSIBlockCommandsDevice::AsyncReadWriteComplete );
status = kIOReturnSuccess;
}
else
{
status = kIOReturnBadArgument;
}
ErrorExit:
return status;
}
#if 0
#pragma mark -
#pragma mark ₯ Static Methods
#pragma mark -
#endif
void
IOSCSIBlockCommandsDevice::AsyncReadWriteComplete (
SCSITaskIdentifier request )
{
void * clientData = NULL;
IOSCSIBlockCommandsDevice * taskOwner = NULL;
SCSITask * task = NULL;
IOReturn status = kIOReturnIOError;
UInt64 actCount = 0;
task = OSDynamicCast ( SCSITask, request );
require_nonzero ( task, FatalError );
taskOwner = OSDynamicCast ( IOSCSIBlockCommandsDevice,
task->GetTaskOwner ( ) );
require_nonzero ( taskOwner, FatalError );
clientData = task->GetApplicationLayerReference ( );
require_nonzero ( clientData, FatalError );
if ( ( task->GetServiceResponse ( ) == kSCSIServiceResponse_TASK_COMPLETE ) &&
( task->GetTaskStatus ( ) == kSCSITaskStatus_GOOD ) )
{
status = kIOReturnSuccess;
actCount = task->GetRealizedDataTransferCount ( );
}
else
{
status = kIOReturnIOError;
if ( task->GetTaskStatus ( ) == kSCSITaskStatus_CHECK_CONDITION )
{
SCSI_Sense_Data senseDataBuffer;
bool senseIsValid;
senseIsValid = task->GetAutoSenseData ( &senseDataBuffer, sizeof ( senseDataBuffer ) );
if ( senseIsValid )
{
ERROR_LOG ( ( "READ or WRITE failed, ASC = 0x%02x, ASCQ = 0x%02x\n",
senseDataBuffer.ADDITIONAL_SENSE_CODE,
senseDataBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ) );
}
}
}
taskOwner->ReleaseSCSITask ( request );
IOBlockStorageServices::AsyncReadWriteComplete ( clientData, status, actCount );
return;
FatalError:
IOPanic ( "SAM SBC: error completing I/O due to bad completion data" );
return;
}
void
IOSCSIBlockCommandsDevice::sProcessPoll ( void * pdtDriver, void * refCon )
{
IOSCSIBlockCommandsDevice * driver = NULL;
driver = ( IOSCSIBlockCommandsDevice * ) pdtDriver;
require_nonzero ( driver, ErrorExit );
driver->ProcessPoll ( );
if ( driver->fPollingMode != kPollingMode_Suspended )
{
driver->EnablePolling ( );
}
driver->release ( );
ErrorExit:
return;
}
#if 0
#pragma mark -
#pragma mark ₯ VTable Padding
#pragma mark -
#endif
OSMetaClassDefineReservedUsed ( IOSCSIBlockCommandsDevice, 1 );
OSMetaClassDefineReservedUsed ( IOSCSIBlockCommandsDevice, 2 );
OSMetaClassDefineReservedUnused ( IOSCSIBlockCommandsDevice, 3 );
OSMetaClassDefineReservedUnused ( IOSCSIBlockCommandsDevice, 4 );
OSMetaClassDefineReservedUnused ( IOSCSIBlockCommandsDevice, 5 );
OSMetaClassDefineReservedUnused ( IOSCSIBlockCommandsDevice, 6 );
OSMetaClassDefineReservedUnused ( IOSCSIBlockCommandsDevice, 7 );
OSMetaClassDefineReservedUnused ( IOSCSIBlockCommandsDevice, 8 );
OSMetaClassDefineReservedUnused ( IOSCSIBlockCommandsDevice, 9 );
OSMetaClassDefineReservedUnused ( IOSCSIBlockCommandsDevice, 10 );
OSMetaClassDefineReservedUnused ( IOSCSIBlockCommandsDevice, 11 );
OSMetaClassDefineReservedUnused ( IOSCSIBlockCommandsDevice, 12 );
OSMetaClassDefineReservedUnused ( IOSCSIBlockCommandsDevice, 13 );
OSMetaClassDefineReservedUnused ( IOSCSIBlockCommandsDevice, 14 );
OSMetaClassDefineReservedUnused ( IOSCSIBlockCommandsDevice, 15 );
OSMetaClassDefineReservedUnused ( IOSCSIBlockCommandsDevice, 16 );