-/*
+ /*
* Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
* @APPLE_LICENSE_HEADER_END@
*/
#include <IOKit/IOWorkLoop.h>
-#include <IOKit/IOCommandQueue.h>
+#include <IOKit/IOCommandGate.h>
#include <IOKit/IOTimerEventSource.h>
#include <IOKit/IOPlatformExpert.h>
+#include <IOKit/IOKitDebug.h>
+#include <IOKit/IOTimeStamp.h>
#include <IOKit/pwr_mgt/RootDomain.h>
-#include <IOKit/pwr_mgt/IOPM.h>
+#include <IOKit/pwr_mgt/IOPMPrivate.h>
#include <IOKit/IOMessage.h>
#include "RootDomainUserClient.h"
#include "IOKit/pwr_mgt/IOPowerConnection.h"
-extern "C" {
-extern void kprintf(const char *, ...);
-}
+extern "C" void kprintf(const char *, ...);
extern const IORegistryPlane * gIOPowerPlane;
-void PMreceiveCmd ( OSObject *, void *, void *, void *, void * );
+// debug trace function
+static inline void
+ioSPMTrace(unsigned int csc,
+ unsigned int a = 0, unsigned int b = 0,
+ unsigned int c = 0, unsigned int d = 0)
+{
+ if (gIOKitDebug & kIOLogTracePower)
+ IOTimeStampConstant(IODBG_POWER(csc), a, b, c, d);
+}
+
+IOReturn broadcast_aggressiveness ( OSObject *, void *, void *, void *, void * );
static void sleepTimerExpired(thread_call_param_t);
+static void wakeupClamshellTimerExpired ( thread_call_param_t us);
#define number_of_power_states 5
bool IOPMrootDomain::start ( IOService * nub )
{
+ OSDictionary *tmpDict;
+
super::start(nub);
gRootDomain = this;
canSleep = true;
wrangler = NULL;
sleepASAP = false;
+ ignoringClamshellDuringWakeup = false;
+ tmpDict = OSDictionary::withCapacity(1);
+ setProperty(kRootDomainSupportedFeatures, tmpDict);
+ tmpDict->release();
+
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);
-
+ clamshellWakeupIgnore = thread_call_allocate((thread_call_func_t)wakeupClamshellTimerExpired, (thread_call_param_t) this);
diskSyncCalloutEntry = thread_call_allocate(&disk_sync_callout, (thread_call_param_t) this);
patriarch = new IORootParent; // create our parent
return true;
}
+// **********************************************************************************
+// setProperties
+//
+// Receive a setProperty call
+// The "System Boot" property means the system is completely booted.
+// **********************************************************************************
+IOReturn IOPMrootDomain::setProperties ( OSObject *props_obj)
+{
+ OSDictionary *dict = OSDynamicCast(OSDictionary, props_obj);
+
+ if(!dict) return kIOReturnBadArgument;
+
+ if(dict->getObject(OSString::withCString("System Boot Complete"))) {
+ systemBooting = false;
+ kprintf("IOPM: received System Boot Complete property");
+ adjustPowerState();
+ }
+
+ return kIOReturnSuccess;
+}
+
//*********************************************************************************
// youAreRoot
// **********************************************************************************
// command_received
//
-// We have received a command from ourselves on the command queue.
-// If it is to send a recently-received aggressiveness factor, do so.
-// Otherwise, it's something the superclass enqueued.
+// No longer used
// **********************************************************************************
-void IOPMrootDomain::command_received ( void * command, void * x, void * y, void * z )
+void IOPMrootDomain::command_received ( void * w, void * x, void * y, void * z )
{
- switch ( (int)command ) {
- case kIOPMBroadcastAggressiveness:
-
- super::setAggressiveness((unsigned long)x,(unsigned long)y);
+ super::command_received(w,x,y,z);
+}
- // 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];
+// **********************************************************************************
+// broadcast_aggressiveness
+//
+// **********************************************************************************
+IOReturn broadcast_aggressiveness ( OSObject * root, void * x, void * y, void *, void * )
+{
+ ((IOPMrootDomain *)root)->broadcast_it((unsigned long)x,(unsigned long)y);
+ return IOPMNoErr;
+}
- if ( (int)x == kPMMinutesToSleep ) {
- 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;
+// **********************************************************************************
+// broadcast_it
+//
+// We are behind the command gate to broadcast an aggressiveness factor. We let the
+// superclass do it, but we need to snoop on factors that affect idle sleep.
+// **********************************************************************************
+void IOPMrootDomain::broadcast_it (unsigned long type, unsigned long value)
+{
+ super::setAggressiveness(type,value);
+
+ // Save user's spin down timer to restore after we replace it for idle sleep
+ if( type == kPMMinutesToSpinDown ) user_spindown = value;
+
+ // Use longestNonSleepSlider to calculate dimming adjust idle sleep timer
+ longestNonSleepSlider = pm_vars->current_aggressiveness_values[kPMMinutesToDim];
+
+
+ if ( type == kPMMinutesToSleep ) {
+ if ( (sleepSlider == 0) && (value != 0) ) {
+ sleepSlider = value;
+ adjustPowerState(); // idle sleep is now enabled, maybe sleep now
+ }
+ sleepSlider = value;
+ 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;
}
}
((IOPMrootDomain *)us)->handleSleepTimerExpiration();
}
+
+static void wakeupClamshellTimerExpired ( thread_call_param_t us)
+{
+ ((IOPMrootDomain *)us)->stopIgnoringClamshellEventsDuringWakeup();
+}
+
// **********************************************************************************
// handleSleepTimerExpiration
}
+void IOPMrootDomain::stopIgnoringClamshellEventsDuringWakeup(void)
+{
+ OSObject * state;
+
+ // Allow clamshell-induced sleep now
+ ignoringClamshellDuringWakeup = false;
+
+ if ((state = getProperty(kAppleClamshellStateKey)))
+ publishResource(kAppleClamshellStateKey, state);
+}
+
//*********************************************************************************
// setAggressiveness
//
-// Some aggressiveness factor has changed. We put this change on our
-// command queue so that we can broadcast it to the hierarchy while on
+// Some aggressiveness factor has changed. We broadcast it to the hierarchy while on
// the Power Mangement workloop thread. This enables objects in the
// hierarchy to successfully alter their idle timers, which are all on the
// same thread.
IOReturn IOPMrootDomain::setAggressiveness ( unsigned long type, unsigned long newLevel )
{
- 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();
+ if ( pm_vars->PMcommandGate ) {
+ pm_vars->PMcommandGate->runAction(broadcast_aggressiveness,(void *)type,(void *)newLevel);
}
-
- pm_vars->commandQueue->enqueueCommand(true, (void *)kIOPMBroadcastAggressiveness, (void *) type, (void *) newLevel );
return kIOReturnSuccess;
}
idleSleepPending = false; // re-enable this timer for next sleep
IOLog("System Sleep\n");
pm_vars->thePlatform->sleepKernel(); // sleep now
-
- // now we're waking
+
+ ioSPMTrace(IOPOWER_WAKE, * (int *) this); // 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
+
+ // Ignore closed clamshell during wakeup and for a few seconds
+ // after wakeup is complete
+ ignoringClamshellDuringWakeup = true;
+
gSleepOrShutdownPending = 0; // sleep transition complete
patriarch->wakeSystem(); // get us some power
IOLog("System Wake\n");
systemWake(); // tell the tree we're waking
-
+
+ // Allow drivers to request extra processing time before clamshell
+ // sleep if kIOREMSleepEnabledKey is present.
+ // Ignore clamshell events for at least 5 seconds
+ if(getProperty(kIOREMSleepEnabledKey)) {
+ // clamshellWakeupIgnore callout clears ignoreClamshellDuringWakeup bit
+ clock_interval_to_deadline(5, kSecondScale, &deadline);
+ if(clamshellWakeupIgnore) thread_call_enter_delayed(clamshellWakeupIgnore, deadline);
+ } else ignoringClamshellDuringWakeup = false;
+
propertyPtr = OSDynamicCast(OSNumber,getProperty("WakeEvent"));
if ( propertyPtr ) { // find out what woke us
theProperty = propertyPtr->unsigned16BitValue();
}
+// **********************************************************************************
+// publishFeature
+//
+// Adds a new feature to the supported features dictionary
+//
+//
+// **********************************************************************************
+void IOPMrootDomain::publishFeature( const char * feature )
+{
+ OSDictionary *features = (OSDictionary *)getProperty(kRootDomainSupportedFeatures);
+
+ features->setObject(feature, kOSBooleanTrue);
+}
+
+
// **********************************************************************************
// newUserClient
//
IOReturn IOPMrootDomain::receivePowerNotification (UInt32 msg)
{
+ if (msg & kIOPMOverTemp) {
+ IOLog("Power Management received emergency overtemp signal. Going to sleep.");
+ (void) sleepSystem ();
+ }
+ if (msg & kIOPMSetDesktopMode) {
+ desktopMode = (0 != (msg & kIOPMSetValue));
+ msg &= ~(kIOPMSetDesktopMode | kIOPMSetValue);
+ }
+ if (msg & kIOPMSetACAdaptorConnected) {
+ acAdaptorConnect = (0 != (msg & kIOPMSetValue));
+ msg &= ~(kIOPMSetACAdaptorConnected | kIOPMSetValue);
+ }
+ if (msg & kIOPMEnableClamshell) {
+ ignoringClamshell = false;
+ }
+ if (msg & kIOPMDisableClamshell) {
+ ignoringClamshell = true;
+ }
+
+ if (msg & kIOPMProcessorSpeedChange) {
+ IOService *pmu = waitForService(serviceMatching("ApplePMU"));
+ pmu->callPlatformFunction("prepareForSleep", false, 0, 0, 0, 0);
+ pm_vars->thePlatform->sleepKernel();
+ pmu->callPlatformFunction("recoverFromSleep", false, 0, 0, 0, 0);
+ }
+
if (msg & kIOPMSleepNow) {
(void) sleepSystem ();
}
}
if (msg & kIOPMClamshellClosed) {
- if ( ! ignoringClamshell ) {
- (void) sleepSystem ();
- }
- }
+ if ( !ignoringClamshell && !ignoringClamshellDuringWakeup
+ && (!desktopMode || !acAdaptorConnect) ) {
- if (msg & kIOPMEnableClamshell) {
- ignoringClamshell = false;
- }
- if (msg & kIOPMDisableClamshell) {
- ignoringClamshell = true;
+ (void) sleepSystem ();
+ }
}
if (msg & kIOPMPowerButton) { // toggle state of sleep/wake
}
iter->release();
}
-
+
if ( (extraSleepDelay == 0) && (powerRequestFlag == 0) ) {
sleepASAP = true;
}
// type to the client or application being notified.
//*********************************************************************************
-void IOPMrootDomain::tellChangeUp ( unsigned long )
+void IOPMrootDomain::tellChangeUp ( unsigned long stateNum)
{
- return tellClients(kIOMessageSystemHasPoweredOn);
+ if ( stateNum == ON_STATE ) {
+ return tellClients(kIOMessageSystemHasPoweredOn);
+ }
}
//*********************************************************************************
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);
}
+//*********************************************************************************
+// changePowerStateTo & changePowerStateToPriv
+//
+// Override of these methods for logging purposes.
+//*********************************************************************************
+
+IOReturn IOPMrootDomain::changePowerStateTo ( unsigned long ordinal )
+{
+ ioSPMTrace(IOPOWER_ROOT, * (int *) this, (int) true, (int) ordinal);
+
+ return super::changePowerStateTo(ordinal);
+}
+
+IOReturn IOPMrootDomain::changePowerStateToPriv ( unsigned long ordinal )
+{
+ ioSPMTrace(IOPOWER_ROOT, * (int *) this, (int) false, (int) ordinal);
+
+ return super::changePowerStateToPriv(ordinal);
+}
+
//*********************************************************************************
// sysPowerDownHandler
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).
// Another option is not to schedule the thread and wait for
// ack timeout...
AbsoluteTime deadline;
- clock_interval_to_deadline( 15, kSecondScale, &deadline );
+ clock_interval_to_deadline( 30, kSecondScale, &deadline );
thread_call_enter1_delayed( rootDomain->diskSyncCalloutEntry,
(thread_call_param_t)params->powerRef,
deadline );
thread_call_enter1(rootDomain->diskSyncCalloutEntry, (thread_call_param_t)params->powerRef);
ret = kIOReturnSuccess;
break;
+
+ case kIOMessageSystemWillPowerOff:
+ case kIOMessageSystemWillRestart:
+ ret = kIOReturnUnsupported;
+ break;
+
default:
ret = kIOReturnUnsupported;
break;
} else {
// accelerate disk spin down if spin down timer is non-zero (zero = never spin down)
- if(0 != rootDomain->user_spindown)
+ // and if system sleep is non-Never
+ if( (0 != rootDomain->user_spindown) && (0 != rootDomain->sleepSlider) )
rootDomain->setQuickSpinDownTimeout();
}