]> git.saurik.com Git - apple/xnu.git/blame - iokit/Drivers/platform/drvAppleRootDomain/RootDomain.cpp
xnu-344.23.tar.gz
[apple/xnu.git] / iokit / Drivers / platform / drvAppleRootDomain / RootDomain.cpp
CommitLineData
9bccf70c 1 /*
1c79356b
A
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
de355530
A
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
1c79356b 11 *
de355530
A
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1c79356b
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
de355530
A
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
1c79356b
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22#include <IOKit/IOWorkLoop.h>
9bccf70c 23#include <IOKit/IOCommandGate.h>
1c79356b
A
24#include <IOKit/IOTimerEventSource.h>
25#include <IOKit/IOPlatformExpert.h>
9bccf70c
A
26#include <IOKit/IOKitDebug.h>
27#include <IOKit/IOTimeStamp.h>
1c79356b 28#include <IOKit/pwr_mgt/RootDomain.h>
d52fe63f 29#include <IOKit/pwr_mgt/IOPMPrivate.h>
1c79356b
A
30#include <IOKit/IOMessage.h>
31#include "RootDomainUserClient.h"
0b4e3aa0 32#include "IOKit/pwr_mgt/IOPowerConnection.h"
1c79356b 33
d52fe63f 34extern "C" void kprintf(const char *, ...);
1c79356b
A
35
36extern const IORegistryPlane * gIOPowerPlane;
37
9bccf70c
A
38// debug trace function
39static inline void
40ioSPMTrace(unsigned int csc,
41 unsigned int a = 0, unsigned int b = 0,
42 unsigned int c = 0, unsigned int d = 0)
43{
44 if (gIOKitDebug & kIOLogTracePower)
45 IOTimeStampConstant(IODBG_POWER(csc), a, b, c, d);
46}
47
48IOReturn broadcast_aggressiveness ( OSObject *, void *, void *, void *, void * );
0b4e3aa0 49static void sleepTimerExpired(thread_call_param_t);
d52fe63f 50static void wakeupClamshellTimerExpired ( thread_call_param_t us);
1c79356b
A
51
52
0b4e3aa0 53#define number_of_power_states 5
1c79356b 54#define OFF_STATE 0
0b4e3aa0
A
55#define RESTART_STATE 1
56#define SLEEP_STATE 2
57#define DOZE_STATE 3
58#define ON_STATE 4
1c79356b 59
0b4e3aa0
A
60#define ON_POWER kIOPMPowerOn
61#define RESTART_POWER kIOPMRestart
62#define SLEEP_POWER kIOPMAuxPowerOn
63#define DOZE_POWER kIOPMDoze
1c79356b
A
64
65static IOPMPowerState ourPowerStates[number_of_power_states] = {
0b4e3aa0
A
66 {1,0, 0, 0,0,0,0,0,0,0,0,0}, // state 0, off
67 {1,kIOPMRestartCapability, kIOPMRestart, RESTART_POWER,0,0,0,0,0,0,0,0}, // state 1, restart
68 {1,kIOPMSleepCapability, kIOPMSleep, SLEEP_POWER,0,0,0,0,0,0,0,0}, // state 2, sleep
69 {1,kIOPMDoze, kIOPMDoze, DOZE_POWER,0,0,0,0,0,0,0,0}, // state 3, doze
70 {1,kIOPMPowerOn, kIOPMPowerOn, ON_POWER,0,0,0,0,0,0,0,0}, // state 4, on
1c79356b
A
71};
72
73static IOPMrootDomain * gRootDomain;
0b4e3aa0
A
74static UInt32 gSleepOrShutdownPending = 0;
75
1c79356b
A
76
77#define super IOService
78OSDefineMetaClassAndStructors(IOPMrootDomain,IOService)
79
80extern "C"
81{
82 IONotifier * registerSleepWakeInterest(IOServiceInterestHandler handler, void * self, void * ref = 0)
83 {
84 return gRootDomain->registerInterest( gIOGeneralInterest, handler, self, ref );
85 }
86
0b4e3aa0
A
87 IONotifier * registerPrioritySleepWakeInterest(IOServiceInterestHandler handler, void * self, void * ref = 0)
88 {
89 return gRootDomain->registerInterest( gIOPriorityPowerStateInterest, handler, self, ref );
90 }
91
1c79356b
A
92 IOReturn acknowledgeSleepWakeNotification(void * PMrefcon)
93 {
94 return gRootDomain->allowPowerChange ( (unsigned long)PMrefcon );
95 }
96
0b4e3aa0
A
97 IOReturn vetoSleepWakeNotification(void * PMrefcon)
98 {
99 return gRootDomain->cancelPowerChange ( (unsigned long)PMrefcon );
100 }
101
102 IOReturn rootDomainRestart ( void )
103 {
104 return gRootDomain->restartSystem();
105 }
106
107 IOReturn rootDomainShutdown ( void )
108 {
109 return gRootDomain->shutdownSystem();
110 }
111
112 void IOSystemShutdownNotification ( void )
113 {
114 for ( int i = 0; i < 100; i++ )
115 {
116 if ( OSCompareAndSwap( 0, 1, &gSleepOrShutdownPending ) ) break;
117 IOSleep( 100 );
118 }
119 }
120
121 int sync_internal(void);
1c79356b
A
122}
123
0b4e3aa0
A
124/*
125A device is always in the highest power state which satisfies its driver, its policy-maker, and any power domain
126children it has, but within the constraint of the power state provided by its parent. The driver expresses its desire by
127calling changePowerStateTo(), the policy-maker expresses its desire by calling changePowerStateToPriv(), and the children
128express their desires by calling requestPowerDomainState().
129
130The Root Power Domain owns the policy for idle and demand sleep and doze for the system. It is a power-managed IOService just
131like the others in the system. It implements several power states which correspond to what we see as Sleep, Doze, etc.
132
133The sleep/doze policy is as follows:
134Sleep and Doze are prevented if the case is open so that nobody will think the machine is off and plug/unplug cards.
135Sleep and Doze are prevented if the sleep timeout slider in the preferences panel is at zero.
136The system cannot Sleep, but can Doze if some object in the tree is in a power state marked kIOPMPreventSystemSleep.
137
138These three conditions are enforced using the "driver clamp" by calling changePowerStateTo(). For example, if the case is
139opened, changePowerStateTo(ON_STATE) is called to hold the system on regardless of the desires of the children of the root or
140the state of the other clamp.
141
142Demand Sleep/Doze is initiated by pressing the front panel power button, closing the clamshell, or selecting the menu item.
143In this case the root's parent actually initiates the power state change so that the root has no choice and does not give
144applications the opportunity to veto the change.
145
146Idle Sleep/Doze occurs if no objects in the tree are in a state marked kIOPMPreventIdleSleep. When this is true, the root's
147children are not holding the root on, so it sets the "policy-maker clamp" by calling changePowerStateToPriv(ON_STATE)
148to hold itself on until the sleep timer expires. This timer is set for the difference between the sleep timeout slider and
149the larger of the display dim timeout slider and the disk spindown timeout slider in the Preferences panel. For example, if
150the system is set to sleep after thirty idle minutes, and the display and disk are set to sleep after five idle minutes,
151when there is no longer an object in the tree holding the system out of Idle Sleep (via kIOPMPreventIdleSleep), the root
152sets its timer for 25 minutes (30 - 5). When the timer expires, it releases its clamp and now nothing is holding it awake,
153so it falls asleep.
154
155Demand sleep is prevented when the system is booting. When preferences are transmitted by the loginwindow at the end of
156boot, a flag is cleared, and this allows subsequent Demand Sleep.
157
158The 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
159a clamp, but sets a flag which is noticed before actually sleeping the kernel. If the flag is set, the root steps up
160one power state from Sleep to Doze, and any objects in the tree for which this is relevent will act appropriately (USB and
161ADB will turn on again so that they can wake the system out of Doze (keyboard/mouse activity will cause the Display Wrangler
162to be tickled)).
163*/
164
165
166// **********************************************************************************
167
168IOPMrootDomain * IOPMrootDomain::construct( void )
169{
170 IOPMrootDomain * root;
171
172 root = new IOPMrootDomain;
173 if( root)
174 root->init();
175
176 return( root );
177}
178
179// **********************************************************************************
180
181static void disk_sync_callout(thread_call_param_t p0, thread_call_param_t p1)
182{
183 IOService * rootDomain = (IOService *) p0;
184 unsigned long pmRef = (unsigned long) p1;
185
186 sync_internal();
187 rootDomain->allowPowerChange(pmRef);
188}
1c79356b
A
189
190// **********************************************************************************
191// start
192//
193// We don't do much here. The real initialization occurs when the platform
194// expert informs us we are the root.
195// **********************************************************************************
0b4e3aa0
A
196
197
1c79356b
A
198bool IOPMrootDomain::start ( IOService * nub )
199{
d52fe63f
A
200 OSDictionary *tmpDict;
201
1c79356b
A
202 super::start(nub);
203
204 gRootDomain = this;
205
206 PMinit();
0b4e3aa0 207 setProperty("IOSleepSupported","");
1c79356b 208 allowSleep = true;
0b4e3aa0 209 sleepIsSupported = true;
1c79356b 210 systemBooting = true;
0b4e3aa0
A
211 ignoringClamshell = true;
212 sleepSlider = 0;
213 idleSleepPending = false;
214 canSleep = true;
215 wrangler = NULL;
216 sleepASAP = false;
d52fe63f 217 ignoringClamshellDuringWakeup = false;
0b4e3aa0 218
d52fe63f
A
219 tmpDict = OSDictionary::withCapacity(1);
220 setProperty(kRootDomainSupportedFeatures, tmpDict);
221 tmpDict->release();
222
1c79356b 223 pm_vars->PMworkloop = IOWorkLoop::workLoop(); // make the workloop
0b4e3aa0 224 extraSleepTimer = thread_call_allocate((thread_call_func_t)sleepTimerExpired, (thread_call_param_t) this);
d52fe63f 225 clamshellWakeupIgnore = thread_call_allocate((thread_call_func_t)wakeupClamshellTimerExpired, (thread_call_param_t) this);
0b4e3aa0 226 diskSyncCalloutEntry = thread_call_allocate(&disk_sync_callout, (thread_call_param_t) this);
1c79356b
A
227
228 patriarch = new IORootParent; // create our parent
229 patriarch->init();
230 patriarch->attach(this);
231 patriarch->start(this);
232 patriarch->youAreRoot();
233 patriarch->wakeSystem();
234 patriarch->addPowerChild(this);
235
236 registerPowerDriver(this,ourPowerStates,number_of_power_states);
237
0b4e3aa0
A
238 setPMRootDomain(this);
239 changePowerStateToPriv(ON_STATE); // set a clamp until we sleep
240
241 registerPrioritySleepWakeInterest( &sysPowerDownHandler, this, 0); // install power change handler
242
243 // Register for a notification when IODisplayWrangler is published
244 addNotification( gIOPublishNotification, serviceMatching("IODisplayWrangler"), &displayWranglerPublished, this, 0);
1c79356b
A
245
246 registerService(); // let clients find us
247
248 return true;
249}
250
9bccf70c
A
251// **********************************************************************************
252// setProperties
253//
254// Receive a setProperty call
255// The "System Boot" property means the system is completely booted.
256// **********************************************************************************
257IOReturn IOPMrootDomain::setProperties ( OSObject *props_obj)
258{
259 OSDictionary *dict = OSDynamicCast(OSDictionary, props_obj);
260
261 if(!dict) return kIOReturnBadArgument;
262
263 if(dict->getObject(OSString::withCString("System Boot Complete"))) {
264 systemBooting = false;
265 kprintf("IOPM: received System Boot Complete property");
266 adjustPowerState();
267 }
268
269 return kIOReturnSuccess;
270}
271
1c79356b
A
272
273//*********************************************************************************
274// youAreRoot
275//
276// Power Managment is informing us that we are the root power domain.
277// We know we are not the root however, since we have just instantiated a parent
278// for ourselves and made it the root. We override this method so it will have
279// no effect
280//*********************************************************************************
281IOReturn IOPMrootDomain::youAreRoot ( void )
282{
283 return IOPMNoErr;
284}
285
1c79356b
A
286// **********************************************************************************
287// command_received
288//
9bccf70c 289// No longer used
1c79356b 290// **********************************************************************************
9bccf70c 291void IOPMrootDomain::command_received ( void * w, void * x, void * y, void * z )
1c79356b 292{
9bccf70c
A
293 super::command_received(w,x,y,z);
294}
0b4e3aa0 295
0b4e3aa0 296
9bccf70c
A
297// **********************************************************************************
298// broadcast_aggressiveness
299//
300// **********************************************************************************
301IOReturn broadcast_aggressiveness ( OSObject * root, void * x, void * y, void *, void * )
302{
303 ((IOPMrootDomain *)root)->broadcast_it((unsigned long)x,(unsigned long)y);
304 return IOPMNoErr;
305}
0b4e3aa0
A
306
307
9bccf70c
A
308// **********************************************************************************
309// broadcast_it
310//
311// We are behind the command gate to broadcast an aggressiveness factor. We let the
312// superclass do it, but we need to snoop on factors that affect idle sleep.
313// **********************************************************************************
314void IOPMrootDomain::broadcast_it (unsigned long type, unsigned long value)
315{
316 super::setAggressiveness(type,value);
317
318 // Save user's spin down timer to restore after we replace it for idle sleep
319 if( type == kPMMinutesToSpinDown ) user_spindown = value;
320
321 // Use longestNonSleepSlider to calculate dimming adjust idle sleep timer
322 longestNonSleepSlider = pm_vars->current_aggressiveness_values[kPMMinutesToDim];
323
324
325 if ( type == kPMMinutesToSleep ) {
326 if ( (sleepSlider == 0) && (value != 0) ) {
327 sleepSlider = value;
328 adjustPowerState(); // idle sleep is now enabled, maybe sleep now
329 }
330 sleepSlider = value;
331 if ( sleepSlider == 0 ) {
332 adjustPowerState(); // idle sleep is now disabled
333 patriarch->wakeSystem(); // make sure we're powered
334 }
335 }
336 if ( sleepSlider > longestNonSleepSlider ) {
337 extraSleepDelay = sleepSlider - longestNonSleepSlider ;
338 }
339 else {
340 extraSleepDelay = 0;
1c79356b
A
341 }
342}
343
344
0b4e3aa0
A
345// **********************************************************************************
346// sleepTimerExpired
347//
348// **********************************************************************************
349static void sleepTimerExpired ( thread_call_param_t us)
350{
351 ((IOPMrootDomain *)us)->handleSleepTimerExpiration();
352 }
353
d52fe63f
A
354
355static void wakeupClamshellTimerExpired ( thread_call_param_t us)
356{
357 ((IOPMrootDomain *)us)->stopIgnoringClamshellEventsDuringWakeup();
358}
359
0b4e3aa0
A
360
361// **********************************************************************************
362// handleSleepTimerExpiration
363//
364// The time between the sleep idle timeout and the next longest one has elapsed.
365// It's time to sleep. Start that by removing the clamp that's holding us awake.
366// **********************************************************************************
367void IOPMrootDomain::handleSleepTimerExpiration ( void )
368{
369 // accelerate disk spin down if spin down timer is non-zero (zero = never spin down)
370 if(0 != user_spindown)
371 setQuickSpinDownTimeout();
372
373 sleepASAP = true;
374 adjustPowerState();
375}
376
377
d52fe63f
A
378void IOPMrootDomain::stopIgnoringClamshellEventsDuringWakeup(void)
379{
380 OSObject * state;
381
382 // Allow clamshell-induced sleep now
383 ignoringClamshellDuringWakeup = false;
384
385 if ((state = getProperty(kAppleClamshellStateKey)))
386 publishResource(kAppleClamshellStateKey, state);
387}
388
1c79356b
A
389//*********************************************************************************
390// setAggressiveness
391//
9bccf70c 392// Some aggressiveness factor has changed. We broadcast it to the hierarchy while on
1c79356b
A
393// the Power Mangement workloop thread. This enables objects in the
394// hierarchy to successfully alter their idle timers, which are all on the
395// same thread.
396//*********************************************************************************
397
398IOReturn IOPMrootDomain::setAggressiveness ( unsigned long type, unsigned long newLevel )
399{
9bccf70c
A
400 if ( pm_vars->PMcommandGate ) {
401 pm_vars->PMcommandGate->runAction(broadcast_aggressiveness,(void *)type,(void *)newLevel);
0b4e3aa0 402 }
1c79356b
A
403
404 return kIOReturnSuccess;
405}
406
407
408// **********************************************************************************
409// sleepSystem
410//
411// **********************************************************************************
412IOReturn IOPMrootDomain::sleepSystem ( void )
413{
414 kprintf("sleep demand received\n");
415 if ( !systemBooting && allowSleep && sleepIsSupported ) {
416 patriarch->sleepSystem();
0b4e3aa0 417 return kIOReturnSuccess;
1c79356b 418 }
0b4e3aa0
A
419 if ( !systemBooting && allowSleep && !sleepIsSupported ) {
420 patriarch->dozeSystem();
421 return kIOReturnSuccess;
422 }
423 return kIOReturnSuccess;
424}
425
426
427// **********************************************************************************
428// shutdownSystem
429//
430// **********************************************************************************
431IOReturn IOPMrootDomain::shutdownSystem ( void )
432{
433 patriarch->shutDownSystem();
434 return kIOReturnSuccess;
435}
436
437
438// **********************************************************************************
439// restartSystem
440//
441// **********************************************************************************
442IOReturn IOPMrootDomain::restartSystem ( void )
443{
444 patriarch->restartSystem();
1c79356b
A
445 return kIOReturnSuccess;
446}
447
448
449// **********************************************************************************
450// powerChangeDone
451//
452// This overrides powerChangeDone in IOService.
0b4e3aa0
A
453//
454// Finder sleep and idle sleep move us from the ON state to the SLEEP_STATE.
455// In this case:
456// If we just finished going to the SLEEP_STATE, and the platform is capable of true sleep,
457// sleep the kernel. Otherwise switch up to the DOZE_STATE which will keep almost
458// everything as off as it can get.
459//
460// **********************************************************************************
461void IOPMrootDomain::powerChangeDone ( unsigned long previousState )
462{
463 OSNumber * propertyPtr;
464 unsigned short theProperty;
465 AbsoluteTime deadline;
466
467 switch ( pm_vars->myCurrentState ) {
468 case SLEEP_STATE:
469 if ( canSleep && sleepIsSupported ) {
470 idleSleepPending = false; // re-enable this timer for next sleep
471 IOLog("System Sleep\n");
472 pm_vars->thePlatform->sleepKernel(); // sleep now
9bccf70c
A
473
474 ioSPMTrace(IOPOWER_WAKE, * (int *) this); // now we're waking
475
0b4e3aa0
A
476 clock_interval_to_deadline(30, kSecondScale, &deadline); // stay awake for at least 30 seconds
477 thread_call_enter_delayed(extraSleepTimer, deadline);
478 idleSleepPending = true; // this gets turned off when we sleep again
d52fe63f
A
479
480 // Ignore closed clamshell during wakeup and for a few seconds
481 // after wakeup is complete
482 ignoringClamshellDuringWakeup = true;
483
0b4e3aa0
A
484 gSleepOrShutdownPending = 0; // sleep transition complete
485 patriarch->wakeSystem(); // get us some power
486
487 IOLog("System Wake\n");
488 systemWake(); // tell the tree we're waking
d52fe63f
A
489
490 // Allow drivers to request extra processing time before clamshell
491 // sleep if kIOREMSleepEnabledKey is present.
492 // Ignore clamshell events for at least 5 seconds
493 if(getProperty(kIOREMSleepEnabledKey)) {
494 // clamshellWakeupIgnore callout clears ignoreClamshellDuringWakeup bit
495 clock_interval_to_deadline(5, kSecondScale, &deadline);
496 if(clamshellWakeupIgnore) thread_call_enter_delayed(clamshellWakeupIgnore, deadline);
497 } else ignoringClamshellDuringWakeup = false;
498
0b4e3aa0
A
499 propertyPtr = OSDynamicCast(OSNumber,getProperty("WakeEvent"));
500 if ( propertyPtr ) { // find out what woke us
501 theProperty = propertyPtr->unsigned16BitValue();
502 IOLog("Wake event %04x\n",theProperty);
503 if ( (theProperty == 0x0008) || //lid
504 (theProperty == 0x0800) || // front panel button
505 (theProperty == 0x0020) || // external keyboard
506 (theProperty == 0x0001) ) { // internal keyboard
507 reportUserInput();
508 }
509 }
510 else {
511 IOLog("Unknown wake event\n");
512 reportUserInput(); // don't know, call it user input then
513 }
514
515 changePowerStateToPriv(ON_STATE); // wake for thirty seconds
516 powerOverrideOffPriv();
517 }
518 else {
519 patriarch->sleepToDoze(); // allow us to step up a power state
520 changePowerStateToPriv(DOZE_STATE); // and do it
521 }
522 break;
523
524 case DOZE_STATE:
525 if ( previousState != DOZE_STATE ) {
526 IOLog("System Doze\n");
527 }
528 idleSleepPending = false; // re-enable this timer for next sleep
529 gSleepOrShutdownPending = 0;
530 break;
531
532 case RESTART_STATE:
533 IOLog("System Restart\n");
534 PEHaltRestart(kPERestartCPU);
535 break;
536
537 case OFF_STATE:
538 IOLog("System Halt\n");
539 PEHaltRestart(kPEHaltCPU);
540 break;
541 }
542}
543
544
545// **********************************************************************************
546// wakeFromDoze
547//
548// The Display Wrangler calls here when it switches to its highest state. If the
549// system is currently dozing, allow it to wake by making sure the parent is
550// providing power.
1c79356b 551// **********************************************************************************
0b4e3aa0 552void IOPMrootDomain::wakeFromDoze( void )
1c79356b 553{
0b4e3aa0
A
554 if ( pm_vars->myCurrentState == DOZE_STATE ) {
555 canSleep = true; // reset this till next attempt
556 powerOverrideOffPriv();
557 patriarch->wakeSystem(); // allow us to wake if children so desire
1c79356b
A
558 }
559}
560
561
d52fe63f
A
562// **********************************************************************************
563// publishFeature
564//
565// Adds a new feature to the supported features dictionary
566//
567//
568// **********************************************************************************
569void IOPMrootDomain::publishFeature( const char * feature )
570{
571 OSDictionary *features = (OSDictionary *)getProperty(kRootDomainSupportedFeatures);
572
573 features->setObject(feature, kOSBooleanTrue);
574}
575
576
1c79356b
A
577// **********************************************************************************
578// newUserClient
579//
580// **********************************************************************************
581IOReturn IOPMrootDomain::newUserClient( task_t owningTask, void * /* security_id */, UInt32 type, IOUserClient ** handler )
582{
583 IOReturn err = kIOReturnSuccess;
584 RootDomainUserClient * client;
585
586 client = RootDomainUserClient::withTask(owningTask);
587
588 if( !client || (false == client->attach( this )) ||
589 (false == client->start( this )) ) {
590 if(client) {
591 client->detach( this );
592 client->release();
593 client = NULL;
594 }
595 err = kIOReturnNoMemory;
596 }
597 *handler = client;
598 return err;
599}
600
601//*********************************************************************************
602// receivePowerNotification
603//
604// The power controller is notifying us of a hardware-related power management
605// event that we must handle. This is a result of an 'environment' interrupt from
606// the power mgt micro.
607//*********************************************************************************
608
609IOReturn IOPMrootDomain::receivePowerNotification (UInt32 msg)
610{
9bccf70c
A
611 if (msg & kIOPMOverTemp) {
612 IOLog("Power Management received emergency overtemp signal. Going to sleep.");
613 (void) sleepSystem ();
614 }
d52fe63f
A
615 if (msg & kIOPMSetDesktopMode) {
616 desktopMode = (0 != (msg & kIOPMSetValue));
617 msg &= ~(kIOPMSetDesktopMode | kIOPMSetValue);
618 }
619 if (msg & kIOPMSetACAdaptorConnected) {
620 acAdaptorConnect = (0 != (msg & kIOPMSetValue));
621 msg &= ~(kIOPMSetACAdaptorConnected | kIOPMSetValue);
622 }
623 if (msg & kIOPMEnableClamshell) {
624 ignoringClamshell = false;
625 }
626 if (msg & kIOPMDisableClamshell) {
627 ignoringClamshell = true;
628 }
629
630 if (msg & kIOPMProcessorSpeedChange) {
631 IOService *pmu = waitForService(serviceMatching("ApplePMU"));
632 pmu->callPlatformFunction("prepareForSleep", false, 0, 0, 0, 0);
633 pm_vars->thePlatform->sleepKernel();
634 pmu->callPlatformFunction("recoverFromSleep", false, 0, 0, 0, 0);
635 }
636
1c79356b
A
637 if (msg & kIOPMSleepNow) {
638 (void) sleepSystem ();
639 }
640
1c79356b
A
641 if (msg & kIOPMPowerEmergency) {
642 (void) sleepSystem ();
643 }
644
d52fe63f
A
645 if (msg & kIOPMClamshellClosed) {
646 if ( !ignoringClamshell && !ignoringClamshellDuringWakeup
647 && (!desktopMode || !acAdaptorConnect) ) {
648
649 (void) sleepSystem ();
650 }
1c79356b
A
651 }
652
0b4e3aa0
A
653 if (msg & kIOPMPowerButton) { // toggle state of sleep/wake
654 if ( pm_vars->myCurrentState == DOZE_STATE ) { // are we dozing?
655 systemWake(); // yes, tell the tree we're waking
656 reportUserInput(); // wake the Display Wrangler
657 }
658 else {
659 (void) sleepSystem ();
1c79356b 660 }
0b4e3aa0
A
661 }
662
663 // if the case has been closed, we allow
664 // the machine to be put to sleep or to idle sleep
665
666 if ( (msg & kIOPMAllowSleep) && !allowSleep ) {
1c79356b 667 allowSleep = true;
0b4e3aa0 668 adjustPowerState();
1c79356b
A
669 }
670
0b4e3aa0 671 // if the case has been opened, we disallow sleep/doze
1c79356b
A
672
673 if (msg & kIOPMPreventSleep) {
1c79356b 674 allowSleep = false;
0b4e3aa0
A
675 if ( pm_vars->myCurrentState == DOZE_STATE ) { // are we dozing?
676 systemWake(); // yes, tell the tree we're waking
677 adjustPowerState();
678 reportUserInput(); // wake the Display Wrangler
679 }
680 else {
681 adjustPowerState();
682 patriarch->wakeSystem(); // make sure we have power to clamp
683 }
1c79356b
A
684 }
685
686 return 0;
687}
688
689
690//*********************************************************************************
691// sleepSupported
692//
693//*********************************************************************************
694
695void IOPMrootDomain::setSleepSupported( IOOptionBits flags )
696{
0b4e3aa0
A
697 if ( flags & kPCICantSleep ) {
698 canSleep = false;
1c79356b 699 }
0b4e3aa0
A
700 else {
701 platformSleepSupport = flags;
702 }
703
704}
705
706//*********************************************************************************
707// requestPowerDomainState
708//
709// The root domain intercepts this call to the superclass.
710//
711// If the clamp bit is not set in the desire, then the child doesn't need the power
712// state it's requesting; it just wants it. The root ignores desires but not needs.
713// If the clamp bit is not set, the root takes it that the child can tolerate no
714// power and interprets the request accordingly. If all children can thus tolerate
715// no power, we are on our way to idle sleep.
716//*********************************************************************************
717
718IOReturn IOPMrootDomain::requestPowerDomainState ( IOPMPowerFlags desiredState, IOPowerConnection * whichChild, unsigned long specification )
719{
720 OSIterator * iter;
721 OSObject * next;
722 IOPowerConnection * connection;
723 unsigned long powerRequestFlag = 0;
724 IOPMPowerFlags editedDesire = desiredState;
725
726 if ( !(desiredState & kIOPMPreventIdleSleep) ) { // if they don't really need it, they don't get it
727 editedDesire = 0;
728 }
729
730
731 IOLockLock(pm_vars->childLock); // recompute sleepIsSupported
732 // and see if all children are asleep
733 iter = getChildIterator(gIOPowerPlane);
734 sleepIsSupported = true;
735
736 if ( iter ) {
737 while ( (next = iter->getNextObject()) ) {
738 if ( (connection = OSDynamicCast(IOPowerConnection,next)) ) {
739 if ( connection == whichChild ) {
740 powerRequestFlag += editedDesire;
741 if ( desiredState & kIOPMPreventSystemSleep ) {
742 sleepIsSupported = false;
743 }
744 }
745 else {
746 powerRequestFlag += connection->getDesiredDomainState();
747 if ( connection->getPreventSystemSleepFlag() ) {
748 sleepIsSupported = false;
749 }
750 }
751 }
752 }
753 iter->release();
754 }
9bccf70c 755
0b4e3aa0
A
756 if ( (extraSleepDelay == 0) && (powerRequestFlag == 0) ) {
757 sleepASAP = true;
1c79356b 758 }
0b4e3aa0
A
759
760 adjustPowerState(); // this may put the system to sleep
761
762 IOLockUnlock(pm_vars->childLock);
763
764 editedDesire |= desiredState & kIOPMPreventSystemSleep;
1c79356b 765
0b4e3aa0 766 return super::requestPowerDomainState(editedDesire,whichChild,specification);
1c79356b
A
767}
768
0b4e3aa0 769
1c79356b
A
770//*********************************************************************************
771// getSleepSupported
772//
773//*********************************************************************************
774
775IOOptionBits IOPMrootDomain::getSleepSupported( void )
776{
777 return( platformSleepSupport );
778}
779
780
781//*********************************************************************************
782// tellChangeDown
783//
784// We override the superclass implementation so we can send a different message
785// type to the client or application being notified.
786//*********************************************************************************
787
788bool IOPMrootDomain::tellChangeDown ( unsigned long stateNum )
789{
0b4e3aa0
A
790 switch ( stateNum ) {
791 case DOZE_STATE:
792 case SLEEP_STATE:
793 return super::tellClientsWithResponse(kIOMessageSystemWillSleep);
794 case RESTART_STATE:
795 return super::tellClientsWithResponse(kIOMessageSystemWillRestart);
796 case OFF_STATE:
797 return super::tellClientsWithResponse(kIOMessageSystemWillPowerOff);
1c79356b 798 }
0b4e3aa0 799 return super::tellChangeDown(stateNum); // this shouldn't execute
1c79356b
A
800}
801
802
803//*********************************************************************************
804// askChangeDown
805//
806// We override the superclass implementation so we can send a different message
807// type to the client or application being notified.
0b4e3aa0
A
808//
809// This must be idle sleep since we don't ask apps during any other power change.
1c79356b
A
810//*********************************************************************************
811
0b4e3aa0 812bool IOPMrootDomain::askChangeDown ( unsigned long )
1c79356b 813{
0b4e3aa0 814 return super::tellClientsWithResponse(kIOMessageCanSystemSleep);
1c79356b
A
815}
816
817
818//*********************************************************************************
819// tellNoChangeDown
820//
821// Notify registered applications and kernel clients that we are not
822// dropping power.
823//
824// We override the superclass implementation so we can send a different message
825// type to the client or application being notified.
0b4e3aa0
A
826//
827// This must be a vetoed idle sleep, since no other power change can be vetoed.
1c79356b
A
828//*********************************************************************************
829
830void IOPMrootDomain::tellNoChangeDown ( unsigned long )
831{
832 return tellClients(kIOMessageSystemWillNotSleep);
833}
834
835
836//*********************************************************************************
837// tellChangeUp
838//
839// Notify registered applications and kernel clients that we are raising power.
840//
841// We override the superclass implementation so we can send a different message
842// type to the client or application being notified.
843//*********************************************************************************
844
7b1edb79 845void IOPMrootDomain::tellChangeUp ( unsigned long stateNum)
1c79356b 846{
7b1edb79
A
847 if ( stateNum == ON_STATE ) {
848 return tellClients(kIOMessageSystemHasPoweredOn);
849 }
1c79356b
A
850}
851
0b4e3aa0
A
852//*********************************************************************************
853// reportUserInput
854//
855//*********************************************************************************
856
857void IOPMrootDomain::reportUserInput ( void )
858{
859 OSIterator * iter;
1c79356b 860
0b4e3aa0
A
861 if(!wrangler) {
862 iter = getMatchingServices(serviceMatching("IODisplayWrangler"));
863 if(iter) {
864 wrangler = (IOService *) iter->getNextObject();
865 iter->release();
866 }
867 }
868
869 if(wrangler)
870 wrangler->activityTickle(0,0);
871}
872
873//*********************************************************************************
874// setQuickSpinDownTimeout
1c79356b 875//
0b4e3aa0
A
876//*********************************************************************************
877
878void IOPMrootDomain::setQuickSpinDownTimeout ( void )
879{
880 //IOLog("setQuickSpinDownTimeout\n");
881 super::setAggressiveness((unsigned long)kPMMinutesToSpinDown,(unsigned long)1);
882}
883
884//*********************************************************************************
885// restoreUserSpinDownTimeout
886//
887//*********************************************************************************
888
889void IOPMrootDomain::restoreUserSpinDownTimeout ( void )
890{
0b4e3aa0
A
891 super::setAggressiveness((unsigned long)kPMMinutesToSpinDown,(unsigned long)user_spindown);
892}
893
9bccf70c
A
894//*********************************************************************************
895// changePowerStateTo & changePowerStateToPriv
896//
897// Override of these methods for logging purposes.
898//*********************************************************************************
899
900IOReturn IOPMrootDomain::changePowerStateTo ( unsigned long ordinal )
901{
902 ioSPMTrace(IOPOWER_ROOT, * (int *) this, (int) true, (int) ordinal);
903
904 return super::changePowerStateTo(ordinal);
905}
906
907IOReturn IOPMrootDomain::changePowerStateToPriv ( unsigned long ordinal )
908{
909 ioSPMTrace(IOPOWER_ROOT, * (int *) this, (int) false, (int) ordinal);
910
911 return super::changePowerStateToPriv(ordinal);
912}
913
0b4e3aa0
A
914
915//*********************************************************************************
916// sysPowerDownHandler
917//
918// Receives a notification when the RootDomain changes state.
919//
920// Allows us to take action on system sleep, power down, and restart after
921// applications have received their power change notifications and replied,
922// but before drivers have powered down. We perform a vfs sync on power down.
923//*********************************************************************************
924
925IOReturn IOPMrootDomain::sysPowerDownHandler( void * target, void * refCon,
926 UInt32 messageType, IOService * service,
927 void * messageArgument, vm_size_t argSize )
928{
929 IOReturn ret;
930 IOPowerStateChangeNotification * params = (IOPowerStateChangeNotification *) messageArgument;
931 IOPMrootDomain * rootDomain = OSDynamicCast(IOPMrootDomain, service);
932
933 if(!rootDomain)
934 return kIOReturnUnsupported;
935
936 switch (messageType) {
937 case kIOMessageSystemWillSleep:
938 rootDomain->powerOverrideOnPriv(); // start ignoring children's requests
939 // (fall through to other cases)
0b4e3aa0
A
940
941 // Interested applications have been notified of an impending power
942 // change and have acked (when applicable).
943 // This is our chance to save whatever state we can before powering
944 // down.
945 // We call sync_internal defined in xnu/bsd/vfs/vfs_syscalls.c,
946 // via callout
947
948 // We will ack within 20 seconds
949 params->returnValue = 20 * 1000 * 1000;
950
951 if ( ! OSCompareAndSwap( 0, 1, &gSleepOrShutdownPending ) )
952 {
953 // Purposely delay the ack and hope that shutdown occurs quickly.
954 // Another option is not to schedule the thread and wait for
955 // ack timeout...
956 AbsoluteTime deadline;
9bccf70c 957 clock_interval_to_deadline( 30, kSecondScale, &deadline );
0b4e3aa0
A
958 thread_call_enter1_delayed( rootDomain->diskSyncCalloutEntry,
959 (thread_call_param_t)params->powerRef,
960 deadline );
961 }
962 else
963 thread_call_enter1(rootDomain->diskSyncCalloutEntry, (thread_call_param_t)params->powerRef);
964 ret = kIOReturnSuccess;
965 break;
fa4905b1
A
966
967 case kIOMessageSystemWillPowerOff:
968 case kIOMessageSystemWillRestart:
d52fe63f 969 ret = kIOReturnUnsupported;
fa4905b1
A
970 break;
971
0b4e3aa0
A
972 default:
973 ret = kIOReturnUnsupported;
974 break;
975 }
976 return ret;
977}
978
979//*********************************************************************************
980// displayWranglerNotification
981//
982// Receives a notification when the IODisplayWrangler changes state.
983//
984// Allows us to take action on display dim/undim.
985//
986// When the display goes dim we:
987// - Start the idle sleep timer
988// - set the quick spin down timeout
989//
990// On wake from display dim:
991// - Cancel the idle sleep timer
992// - restore the user's chosen spindown timer from the "quick" spin down value
993//*********************************************************************************
1c79356b 994
0b4e3aa0
A
995IOReturn IOPMrootDomain::displayWranglerNotification( void * target, void * refCon,
996 UInt32 messageType, IOService * service,
997 void * messageArgument, vm_size_t argSize )
1c79356b 998{
0b4e3aa0
A
999 IOPMrootDomain * rootDomain = OSDynamicCast(IOPMrootDomain, (IOService *)target);
1000 AbsoluteTime deadline;
1001 static bool deviceAlreadyPoweredOff = false;
1002
1003 if(!rootDomain)
1004 return kIOReturnUnsupported;
1005
1006 switch (messageType) {
1007 case kIOMessageDeviceWillPowerOff:
1008 // The IODisplayWrangler has powered off either because of idle display sleep
1009 // or force system sleep.
1010
1011 // The display wrangler will send the DeviceWillPowerOff message 4 times until
1012 // it gets into its lowest state. We only want to act on the first of those 4.
1013 if( deviceAlreadyPoweredOff ) return kIOReturnUnsupported;
1014
1015 deviceAlreadyPoweredOff = true;
1016
1017 if( rootDomain->extraSleepDelay ) {
1018
1019 // start the extra sleep timer
1020 clock_interval_to_deadline(rootDomain->extraSleepDelay*60, kSecondScale, &deadline );
1021 thread_call_enter_delayed(rootDomain->extraSleepTimer, deadline);
1022 rootDomain->idleSleepPending = true;
1023
1024 } else {
1025
1026 // accelerate disk spin down if spin down timer is non-zero (zero = never spin down)
9bccf70c
A
1027 // and if system sleep is non-Never
1028 if( (0 != rootDomain->user_spindown) && (0 != rootDomain->sleepSlider) )
0b4e3aa0
A
1029 rootDomain->setQuickSpinDownTimeout();
1030 }
1031
1032 break;
1033
1034 case kIOMessageDeviceHasPoweredOn:
1035
1036 // The display has powered on either because of UI activity or wake from sleep/doze
1037 deviceAlreadyPoweredOff = false;
1038 rootDomain->adjustPowerState();
1039
1040
1041 // cancel any pending idle sleep
1042 if(rootDomain->idleSleepPending) {
1043 thread_call_cancel(rootDomain->extraSleepTimer);
1044 rootDomain->idleSleepPending = false;
1045 }
1046
1047 // Change the spindown value back to the user's selection from our accelerated setting
1048 if(0 != rootDomain->user_spindown)
1049 rootDomain->restoreUserSpinDownTimeout();
1050
1051 // Put on the policy maker's on clamp.
1052
1053 break;
1054
1055 default:
1056 break;
1057 }
1058 return kIOReturnUnsupported;
1059 }
1060
1061//*********************************************************************************
1062// displayWranglerPublished
1063//
1064// Receives a notification when the IODisplayWrangler is published.
1065// When it's published we install a power state change handler.
1066//
1067//*********************************************************************************
1068
1069bool IOPMrootDomain::displayWranglerPublished( void * target, void * refCon,
1070 IOService * newService)
1071{
1072 IOPMrootDomain * rootDomain = OSDynamicCast(IOPMrootDomain, (IOService *)target);
1073
1074 if(!rootDomain)
1075 return false;
1076
1077 rootDomain->wrangler = newService;
1078
1079 // we found the display wrangler, now install a handler
1080 if( !rootDomain->wrangler->registerInterest( gIOGeneralInterest, &displayWranglerNotification, target, 0) ) {
1081 IOLog("IOPMrootDomain::displayWranglerPublished registerInterest failed\n");
1082 return false;
1083 }
1084
1085 return true;
1c79356b
A
1086}
1087
1088
0b4e3aa0
A
1089//*********************************************************************************
1090// adjustPowerState
1091//
1092// Some condition that affects our wake/sleep/doze decision has changed.
1093//
1094// If the sleep slider is in the off position, we cannot sleep or doze.
1095// If the enclosure is open, we cannot sleep or doze.
1096// If the system is still booting, we cannot sleep or doze.
1097//
1098// In those circumstances, we prevent sleep and doze by holding power on with
1099// changePowerStateToPriv(ON).
1100//
1101// If the above conditions do not exist, and also the sleep timer has expired, we
1102// allow sleep or doze to occur with either changePowerStateToPriv(SLEEP) or
1103// changePowerStateToPriv(DOZE) depending on whether or not we already know the
1104// platform cannot sleep.
1105//
1106// In this case, sleep or doze will either occur immediately or at the next time
1107// that no children are holding the system out of idle sleep via the
1108// kIOPMPreventIdleSleep flag in their power state arrays.
1109//*********************************************************************************
1110
1111void IOPMrootDomain::adjustPowerState( void )
1112{
1113 if ( (sleepSlider == 0) ||
1114 ! allowSleep ||
1115 systemBooting ) {
1116 changePowerStateToPriv(ON_STATE);
1117 }
1118 else {
1119 if ( sleepASAP ) {
1120 sleepASAP = false;
1121 if ( sleepIsSupported ) {
1122 changePowerStateToPriv(SLEEP_STATE);
1123 }
1124 else {
1125 changePowerStateToPriv(DOZE_STATE);
1126 }
1127 }
1128 }
1129}
1c79356b
A
1130
1131
1132/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1133
1134#undef super
1135#define super IOService
1136
1137OSDefineMetaClassAndStructors(IORootParent, IOService)
1138
0b4e3aa0
A
1139// This array exactly parallels the state array for the root domain.
1140// Power state changes initiated by a device can be vetoed by a client of the device, and
1141// power state changes initiated by the parent of a device cannot be vetoed by a client of the device,
1142// so when the root domain wants a power state change that cannot be vetoed (e.g. demand sleep), it asks
1143// its parent to make the change. That is the reason for this complexity.
1c79356b 1144
0b4e3aa0 1145static IOPMPowerState patriarchPowerStates[number_of_power_states] = {
1c79356b 1146 {1,0,0,0,0,0,0,0,0,0,0,0}, // off
0b4e3aa0 1147 {1,0,RESTART_POWER,0,0,0,0,0,0,0,0,0}, // reset
1c79356b 1148 {1,0,SLEEP_POWER,0,0,0,0,0,0,0,0,0}, // sleep
0b4e3aa0 1149 {1,0,DOZE_POWER,0,0,0,0,0,0,0,0,0}, // doze
1c79356b
A
1150 {1,0,ON_POWER,0,0,0,0,0,0,0,0,0} // running
1151};
1152
1c79356b
A
1153bool IORootParent::start ( IOService * nub )
1154{
0b4e3aa0 1155 mostRecentChange = ON_STATE;
1c79356b
A
1156 super::start(nub);
1157 PMinit();
0b4e3aa0 1158 registerPowerDriver(this,patriarchPowerStates,number_of_power_states);
1c79356b
A
1159 powerOverrideOnPriv();
1160 return true;
1161}
1162
1163
1164void IORootParent::shutDownSystem ( void )
1165{
0b4e3aa0
A
1166 mostRecentChange = OFF_STATE;
1167 changePowerStateToPriv(OFF_STATE);
1168}
1169
1170
1171void IORootParent::restartSystem ( void )
1172{
1173 mostRecentChange = RESTART_STATE;
1174 changePowerStateToPriv(RESTART_STATE);
1c79356b
A
1175}
1176
1177
1178void IORootParent::sleepSystem ( void )
1179{
0b4e3aa0
A
1180 mostRecentChange = SLEEP_STATE;
1181 changePowerStateToPriv(SLEEP_STATE);
1182}
1183
1184
1185void IORootParent::dozeSystem ( void )
1186{
1187 mostRecentChange = DOZE_STATE;
1188 changePowerStateToPriv(DOZE_STATE);
1189}
1190
1191// Called in demand sleep when sleep discovered to be impossible after actually attaining that state.
1192// This brings the parent to doze, which allows the root to step up from sleep to doze.
1193
1194// In idle sleep, do nothing because the parent is still on and the root can freely change state.
1195
1196void IORootParent::sleepToDoze ( void )
1197{
1198 if ( mostRecentChange == SLEEP_STATE ) {
1199 changePowerStateToPriv(DOZE_STATE);
1200 }
1c79356b
A
1201}
1202
1203
1204void IORootParent::wakeSystem ( void )
1205{
0b4e3aa0
A
1206 mostRecentChange = ON_STATE;
1207 changePowerStateToPriv(ON_STATE);
1c79356b
A
1208}
1209