#include <IOKit/storage/ata/IOATAHDDrive.h>
#include <IOKit/IOBufferMemoryDescriptor.h>
#define super IOService
#define kIOATAPowerStates 2
static IOPMPowerState ourPowerStates[kIOATAPowerStates] =
{
{1,0,0,0,0,0,0,0,0,0,0,0},
{1, IOPMDeviceUsable, IOPMPowerOn, IOPMPowerOn, 0,0,0,0,0,0,0,0}
};
static const char * ataPowerStateNames[] =
{
"Sleep",
"Standby",
"Idle",
"Active"
};
IOATAPowerState
IOATAHDDrive::getATAPowerStateForStateOrdinal(UInt32 stateOrdinal)
{
IOATAPowerState stateOrdinalToATAPowerState[kIOATAPowerStates] =
{
kIOATAPowerStateStandby,
kIOATAPowerStateActive,
};
if (stateOrdinal > (kIOATAPowerStates - 1))
stateOrdinal = (kIOATAPowerStates - 1);
return stateOrdinalToATAPowerState[stateOrdinal];
}
void
IOATAHDDrive::initForPM()
{
registerPowerDriver(this, ourPowerStates, kIOATAPowerStates);
_pmReady = true;
}
IOReturn
IOATAHDDrive::setAggressiveness(UInt32 type, UInt32 minutes)
{
if (type == kPMMinutesToSpinDown)
{
setIdleTimerPeriod(minutes * 60); }
return super::setAggressiveness(type, minutes);
}
UInt32
IOATAHDDrive::initialPowerStateForDomainState(IOPMPowerFlags domainState)
{
UInt32 ret;
_cmdGate->runAction((IOCommandGate::Action)
&IOATAHDDrive::sHandleInitialPowerStateForDomainState,
(void *) domainState,
(void *) &ret);
return ret;
}
void
IOATAHDDrive::sHandleInitialPowerStateForDomainState(IOATAHDDrive * self,
IOPMPowerFlags domainState,
UInt32 * state)
{
*state = self->handleInitialPowerStateForDomainState(domainState);
}
UInt32
IOATAHDDrive::handleInitialPowerStateForDomainState(IOPMPowerFlags domainState)
{
if (domainState & IOPMPowerOn)
return ((_currentATAPowerState == kIOATAPowerStateActive) ? 1 : 0);
else
return 0;
}
IOReturn
IOATAHDDrive::setPowerState(UInt32 powerStateOrdinal,
IOService * whatDevice)
{
IOReturn ret;
_cmdGate->runAction((IOCommandGate::Action)
&IOATAHDDrive::sHandleSetPowerState,
(void *) powerStateOrdinal,
(void *) whatDevice,
(void *) &ret);
kprintf("%s::%s(0x%08lx, 0x%08lx) returns 0x%08lx\n",getName(), __FUNCTION__,powerStateOrdinal, whatDevice, ret);
return ret;
}
void
IOATAHDDrive::sHandleSetPowerState(IOATAHDDrive * self,
UInt32 powerStateOrdinal,
IOService * whatDevice,
IOReturn * handlerReturn)
{
*handlerReturn = self->handleSetPowerState(powerStateOrdinal, whatDevice);
}
void
IOATAHDDrive::sHandleStandbyStateTransition(IOATAHDDrive * self,
void * stage,
IOReturn status,
UInt64 bytesTransferred)
{
self->handleStandbyStateTransition((UInt32) stage, status);
}
void
IOATAHDDrive::sHandleActiveStateTransition(IOATAHDDrive * self,
void * stage,
IOReturn status,
UInt64 bytesTransferred)
{
self->handleActiveStateTransition((UInt32) stage, status);
}
void
IOATAHDDrive::sHandleIdleStateTransition(IOATAHDDrive * self,
void * stage,
IOReturn status,
UInt64 bytesTransferred)
{
self->handleIdleStateTransition((UInt32) stage, status);
}
void
IOATAHDDrive::sHandleSleepStateTransition(IOATAHDDrive * self,
void * stage,
IOReturn status,
UInt64 bytesTransferred)
{
self->handleSleepStateTransition((UInt32) stage, status);
}
IOReturn
IOATAHDDrive::handleSetPowerState(UInt32 powerStateOrdinal,
IOService * whatDevice)
{
IOATAPowerState ataPowerState =
getATAPowerStateForStateOrdinal(powerStateOrdinal);
#if 1
kprintf("%s::%s %d (%d) %lx\n", getName(), __FUNCTION__, ataPowerState,
_currentATAPowerState, (UInt32) whatDevice);
#endif
if (_powerStateChanging) {
kprintf("%s::%s overlap detected\n",getName(), __FUNCTION__);
IOLog("%s::%s overlap detected\n",getName(), __FUNCTION__);
return IOPMAckImplied; }
if (ataPowerState == _currentATAPowerState) {
kprintf("%s::%s already in the given sate\n",getName(), __FUNCTION__);
return IOPMAckImplied;
}
_powerStateChanging = true;
_setPowerAckPending = true;
startATAPowerStateTransition(ataPowerState);
return (100 * 1000 * 1000);
}
void
IOATAHDDrive::startATAPowerStateTransition(IOATAPowerState ataPowerState)
{
_proposedATAPowerState = ataPowerState;
switch (ataPowerState)
{
case kIOATAPowerStateStandby:
_ataDevice->notifyIdle(this,
(CallbackFn) &IOATAHDDrive::sHandleStandbyStateTransition,
(void *) kIOATAStandbyStage0);
break;
case kIOATAPowerStateActive:
sHandleActiveStateTransition(this,
(void *) kIOATAActiveStage0,
kIOReturnSuccess,
0);
break;
default:
IOPanic("IOATAHDDrive::startATAPowerStateTransition\n");
}
}
void
IOATAHDDrive::abortATAPowerStateTransition()
{
_setPowerAckPending = false;
if (_proposedATAPowerState != _currentATAPowerState)
{
startATAPowerStateTransition(_currentATAPowerState);
}
else
{
IOLog("%s::%s Unable to revert to previous state\n",
getName(), __FUNCTION__);
endATAPowerStateTransition(_currentATAPowerState);
}
}
void
IOATAHDDrive::endATAPowerStateTransition(IOATAPowerState ataPowerState)
{
_currentATAPowerState = ataPowerState;
if (_setPowerAckPending) {
thread_call_func(acknowledgeATAPowerStateTransition, this, 1);
}
}
void
IOATAHDDrive::acknowledgeATAPowerStateTransition(void *castMeToIOATAHDDrive, void*)
{
IOATAHDDrive *myThis = OSDynamicCast(IOATAHDDrive, (OSObject*)castMeToIOATAHDDrive);
if (myThis !=NULL) {
myThis->_powerStateChanging = false;
myThis->acknowledgeSetPowerState();
}
}
void
IOATAHDDrive::handleStandbyStateTransition(UInt32 stage, IOReturn status)
{
bool doAbort = false;
IOATACommand * cmd = 0;
IOStorageCompletion completion;
switch (stage)
{
case kIOATAStandbyStage0:
_ataDevice->holdQueue(kATAQTypeNormalQ);
status = kIOReturnSuccess;
case kIOATAStandbyStage1:
if ( reportATADeviceType() == kATADeviceATA )
{
if ((cmd = ataCommandFlushCache()) == 0)
{
doAbort = true; break;
}
cmd->setQueueInfo(kATAQTypeBypassQ);
completion.target = this;
completion.action = sHandleStandbyStateTransition;
completion.parameter = (void *) kIOATAStandbyStage2;
asyncExecute(cmd,
completion,
60000,
0);
break;
}
case kIOATAStandbyStage2:
if ( reportATADeviceType() == kATADeviceATA )
{
if ((cmd = ataCommandStandby()) == 0)
{
doAbort = true; break;
}
cmd->setQueueInfo(kATAQTypeBypassQ);
completion.target = this;
completion.action = sHandleStandbyStateTransition;
completion.parameter = (void *) kIOATAStandbyStage3;
asyncExecute(cmd,
completion,
30000,
0);
break;
}
case kIOATAStandbyStage3:
if (status != kIOReturnSuccess) {
doAbort = true; break;
}
else {
endATAPowerStateTransition(kIOATAPowerStateStandby);
}
break;
default:
IOLog("%s::%s unknown stage %ld\n", getName(), __FUNCTION__, stage);
}
if (cmd) cmd->release();
if (doAbort)
abortATAPowerStateTransition();
}
void
IOATAHDDrive::handleActiveStateTransition(UInt32 stage, IOReturn status)
{
IOStorageCompletion completion;
#if 0
IOLog("IOATAHDDrive::handleActiveStateTransition %p %ld %x\n",
this, stage, status);
#endif
switch (stage)
{
case kIOATAActiveStage0:
kprintf("kIOATAActiveStage0 current power state is sleep %d\n", _currentATAPowerState == kIOATAPowerStateSleep);
#if 0 // This des not work.
{
kprintf("Attempting to reset on kIOATAActiveStage0\n");
_ataDevice->reset();
}
#endif
case kIOATAActiveStage1:
kprintf("kIOATAActiveStage1\n");
if ( reportATADeviceType() == kATADeviceATA )
{
completion.target = this;
completion.action = sHandleActiveStateTransition,
completion.parameter = (void *) kIOATAActiveStage2;
readSector(completion);
break;
}
case kIOATAActiveStage2:
kprintf("kIOATAActiveStage2\n");
_ataDevice->releaseQueue(kATAQTypeNormalQ);
case kIOATAActiveStage3:
kprintf("kIOATAActiveStage3\n");
endATAPowerStateTransition(kIOATAPowerStateActive);
break;
default:
IOLog("%s::%s unknown stage %ld\n", getName(), __FUNCTION__, stage);
}
}
void
IOATAHDDrive::handleIdleStateTransition(UInt32 stage, IOReturn status)
{
IOLog("%s::%s unimplemented!\n", getName(), __FUNCTION__);
}
void
IOATAHDDrive::handleSleepStateTransition(UInt32 stage, IOReturn status)
{
IOLog("%s::%s unimplemented!\n", getName(), __FUNCTION__);
}
IOReturn IOATAHDDrive::readSector(IOStorageCompletion completion,
UInt32 sector = 0)
{
IOBufferMemoryDescriptor * desc;
IOATACommand * cmd;
IOReturn ret;
desc = IOBufferMemoryDescriptor::withCapacity(kIOATASectorSize,
kIODirectionIn);
if (!desc)
return kIOReturnNoMemory;
desc->setLength(desc->getCapacity());
cmd = ataCommandReadWrite(desc, sector, 1);
if (!cmd)
return kIOReturnNoMemory;
cmd->setQueueInfo(kATAQTypeBypassQ);
ret = asyncExecute(cmd, completion, 60000);
desc->release();
cmd->release();
return kIOReturnSuccess;
}