IOSCSIBlockCommandsDevicePM.cpp [plain text]
#include <IOKit/scsi-commands/SCSITask.h>
#include "IOSCSIBlockCommandsDevice.h"
#define SCSI_SBC_DEVICE_DEBUGGING_LEVEL 0
#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
static IOPMPowerState sPowerStates[kSBCNumPowerStates] =
{
{ kIOPMPowerStateVersion1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ kIOPMPowerStateVersion1, 0, IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 },
{ kIOPMPowerStateVersion1, (IOPMDeviceUsable | kIOPMPreventIdleSleep), IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 },
{ kIOPMPowerStateVersion1, (IOPMDeviceUsable | kIOPMPreventIdleSleep), IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 },
{ kIOPMPowerStateVersion1, (IOPMDeviceUsable | IOPMMaxPerformance | kIOPMPreventIdleSleep), IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 }
};
UInt32
IOSCSIBlockCommandsDevice::GetInitialPowerState ( void )
{
STATUS_LOG ( ( "IOSCSIBlockCommandsDevice::%s called%s\n", "\033[33m",
__FUNCTION__, "\033[0m" ) );
return kSBCPowerStateActive;
}
UInt32
IOSCSIBlockCommandsDevice::GetNumberOfPowerStateTransitions ( void )
{
return ( kSBCPowerStateActive - kSBCPowerStateSleep );
}
void
IOSCSIBlockCommandsDevice::InitializePowerManagement ( IOService * provider )
{
STATUS_LOG ( ( "IOSCSIBlockCommandsDevice::InitializePowerManagement called\n" ) );
fCurrentPowerState = kSBCPowerStateActive;
super::InitializePowerManagement ( provider );
registerPowerDriver ( this, sPowerStates, kSBCNumPowerStates );
changePowerStateTo ( kSBCPowerStateSleep );
}
void
IOSCSIBlockCommandsDevice::HandleCheckPowerState ( void )
{
if ( IsDeviceAccessEnabled ( ) )
{
super::HandleCheckPowerState ( kSBCPowerStateActive );
}
}
void
IOSCSIBlockCommandsDevice::TicklePowerManager ( void )
{
( void ) super::TicklePowerManager ( kSBCPowerStateActive );
}
void
IOSCSIBlockCommandsDevice::HandlePowerChange ( void )
{
SCSIServiceResponse serviceResponse;
SCSITaskIdentifier request = NULL;
STATUS_LOG ( ( "IOSCSIBlockCommandsDevice::HandlePowerChange called\n" ) );
request = GetSCSITask ( );
while ( ( fProposedPowerState != fCurrentPowerState ) && ( isInactive ( ) == false ) )
{
STATUS_LOG ( ( "fProposedPowerState = %ld, fCurrentPowerState = %ld\n",
fProposedPowerState, fCurrentPowerState ) );
if ( ( fCurrentPowerState <= kSBCPowerStateSleep ) && ( fProposedPowerState > kSBCPowerStateSleep ) )
{
STATUS_LOG ( ( "We think we're in sleep\n" ) );
if ( START_STOP_UNIT ( request, 0x00, 0x00, 0x00, 0x01, 0x00 ) == true )
{
serviceResponse = SendCommand ( request, 0 );
}
STATUS_LOG ( ( "calling ClearPowerOnReset\n" ) );
if ( ClearPowerOnReset ( ) == false )
break;
STATUS_LOG ( ( "calling ClearNotReadyStatus\n" ) );
if ( ClearNotReadyStatus ( ) == false )
break;
if ( fMediaIsRemovable == true )
{
if ( fMediumPresent == true )
{
bool mediaPresent = VerifyMediumPresence ( );
if ( mediaPresent != fMediumPresent )
{
PANIC_NOW ( ( "Unexpected Media Removal when waking up from sleep\n" ) );
}
else
{
if ( PREVENT_ALLOW_MEDIUM_REMOVAL ( request, 1, 0 ) == true )
{
serviceResponse = SendCommand ( request, 0 );
}
else
{
PANIC_NOW ( ( "IOSCSIBlockCommandsDevice::PollForMedia malformed command" ) );
}
}
}
else
{
fPollingMode = kPollingMode_NewMedia;
EnablePolling ( );
}
}
}
switch ( fProposedPowerState )
{
case kSBCPowerStateSystemSleep:
{
UInt32 previousPowerState;
STATUS_LOG ( ( "case kSBCPowerStateSystemSleep\n" ) );
if ( fMediumPresent == false )
{
STATUS_LOG ( ( "Disabling polling\n" ) );
DisablePolling ( );
}
previousPowerState = fCurrentPowerState;
fCurrentPowerState = fProposedPowerState;
while ( fNumCommandsOutstanding > 1 )
{
IOSleep ( 1 );
}
if ( previousPowerState != kSBCPowerStateSleep )
{
if ( fDeviceSupportsPowerConditions )
{
STATUS_LOG ( ( "Sending START_STOP_UNIT to drive to turn it off\n" ) );
if ( START_STOP_UNIT ( request, 0, 0x05, 0, 0, 0 ) == true )
{
serviceResponse = SendCommand ( request, 0 );
}
}
else
{
STATUS_LOG ( ( "Power conditions not supported, make sure drive is spun down\n" ) );
if ( START_STOP_UNIT ( request, 0, 0, 0, 0, 0 ) == true )
{
serviceResponse = SendCommand ( request, 0 );
}
}
}
fCurrentPowerState = kSBCPowerStateSystemSleep;
}
break;
case kSBCPowerStateSleep:
{
STATUS_LOG ( ( "case kSBCPowerStateSleep\n" ) );
if ( fCurrentPowerState < kSBCPowerStateSleep )
{
STATUS_LOG ( ( "TicklePowerManager.\n" ) );
TicklePowerManager ( );
STATUS_LOG ( ( "Wakeup path completed.\n" ) );
fCurrentPowerState = fProposedPowerState;
break;
}
if ( fCurrentPowerState > kSBCPowerStateSleep )
{
STATUS_LOG ( ( "At minimum, make sure drive is spun down.\n" ) );
if ( START_STOP_UNIT ( request, 0, 0, 0, 0, 0 ) == true )
{
serviceResponse = SendCommand ( request, 0 );
}
}
fCurrentPowerState = kSBCPowerStateSleep;
}
break;
case kSBCPowerStateStandby:
{
STATUS_LOG ( ( "case kSBCPowerStateStandby\n" ) );
STATUS_LOG ( ( "At minimum, make sure drive is spun down.\n" ) );
if ( fCurrentPowerState > kSBCPowerStateStandby )
{
if ( START_STOP_UNIT ( request, 0, 0, 0, 0, 0 ) == true )
{
serviceResponse = SendCommand ( request, 0 );
}
}
fCurrentPowerState = kSBCPowerStateStandby;
}
break;
case kSBCPowerStateIdle:
{
STATUS_LOG ( ( "case kSBCPowerStateIdle\n" ) );
fCurrentPowerState = kSBCPowerStateIdle;
}
break;
case kSBCPowerStateActive:
{
STATUS_LOG ( ( "case kSBCPowerStateActive\n" ) );
if ( START_STOP_UNIT ( request, 0, 0, 0, 1, 0 ) == true )
{
serviceResponse = SendCommand ( request, 0 );
}
fCurrentPowerState = kSBCPowerStateActive;
fCommandGate->commandWakeup ( &fCurrentPowerState, false );
}
break;
default:
PANIC_NOW ( ( "Undefined power state issued\n" ) );
break;
}
}
ReleaseSCSITask ( request );
}
bool
IOSCSIBlockCommandsDevice::VerifyMediumPresence ( void )
{
SCSI_Sense_Data senseBuffer;
IOMemoryDescriptor * bufferDesc;
SCSITaskIdentifier request;
bool mediaPresent = false;
bool driveReady = false;
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) );
bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) &senseBuffer,
kSenseDefaultSize,
kIODirectionIn );
request = GetSCSITask ( );
do
{
if ( TEST_UNIT_READY ( request, 0 ) == true )
{
STATUS_LOG ( ( "sending TUR.\n" ) );
serviceResponse = SendCommand ( request, 0 );
}
else
{
PANIC_NOW ( ( "IOSCSIBlockCommandsDevice::VerifyMediumPresence malformed command" ) );
}
if ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE )
{
STATUS_LOG ( ( "TUR completed.\n" ) );
bool validSense = false;
if ( GetTaskStatus ( request ) == kSCSITaskStatus_CHECK_CONDITION )
{
validSense = GetAutoSenseData ( request, &senseBuffer );
if ( validSense == false )
{
if ( REQUEST_SENSE ( request, bufferDesc, kSenseDefaultSize, 0 ) == true )
{
STATUS_LOG ( ( "sending REQ_SENSE.\n" ) );
serviceResponse = SendCommand ( request, 0 );
}
else
{
PANIC_NOW ( ( "IOSCSIBlockCommandsDevice::VerifyMediumPresence malformed command" ) );
}
if ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE )
{
STATUS_LOG ( ( "validSense data.\n" ) );
validSense = true;
}
}
if ( validSense == true )
{
STATUS_LOG ( ( "sense data: %01x, %02x, %02x\n",
( senseBuffer.SENSE_KEY & kSENSE_KEY_Mask ),
senseBuffer.ADDITIONAL_SENSE_CODE,
senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ) );
if ( ( ( senseBuffer.SENSE_KEY & kSENSE_KEY_Mask ) == kSENSE_KEY_NOT_READY ) ||
( ( senseBuffer.SENSE_KEY & kSENSE_KEY_Mask ) == kSENSE_KEY_MEDIUM_ERROR ) )
{
if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x04 ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x02 ) )
{
if ( START_STOP_UNIT ( request, 0x00, 0x00, 0x00, 0x01, 0x00 ) == true )
{
STATUS_LOG ( ( "Sending START_STOP_UNIT.\n" ) );
serviceResponse = SendCommand ( request, 0 );
}
else
{
PANIC_NOW ( ( "IOSCSIBlockCommandsDevice::VerifyMediumPresence malformed command" ) );
}
STATUS_LOG ( ( "%s::drive NOT READY\n", getName ( ) ) );
IOSleep ( 200 );
continue;
}
else if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x3A ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x00 ) )
{
STATUS_LOG ( ( "No Media.\n" ) );
driveReady = true;
mediaPresent = false;
}
else
{
STATUS_LOG ( ( "%s::drive NOT READY\n", getName ( ) ) );
IOSleep ( 200 );
continue;
}
}
else
{
STATUS_LOG ( ( "%s::drive READY, media present\n", getName ( ) ) );
driveReady = true;
mediaPresent = true;
}
}
}
else
{
STATUS_LOG ( ( "%s::drive READY, media present\n", getName ( ) ) );
driveReady = true;
mediaPresent = true;
}
}
else
{
IOSleep ( 200 );
}
} while ( ( driveReady == false ) && ( isInactive ( ) == false ) );
bufferDesc->release ( );
ReleaseSCSITask ( request );
return mediaPresent;
}