#include <IOKit/IOService.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOCommandQueue.h>
#include <IOKit/IOCommandGate.h>
#include <IOKit/IOTimerEventSource.h>
#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/assert.h>
#include <IOKit/IOMessage.h>
#include <IOKit/pwr_mgt/IOPMinformee.h>
#include "IOKit/pwr_mgt/IOPMinformeeList.h"
#include "IOKit/pwr_mgt/IOPMchangeNoteList.h"
#include "IOKit/pwr_mgt/IOPMlog.h"
#include "IOKit/pwr_mgt/IOPowerConnection.h"
#include <kern/clock.h>
#define super IORegistryEntry
static void ack_timer_expired(thread_call_param_t);
static void settle_timer_expired(thread_call_param_t);
void PMreceiveCmd ( OSObject *, void *, void *, void *, void * );
static void PM_idle_timer_expired(OSObject *, IOTimerEventSource *);
static void c_PM_Clamp_Timer_Expired (OSObject * client,IOTimerEventSource *);
void tellAppWithResponse ( OSObject * object, void * context);
void tellClientWithResponse ( OSObject * object, void * context);
void tellClient ( OSObject * object, void * context);
IOReturn serializedAllowPowerChange ( OSObject *, void *, void *, void *, void *);
IOReturn serializedCancelPowerChange ( OSObject *, void *, void *, void *, void *);
extern const IORegistryPlane * gIOPowerPlane;
#define ns_per_us 1000
enum {
IOPMour_prechange_03 = 1,
IOPMour_prechange_04,
IOPMour_prechange_05,
IOPMour_prechange_1,
IOPMour_prechange_2,
IOPMour_prechange_3,
IOPMour_prechange_4,
IOPMparent_down_0,
IOPMparent_down_05,
IOPMparent_down_2,
IOPMparent_down_3,
IOPMparent_down_4,
IOPMparent_down_5,
IOPMparent_down_6,
IOPMparent_up_0,
IOPMparent_up_1,
IOPMparent_up_4,
IOPMparent_up_5,
IOPMparent_up_6,
IOPMfinished
};
enum { kNotifyApps,
kNotifyPriority
};
struct context { OSArray * responseFlags;
UInt16 serialNumber;
UInt16 counter;
UInt32 maxTimeRequested;
int msgType;
IOService * us;
IOLock * flags_lock;
unsigned long stateNumber;
IOPMPowerFlags stateFlags;
};
#define FIVE_MINUTES 5*60*1000000
#define k30seconds 30*1000000
const char priv_key[ ] = "Power Management private data";
const char prot_key[ ] = "Power Management protected data";
void IOService::PMinit ( void )
{
if ( ! initialized ) {
pm_vars = new IOPMprot; priv = new IOPMpriv;
pm_vars->init();
priv->init();
setProperty(prot_key, (OSObject *) pm_vars); setProperty(priv_key, (OSObject *) priv);
priv->owner = this;
pm_vars->theNumberOfPowerStates = 0; priv->we_are_root = false;
pm_vars->theControllingDriver = NULL;
priv->our_lock = IOLockAlloc();
priv->flags_lock = IOLockAlloc();
priv->queue_lock = IOLockAlloc();
pm_vars->childLock = IOLockAlloc();
pm_vars->parentLock = IOLockAlloc();
priv->interestedDrivers = new IOPMinformeeList;
priv->interestedDrivers->initialize();
priv->changeList = new IOPMchangeNoteList;
priv->changeList->initialize();
pm_vars->aggressiveness = 0;
for (unsigned int i = 0; i <= kMaxType; i++) {
pm_vars->current_aggressiveness_values[i] = 0;
pm_vars->current_aggressiveness_valid[i] = false;
}
pm_vars->myCurrentState = 0;
priv->imminentState = 0;
priv->ourDesiredPowerState = 0;
pm_vars->parentsCurrentPowerFlags = 0;
pm_vars->maxCapability = 0;
priv->driverDesire = 0;
priv->deviceDesire = 0;
priv->initial_change = true;
priv->need_to_become_usable = false;
priv->previousRequest = 0;
priv->device_overrides = false;
priv->machine_state = IOPMfinished;
pm_vars->commandQueue = NULL;
priv->timerEventSrc = NULL;
priv->clampTimerEventSrc = NULL;
pm_vars->PMworkloop = NULL;
priv->activityLock = NULL;
pm_vars->ourName = getName();
pm_vars->thePlatform = getPlatform();
pm_vars->parentsKnowState = false;
assert( pm_vars->thePlatform != 0 );
priv->clampOn = false;
pm_vars->serialNumber = 0;
pm_vars->responseFlags = NULL;
pm_vars->doNotPowerDown = true;
pm_vars->PMcommandGate = NULL;
priv->ackTimer = thread_call_allocate((thread_call_func_t)ack_timer_expired, (thread_call_param_t)this);
priv->settleTimer = thread_call_allocate((thread_call_func_t)settle_timer_expired, (thread_call_param_t)this);
initialized = true;
}
}
void IOService::PMfree ( void )
{
if ( priv ) {
if ( priv->clampTimerEventSrc != NULL ) {
getPMworkloop()->removeEventSource(priv->clampTimerEventSrc);
priv->clampTimerEventSrc->release();
priv->clampTimerEventSrc = NULL;
}
if ( priv->timerEventSrc != NULL ) {
pm_vars->PMworkloop->removeEventSource(priv->timerEventSrc);
priv->timerEventSrc->release();
priv->timerEventSrc = NULL;
}
if ( priv->settleTimer ) {
thread_call_cancel(priv->settleTimer);
thread_call_free(priv->settleTimer);
priv->settleTimer = NULL;
}
if ( priv->ackTimer ) {
thread_call_cancel(priv->ackTimer);
thread_call_free(priv->ackTimer);
priv->ackTimer = NULL;
}
if ( priv->our_lock ) {
IOLockFree(priv->our_lock);
priv->our_lock = NULL;
}
if ( priv->flags_lock ) {
IOLockFree(priv->flags_lock);
priv->flags_lock = NULL;
}
if ( priv->activityLock ) {
IOLockFree(priv->activityLock);
priv->activityLock = NULL;
}
priv->interestedDrivers->release();
priv->changeList->release();
priv->release(); }
if ( pm_vars ) {
if ( pm_vars->commandQueue ) {
pm_vars->commandQueue->release();
pm_vars->commandQueue = NULL;
}
if ( pm_vars->PMcommandGate ) {
pm_vars->PMcommandGate->release();
pm_vars->PMcommandGate = NULL;
}
if ( pm_vars->PMworkloop ) {
pm_vars->PMworkloop = NULL;
}
if ( pm_vars->responseFlags ) {
pm_vars->responseFlags->release();
pm_vars->responseFlags = NULL;
}
pm_vars->release(); }
}
void IOService::PMstop ( void )
{
OSIterator * iter;
OSObject * next;
IOPowerConnection * connection;
IOService * theChild;
IOService * theParent;
removeProperty(prot_key); removeProperty(priv_key);
iter = getParentIterator(gIOPowerPlane);
if ( iter ) {
while ( (next = iter->getNextObject()) ) {
if ( (connection = OSDynamicCast(IOPowerConnection,next)) ) {
theParent = (IOService *)connection->copyParentEntry(gIOPowerPlane);
if ( theParent ) {
theParent->removePowerChild(connection);
theParent->release();
}
}
}
iter->release();
}
detachAbove( gIOPowerPlane );
pm_vars->parentsKnowState = false;
iter = getChildIterator(gIOPowerPlane);
if ( iter ) {
while ( (next = iter->getNextObject()) ) {
if ( (connection = OSDynamicCast(IOPowerConnection,next)) ) {
theChild = ((IOService *)(connection->copyChildEntry(gIOPowerPlane)));
if ( theChild ) {
connection->detachFromChild(theChild,gIOPowerPlane); theChild->release();
}
detachFromChild(connection,gIOPowerPlane); }
}
iter->release();
}
if ( priv && priv->interestedDrivers )
{
IOPMinformee * informee;
while (( informee = priv->interestedDrivers->firstInList() ))
deRegisterInterestedDriver( informee->whatObject );
}
}
void IOService::joinPMtree ( IOService * driver )
{
IOPlatformExpert * thePlatform;
thePlatform = getPlatform();
assert(thePlatform != 0 );
thePlatform->PMRegisterDevice(this,driver);
}
IOReturn IOService::youAreRoot ( void )
{
priv-> we_are_root = true;
pm_vars->parentsKnowState = true;
attachToParent( getRegistryRoot(),gIOPowerPlane );
return IOPMNoErr;
}
IOReturn IOService::setPowerParent ( IOPowerConnection * theParent, bool stateKnown, IOPMPowerFlags currentState )
{
OSIterator * iter;
OSObject * next;
IOPowerConnection * connection;
unsigned long tempDesire;
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogSetParent,stateKnown,currentState);
IOLockLock(pm_vars->parentLock);
if ( stateKnown && ((pm_vars->PMworkloop == NULL) || (pm_vars->PMcommandGate == NULL)) ) {
getPMworkloop(); if ( pm_vars->PMworkloop != NULL ) { if ( pm_vars->PMcommandGate == NULL ) { pm_vars->PMcommandGate = IOCommandGate::commandGate((OSObject *)this);
if ( pm_vars->PMcommandGate != NULL ) {
pm_vars->PMworkloop->addEventSource(pm_vars->PMcommandGate);
}
}
}
}
IOLockUnlock(pm_vars->parentLock);
theParent->setParentCurrentPowerFlags(currentState); theParent->setParentKnowsState(stateKnown);
pm_vars->parentsKnowState = true; pm_vars->parentsCurrentPowerFlags = 0;
iter = getParentIterator(gIOPowerPlane);
if ( iter ) {
while ( (next = iter->getNextObject()) ) {
if ( (connection = OSDynamicCast(IOPowerConnection,next)) ) {
pm_vars->parentsKnowState &= connection->parentKnowsState();
pm_vars->parentsCurrentPowerFlags |= connection->parentCurrentPowerFlags();
}
}
iter->release();
}
if ( (pm_vars->theControllingDriver != NULL) &&
(pm_vars->parentsKnowState) ) {
pm_vars->maxCapability = pm_vars->theControllingDriver->maxCapabilityForDomainState(pm_vars->parentsCurrentPowerFlags);
tempDesire = priv->deviceDesire; priv->deviceDesire = pm_vars->theControllingDriver->initialPowerStateForDomainState(pm_vars->parentsCurrentPowerFlags);
computeDesiredState();
priv->previousRequest = 0xffffffff;
changeState();
priv->deviceDesire = tempDesire; }
return IOPMNoErr;
}
IOReturn IOService::addPowerChild ( IOService * theChild )
{
IOPowerConnection * connection;
unsigned int i;
if ( ! initialized ) {
return IOPMNotYetInitialized; }
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogAddChild,0,0);
connection = new IOPowerConnection;
connection->init();
connection->start(this);
connection->setAwaitingAck(false);
attachToChild( connection,gIOPowerPlane ); connection->attachToChild( theChild,gIOPowerPlane );
connection->release();
if ( (pm_vars->theControllingDriver == NULL) || ! (inPlane(gIOPowerPlane)) ||
! (pm_vars->parentsKnowState) ) {
theChild->setPowerParent(connection,false,0);
if ( inPlane(gIOPowerPlane) ) {
for (i = 0; i <= kMaxType; i++) {
if ( pm_vars->current_aggressiveness_valid[i] ) {
theChild->setAggressiveness (i, pm_vars->current_aggressiveness_values[i]);
}
}
}
}
else {
theChild->setPowerParent(connection,true,pm_vars->thePowerStates[pm_vars->myCurrentState].outputPowerCharacter);
for (i = 0; i <= kMaxType; i++) {
if ( pm_vars->current_aggressiveness_valid[i] ) {
theChild->setAggressiveness (i, pm_vars->current_aggressiveness_values[i]);
}
}
add_child_to_active_change(connection); }
return IOPMNoErr;
}
IOReturn IOService::removePowerChild ( IOPowerConnection * theNub )
{
IORegistryEntry * theChild;
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogRemoveChild,0,0);
theNub->retain();
theChild = theNub->copyChildEntry(gIOPowerPlane); if ( theChild ) {
theNub->detachFromChild(theChild, gIOPowerPlane);
theChild->release();
}
detachFromChild(theNub,gIOPowerPlane);
if ( theNub->getAwaitingAck() ) { theNub->setAwaitingAck(false); if ( acquire_lock() ) {
if (priv->head_note_pendingAcks != 0 ) {
priv->head_note_pendingAcks -= 1; if ( priv->head_note_pendingAcks == 0 ) { stop_ack_timer(); IOUnlock(priv->our_lock);
all_acked(); }
else {
IOUnlock(priv->our_lock);
}
}
else {
IOUnlock(priv->our_lock);
}
}
}
theNub->release();
if ( (pm_vars->theControllingDriver == NULL) || ! (inPlane(gIOPowerPlane)) ||
! (pm_vars->parentsKnowState) ) {
return IOPMNoErr; }
computeDesiredState(); changeState();
return IOPMNoErr;
}
IOReturn IOService::registerPowerDriver ( IOService * controllingDriver, IOPMPowerState* powerStates, unsigned long numberOfStates )
{
unsigned long i;
unsigned long tempDesire;
if ( (numberOfStates > pm_vars->theNumberOfPowerStates) && (numberOfStates > 1) ) {
if ( priv->changeList->currentChange() == -1 ) {
if ( controllingDriver != NULL ) {
if ( numberOfStates <= IOPMMaxPowerStates ) {
switch ( powerStates[0].version ) {
case 1:
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogControllingDriver,
(unsigned long)numberOfStates, (unsigned long)powerStates[0].version);
for ( i = 0; i < numberOfStates; i++ ) {
pm_vars->thePowerStates[i] = powerStates[i];
}
break;
case 2:
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogControllingDriver,
(unsigned long) numberOfStates,(unsigned long) powerStates[0].version);
for ( i = 0; i < numberOfStates; i++ ) {
pm_vars->thePowerStates[i].version = powerStates[i].version;
pm_vars->thePowerStates[i].capabilityFlags = powerStates[i].capabilityFlags;
pm_vars->thePowerStates[i].outputPowerCharacter = powerStates[i].outputPowerCharacter;
pm_vars->thePowerStates[i].inputPowerRequirement = powerStates[i].inputPowerRequirement;
pm_vars->thePowerStates[i].staticPower = powerStates[i].staticPower;
pm_vars->thePowerStates[i].unbudgetedPower = powerStates[i].unbudgetedPower;
pm_vars->thePowerStates[i].powerToAttain = powerStates[i].powerToAttain;
pm_vars->thePowerStates[i].timeToAttain = powerStates[i].timeToAttain;
pm_vars->thePowerStates[i].settleUpTime = powerStates[i].settleUpTime;
pm_vars->thePowerStates[i].timeToLower = powerStates[i].timeToLower;
pm_vars->thePowerStates[i].settleDownTime = powerStates[i].settleDownTime;
pm_vars->thePowerStates[i].powerDomainBudget = powerStates[i].powerDomainBudget;
}
break;
default:
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogControllingDriverErr1,
(unsigned long)powerStates[0].version,0);
return IOPMNoErr;
}
pm_vars->myCharacterFlags = 0; for ( i = 0; i < numberOfStates; i++ ) {
pm_vars->myCharacterFlags |= pm_vars->thePowerStates[i].outputPowerCharacter;
}
pm_vars->theNumberOfPowerStates = numberOfStates;
pm_vars->theControllingDriver = controllingDriver;
if ( priv->interestedDrivers->findItem(controllingDriver) == NULL ) { registerInterestedDriver (controllingDriver ); }
if ( priv->need_to_become_usable ) {
priv->need_to_become_usable = false;
priv->deviceDesire = pm_vars->theNumberOfPowerStates - 1;
}
if ( inPlane(gIOPowerPlane) &&
(pm_vars->parentsKnowState) ) {
pm_vars->maxCapability = pm_vars->theControllingDriver->maxCapabilityForDomainState(pm_vars->parentsCurrentPowerFlags);
tempDesire = priv->deviceDesire; priv->deviceDesire = pm_vars->theControllingDriver->initialPowerStateForDomainState(pm_vars->parentsCurrentPowerFlags);
computeDesiredState();
changeState();
priv->deviceDesire = tempDesire; }
}
else {
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogControllingDriverErr2,(unsigned long)numberOfStates,0);
}
}
else {
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogControllingDriverErr4,0,0);
}
}
}
else {
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogControllingDriverErr5,(unsigned long)numberOfStates,0);
}
return IOPMNoErr;
}
IOPMPowerFlags IOService::registerInterestedDriver ( IOService * theDriver )
{
IOPMinformee * newInformee;
IOPMPowerFlags futureCapability;
if (theDriver == NULL ) {
return 0;
}
newInformee = new IOPMinformee; newInformee->initialize(theDriver);
priv->interestedDrivers->addToList(newInformee);
if ( (pm_vars->theControllingDriver == NULL) ||
! (inPlane(gIOPowerPlane)) ||
! (pm_vars->parentsKnowState) ) {
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogInterestedDriver,IOPMNotPowerManaged,0);
return IOPMNotPowerManaged; }
switch (priv->machine_state) { case IOPMour_prechange_1:
case IOPMour_prechange_4:
case IOPMparent_down_4:
case IOPMparent_down_6:
case IOPMparent_up_0:
case IOPMparent_up_6:
futureCapability = priv->head_note_capabilityFlags; pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogInterestedDriver,(unsigned long)futureCapability,1);
add_driver_to_active_change(newInformee); return futureCapability; }
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogInterestedDriver,
(unsigned long) pm_vars->thePowerStates[pm_vars->myCurrentState].capabilityFlags,2);
return pm_vars->thePowerStates[pm_vars->myCurrentState].capabilityFlags; }
IOReturn IOService::deRegisterInterestedDriver ( IOService * theDriver )
{
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogRemoveDriver,0,0);
priv->interestedDrivers->removeFromList(theDriver);
return IOPMNoErr;
}
IOReturn IOService::acknowledgePowerChange ( IOService * whichObject )
{
IOPMinformee * ackingObject;
unsigned long childPower = kIOPMUnknown;
IOService * theChild;
ackingObject = priv->interestedDrivers->findItem(whichObject); if ( ackingObject == NULL ) {
if ( ! isChild(whichObject,gIOPowerPlane) ) {
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogAcknowledgeErr1,0,0);
kprintf("errant driver: %s\n",whichObject->getName());
return IOPMNoErr; }
else {
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogChildAcknowledge,priv->head_note_pendingAcks,0);
}
}
else {
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogDriverAcknowledge,priv->head_note_pendingAcks,0);
}
if (! acquire_lock() ) {
return IOPMNoErr;
}
if (priv->head_note_pendingAcks != 0 ) { if ( ackingObject != NULL ) { if ( ackingObject->timer != 0 ) { ackingObject->timer = 0; priv->head_note_pendingAcks -= 1; if ( priv->head_note_pendingAcks == 0 ) { stop_ack_timer(); IOUnlock(priv->our_lock);
all_acked(); return IOPMNoErr;
}
}
else {
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogAcknowledgeErr2,0,0); kprintf("errant driver: %s\n",whichObject->getName());
}
}
else { if ( ((IOPowerConnection *)whichObject)->getAwaitingAck() ) { priv->head_note_pendingAcks -= 1; ((IOPowerConnection *)whichObject)->setAwaitingAck(false);
theChild = (IOService *)whichObject->copyChildEntry(gIOPowerPlane);
if ( theChild ) {
childPower = theChild->currentPowerConsumption();
theChild->release();
}
if ( childPower == kIOPMUnknown ) {
pm_vars->thePowerStates[priv->head_note_state].staticPower = kIOPMUnknown;
}
else {
if ( pm_vars->thePowerStates[priv->head_note_state].staticPower != kIOPMUnknown ) {
pm_vars->thePowerStates[priv->head_note_state].staticPower += childPower;
}
}
if ( priv->head_note_pendingAcks == 0 ) { stop_ack_timer(); IOUnlock(priv->our_lock);
all_acked(); return IOPMNoErr;
}
}
}
}
else {
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogAcknowledgeErr3,0,0); kprintf("errant driver: %s\n",whichObject->getName());
}
IOUnlock(priv->our_lock);
return IOPMNoErr;
}
IOReturn IOService::acknowledgeSetPowerState ( void )
{
if (! acquire_lock() ) {
return IOPMNoErr;
}
if ( priv->driver_timer == -1 ) {
priv->driver_timer = 0; }
else {
if ( priv->driver_timer > 0 ) { stop_ack_timer(); priv->driver_timer = 0;
IOUnlock(priv->our_lock);
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogDriverAcknowledgeSet,0,0);
driver_acked();
return IOPMNoErr;
}
else {
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogAcknowledgeErr4,0,0); }
}
IOUnlock(priv->our_lock);
return IOPMNoErr;
}
void IOService::driver_acked ( void )
{
switch (priv->machine_state) {
case IOPMour_prechange_2:
our_prechange_2();
break;
case IOPMparent_down_5:
parent_down_5();
break;
case IOPMparent_up_4:
parent_up_4();
break;
}
}
IOReturn IOService::powerDomainWillChangeTo ( IOPMPowerFlags newPowerStateFlags, IOPowerConnection * whichParent )
{
OSIterator * iter;
OSObject * next;
IOPowerConnection * connection;
unsigned long newStateNumber;
IOPMPowerFlags combinedPowerFlags;
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogWillChange,(unsigned long)newPowerStateFlags,0);
if ( ! inPlane(gIOPowerPlane) ) {
return IOPMAckImplied; }
IOLockLock(pm_vars->parentLock);
if ( (pm_vars->PMworkloop == NULL) || (pm_vars->PMcommandGate == NULL) ) {
getPMworkloop(); if ( pm_vars->PMworkloop != NULL ) { if ( pm_vars->PMcommandGate == NULL ) { pm_vars->PMcommandGate = IOCommandGate::commandGate((OSObject *)this);
if ( pm_vars->PMcommandGate != NULL ) {
pm_vars->PMworkloop->addEventSource(pm_vars->PMcommandGate);
}
}
}
}
IOLockUnlock(pm_vars->parentLock);
combinedPowerFlags = 0;
iter = getParentIterator(gIOPowerPlane);
if ( iter ) {
while ( (next = iter->getNextObject()) ) {
if ( (connection = OSDynamicCast(IOPowerConnection,next)) ) {
if ( connection == whichParent ){
combinedPowerFlags |= newPowerStateFlags;
}
else {
combinedPowerFlags |= connection->parentCurrentPowerFlags();
}
}
}
iter->release();
}
if ( pm_vars->theControllingDriver == NULL ) { return IOPMAckImplied;
}
newStateNumber = pm_vars->theControllingDriver->maxCapabilityForDomainState(combinedPowerFlags);
return enqueuePowerChange(IOPMParentInitiated | IOPMDomainWillChange,
newStateNumber,combinedPowerFlags,whichParent,newPowerStateFlags); }
IOReturn IOService::powerDomainDidChangeTo ( IOPMPowerFlags newPowerStateFlags, IOPowerConnection * whichParent )
{
unsigned long newStateNumber;
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogDidChange,newPowerStateFlags,0);
setParentInfo(newPowerStateFlags,whichParent);
if ( pm_vars->theControllingDriver == NULL ) {
return IOPMAckImplied;
}
newStateNumber = pm_vars->theControllingDriver->maxCapabilityForDomainState(pm_vars->parentsCurrentPowerFlags);
return enqueuePowerChange(IOPMParentInitiated | IOPMDomainDidChange,
newStateNumber,pm_vars->parentsCurrentPowerFlags,whichParent,0); }
void IOService::setParentInfo ( IOPMPowerFlags newPowerStateFlags, IOPowerConnection * whichParent )
{
OSIterator * iter;
OSObject * next;
IOPowerConnection * connection;
whichParent->setParentCurrentPowerFlags(newPowerStateFlags); whichParent->setParentKnowsState(true);
IOLockLock(pm_vars->parentLock);
pm_vars->parentsCurrentPowerFlags = 0; pm_vars->parentsKnowState = true;
iter = getParentIterator(gIOPowerPlane);
if ( iter ) {
while ( (next = iter->getNextObject()) ) {
if ( (connection = OSDynamicCast(IOPowerConnection,next)) ) {
pm_vars->parentsKnowState &= connection->parentKnowsState();
pm_vars->parentsCurrentPowerFlags |= connection->parentCurrentPowerFlags();
}
}
iter->release();
}
IOLockUnlock(pm_vars->parentLock);
}
IOReturn IOService::requestPowerDomainState ( IOPMPowerFlags desiredState, IOPowerConnection * whichChild, unsigned long specification )
{
unsigned long i;
unsigned long computedState;
unsigned long theDesiredState = desiredState & ~(kIOPMPreventIdleSleep | kIOPMPreventSystemSleep);
OSIterator * iter;
OSObject * next;
IOPowerConnection * connection;
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogRequestDomain,
(unsigned long)desiredState,(unsigned long)specification);
if ( pm_vars->theControllingDriver == NULL) {
return IOPMNotYetInitialized;
}
switch (specification) {
case IOPMLowestState:
i = 0;
while ( i < pm_vars->theNumberOfPowerStates ) {
if ( ( pm_vars->thePowerStates[i].outputPowerCharacter & theDesiredState) == (theDesiredState & pm_vars->myCharacterFlags) ) {
break;
}
i++;
}
if ( i >= pm_vars->theNumberOfPowerStates ) {
return IOPMNoSuchState;
}
break;
case IOPMNextLowerState:
i = pm_vars->myCurrentState - 1;
while ( i >= 0 ) {
if ( ( pm_vars->thePowerStates[i].outputPowerCharacter & theDesiredState) == (theDesiredState & pm_vars->myCharacterFlags) ) {
break;
}
i--;
}
if ( i < 0 ) {
return IOPMNoSuchState;
}
break;
case IOPMHighestState:
i = pm_vars->theNumberOfPowerStates;
while ( i >= 0 ) {
i--;
if ( ( pm_vars->thePowerStates[i].outputPowerCharacter & theDesiredState) == (theDesiredState & pm_vars->myCharacterFlags) ) {
break;
}
}
if ( i < 0 ) {
return IOPMNoSuchState;
}
break;
case IOPMNextHigherState:
i = pm_vars->myCurrentState + 1;
while ( i < pm_vars->theNumberOfPowerStates ) {
if ( ( pm_vars->thePowerStates[i].outputPowerCharacter & theDesiredState) == (theDesiredState & pm_vars->myCharacterFlags) ) {
break;
}
i++;
}
if ( i == pm_vars->theNumberOfPowerStates ) {
return IOPMNoSuchState;
}
break;
default:
return IOPMBadSpecification;
}
computedState = i;
IOLockLock(pm_vars->childLock);
for ( i = 0; i < pm_vars->theNumberOfPowerStates; i++ ) {
pm_vars->thePowerStates[i].capabilityFlags &= ~(kIOPMChildClamp | kIOPMChildClamp2);
}
iter = getChildIterator(gIOPowerPlane);
if ( iter ) {
while ( (next = iter->getNextObject()) ) {
if ( (connection = OSDynamicCast(IOPowerConnection,next)) ) {
if ( connection == whichChild ) {
connection->setDesiredDomainState(computedState);
connection->setPreventIdleSleepFlag(desiredState & kIOPMPreventIdleSleep);
connection->setPreventSystemSleepFlag(desiredState & kIOPMPreventSystemSleep);
connection->setChildHasRequestedPower();
}
if ( connection->getPreventIdleSleepFlag() ) {
pm_vars->thePowerStates[connection->getDesiredDomainState()].capabilityFlags |= kIOPMChildClamp;
}
if ( connection->getPreventSystemSleepFlag() ) {
pm_vars->thePowerStates[connection->getDesiredDomainState()].capabilityFlags |= kIOPMChildClamp2;
}
}
}
iter->release();
}
IOLockUnlock(pm_vars->childLock);
computeDesiredState();
if ( inPlane(gIOPowerPlane) &&
(pm_vars->parentsKnowState) ) {
changeState(); }
if ( priv->clampOn ) { priv->clampOn = false; changePowerStateToPriv(0);
}
return IOPMNoErr;
}
IOReturn IOService::temporaryPowerClampOn ( void )
{
priv->clampOn = true;
makeUsable();
return IOPMNoErr;
}
IOReturn IOService::makeUsable ( void )
{
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogMakeUsable,0,0);
if ( pm_vars->theControllingDriver == NULL ) {
priv->need_to_become_usable = true;
return IOPMNoErr;
}
priv->deviceDesire = pm_vars->theNumberOfPowerStates - 1;
computeDesiredState();
if ( inPlane(gIOPowerPlane) && (pm_vars->parentsKnowState) ) {
return changeState();
}
return IOPMNoErr;
}
IOPMPowerFlags IOService::currentCapability ( void )
{
if ( pm_vars->theControllingDriver == NULL ) {
return 0;
}
else {
return pm_vars->thePowerStates[pm_vars->myCurrentState].capabilityFlags;
}
}
IOReturn IOService::changePowerStateTo ( unsigned long ordinal )
{
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogChangeStateTo,ordinal,0);
if ( ordinal >= pm_vars->theNumberOfPowerStates ) {
return IOPMParameterError;
}
priv->driverDesire = ordinal;
computeDesiredState();
if ( inPlane(gIOPowerPlane) && (pm_vars->parentsKnowState) ) {
return changeState();
}
return IOPMNoErr;
}
IOReturn IOService::changePowerStateToPriv ( unsigned long ordinal )
{
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogChangeStateToPriv,ordinal,0);
if ( pm_vars->theControllingDriver == NULL) {
return IOPMNotYetInitialized;
}
if ( ordinal >= pm_vars->theNumberOfPowerStates ) {
return IOPMParameterError;
}
priv->deviceDesire = ordinal;
computeDesiredState();
if ( inPlane(gIOPowerPlane) && (pm_vars->parentsKnowState) ) {
return changeState();
}
return IOPMNoErr;
}
void IOService::computeDesiredState ( void )
{
OSIterator * iter;
OSObject * next;
IOPowerConnection * connection;
unsigned long newDesiredState = 0;
if ( ! priv->device_overrides ) {
iter = getChildIterator(gIOPowerPlane);
if ( iter ) {
while ( (next = iter->getNextObject()) ) {
if ( (connection = OSDynamicCast(IOPowerConnection,next)) ) {
if ( connection->getDesiredDomainState() > newDesiredState ) {
newDesiredState = connection->getDesiredDomainState();
}
}
}
iter->release();
}
if ( priv->driverDesire > newDesiredState ) {
newDesiredState = priv->driverDesire;
}
}
if ( priv->deviceDesire > newDesiredState ) {
newDesiredState = priv->deviceDesire;
}
priv->ourDesiredPowerState = newDesiredState;
}
IOReturn IOService::changeState ( void )
{
if ( (pm_vars->theControllingDriver == NULL) || ! (inPlane(gIOPowerPlane)) ||
! (pm_vars->parentsKnowState) ) {
return IOPMNoErr; }
return enqueuePowerChange(IOPMWeInitiated,priv->ourDesiredPowerState,0,0,0);
}
unsigned long IOService::currentPowerConsumption ( void )
{
if ( pm_vars->theControllingDriver == NULL ) {
return kIOPMUnknown;
}
if ( pm_vars->thePowerStates[pm_vars->myCurrentState].capabilityFlags & kIOPMStaticPowerValid ) {
return pm_vars->thePowerStates[pm_vars->myCurrentState].staticPower;
}
return kIOPMUnknown;
}
bool IOService::activityTickle ( unsigned long type, unsigned long stateNumber=0 )
{
AbsoluteTime uptime;
if ( type == kIOPMSuperclassPolicy1 ) {
if ( (priv->activityLock == NULL) ||
(pm_vars->theControllingDriver == NULL) ||
(pm_vars->commandQueue == NULL) ) {
return true;
}
IOTakeLock(priv->activityLock);
priv->device_active = true;
clock_get_uptime(&uptime);
priv->device_active_timestamp = uptime;
if ( pm_vars->myCurrentState >= stateNumber) {
IOUnlock(priv->activityLock);
return true;
}
IOUnlock(priv->activityLock); pm_vars->commandQueue->enqueueCommand(true, (void *)kIOPMUnidleDevice, (void *)stateNumber);
return false;
}
return true;
}
IOWorkLoop * IOService::getPMworkloop ( void )
{
IOService * nub;
IOService * parent;
if ( ! inPlane(gIOPowerPlane) ) {
return NULL;
}
if ( pm_vars->PMworkloop == NULL ) { nub = (IOService *)copyParentEntry(gIOPowerPlane);
if ( nub ) {
parent = (IOService *)nub->copyParentEntry(gIOPowerPlane);
nub->release();
if ( parent ) { pm_vars->PMworkloop = parent->getPMworkloop();
parent->release();
}
}
}
return pm_vars->PMworkloop;
}
IOReturn IOService::setIdleTimerPeriod ( unsigned long period )
{
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMsetIdleTimerPeriod,period, 0);
priv->idle_timer_period = period;
if ( period > 0 ) {
if ( getPMworkloop() == NULL ) {
return kIOReturnError;
}
if (pm_vars->commandQueue == NULL ) { pm_vars->commandQueue = IOCommandQueue::commandQueue(this, PMreceiveCmd);
if (! pm_vars->commandQueue ||
( pm_vars->PMworkloop->addEventSource( pm_vars->commandQueue) != kIOReturnSuccess) ) {
return kIOReturnError;
}
}
if ( priv->timerEventSrc == NULL ) {
priv->timerEventSrc = IOTimerEventSource::timerEventSource(this,
PM_idle_timer_expired);
if ( ! priv->timerEventSrc ||
( pm_vars->PMworkloop->addEventSource( priv->timerEventSrc) != kIOReturnSuccess) ) {
return kIOReturnError;
}
}
if ( priv->activityLock == NULL ) {
priv->activityLock = IOLockAlloc();
}
start_PM_idle_timer();
}
return IOPMNoErr;
}
void IOService::start_PM_idle_timer ( void )
{
AbsoluteTime uptime;
AbsoluteTime delta;
UInt64 delta_ns;
UInt64 delta_secs;
UInt64 delay_secs;
IOLockLock(priv->activityLock);
clock_get_uptime(&uptime);
delta = uptime;
SUB_ABSOLUTETIME(&delta, &(priv->device_active_timestamp));
absolutetime_to_nanoseconds(delta, &delta_ns);
delta_secs = delta_ns / NSEC_PER_SEC;
if (delta_secs < priv->idle_timer_period ) {
delay_secs = priv->idle_timer_period - delta_secs;
} else {
delay_secs = priv->idle_timer_period;
}
priv->timerEventSrc->setTimeout(delay_secs, NSEC_PER_SEC);
IOLockUnlock(priv->activityLock);
return;
}
void PM_idle_timer_expired(OSObject * ourSelves, IOTimerEventSource *)
{
((IOService *)ourSelves)->PM_idle_timer_expiration();
}
void IOService::PM_idle_timer_expiration ( void )
{
if ( ! initialized ) {
return; }
if ( priv->idle_timer_period > 0 ) {
IOTakeLock(priv->activityLock);
if ( priv->device_active ) {
priv->device_active = false;
IOUnlock(priv->activityLock);
start_PM_idle_timer();
return;
}
if ( pm_vars->myCurrentState > 0 ) {
IOUnlock(priv->activityLock);
changePowerStateToPriv(pm_vars->myCurrentState - 1);
start_PM_idle_timer();
return;
}
IOUnlock(priv->activityLock);
start_PM_idle_timer();
}
}
void PMreceiveCmd ( OSObject * theDriver, void * command, void * param1, void * param2, void *param3 )
{
((IOService *)theDriver)->command_received(command,param1,param2,param3);
}
void IOService::command_received ( void * command, void *stateNumber , void * , void *)
{
if ( ! initialized ) {
return; }
if ( command == (void *)kIOPMUnidleDevice ) {
if ( (pm_vars->myCurrentState < (unsigned long)stateNumber) &&
(priv->imminentState < (unsigned long)stateNumber) ) {
changePowerStateToPriv((unsigned long)stateNumber);
}
}
}
IOReturn IOService::setAggressiveness ( unsigned long type, unsigned long newLevel )
{
OSIterator * iter;
OSObject * next;
IOPowerConnection * connection;
IOService * child;
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogSetAggressiveness,type, newLevel);
if ( type <= kMaxType ) {
pm_vars->current_aggressiveness_values[type] = newLevel;
pm_vars->current_aggressiveness_valid[type] = true;
}
iter = getChildIterator(gIOPowerPlane);
if ( iter ) {
while ( (next = iter->getNextObject()) ) {
if ( (connection = OSDynamicCast(IOPowerConnection,next)) ) {
child = ((IOService *)(connection->copyChildEntry(gIOPowerPlane)));
if ( child ) {
child->setAggressiveness(type, newLevel);
child->release();
}
}
}
iter->release();
}
return IOPMNoErr;
}
IOReturn IOService::getAggressiveness ( unsigned long type, unsigned long * currentLevel )
{
if ( type <= kMaxType ) {
*currentLevel = pm_vars->current_aggressiveness_values[type];
}
return kIOReturnSuccess;
}
IOReturn IOService::systemWake ( void )
{
OSIterator * iter;
OSObject * next;
IOPowerConnection * connection;
IOService * theChild;
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogSystemWake,0, 0);
iter = getChildIterator(gIOPowerPlane);
if ( iter ) {
while ( (next = iter->getNextObject()) ) {
if ( (connection = OSDynamicCast(IOPowerConnection,next)) ) {
theChild = (IOService *)connection->copyChildEntry(gIOPowerPlane);
if ( theChild ) {
theChild->systemWake();
theChild->release();
}
}
}
iter->release();
}
if ( pm_vars->theControllingDriver != NULL ) {
if ( pm_vars->theControllingDriver->didYouWakeSystem() ) {
makeUsable();
}
}
return IOPMNoErr;
}
IOReturn IOService::temperatureCriticalForZone ( IOService * whichZone )
{
IOService * theParent;
IOService * theNub;
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogCriticalTemp,0,0);
if ( inPlane(gIOPowerPlane) && ! (priv->we_are_root) ) {
theNub = (IOService *)copyParentEntry(gIOPowerPlane);
if ( theNub ) {
theParent = (IOService *)theNub->copyParentEntry(gIOPowerPlane);
theNub->release();
if ( theParent ) {
theParent->temperatureCriticalForZone(whichZone);
theParent->release();
}
}
}
return IOPMNoErr;
}
IOReturn IOService::powerOverrideOnPriv ( void )
{
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogOverrideOn,0,0);
priv->device_overrides = true; computeDesiredState();
return changeState(); }
IOReturn IOService::powerOverrideOffPriv ( void )
{
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogOverrideOff,0,0);
priv->device_overrides = false; computeDesiredState();
if( priv->clampOn)
return makeUsable();
else
return changeState(); }
IOReturn IOService::enqueuePowerChange ( unsigned long flags, unsigned long whatStateOrdinal, unsigned long domainState, IOPowerConnection * whichParent, unsigned long singleParentState )
{
long newNote;
long previousNote;
IOLockLock(priv->queue_lock);
newNote = priv->changeList->createChangeNote();
if ( newNote == -1 ) {
IOLockUnlock(priv->queue_lock);
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogEnqueueErr,0,0);
return IOPMAckImplied; }
priv->changeList->changeNote[newNote].newStateNumber = whatStateOrdinal;
priv->changeList->changeNote[newNote].outputPowerCharacter = pm_vars->thePowerStates[whatStateOrdinal].outputPowerCharacter;
priv->changeList->changeNote[newNote].inputPowerRequirement = pm_vars->thePowerStates[whatStateOrdinal].inputPowerRequirement;
priv->changeList->changeNote[newNote].capabilityFlags = pm_vars->thePowerStates[whatStateOrdinal].capabilityFlags;
priv->changeList->changeNote[newNote].flags = flags;
if (flags & IOPMParentInitiated ) {
priv->changeList->changeNote[newNote].domainState = domainState;
priv->changeList->changeNote[newNote].parent = whichParent;
whichParent->retain();
priv->changeList->changeNote[newNote].singleParentState = singleParentState;
}
previousNote = priv->changeList->previousChangeNote(newNote);
if ( previousNote == -1 ) {
if (flags & IOPMWeInitiated ) {
IOLockUnlock(priv->queue_lock);
start_our_change(newNote);
return 0;
}
else {
IOLockUnlock(priv->queue_lock);
return start_parent_change(newNote);
}
}
while ( (previousNote != priv->head_note) && (previousNote != -1) &&
(priv->changeList->changeNote[newNote].flags & priv->changeList->changeNote[previousNote].flags & IOPMWeInitiated) ) {
priv->changeList->changeNote[previousNote].outputPowerCharacter = priv->changeList->changeNote[newNote].outputPowerCharacter;
priv->changeList->changeNote[previousNote].inputPowerRequirement = priv->changeList->changeNote[newNote].inputPowerRequirement;
priv->changeList->changeNote[previousNote].capabilityFlags =priv-> changeList->changeNote[newNote].capabilityFlags;
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogCollapseQueue,priv->changeList->changeNote[newNote].newStateNumber,
priv->changeList->changeNote[previousNote].newStateNumber);
priv->changeList->changeNote[previousNote].newStateNumber = priv->changeList->changeNote[newNote].newStateNumber;
priv->changeList->releaseTailChangeNote();
newNote = previousNote;
previousNote = priv->changeList->previousChangeNote(newNote);
}
IOLockUnlock(priv->queue_lock);
return IOPMWillAckLater; }
IOReturn IOService::notifyAll ( bool is_prechange )
{
IOPMinformee * nextObject;
OSIterator * iter;
OSObject * next;
IOPowerConnection * connection;
priv->head_note_pendingAcks =1;
nextObject = priv->interestedDrivers->firstInList(); while ( nextObject != NULL ) {
priv->head_note_pendingAcks +=1;
if (! inform(nextObject, is_prechange) ) {
}
nextObject = priv->interestedDrivers->nextInList(nextObject);
}
if (! acquire_lock() ) {
return IOPMNoErr;
}
if ( priv->head_note_pendingAcks > 1 ) { pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogStartAckTimer,0,0); start_ack_timer();
}
IOUnlock(priv->our_lock);
iter = getChildIterator(gIOPowerPlane); pm_vars->thePowerStates[priv->head_note_state].staticPower = 0;
if ( iter ) {
while ( (next = iter->getNextObject()) ) {
if ( (connection = OSDynamicCast(IOPowerConnection,next)) ) {
priv->head_note_pendingAcks +=1;
notifyChild(connection, is_prechange);
}
}
iter->release();
}
if (! acquire_lock() ) {
return IOPMNoErr;
}
priv->head_note_pendingAcks -= 1; if (priv->head_note_pendingAcks == 0 ) { IOUnlock(priv->our_lock); return IOPMAckImplied; }
IOUnlock(priv->our_lock); return IOPMWillAckLater;
}
bool IOService::notifyChild ( IOPowerConnection * theNub, bool is_prechange )
{
IOReturn k = IOPMAckImplied;
unsigned long childPower;
IOService * theChild = (IOService *)(theNub->copyChildEntry(gIOPowerPlane));
theNub->setAwaitingAck(true);
if ( ! theChild ) {
return true;
}
if ( is_prechange ) {
k = theChild->powerDomainWillChangeTo(priv->head_note_outputFlags,theNub);
}
else {
k = theChild->powerDomainDidChangeTo(priv->head_note_outputFlags,theNub);
}
if ( k == IOPMAckImplied ) { priv->head_note_pendingAcks -=1; theNub->setAwaitingAck(false);
childPower = theChild->currentPowerConsumption();
if ( childPower == kIOPMUnknown ) {
pm_vars->thePowerStates[priv->head_note_state].staticPower = kIOPMUnknown;
}
else {
if ( pm_vars->thePowerStates[priv->head_note_state].staticPower != kIOPMUnknown ) {
pm_vars->thePowerStates[priv->head_note_state].staticPower += childPower;
}
}
theChild->release();
return true;
}
theChild->release();
return false;
}
bool IOService::inform ( IOPMinformee * nextObject, bool is_prechange )
{
IOReturn k = IOPMAckImplied;
nextObject->timer = -1;
if ( is_prechange ) {
pm_vars->thePlatform->PMLog (pm_vars->ourName,PMlogInformDriverPreChange,
(unsigned long)priv->head_note_capabilityFlags,(unsigned long)priv->head_note_state);
k = nextObject->whatObject->powerStateWillChangeTo( priv->head_note_capabilityFlags,priv->head_note_state,this);
}
else {
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogInformDriverPostChange,
(unsigned long)priv->head_note_capabilityFlags,(unsigned long)priv->head_note_state);
k = nextObject->whatObject->powerStateDidChangeTo(priv->head_note_capabilityFlags,priv->head_note_state,this);
}
if ( nextObject->timer == 0 ) { return true; }
if ( k ==IOPMAckImplied ) { nextObject->timer = 0; priv->head_note_pendingAcks -= 1;
return true;
}
if ( k < 0 ) {
nextObject->timer = 0; priv-> head_note_pendingAcks -= 1;
return true;
}
nextObject->timer = (k * ns_per_us / ACK_TIMER_PERIOD) + 1; return false;
}
void IOService::our_prechange_03 ( void )
{
priv->machine_state = IOPMour_prechange_04; if ( tellChangeDown1(priv->head_note_state) ) { our_prechange_04(); }
}
void IOService::our_prechange_04 ( void )
{
priv->machine_state = IOPMour_prechange_05; if ( tellChangeDown2(priv->head_note_state) ) { return our_prechange_05(); }
}
void IOService::our_prechange_05 ( void )
{
priv->machine_state = IOPMour_prechange_1; if ( notifyAll(true) == IOPMAckImplied ) {
our_prechange_1();
}
}
void IOService::our_prechange_1 ( void )
{
if ( instruct_driver(priv->head_note_state) == IOPMAckImplied ) {
our_prechange_2(); }
else {
priv->machine_state = IOPMour_prechange_2; pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogStartAckTimer,0,0);
start_ack_timer();
}
}
void IOService::our_prechange_2 ( void )
{
priv->settle_time = compute_settle_time();
if ( priv->settle_time == 0 ) {
our_prechange_3();
}
else {
priv->machine_state = IOPMour_prechange_3;
startSettleTimer(priv->settle_time);
}
}
void IOService::our_prechange_3 ( void )
{
priv->machine_state = IOPMour_prechange_4; if ( notifyAll(false) == IOPMAckImplied ) {
our_prechange_4();
}
}
void IOService::our_prechange_4 ( void )
{
all_done();
}
IOReturn IOService::parent_down_0 ( void )
{
priv->machine_state = IOPMparent_down_05; if ( tellChangeDown2(priv->head_note_state) ) { return parent_down_02(); }
return IOPMWillAckLater; }
IOReturn IOService::parent_down_02 ( void )
{
priv->machine_state = IOPMparent_down_4; if ( notifyAll(true) == IOPMAckImplied ) {
return parent_down_1(); }
return IOPMWillAckLater; }
void IOService::parent_down_04 ( void )
{
priv->machine_state = IOPMparent_down_05; if ( tellChangeDown2(priv->head_note_state) ) { parent_down_05(); }
}
void IOService::parent_down_05 ( void )
{
priv->machine_state = IOPMparent_down_4; if ( notifyAll(true) == IOPMAckImplied ) {
parent_down_4(); }
}
IOReturn IOService::parent_down_1 ( void )
{
if ( instruct_driver(priv->head_note_state) == IOPMAckImplied ) {
return parent_down_2(); }
priv->machine_state = IOPMparent_down_5; pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogStartAckTimer,0,0);
start_ack_timer();
return IOPMWillAckLater;
}
void IOService::parent_down_4 ( void )
{
if ( instruct_driver(priv->head_note_state) == IOPMAckImplied ) {
parent_down_5(); }
else {
priv-> machine_state = IOPMparent_down_5; pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogStartAckTimer,0,0);
start_ack_timer();
}
}
IOReturn IOService::parent_down_2 ( void )
{
IOService * nub;
priv->settle_time = compute_settle_time();
if ( priv->settle_time == 0 ) {
priv->machine_state = IOPMparent_down_6; if ( notifyAll(false) == IOPMAckImplied ) {
nub = priv->head_note_parent;
all_done();
nub->release();
return IOPMAckImplied;
}
return IOPMWillAckLater; }
else {
priv->machine_state = IOPMparent_down_3;
startSettleTimer(priv->settle_time);
return IOPMWillAckLater;
}
}
void IOService::parent_down_5 ( void )
{
priv->settle_time = compute_settle_time();
if ( priv->settle_time == 0 ) {
parent_down_3();
}
else {
priv->machine_state = IOPMparent_down_3;
startSettleTimer(priv->settle_time);
}
}
void IOService::parent_down_3 ( void )
{
IORegistryEntry * nub;
IOService * parent;
priv->machine_state = IOPMparent_down_6; if ( notifyAll(false) == IOPMAckImplied ) {
nub = priv->head_note_parent;
all_done();
parent = (IOService *)nub->copyParentEntry(gIOPowerPlane);
if ( parent ) {
parent->acknowledgePowerChange((IOService *)nub);
parent->release();
}
nub->release();
}
}
void IOService::parent_down_6 ( void )
{
IORegistryEntry * nub;
IOService * parent;
nub = priv->head_note_parent;
all_done();
parent = (IOService *)nub->copyParentEntry(gIOPowerPlane);
if ( parent ) {
parent->acknowledgePowerChange((IOService *)nub);
parent->release();
}
nub->release();
}
void IOService::parent_up_0 ( void )
{
if ( instruct_driver(priv->head_note_state) == IOPMAckImplied ) {
parent_up_4(); }
else {
priv->machine_state = IOPMparent_up_4; pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogStartAckTimer,0,0);
start_ack_timer();
}
}
IOReturn IOService::parent_up_1 ( void )
{
if ( instruct_driver(priv->head_note_state) == IOPMAckImplied ) {
return parent_up_2(); }
else {
priv->machine_state = IOPMparent_up_4; pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogStartAckTimer,0,0);
start_ack_timer();
return IOPMWillAckLater;
}
}
IOReturn IOService::parent_up_2 ( void )
{
priv->settle_time = compute_settle_time();
if ( priv->settle_time == 0 ) {
return parent_up_3();
}
else {
priv->machine_state = IOPMparent_up_5;
startSettleTimer(priv->settle_time);
return IOPMWillAckLater;
}
}
void IOService::parent_up_4 ( void )
{
priv->settle_time = compute_settle_time();
if ( priv->settle_time == 0 ) {
parent_up_5();
}
else {
priv->machine_state = IOPMparent_up_5;
startSettleTimer(priv->settle_time);
}
}
IOReturn IOService::parent_up_3 ( void )
{
IOService * nub;
priv->machine_state = IOPMparent_up_6; if ( notifyAll(false) == IOPMAckImplied ) {
nub = priv->head_note_parent;
all_done();
nub->release();
return IOPMAckImplied;
}
return IOPMWillAckLater; }
void IOService::parent_up_5 ( void )
{
priv->machine_state = IOPMparent_up_6; if ( notifyAll(false) == IOPMAckImplied ) {
parent_up_6();
}
}
void IOService::parent_up_6 ( void )
{
IORegistryEntry * nub;
IOService * parent;
nub = priv->head_note_parent;
all_done();
parent = (IOService *)nub->copyParentEntry(gIOPowerPlane);
if ( parent ) {
parent->acknowledgePowerChange((IOService *)nub);
parent->release();
}
nub->release();
}
void IOService::all_done ( void )
{
unsigned long previous_state;
IORegistryEntry * nub;
IOService * parent;
priv->machine_state = IOPMfinished;
if ( priv->head_note_flags & IOPMWeInitiated ) { if ( !( priv->head_note_flags & IOPMNotDone) ) { if ( pm_vars->myCurrentState < priv->head_note_state ) { tellChangeUp (priv->head_note_state); }
else {
if ( ! priv->we_are_root ) { ask_parent(priv->head_note_state); }
}
previous_state = pm_vars->myCurrentState;
pm_vars->myCurrentState = priv->head_note_state; priv->imminentState = pm_vars->myCurrentState;
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogChangeDone,(unsigned long)pm_vars->myCurrentState,0);
powerChangeDone(previous_state); }
}
if ( priv->head_note_flags & IOPMParentInitiated) { if ( ((priv->head_note_flags & IOPMDomainWillChange) && (pm_vars->myCurrentState >= priv->head_note_state)) ||
((priv->head_note_flags & IOPMDomainDidChange) && (pm_vars->myCurrentState < priv->head_note_state)) ) {
if ( pm_vars->myCurrentState < priv->head_note_state ) { tellChangeUp (priv->head_note_state); }
previous_state = pm_vars->myCurrentState; pm_vars->myCurrentState = priv->head_note_state;
priv->imminentState = pm_vars->myCurrentState;
pm_vars->maxCapability = pm_vars->theControllingDriver->maxCapabilityForDomainState(priv->head_note_domainState);
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogChangeDone,(unsigned long)pm_vars->myCurrentState,0);
powerChangeDone(previous_state); }
}
IOLockLock(priv->queue_lock);
priv->changeList->releaseHeadChangeNote();
priv->head_note = priv->changeList->currentChange(); if ( priv->head_note != -1 ) {
IOLockUnlock(priv->queue_lock);
if (priv->changeList->changeNote[priv->head_note].flags & IOPMWeInitiated ) {
start_our_change(priv->head_note);
}
else {
nub = priv->changeList->changeNote[priv->head_note].parent;
if ( start_parent_change(priv->head_note) == IOPMAckImplied ) {
parent = (IOService *)nub->copyParentEntry(gIOPowerPlane);
if ( parent ) {
parent->acknowledgePowerChange((IOService *)nub);
parent->release();
}
}
}
}
IOLockUnlock(priv->queue_lock);
}
void IOService::all_acked ( void )
{
switch (priv->machine_state) {
case IOPMour_prechange_1:
our_prechange_1();
break;
case IOPMour_prechange_4:
our_prechange_4();
break;
case IOPMparent_down_4:
parent_down_4();
break;
case IOPMparent_down_6:
parent_down_6();
break;
case IOPMparent_up_0:
parent_up_0();
break;
case IOPMparent_up_6:
parent_up_6();
break;
}
}
void IOService::settleTimerExpired ( void )
{
if ( ! initialized ) {
return; }
switch (priv->machine_state) {
case IOPMour_prechange_3:
our_prechange_3();
break;
case IOPMparent_down_3:
parent_down_3();
break;
case IOPMparent_up_5:
parent_up_5();
break;
}
}
unsigned long IOService::compute_settle_time ( void )
{
unsigned long totalTime;
unsigned long i;
totalTime = 0; i = pm_vars->myCurrentState;
if ( priv->head_note_state < pm_vars->myCurrentState ) { while ( i > priv->head_note_state ) {
totalTime += pm_vars->thePowerStates[i].settleDownTime;
i--;
}
}
if ( priv->head_note_state > pm_vars->myCurrentState ) { while ( i < priv->head_note_state ) {
totalTime += pm_vars->thePowerStates[i+1].settleUpTime;
i++;
}
}
return totalTime;
}
IOReturn IOService::startSettleTimer ( unsigned long delay )
{
AbsoluteTime deadline;
clock_interval_to_deadline(delay, kMicrosecondScale, &deadline);
thread_call_enter_delayed(priv->settleTimer, deadline);
return IOPMNoErr;
}
void IOService::ack_timer_ticked ( void )
{
IOPMinformee * nextObject;
if ( ! initialized ) {
return; }
if (! acquire_lock() ) {
return;
}
switch (priv->machine_state) {
case IOPMour_prechange_2:
case IOPMparent_down_5:
case IOPMparent_up_4:
if ( priv->driver_timer != 0 ) { priv->driver_timer -= 1; if ( priv->driver_timer == 0 ) { IOUnlock(priv->our_lock);
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogCtrlDriverTardy,0,0);
driver_acked();
}
else { start_ack_timer();
IOUnlock(priv->our_lock);
}
}
else {
IOUnlock(priv->our_lock);
}
break;
case IOPMour_prechange_1:
case IOPMour_prechange_4:
case IOPMparent_down_4:
case IOPMparent_down_6:
case IOPMparent_up_0:
case IOPMparent_up_6:
if (priv->head_note_pendingAcks != 0 ) { nextObject = priv->interestedDrivers->firstInList(); while ( nextObject != NULL ) { if ( nextObject->timer > 0 ) {
nextObject->timer -= 1;
if ( nextObject->timer == 0 ) { pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogIntDriverTardy,0,0);
kprintf("interested driver tardy: %s\n",nextObject->whatObject->getName());
priv->head_note_pendingAcks -= 1;
}
}
nextObject = priv->interestedDrivers->nextInList(nextObject);
}
if ( priv->head_note_pendingAcks == 0 ) { IOUnlock(priv->our_lock);
all_acked(); }
else { start_ack_timer();
IOUnlock(priv->our_lock);
}
}
else {
IOUnlock(priv->our_lock);
}
break;
case IOPMparent_down_0: IOUnlock(priv->our_lock);
IOLockLock(priv->flags_lock);
if (pm_vars->responseFlags) {
pm_vars->responseFlags->release(); pm_vars->responseFlags = NULL;
}
IOLockUnlock(priv->flags_lock);
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogClientTardy,0,5);
parent_down_04(); break;
case IOPMparent_down_05:
IOUnlock(priv->our_lock);
IOLockLock(priv->flags_lock);
if (pm_vars->responseFlags) {
pm_vars->responseFlags->release(); pm_vars->responseFlags = NULL;
}
IOLockUnlock(priv->flags_lock);
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogClientTardy,0,1);
parent_down_05(); break;
case IOPMour_prechange_03: IOUnlock(priv->our_lock);
IOLockLock(priv->flags_lock);
if (pm_vars->responseFlags) {
pm_vars->responseFlags->release(); pm_vars->responseFlags = NULL;
}
IOLockUnlock(priv->flags_lock);
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogClientTardy,0,2);
tellNoChangeDown(priv->head_note_state); priv->head_note_flags |= IOPMNotDone; all_done(); break;
case IOPMour_prechange_04: IOUnlock(priv->our_lock);
IOLockLock(priv->flags_lock);
if (pm_vars->responseFlags) {
pm_vars->responseFlags->release(); pm_vars->responseFlags = NULL;
}
IOLockUnlock(priv->flags_lock);
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogClientTardy,0,4);
our_prechange_04(); break;
case IOPMour_prechange_05: IOUnlock(priv->our_lock);
IOLockLock(priv->flags_lock);
if (pm_vars->responseFlags) {
pm_vars->responseFlags->release(); pm_vars->responseFlags = NULL;
}
IOLockUnlock(priv->flags_lock);
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogClientTardy,0,3);
our_prechange_05(); break;
default:
IOUnlock(priv->our_lock); break;
}
}
void IOService::start_ack_timer ( void )
{
AbsoluteTime deadline;
clock_interval_to_deadline(ACK_TIMER_PERIOD, kNanosecondScale, &deadline);
thread_call_enter_delayed(priv->ackTimer, deadline);
}
void IOService::stop_ack_timer ( void )
{
thread_call_cancel(priv->ackTimer);
}
static void ack_timer_expired ( thread_call_param_t us)
{
((IOService *)us)->ack_timer_ticked();
}
static void settle_timer_expired ( thread_call_param_t us)
{
((IOService *)us)->settleTimerExpired();
}
IOReturn IOService::add_child_to_active_change ( IOPowerConnection * newObject )
{
if (! acquire_lock() ) {
return IOPMNoErr;
}
switch (priv->machine_state) {
case IOPMour_prechange_1:
case IOPMparent_down_4:
case IOPMparent_up_0:
priv->head_note_pendingAcks += 2; IOUnlock(priv->our_lock); notifyChild(newObject, true);
if (! acquire_lock() ) {
--priv->head_note_pendingAcks; return IOPMNoErr;
}
if ( --priv->head_note_pendingAcks == 0 ) { stop_ack_timer(); IOUnlock(priv->our_lock);
all_acked(); return IOPMNoErr;
}
break;
case IOPMour_prechange_4:
case IOPMparent_down_6:
case IOPMparent_up_6:
priv->head_note_pendingAcks += 2; IOUnlock(priv->our_lock); notifyChild(newObject, false);
if (! acquire_lock() ) {
--priv->head_note_pendingAcks; return IOPMNoErr;
}
if ( --priv->head_note_pendingAcks == 0 ) { stop_ack_timer(); IOUnlock(priv->our_lock);
all_acked(); return IOPMNoErr;
}
break;
}
IOUnlock(priv->our_lock);
return IOPMNoErr;
}
IOReturn IOService::add_driver_to_active_change ( IOPMinformee * newObject )
{
if (! acquire_lock() ) {
return IOPMNoErr;
}
switch (priv->machine_state) {
case IOPMour_prechange_1:
case IOPMparent_down_4:
case IOPMparent_up_0:
priv->head_note_pendingAcks += 2; IOUnlock(priv->our_lock); inform(newObject, true); if (! acquire_lock() ) {
--priv->head_note_pendingAcks; return IOPMNoErr;
}
if ( --priv->head_note_pendingAcks == 0 ) { stop_ack_timer(); IOUnlock(priv->our_lock);
all_acked(); return IOPMNoErr;
}
break;
case IOPMour_prechange_4:
case IOPMparent_down_6:
case IOPMparent_up_6:
priv->head_note_pendingAcks += 2; IOUnlock(priv->our_lock); inform(newObject, false); if (! acquire_lock() ) {
--priv->head_note_pendingAcks; return IOPMNoErr;
}
if ( --priv->head_note_pendingAcks == 0 ) { stop_ack_timer(); IOUnlock(priv->our_lock);
all_acked(); return IOPMNoErr;
}
break;
}
IOUnlock(priv->our_lock);
return IOPMNoErr;
}
IOReturn IOService::start_parent_change ( unsigned long queue_head )
{
priv->head_note = queue_head;
priv->head_note_flags = priv-> changeList->changeNote[priv->head_note].flags;
priv->head_note_state = priv->changeList->changeNote[priv->head_note].newStateNumber;
priv->imminentState = priv->head_note_state;
priv->head_note_outputFlags = priv->changeList->changeNote[priv->head_note].outputPowerCharacter;
priv->head_note_domainState = priv->changeList->changeNote[priv->head_note].domainState;
priv->head_note_parent = priv->changeList->changeNote[priv->head_note].parent;
priv->head_note_capabilityFlags = priv->changeList->changeNote[priv->head_note].capabilityFlags;
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogStartParentChange,
(unsigned long)priv->head_note_state,(unsigned long)pm_vars->myCurrentState);
ask_parent( priv->ourDesiredPowerState);
if ( priv->head_note_state < pm_vars->myCurrentState ) { setParentInfo(priv->changeList->changeNote[priv->head_note].singleParentState,priv->head_note_parent);
priv->initial_change = false;
priv->machine_state = IOPMparent_down_0; if ( tellChangeDown1(priv->head_note_state) ) { return parent_down_0(); }
return IOPMWillAckLater; }
if ( priv->head_note_state > pm_vars->myCurrentState ) { if ( priv->ourDesiredPowerState > pm_vars->myCurrentState ) {
if ( priv->ourDesiredPowerState < priv->head_note_state ) {
priv->head_note_state = priv->ourDesiredPowerState; priv->imminentState = priv->head_note_state;
priv->head_note_outputFlags = pm_vars->thePowerStates[priv->head_note_state].outputPowerCharacter;
priv->head_note_capabilityFlags = pm_vars->thePowerStates[priv->head_note_state].capabilityFlags;
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogAmendParentChange,(unsigned long)priv->head_note_state,0);
}
}
else {
priv->head_note_state = pm_vars->myCurrentState; priv->imminentState = priv->head_note_state;
priv->head_note_outputFlags = pm_vars->thePowerStates[priv->head_note_state].outputPowerCharacter;
priv->head_note_capabilityFlags = pm_vars->thePowerStates[priv->head_note_state].capabilityFlags;
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogAmendParentChange,(unsigned long)priv->head_note_state,0);
}
}
if ( (priv->head_note_state > pm_vars->myCurrentState) &&
(priv->head_note_flags & IOPMDomainDidChange) ) { priv->initial_change = false;
priv->machine_state = IOPMparent_up_0;
if ( notifyAll(true) == IOPMAckImplied ) {
return parent_up_1();
}
return IOPMWillAckLater; }
all_done();
return IOPMAckImplied; }
void IOService::start_our_change ( unsigned long queue_head )
{
priv->head_note = queue_head;
priv->head_note_flags = priv->changeList->changeNote[priv->head_note].flags;
priv->head_note_state = priv->changeList->changeNote[priv->head_note].newStateNumber;
priv->imminentState = priv->head_note_state;
priv->head_note_outputFlags = priv->changeList->changeNote[priv->head_note].outputPowerCharacter;
priv->head_note_capabilityFlags = priv->changeList->changeNote[priv->head_note].capabilityFlags;
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogStartDeviceChange,
(unsigned long)priv->head_note_state,(unsigned long)pm_vars->myCurrentState);
if ( priv->head_note_capabilityFlags & IOPMNotAttainable ) { if ( ! priv->we_are_root ) { ask_parent(priv->head_note_state);
}
priv-> head_note_flags |= IOPMNotDone; all_done(); return;
}
if ( (pm_vars->maxCapability < priv->head_note_state) && (! priv->we_are_root) ) {
if ( ! priv->we_are_root ) { ask_parent(priv->head_note_state);
}
priv->head_note_flags |= IOPMNotDone; all_done(); return; }
if ( ! priv->initial_change ) {
if ( priv->head_note_state == pm_vars->myCurrentState ) {
all_done(); return;
}
}
priv->initial_change = false;
if ( priv->head_note_state < pm_vars->myCurrentState ) { priv->machine_state = IOPMour_prechange_03; pm_vars->doNotPowerDown = false;
pm_vars->outofbandparameter = kNotifyApps; if ( askChangeDown(priv->head_note_state) ) {
if ( pm_vars->doNotPowerDown ) { tellNoChangeDown(priv->head_note_state); priv-> head_note_flags |= IOPMNotDone; all_done(); }
else {
our_prechange_03(); }
}
}
else {
if ( ! priv->we_are_root ) { ask_parent(priv->head_note_state); }
priv->machine_state = IOPMour_prechange_1; if ( notifyAll(true) == IOPMAckImplied ) { our_prechange_1();
}
}
}
IOReturn IOService::ask_parent ( unsigned long requestedState )
{
OSIterator * iter;
OSObject * next;
IOPowerConnection * connection;
IOService * parent;
unsigned long ourRequest = pm_vars->thePowerStates[requestedState].inputPowerRequirement;
if ( pm_vars->thePowerStates[requestedState].capabilityFlags & (kIOPMChildClamp | kIOPMPreventIdleSleep) ) {
ourRequest |= kIOPMPreventIdleSleep;
}
if ( pm_vars->thePowerStates[requestedState].capabilityFlags & (kIOPMChildClamp2 | kIOPMPreventSystemSleep) ) {
ourRequest |= kIOPMPreventSystemSleep;
}
if ( priv->previousRequest == ourRequest ) { return IOPMNoErr; }
if ( priv->we_are_root ) {
return IOPMNoErr;
}
priv->previousRequest = ourRequest;
iter = getParentIterator(gIOPowerPlane);
if ( iter ) {
while ( (next = iter->getNextObject()) ) {
if ( (connection = OSDynamicCast(IOPowerConnection,next)) ) {
parent = (IOService *)connection->copyParentEntry(gIOPowerPlane);
if ( parent ) {
if ( parent->requestPowerDomainState(ourRequest,connection,IOPMLowestState)!= IOPMNoErr ) {
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogRequestDenied,
(unsigned long)priv->previousRequest,0);
}
parent->release();
}
}
}
iter->release();
}
return IOPMNoErr;
}
IOReturn IOService::instruct_driver ( unsigned long newState )
{
IOReturn return_code;
if ( pm_vars->thePowerStates[newState].capabilityFlags & IOPMNotAttainable ) { return IOPMAckImplied; }
priv->driver_timer = -1;
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogProgramHardware,newState,0);
return_code = pm_vars->theControllingDriver->setPowerState( newState,this ); if ( return_code == IOPMAckImplied ) { priv->driver_timer = 0;
return IOPMAckImplied;
}
if ( priv->driver_timer == 0 ) { return IOPMAckImplied;
}
if ( return_code < 0 ) { return IOPMAckImplied;
}
priv->driver_timer = (return_code * ns_per_us / ACK_TIMER_PERIOD) + 1; return IOPMWillAckLater;
}
bool IOService::acquire_lock ( void )
{
long current_change_note;
current_change_note = priv->head_note;
if ( current_change_note == -1 ) {
return FALSE;
}
IOTakeLock(priv->our_lock);
if ( current_change_note == priv->head_note ) {
return TRUE;
}
else { IOUnlock(priv->our_lock); return FALSE;
}
}
bool IOService::askChangeDown ( unsigned long stateNum )
{
return tellClientsWithResponse(kIOMessageCanDevicePowerOff);
}
bool IOService::tellChangeDown1 ( unsigned long stateNum )
{
pm_vars->outofbandparameter = kNotifyApps;
return tellChangeDown(stateNum);
}
bool IOService::tellChangeDown2 ( unsigned long stateNum )
{
pm_vars->outofbandparameter = kNotifyPriority;
return tellChangeDown(stateNum);
}
bool IOService::tellChangeDown ( unsigned long stateNum )
{
return tellClientsWithResponse(kIOMessageDeviceWillPowerOff);
}
bool IOService::tellClientsWithResponse ( int messageType )
{
struct context theContext;
AbsoluteTime deadline;
OSBoolean * aBool;
pm_vars->responseFlags = OSArray::withCapacity( 1 );
pm_vars->serialNumber += 1;
theContext.responseFlags = pm_vars->responseFlags;
theContext.serialNumber = pm_vars->serialNumber;
theContext.flags_lock = priv->flags_lock;
theContext.counter = 1;
theContext.msgType = messageType;
theContext.us = this;
theContext.maxTimeRequested = 0;
theContext.stateNumber = priv->head_note_state;
theContext.stateFlags = priv->head_note_capabilityFlags;
IOLockLock(priv->flags_lock);
aBool = OSBoolean::withBoolean(false); theContext.responseFlags->setObject(0,aBool); aBool->release();
IOLockUnlock(priv->flags_lock);
switch ( pm_vars->outofbandparameter ) {
case kNotifyApps:
applyToInterested(gIOAppPowerStateInterest,tellAppWithResponse,(void *)&theContext);
applyToInterested(gIOGeneralInterest,tellClientWithResponse,(void *)&theContext);
break;
case kNotifyPriority:
applyToInterested(gIOPriorityPowerStateInterest,tellClientWithResponse,(void *)&theContext);
break;
}
if (! acquire_lock() ) {
return true;
}
IOLockLock(priv->flags_lock);
aBool = OSBoolean::withBoolean(true); theContext.responseFlags->replaceObject(0,aBool);
aBool->release();
IOLockUnlock(priv->flags_lock);
if ( ! checkForDone() ) { pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogStartAckTimer,theContext.maxTimeRequested,0);
clock_interval_to_deadline(theContext.maxTimeRequested / 1000, kMillisecondScale, &deadline);
thread_call_enter_delayed(priv->ackTimer, deadline);
IOUnlock(priv->our_lock); return false;
}
IOUnlock(priv->our_lock);
IOLockLock(priv->flags_lock);
pm_vars->responseFlags->release(); pm_vars->responseFlags = NULL;
IOLockUnlock(priv->flags_lock);
return true;
}
void tellAppWithResponse ( OSObject * object, void * context)
{
struct context * theContext = (struct context *)context;
UInt32 refcon;
OSBoolean * aBool;
if( OSDynamicCast( IOService, object) ) {
IOLockLock(theContext->flags_lock);
aBool = OSBoolean::withBoolean(true);
theContext->responseFlags->setObject(theContext->counter,aBool);
aBool->release();
IOLockUnlock(theContext->flags_lock);
}
else {
refcon = ((theContext->serialNumber & 0xFFFF)<<16) + (theContext->counter & 0xFFFF);
IOLockLock(theContext->flags_lock);
aBool = OSBoolean::withBoolean(false);
theContext->responseFlags->setObject(theContext->counter,aBool);
aBool->release();
IOLockUnlock(theContext->flags_lock);
theContext->us->messageClient(theContext->msgType,object,(void *)refcon);
if ( theContext->maxTimeRequested < k30seconds ) {
theContext->maxTimeRequested = k30seconds;
}
}
theContext->counter += 1;
}
void tellClientWithResponse ( OSObject * object, void * context)
{
struct context * theContext = (struct context *)context;
IOPowerStateChangeNotification notify;
UInt32 refcon;
IOReturn retCode;
OSBoolean * aBool;
OSObject * theFlag;
refcon = ((theContext->serialNumber & 0xFFFF)<<16) + (theContext->counter & 0xFFFF);
IOLockLock(theContext->flags_lock);
aBool = OSBoolean::withBoolean(false);
theContext->responseFlags->setObject(theContext->counter,aBool);
aBool->release();
IOLockUnlock(theContext->flags_lock);
notify.powerRef = (void *)refcon;
notify.returnValue = 0;
notify.stateNumber = theContext->stateNumber;
notify.stateFlags = theContext->stateFlags;
retCode = theContext->us->messageClient(theContext->msgType,object,(void *)¬ify);
if ( retCode == kIOReturnSuccess ) {
if ( notify.returnValue == 0 ) { IOLockLock(theContext->flags_lock);
aBool = OSBoolean::withBoolean(true);
theContext->responseFlags->replaceObject(theContext->counter,aBool); aBool->release();
IOLockUnlock(theContext->flags_lock);
}
else {
IOLockLock(theContext->flags_lock);
theFlag = theContext->responseFlags->getObject(theContext->counter); if ( theFlag != 0 ) { if ( ((OSBoolean *)theFlag)->isFalse() ) { if ( theContext->maxTimeRequested < notify.returnValue ) {
theContext->maxTimeRequested = notify.returnValue;
}
}
}
IOLockUnlock(theContext->flags_lock);
}
}
else { IOLockLock(theContext->flags_lock);
aBool = OSBoolean::withBoolean(true); theContext->responseFlags->replaceObject(theContext->counter,aBool);
aBool->release();
IOLockUnlock(theContext->flags_lock);
}
theContext->counter += 1;
}
void IOService::tellNoChangeDown ( unsigned long )
{
return tellClients(kIOMessageDeviceWillNotPowerOff);
}
void IOService::tellChangeUp ( unsigned long )
{
return tellClients(kIOMessageDeviceHasPoweredOn);
}
void IOService::tellClients ( int messageType )
{
struct context theContext;
theContext.msgType = messageType;
theContext.us = this;
theContext.stateNumber = priv->head_note_state;
theContext.stateFlags = priv->head_note_capabilityFlags;
applyToInterested(gIOAppPowerStateInterest,tellClient,(void *)&theContext);
applyToInterested(gIOGeneralInterest,tellClient,(void *)&theContext);
}
void tellClient ( OSObject * object, void * context)
{
struct context * theContext = (struct context *)context;
IOPowerStateChangeNotification notify;
notify.powerRef = (void *) 0;
notify.returnValue = 0;
notify.stateNumber = theContext->stateNumber;
notify.stateFlags = theContext->stateFlags;
theContext->us->messageClient(theContext->msgType,object, ¬ify);
}
bool IOService::checkForDone ( void )
{
int i = 0;
OSObject * theFlag;
IOLockLock(priv->flags_lock);
if ( pm_vars->responseFlags == NULL ) {
IOLockUnlock(priv->flags_lock);
return true;
}
for ( i = 0; ; i++ ) {
theFlag = pm_vars->responseFlags->getObject(i);
if ( theFlag == NULL ) {
break;
}
if ( ((OSBoolean *)theFlag)->isFalse() ) {
IOLockUnlock(priv->flags_lock);
return false;
}
}
IOLockUnlock(priv->flags_lock);
return true;
}
bool IOService::responseValid ( unsigned long x )
{
UInt16 serialComponent;
UInt16 ordinalComponent;
OSObject * theFlag;
unsigned long refcon = (unsigned long)x;
OSBoolean * aBool;
serialComponent = (refcon>>16) & 0xFFFF;
ordinalComponent = refcon & 0xFFFF;
if ( serialComponent != pm_vars->serialNumber ) {
return false;
}
IOLockLock(priv->flags_lock);
if ( pm_vars->responseFlags == NULL ) {
IOLockUnlock(priv->flags_lock);
return false;
}
theFlag = pm_vars->responseFlags->getObject(ordinalComponent);
if ( theFlag == 0 ) {
IOLockUnlock(priv->flags_lock);
return false;
}
if ( ((OSBoolean *)theFlag)->isFalse() ) {
aBool = OSBoolean::withBoolean(true);
pm_vars->responseFlags->replaceObject(ordinalComponent,aBool);
aBool->release();
}
IOLockUnlock(priv->flags_lock);
return true;
}
IOReturn IOService::allowPowerChange ( unsigned long refcon )
{
if ( ! initialized ) {
return kIOReturnSuccess; }
return pm_vars->PMcommandGate->runAction(serializedAllowPowerChange,(void *)refcon);
}
IOReturn serializedAllowPowerChange ( OSObject *owner, void * refcon, void *, void *, void *)
{
return ((IOService *)owner)->serializedAllowPowerChange2((unsigned long)refcon);
}
IOReturn IOService::serializedAllowPowerChange2 ( unsigned long refcon )
{
if ( ! responseValid(refcon) ) { pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogAcknowledgeErr5,refcon,0);
return kIOReturnSuccess; }
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogClientAcknowledge,refcon,0);
return allowCancelCommon();
}
IOReturn IOService::cancelPowerChange ( unsigned long refcon )
{
if ( ! initialized ) {
return kIOReturnSuccess; }
return pm_vars->PMcommandGate->runAction(serializedCancelPowerChange,(void *)refcon);
}
IOReturn serializedCancelPowerChange ( OSObject *owner, void * refcon, void *, void *, void *)
{
return ((IOService *)owner)->serializedCancelPowerChange2((unsigned long)refcon);
}
IOReturn IOService::serializedCancelPowerChange2 ( unsigned long refcon )
{
if ( ! responseValid(refcon) ) { pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogAcknowledgeErr5,refcon,0);
return kIOReturnSuccess; }
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogClientCancel,refcon,0);
pm_vars->doNotPowerDown = true;
return allowCancelCommon();
}
IOReturn IOService::allowCancelCommon ( void )
{
if (! acquire_lock() ) {
return kIOReturnSuccess;
}
if ( checkForDone() ) { stop_ack_timer(); IOUnlock(priv->our_lock);
IOLockLock(priv->flags_lock);
if ( pm_vars->responseFlags ) {
pm_vars->responseFlags->release();
pm_vars->responseFlags = NULL;
}
IOLockUnlock(priv->flags_lock);
switch (priv->machine_state) {
case IOPMour_prechange_03: if ( ! pm_vars->doNotPowerDown ) {
our_prechange_03(); }
else {
tellNoChangeDown(priv->head_note_state); priv->head_note_flags |= IOPMNotDone; all_done(); }
break;
case IOPMour_prechange_04:
our_prechange_04();
break;
case IOPMour_prechange_05:
our_prechange_05(); break;
case IOPMparent_down_0:
parent_down_04(); break;
case IOPMparent_down_05:
parent_down_05(); break;
}
}
IOUnlock(priv->our_lock); return kIOReturnSuccess;
}
#define kFiveMinutesInNanoSeconds (300 * NSEC_PER_SEC)
void IOService::clampPowerOn (unsigned long duration)
{
changePowerStateToPriv (pm_vars->theNumberOfPowerStates-1);
if ( priv->clampTimerEventSrc == NULL ) {
priv->clampTimerEventSrc = IOTimerEventSource::timerEventSource(this,
c_PM_Clamp_Timer_Expired);
IOWorkLoop * workLoop = getPMworkloop ();
if ( !priv->clampTimerEventSrc || !workLoop ||
( workLoop->addEventSource( priv->clampTimerEventSrc) != kIOReturnSuccess) ) {
}
}
priv->clampTimerEventSrc->setTimeout(kFiveMinutesInNanoSeconds, NSEC_PER_SEC);
}
void IOService::PM_Clamp_Timer_Expired (void)
{
if ( ! initialized ) {
return; }
changePowerStateToPriv (0);
}
void c_PM_Clamp_Timer_Expired (OSObject * client, IOTimerEventSource *)
{
if (client)
((IOService *)client)->PM_Clamp_Timer_Expired ();
}
IOReturn IOService::setPowerState ( unsigned long powerStateOrdinal, IOService* whatDevice )
{
return IOPMNoErr;
}
unsigned long IOService::maxCapabilityForDomainState ( IOPMPowerFlags domainState )
{
int i;
if (pm_vars->theNumberOfPowerStates == 0 ) {
return 0;
}
for ( i = (pm_vars->theNumberOfPowerStates)-1; i >= 0; i-- ) {
if ( (domainState & pm_vars->thePowerStates[i].inputPowerRequirement) == pm_vars->thePowerStates[i].inputPowerRequirement ) {
return i;
}
}
return 0;
}
unsigned long IOService::initialPowerStateForDomainState ( IOPMPowerFlags domainState )
{
int i;
if (pm_vars->theNumberOfPowerStates == 0 ) {
return 0;
}
for ( i = (pm_vars->theNumberOfPowerStates)-1; i >= 0; i-- ) {
if ( (domainState & pm_vars->thePowerStates[i].inputPowerRequirement) == pm_vars->thePowerStates[i].inputPowerRequirement ) {
return i;
}
}
return 0;
}
unsigned long IOService::powerStateForDomainState ( IOPMPowerFlags domainState )
{
int i;
if (pm_vars->theNumberOfPowerStates == 0 ) {
return 0;
}
for ( i = (pm_vars->theNumberOfPowerStates)-1; i >= 0; i-- ) {
if ( (domainState & pm_vars->thePowerStates[i].inputPowerRequirement) == pm_vars->thePowerStates[i].inputPowerRequirement ) {
return i;
}
}
return 0;
}
bool IOService::didYouWakeSystem ( void )
{
return false;
}
IOReturn IOService::powerStateWillChangeTo ( IOPMPowerFlags, unsigned long, IOService*)
{
return 0;
}
IOReturn IOService::powerStateDidChangeTo ( IOPMPowerFlags, unsigned long, IOService*)
{
return 0;
}
void IOService::powerChangeDone ( unsigned long )
{
}
IOReturn IOService::newTemperature ( long currentTemp, IOService * whichZone )
{
return IOPMNoErr;
}
#undef super
#define super OSObject
OSDefineMetaClassAndStructors(IOPMprot, OSObject)
bool IOPMprot::serialize(OSSerialize *s) const
{
OSString * theOSString;
char * buffer;
char * ptr;
int i;
bool rtn_code;
buffer = ptr = IONew(char, 2000);
if(!buffer)
return false;
ptr += sprintf(ptr,"{ theNumberOfPowerStates = %d, ",(unsigned int)theNumberOfPowerStates);
if ( theNumberOfPowerStates != 0 ) {
ptr += sprintf(ptr,"version %d, ",(unsigned int)thePowerStates[0].version);
}
if ( theNumberOfPowerStates != 0 ) {
for ( i = 0; i < (int)theNumberOfPowerStates; i++ ) {
ptr += sprintf(ptr,"power state %d = { ",i);
ptr += sprintf(ptr,"capabilityFlags %08x, ",(unsigned int)thePowerStates[i].capabilityFlags);
ptr += sprintf(ptr,"outputPowerCharacter %08x, ",(unsigned int)thePowerStates[i].outputPowerCharacter);
ptr += sprintf(ptr,"inputPowerRequirement %08x, ",(unsigned int)thePowerStates[i].inputPowerRequirement);
ptr += sprintf(ptr,"staticPower %d, ",(unsigned int)thePowerStates[i].staticPower);
ptr += sprintf(ptr,"unbudgetedPower %d, ",(unsigned int)thePowerStates[i].unbudgetedPower);
ptr += sprintf(ptr,"powerToAttain %d, ",(unsigned int)thePowerStates[i].powerToAttain);
ptr += sprintf(ptr,"timeToAttain %d, ",(unsigned int)thePowerStates[i].timeToAttain);
ptr += sprintf(ptr,"settleUpTime %d, ",(unsigned int)thePowerStates[i].settleUpTime);
ptr += sprintf(ptr,"timeToLower %d, ",(unsigned int)thePowerStates[i].timeToLower);
ptr += sprintf(ptr,"settleDownTime %d, ",(unsigned int)thePowerStates[i].settleDownTime);
ptr += sprintf(ptr,"powerDomainBudget %d }, ",(unsigned int)thePowerStates[i].powerDomainBudget);
}
}
ptr += sprintf(ptr,"aggressiveness = %d, ",(unsigned int)aggressiveness);
ptr += sprintf(ptr,"myCurrentState = %d, ",(unsigned int)myCurrentState);
ptr += sprintf(ptr,"parentsCurrentPowerFlags = %08x, ",(unsigned int)parentsCurrentPowerFlags);
ptr += sprintf(ptr,"maxCapability = %d }",(unsigned int)maxCapability);
theOSString = OSString::withCString(buffer);
rtn_code = theOSString->serialize(s);
theOSString->release();
IODelete(buffer, char, 2000);
return rtn_code;
}
#undef super
#define super OSObject
OSDefineMetaClassAndStructors(IOPMpriv, OSObject)
bool IOPMpriv::serialize(OSSerialize *s) const
{
OSString * theOSString;
bool rtn_code;
char * buffer;
char * ptr;
IOPMinformee * nextObject;
buffer = ptr = IONew(char, 2000);
if(!buffer)
return false;
ptr += sprintf(ptr,"{ this object = %08x",(unsigned int)owner);
if ( we_are_root ) {
ptr += sprintf(ptr," (root)");
}
ptr += sprintf(ptr,", ");
nextObject = interestedDrivers->firstInList(); while ( nextObject != NULL ) {
ptr += sprintf(ptr,"interested driver = %08x, ",(unsigned int)nextObject->whatObject);
nextObject = interestedDrivers->nextInList(nextObject);
}
if ( machine_state != IOPMfinished ) {
ptr += sprintf(ptr,"machine_state = %d, ",(unsigned int)machine_state);
ptr += sprintf(ptr,"driver_timer = %d, ",(unsigned int)driver_timer);
ptr += sprintf(ptr,"settle_time = %d, ",(unsigned int)settle_time);
ptr += sprintf(ptr,"head_note_flags = %08x, ",(unsigned int)head_note_flags);
ptr += sprintf(ptr,"head_note_state = %d, ",(unsigned int)head_note_state);
ptr += sprintf(ptr,"head_note_outputFlags = %08x, ",(unsigned int)head_note_outputFlags);
ptr += sprintf(ptr,"head_note_domainState = %08x, ",(unsigned int)head_note_domainState);
ptr += sprintf(ptr,"head_note_capabilityFlags = %08x, ",(unsigned int)head_note_capabilityFlags);
ptr += sprintf(ptr,"head_note_pendingAcks = %d, ",(unsigned int)head_note_pendingAcks);
}
if ( device_overrides ) {
ptr += sprintf(ptr,"device overrides, ");
}
ptr += sprintf(ptr,"driverDesire = %d, ",(unsigned int)driverDesire);
ptr += sprintf(ptr,"deviceDesire = %d, ",(unsigned int)deviceDesire);
ptr += sprintf(ptr,"ourDesiredPowerState = %d, ",(unsigned int)ourDesiredPowerState);
ptr += sprintf(ptr,"previousRequest = %d }",(unsigned int)previousRequest);
theOSString = OSString::withCString(buffer);
rtn_code = theOSString->serialize(s);
theOSString->release();
IODelete(buffer, char, 2000);
return rtn_code;
}