IOSCSIBlockCommandsDevice.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <libkern/c++/OSDictionary.h>
#include <libkern/c++/OSNumber.h>
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
#include "IOSCSIBlockCommandsDevice.h"
#include "SCSIBlockCommands.h"
#include <IOKit/scsi/SCSICommandDefinitions.h>
#include <IOKit/scsi/SCSICmds_MODE_Definitions.h>
#include <IOKit/scsi/SCSICmds_READ_CAPACITY_Definitions.h>
#include <IOKit/scsi/IOBlockStorageServices.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 kAppleKeySwitchProperty "AppleKeyswitch"
#define kFibreChannelHDIconKey "FibreChannelHD.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
IOSCSIBlockCommandsDevice::SyncReadWrite (
IOMemoryDescriptor * buffer,
UInt64 startBlock,
UInt64 blockCount,
UInt64 blockSize )
{
return kIOReturnUnsupported;
}
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 )
{
if ( getProperty ( "Power Off" ) != NULL )
{
if ( ( fCurrentPowerState > kSBCPowerStateSleep ) && ( fDeviceIsShared == false ) )
{
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
if ( START_STOP_UNIT ( request, 1, 0, 0, 0, 0 ) == true )
{
serviceResponse = SendCommand ( request, 0 );
}
IOSleep ( 500 );
ReleaseSCSITask ( request );
request = NULL;
}
fCurrentPowerState = kSBCPowerStateSleep;
}
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 fMediumBlockCount64;
}
bool
IOSCSIBlockCommandsDevice::ReportMediumWriteProtection ( void )
{
return fMediumIsWriteProtected;
}
UInt64
IOSCSIBlockCommandsDevice::ReportDeviceMaxBlocksReadTransfer ( void )
{
UInt32 maxBlockCount = kDefaultMaxBlocksPerIO;
UInt64 maxByteCount = 0;
bool supported = false;
STATUS_LOG ( ( "IOSCSIBlockCommandsDevice::ReportDeviceMaxBlocksWriteTransfer.\n" ) );
supported = GetProtocolDriver ( )->IsProtocolServiceSupported (
kSCSIProtocolFeature_MaximumReadBlockTransferCount,
&maxBlockCount );
if ( supported == false )
maxBlockCount = kDefaultMaxBlocksPerIO;
supported = GetProtocolDriver ( )->IsProtocolServiceSupported (
kSCSIProtocolFeature_MaximumReadTransferByteCount,
&maxByteCount );
if ( ( supported == true ) && ( maxByteCount > 0 ) )
{
setProperty ( kIOMaximumByteCountReadKey, maxByteCount, 64 );
if ( fMediumBlockSize > 0 )
maxBlockCount = min ( maxBlockCount, ( maxByteCount / fMediumBlockSize ) );
}
return maxBlockCount;
}
UInt64
IOSCSIBlockCommandsDevice::ReportDeviceMaxBlocksWriteTransfer ( void )
{
UInt32 maxBlockCount = kDefaultMaxBlocksPerIO;
UInt64 maxByteCount = 0;
bool supported = false;
STATUS_LOG ( ( "IOSCSIBlockCommandsDevice::ReportDeviceMaxBlocksWriteTransfer.\n" ) );
supported = GetProtocolDriver ( )->IsProtocolServiceSupported (
kSCSIProtocolFeature_MaximumWriteBlockTransferCount,
&maxBlockCount );
if ( supported == false )
maxBlockCount = kDefaultMaxBlocksPerIO;
supported = GetProtocolDriver ( )->IsProtocolServiceSupported (
kSCSIProtocolFeature_MaximumWriteTransferByteCount,
&maxByteCount );
if ( ( supported == true ) && ( maxByteCount > 0 ) )
{
setProperty ( kIOMaximumByteCountWriteKey, maxByteCount, 64 );
if ( fMediumBlockSize > 0 )
maxBlockCount = min ( 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;
STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) );
fIOSCSIBlockCommandsDeviceReserved = IONew ( IOSCSIBlockCommandsDeviceExpansionData, 1 );
require_nonzero ( fIOSCSIBlockCommandsDeviceReserved, ErrorExit );
bzero ( fIOSCSIBlockCommandsDeviceReserved,
sizeof ( IOSCSIBlockCommandsDeviceExpansionData ) );
fMediaIsRemovable = false;
fKnownManualEject = false;
ResetMediumCharacteristics( );
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;
fDeviceIsShared = true;
}
}
if ( GetProtocolDriver ( )->getProperty ( kIOPropertyProtocolCharacteristicsKey ) != NULL )
{
OSDictionary * characterDict = NULL;
characterDict = OSDynamicCast (
OSDictionary,
GetProtocolDriver ( )->getProperty ( kIOPropertyProtocolCharacteristicsKey ) );
if ( characterDict->getObject ( kIOPropertySCSIProtocolMultiInitKey ) != NULL )
{
fDeviceIsShared = 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 ) );
SetMediumCharacteristics ( ( UInt64 ) 0, ( UInt64 ) 0 );
return setupSuccessful;
ReleaseReservedMemory:
require_nonzero_quiet ( fIOSCSIBlockCommandsDeviceReserved, ErrorExit );
IODelete ( fIOSCSIBlockCommandsDeviceReserved, IOSCSIBlockCommandsDeviceExpansionData, 1 );
fIOSCSIBlockCommandsDeviceReserved = NULL;
ErrorExit:
return setupSuccessful;
}
void
IOSCSIBlockCommandsDevice::StartDeviceSupport ( void )
{
EnablePolling ( );
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 )
{
IODelete ( fIOSCSIBlockCommandsDeviceReserved, IOSCSIBlockCommandsDeviceExpansionData, 1 );
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 == 0x00 ) ||
( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x02 ) ||
( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x03 ) ) )
{
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;
IOReturn status = kIOReturnSuccess;
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;
}
ERROR_LOG ( ( "!!! INQUIRY failed !!!, serviceResponse = %d, taskStatus = %d\n",
serviceResponse, GetTaskStatus ( request ) ) );
IOSleep ( 1000 );
}
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;
}
SetCMDQUE ( inquiryBuffer->flags2 & kINQUIRY_Byte7_CMDQUE_Mask );
buffer->release ( );
buffer = NULL;
buffer = IOBufferMemoryDescriptor::withCapacity ( sizeof ( SBCModePageCaching ) + sizeof ( SPCModeParameterHeader6 ),
kIODirectionOutIn );
require_nonzero ( buffer, ReleaseTask );
status = GetWriteCacheState ( buffer, kModePageControlSavedValues );
require_success ( status, ReleaseTask );
{
SPCModeParameterHeader6 * header = NULL;
SBCModePageCaching * cachePage = NULL;
UInt8 * bufferPtr = NULL;
UInt8 pageCode = 0;
UInt8 minSize = 0;
UInt8 pageSize = 0;
bool WCEBit = false;
bufferPtr = ( UInt8 * ) buffer->getBytesNoCopy ( );
header = ( SPCModeParameterHeader6 * ) bufferPtr;
minSize = sizeof ( SPCModeParameterHeader6 ) + header->BLOCK_DESCRIPTOR_LENGTH +
offsetof ( SBCModePageCaching, DEMAND_READ_WRITE_RETENTION_PRIORITY ) + sizeof ( header->MODE_DATA_LENGTH );
require ( ( ( header->MODE_DATA_LENGTH + sizeof ( header->MODE_DATA_LENGTH ) ) >= minSize ), ReleaseTask );
cachePage = ( SBCModePageCaching * ) &bufferPtr[ sizeof ( SPCModeParameterHeader6 ) + header->BLOCK_DESCRIPTOR_LENGTH ];
pageCode = cachePage->header.PS_PAGE_CODE & kModePageFormat_PAGE_CODE_Mask;
require ( ( pageCode == kSBCModePageCachingCode ), ReleaseTask );
cachePage->header.PS_PAGE_CODE = kSBCModePageCachingCode;
WCEBit = cachePage->flags & kSBCModePageCaching_WCE_Mask;
pageSize = header->MODE_DATA_LENGTH + sizeof ( header->MODE_DATA_LENGTH );
header->MODE_DATA_LENGTH = 0x00;
if ( MODE_SELECT_6 ( request,
buffer,
0x01, 0x00, pageSize,
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 )
{
SetMediumCharacteristics ( ( UInt64 ) blockSize, ( UInt64 ) blockCount );
}
void
IOSCSIBlockCommandsDevice::SetMediumCharacteristics (
UInt64 blockSize,
UInt64 blockCount )
{
UInt64 maxBlocksRead = 0;
UInt64 maxBlocksWrite = 0;
STATUS_LOG ( ( "mediumBlockSize = %qd, blockCount = %qd\n",
blockSize, blockCount ) );
fMediumBlockSize = blockSize;
fMediumBlockCount64 = blockCount;
if ( fMediumBlockCount64 > kREPORT_CAPACITY_MaximumLBA )
{
fMediumBlockCount = kREPORT_CAPACITY_MaximumLBA;
}
else
{
fMediumBlockCount = blockCount;
}
maxBlocksRead = ReportDeviceMaxBlocksReadTransfer ( );
maxBlocksWrite = ReportDeviceMaxBlocksWriteTransfer ( );
setProperty ( kIOMaximumBlockCountReadKey, maxBlocksRead, 64 );
setProperty ( kIOMaximumBlockCountWriteKey, maxBlocksWrite, 64 );
}
void
IOSCSIBlockCommandsDevice::ResetMediumCharacteristics ( void )
{
fMediumBlockSize = 0;
fMediumBlockCount = 0;
fMediumBlockCount64 = 0;
fMediumPresent = false;
fMediumIsWriteProtected = true;
fMediumRemovalPrevented = false;
}
void
IOSCSIBlockCommandsDevice::CreateStorageServiceNub ( void )
{
IOService * nub = NULL;
nub = OSTypeAlloc ( 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 );
DetermineMediumGeometry ( );
SetMediumCharacteristics ( blockSize, blockCount );
fMediumIsWriteProtected = DetermineMediumWriteProtectState ( );
fMediumPresent = true;
fPollingMode = kPollingMode_Suspended;
SetMediumIcon ( );
messageClients ( kIOMessageMediaStateHasChanged,
( void * ) kIOMediaStateOnline,
0 );
if ( fMediumRemovalPrevented == false )
{
fPollingMode = kPollingMode_MediaRemoval;
fKnownManualEject = true;
fDeviceIsShared = true;
}
Exit:
return;
}
bool
IOSCSIBlockCommandsDevice::DetermineMediaPresence ( void )
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
SCSITaskIdentifier request = NULL;
bool mediaFound = false;
OSBoolean * keySwitchLocked = NULL;
keySwitchLocked = OSDynamicCast (
OSBoolean,
getProperty ( kAppleKeySwitchProperty ) );
if ( keySwitchLocked != NULL )
{
if ( keySwitchLocked->isTrue ( ) )
{
goto ErrorExit;
}
}
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;
SCSI_Capacity_Data capacityData;
IOMemoryDescriptor * bufferDesc = NULL;
SCSITaskIdentifier request = NULL;
bool result = false;
*blockSize = 0;
*blockCount = 0;
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
bzero ( &capacityData, sizeof ( capacityData ) );
bufferDesc = IOMemoryDescriptor::withAddress ( &capacityData,
kREPORT_CAPACITY_DataSize,
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 = OSSwapBigToHostInt32 ( capacityData.BLOCK_LENGTH_IN_BYTES );
*blockCount = ( ( UInt64 ) OSSwapBigToHostInt32 ( capacityData.RETURNED_LOGICAL_BLOCK_ADDRESS ) ) + 1;
result = true;
if ( ( capacityData.RETURNED_LOGICAL_BLOCK_ADDRESS == kREPORT_CAPACITY_MaximumLBA ) &&
( GetANSIVersion ( ) >= kINQUIRY_ANSI_VERSION_SCSI_SPC_3_Compliant ) )
{
SCSI_Capacity_Data_Long longCapacityData;
bzero ( &longCapacityData, sizeof ( longCapacityData ) );
bufferDesc->release ( );
bufferDesc = IOMemoryDescriptor::withAddress ( &longCapacityData,
kREPORT_CAPACITY_16_DataSize,
kIODirectionIn );
require_nonzero ( bufferDesc, ReleaseTask );
if ( READ_CAPACITY_16 ( request, bufferDesc, 0, kREPORT_CAPACITY_16_DataSize, 0, 0 ) == true )
{
serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS );
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
*blockSize = OSSwapBigToHostInt32 ( longCapacityData.BLOCK_LENGTH_IN_BYTES );
*blockCount = OSSwapBigToHostInt64 ( longCapacityData.RETURNED_LOGICAL_BLOCK_ADDRESS ) + 1;
}
}
}
STATUS_LOG ( ( "%s: Media capacity: %qx and block size: %qx\n",
getName ( ), *blockCount, *blockSize ) );
}
bufferDesc->release ( );
bufferDesc = NULL;
ReleaseTask:
require_nonzero_quiet ( request, ErrorExit );
ReleaseSCSITask ( request );
request = NULL;
ErrorExit:
return result;
}
void
IOSCSIBlockCommandsDevice::DetermineMediumGeometry ( void )
{
IOBufferMemoryDescriptor * bufferDesc = NULL;
OSDictionary * dict = NULL;
IOReturn status = kIOReturnSuccess;
UInt32 minBufferSize = 0;
UInt16 blockDescriptorLength = 0;
UInt32 modeDataLength = 0;
UInt8 headerSize = 0;
UInt8 * buffer = NULL;
bool use10ByteModeSense = true;
require_quiet ( ( fMediaIsRemovable == false ), ErrorExit );
dict = OSDictionary::withCapacity ( 4 );
require_nonzero ( dict, ErrorExit );
bufferDesc = IOBufferMemoryDescriptor::withCapacity ( sizeof ( SPCModeParameterHeader10 ), kIODirectionIn );
require_nonzero ( bufferDesc, ReleaseDictionary );
buffer = ( UInt8 * ) bufferDesc->getBytesNoCopy ( );
bzero ( buffer, bufferDesc->getLength ( ) );
status = GetModeSense ( bufferDesc,
kSBCModePageFormatDeviceCode,
sizeof ( SPCModeParameterHeader10 ),
&use10ByteModeSense );
require_success ( status, RigidDisk );
if ( use10ByteModeSense )
{
SPCModeParameterHeader10 * header = ( SPCModeParameterHeader10 * ) buffer;
modeDataLength = OSSwapBigToHostInt16 ( header->MODE_DATA_LENGTH ) + sizeof ( header->MODE_DATA_LENGTH );
modeDataLength = min ( modeDataLength, sizeof ( SPCModeParameterHeader10 ) + header->BLOCK_DESCRIPTOR_LENGTH + sizeof ( SBCModePageFormatDevice ) );
}
else
{
SPCModeParameterHeader6 * header = ( SPCModeParameterHeader6 * ) buffer;
modeDataLength = header->MODE_DATA_LENGTH + sizeof ( header->MODE_DATA_LENGTH );
modeDataLength = min ( modeDataLength, sizeof ( SPCModeParameterHeader6 ) + header->BLOCK_DESCRIPTOR_LENGTH + sizeof ( SBCModePageFormatDevice ) );
}
bufferDesc->release ( );
bufferDesc = IOBufferMemoryDescriptor::withCapacity ( modeDataLength, kIODirectionIn );
require_nonzero ( bufferDesc, ReleaseDictionary );
buffer = ( UInt8 * ) bufferDesc->getBytesNoCopy ( );
bzero ( buffer, bufferDesc->getLength ( ) );
status = GetModeSense ( bufferDesc,
kSBCModePageFormatDeviceCode,
modeDataLength,
&use10ByteModeSense );
if ( use10ByteModeSense )
{
SPCModeParameterHeader10 * header = ( SPCModeParameterHeader10 * ) buffer;
blockDescriptorLength = OSSwapBigToHostInt16 ( header->BLOCK_DESCRIPTOR_LENGTH );
headerSize = sizeof ( SPCModeParameterHeader10 );
}
else
{
SPCModeParameterHeader6 * header = ( SPCModeParameterHeader6 * ) buffer;
blockDescriptorLength = header->BLOCK_DESCRIPTOR_LENGTH;
headerSize = sizeof ( SPCModeParameterHeader6 );
}
minBufferSize = headerSize + blockDescriptorLength + offsetof ( SBCModePageFormatDevice, INTERLEAVE );
require ( ( modeDataLength >= minBufferSize ), RigidDisk );
{
SBCModePageFormatDevice * formatDevice = NULL;
OSNumber * number = NULL;
UInt8 pageCode = 0;
formatDevice = ( SBCModePageFormatDevice * ) &buffer[headerSize + blockDescriptorLength];
pageCode = formatDevice->header.PS_PAGE_CODE & kModePageFormat_PAGE_CODE_Mask;
require ( ( pageCode == kSBCModePageFormatDeviceCode ), RigidDisk );
formatDevice->SECTORS_PER_TRACK = OSSwapBigToHostInt16 ( formatDevice->SECTORS_PER_TRACK );
formatDevice->DATA_BYTES_PER_PHYSICAL_SECTOR = OSSwapBigToHostInt16 ( formatDevice->DATA_BYTES_PER_PHYSICAL_SECTOR );
number = OSNumber::withNumber ( formatDevice->SECTORS_PER_TRACK, 16 );
if ( number != NULL )
{
dict->setObject ( kIOPropertySectorCountPerTrackKey, number );
number->release ( );
number = NULL;
}
number = OSNumber::withNumber ( formatDevice->DATA_BYTES_PER_PHYSICAL_SECTOR, 16 );
if ( number != NULL )
{
dict->setObject ( kIOPropertyBytesPerPhysicalSectorKey, number );
number->release ( );
number = NULL;
}
}
RigidDisk:
bufferDesc->release ( );
bufferDesc = IOBufferMemoryDescriptor::withCapacity ( sizeof ( SPCModeParameterHeader10 ), kIODirectionIn );
require_nonzero ( bufferDesc, ReleaseDictionary );
buffer = ( UInt8 * ) bufferDesc->getBytesNoCopy ( );
bzero ( buffer, bufferDesc->getLength ( ) );
status = GetModeSense ( bufferDesc,
kSBCModePageRigidDiskGeometryCode,
sizeof ( SPCModeParameterHeader10 ),
&use10ByteModeSense );
require_success ( status, ReleaseDescriptor );
if ( use10ByteModeSense )
{
SPCModeParameterHeader10 * header = ( SPCModeParameterHeader10 * ) buffer;
modeDataLength = OSSwapBigToHostInt16 ( header->MODE_DATA_LENGTH ) + sizeof ( header->MODE_DATA_LENGTH );
modeDataLength = min ( modeDataLength, sizeof ( SPCModeParameterHeader10 ) + header->BLOCK_DESCRIPTOR_LENGTH + sizeof ( SBCModePageRigidDiskGeometry ) );
}
else
{
SPCModeParameterHeader6 * header = ( SPCModeParameterHeader6 * ) buffer;
modeDataLength = header->MODE_DATA_LENGTH + sizeof ( header->MODE_DATA_LENGTH );
modeDataLength = min ( modeDataLength, sizeof ( SPCModeParameterHeader6 ) + header->BLOCK_DESCRIPTOR_LENGTH + sizeof ( SBCModePageRigidDiskGeometry ) );
}
bufferDesc->release ( );
bufferDesc = IOBufferMemoryDescriptor::withCapacity ( modeDataLength, kIODirectionIn );
require_nonzero ( bufferDesc, ReleaseDictionary );
buffer = ( UInt8 * ) bufferDesc->getBytesNoCopy ( );
bzero ( buffer, bufferDesc->getLength ( ) );
status = GetModeSense ( bufferDesc,
kSBCModePageRigidDiskGeometryCode,
modeDataLength,
&use10ByteModeSense );
require_success ( status, ReleaseDescriptor );
if ( use10ByteModeSense )
{
SPCModeParameterHeader10 * header = ( SPCModeParameterHeader10 * ) buffer;
blockDescriptorLength = OSSwapBigToHostInt16 ( header->BLOCK_DESCRIPTOR_LENGTH );
headerSize = sizeof ( SPCModeParameterHeader10 );
}
else
{
SPCModeParameterHeader6 * header = ( SPCModeParameterHeader6 * ) buffer;
blockDescriptorLength = header->BLOCK_DESCRIPTOR_LENGTH;
headerSize = sizeof ( SPCModeParameterHeader6 );
}
minBufferSize = headerSize + blockDescriptorLength + offsetof ( SBCModePageRigidDiskGeometry, STARTING_CYLINDER_WRITE_PRECOMPENSATION );
require ( ( modeDataLength >= minBufferSize ), ReleaseDescriptor );
{
SBCModePageRigidDiskGeometry * rigidDisk = NULL;
OSNumber * number = NULL;
UInt32 cylinders = 0;
UInt8 pageCode = 0;
rigidDisk = ( SBCModePageRigidDiskGeometry * ) &buffer[headerSize + blockDescriptorLength];
pageCode = rigidDisk->header.PS_PAGE_CODE & kModePageFormat_PAGE_CODE_Mask;
require ( ( pageCode == kSBCModePageRigidDiskGeometryCode ), ReleaseDescriptor );
cylinders = ( rigidDisk->NUMBER_OF_CYLINDERS[0] << 16 ) +
( rigidDisk->NUMBER_OF_CYLINDERS[1] << 8 ) +
rigidDisk->NUMBER_OF_CYLINDERS[2];
number = OSNumber::withNumber ( cylinders, 32 );
if ( number != NULL )
{
dict->setObject ( kIOPropertyCylinderCountKey, number );
number->release ( );
number = NULL;
}
number = OSNumber::withNumber ( rigidDisk->NUMBER_OF_HEADS, 8 );
if ( number != NULL )
{
dict->setObject ( kIOPropertyHeadCountKey, number );
number->release ( );
number = NULL;
}
}
GetDeviceCharacteristicsDictionary ( )->setObject ( kIOPropertyRigidDiskGeometryKey, dict );
ReleaseDescriptor:
require_nonzero_quiet ( bufferDesc, ReleaseDictionary );
bufferDesc->release ( );
bufferDesc = NULL;
ReleaseDictionary:
require_nonzero_quiet ( dict, ErrorExit );
dict->release ( );
dict = NULL;
ErrorExit:
return;
}
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 ) )
{
if ( ( modeBuffer[3] & kModeSenseSBCDeviceSpecific_WriteProtectMask ) == 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 ) )
{
if ( ( modeBuffer[2] & kModeSenseSBCDeviceSpecific_WriteProtectMask ) == 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 ( 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
IOSCSIBlockCommandsDevice::GetWriteCacheState (
IOMemoryDescriptor * buffer,
UInt8 modePageControlValue )
{
IOReturn status = kIOReturnUnsupported;
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
SCSITaskIdentifier request = NULL;
require ( ( fMediaIsRemovable == false ), ErrorExit );
request = GetSCSITask ( );
require_nonzero_action ( request, ErrorExit, status = kIOReturnNoResources );
if ( MODE_SENSE_6 ( request,
buffer,
0x00,
modePageControlValue,
kSBCModePageCachingCode,
buffer->getLength ( ),
0x00 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
status = kIOReturnSuccess;
}
else
{
status = kIOReturnError;
}
ReleaseSCSITask ( request );
request = NULL;
ErrorExit:
return status;
}
IOReturn
IOSCSIBlockCommandsDevice::GetWriteCacheState ( bool * enabled )
{
IOReturn status = kIOReturnUnsupported;
IOBufferMemoryDescriptor * bufferDesc = NULL;
SPCModeParameterHeader6 * header = NULL;
SBCModePageCaching * cachePage = NULL;
UInt8 * buffer = NULL;
UInt8 pageCode = 0;
UInt8 minSize = 0;
bufferDesc = IOBufferMemoryDescriptor::withCapacity ( sizeof ( SBCModePageCaching ),
kIODirectionOutIn );
require_nonzero ( bufferDesc, ErrorExit );
status = GetWriteCacheState ( bufferDesc, kModePageControlCurrentValues );
require_success ( status, ReleaseDescriptor );
buffer = ( UInt8 * ) bufferDesc->getBytesNoCopy ( );
header = ( SPCModeParameterHeader6 * ) buffer;
minSize = sizeof ( SPCModeParameterHeader6 ) + header->BLOCK_DESCRIPTOR_LENGTH +
offsetof ( SBCModePageCaching, DEMAND_READ_WRITE_RETENTION_PRIORITY ) + sizeof ( header->MODE_DATA_LENGTH );
require_action ( ( ( header->MODE_DATA_LENGTH + sizeof ( header->MODE_DATA_LENGTH ) ) >= minSize ), ReleaseDescriptor, status = kIOReturnError );
cachePage = ( SBCModePageCaching * ) &buffer[ sizeof ( SPCModeParameterHeader6 ) + header->BLOCK_DESCRIPTOR_LENGTH ];
pageCode = cachePage->header.PS_PAGE_CODE & kModePageFormat_PAGE_CODE_Mask;
require_action ( ( pageCode == kSBCModePageCachingCode ), ReleaseDescriptor, status = kIOReturnError );
*enabled = cachePage->flags & kSBCModePageCaching_WCE_Mask;
ReleaseDescriptor:
require_nonzero ( bufferDesc, ErrorExit );
bufferDesc->release ( );
bufferDesc = NULL;
ErrorExit:
return status;
}
IOReturn
IOSCSIBlockCommandsDevice::SetWriteCacheState ( bool enabled )
{
IOReturn status = kIOReturnUnsupported;
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
SCSITaskIdentifier request = NULL;
IOBufferMemoryDescriptor * bufferDesc = NULL;
SPCModeParameterHeader6 * header = NULL;
SBCModePageCaching * cachePage = NULL;
UInt8 * buffer = NULL;
UInt8 pageCode = 0;
UInt8 minSize = 0;
bufferDesc = IOBufferMemoryDescriptor::withCapacity ( sizeof ( SBCModePageCaching ),
kIODirectionOutIn );
require_nonzero ( bufferDesc, ErrorExit );
status = GetWriteCacheState ( bufferDesc, kModePageControlCurrentValues );
require_success ( status, ReleaseDescriptor );
buffer = ( UInt8 * ) bufferDesc->getBytesNoCopy ( );
header = ( SPCModeParameterHeader6 * ) buffer;
minSize = sizeof ( SPCModeParameterHeader6 ) + header->BLOCK_DESCRIPTOR_LENGTH +
offsetof ( SBCModePageCaching, DEMAND_READ_WRITE_RETENTION_PRIORITY ) + sizeof ( header->MODE_DATA_LENGTH );
require_action ( ( ( header->MODE_DATA_LENGTH + sizeof ( header->MODE_DATA_LENGTH ) ) >= minSize ), ReleaseDescriptor, status = kIOReturnError );
cachePage = ( SBCModePageCaching * ) &buffer[ sizeof ( SPCModeParameterHeader6 ) + header->BLOCK_DESCRIPTOR_LENGTH ];
pageCode = cachePage->header.PS_PAGE_CODE & kModePageFormat_PAGE_CODE_Mask;
require_action ( ( pageCode == kSBCModePageCachingCode ), ReleaseDescriptor, status = kIOReturnError );
cachePage->header.PS_PAGE_CODE = kSBCModePageCachingCode;
if ( enabled == true )
cachePage->flags |= kSBCModePageCaching_WCE_Mask;
else
cachePage->flags &= ~kSBCModePageCaching_WCE_Mask;
request = GetSCSITask ( );
require_nonzero_action ( request, ReleaseDescriptor, status = kIOReturnNoResources );
if ( MODE_SELECT_6 ( request,
bufferDesc,
0x01, 0x00, header->MODE_DATA_LENGTH + sizeof ( header->MODE_DATA_LENGTH ),
0x00 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
fWriteCacheEnabled = enabled;
status = kIOReturnSuccess;
}
else
{
status = kIOReturnError;
}
}
ReleaseSCSITask ( request );
request = NULL;
ReleaseDescriptor:
require_nonzero ( bufferDesc, ErrorExit );
bufferDesc->release ( );
bufferDesc = NULL;
ErrorExit:
return status;
}
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 )
{
return kIOReturnUnsupported;
}
IOReturn
IOSCSIBlockCommandsDevice::IssueRead (
IOMemoryDescriptor * buffer,
UInt64 startBlock,
UInt64 blockCount,
void * clientData )
{
IOReturn status = kIOReturnNoResources;
SCSITaskIdentifier request = NULL;
bool cmdStatus = false;
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
if ( startBlock > kREPORT_CAPACITY_MaximumLBA )
{
cmdStatus = READ_16 ( request,
buffer,
fMediumBlockSize,
0,
0,
0,
0,
( SCSICmdField8Byte ) startBlock,
( SCSICmdField4Byte ) blockCount,
0,
0 );
}
else
{
cmdStatus = READ_10 ( request,
buffer,
fMediumBlockSize,
0,
0,
0,
( SCSICmdField4Byte ) startBlock,
( SCSICmdField2Byte ) blockCount,
0 );
}
if ( cmdStatus == true )
{
SetApplicationLayerReference ( request, clientData );
if ( GetCMDQUE ( ) == true )
{
SetTaskAttribute ( request, kSCSITask_SIMPLE );
SetTaggedTaskIdentifier ( request, GetUniqueTagID ( ) );
}
SendCommand ( request,
fReadTimeoutDuration,
&IOSCSIBlockCommandsDevice::AsyncReadWriteComplete );
status = kIOReturnSuccess;
}
else
{
ReleaseSCSITask ( request );
request = NULL;
status = kIOReturnBadArgument;
}
ErrorExit:
return status;
}
IOReturn
IOSCSIBlockCommandsDevice::IssueWrite ( IOMemoryDescriptor * buffer,
UInt64 startBlock,
UInt64 blockCount )
{
return kIOReturnUnsupported;
}
IOReturn
IOSCSIBlockCommandsDevice::IssueWrite (
IOMemoryDescriptor * buffer,
UInt64 startBlock,
UInt64 blockCount,
void * clientData )
{
IOReturn status = kIOReturnNoResources;
SCSITaskIdentifier request = NULL;
bool cmdStatus = false;
request = GetSCSITask ( );
require_nonzero ( request, ErrorExit );
if ( startBlock > kREPORT_CAPACITY_MaximumLBA )
{
cmdStatus = WRITE_16 ( request,
buffer,
fMediumBlockSize,
0,
0,
0,
0,
( SCSICmdField8Byte ) startBlock,
( SCSICmdField4Byte ) blockCount,
0,
0 );
}
else
{
cmdStatus = WRITE_10 ( request,
buffer,
fMediumBlockSize,
0,
0,
0,
0,
( SCSICmdField4Byte ) startBlock,
( SCSICmdField2Byte ) blockCount,
0 );
}
if ( cmdStatus == true )
{
SetApplicationLayerReference ( request, clientData );
if ( GetCMDQUE ( ) == true )
{
SetTaskAttribute ( request, kSCSITask_SIMPLE );
SetTaggedTaskIdentifier ( request, GetUniqueTagID ( ) );
}
SendCommand ( request,
fWriteTimeoutDuration,
&IOSCSIBlockCommandsDevice::AsyncReadWriteComplete );
status = kIOReturnSuccess;
}
else
{
ReleaseSCSITask ( request );
request = NULL;
status = kIOReturnBadArgument;
}
ErrorExit:
return status;
}
void
IOSCSIBlockCommandsDevice::AsyncReadWriteCompletion (
SCSITaskIdentifier completedTask )
{
IOReturn status = kIOReturnSuccess;
UInt64 actCount = 0;
void * clientData = NULL;
clientData = GetApplicationLayerReference ( completedTask );
require_nonzero ( clientData, ErrorExit );
if ( ( GetServiceResponse ( completedTask ) == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( completedTask ) == kSCSITaskStatus_GOOD ) )
{
actCount = GetRealizedDataTransferCount ( completedTask );
}
else
{
status = kIOReturnIOError;
if ( GetServiceResponse ( completedTask ) == kSCSIServiceResponse_TASK_COMPLETE )
{
if ( GetTaskStatus ( completedTask ) == kSCSITaskStatus_CHECK_CONDITION )
{
SCSI_Sense_Data senseDataBuffer = { 0 };
bool senseIsValid = false;
senseIsValid = GetAutoSenseData ( completedTask, &senseDataBuffer, sizeof ( senseDataBuffer ) );
if ( senseIsValid )
{
if ( ( ( senseDataBuffer.SENSE_KEY & kSENSE_KEY_Mask ) == kSENSE_KEY_RECOVERED_ERROR ) &&
( GetRequestedDataTransferCount ( completedTask ) == GetRealizedDataTransferCount ( completedTask ) ) )
{
IOLog ( "READ or WRITE succeeded, but recoverable (soft) error occurred, SENSE_KEY = 0x%01x, ASC = 0x%02x, ASCQ = 0x%02x, LBA = 0x%08x\n",
senseDataBuffer.SENSE_KEY & kSENSE_KEY_Mask,
senseDataBuffer.ADDITIONAL_SENSE_CODE,
senseDataBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER,
OSReadBigInt32 ( &senseDataBuffer.INFORMATION_1, 0 ) );
status = kIOReturnSuccess;
actCount = GetRealizedDataTransferCount ( completedTask );
}
else
{
ERROR_LOG ( ( "READ or WRITE failed, ASC = 0x%02x, ASCQ = 0x%02x\n",
senseDataBuffer.ADDITIONAL_SENSE_CODE,
senseDataBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ) );
}
}
}
}
}
IOBlockStorageServices::AsyncReadWriteComplete ( clientData, status, actCount );
ReleaseSCSITask ( completedTask );
ErrorExit:
return;
}
#if 0
#pragma mark -
#pragma mark ₯ Static Methods
#pragma mark -
#endif
void
IOSCSIBlockCommandsDevice::AsyncReadWriteComplete (
SCSITaskIdentifier request )
{
IOSCSIBlockCommandsDevice * taskOwner = NULL;
require_nonzero ( request, ErrorExit );
taskOwner = OSDynamicCast ( IOSCSIBlockCommandsDevice, sGetOwnerForTask ( request ) );
require_nonzero ( taskOwner, ErrorExit );
taskOwner->AsyncReadWriteCompletion ( request );
ErrorExit:
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 );
OSMetaClassDefineReservedUsed ( 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 );