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