#include <IOKit/pwr_mgt/IOPM.h>
#include <IOKit/IOMessage.h>
#include "RootDomainUserClient.h"
+#include "IOKit/pwr_mgt/IOPowerConnection.h"
extern "C" {
extern void kprintf(const char *, ...);
extern const IORegistryPlane * gIOPowerPlane;
void PMreceiveCmd ( OSObject *, void *, void *, void *, void * );
-bool rootHasPMU( OSObject * us, void *, IOService * yourDevice );
+static void sleepTimerExpired(thread_call_param_t);
-#define number_of_power_states 3
+#define number_of_power_states 5
#define OFF_STATE 0
-#define SLEEP_STATE 1
-#define ON_STATE 2
+#define RESTART_STATE 1
+#define SLEEP_STATE 2
+#define DOZE_STATE 3
+#define ON_STATE 4
-#define ON_POWER IOPMPowerOn
-#define SLEEP_POWER IOPMAuxPowerOn
+#define ON_POWER kIOPMPowerOn
+#define RESTART_POWER kIOPMRestart
+#define SLEEP_POWER kIOPMAuxPowerOn
+#define DOZE_POWER kIOPMDoze
static IOPMPowerState ourPowerStates[number_of_power_states] = {
- {1,0,0,0,0,0,0,0,0,0,0,0},
- {1,0,0,SLEEP_POWER,0,0,0,0,0,0,0,0},
- {1,IOPMPowerOn,IOPMPowerOn,ON_POWER,0,0,0,0,0,0,0,0},
+ {1,0, 0, 0,0,0,0,0,0,0,0,0}, // state 0, off
+ {1,kIOPMRestartCapability, kIOPMRestart, RESTART_POWER,0,0,0,0,0,0,0,0}, // state 1, restart
+ {1,kIOPMSleepCapability, kIOPMSleep, SLEEP_POWER,0,0,0,0,0,0,0,0}, // state 2, sleep
+ {1,kIOPMDoze, kIOPMDoze, DOZE_POWER,0,0,0,0,0,0,0,0}, // state 3, doze
+ {1,kIOPMPowerOn, kIOPMPowerOn, ON_POWER,0,0,0,0,0,0,0,0}, // state 4, on
};
static IOPMrootDomain * gRootDomain;
+static UInt32 gSleepOrShutdownPending = 0;
+
#define super IOService
OSDefineMetaClassAndStructors(IOPMrootDomain,IOService)
return gRootDomain->registerInterest( gIOGeneralInterest, handler, self, ref );
}
+ IONotifier * registerPrioritySleepWakeInterest(IOServiceInterestHandler handler, void * self, void * ref = 0)
+ {
+ return gRootDomain->registerInterest( gIOPriorityPowerStateInterest, handler, self, ref );
+ }
+
IOReturn acknowledgeSleepWakeNotification(void * PMrefcon)
{
return gRootDomain->allowPowerChange ( (unsigned long)PMrefcon );
}
+ IOReturn vetoSleepWakeNotification(void * PMrefcon)
+ {
+ return gRootDomain->cancelPowerChange ( (unsigned long)PMrefcon );
+ }
+
+ IOReturn rootDomainRestart ( void )
+ {
+ return gRootDomain->restartSystem();
+ }
+
+ IOReturn rootDomainShutdown ( void )
+ {
+ return gRootDomain->shutdownSystem();
+ }
+
+ void IOSystemShutdownNotification ( void )
+ {
+ for ( int i = 0; i < 100; i++ )
+ {
+ if ( OSCompareAndSwap( 0, 1, &gSleepOrShutdownPending ) ) break;
+ IOSleep( 100 );
+ }
+ }
+
+ int sync_internal(void);
}
+/*
+A device is always in the highest power state which satisfies its driver, its policy-maker, and any power domain
+children it has, but within the constraint of the power state provided by its parent. The driver expresses its desire by
+calling changePowerStateTo(), the policy-maker expresses its desire by calling changePowerStateToPriv(), and the children
+express their desires by calling requestPowerDomainState().
+
+The Root Power Domain owns the policy for idle and demand sleep and doze for the system. It is a power-managed IOService just
+like the others in the system. It implements several power states which correspond to what we see as Sleep, Doze, etc.
+
+The sleep/doze policy is as follows:
+Sleep and Doze are prevented if the case is open so that nobody will think the machine is off and plug/unplug cards.
+Sleep and Doze are prevented if the sleep timeout slider in the preferences panel is at zero.
+The system cannot Sleep, but can Doze if some object in the tree is in a power state marked kIOPMPreventSystemSleep.
+
+These three conditions are enforced using the "driver clamp" by calling changePowerStateTo(). For example, if the case is
+opened, changePowerStateTo(ON_STATE) is called to hold the system on regardless of the desires of the children of the root or
+the state of the other clamp.
+
+Demand Sleep/Doze is initiated by pressing the front panel power button, closing the clamshell, or selecting the menu item.
+In this case the root's parent actually initiates the power state change so that the root has no choice and does not give
+applications the opportunity to veto the change.
+
+Idle Sleep/Doze occurs if no objects in the tree are in a state marked kIOPMPreventIdleSleep. When this is true, the root's
+children are not holding the root on, so it sets the "policy-maker clamp" by calling changePowerStateToPriv(ON_STATE)
+to hold itself on until the sleep timer expires. This timer is set for the difference between the sleep timeout slider and
+the larger of the display dim timeout slider and the disk spindown timeout slider in the Preferences panel. For example, if
+the system is set to sleep after thirty idle minutes, and the display and disk are set to sleep after five idle minutes,
+when there is no longer an object in the tree holding the system out of Idle Sleep (via kIOPMPreventIdleSleep), the root
+sets its timer for 25 minutes (30 - 5). When the timer expires, it releases its clamp and now nothing is holding it awake,
+so it falls asleep.
+
+Demand sleep is prevented when the system is booting. When preferences are transmitted by the loginwindow at the end of
+boot, a flag is cleared, and this allows subsequent Demand Sleep.
+
+The system will not Sleep, but will Doze if some object calls setSleepSupported(kPCICantSleep) during a power change to the sleep state (this can be done by the PCI Aux Power Supply drivers, Slots99, MacRISC299, etc.). This is not enforced with
+a clamp, but sets a flag which is noticed before actually sleeping the kernel. If the flag is set, the root steps up
+one power state from Sleep to Doze, and any objects in the tree for which this is relevent will act appropriately (USB and
+ADB will turn on again so that they can wake the system out of Doze (keyboard/mouse activity will cause the Display Wrangler
+to be tickled)).
+*/
+
+
+// **********************************************************************************
+
+IOPMrootDomain * IOPMrootDomain::construct( void )
+{
+ IOPMrootDomain * root;
+
+ root = new IOPMrootDomain;
+ if( root)
+ root->init();
+
+ return( root );
+}
+
+// **********************************************************************************
+
+static void disk_sync_callout(thread_call_param_t p0, thread_call_param_t p1)
+{
+ IOService * rootDomain = (IOService *) p0;
+ unsigned long pmRef = (unsigned long) p1;
+
+ sync_internal();
+ rootDomain->allowPowerChange(pmRef);
+}
// **********************************************************************************
// start
// We don't do much here. The real initialization occurs when the platform
// expert informs us we are the root.
// **********************************************************************************
+
+
bool IOPMrootDomain::start ( IOService * nub )
{
super::start(nub);
gRootDomain = this;
PMinit();
+ setProperty("IOSleepSupported","");
allowSleep = true;
- sleepIsSupported = false;
- idlePeriod = 0;
+ sleepIsSupported = true;
systemBooting = true;
-// systemBooting = false; // temporary work-around for 2589847
- ignoringClamshell = false;
-
+ ignoringClamshell = true;
+ sleepSlider = 0;
+ idleSleepPending = false;
+ canSleep = true;
+ wrangler = NULL;
+ sleepASAP = false;
+
pm_vars->PMworkloop = IOWorkLoop::workLoop(); // make the workloop
pm_vars->commandQueue = IOCommandQueue::commandQueue(this, PMreceiveCmd); // make a command queue
if (! pm_vars->commandQueue ||
( pm_vars->PMworkloop->addEventSource( pm_vars->commandQueue) != kIOReturnSuccess) ) {
return IOPMNoErr;
}
+ extraSleepTimer = thread_call_allocate((thread_call_func_t)sleepTimerExpired, (thread_call_param_t) this);
+
+ diskSyncCalloutEntry = thread_call_allocate(&disk_sync_callout, (thread_call_param_t) this);
patriarch = new IORootParent; // create our parent
patriarch->init();
registerPowerDriver(this,ourPowerStates,number_of_power_states);
- // Clamp power on. We will revisit this decision when the login window is displayed
- // and we receive preferences via SetAggressiveness.
- changePowerStateToPriv(ON_STATE); // clamp power on
- powerOverrideOnPriv();
+ setPMRootDomain(this);
+ changePowerStateToPriv(ON_STATE); // set a clamp until we sleep
+
+ registerPrioritySleepWakeInterest( &sysPowerDownHandler, this, 0); // install power change handler
+
+ // Register for a notification when IODisplayWrangler is published
+ addNotification( gIOPublishNotification, serviceMatching("IODisplayWrangler"), &displayWranglerPublished, this, 0);
registerService(); // let clients find us
return IOPMNoErr;
}
-
// **********************************************************************************
// command_received
//
void IOPMrootDomain::command_received ( void * command, void * x, void * y, void * z )
{
switch ( (int)command ) {
- case kPMbroadcastAggressiveness:
+ case kIOPMBroadcastAggressiveness:
+
+ super::setAggressiveness((unsigned long)x,(unsigned long)y);
+
+ // Save user's spin down timer to restore after we replace it for idle sleep
+ if( (int)x == kPMMinutesToSpinDown ) user_spindown = (unsigned int) y;
+
+ // Use longestNonSleepSlider to calculate dimming adjust idle sleep timer
+ longestNonSleepSlider = pm_vars->current_aggressiveness_values[kPMMinutesToDim];
+
+
if ( (int)x == kPMMinutesToSleep ) {
- idlePeriod = (int)y*60;
- if ( allowSleep && sleepIsSupported ) {
- setIdleTimerPeriod(idlePeriod); // set new timeout
+ if ( (sleepSlider == 0) && ((int)y != 0) ) {
+ sleepSlider = (int)y;
+ adjustPowerState(); // idle sleep is now enabled, maybe sleep now
}
+ sleepSlider = (int)y;
+ if ( sleepSlider == 0 ) {
+ adjustPowerState(); // idle sleep is now disabled
+ patriarch->wakeSystem(); // make sure we're powered
+ }
+ }
+ if ( sleepSlider > longestNonSleepSlider ) {
+ extraSleepDelay = sleepSlider - longestNonSleepSlider ;
+ }
+ else {
+ extraSleepDelay = 0;
}
break;
+
default:
super::command_received(command,x,y,z);
break;
}
+// **********************************************************************************
+// sleepTimerExpired
+//
+// **********************************************************************************
+static void sleepTimerExpired ( thread_call_param_t us)
+{
+ ((IOPMrootDomain *)us)->handleSleepTimerExpiration();
+ }
+
+
+// **********************************************************************************
+// handleSleepTimerExpiration
+//
+// The time between the sleep idle timeout and the next longest one has elapsed.
+// It's time to sleep. Start that by removing the clamp that's holding us awake.
+// **********************************************************************************
+void IOPMrootDomain::handleSleepTimerExpiration ( void )
+{
+ // accelerate disk spin down if spin down timer is non-zero (zero = never spin down)
+ if(0 != user_spindown)
+ setQuickSpinDownTimeout();
+
+ sleepASAP = true;
+ adjustPowerState();
+}
+
+
//*********************************************************************************
// setAggressiveness
//
IOReturn IOPMrootDomain::setAggressiveness ( unsigned long type, unsigned long newLevel )
{
- systemBooting = false; // when the finder launches, this method gets called -- system booting is done.
+ if ( systemBooting && (type == kPMMinutesToDim) ) {
+ systemBooting = false; // when the login window launches, this method gets called -- system booting is done.
+ IOLog("Root power domain receiving initial preferences\n");
+ adjustPowerState();
+ }
- pm_vars->commandQueue->enqueueCommand(true, (void *)kPMbroadcastAggressiveness, (void *) type, (void *) newLevel );
- super::setAggressiveness(type,newLevel);
+ pm_vars->commandQueue->enqueueCommand(true, (void *)kIOPMBroadcastAggressiveness, (void *) type, (void *) newLevel );
return kIOReturnSuccess;
}
kprintf("sleep demand received\n");
if ( !systemBooting && allowSleep && sleepIsSupported ) {
patriarch->sleepSystem();
+ return kIOReturnSuccess;
}
+ if ( !systemBooting && allowSleep && !sleepIsSupported ) {
+ patriarch->dozeSystem();
+ return kIOReturnSuccess;
+ }
+ return kIOReturnSuccess;
+}
+
+
+// **********************************************************************************
+// shutdownSystem
+//
+// **********************************************************************************
+IOReturn IOPMrootDomain::shutdownSystem ( void )
+{
+ patriarch->shutDownSystem();
+ return kIOReturnSuccess;
+}
+
+
+// **********************************************************************************
+// restartSystem
+//
+// **********************************************************************************
+IOReturn IOPMrootDomain::restartSystem ( void )
+{
+ patriarch->restartSystem();
return kIOReturnSuccess;
}
// powerChangeDone
//
// This overrides powerChangeDone in IOService.
-// If we just finished switching to state zero, call the platform expert to
-// sleep the kernel.
-// Then later, when we awake, the kernel returns here and we wake the system.
+//
+// Finder sleep and idle sleep move us from the ON state to the SLEEP_STATE.
+// In this case:
+// If we just finished going to the SLEEP_STATE, and the platform is capable of true sleep,
+// sleep the kernel. Otherwise switch up to the DOZE_STATE which will keep almost
+// everything as off as it can get.
+//
+// **********************************************************************************
+void IOPMrootDomain::powerChangeDone ( unsigned long previousState )
+{
+ OSNumber * propertyPtr;
+ unsigned short theProperty;
+ AbsoluteTime deadline;
+
+ switch ( pm_vars->myCurrentState ) {
+ case SLEEP_STATE:
+ if ( canSleep && sleepIsSupported ) {
+ idleSleepPending = false; // re-enable this timer for next sleep
+ IOLog("System Sleep\n");
+ pm_vars->thePlatform->sleepKernel(); // sleep now
+
+ // now we're waking
+ clock_interval_to_deadline(30, kSecondScale, &deadline); // stay awake for at least 30 seconds
+ thread_call_enter_delayed(extraSleepTimer, deadline);
+ idleSleepPending = true; // this gets turned off when we sleep again
+ gSleepOrShutdownPending = 0; // sleep transition complete
+ patriarch->wakeSystem(); // get us some power
+
+ IOLog("System Wake\n");
+ systemWake(); // tell the tree we're waking
+
+ propertyPtr = OSDynamicCast(OSNumber,getProperty("WakeEvent"));
+ if ( propertyPtr ) { // find out what woke us
+ theProperty = propertyPtr->unsigned16BitValue();
+ IOLog("Wake event %04x\n",theProperty);
+ if ( (theProperty == 0x0008) || //lid
+ (theProperty == 0x0800) || // front panel button
+ (theProperty == 0x0020) || // external keyboard
+ (theProperty == 0x0001) ) { // internal keyboard
+ reportUserInput();
+ }
+ }
+ else {
+ IOLog("Unknown wake event\n");
+ reportUserInput(); // don't know, call it user input then
+ }
+
+ changePowerStateToPriv(ON_STATE); // wake for thirty seconds
+ powerOverrideOffPriv();
+ }
+ else {
+ patriarch->sleepToDoze(); // allow us to step up a power state
+ changePowerStateToPriv(DOZE_STATE); // and do it
+ }
+ break;
+
+ case DOZE_STATE:
+ if ( previousState != DOZE_STATE ) {
+ IOLog("System Doze\n");
+ }
+ idleSleepPending = false; // re-enable this timer for next sleep
+ gSleepOrShutdownPending = 0;
+ break;
+
+ case RESTART_STATE:
+ IOLog("System Restart\n");
+ PEHaltRestart(kPERestartCPU);
+ break;
+
+ case OFF_STATE:
+ IOLog("System Halt\n");
+ PEHaltRestart(kPEHaltCPU);
+ break;
+ }
+}
+
+
+// **********************************************************************************
+// wakeFromDoze
+//
+// The Display Wrangler calls here when it switches to its highest state. If the
+// system is currently dozing, allow it to wake by making sure the parent is
+// providing power.
// **********************************************************************************
-void IOPMrootDomain::powerChangeDone ( unsigned long powerStateOrdinal )
+void IOPMrootDomain::wakeFromDoze( void )
{
- if ( powerStateOrdinal == SLEEP_STATE ) {
- pm_vars->thePlatform->sleepKernel();
- activityTickle(kIOPMSubclassPolicy); // reset idle sleep
- systemWake(); // tell the tree we're waking
- patriarch->wakeSystem(); // make sure we have power
- changePowerStateToPriv(ON_STATE); // and wake
+ if ( pm_vars->myCurrentState == DOZE_STATE ) {
+ canSleep = true; // reset this till next attempt
+ powerOverrideOffPriv();
+ patriarch->wakeSystem(); // allow us to wake if children so desire
}
}
(void) sleepSystem ();
}
- if (msg & kIOPMPowerButton) {
- (void) sleepSystem ();
- }
-
if (msg & kIOPMPowerEmergency) {
(void) sleepSystem ();
}
}
}
- if (msg & kIOPMIgnoreClamshell) {
+ if (msg & kIOPMEnableClamshell) {
+ ignoringClamshell = false;
+ }
+ if (msg & kIOPMDisableClamshell) {
ignoringClamshell = true;
}
- if (msg & kIOPMAllowSleep) {
- if ( sleepIsSupported ) {
- setIdleTimerPeriod(idlePeriod);
+ if (msg & kIOPMPowerButton) { // toggle state of sleep/wake
+ if ( pm_vars->myCurrentState == DOZE_STATE ) { // are we dozing?
+ systemWake(); // yes, tell the tree we're waking
+ reportUserInput(); // wake the Display Wrangler
+ }
+ else {
+ (void) sleepSystem ();
}
+ }
+
+ // if the case has been closed, we allow
+ // the machine to be put to sleep or to idle sleep
+
+ if ( (msg & kIOPMAllowSleep) && !allowSleep ) {
allowSleep = true;
- changePowerStateTo (0);
+ adjustPowerState();
}
- // if the case is open on some machines, we must now
- // allow the machine to be put to sleep or to idle sleep
+ // if the case has been opened, we disallow sleep/doze
if (msg & kIOPMPreventSleep) {
- if ( sleepIsSupported ) {
- setIdleTimerPeriod(0);
- }
allowSleep = false;
- changePowerStateTo (number_of_power_states-1);
+ if ( pm_vars->myCurrentState == DOZE_STATE ) { // are we dozing?
+ systemWake(); // yes, tell the tree we're waking
+ adjustPowerState();
+ reportUserInput(); // wake the Display Wrangler
+ }
+ else {
+ adjustPowerState();
+ patriarch->wakeSystem(); // make sure we have power to clamp
+ }
}
return 0;
void IOPMrootDomain::setSleepSupported( IOOptionBits flags )
{
- platformSleepSupport = flags;
- if ( flags & kRootDomainSleepSupported ) {
- sleepIsSupported = true;
- setProperty("IOSleepSupported","");
+ if ( flags & kPCICantSleep ) {
+ canSleep = false;
}
- else
- {
- sleepIsSupported = false;
- removeProperty("IOSleepSupported");
+ else {
+ platformSleepSupport = flags;
+ }
+
+}
+
+//*********************************************************************************
+// requestPowerDomainState
+//
+// The root domain intercepts this call to the superclass.
+//
+// If the clamp bit is not set in the desire, then the child doesn't need the power
+// state it's requesting; it just wants it. The root ignores desires but not needs.
+// If the clamp bit is not set, the root takes it that the child can tolerate no
+// power and interprets the request accordingly. If all children can thus tolerate
+// no power, we are on our way to idle sleep.
+//*********************************************************************************
+
+IOReturn IOPMrootDomain::requestPowerDomainState ( IOPMPowerFlags desiredState, IOPowerConnection * whichChild, unsigned long specification )
+{
+ OSIterator * iter;
+ OSObject * next;
+ IOPowerConnection * connection;
+ unsigned long powerRequestFlag = 0;
+ IOPMPowerFlags editedDesire = desiredState;
+
+ if ( !(desiredState & kIOPMPreventIdleSleep) ) { // if they don't really need it, they don't get it
+ editedDesire = 0;
+ }
+
+
+ IOLockLock(pm_vars->childLock); // recompute sleepIsSupported
+ // and see if all children are asleep
+ iter = getChildIterator(gIOPowerPlane);
+ sleepIsSupported = true;
+
+ if ( iter ) {
+ while ( (next = iter->getNextObject()) ) {
+ if ( (connection = OSDynamicCast(IOPowerConnection,next)) ) {
+ if ( connection == whichChild ) {
+ powerRequestFlag += editedDesire;
+ if ( desiredState & kIOPMPreventSystemSleep ) {
+ sleepIsSupported = false;
+ }
+ }
+ else {
+ powerRequestFlag += connection->getDesiredDomainState();
+ if ( connection->getPreventSystemSleepFlag() ) {
+ sleepIsSupported = false;
+ }
+ }
+ }
+ }
+ iter->release();
+ }
+
+ if ( (extraSleepDelay == 0) && (powerRequestFlag == 0) ) {
+ sleepASAP = true;
}
+
+ adjustPowerState(); // this may put the system to sleep
+
+ IOLockUnlock(pm_vars->childLock);
+
+ editedDesire |= desiredState & kIOPMPreventSystemSleep;
+ return super::requestPowerDomainState(editedDesire,whichChild,specification);
}
+
//*********************************************************************************
// getSleepSupported
//
bool IOPMrootDomain::tellChangeDown ( unsigned long stateNum )
{
- if ( stateNum == SLEEP_STATE ) {
- return super::tellClientsWithResponse(kIOMessageSystemWillSleep);
+ switch ( stateNum ) {
+ case DOZE_STATE:
+ case SLEEP_STATE:
+ return super::tellClientsWithResponse(kIOMessageSystemWillSleep);
+ case RESTART_STATE:
+ return super::tellClientsWithResponse(kIOMessageSystemWillRestart);
+ case OFF_STATE:
+ return super::tellClientsWithResponse(kIOMessageSystemWillPowerOff);
}
- return super::tellChangeDown(stateNum);
+ return super::tellChangeDown(stateNum); // this shouldn't execute
}
//
// We override the superclass implementation so we can send a different message
// type to the client or application being notified.
+//
+// This must be idle sleep since we don't ask apps during any other power change.
//*********************************************************************************
-bool IOPMrootDomain::askChangeDown (unsigned long stateNum)
+bool IOPMrootDomain::askChangeDown ( unsigned long )
{
- if ( stateNum == SLEEP_STATE ) {
- return super::tellClientsWithResponse(kIOMessageCanSystemSleep);
- }
- return super::askChangeDown(stateNum);
+ return super::tellClientsWithResponse(kIOMessageCanSystemSleep);
}
//
// We override the superclass implementation so we can send a different message
// type to the client or application being notified.
+//
+// This must be a vetoed idle sleep, since no other power change can be vetoed.
//*********************************************************************************
void IOPMrootDomain::tellNoChangeDown ( unsigned long )
return tellClients(kIOMessageSystemHasPoweredOn);
}
+//*********************************************************************************
+// reportUserInput
+//
+//*********************************************************************************
+
+void IOPMrootDomain::reportUserInput ( void )
+{
+ OSIterator * iter;
-// **********************************************************************************
-// activityTickle
+ if(!wrangler) {
+ iter = getMatchingServices(serviceMatching("IODisplayWrangler"));
+ if(iter) {
+ wrangler = (IOService *) iter->getNextObject();
+ iter->release();
+ }
+ }
+
+ if(wrangler)
+ wrangler->activityTickle(0,0);
+}
+
+//*********************************************************************************
+// setQuickSpinDownTimeout
//
-// This is called by the HID system and calls the superclass in turn.
-// **********************************************************************************
+//*********************************************************************************
+
+void IOPMrootDomain::setQuickSpinDownTimeout ( void )
+{
+ //IOLog("setQuickSpinDownTimeout\n");
+ super::setAggressiveness((unsigned long)kPMMinutesToSpinDown,(unsigned long)1);
+}
+
+//*********************************************************************************
+// restoreUserSpinDownTimeout
+//
+//*********************************************************************************
+
+void IOPMrootDomain::restoreUserSpinDownTimeout ( void )
+{
+ if(systemBooting) {
+ IOLog("!!!!! WARNING !!!!! restoreUserSpinDownTimeout called too early\n");
+ }
+ //IOLog("restoreUserSpinDownTimeout, user_spindown = %u\n", user_spindown);
+
+ super::setAggressiveness((unsigned long)kPMMinutesToSpinDown,(unsigned long)user_spindown);
+}
+
+
+//*********************************************************************************
+// sysPowerDownHandler
+//
+// Receives a notification when the RootDomain changes state.
+//
+// Allows us to take action on system sleep, power down, and restart after
+// applications have received their power change notifications and replied,
+// but before drivers have powered down. We perform a vfs sync on power down.
+//*********************************************************************************
+
+IOReturn IOPMrootDomain::sysPowerDownHandler( void * target, void * refCon,
+ UInt32 messageType, IOService * service,
+ void * messageArgument, vm_size_t argSize )
+{
+ IOReturn ret;
+ IOPowerStateChangeNotification * params = (IOPowerStateChangeNotification *) messageArgument;
+ IOPMrootDomain * rootDomain = OSDynamicCast(IOPMrootDomain, service);
+
+ if(!rootDomain)
+ return kIOReturnUnsupported;
+
+ switch (messageType) {
+ case kIOMessageSystemWillSleep:
+ rootDomain->powerOverrideOnPriv(); // start ignoring children's requests
+ // (fall through to other cases)
+ case kIOMessageSystemWillPowerOff:
+ case kIOMessageSystemWillRestart:
+
+ // Interested applications have been notified of an impending power
+ // change and have acked (when applicable).
+ // This is our chance to save whatever state we can before powering
+ // down.
+ // We call sync_internal defined in xnu/bsd/vfs/vfs_syscalls.c,
+ // via callout
+
+ // We will ack within 20 seconds
+ params->returnValue = 20 * 1000 * 1000;
+
+ if ( ! OSCompareAndSwap( 0, 1, &gSleepOrShutdownPending ) )
+ {
+ // Purposely delay the ack and hope that shutdown occurs quickly.
+ // Another option is not to schedule the thread and wait for
+ // ack timeout...
+ AbsoluteTime deadline;
+ clock_interval_to_deadline( 15, kSecondScale, &deadline );
+ thread_call_enter1_delayed( rootDomain->diskSyncCalloutEntry,
+ (thread_call_param_t)params->powerRef,
+ deadline );
+ }
+ else
+ thread_call_enter1(rootDomain->diskSyncCalloutEntry, (thread_call_param_t)params->powerRef);
+ ret = kIOReturnSuccess;
+ break;
+ default:
+ ret = kIOReturnUnsupported;
+ break;
+ }
+ return ret;
+}
+
+//*********************************************************************************
+// displayWranglerNotification
+//
+// Receives a notification when the IODisplayWrangler changes state.
+//
+// Allows us to take action on display dim/undim.
+//
+// When the display goes dim we:
+// - Start the idle sleep timer
+// - set the quick spin down timeout
+//
+// On wake from display dim:
+// - Cancel the idle sleep timer
+// - restore the user's chosen spindown timer from the "quick" spin down value
+//*********************************************************************************
-bool IOPMrootDomain::activityTickle ( unsigned long, unsigned long x=0 )
+IOReturn IOPMrootDomain::displayWranglerNotification( void * target, void * refCon,
+ UInt32 messageType, IOService * service,
+ void * messageArgument, vm_size_t argSize )
{
- return super::activityTickle (kIOPMSuperclassPolicy1,ON_STATE);
+ IOPMrootDomain * rootDomain = OSDynamicCast(IOPMrootDomain, (IOService *)target);
+ AbsoluteTime deadline;
+ static bool deviceAlreadyPoweredOff = false;
+
+ if(!rootDomain)
+ return kIOReturnUnsupported;
+
+ switch (messageType) {
+ case kIOMessageDeviceWillPowerOff:
+ // The IODisplayWrangler has powered off either because of idle display sleep
+ // or force system sleep.
+
+ // The display wrangler will send the DeviceWillPowerOff message 4 times until
+ // it gets into its lowest state. We only want to act on the first of those 4.
+ if( deviceAlreadyPoweredOff ) return kIOReturnUnsupported;
+
+ deviceAlreadyPoweredOff = true;
+
+ if( rootDomain->extraSleepDelay ) {
+
+ // start the extra sleep timer
+ clock_interval_to_deadline(rootDomain->extraSleepDelay*60, kSecondScale, &deadline );
+ thread_call_enter_delayed(rootDomain->extraSleepTimer, deadline);
+ rootDomain->idleSleepPending = true;
+
+ } else {
+
+ // accelerate disk spin down if spin down timer is non-zero (zero = never spin down)
+ if(0 != rootDomain->user_spindown)
+ rootDomain->setQuickSpinDownTimeout();
+ }
+
+ break;
+
+ case kIOMessageDeviceHasPoweredOn:
+
+ // The display has powered on either because of UI activity or wake from sleep/doze
+ deviceAlreadyPoweredOff = false;
+ rootDomain->adjustPowerState();
+
+
+ // cancel any pending idle sleep
+ if(rootDomain->idleSleepPending) {
+ thread_call_cancel(rootDomain->extraSleepTimer);
+ rootDomain->idleSleepPending = false;
+ }
+
+ // Change the spindown value back to the user's selection from our accelerated setting
+ if(0 != rootDomain->user_spindown)
+ rootDomain->restoreUserSpinDownTimeout();
+
+ // Put on the policy maker's on clamp.
+
+ break;
+
+ default:
+ break;
+ }
+ return kIOReturnUnsupported;
+ }
+
+//*********************************************************************************
+// displayWranglerPublished
+//
+// Receives a notification when the IODisplayWrangler is published.
+// When it's published we install a power state change handler.
+//
+//*********************************************************************************
+
+bool IOPMrootDomain::displayWranglerPublished( void * target, void * refCon,
+ IOService * newService)
+{
+ IOPMrootDomain * rootDomain = OSDynamicCast(IOPMrootDomain, (IOService *)target);
+
+ if(!rootDomain)
+ return false;
+
+ rootDomain->wrangler = newService;
+
+ // we found the display wrangler, now install a handler
+ if( !rootDomain->wrangler->registerInterest( gIOGeneralInterest, &displayWranglerNotification, target, 0) ) {
+ IOLog("IOPMrootDomain::displayWranglerPublished registerInterest failed\n");
+ return false;
+ }
+
+ return true;
}
+//*********************************************************************************
+// adjustPowerState
+//
+// Some condition that affects our wake/sleep/doze decision has changed.
+//
+// If the sleep slider is in the off position, we cannot sleep or doze.
+// If the enclosure is open, we cannot sleep or doze.
+// If the system is still booting, we cannot sleep or doze.
+//
+// In those circumstances, we prevent sleep and doze by holding power on with
+// changePowerStateToPriv(ON).
+//
+// If the above conditions do not exist, and also the sleep timer has expired, we
+// allow sleep or doze to occur with either changePowerStateToPriv(SLEEP) or
+// changePowerStateToPriv(DOZE) depending on whether or not we already know the
+// platform cannot sleep.
+//
+// In this case, sleep or doze will either occur immediately or at the next time
+// that no children are holding the system out of idle sleep via the
+// kIOPMPreventIdleSleep flag in their power state arrays.
+//*********************************************************************************
+
+void IOPMrootDomain::adjustPowerState( void )
+{
+ if ( (sleepSlider == 0) ||
+ ! allowSleep ||
+ systemBooting ) {
+ changePowerStateToPriv(ON_STATE);
+ }
+ else {
+ if ( sleepASAP ) {
+ sleepASAP = false;
+ if ( sleepIsSupported ) {
+ changePowerStateToPriv(SLEEP_STATE);
+ }
+ else {
+ changePowerStateToPriv(DOZE_STATE);
+ }
+ }
+ }
+}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
OSDefineMetaClassAndStructors(IORootParent, IOService)
-#define number_of_patriarch_power_states 3
+// This array exactly parallels the state array for the root domain.
+// Power state changes initiated by a device can be vetoed by a client of the device, and
+// power state changes initiated by the parent of a device cannot be vetoed by a client of the device,
+// so when the root domain wants a power state change that cannot be vetoed (e.g. demand sleep), it asks
+// its parent to make the change. That is the reason for this complexity.
-static IOPMPowerState patriarchPowerStates[number_of_patriarch_power_states] = {
+static IOPMPowerState patriarchPowerStates[number_of_power_states] = {
{1,0,0,0,0,0,0,0,0,0,0,0}, // off
+ {1,0,RESTART_POWER,0,0,0,0,0,0,0,0,0}, // reset
{1,0,SLEEP_POWER,0,0,0,0,0,0,0,0,0}, // sleep
+ {1,0,DOZE_POWER,0,0,0,0,0,0,0,0,0}, // doze
{1,0,ON_POWER,0,0,0,0,0,0,0,0,0} // running
};
-#define PATRIARCH_OFF 0
-#define PATRIARCH_SLEEP 1
-#define PATRIARCH_ON 2
-
-
bool IORootParent::start ( IOService * nub )
{
+ mostRecentChange = ON_STATE;
super::start(nub);
PMinit();
- registerPowerDriver(this,patriarchPowerStates,number_of_patriarch_power_states);
+ registerPowerDriver(this,patriarchPowerStates,number_of_power_states);
powerOverrideOnPriv();
return true;
}
void IORootParent::shutDownSystem ( void )
{
- changePowerStateToPriv(PATRIARCH_OFF);
+ mostRecentChange = OFF_STATE;
+ changePowerStateToPriv(OFF_STATE);
+}
+
+
+void IORootParent::restartSystem ( void )
+{
+ mostRecentChange = RESTART_STATE;
+ changePowerStateToPriv(RESTART_STATE);
}
void IORootParent::sleepSystem ( void )
{
- changePowerStateToPriv(PATRIARCH_SLEEP);
+ mostRecentChange = SLEEP_STATE;
+ changePowerStateToPriv(SLEEP_STATE);
+}
+
+
+void IORootParent::dozeSystem ( void )
+{
+ mostRecentChange = DOZE_STATE;
+ changePowerStateToPriv(DOZE_STATE);
+}
+
+// Called in demand sleep when sleep discovered to be impossible after actually attaining that state.
+// This brings the parent to doze, which allows the root to step up from sleep to doze.
+
+// In idle sleep, do nothing because the parent is still on and the root can freely change state.
+
+void IORootParent::sleepToDoze ( void )
+{
+ if ( mostRecentChange == SLEEP_STATE ) {
+ changePowerStateToPriv(DOZE_STATE);
+ }
}
void IORootParent::wakeSystem ( void )
{
- changePowerStateToPriv(PATRIARCH_ON);
+ mostRecentChange = ON_STATE;
+ changePowerStateToPriv(ON_STATE);
}