IOSCSIReducedBlockCommandsDevice.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <IOKit/scsi-commands/SCSICommandDefinitions.h>
#include <IOKit/scsi-commands/IOReducedBlockServices.h>
#include <IOKit/scsi-commands/IOSCSIReducedBlockCommandsDevice.h>
#include "SCSIReducedBlockCommands.h"
#define DEBUG 0
#define DEBUG_ASSERT_COMPONENT_NAME_STRING "RBC"
#if DEBUG
#define SCSI_RBC_DEVICE_DEBUGGING_LEVEL 0
#endif
#include "IOSCSIArchitectureModelFamilyDebugging.h"
#if ( SCSI_RBC_DEVICE_DEBUGGING_LEVEL >= 1 )
#define PANIC_NOW(x) IOPanic x
#else
#define PANIC_NOW(x)
#endif
#if ( SCSI_RBC_DEVICE_DEBUGGING_LEVEL >= 2 )
#define ERROR_LOG(x) IOLog x
#else
#define ERROR_LOG(x)
#endif
#if ( SCSI_RBC_DEVICE_DEBUGGING_LEVEL >= 3 )
#define STATUS_LOG(x) IOLog x
#else
#define STATUS_LOG(x)
#endif
#define super IOSCSIPrimaryCommandsDevice
OSDefineMetaClass ( IOSCSIReducedBlockCommandsDevice, IOSCSIPrimaryCommandsDevice );
OSDefineAbstractStructors ( IOSCSIReducedBlockCommandsDevice, IOSCSIPrimaryCommandsDevice );
#define kMaxRetryCount 8
#define kCapacityDataBufferSize 8
#define kModeSenseWriteProtectBufferSize 17
#define kWriteProtectMask 0x04
#define kAppleKeySwitchProperty "AppleKeyswitch"
#define kFibreChannelHDIconKey "FibreChannleHD.icns"
#define kFireWireHDIconKey "FireWireHD.icns"
#define kUSBHDIconKey "USBHD.icns"
#define kDefaultMaxBlocksPerIO 65535
#if 0
#pragma mark -
#pragma mark ₯ Public Methods - API Exported to layers above
#pragma mark -
#endif
IOReturn
IOSCSIReducedBlockCommandsDevice::SyncReadWrite (
IOMemoryDescriptor * buffer,
UInt64 startBlock,
UInt64 blockCount )
{
IODirection direction;
IOReturn status = kIOReturnBadArgument;
direction = buffer->getDirection ( );
if ( direction == kIODirectionIn )
{
status = IssueRead ( buffer, startBlock, blockCount );
}
else if ( direction == kIODirectionOut )
{
status = IssueWrite ( buffer, startBlock, blockCount );
}
return status;
}
IOReturn
IOSCSIReducedBlockCommandsDevice::AsyncReadWrite (
IOMemoryDescriptor * buffer,
UInt64 startBlock,
UInt64 blockCount,
void * clientData )
{
IODirection direction;
IOReturn status = kIOReturnBadArgument;
direction = buffer->getDirection ( );
if ( direction == kIODirectionIn )
{
status = IssueRead ( buffer, startBlock, blockCount, clientData );
}
else if ( direction == kIODirectionOut )
{
status = IssueWrite ( buffer, startBlock, blockCount, clientData );
}
return status;
}
IOReturn
IOSCSIReducedBlockCommandsDevice::EjectTheMedia ( void )
{
SCSITaskIdentifier request = NULL;
IOReturn status = kIOReturnNoResources;
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 ( kRBCPowerStateSleep );
}
else
{
if ( fKnownManualEject == false )
{
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
if ( PREVENT_ALLOW_MEDIUM_REMOVAL ( request, kMediaStateUnlocked ) == true )
{
( void ) SendCommand ( request, kTenSecondTimeoutInMS );
}
if ( START_STOP_UNIT ( request, 0, 0, 1, 0 ) == true )
{
SCSIServiceResponse serviceResponse;
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) != kSCSITaskStatus_GOOD ) )
{
doPollForRemoval = true;
}
}
ReleaseSCSITask ( request );
request = NULL;
}
else
{
doPollForRemoval = true;
}
ResetMediaCharacteristics ( );
fMediaIsWriteProtected = true;
if ( ( doPollForRemoval == true ) || ( fMediumRemovalPrevented == false ) )
{
fPollingMode = kPollingMode_MediaRemoval;
}
else
{
fPollingMode = kPollingMode_NewMedia;
}
EnablePolling ( );
}
status = kIOReturnSuccess;
ErrorExit:
return status;
}
IOReturn
IOSCSIReducedBlockCommandsDevice::FormatMedia ( UInt64 byteCapacity )
{
IOReturn status = kIOReturnUnsupported;
return status;
}
UInt32
IOSCSIReducedBlockCommandsDevice::GetFormatCapacities (
UInt64 * capacities,
UInt32 capacitiesMaxCount ) const
{
return 0;
}
IOReturn
IOSCSIReducedBlockCommandsDevice::LockUnlockMedia ( bool doLock )
{
IOReturn status = kIOReturnSuccess;
require_action ( IsProtocolAccessEnabled ( ), ErrorExit, status = kIOReturnNotAttached );
require_action ( IsProtocolAccessEnabled ( ), ErrorExit, status = kIOReturnOffline );
ErrorExit:
return status;
}
IOReturn
IOSCSIReducedBlockCommandsDevice::SynchronizeCache ( void )
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
SCSITaskIdentifier request = NULL;
IOReturn status = kIOReturnNoResources;
STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) );
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
if ( SYNCHRONIZE_CACHE ( request ) == true )
{
serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
status = kIOReturnSuccess;
}
else
{
status = kIOReturnInternalError;
}
ReleaseSCSITask ( request );
ErrorExit:
return status;
}
IOReturn
IOSCSIReducedBlockCommandsDevice::ReportBlockSize ( UInt64 * blockSize )
{
*blockSize = fMediaBlockSize;
return kIOReturnSuccess;
}
IOReturn
IOSCSIReducedBlockCommandsDevice::ReportEjectability ( bool * isEjectable )
{
*isEjectable = fMediaIsRemovable;
return kIOReturnSuccess;
}
IOReturn
IOSCSIReducedBlockCommandsDevice::ReportLockability ( bool * isLockable )
{
*isLockable = true;
return kIOReturnSuccess;
}
IOReturn
IOSCSIReducedBlockCommandsDevice::ReportPollRequirements (
bool * pollIsRequired,
bool * pollIsExpensive )
{
*pollIsRequired = false;
*pollIsExpensive = false;
return kIOReturnSuccess;
}
IOReturn
IOSCSIReducedBlockCommandsDevice::ReportMaxReadTransfer (
UInt64 blockSize,
UInt64 * max )
{
UInt64 maxBlockCount = kDefaultMaxBlocksPerIO;
UInt64 maxByteCount = 0;
bool supported = false;
STATUS_LOG ( ( "IOSCSIReducedBlockCommandsDevice::ReportMaxReadTransfer.\n" ) );
supported = GetProtocolDriver ( )->IsProtocolServiceSupported (
kSCSIProtocolFeature_MaximumReadBlockTransferCount,
&maxBlockCount );
if ( supported == false )
maxBlockCount = kDefaultMaxBlocksPerIO;
supported = GetProtocolDriver ( )->IsProtocolServiceSupported (
kSCSIProtocolFeature_MaximumReadTransferByteCount,
&maxByteCount );
if ( ( supported == true ) && ( maxByteCount > 0 ) && ( fMediaBlockSize > 0 ) )
{
maxBlockCount = min ( maxBlockCount, ( maxByteCount / fMediaBlockSize ) );
}
*max = maxBlockCount * blockSize;
return kIOReturnSuccess;
}
IOReturn
IOSCSIReducedBlockCommandsDevice::ReportMaxWriteTransfer (
UInt64 blockSize,
UInt64 * max )
{
UInt64 maxBlockCount = kDefaultMaxBlocksPerIO;
UInt64 maxByteCount = 0;
bool supported = false;
STATUS_LOG ( ( "IOSCSIReducedBlockCommandsDevice::ReportMaxWriteTransfer.\n" ) );
supported = GetProtocolDriver ( )->IsProtocolServiceSupported (
kSCSIProtocolFeature_MaximumWriteBlockTransferCount,
&maxBlockCount );
if ( supported == false )
maxBlockCount = kDefaultMaxBlocksPerIO;
supported = GetProtocolDriver ( )->IsProtocolServiceSupported (
kSCSIProtocolFeature_MaximumWriteTransferByteCount,
&maxByteCount );
if ( ( supported == true ) && ( maxByteCount > 0 ) && ( fMediaBlockSize > 0 ) )
{
maxBlockCount = min ( maxBlockCount, ( maxByteCount / fMediaBlockSize ) );
}
*max = maxBlockCount * blockSize;
return kIOReturnSuccess;
}
IOReturn
IOSCSIReducedBlockCommandsDevice::ReportMaxValidBlock ( UInt64 * maxBlock )
{
*maxBlock = fMediaBlockCount - 1;
return kIOReturnSuccess;
}
IOReturn
IOSCSIReducedBlockCommandsDevice::ReportMediaState (
bool * mediaPresent,
bool * changed )
{
*mediaPresent = fMediaPresent;
*changed = fMediaChanged;
if ( fMediaChanged )
{
fMediaChanged = !fMediaChanged;
}
return kIOReturnSuccess;
}
IOReturn
IOSCSIReducedBlockCommandsDevice::ReportRemovability ( bool * isRemovable )
{
*isRemovable = fMediaIsRemovable;
return kIOReturnSuccess;
}
IOReturn
IOSCSIReducedBlockCommandsDevice::ReportWriteProtection (
bool * isWriteProtected )
{
*isWriteProtected = fMediaIsWriteProtected;
return kIOReturnSuccess;
}
#if 0
#pragma mark -
#pragma mark ₯ Protected Methods - Methods used by this class and subclasses
#pragma mark -
#endif
bool
IOSCSIReducedBlockCommandsDevice::InitializeDeviceSupport ( void )
{
bool setupSuccessful = false;
fMediaChanged = false;
fMediaPresent = false;
fMediaIsRemovable = false;
fMediaIsWriteProtected = true;
STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) );
fIOSCSIReducedBlockCommandsDeviceReserved =
( IOSCSIReducedBlockCommandsDeviceExpansionData * )
IOMalloc ( sizeof ( IOSCSIReducedBlockCommandsDeviceExpansionData ) );
require_nonzero ( fIOSCSIReducedBlockCommandsDeviceReserved, ErrorExit );
fMediumRemovalPrevented = false;
fKnownManualEject = false;
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 ) IOSCSIReducedBlockCommandsDevice::sPollForMedia,
( 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 ) );
setProperty ( kIOMaximumBlockCountReadKey, kDefaultMaxBlocksPerIO, 64 );
setProperty ( kIOMaximumBlockCountWriteKey, kDefaultMaxBlocksPerIO, 64 );
return setupSuccessful;
ReleaseReservedMemory:
require_nonzero_quiet ( fIOSCSIReducedBlockCommandsDeviceReserved, ErrorExit );
IOFree ( fIOSCSIReducedBlockCommandsDeviceReserved,
sizeof ( IOSCSIReducedBlockCommandsDeviceExpansionData ) );
fIOSCSIReducedBlockCommandsDeviceReserved = NULL;
ErrorExit:
return setupSuccessful;
}
void
IOSCSIReducedBlockCommandsDevice::StartDeviceSupport ( void )
{
OSBoolean * shouldNotPoll = NULL;
shouldNotPoll = OSDynamicCast (
OSBoolean,
getProperty ( kAppleKeySwitchProperty ) );
if ( shouldNotPoll != NULL )
{
require ( shouldNotPoll->isFalse ( ), Exit );
}
EnablePolling ( );
Exit:
CreateStorageServiceNub ( );
}
void
IOSCSIReducedBlockCommandsDevice::SuspendDeviceSupport ( void )
{
if ( fPollingMode != kPollingMode_Suspended )
{
DisablePolling ( );
}
}
void
IOSCSIReducedBlockCommandsDevice::ResumeDeviceSupport ( void )
{
if ( fMediaPresent == false )
{
fPollingMode = kPollingMode_NewMedia;
EnablePolling ( );
}
}
void
IOSCSIReducedBlockCommandsDevice::StopDeviceSupport ( void )
{
DisablePolling ( );
}
void
IOSCSIReducedBlockCommandsDevice::TerminateDeviceSupport ( void )
{
if ( fPollingThread != NULL )
{
thread_call_free ( fPollingThread );
fPollingThread = NULL;
}
if ( fPowerDownNotifier != NULL )
{
fPowerDownNotifier->remove ( );
fPowerDownNotifier = NULL;
}
}
bool
IOSCSIReducedBlockCommandsDevice::CreateCommandSetObjects ( void )
{
bool result = false;
STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) );
fSCSIReducedBlockCommandObject =
SCSIReducedBlockCommands::CreateSCSIReducedBlockCommandObject ( );
require_nonzero ( fSCSIReducedBlockCommandObject, ErrorExit );
result = true;
ErrorExit:
return result;
}
void
IOSCSIReducedBlockCommandsDevice::FreeCommandSetObjects ( void )
{
if ( fSCSIReducedBlockCommandObject != NULL )
{
fSCSIReducedBlockCommandObject->release ( );
fSCSIReducedBlockCommandObject = NULL;
}
if ( fIOSCSIReducedBlockCommandsDeviceReserved != NULL )
{
IOFree ( fIOSCSIReducedBlockCommandsDeviceReserved,
sizeof ( IOSCSIReducedBlockCommandsDeviceExpansionData ) );
fIOSCSIReducedBlockCommandsDeviceReserved = NULL;
}
}
SCSIReducedBlockCommands *
IOSCSIReducedBlockCommandsDevice::GetSCSIReducedBlockCommandObject ( void )
{
check ( fSCSIReducedBlockCommandObject );
return fSCSIReducedBlockCommandObject;
}
SCSIPrimaryCommands *
IOSCSIReducedBlockCommandsDevice::GetSCSIPrimaryCommandObject ( void )
{
check ( fSCSIReducedBlockCommandObject );
return OSDynamicCast ( SCSIPrimaryCommands,
GetSCSIReducedBlockCommandObject ( ) );
}
bool
IOSCSIReducedBlockCommandsDevice::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 ) == 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 ) == 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 ) == 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 ( );
ReleaseSCSITask ( request );
result = isInactive ( ) ? false : true;
return result;
}
void
IOSCSIReducedBlockCommandsDevice::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
IOSCSIReducedBlockCommandsDevice::DisablePolling ( void )
{
fPollingMode = kPollingMode_Suspended;
require ( thread_call_cancel ( fPollingThread ), Exit );
release ( );
Exit:
return;
}
bool
IOSCSIReducedBlockCommandsDevice::DetermineDeviceCharacteristics ( void )
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
UInt8 inquiryBufferCount = 0;
SCSICmd_INQUIRY_StandardData * inquiryBuffer = NULL;
IOMemoryDescriptor * bufferDesc = NULL;
SCSITaskIdentifier request = NULL;
bool succeeded = false;
UInt8 loop = 0;
STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) );
if ( fDefaultInquiryCount == 0 )
{
STATUS_LOG ( ( "%s: use sizeof(SCSICmd_INQUIRY_StandardData) for Inquiry.\n", getName ( ) ) );
inquiryBufferCount = sizeof ( SCSICmd_INQUIRY_StandardData );
}
else
{
STATUS_LOG ( ( "%s: use fDefaultInquiryCount for Inquiry.\n", getName ( ) ) );
inquiryBufferCount = fDefaultInquiryCount;
}
inquiryBuffer = ( SCSICmd_INQUIRY_StandardData * ) IOMalloc ( inquiryBufferCount );
require_nonzero_string ( inquiryBuffer, ErrorExit,
"Couldn't allocate INQUIRY buffer" );
bufferDesc = IOMemoryDescriptor::withAddress ( inquiryBuffer,
inquiryBufferCount,
kIODirectionIn );
require_nonzero_string ( bufferDesc, ReleaseBuffer,
"Couldn't allocate INQUIRY memory descriptor" );
request = GetSCSITask ( );
require_nonzero ( request, ReleaseDescriptor );
for ( loop = 0; ( ( loop < kMaxRetryCount ) && ( isInactive ( ) == false ) ); loop++ )
{
if ( INQUIRY ( request,
bufferDesc,
0,
0,
0x00,
inquiryBufferCount ) == 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 );
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;
}
ReleaseTask:
require_nonzero ( request, ReleaseDescriptor );
ReleaseSCSITask ( request );
request = NULL;
ReleaseDescriptor:
require_nonzero ( bufferDesc, ReleaseBuffer );
bufferDesc->release ( );
bufferDesc = NULL;
ReleaseBuffer:
require_nonzero ( inquiryBuffer, ErrorExit );
IOFree ( ( void * ) inquiryBuffer, inquiryBufferCount );
inquiryBuffer = NULL;
ErrorExit:
STATUS_LOG ( ( "%s::%s succeeded = %d\n", getName ( ), __FUNCTION__, succeeded ) );
return succeeded;
}
void
IOSCSIReducedBlockCommandsDevice::SetMediaCharacteristics (
UInt32 blockSize,
UInt32 blockCount )
{
UInt64 maxBytesRead = 0;
UInt64 maxBytesWrite = 0;
UInt64 maxBlocksRead = 0;
UInt64 maxBlocksWrite = 0;
STATUS_LOG ( ( "mediaBlockSize = %ld, blockCount = %ld\n",
blockSize, blockCount ) );
fMediaBlockSize = blockSize;
fMediaBlockCount = blockCount;
ReportMaxReadTransfer ( fMediaBlockSize, &maxBytesRead );
ReportMaxWriteTransfer ( fMediaBlockSize, &maxBytesWrite );
if ( fMediaBlockSize > 0 )
{
maxBlocksRead = maxBytesRead / fMediaBlockSize;
maxBlocksWrite = maxBytesWrite / fMediaBlockSize;
setProperty ( kIOMaximumBlockCountReadKey, maxBlocksRead, 64 );
setProperty ( kIOMaximumBlockCountWriteKey, maxBlocksWrite, 64 );
}
}
void
IOSCSIReducedBlockCommandsDevice::ResetMediaCharacteristics ( void )
{
fMediaBlockSize = 0;
fMediaBlockCount = 0;
fMediaPresent = false;
fMediaIsWriteProtected = true;
}
void
IOSCSIReducedBlockCommandsDevice::CreateStorageServiceNub ( void )
{
IOService * nub = NULL;
nub = new IOReducedBlockServices;
require_nonzero ( nub, ErrorExit );
nub->init ( );
require ( nub->attach ( this ), ErrorExit );
nub->registerService ( );
nub->release ( );
return;
ErrorExit:
PANIC_NOW ( ( "IOSCSIReducedBlockCommandsDevice::CreateStorageServiceNub failed" ) );
return;
}
void
IOSCSIReducedBlockCommandsDevice::PollForMedia ( void )
{
IOMemoryDescriptor * bufferDesc = NULL;
SCSITaskIdentifier request = NULL;
switch ( fPollingMode )
{
case kPollingMode_NewMedia:
{
SCSI_Sense_Data senseBuffer = { 0 };
UInt32 capacityData[2] = { 0 };
bool mediaFound = false;
bool validSense = false;
SCSIServiceResponse serviceResponse;
serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) &senseBuffer,
kSenseDefaultSize,
kIODirectionIn );
require_nonzero ( bufferDesc, ErrorExit );
request = GetSCSITask ( );
require_nonzero ( request, ReleaseDescriptor );
if ( TEST_UNIT_READY ( request ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
require ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE, ReleaseTask );
if ( GetTaskStatus ( request ) == kSCSITaskStatus_CHECK_CONDITION )
{
validSense = GetAutoSenseData ( request, &senseBuffer );
if ( validSense == true )
{
if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x00 ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x00 ) )
{
mediaFound = true;
}
}
}
else
{
mediaFound = true;
}
bufferDesc->release ( );
bufferDesc = NULL;
require_quiet ( mediaFound, ReleaseTask );
if ( fMediaIsRemovable == true )
{
fMediumRemovalPrevented = false;
if ( PREVENT_ALLOW_MEDIUM_REMOVAL ( request, kMediaStateLocked ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
fMediumRemovalPrevented = true;
}
}
}
else
{
fMediumRemovalPrevented = true;
}
bufferDesc = IOMemoryDescriptor::withAddress ( capacityData,
kCapacityDataBufferSize,
kIODirectionIn );
require_nonzero ( bufferDesc, ReleaseTask );
if ( READ_CAPACITY ( request, bufferDesc ) == true )
{
serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS );
}
require_string ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ),
ReleaseTask,
"Read Capacity failed\n" );
SetMediaCharacteristics (
OSSwapBigToHostInt32 ( capacityData[1] ),
OSSwapBigToHostInt32 ( capacityData[0] ) + 1 );
STATUS_LOG ( ( "%s: Media capacity: %x and block size: %x\n",
getName ( ), fMediaBlockCount, fMediaBlockSize ) );
CheckWriteProtection ( );
fMediaPresent = true;
fMediaChanged = true;
fPollingMode = kPollingMode_Suspended;
SetMediaIcon ( );
messageClients ( kIOMessageMediaStateHasChanged,
( void * ) kIOMediaStateOnline );
if ( fMediumRemovalPrevented == false )
{
fPollingMode = kPollingMode_MediaRemoval;
}
}
break;
case kPollingMode_MediaRemoval:
{
SCSIServiceResponse serviceResponse;
serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
if ( TEST_UNIT_READY ( request ) == true )
{
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 );
if ( validSense == false )
{
bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) &senseBuffer,
kSenseDefaultSize,
kIODirectionIn );
require_nonzero ( bufferDesc, ReleaseTask );
if ( REQUEST_SENSE ( request, bufferDesc, kSenseDefaultSize ) == 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 );
ResetMediaCharacteristics ( );
EnablePolling ( );
}
}
}
}
break;
default:
{
ERROR_LOG ( ( "%s:ProcessPoll Unknown polling mode.\n", getName ( ) ) );
}
break;
}
ReleaseTask:
require_nonzero_quiet ( request, ReleaseDescriptor );
ReleaseSCSITask ( request );
request = NULL;
ReleaseDescriptor:
require_nonzero_quiet ( bufferDesc, ErrorExit );
bufferDesc->release ( );
bufferDesc = NULL;
ErrorExit:
return;
}
void
IOSCSIReducedBlockCommandsDevice::CheckWriteProtection ( void )
{
UInt8 modeSenseBuffer[kModeSenseWriteProtectBufferSize] = { 0 };
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
IOMemoryDescriptor * bufferDesc = NULL;
SCSITaskIdentifier request = NULL;
SCSICmdField1Bit DBD = 0;
bufferDesc = IOMemoryDescriptor::withAddress ( modeSenseBuffer,
kModeSenseWriteProtectBufferSize,
kIODirectionIn );
require_nonzero ( bufferDesc, ErrorExit );
request = GetSCSITask ( );
require_nonzero ( request, ReleaseDescriptor );
DBD = 1;
Loop:
if ( MODE_SENSE_6 ( request,
bufferDesc,
DBD,
0x00,
0x06,
kModeSenseWriteProtectBufferSize ) == true )
{
serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
STATUS_LOG ( ( "%s: Returned Mode sense data: ", getName ( ) ) );
#if ( SCSI_RBC_DEVICE_DEBUGGING_LEVEL >=3 )
for ( UInt32 i = 0; i < kModeSenseWriteProtectBufferSize; i++ )
{
STATUS_LOG ( ( "%x: ", modeSenseBuffer[i] ) );
}
STATUS_LOG ( ( "\n" ) );
#endif
if ( ( modeSenseBuffer[15] & kWriteProtectMask ) != 0 )
{
fMediaIsWriteProtected = true;
}
else
{
fMediaIsWriteProtected = false;
}
}
else
{
if ( DBD == 1 )
{
DBD = 0;
goto Loop;
}
STATUS_LOG ( ( "%s: Mode Sense failed with service response = %x\n", getName ( ), serviceResponse ) );
fMediaIsWriteProtected = true;
}
ReleaseSCSITask ( request );
ReleaseDescriptor:
require_nonzero ( bufferDesc, ErrorExit );
bufferDesc->release ( );
bufferDesc = NULL;
ErrorExit:
return;
}
void
IOSCSIReducedBlockCommandsDevice::SetMediaIcon ( void )
{
STATUS_LOG ( ( "IOSCSIBlockCommandsDevice::SetMediaIcon 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 ( strcmp ( protocol, "Fibre Channel Interface" ) == 0 )
{
resourceFile = OSString::withCString ( kFibreChannelHDIconKey );
}
}
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;
}
}
}
}
}
}
IOReturn
IOSCSIReducedBlockCommandsDevice::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,
fMediaBlockSize,
( SCSICmdField4Byte ) startBlock,
( SCSICmdField2Byte ) blockCount ) == 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
IOSCSIReducedBlockCommandsDevice::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,
fMediaBlockSize,
( SCSICmdField4Byte ) startBlock,
( SCSICmdField2Byte ) blockCount ) == true )
{
SetApplicationLayerReference ( request, clientData );
SendCommand ( request,
0,
&IOSCSIReducedBlockCommandsDevice::AsyncReadWriteComplete );
status = kIOReturnSuccess;
}
else
{
status = kIOReturnBadArgument;
}
ErrorExit:
return status;
}
IOReturn
IOSCSIReducedBlockCommandsDevice::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,
fMediaBlockSize,
0,
( SCSICmdField4Byte ) startBlock,
( SCSICmdField2Byte ) blockCount ) == 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
IOSCSIReducedBlockCommandsDevice::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,
fMediaBlockSize,
0,
( SCSICmdField4Byte ) startBlock,
( SCSICmdField2Byte ) blockCount ) == true )
{
SetApplicationLayerReference ( request, clientData );
SendCommand ( request,
0,
&IOSCSIReducedBlockCommandsDevice::AsyncReadWriteComplete );
status = kIOReturnSuccess;
}
else
{
status = kIOReturnBadArgument;
}
ErrorExit:
return status;
}
#if 0
#pragma mark -
#pragma mark ₯ Static Methods
#pragma mark -
#endif
void
IOSCSIReducedBlockCommandsDevice::AsyncReadWriteComplete (
SCSITaskIdentifier request )
{
void * clientData = NULL;
IOSCSIReducedBlockCommandsDevice * taskOwner = NULL;
SCSITask * scsiRequest = NULL;
IOReturn status = kIOReturnIOError;
UInt64 actCount = 0;
scsiRequest = OSDynamicCast ( SCSITask, request );
require_nonzero ( scsiRequest, ErrorExit );
taskOwner = OSDynamicCast ( IOSCSIReducedBlockCommandsDevice,
scsiRequest->GetTaskOwner ( ) );
require_nonzero ( taskOwner, ErrorExit );
clientData = scsiRequest->GetApplicationLayerReference ( );
require_nonzero ( clientData, ErrorExit );
if ( ( scsiRequest->GetServiceResponse ( ) == kSCSIServiceResponse_TASK_COMPLETE ) &&
( scsiRequest->GetTaskStatus ( ) == kSCSITaskStatus_GOOD ) )
{
status = kIOReturnSuccess;
actCount = scsiRequest->GetRealizedDataTransferCount ( );
}
else
{
status = kIOReturnIOError;
if ( scsiRequest->GetTaskStatus ( ) == kSCSITaskStatus_CHECK_CONDITION )
{
SCSI_Sense_Data senseDataBuffer;
bool senseIsValid;
senseIsValid = scsiRequest->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 );
IOReducedBlockServices::AsyncReadWriteComplete ( clientData, status, actCount );
return;
ErrorExit:
IOPanic ( "SAM RBC: error completing I/O due to bad completion data" );
return;
}
void
IOSCSIReducedBlockCommandsDevice::sPollForMedia (
void * pdtDriver,
void * refCon )
{
IOSCSIReducedBlockCommandsDevice * driver = NULL;
driver = ( IOSCSIReducedBlockCommandsDevice * ) pdtDriver;
require_nonzero ( driver, ErrorExit );
driver->PollForMedia ( );
if ( driver->fPollingMode != kPollingMode_Suspended )
{
driver->EnablePolling ( );
}
driver->release ( );
ErrorExit:
return;
}
#if 0
#pragma mark -
#pragma mark ₯ VTable Padding
#pragma mark -
#endif
OSMetaClassDefineReservedUsed ( IOSCSIReducedBlockCommandsDevice, 1 );
OSMetaClassDefineReservedUsed ( IOSCSIReducedBlockCommandsDevice, 2 );
OSMetaClassDefineReservedUnused ( IOSCSIReducedBlockCommandsDevice, 3 );
OSMetaClassDefineReservedUnused ( IOSCSIReducedBlockCommandsDevice, 4 );
OSMetaClassDefineReservedUnused ( IOSCSIReducedBlockCommandsDevice, 5 );
OSMetaClassDefineReservedUnused ( IOSCSIReducedBlockCommandsDevice, 6 );
OSMetaClassDefineReservedUnused ( IOSCSIReducedBlockCommandsDevice, 7 );
OSMetaClassDefineReservedUnused ( IOSCSIReducedBlockCommandsDevice, 8 );
OSMetaClassDefineReservedUnused ( IOSCSIReducedBlockCommandsDevice, 9 );
OSMetaClassDefineReservedUnused ( IOSCSIReducedBlockCommandsDevice, 10 );
OSMetaClassDefineReservedUnused ( IOSCSIReducedBlockCommandsDevice, 11 );
OSMetaClassDefineReservedUnused ( IOSCSIReducedBlockCommandsDevice, 12 );
OSMetaClassDefineReservedUnused ( IOSCSIReducedBlockCommandsDevice, 13 );
OSMetaClassDefineReservedUnused ( IOSCSIReducedBlockCommandsDevice, 14 );
OSMetaClassDefineReservedUnused ( IOSCSIReducedBlockCommandsDevice, 15 );
OSMetaClassDefineReservedUnused ( IOSCSIReducedBlockCommandsDevice, 16 );