]> git.saurik.com Git - apple/xnu.git/blame - iokit/Kernel/IOPMrootDomain.cpp
xnu-1228.7.58.tar.gz
[apple/xnu.git] / iokit / Kernel / IOPMrootDomain.cpp
CommitLineData
91447636 1/*
0c530ab8 2 * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved.
1c79356b 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
2d21ac55
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
8f6c56a5 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
27 */
28#include <IOKit/IOWorkLoop.h>
9bccf70c 29#include <IOKit/IOCommandGate.h>
1c79356b
A
30#include <IOKit/IOTimerEventSource.h>
31#include <IOKit/IOPlatformExpert.h>
9bccf70c
A
32#include <IOKit/IOKitDebug.h>
33#include <IOKit/IOTimeStamp.h>
1c79356b 34#include <IOKit/pwr_mgt/RootDomain.h>
d52fe63f 35#include <IOKit/pwr_mgt/IOPMPrivate.h>
91447636 36#include <IOKit/IODeviceTreeSupport.h>
1c79356b 37#include <IOKit/IOMessage.h>
0c530ab8 38#include <IOKit/IOReturn.h>
1c79356b 39#include "RootDomainUserClient.h"
0b4e3aa0 40#include "IOKit/pwr_mgt/IOPowerConnection.h"
55e303ae 41#include "IOPMPowerStateQueue.h"
91447636 42#include <IOKit/IOCatalogue.h>
2d21ac55 43#if HIBERNATION
3a60a9f5 44#include <IOKit/IOHibernatePrivate.h>
2d21ac55
A
45#endif
46#include <sys/syslog.h>
47#include <sys/sysctl.h>
48#include <sys/time.h>
49#include "IOServicePrivate.h" // _IOServiceInterestNotifier
50
51
52#if __i386__
53__BEGIN_DECLS
54#include "IOPMrootDomainInternal.h"
55__END_DECLS
56#endif
57
91447636 58
2d21ac55
A
59//#define DEBUG 1
60#if DEBUG
61#define DEBUG_LOG(x...) do { kprintf(x); } while (0)
62#else
63#define DEBUG_LOG(x...)
3a60a9f5 64#endif
2d21ac55 65#define HaltRestartLog(x...) do { kprintf(x); } while (0)
1c79356b 66
0c530ab8
A
67extern "C" {
68IOReturn OSMetaClassSystemSleepOrWake( UInt32 );
69}
1c79356b
A
70
71extern const IORegistryPlane * gIOPowerPlane;
72
9bccf70c 73IOReturn broadcast_aggressiveness ( OSObject *, void *, void *, void *, void * );
0b4e3aa0 74static void sleepTimerExpired(thread_call_param_t);
d52fe63f 75static void wakeupClamshellTimerExpired ( thread_call_param_t us);
2d21ac55 76static void notifySystemShutdown( IOService * root, unsigned long event );
1c79356b 77
0c530ab8
A
78// "IOPMSetSleepSupported" callPlatformFunction name
79static const OSSymbol *sleepSupportedPEFunction = NULL;
80
81#define kIOSleepSupportedKey "IOSleepSupported"
82
83#define kRD_AllPowerSources (kIOPMSupportedOnAC \
84 | kIOPMSupportedOnBatt \
85 | kIOPMSupportedOnUPS)
1c79356b 86
0b4e3aa0 87#define number_of_power_states 5
1c79356b 88#define OFF_STATE 0
0b4e3aa0
A
89#define RESTART_STATE 1
90#define SLEEP_STATE 2
91#define DOZE_STATE 3
92#define ON_STATE 4
1c79356b 93
0b4e3aa0
A
94#define ON_POWER kIOPMPowerOn
95#define RESTART_POWER kIOPMRestart
96#define SLEEP_POWER kIOPMAuxPowerOn
97#define DOZE_POWER kIOPMDoze
1c79356b 98
2d21ac55
A
99enum
100{
101 // not idle around autowake time, secs
102 kAutoWakePreWindow = 45,
103 kAutoWakePostWindow = 15
104};
105
106
0c530ab8
A
107#define kLocalEvalClamshellCommand (1 << 15)
108
1c79356b 109static IOPMPowerState ourPowerStates[number_of_power_states] = {
0c530ab8
A
110 // state 0, off
111 {1,0, 0, 0,0,0,0,0,0,0,0,0},
112 // state 1, restart
113 {1,kIOPMRestartCapability, kIOPMRestart, RESTART_POWER,0,0,0,0,0,0,0,0},
114 // state 2, sleep
115 {1,kIOPMSleepCapability, kIOPMSleep, SLEEP_POWER,0,0,0,0,0,0,0,0},
116 // state 3, doze
117 {1,kIOPMDoze, kIOPMDoze, DOZE_POWER,0,0,0,0,0,0,0,0},
118 // state 4, on
119 {1,kIOPMPowerOn, kIOPMPowerOn, ON_POWER,0,0,0,0,0,0,0,0},
1c79356b
A
120};
121
6601e61a
A
122static IOPMrootDomain * gRootDomain;
123static UInt32 gSleepOrShutdownPending = 0;
4452a7af 124
2d21ac55
A
125struct timeval gIOLastSleepTime;
126struct timeval gIOLastWakeTime;
127
128// Constants used as arguments to IOPMrootDomain::informCPUStateChange
129#define kCPUUnknownIndex 9999999
130enum {
131 kInformAC = 0,
132 kInformLid = 1,
133 kInformableCount = 2
134};
4452a7af 135
0c530ab8
A
136class PMSettingObject : public OSObject
137{
138 OSDeclareDefaultStructors(PMSettingObject)
139private:
140 IOPMrootDomain *parent;
141 IOPMSettingControllerCallback func;
142 OSObject *target;
143 uintptr_t refcon;
144 uint32_t *publishedFeatureID;
145 int releaseAtCount;
146public:
147 static PMSettingObject *pmSettingObject(
148 IOPMrootDomain *parent_arg,
149 IOPMSettingControllerCallback handler_arg,
150 OSObject *target_arg,
151 uintptr_t refcon_arg,
152 uint32_t supportedPowerSources,
153 const OSSymbol *settings[]);
154
155 void setPMSetting(const OSSymbol *type, OSObject *obj);
156
157 void taggedRelease(const void *tag, const int when) const;
158 void free(void);
159};
160
2d21ac55
A
161/*
162 * Internal helper object for Shutdown/Restart notifications.
163 */
164#define kPMHaltMaxWorkers 8
165#define kPMHaltTimeoutMS 100
166
167class PMHaltWorker : public OSObject
168{
169 OSDeclareDefaultStructors( PMHaltWorker )
170
171public:
172 IOService * service; // service being worked on
173 AbsoluteTime startTime; // time when work started
174 int depth; // work on nubs at this PM-tree depth
175 int visits; // number of nodes visited (debug)
176 IOLock * lock;
177 bool timeout; // service took too long
178
179 static PMHaltWorker * worker( void );
180 static void main( void * arg );
181 static void work( PMHaltWorker * me );
182 static void checkTimeout( PMHaltWorker * me, AbsoluteTime * now );
183 virtual void free( void );
184};
185
186OSDefineMetaClassAndStructors( PMHaltWorker, OSObject )
0c530ab8
A
187
188
1c79356b
A
189#define super IOService
190OSDefineMetaClassAndStructors(IOPMrootDomain,IOService)
191
192extern "C"
193{
91447636 194 IONotifier * registerSleepWakeInterest(IOServiceInterestHandler handler, void * self, void * ref)
1c79356b
A
195 {
196 return gRootDomain->registerInterest( gIOGeneralInterest, handler, self, ref );
197 }
198
91447636 199 IONotifier * registerPrioritySleepWakeInterest(IOServiceInterestHandler handler, void * self, void * ref)
0b4e3aa0
A
200 {
201 return gRootDomain->registerInterest( gIOPriorityPowerStateInterest, handler, self, ref );
202 }
203
1c79356b
A
204 IOReturn acknowledgeSleepWakeNotification(void * PMrefcon)
205 {
206 return gRootDomain->allowPowerChange ( (unsigned long)PMrefcon );
207 }
208
0b4e3aa0
A
209 IOReturn vetoSleepWakeNotification(void * PMrefcon)
210 {
211 return gRootDomain->cancelPowerChange ( (unsigned long)PMrefcon );
212 }
213
214 IOReturn rootDomainRestart ( void )
215 {
216 return gRootDomain->restartSystem();
217 }
218
219 IOReturn rootDomainShutdown ( void )
220 {
221 return gRootDomain->shutdownSystem();
222 }
223
224 void IOSystemShutdownNotification ( void )
225 {
91447636 226 IOCatalogue::disableExternalLinker();
0b4e3aa0
A
227 for ( int i = 0; i < 100; i++ )
228 {
229 if ( OSCompareAndSwap( 0, 1, &gSleepOrShutdownPending ) ) break;
230 IOSleep( 100 );
231 }
232 }
233
234 int sync_internal(void);
1c79356b
A
235}
236
0b4e3aa0
A
237/*
238A device is always in the highest power state which satisfies its driver, its policy-maker, and any power domain
239children it has, but within the constraint of the power state provided by its parent. The driver expresses its desire by
240calling changePowerStateTo(), the policy-maker expresses its desire by calling changePowerStateToPriv(), and the children
241express their desires by calling requestPowerDomainState().
242
243The Root Power Domain owns the policy for idle and demand sleep and doze for the system. It is a power-managed IOService just
244like the others in the system. It implements several power states which correspond to what we see as Sleep, Doze, etc.
245
246The sleep/doze policy is as follows:
247Sleep and Doze are prevented if the case is open so that nobody will think the machine is off and plug/unplug cards.
248Sleep and Doze are prevented if the sleep timeout slider in the preferences panel is at zero.
249The system cannot Sleep, but can Doze if some object in the tree is in a power state marked kIOPMPreventSystemSleep.
250
251These three conditions are enforced using the "driver clamp" by calling changePowerStateTo(). For example, if the case is
252opened, changePowerStateTo(ON_STATE) is called to hold the system on regardless of the desires of the children of the root or
253the state of the other clamp.
254
255Demand Sleep/Doze is initiated by pressing the front panel power button, closing the clamshell, or selecting the menu item.
256In this case the root's parent actually initiates the power state change so that the root has no choice and does not give
257applications the opportunity to veto the change.
258
259Idle Sleep/Doze occurs if no objects in the tree are in a state marked kIOPMPreventIdleSleep. When this is true, the root's
260children are not holding the root on, so it sets the "policy-maker clamp" by calling changePowerStateToPriv(ON_STATE)
261to hold itself on until the sleep timer expires. This timer is set for the difference between the sleep timeout slider and
262the larger of the display dim timeout slider and the disk spindown timeout slider in the Preferences panel. For example, if
263the system is set to sleep after thirty idle minutes, and the display and disk are set to sleep after five idle minutes,
264when there is no longer an object in the tree holding the system out of Idle Sleep (via kIOPMPreventIdleSleep), the root
265sets its timer for 25 minutes (30 - 5). When the timer expires, it releases its clamp and now nothing is holding it awake,
266so it falls asleep.
267
268Demand sleep is prevented when the system is booting. When preferences are transmitted by the loginwindow at the end of
269boot, a flag is cleared, and this allows subsequent Demand Sleep.
270
271The 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
272a clamp, but sets a flag which is noticed before actually sleeping the kernel. If the flag is set, the root steps up
273one power state from Sleep to Doze, and any objects in the tree for which this is relevent will act appropriately (USB and
274ADB will turn on again so that they can wake the system out of Doze (keyboard/mouse activity will cause the Display Wrangler
275to be tickled)).
276*/
2d21ac55 277
0b4e3aa0
A
278// **********************************************************************************
279
280IOPMrootDomain * IOPMrootDomain::construct( void )
281{
55e303ae 282 IOPMrootDomain *root;
0b4e3aa0
A
283
284 root = new IOPMrootDomain;
285 if( root)
286 root->init();
287
288 return( root );
289}
290
291// **********************************************************************************
292
293static void disk_sync_callout(thread_call_param_t p0, thread_call_param_t p1)
294{
2d21ac55
A
295 IOService *rootDomain = (IOService *) p0;
296 unsigned long pmRef = (unsigned long) p1;
297
298 DEBUG_LOG("disk_sync_callout: start\n");
0b4e3aa0 299
2d21ac55 300#if HIBERNATION
3a60a9f5 301 IOHibernateSystemSleep();
2d21ac55 302#endif
0b4e3aa0
A
303 sync_internal();
304 rootDomain->allowPowerChange(pmRef);
2d21ac55 305 DEBUG_LOG("disk_sync_callout: finish\n");
0b4e3aa0 306}
1c79356b 307
0c530ab8 308// **********************************************************************************
2d21ac55
A
309
310static UInt32 computeDeltaTimeMS( const AbsoluteTime * startTime )
0c530ab8 311{
2d21ac55
A
312 AbsoluteTime endTime;
313 UInt64 nano = 0;
314
315 clock_get_uptime(&endTime);
316 if (CMP_ABSOLUTETIME(&endTime, startTime) > 0)
317 {
318 SUB_ABSOLUTETIME(&endTime, startTime);
319 absolutetime_to_nanoseconds(endTime, &nano);
320 }
0c530ab8 321
2d21ac55
A
322 return (UInt32)(nano / 1000000ULL);
323}
0c530ab8 324
1c79356b
A
325// **********************************************************************************
326// start
327//
328// We don't do much here. The real initialization occurs when the platform
329// expert informs us we are the root.
330// **********************************************************************************
0b4e3aa0 331
2d21ac55
A
332#define kRootDomainSettingsCount 14
333
334static SYSCTL_STRUCT(_kern, OID_AUTO, sleeptime,
335 CTLFLAG_RD | CTLFLAG_NOAUTO | CTLFLAG_KERN,
336 &gIOLastSleepTime, timeval, "");
337
338static SYSCTL_STRUCT(_kern, OID_AUTO, waketime,
339 CTLFLAG_RD | CTLFLAG_NOAUTO | CTLFLAG_KERN,
340 &gIOLastWakeTime, timeval, "");
341
342static const OSSymbol * gIOPMSettingAutoWakeSecondsKey;
0b4e3aa0 343
1c79356b
A
344bool IOPMrootDomain::start ( IOService * nub )
345{
0c530ab8
A
346 OSIterator *psIterator;
347 OSDictionary *tmpDict;
2d21ac55
A
348
349 gIOPMSettingAutoWakeSecondsKey = OSSymbol::withCString(kIOPMSettingAutoWakeSecondsKey);
350
0c530ab8
A
351 const OSSymbol *settingsArr[kRootDomainSettingsCount] =
352 {
353 OSSymbol::withCString(kIOPMSettingSleepOnPowerButtonKey),
2d21ac55 354 gIOPMSettingAutoWakeSecondsKey,
0c530ab8
A
355 OSSymbol::withCString(kIOPMSettingAutoPowerSecondsKey),
356 OSSymbol::withCString(kIOPMSettingAutoWakeCalendarKey),
357 OSSymbol::withCString(kIOPMSettingAutoPowerCalendarKey),
358 OSSymbol::withCString(kIOPMSettingDebugWakeRelativeKey),
359 OSSymbol::withCString(kIOPMSettingDebugPowerRelativeKey),
360 OSSymbol::withCString(kIOPMSettingWakeOnRingKey),
361 OSSymbol::withCString(kIOPMSettingRestartOnPowerLossKey),
362 OSSymbol::withCString(kIOPMSettingWakeOnClamshellKey),
363 OSSymbol::withCString(kIOPMSettingWakeOnACChangeKey),
364 OSSymbol::withCString(kIOPMSettingTimeZoneOffsetKey),
2d21ac55 365 OSSymbol::withCString(kIOPMSettingDisplaySleepUsesDimKey),
0c530ab8
A
366 OSSymbol::withCString(kIOPMSettingMobileMotionModuleKey)
367 };
368
55e303ae
A
369
370 pmPowerStateQueue = 0;
d52fe63f 371
e5568f75
A
372 _reserved = (ExpansionData *)IOMalloc(sizeof(ExpansionData));
373 if(!_reserved) return false;
374
1c79356b
A
375 super::start(nub);
376
377 gRootDomain = this;
378
379 PMinit();
0c530ab8
A
380
381 sleepSupportedPEFunction = OSSymbol::withCString("IOPMSetSleepSupported");
382 canSleep = true;
383 setProperty(kIOSleepSupportedKey,true);
91447636 384
2d21ac55 385 userDisabledAllSleep = false;
1c79356b 386 allowSleep = true;
0b4e3aa0 387 sleepIsSupported = true;
1c79356b 388 systemBooting = true;
0b4e3aa0
A
389 sleepSlider = 0;
390 idleSleepPending = false;
0b4e3aa0
A
391 wrangler = NULL;
392 sleepASAP = false;
0c530ab8
A
393 clamshellIsClosed = false;
394 clamshellExists = false;
395 ignoringClamshell = true;
d52fe63f 396 ignoringClamshellDuringWakeup = false;
0c530ab8 397 acAdaptorConnect = true;
2d21ac55
A
398
399 idxPMCPUClamshell = kCPUUnknownIndex;
400 idxPMCPULimitedPower = kCPUUnknownIndex;
401
d52fe63f
A
402 tmpDict = OSDictionary::withCapacity(1);
403 setProperty(kRootDomainSupportedFeatures, tmpDict);
404 tmpDict->release();
91447636 405
0c530ab8
A
406 settingsCallbacks = OSDictionary::withCapacity(1);
407
408 // Create a list of the valid PM settings that we'll relay to
409 // interested clients in setProperties() => setPMSetting()
410 allowedPMSettings = OSArray::withObjects(
411 (const OSObject **)settingsArr,
412 kRootDomainSettingsCount,
413 0);
414
415 fPMSettingsDict = OSDictionary::withCapacity(5);
416
55e303ae 417 pmPowerStateQueue = IOPMPowerStateQueue::PMPowerStateQueue(this);
2d21ac55 418 getPMworkloop()->addEventSource(pmPowerStateQueue);
0c530ab8 419
91447636 420 featuresDictLock = IOLockAlloc();
0c530ab8 421 settingsCtrlLock = IORecursiveLockAlloc();
91447636 422
0c530ab8
A
423 extraSleepTimer = thread_call_allocate(
424 (thread_call_func_t)sleepTimerExpired,
425 (thread_call_param_t) this);
426 clamshellWakeupIgnore = thread_call_allocate(
427 (thread_call_func_t)wakeupClamshellTimerExpired,
428 (thread_call_param_t) this);
429 diskSyncCalloutEntry = thread_call_allocate(
430 &disk_sync_callout,
431 (thread_call_param_t) this);
1c79356b 432
55e303ae
A
433 // create our parent
434 patriarch = new IORootParent;
1c79356b
A
435 patriarch->init();
436 patriarch->attach(this);
437 patriarch->start(this);
1c79356b 438 patriarch->addPowerChild(this);
e5568f75 439
1c79356b
A
440 registerPowerDriver(this,ourPowerStates,number_of_power_states);
441
0b4e3aa0 442 setPMRootDomain(this);
55e303ae
A
443 // set a clamp until we sleep
444 changePowerStateToPriv(ON_STATE);
0b4e3aa0 445
55e303ae
A
446 // install power change handler
447 registerPrioritySleepWakeInterest( &sysPowerDownHandler, this, 0);
0b4e3aa0 448
2d21ac55 449#if !NO_KERNEL_HID
0b4e3aa0 450 // Register for a notification when IODisplayWrangler is published
0c530ab8
A
451 _displayWranglerNotifier = addNotification(
452 gIOPublishNotification, serviceMatching("IODisplayWrangler"),
453 &displayWranglerPublished, this, 0);
2d21ac55 454#endif
483a1d10 455
0c530ab8
A
456 // Battery location published - ApplePMU support only
457 _batteryPublishNotifier = addNotification(
458 gIOPublishNotification, serviceMatching("IOPMPowerSource"),
459 &batteryPublished, this, this);
460
1c79356b 461
55e303ae 462 const OSSymbol *ucClassName = OSSymbol::withCStringNoCopy("RootDomainUserClient");
91447636 463 setProperty(gIOUserClientClassKey, (OSObject *) ucClassName);
55e303ae
A
464 ucClassName->release();
465
0c530ab8
A
466 // IOBacklightDisplay can take a long time to load at boot, or it may
467 // not load at all if you're booting with clamshell closed. We publish
468 // 'DisplayDims' here redundantly to get it published early and at all.
469 psIterator = getMatchingServices( serviceMatching("IOPMPowerSource") );
470 if( psIterator && psIterator->getNextObject() )
91447636 471 {
0c530ab8
A
472 // There's at least one battery on the system, so we publish
473 // 'DisplayDims' support for the LCD.
91447636 474 publishFeature("DisplayDims");
0c530ab8
A
475 }
476 if(psIterator) {
477 psIterator->release();
91447636
A
478 }
479
2d21ac55
A
480
481 sysctl_register_oid(&sysctl__kern_sleeptime);
482 sysctl_register_oid(&sysctl__kern_waketime);
483
484#if HIBERNATION
3a60a9f5 485 IOHibernateSystemInit(this);
2d21ac55 486#endif
91447636 487
1c79356b
A
488 registerService(); // let clients find us
489
490 return true;
491}
492
9bccf70c
A
493// **********************************************************************************
494// setProperties
495//
496// Receive a setProperty call
497// The "System Boot" property means the system is completely booted.
498// **********************************************************************************
499IOReturn IOPMrootDomain::setProperties ( OSObject *props_obj)
500{
0c530ab8
A
501 IOReturn return_value = kIOReturnSuccess;
502 OSDictionary *dict = OSDynamicCast(OSDictionary, props_obj);
503 OSBoolean *b;
504 OSNumber *n;
505 OSString *str;
506 OSSymbol *type;
507 OSObject *obj;
508 unsigned int i;
509
510 const OSSymbol *boot_complete_string =
511 OSSymbol::withCString("System Boot Complete");
512 const OSSymbol *sys_shutdown_string =
513 OSSymbol::withCString("System Shutdown");
514 const OSSymbol *stall_halt_string =
515 OSSymbol::withCString("StallSystemAtHalt");
2d21ac55
A
516 const OSSymbol *battery_warning_disabled_string =
517 OSSymbol::withCString("BatteryWarningsDisabled");
518 const OSSymbol *idle_seconds_string =
519 OSSymbol::withCString("System Idle Seconds");
520#if HIBERNATION
0c530ab8
A
521 const OSSymbol *hibernatemode_string =
522 OSSymbol::withCString(kIOHibernateModeKey);
523 const OSSymbol *hibernatefile_string =
524 OSSymbol::withCString(kIOHibernateFileKey);
525 const OSSymbol *hibernatefreeratio_string =
526 OSSymbol::withCString(kIOHibernateFreeRatioKey);
527 const OSSymbol *hibernatefreetime_string =
528 OSSymbol::withCString(kIOHibernateFreeTimeKey);
2d21ac55
A
529#endif
530 const OSSymbol *sleepdisabled_string =
531 OSSymbol::withCString("SleepDisabled");
9bccf70c 532
e5568f75
A
533 if(!dict)
534 {
535 return_value = kIOReturnBadArgument;
536 goto exit;
537 }
4a249263 538
2d21ac55
A
539 if ((n = OSDynamicCast(OSNumber, dict->getObject(idle_seconds_string))))
540 {
541 setProperty(idle_seconds_string, n);
542 idleSeconds = n->unsigned32BitValue();
543 }
544
e5568f75
A
545 if( systemBooting
546 && boot_complete_string
547 && dict->getObject(boot_complete_string))
55e303ae 548 {
9bccf70c 549 systemBooting = false;
9bccf70c 550 adjustPowerState();
0c530ab8
A
551
552 // If lid is closed, re-send lid closed notification
553 // now that booting is complete.
554 if( clamshellIsClosed )
555 {
556 this->receivePowerNotification(kLocalEvalClamshellCommand);
557 }
9bccf70c
A
558 }
559
2d21ac55
A
560 if( battery_warning_disabled_string
561 && dict->getObject(battery_warning_disabled_string))
562 {
563 setProperty( battery_warning_disabled_string,
564 dict->getObject(battery_warning_disabled_string));
565 }
566
0c530ab8
A
567 if( sys_shutdown_string
568 && (b = OSDynamicCast(OSBoolean, dict->getObject(sys_shutdown_string))))
8f6c56a5 569 {
0c530ab8
A
570
571 if(kOSBooleanTrue == b)
572 {
573 /* We set systemShutdown = true during shutdown
574 to prevent sleep at unexpected times while loginwindow is trying
575 to shutdown apps and while the OS is trying to transition to
576 complete power of.
577
578 Set to true during shutdown, as soon as loginwindow shows
579 the "shutdown countdown dialog", through individual app
580 termination, and through black screen kernel shutdown.
581 */
582 kprintf("systemShutdown true\n");
583 systemShutdown = true;
584 } else {
585 /*
586 A shutdown was initiated, but then the shutdown
587 was cancelled, clearing systemShutdown to false here.
588 */
589 kprintf("systemShutdown false\n");
590 systemShutdown = false;
591 }
8f6c56a5 592 }
0c530ab8 593
e5568f75
A
594 if( stall_halt_string
595 && (b = OSDynamicCast(OSBoolean, dict->getObject(stall_halt_string))) )
55e303ae 596 {
4a249263 597 setProperty(stall_halt_string, b);
55e303ae 598 }
91447636 599
2d21ac55 600#if HIBERNATION
3a60a9f5 601 if ( hibernatemode_string
0c530ab8 602 && (n = OSDynamicCast(OSNumber, dict->getObject(hibernatemode_string))))
3a60a9f5 603 {
0c530ab8 604 setProperty(hibernatemode_string, n);
3a60a9f5
A
605 }
606 if ( hibernatefreeratio_string
0c530ab8 607 && (n = OSDynamicCast(OSNumber, dict->getObject(hibernatefreeratio_string))))
3a60a9f5 608 {
0c530ab8 609 setProperty(hibernatefreeratio_string, n);
3a60a9f5
A
610 }
611 if ( hibernatefreetime_string
0c530ab8 612 && (n = OSDynamicCast(OSNumber, dict->getObject(hibernatefreetime_string))))
3a60a9f5 613 {
0c530ab8 614 setProperty(hibernatefreetime_string, n);
3a60a9f5
A
615 }
616 if ( hibernatefile_string
0c530ab8 617 && (str = OSDynamicCast(OSString, dict->getObject(hibernatefile_string))))
8f6c56a5 618 {
0c530ab8 619 setProperty(hibernatefile_string, str);
8f6c56a5 620 }
2d21ac55
A
621#endif
622
623 if( sleepdisabled_string
624 && (b = OSDynamicCast(OSBoolean, dict->getObject(sleepdisabled_string))) )
625 {
626 setProperty(sleepdisabled_string, b);
627
628 userDisabledAllSleep = (kOSBooleanTrue == b);
629 }
8f6c56a5 630
0c530ab8
A
631 // Relay our allowed PM settings onto our registered PM clients
632 for(i = 0; i < allowedPMSettings->getCount(); i++) {
e5568f75 633
0c530ab8
A
634 type = (OSSymbol *)allowedPMSettings->getObject(i);
635 if(!type) continue;
8ad349bb 636
0c530ab8
A
637 obj = dict->getObject(type);
638 if(!obj) continue;
2d21ac55
A
639
640 if ((gIOPMSettingAutoWakeSecondsKey == type) && ((n = OSDynamicCast(OSNumber, obj))))
641 {
642 UInt32 rsecs = n->unsigned32BitValue();
643 if (!rsecs)
644 autoWakeStart = autoWakeEnd = 0;
645 else
646 {
647 AbsoluteTime deadline;
648 clock_interval_to_deadline(rsecs + kAutoWakePostWindow, kSecondScale, &deadline);
649 autoWakeEnd = AbsoluteTime_to_scalar(&deadline);
650 if (rsecs > kAutoWakePreWindow)
651 rsecs -= kAutoWakePreWindow;
652 else
653 rsecs = 0;
654 clock_interval_to_deadline(rsecs, kSecondScale, &deadline);
655 autoWakeStart = AbsoluteTime_to_scalar(&deadline);
656 }
657 }
0c530ab8
A
658
659 return_value = setPMSetting(type, obj);
660
6601e61a
A
661 if(kIOReturnSuccess != return_value) goto exit;
662 }
663
2d21ac55
A
664exit:
665 if(sleepdisabled_string) sleepdisabled_string->release();
4a249263 666 if(boot_complete_string) boot_complete_string->release();
4a249263 667 if(stall_halt_string) stall_halt_string->release();
2d21ac55 668 if(idle_seconds_string) idle_seconds_string->release();
e5568f75 669 return return_value;
9bccf70c
A
670}
671
1c79356b
A
672
673//*********************************************************************************
674// youAreRoot
675//
676// Power Managment is informing us that we are the root power domain.
677// We know we are not the root however, since we have just instantiated a parent
678// for ourselves and made it the root. We override this method so it will have
679// no effect
680//*********************************************************************************
681IOReturn IOPMrootDomain::youAreRoot ( void )
682{
683 return IOPMNoErr;
684}
685
1c79356b
A
686// **********************************************************************************
687// command_received
688//
9bccf70c 689// No longer used
1c79356b 690// **********************************************************************************
9bccf70c 691void IOPMrootDomain::command_received ( void * w, void * x, void * y, void * z )
1c79356b 692{
9bccf70c
A
693 super::command_received(w,x,y,z);
694}
0b4e3aa0 695
0b4e3aa0 696
9bccf70c
A
697// **********************************************************************************
698// broadcast_aggressiveness
699//
700// **********************************************************************************
701IOReturn broadcast_aggressiveness ( OSObject * root, void * x, void * y, void *, void * )
702{
703 ((IOPMrootDomain *)root)->broadcast_it((unsigned long)x,(unsigned long)y);
704 return IOPMNoErr;
705}
0b4e3aa0
A
706
707
9bccf70c
A
708// **********************************************************************************
709// broadcast_it
710//
711// We are behind the command gate to broadcast an aggressiveness factor. We let the
712// superclass do it, but we need to snoop on factors that affect idle sleep.
713// **********************************************************************************
714void IOPMrootDomain::broadcast_it (unsigned long type, unsigned long value)
715{
716 super::setAggressiveness(type,value);
717
718 // Save user's spin down timer to restore after we replace it for idle sleep
719 if( type == kPMMinutesToSpinDown ) user_spindown = value;
720
721 // Use longestNonSleepSlider to calculate dimming adjust idle sleep timer
2d21ac55
A
722 if (getAggressiveness(kPMMinutesToDim, (unsigned long *)&longestNonSleepSlider)
723 != kIOReturnSuccess)
724 longestNonSleepSlider = 0;
9bccf70c
A
725
726 if ( type == kPMMinutesToSleep ) {
2d21ac55
A
727 DEBUG_LOG("PM idle time -> %ld secs (ena %d)\n", idleSeconds, (value != 0));
728 if (0x7fffffff == value)
729 value = idleSeconds;
730
9bccf70c 731 if ( (sleepSlider == 0) && (value != 0) ) {
2d21ac55
A
732 if (!wrangler)
733 {
734 sleepASAP = false;
735 changePowerStateToPriv(ON_STATE);
736 if (idleSeconds)
737 {
738 AbsoluteTime deadline;
739 // stay awake for at least idleSeconds
740 clock_interval_to_deadline(idleSeconds, kSecondScale, &deadline);
741 thread_call_enter_delayed(extraSleepTimer, deadline);
742 // this gets turned off when we sleep again
743 idleSleepPending = true;
744 }
745 }
746 else
747 {
748 // If sleepASAP is already set, then calling adjustPowerState() here
749 // will put the system to sleep immediately which is bad. Note that
750 // this aggressiveness change can occur without waking up the display
751 // by (dis)connecting the AC adapter. To get around this, the power
752 // clamp is restore to ON state then dropped after waiting for the
753 // sleep timer to expire.
754
755 if (sleepASAP)
756 {
757 AbsoluteTime deadline;
758 // stay awake for at least sleepSlider minutes
759 clock_interval_to_deadline(value * 60, kSecondScale, &deadline);
760 thread_call_enter_delayed(extraSleepTimer, deadline);
761 // this gets turned off when we sleep again
762 idleSleepPending = true;
763 sleepASAP = false;
764 }
765 }
9bccf70c
A
766 }
767 sleepSlider = value;
768 if ( sleepSlider == 0 ) {
55e303ae
A
769 // idle sleep is now disabled
770 adjustPowerState();
771 // make sure we're powered
772 patriarch->wakeSystem();
9bccf70c
A
773 }
774 }
775 if ( sleepSlider > longestNonSleepSlider ) {
776 extraSleepDelay = sleepSlider - longestNonSleepSlider ;
777 }
778 else {
779 extraSleepDelay = 0;
1c79356b
A
780 }
781}
782
783
0b4e3aa0
A
784// **********************************************************************************
785// sleepTimerExpired
786//
787// **********************************************************************************
788static void sleepTimerExpired ( thread_call_param_t us)
789{
790 ((IOPMrootDomain *)us)->handleSleepTimerExpiration();
791 }
792
d52fe63f
A
793
794static void wakeupClamshellTimerExpired ( thread_call_param_t us)
795{
796 ((IOPMrootDomain *)us)->stopIgnoringClamshellEventsDuringWakeup();
797}
798
0b4e3aa0
A
799
800// **********************************************************************************
801// handleSleepTimerExpiration
802//
803// The time between the sleep idle timeout and the next longest one has elapsed.
804// It's time to sleep. Start that by removing the clamp that's holding us awake.
805// **********************************************************************************
806void IOPMrootDomain::handleSleepTimerExpiration ( void )
807{
2d21ac55
A
808 DEBUG_LOG("SleepTimerExpired\n");
809
810 AbsoluteTime time;
811
812 clock_get_uptime(&time);
813 if ((AbsoluteTime_to_scalar(&time) > autoWakeStart) && (AbsoluteTime_to_scalar(&time) < autoWakeEnd))
814 {
815 thread_call_enter_delayed(extraSleepTimer, *((AbsoluteTime *) &autoWakeEnd));
816 return;
817 }
818
0b4e3aa0
A
819 // accelerate disk spin down if spin down timer is non-zero (zero = never spin down)
820 if(0 != user_spindown)
821 setQuickSpinDownTimeout();
822
823 sleepASAP = true;
824 adjustPowerState();
825}
826
827
d52fe63f
A
828void IOPMrootDomain::stopIgnoringClamshellEventsDuringWakeup(void)
829{
d52fe63f
A
830 // Allow clamshell-induced sleep now
831 ignoringClamshellDuringWakeup = false;
832
0c530ab8
A
833 // Re-send clamshell event, in case it causes a sleep
834 if(clamshellIsClosed)
835 this->receivePowerNotification( kLocalEvalClamshellCommand );
d52fe63f
A
836}
837
1c79356b
A
838//*********************************************************************************
839// setAggressiveness
840//
9bccf70c 841// Some aggressiveness factor has changed. We broadcast it to the hierarchy while on
1c79356b
A
842// the Power Mangement workloop thread. This enables objects in the
843// hierarchy to successfully alter their idle timers, which are all on the
844// same thread.
845//*********************************************************************************
846
847IOReturn IOPMrootDomain::setAggressiveness ( unsigned long type, unsigned long newLevel )
848{
2d21ac55
A
849 IOWorkLoop * pmWorkLoop = getPMworkloop();
850 if (pmWorkLoop)
851 pmWorkLoop->runAction(broadcast_aggressiveness,this,(void *)type,(void *)newLevel);
3a60a9f5 852
1c79356b
A
853 return kIOReturnSuccess;
854}
855
856
857// **********************************************************************************
858// sleepSystem
859//
860// **********************************************************************************
2d21ac55 861/* public */
1c79356b
A
862IOReturn IOPMrootDomain::sleepSystem ( void )
863{
2d21ac55
A
864 return sleepSystemOptions (NULL);
865}
866
867/* private */
868IOReturn IOPMrootDomain::sleepSystemOptions ( OSDictionary *options )
869{
870 /* sleepSystem is a public function, and may be called by any kernel driver.
871 * And that's bad - drivers should sleep the system by calling
872 * receivePowerNotification() instead. Drivers should not use sleepSystem.
873 *
874 * Note that user space app calls to IOPMSleepSystem() will also travel
875 * this code path and thus be correctly identified as software sleeps.
876 */
877
878 if (options && options->getObject("OSSwitch"))
879 {
880
881 // Log specific sleep cause for OS Switch hibernation
882 return privateSleepSystem( kIOPMOSSwitchHibernationKey) ;
883
884 } else {
885
886 return privateSleepSystem( kIOPMSoftwareSleepKey);
887
888 }
889}
890
891/* private */
892IOReturn IOPMrootDomain::privateSleepSystem ( const char *sleepReason )
893{
894 // Record sleep cause in IORegistry
895 if (sleepReason) {
896 setProperty(kRootDomainSleepReasonKey, sleepReason);
897 }
898
0c530ab8
A
899 if(systemShutdown) {
900 kprintf("Preventing system sleep on grounds of systemShutdown.\n");
1c79356b 901 }
2d21ac55
A
902
903 if( userDisabledAllSleep )
904 {
905 /* Prevent sleep of all kinds if directed to by user space */
906 return kIOReturnNotPermitted;
907 }
0c530ab8 908
2d21ac55
A
909 if ( !systemBooting
910 && !systemShutdown
911 && allowSleep)
912 {
913 if ( !sleepIsSupported ) {
0c530ab8 914 setSleepSupported( kPCICantSleep );
2d21ac55
A
915 kprintf("Sleep prevented by kIOPMPreventSystemSleep flag\n");
916 }
0c530ab8 917 patriarch->sleepSystem();
0b4e3aa0 918 return kIOReturnSuccess;
2d21ac55
A
919 } else {
920 // Unable to sleep because system is in the process of booting or shutting down,
921 // or sleep has otherwise been disallowed.
922 return kIOReturnError;
0b4e3aa0 923 }
0b4e3aa0
A
924}
925
926
927// **********************************************************************************
928// shutdownSystem
929//
930// **********************************************************************************
931IOReturn IOPMrootDomain::shutdownSystem ( void )
932{
55e303ae
A
933 //patriarch->shutDownSystem();
934 return kIOReturnUnsupported;
0b4e3aa0
A
935}
936
937
938// **********************************************************************************
939// restartSystem
940//
941// **********************************************************************************
942IOReturn IOPMrootDomain::restartSystem ( void )
943{
55e303ae
A
944 //patriarch->restartSystem();
945 return kIOReturnUnsupported;
1c79356b
A
946}
947
948
949// **********************************************************************************
950// powerChangeDone
951//
952// This overrides powerChangeDone in IOService.
0b4e3aa0
A
953//
954// Finder sleep and idle sleep move us from the ON state to the SLEEP_STATE.
955// In this case:
956// If we just finished going to the SLEEP_STATE, and the platform is capable of true sleep,
957// sleep the kernel. Otherwise switch up to the DOZE_STATE which will keep almost
958// everything as off as it can get.
959//
960// **********************************************************************************
961void IOPMrootDomain::powerChangeDone ( unsigned long previousState )
962{
963 OSNumber * propertyPtr;
964 unsigned short theProperty;
2d21ac55
A
965 AbsoluteTime deadline;
966
967 DEBUG_LOG("PowerChangeDone: %ld -> %ld\n", previousState, getPowerState());
968
969 switch ( getPowerState() ) {
0b4e3aa0 970 case SLEEP_STATE:
2d21ac55
A
971 if ( previousState != ON_STATE )
972 break;
973
55e303ae
A
974 if ( canSleep && sleepIsSupported )
975 {
976 // re-enable this timer for next sleep
977 idleSleepPending = false;
91447636 978
2d21ac55
A
979 uint32_t secs, microsecs;
980 clock_get_calendar_microtime(&secs, &microsecs);
981 logtime(secs);
982 gIOLastSleepTime.tv_sec = secs;
983 gIOLastSleepTime.tv_usec = microsecs;
984
985#if HIBERNATION
3a60a9f5
A
986 IOLog("System %sSleep\n", gIOHibernateState ? "Safe" : "");
987
988 IOHibernateSystemHasSlept();
2d21ac55
A
989#else
990 IOLog("System Sleep\n");
991#endif
3a60a9f5 992
2d21ac55 993 getPlatform()->sleepKernel();
9bccf70c 994
55e303ae 995 // The CPU(s) are off at this point. When they're awakened by CPU interrupt,
91447636
A
996 // code will resume execution here.
997
55e303ae 998 // Now we're waking...
2d21ac55 999#if HIBERNATION
0c530ab8 1000 IOHibernateSystemWake();
2d21ac55 1001#endif
9bccf70c 1002
55e303ae
A
1003 // stay awake for at least 30 seconds
1004 clock_interval_to_deadline(30, kSecondScale, &deadline);
0b4e3aa0 1005 thread_call_enter_delayed(extraSleepTimer, deadline);
55e303ae
A
1006 // this gets turned off when we sleep again
1007 idleSleepPending = true;
d52fe63f
A
1008
1009 // Ignore closed clamshell during wakeup and for a few seconds
1010 // after wakeup is complete
1011 ignoringClamshellDuringWakeup = true;
1012
55e303ae
A
1013 // sleep transition complete
1014 gSleepOrShutdownPending = 0;
1015
2d21ac55
A
1016 // trip the reset of the calendar clock
1017 clock_wakeup_calendar();
55e303ae
A
1018
1019 // get us some power
1020 patriarch->wakeSystem();
0b4e3aa0 1021
55e303ae
A
1022 // early stage wake notification
1023 tellClients(kIOMessageSystemWillPowerOn);
1024
1025 // tell the tree we're waking
2d21ac55 1026#if HIBERNATION
3a60a9f5 1027 IOLog("System %sWake\n", gIOHibernateState ? "SafeSleep " : "");
2d21ac55 1028#endif
55e303ae 1029 systemWake();
d52fe63f
A
1030
1031 // Allow drivers to request extra processing time before clamshell
1032 // sleep if kIOREMSleepEnabledKey is present.
1033 // Ignore clamshell events for at least 5 seconds
1034 if(getProperty(kIOREMSleepEnabledKey)) {
1035 // clamshellWakeupIgnore callout clears ignoreClamshellDuringWakeup bit
1036 clock_interval_to_deadline(5, kSecondScale, &deadline);
0c530ab8
A
1037 if(clamshellWakeupIgnore) {
1038 thread_call_enter_delayed(clamshellWakeupIgnore, deadline);
1039 }
d52fe63f 1040 } else ignoringClamshellDuringWakeup = false;
55e303ae
A
1041
1042 // Find out what woke us
0b4e3aa0 1043 propertyPtr = OSDynamicCast(OSNumber,getProperty("WakeEvent"));
2d21ac55 1044 if ( propertyPtr ) {
0b4e3aa0
A
1045 theProperty = propertyPtr->unsigned16BitValue();
1046 IOLog("Wake event %04x\n",theProperty);
43866e37
A
1047 if ( (theProperty & 0x0008) || //lid
1048 (theProperty & 0x0800) || // front panel button
1049 (theProperty & 0x0020) || // external keyboard
1050 (theProperty & 0x0001) ) { // internal keyboard
55e303ae
A
1051 // We've identified the wakeup event as UI driven
1052 reportUserInput();
0b4e3aa0 1053 }
55e303ae
A
1054 } else {
1055 // Since we can't identify the wakeup event, treat it as UI activity
1056 reportUserInput();
0b4e3aa0 1057 }
55e303ae
A
1058
1059 // Wake for thirty seconds
2d21ac55 1060 changePowerStateToPriv(ON_STATE);
55e303ae
A
1061 } else {
1062 // allow us to step up a power state
1063 patriarch->sleepToDoze();
2d21ac55
A
1064
1065 // ignore children's request for higher power during doze.
1066 powerOverrideOnPriv();
55e303ae 1067 changePowerStateToPriv(DOZE_STATE);
0b4e3aa0
A
1068 }
1069 break;
1070
1071 case DOZE_STATE:
55e303ae
A
1072 if ( previousState != DOZE_STATE )
1073 {
0b4e3aa0
A
1074 IOLog("System Doze\n");
1075 }
55e303ae
A
1076 // re-enable this timer for next sleep
1077 idleSleepPending = false;
0b4e3aa0 1078 gSleepOrShutdownPending = 0;
4a3eedf9
A
1079
1080 // Invalidate prior activity tickles to allow wake from doze.
1081 if (wrangler) wrangler->changePowerStateTo(0);
0b4e3aa0
A
1082 break;
1083
1084 case RESTART_STATE:
1085 IOLog("System Restart\n");
1086 PEHaltRestart(kPERestartCPU);
1087 break;
1088
1089 case OFF_STATE:
1090 IOLog("System Halt\n");
1091 PEHaltRestart(kPEHaltCPU);
1092 break;
1093 }
1094}
1095
1096
1097// **********************************************************************************
1098// wakeFromDoze
1099//
1100// The Display Wrangler calls here when it switches to its highest state. If the
1101// system is currently dozing, allow it to wake by making sure the parent is
1102// providing power.
1c79356b 1103// **********************************************************************************
0b4e3aa0 1104void IOPMrootDomain::wakeFromDoze( void )
1c79356b 1105{
2d21ac55 1106 if ( getPowerState() == DOZE_STATE )
55e303ae 1107 {
0c530ab8
A
1108 // Reset sleep support till next sleep attempt.
1109 // A machine's support of sleep vs. doze can change over the course of
1110 // a running system, so we recalculate it before every sleep.
1111 setSleepSupported(0);
1112
2d21ac55 1113 changePowerStateToPriv(ON_STATE);
0b4e3aa0 1114 powerOverrideOffPriv();
55e303ae
A
1115
1116 // early wake notification
1117 tellClients(kIOMessageSystemWillPowerOn);
1118
1119 // allow us to wake if children so desire
1120 patriarch->wakeSystem();
1c79356b
A
1121 }
1122}
1123
1124
0c530ab8 1125// *****************************************************************************
d52fe63f
A
1126// publishFeature
1127//
1128// Adds a new feature to the supported features dictionary
1129//
1130//
0c530ab8 1131// *****************************************************************************
d52fe63f
A
1132void IOPMrootDomain::publishFeature( const char * feature )
1133{
0c530ab8
A
1134 publishFeature(feature, kIOPMSupportedOnAC
1135 | kIOPMSupportedOnBatt
1136 | kIOPMSupportedOnUPS,
1137 NULL);
1138 return;
1139}
1140
1141
1142// *****************************************************************************
1143// publishFeature (with supported power source specified)
1144//
1145// Adds a new feature to the supported features dictionary
1146//
1147//
1148// *****************************************************************************
1149void IOPMrootDomain::publishFeature(
1150 const char *feature,
1151 uint32_t supportedWhere,
1152 uint32_t *uniqueFeatureID)
1153{
1154 static uint16_t next_feature_id = 500;
1155
1156 OSNumber *new_feature_data = NULL;
1157 OSNumber *existing_feature = NULL;
1158 OSArray *existing_feature_arr = NULL;
1159 OSObject *osObj = NULL;
1160 uint32_t feature_value = 0;
1161
1162 supportedWhere &= kRD_AllPowerSources; // mask off any craziness!
1163
2d21ac55
A
1164// kprintf("IOPMrootDomain::publishFeature [\"%s\":%0x01x]\n", feature, supportedWhere);
1165
0c530ab8
A
1166 if(!supportedWhere) {
1167 // Feature isn't supported anywhere!
1168 return;
1169 }
1170
1171 if(next_feature_id > 5000) {
1172 // Far, far too many features!
1173 return;
1174 }
1175
91447636 1176 if(featuresDictLock) IOLockLock(featuresDictLock);
0c530ab8 1177
91447636
A
1178 OSDictionary *features =
1179 (OSDictionary *) getProperty(kRootDomainSupportedFeatures);
1180
0c530ab8
A
1181 // Create new features dict if necessary
1182 if ( features && OSDynamicCast(OSDictionary, features)) {
91447636 1183 features = OSDictionary::withDictionary(features);
0c530ab8 1184 } else {
91447636 1185 features = OSDictionary::withCapacity(1);
0c530ab8
A
1186 }
1187
1188 // Create OSNumber to track new feature
1189
1190 next_feature_id += 1;
1191 if( uniqueFeatureID ) {
1192 // We don't really mind if the calling kext didn't give us a place
1193 // to stash their unique id. Many kexts don't plan to unload, and thus
1194 // have no need to remove themselves later.
1195 *uniqueFeatureID = next_feature_id;
1196 }
1197
1198 feature_value = supportedWhere + (next_feature_id << 16);
1199 new_feature_data = OSNumber::withNumber(
1200 (unsigned long long)feature_value, 32);
1201
1202 // Does features object already exist?
1203 if( (osObj = features->getObject(feature)) )
1204 {
1205 if(( existing_feature = OSDynamicCast(OSNumber, osObj) ))
1206 {
1207 // We need to create an OSArray to hold the now 2 elements.
1208 existing_feature_arr = OSArray::withObjects(
1209 (const OSObject **)&existing_feature, 1, 2);
1210 existing_feature_arr->setObject(new_feature_data);
1211 features->setObject(feature, existing_feature_arr);
1212 } else if(( existing_feature_arr = OSDynamicCast(OSArray, osObj) ))
1213 {
1214 // Add object to existing array
1215 existing_feature_arr->setObject(new_feature_data);
1216 }
1217 } else {
1218 // The easy case: no previously existing features listed. We simply
1219 // set the OSNumber at key 'feature' and we're on our way.
1220 features->setObject(feature, new_feature_data);
1221 }
1222
1223 new_feature_data->release();
91447636 1224
91447636 1225 setProperty(kRootDomainSupportedFeatures, features);
0c530ab8 1226
91447636 1227 features->release();
0c530ab8 1228
0c530ab8 1229 if(featuresDictLock) IOLockUnlock(featuresDictLock);
2d21ac55
A
1230
1231 // Notify EnergySaver and all those in user space so they might
1232 // re-populate their feature specific UI
1233 if(pmPowerStateQueue) {
1234 pmPowerStateQueue->featureChangeOccurred(
1235 kIOPMMessageFeatureChange, this);
1236 }
0c530ab8
A
1237}
1238
1239// *****************************************************************************
1240// removePublishedFeature
1241//
1242// Removes previously published feature
1243//
1244//
1245// *****************************************************************************
1246IOReturn IOPMrootDomain::removePublishedFeature( uint32_t removeFeatureID )
1247{
1248 IOReturn ret = kIOReturnError;
1249 uint32_t feature_value = 0;
1250 uint16_t feature_id = 0;
1251 bool madeAChange = false;
1252
1253 OSSymbol *dictKey = NULL;
1254 OSCollectionIterator *dictIterator = NULL;
1255 OSArray *arrayMember = NULL;
1256 OSNumber *numberMember = NULL;
1257 OSObject *osObj = NULL;
1258 OSNumber *osNum = NULL;
1259
1260 if(featuresDictLock) IOLockLock(featuresDictLock);
1261
1262 OSDictionary *features =
1263 (OSDictionary *) getProperty(kRootDomainSupportedFeatures);
1264
1265 if ( features && OSDynamicCast(OSDictionary, features) )
1266 {
1267 // Any modifications to the dictionary are made to the copy to prevent
1268 // races & crashes with userland clients. Dictionary updated
1269 // automically later.
1270 features = OSDictionary::withDictionary(features);
1271 } else {
1272 features = NULL;
1273 ret = kIOReturnNotFound;
1274 goto exit;
1275 }
1276
1277 // We iterate 'features' dictionary looking for an entry tagged
1278 // with 'removeFeatureID'. If found, we remove it from our tracking
1279 // structures and notify the OS via a general interest message.
1280
1281 dictIterator = OSCollectionIterator::withCollection(features);
1282 if(!dictIterator) {
1283 goto exit;
1284 }
1285
1286 while( (dictKey = OSDynamicCast(OSSymbol, dictIterator->getNextObject())) )
1287 {
1288 osObj = features->getObject(dictKey);
1289
1290 // Each Feature is either tracked by an OSNumber
1291 if( osObj && (numberMember = OSDynamicCast(OSNumber, osObj)) )
1292 {
1293 feature_value = numberMember->unsigned32BitValue();
1294 feature_id = (uint16_t)(feature_value >> 16);
1295
1296 if( feature_id == (uint16_t)removeFeatureID )
1297 {
1298 // Remove this node
1299 features->removeObject(dictKey);
1300 madeAChange = true;
1301 break;
1302 }
1303
1304 // Or tracked by an OSArray of OSNumbers
1305 } else if( osObj && (arrayMember = OSDynamicCast(OSArray, osObj)) )
1306 {
1307 unsigned int arrayCount = arrayMember->getCount();
1308
1309 for(unsigned int i=0; i<arrayCount; i++)
1310 {
1311 osNum = OSDynamicCast(OSNumber, arrayMember->getObject(i));
1312 if(!osNum) {
1313 continue;
1314 }
1315
1316 feature_value = osNum->unsigned32BitValue();
1317 feature_id = (uint16_t)(feature_value >> 16);
1318
1319 if( feature_id == (uint16_t)removeFeatureID )
1320 {
1321 // Remove this node
1322 if( 1 == arrayCount ) {
1323 // If the array only contains one element, remove
1324 // the whole thing.
1325 features->removeObject(dictKey);
1326 } else {
1327 // Otherwise just remove the element in question.
1328 arrayMember->removeObject(i);
1329 }
1330
1331 madeAChange = true;
1332 break;
1333 }
1334 }
1335 }
1336 }
1337
1338
1339 dictIterator->release();
1340
1341 if( madeAChange )
1342 {
1343 ret = kIOReturnSuccess;
1344
1345 setProperty(kRootDomainSupportedFeatures, features);
1346
1347 // Notify EnergySaver and all those in user space so they might
2d21ac55
A
1348 // re-populate their feature specific UI
1349 if(pmPowerStateQueue) {
1350 pmPowerStateQueue->featureChangeOccurred(
1351 kIOPMMessageFeatureChange, this);
1352 }
0c530ab8
A
1353 } else {
1354 ret = kIOReturnNotFound;
1355 }
1356
1357exit:
1358 if(features) features->release();
1359 if(featuresDictLock) IOLockUnlock(featuresDictLock);
1360 return ret;
5d5c5d0d
A
1361}
1362
5d5c5d0d 1363
0c530ab8
A
1364// **********************************************************************************
1365// unIdleDevice
1366//
1367// Enqueues unidle event to be performed later in a serialized context.
1368//
1369// **********************************************************************************
55e303ae 1370void IOPMrootDomain::unIdleDevice( IOService *theDevice, unsigned long theState )
1c79356b 1371{
55e303ae
A
1372 if(pmPowerStateQueue)
1373 pmPowerStateQueue->unIdleOccurred(theDevice, theState);
1374}
1c79356b 1375
0c530ab8
A
1376// **********************************************************************************
1377// announcePowerSourceChange
1378//
1379// Notifies "interested parties" that the batteries have changed state
1380//
1381// **********************************************************************************
55e303ae
A
1382void IOPMrootDomain::announcePowerSourceChange( void )
1383{
0c530ab8
A
1384 IORegistryEntry *_batteryRegEntry = (IORegistryEntry *) getProperty("BatteryEntry");
1385
1386 // (if possible) re-publish power source state under IOPMrootDomain;
1387 // only do so if the battery controller publishes an IOResource
1388 // defining battery location. Called from ApplePMU battery driver.
483a1d10 1389
483a1d10
A
1390 if(_batteryRegEntry)
1391 {
1392 OSArray *batt_info;
91447636 1393 batt_info = (OSArray *) _batteryRegEntry->getProperty(kIOBatteryInfoKey);
483a1d10
A
1394 if(batt_info)
1395 setProperty(kIOBatteryInfoKey, batt_info);
1396 }
1397
1c79356b
A
1398}
1399
0c530ab8
A
1400
1401// *****************************************************************************
1402// setPMSetting (private)
1403//
1404// Internal helper to relay PM settings changes from user space to individual
1405// drivers. Should be called only by IOPMrootDomain::setProperties.
1406//
1407// *****************************************************************************
1408IOReturn IOPMrootDomain::setPMSetting(
1409 const OSSymbol *type,
1410 OSObject *obj)
e5568f75 1411{
0c530ab8
A
1412 OSArray *arr = NULL;
1413 PMSettingObject *p_obj = NULL;
1414 int count;
1415 int i;
1416
1417 if(NULL == type) return kIOReturnBadArgument;
1418
1419 IORecursiveLockLock(settingsCtrlLock);
6601e61a 1420
0c530ab8
A
1421 fPMSettingsDict->setObject(type, obj);
1422
1423 arr = (OSArray *)settingsCallbacks->getObject(type);
1424 if(NULL == arr) goto exit;
1425 count = arr->getCount();
1426 for(i=0; i<count; i++) {
1427 p_obj = (PMSettingObject *)OSDynamicCast(PMSettingObject, arr->getObject(i));
1428 if(p_obj) p_obj->setPMSetting(type, obj);
1429 }
1430
1431exit:
1432 IORecursiveLockUnlock(settingsCtrlLock);
e5568f75
A
1433 return kIOReturnSuccess;
1434}
1435
0c530ab8
A
1436// *****************************************************************************
1437// copyPMSetting (public)
1438//
1439// Allows kexts to safely read setting values, without being subscribed to
1440// notifications.
1441//
1442// *****************************************************************************
1443OSObject * IOPMrootDomain::copyPMSetting(
1444 OSSymbol *whichSetting)
1445{
1446 OSObject *obj = NULL;
4452a7af 1447
0c530ab8
A
1448 if(!whichSetting) return NULL;
1449
1450 IORecursiveLockLock(settingsCtrlLock);
1451 obj = fPMSettingsDict->getObject(whichSetting);
1452 if(obj) {
1453 obj->retain();
1454 }
1455 IORecursiveLockUnlock(settingsCtrlLock);
1456
1457 return obj;
1458}
1459
1460// *****************************************************************************
1461// registerPMSettingController (public)
1c79356b 1462//
0c530ab8
A
1463// direct wrapper to registerPMSettingController with uint32_t power source arg
1464// *****************************************************************************
1465IOReturn IOPMrootDomain::registerPMSettingController(
1466 const OSSymbol * settings[],
1467 IOPMSettingControllerCallback func,
1468 OSObject *target,
1469 uintptr_t refcon,
1470 OSObject **handle)
1471{
1472 return registerPMSettingController(
1473 settings,
1474 (kIOPMSupportedOnAC | kIOPMSupportedOnBatt | kIOPMSupportedOnUPS),
1475 func, target, refcon, handle);
1476}
1c79356b 1477
0c530ab8
A
1478// *****************************************************************************
1479// registerPMSettingController (public)
1480//
1481// Kexts may register for notifications when a particular setting is changed.
1482// A list of settings is available in IOPM.h.
1483// Arguments:
1484// * settings - An OSArray containing OSSymbols. Caller should populate this
1485// array with a list of settings caller wants notifications from.
1486// * func - A C function callback of the type IOPMSettingControllerCallback
1487// * target - caller may provide an OSObject *, which PM will pass as an
1488// target to calls to "func"
1489// * refcon - caller may provide an void *, which PM will pass as an
1490// argument to calls to "func"
1491// * handle - This is a return argument. We will populate this pointer upon
1492// call success. Hold onto this and pass this argument to
1493// IOPMrootDomain::deRegisterPMSettingCallback when unloading your kext
1494// Returns:
1495// kIOReturnSuccess on success
1496// *****************************************************************************
1497IOReturn IOPMrootDomain::registerPMSettingController(
1498 const OSSymbol * settings[],
1499 uint32_t supportedPowerSources,
1500 IOPMSettingControllerCallback func,
1501 OSObject *target,
1502 uintptr_t refcon,
1503 OSObject **handle)
1c79356b 1504{
0c530ab8
A
1505 PMSettingObject *pmso = NULL;
1506 OSArray *list = NULL;
1507 IOReturn ret = kIOReturnSuccess;
1508 int i;
1509
1510 if( NULL == settings ||
1511 NULL == func ||
1512 NULL == handle)
55e303ae 1513 {
0c530ab8 1514 return kIOReturnBadArgument;
4452a7af 1515 }
0c530ab8 1516
0c530ab8
A
1517 pmso = PMSettingObject::pmSettingObject(
1518 (IOPMrootDomain *)this, func, target,
1519 refcon, supportedPowerSources, settings);
1520
1521 if(!pmso) {
1522 ret = kIOReturnInternalError;
1523 goto bail_no_unlock;
1524 }
1525
1526 IORecursiveLockLock(settingsCtrlLock);
1527 for(i=0; settings[i]; i++)
55e303ae 1528 {
0c530ab8
A
1529 list = (OSArray *)settingsCallbacks->getObject(settings[i]);
1530 if(!list) {
1531 // New array of callbacks for this setting
1532 list = OSArray::withCapacity(1);
1533 settingsCallbacks->setObject(settings[i], list);
1534 list->release();
1535 }
1536
1537 // Add caller to the callback list
1538 list->setObject(pmso);
d52fe63f 1539 }
0c530ab8 1540
2d21ac55
A
1541 IORecursiveLockUnlock(settingsCtrlLock);
1542
0c530ab8
A
1543 ret = kIOReturnSuccess;
1544
1545 // Track this instance by its OSData ptr from now on
1546 *handle = pmso;
0c530ab8
A
1547
1548bail_no_unlock:
1549 if(kIOReturnSuccess != ret)
55e303ae 1550 {
0c530ab8
A
1551 // Error return case
1552 if(pmso) pmso->release();
1553 if(handle) *handle = NULL;
d52fe63f 1554 }
0c530ab8
A
1555 return ret;
1556}
1557
2d21ac55 1558
0c530ab8
A
1559//******************************************************************************
1560// sleepOnClamshellClosed
1561//
1562// contains the logic to determine if the system should sleep when the clamshell
1563// is closed.
1564//******************************************************************************
1565
1566bool IOPMrootDomain::shouldSleepOnClamshellClosed ( void )
1567{
1568 return ( !ignoringClamshell
1569 && !ignoringClamshellDuringWakeup
1570 && !(desktopMode && acAdaptorConnect) );
1571}
1572
1573void IOPMrootDomain::sendClientClamshellNotification ( void )
1574{
1575 /* Only broadcast clamshell alert if clamshell exists. */
1576 if(!clamshellExists)
1577 return;
1578
1579 setProperty(kAppleClamshellStateKey,
1580 clamshellIsClosed ? kOSBooleanTrue : kOSBooleanFalse);
1581
1582 setProperty(kAppleClamshellCausesSleepKey,
1583 shouldSleepOnClamshellClosed() ? kOSBooleanTrue : kOSBooleanFalse);
1584
1585
2d21ac55 1586 /* Argument to message is a bitfiel of
0c530ab8 1587 * ( kClamshellStateBit | kClamshellSleepBit )
0c530ab8 1588 */
2d21ac55
A
1589 messageClients(kIOPMMessageClamshellStateChange,
1590 (void *) ( (clamshellIsClosed ? kClamshellStateBit : 0)
1591 | ( shouldSleepOnClamshellClosed() ? kClamshellSleepBit : 0)) );
0c530ab8
A
1592}
1593
2d21ac55
A
1594//******************************************************************************
1595// informCPUStateChange
1596//
1597// Call into PM CPU code so that CPU power savings may dynamically adjust for
1598// running on battery, with the lid closed, etc.
1599//
1600// informCPUStateChange is a no-op on non x86 systems
1601// only x86 has explicit support in the IntelCPUPowerManagement kext
1602//******************************************************************************
1603
1604void IOPMrootDomain::informCPUStateChange(
1605 uint32_t type,
1606 uint32_t value )
1607{
1608#ifdef __i386__
1609
1610 pmioctlVariableInfo_t varInfoStruct;
1611 int pmCPUret = 0;
1612 const char *varNameStr = NULL;
1613 int32_t *varIndex = NULL;
1614
1615 if (kInformAC == type) {
1616 varNameStr = kIOPMRootDomainBatPowerCString;
1617 varIndex = &idxPMCPULimitedPower;
1618 } else if (kInformLid == type) {
1619 varNameStr = kIOPMRootDomainLidCloseCString;
1620 varIndex = &idxPMCPUClamshell;
1621 } else {
1622 return;
1623 }
1624
1625 // Set the new value!
1626 // pmCPUControl will assign us a new ID if one doesn't exist yet
1627 bzero(&varInfoStruct, sizeof(pmioctlVariableInfo_t));
1628 varInfoStruct.varID = *varIndex;
1629 varInfoStruct.varType = vBool;
1630 varInfoStruct.varInitValue = value;
1631 varInfoStruct.varCurValue = value;
1632 strncpy( (char *)varInfoStruct.varName,
1633 (const char *)varNameStr,
1634 strlen(varNameStr) + 1 );
1635
1636 // Set!
1637 pmCPUret = pmCPUControl( PMIOCSETVARINFO, (void *)&varInfoStruct );
1638
1639 // pmCPU only assigns numerical id's when a new varName is specified
1640 if ((0 == pmCPUret)
1641 && (*varIndex == kCPUUnknownIndex))
1642 {
1643 // pmCPUControl has assigned us a new variable ID.
1644 // Let's re-read the structure we just SET to learn that ID.
1645 pmCPUret = pmCPUControl( PMIOCGETVARNAMEINFO, (void *)&varInfoStruct );
1646
1647 if (0 == pmCPUret)
1648 {
1649 // Store it in idxPMCPUClamshell or idxPMCPULimitedPower
1650 *varIndex = varInfoStruct.varID;
1651 }
1652 }
1653
1654 return;
1655
1656#endif __i386__
1657}
1658
4a3eedf9
A
1659//******************************************************************************
1660// systemPowerEventOccurred
1661//
1662// The power controller is notifying us of a hardware-related power management
1663// event that we must handle.
1664//
1665// systemPowerEventOccurred covers the same functionality that receivePowerNotification
1666// does; it simply provides a richer API for conveying more information.
1667//******************************************************************************
1668IOReturn IOPMrootDomain::systemPowerEventOccurred(
1669 const OSSymbol *event,
1670 uint32_t intValue)
1671{
1672 IOReturn attempt = kIOReturnSuccess;
1673 OSNumber *newNumber = NULL;
1674
1675 if (!event)
1676 return kIOReturnBadArgument;
1677
1678 newNumber = OSNumber::withNumber(intValue, 8*sizeof(intValue));
1679 if (!newNumber)
1680 return kIOReturnInternalError;
1681
1682 attempt = systemPowerEventOccurred(event, (OSObject *)newNumber);
1683
1684 newNumber->release();
1685
1686 return attempt;
1687}
1688
1689IOReturn IOPMrootDomain::systemPowerEventOccurred(
1690 const OSSymbol *event,
1691 OSObject *value)
1692{
1693 OSDictionary *thermalsDict = NULL;
1694 bool shouldUpdate = true;
1695
1696 if (!event || !value)
1697 return kIOReturnBadArgument;
1698
1699 // LOCK
1700 // We reuse featuresDict Lock because it already exists and guards
1701 // the very infrequently used publish/remove feature mechanism; so there's zero rsk
1702 // of stepping on that lock.
1703 if (featuresDictLock) IOLockLock(featuresDictLock);
1704
1705 thermalsDict = (OSDictionary *)getProperty(kIOPMRootDomainPowerStatusKey);
1706
1707 if (thermalsDict && OSDynamicCast(OSDictionary, thermalsDict)) {
1708 thermalsDict = OSDictionary::withDictionary(thermalsDict);
1709 } else {
1710 thermalsDict = OSDictionary::withCapacity(1);
1711 }
1712
1713 if (!thermalsDict) {
1714 shouldUpdate = false;
1715 goto exit;
1716 }
1717
1718 thermalsDict->setObject (event, value);
1719
1720 setProperty (kIOPMRootDomainPowerStatusKey, thermalsDict);
1721
1722 thermalsDict->release();
1723
1724exit:
1725 // UNLOCK
1726 if (featuresDictLock) IOLockUnlock(featuresDictLock);
1727
1728 if (shouldUpdate)
1729 messageClients (kIOPMMessageSystemPowerEventOccurred, (void *)NULL);
1730
1731 return kIOReturnSuccess;
1732}
1733
2d21ac55 1734
0c530ab8
A
1735//******************************************************************************
1736// receivePowerNotification
1737//
1738// The power controller is notifying us of a hardware-related power management
4a3eedf9 1739// event that we must handle. This may be a result of an 'environment' interrupt from
0c530ab8
A
1740// the power mgt micro.
1741//******************************************************************************
1742
1743IOReturn IOPMrootDomain::receivePowerNotification (UInt32 msg)
1744{
1745 bool eval_clamshell = false;
1746
1747 /*
1748 * Local (IOPMrootDomain only) eval clamshell command
1749 */
1750 if (msg & kLocalEvalClamshellCommand)
55e303ae 1751 {
0c530ab8 1752 eval_clamshell = true;
d52fe63f 1753 }
0c530ab8
A
1754
1755 /*
1756 * Overtemp
1757 */
1758 if (msg & kIOPMOverTemp)
55e303ae 1759 {
0c530ab8 1760 IOLog("PowerManagement emergency overtemp signal. Going to sleep!");
2d21ac55
A
1761
1762 privateSleepSystem (kIOPMThermalEmergencySleepKey);
d52fe63f
A
1763 }
1764
0c530ab8
A
1765 /*
1766 * PMU Processor Speed Change
1767 */
55e303ae
A
1768 if (msg & kIOPMProcessorSpeedChange)
1769 {
1770 IOService *pmu = waitForService(serviceMatching("ApplePMU"));
1771 pmu->callPlatformFunction("prepareForSleep", false, 0, 0, 0, 0);
2d21ac55 1772 getPlatform()->sleepKernel();
55e303ae 1773 pmu->callPlatformFunction("recoverFromSleep", false, 0, 0, 0, 0);
d52fe63f
A
1774 }
1775
0c530ab8
A
1776 /*
1777 * Sleep Now!
1778 */
55e303ae
A
1779 if (msg & kIOPMSleepNow)
1780 {
2d21ac55 1781 privateSleepSystem (kIOPMSoftwareSleepKey);
1c79356b
A
1782 }
1783
0c530ab8
A
1784 /*
1785 * Power Emergency
1786 */
55e303ae
A
1787 if (msg & kIOPMPowerEmergency)
1788 {
2d21ac55 1789 privateSleepSystem (kIOPMLowPowerSleepKey);
1c79356b
A
1790 }
1791
0c530ab8
A
1792
1793 /*
1794 * Clamshell OPEN
1795 */
1796 if (msg & kIOPMClamshellOpened)
1797 {
1798 // Received clamshel open message from clamshell controlling driver
1799 // Update our internal state and tell general interest clients
1800 clamshellIsClosed = false;
1801 clamshellExists = true;
1802
2d21ac55
A
1803 // Tell PMCPU
1804 informCPUStateChange(kInformLid, 0);
1805
1806 // Tell general interest clients
0c530ab8
A
1807 sendClientClamshellNotification();
1808 }
1809
1810 /*
1811 * Clamshell CLOSED
1812 * Send the clamshell interest notification since the lid is closing.
1813 */
1814 if (msg & kIOPMClamshellClosed)
1815 {
1816 // Received clamshel open message from clamshell controlling driver
1817 // Update our internal state and tell general interest clients
1818 clamshellIsClosed = true;
1819 clamshellExists = true;
1820
2d21ac55
A
1821 // Tell PMCPU
1822 informCPUStateChange(kInformLid, 1);
1823
1824 // Tell general interest clients
0c530ab8
A
1825 sendClientClamshellNotification();
1826
1827 // And set eval_clamshell = so we can attempt
1828 eval_clamshell = true;
1829 }
1830
1831 /*
1832 * Set Desktop mode (sent from graphics)
1833 *
1834 * -> reevaluate lid state
1835 */
1836 if (msg & kIOPMSetDesktopMode)
4452a7af 1837 {
0c530ab8
A
1838 desktopMode = (0 != (msg & kIOPMSetValue));
1839 msg &= ~(kIOPMSetDesktopMode | kIOPMSetValue);
1840
1841 sendClientClamshellNotification();
1842
1843 // Re-evaluate the lid state
1844 if( clamshellIsClosed )
4452a7af 1845 {
0c530ab8
A
1846 eval_clamshell = true;
1847 }
1848 }
1849
1850 /*
1851 * AC Adaptor connected
1852 *
1853 * -> reevaluate lid state
1854 */
1855 if (msg & kIOPMSetACAdaptorConnected)
1856 {
1857 acAdaptorConnect = (0 != (msg & kIOPMSetValue));
1858 msg &= ~(kIOPMSetACAdaptorConnected | kIOPMSetValue);
4452a7af 1859
2d21ac55
A
1860 // Tell PMCPU
1861 informCPUStateChange(kInformAC, !acAdaptorConnect);
1862
0c530ab8
A
1863 sendClientClamshellNotification();
1864
1865 // Re-evaluate the lid state
1866 if( clamshellIsClosed )
1867 {
1868 eval_clamshell = true;
4452a7af 1869 }
0c530ab8
A
1870
1871 }
1872
1873 /*
1874 * Enable Clamshell (external display disappear)
1875 *
1876 * -> reevaluate lid state
1877 */
1878 if (msg & kIOPMEnableClamshell)
1879 {
1880 // Re-evaluate the lid state
1881 // System should sleep on external display disappearance
1882 // in lid closed operation.
1883 if( clamshellIsClosed && (true == ignoringClamshell) )
1884 {
1885 eval_clamshell = true;
1886 }
1887
1888 ignoringClamshell = false;
1889
1890 sendClientClamshellNotification();
1891 }
1892
1893 /*
1894 * Disable Clamshell (external display appeared)
1895 * We don't bother re-evaluating clamshell state. If the system is awake,
1896 * the lid is probably open.
1897 */
1898 if (msg & kIOPMDisableClamshell)
1899 {
1900 ignoringClamshell = true;
1901
1902 sendClientClamshellNotification();
1903 }
1904
1905 /*
1906 * Evaluate clamshell and SLEEP if appropiate
1907 */
1908 if ( eval_clamshell && shouldSleepOnClamshellClosed() )
1909 {
1910
1911
1912 // SLEEP!
2d21ac55 1913 privateSleepSystem (kIOPMClamshellSleepKey);
4452a7af
A
1914 }
1915
0c530ab8
A
1916 /*
1917 * Power Button
1918 */
55e303ae 1919 if (msg & kIOPMPowerButton)
2d21ac55 1920 {
55e303ae
A
1921 // toggle state of sleep/wake
1922 // are we dozing?
2d21ac55 1923 if ( getPowerState() == DOZE_STATE )
55e303ae
A
1924 {
1925 // yes, tell the tree we're waking
1926 systemWake();
1927 // wake the Display Wrangler
2d21ac55 1928 reportUserInput();
0b4e3aa0
A
1929 }
1930 else {
0c530ab8 1931 OSString *pbs = OSString::withCString("DisablePowerButtonSleep");
55e303ae 1932 // Check that power button sleep is enabled
0c530ab8
A
1933 if( pbs ) {
1934 if( kOSBooleanTrue != getProperty(pbs))
2d21ac55 1935 privateSleepSystem (kIOPMPowerButtonSleepKey);
0c530ab8 1936 }
1c79356b 1937 }
0b4e3aa0
A
1938 }
1939
0c530ab8
A
1940 /*
1941 * Allow Sleep
1942 *
1943 */
55e303ae
A
1944 if ( (msg & kIOPMAllowSleep) && !allowSleep )
1945 {
1946 allowSleep = true;
0b4e3aa0 1947 adjustPowerState();
1c79356b
A
1948 }
1949
0c530ab8
A
1950 /*
1951 * Prevent Sleep
1952 *
1953 */
1c79356b 1954 if (msg & kIOPMPreventSleep) {
55e303ae
A
1955 allowSleep = false;
1956 // are we dozing?
2d21ac55 1957 if ( getPowerState() == DOZE_STATE ) {
55e303ae
A
1958 // yes, tell the tree we're waking
1959 systemWake();
0b4e3aa0 1960 adjustPowerState();
55e303ae
A
1961 // wake the Display Wrangler
1962 reportUserInput();
1963 } else {
0b4e3aa0 1964 adjustPowerState();
55e303ae
A
1965 // make sure we have power to clamp
1966 patriarch->wakeSystem();
0b4e3aa0 1967 }
1c79356b
A
1968 }
1969
1970 return 0;
1971}
1972
1973
1974//*********************************************************************************
1975// sleepSupported
1976//
1977//*********************************************************************************
1978
1979void IOPMrootDomain::setSleepSupported( IOOptionBits flags )
1980{
55e303ae
A
1981 if ( flags & kPCICantSleep )
1982 {
0b4e3aa0 1983 canSleep = false;
55e303ae 1984 } else {
0c530ab8 1985 canSleep = true;
0b4e3aa0
A
1986 platformSleepSupport = flags;
1987 }
1988
0c530ab8
A
1989 setProperty(kIOSleepSupportedKey, canSleep);
1990
0b4e3aa0
A
1991}
1992
1993//*********************************************************************************
1994// requestPowerDomainState
1995//
1996// The root domain intercepts this call to the superclass.
2d21ac55 1997// Called on the PM work loop thread.
0b4e3aa0
A
1998//
1999// If the clamp bit is not set in the desire, then the child doesn't need the power
2000// state it's requesting; it just wants it. The root ignores desires but not needs.
2001// If the clamp bit is not set, the root takes it that the child can tolerate no
2002// power and interprets the request accordingly. If all children can thus tolerate
2003// no power, we are on our way to idle sleep.
2004//*********************************************************************************
2005
2d21ac55
A
2006IOReturn IOPMrootDomain::requestPowerDomainState (
2007 IOPMPowerFlags desiredState,
2008 IOPowerConnection * whichChild,
2009 unsigned long specification )
0b4e3aa0 2010{
2d21ac55
A
2011 OSIterator *iter;
2012 OSObject *next;
2013 IOPowerConnection *connection;
2014 unsigned long powerRequestFlag = 0;
2015 IOPMPowerFlags editedDesire;
2016
2017#if DEBUG
2018 IOService *powerChild;
2019 powerChild = (IOService *) whichChild->getChildEntry(gIOPowerPlane);
2020#endif
55e303ae 2021
2d21ac55
A
2022 DEBUG_LOG("RequestPowerDomainState: flags %lx, child %p [%s], spec %lx\n",
2023 desiredState, powerChild, powerChild ? powerChild->getName() : "?",
2024 specification);
2025
2026 // Force the child's input power requirements to 0 unless the prevent
2027 // idle-sleep flag is set. No input power flags map to our state 0.
2028 // Our power clamp (deviceDesire) keeps the minimum power state at 2.
0b4e3aa0 2029
2d21ac55
A
2030 if (desiredState & kIOPMPreventIdleSleep)
2031 editedDesire = desiredState;
2032 else
2033 editedDesire = 0;
0b4e3aa0 2034
2d21ac55
A
2035 // Recompute sleep supported flag (doze if not supported)
2036 sleepIsSupported = true;
55e303ae 2037
0b4e3aa0 2038 iter = getChildIterator(gIOPowerPlane);
55e303ae
A
2039 if ( iter )
2040 {
2041 while ( (next = iter->getNextObject()) )
2042 {
2d21ac55 2043 if ( (connection = OSDynamicCast(IOPowerConnection, next)) )
55e303ae 2044 {
2d21ac55
A
2045 // Ignore child that are in the process of joining.
2046 if (connection->getReadyFlag() == false)
2047 continue;
2048
2049 // Is this connection attached to the child that called
2050 // requestPowerDomainState()?
2051
55e303ae
A
2052 if ( connection == whichChild )
2053 {
2d21ac55
A
2054 // Yes, OR in the child's input power requirements.
2055 powerRequestFlag |= editedDesire;
2056
2057 if ( desiredState & kIOPMPreventSystemSleep )
0b4e3aa0 2058 sleepIsSupported = false;
2d21ac55
A
2059 }
2060 else
2061 {
2062#if DEBUG
2063 powerChild = (IOService *) connection->getChildEntry(gIOPowerPlane);
2064#endif
2065 DEBUG_LOG(" child %p, PState %ld, noIdle %d, noSleep %d, valid %d %s\n",
2066 powerChild,
2067 connection->getDesiredDomainState(),
2068 connection->getPreventIdleSleepFlag(),
2069 connection->getPreventSystemSleepFlag(),
2070 connection->getReadyFlag(),
2071 powerChild ? powerChild->getName() : "?");
2072
2073 // No, OR in the child's desired power domain state.
2074 // Which is our power state desired by this child.
2075 powerRequestFlag |= connection->getDesiredDomainState();
2076
2077 if ( connection->getPreventSystemSleepFlag() )
0b4e3aa0 2078 sleepIsSupported = false;
0b4e3aa0
A
2079 }
2080 }
2081 }
2082 iter->release();
2083 }
9bccf70c 2084
2d21ac55 2085 if ( !powerRequestFlag && !systemBooting )
55e303ae 2086 {
2d21ac55
A
2087 if (!wrangler)
2088 {
2089 sleepASAP = false;
2090 changePowerStateToPriv(ON_STATE);
2091 if (idleSeconds)
2092 {
2093 AbsoluteTime deadline;
2094 // stay awake for at least idleSeconds
2095 clock_interval_to_deadline(idleSeconds, kSecondScale, &deadline);
2096 thread_call_enter_delayed(extraSleepTimer, deadline);
2097 // this gets turned off when we sleep again
2098 idleSleepPending = true;
2099 }
2100 }
2101 else if (extraSleepDelay == 0)
2102 {
2103 sleepASAP = true;
2104 }
1c79356b 2105 }
0b4e3aa0 2106
2d21ac55
A
2107 DEBUG_LOG(" sleepDelay %lx, mergedFlags %lx, sleepASAP %x, booting %x\n",
2108 extraSleepDelay, powerRequestFlag, sleepASAP, systemBooting);
2109
2110 // Drop our power clamp to SLEEP_STATE when all devices become idle.
2111 // Needed when the system sleep and display sleep timeouts are the same.
2112 // Otherwise, the extra sleep timer will also drop our power clamp.
2113
55e303ae 2114 adjustPowerState();
0b4e3aa0 2115
2d21ac55 2116 editedDesire |= (desiredState & kIOPMPreventSystemSleep);
1c79356b 2117
2d21ac55
A
2118 // If our power clamp has already dropped to SLEEP_STATE, and no child
2119 // is keeping us at max power, then this will trigger idle sleep.
2120
2121 return super::requestPowerDomainState(editedDesire, whichChild, specification);
1c79356b
A
2122}
2123
0b4e3aa0 2124
1c79356b
A
2125//*********************************************************************************
2126// getSleepSupported
2127//
2128//*********************************************************************************
2129
2130IOOptionBits IOPMrootDomain::getSleepSupported( void )
2131{
2132 return( platformSleepSupport );
2133}
2134
2135
2d21ac55
A
2136//*********************************************************************************
2137// handlePlatformHaltRestart
2138//
2139//*********************************************************************************
2140
2141struct HaltRestartApplierContext {
2142 IOPMrootDomain * RootDomain;
2143 unsigned long PowerState;
2144 IOPMPowerFlags PowerFlags;
2145 UInt32 MessageType;
2146 UInt32 Counter;
2147};
2148
2149static void
2150platformHaltRestartApplier( OSObject * object, void * context )
2151{
2152 IOPowerStateChangeNotification notify;
2153 HaltRestartApplierContext * ctx;
2154 AbsoluteTime startTime;
2155 UInt32 deltaTime;
2156
2157 ctx = (HaltRestartApplierContext *) context;
2158
2159 memset(&notify, 0, sizeof(notify));
2160 notify.powerRef = (void *)ctx->Counter;
2161 notify.returnValue = 0;
2162 notify.stateNumber = ctx->PowerState;
2163 notify.stateFlags = ctx->PowerFlags;
2164
2165 clock_get_uptime(&startTime);
2166 ctx->RootDomain->messageClient( ctx->MessageType, object, (void *)&notify );
2167 deltaTime = computeDeltaTimeMS(&startTime);
2168
2169 if ((deltaTime > kPMHaltTimeoutMS) || (gIOKitDebug & kIOLogDebugPower))
2170 {
2171 _IOServiceInterestNotifier * notifier;
2172 notifier = OSDynamicCast(_IOServiceInterestNotifier, object);
2173
2174 // IOService children of IOPMrootDomain are not instrumented.
2175 // Only IORootParent currently falls under that group.
2176
2177 if (notifier)
2178 {
2179 HaltRestartLog("%s handler %p took %lu ms\n",
2180 (ctx->MessageType == kIOMessageSystemWillPowerOff) ?
2181 "PowerOff" : "Restart",
2182 notifier->handler, deltaTime );
2183 }
2184 }
2185
2186 ctx->Counter++;
2187}
2188
2189void IOPMrootDomain::handlePlatformHaltRestart( UInt32 pe_type )
2190{
2191 HaltRestartApplierContext ctx;
2192 AbsoluteTime startTime;
2193 UInt32 deltaTime;
2194
2195 memset(&ctx, 0, sizeof(ctx));
2196 ctx.RootDomain = this;
2197
2198 clock_get_uptime(&startTime);
2199 switch (pe_type)
2200 {
2201 case kPEHaltCPU:
2202 ctx.PowerState = OFF_STATE;
2203 ctx.MessageType = kIOMessageSystemWillPowerOff;
2204 break;
2205
2206 case kPERestartCPU:
2207 ctx.PowerState = RESTART_STATE;
2208 ctx.MessageType = kIOMessageSystemWillRestart;
2209 break;
2210
2211 default:
2212 return;
2213 }
2214
2215 // Notify legacy clients
2216 applyToInterested(gIOPriorityPowerStateInterest, platformHaltRestartApplier, &ctx);
2217
2218 // Notify in power tree order
2219 notifySystemShutdown(this, ctx.MessageType);
2220
2221 deltaTime = computeDeltaTimeMS(&startTime);
2222 HaltRestartLog("%s all drivers took %lu ms\n",
2223 (ctx.MessageType == kIOMessageSystemWillPowerOff) ?
2224 "PowerOff" : "Restart",
2225 deltaTime );
2226}
2227
2228
1c79356b
A
2229//*********************************************************************************
2230// tellChangeDown
2231//
2232// We override the superclass implementation so we can send a different message
2233// type to the client or application being notified.
2234//*********************************************************************************
2235
2236bool IOPMrootDomain::tellChangeDown ( unsigned long stateNum )
2237{
0b4e3aa0
A
2238 switch ( stateNum ) {
2239 case DOZE_STATE:
2240 case SLEEP_STATE:
0c530ab8
A
2241
2242 // Direct callout into OSMetaClass so it can disable kmod unloads
2243 // during sleep/wake to prevent deadlocks.
2244 OSMetaClassSystemSleepOrWake( kIOMessageSystemWillSleep );
2245
0b4e3aa0 2246 return super::tellClientsWithResponse(kIOMessageSystemWillSleep);
1c79356b 2247 }
55e303ae 2248 return super::tellChangeDown(stateNum);
1c79356b
A
2249}
2250
2251
2252//*********************************************************************************
2253// askChangeDown
2254//
2255// We override the superclass implementation so we can send a different message
2256// type to the client or application being notified.
0b4e3aa0
A
2257//
2258// This must be idle sleep since we don't ask apps during any other power change.
1c79356b
A
2259//*********************************************************************************
2260
0b4e3aa0 2261bool IOPMrootDomain::askChangeDown ( unsigned long )
1c79356b 2262{
0b4e3aa0 2263 return super::tellClientsWithResponse(kIOMessageCanSystemSleep);
1c79356b
A
2264}
2265
2266
2267//*********************************************************************************
2268// tellNoChangeDown
2269//
2270// Notify registered applications and kernel clients that we are not
2271// dropping power.
2272//
2273// We override the superclass implementation so we can send a different message
2274// type to the client or application being notified.
0b4e3aa0
A
2275//
2276// This must be a vetoed idle sleep, since no other power change can be vetoed.
1c79356b
A
2277//*********************************************************************************
2278
2279void IOPMrootDomain::tellNoChangeDown ( unsigned long )
2280{
2d21ac55
A
2281 if (idleSeconds && !wrangler)
2282 {
2283 AbsoluteTime deadline;
2284 sleepASAP = false;
2285 // stay awake for at least idleSeconds
2286 clock_interval_to_deadline(idleSeconds, kSecondScale, &deadline);
2287 thread_call_enter_delayed(extraSleepTimer, deadline);
2288 // this gets turned off when we sleep again
2289 idleSleepPending = true;
2290 }
1c79356b
A
2291 return tellClients(kIOMessageSystemWillNotSleep);
2292}
2293
2294
2295//*********************************************************************************
2296// tellChangeUp
2297//
2298// Notify registered applications and kernel clients that we are raising power.
2299//
2300// We override the superclass implementation so we can send a different message
2301// type to the client or application being notified.
2302//*********************************************************************************
2303
7b1edb79 2304void IOPMrootDomain::tellChangeUp ( unsigned long stateNum)
1c79356b 2305{
55e303ae
A
2306 if ( stateNum == ON_STATE )
2307 {
2d21ac55 2308#if HIBERNATION
0c530ab8
A
2309 // Direct callout into OSMetaClass so it can disable kmod unloads
2310 // during sleep/wake to prevent deadlocks.
2311 OSMetaClassSystemSleepOrWake( kIOMessageSystemHasPoweredOn );
2312
3a60a9f5 2313 IOHibernateSystemPostWake();
2d21ac55 2314#endif
7b1edb79
A
2315 return tellClients(kIOMessageSystemHasPoweredOn);
2316 }
1c79356b
A
2317}
2318
0b4e3aa0
A
2319//*********************************************************************************
2320// reportUserInput
2321//
2322//*********************************************************************************
2323
2324void IOPMrootDomain::reportUserInput ( void )
2325{
2d21ac55 2326#if !NO_KERNEL_HID
0b4e3aa0 2327 OSIterator * iter;
1c79356b 2328
55e303ae
A
2329 if(!wrangler)
2330 {
0b4e3aa0 2331 iter = getMatchingServices(serviceMatching("IODisplayWrangler"));
55e303ae
A
2332 if(iter)
2333 {
0b4e3aa0
A
2334 wrangler = (IOService *) iter->getNextObject();
2335 iter->release();
2336 }
2337 }
2338
2339 if(wrangler)
2340 wrangler->activityTickle(0,0);
2d21ac55 2341#endif
0b4e3aa0
A
2342}
2343
2344//*********************************************************************************
2345// setQuickSpinDownTimeout
1c79356b 2346//
0b4e3aa0
A
2347//*********************************************************************************
2348
2349void IOPMrootDomain::setQuickSpinDownTimeout ( void )
2350{
0b4e3aa0
A
2351 super::setAggressiveness((unsigned long)kPMMinutesToSpinDown,(unsigned long)1);
2352}
2353
2354//*********************************************************************************
2355// restoreUserSpinDownTimeout
2356//
2357//*********************************************************************************
2358
2359void IOPMrootDomain::restoreUserSpinDownTimeout ( void )
2360{
0b4e3aa0
A
2361 super::setAggressiveness((unsigned long)kPMMinutesToSpinDown,(unsigned long)user_spindown);
2362}
2363
9bccf70c
A
2364//*********************************************************************************
2365// changePowerStateTo & changePowerStateToPriv
2366//
2367// Override of these methods for logging purposes.
2368//*********************************************************************************
2369
2370IOReturn IOPMrootDomain::changePowerStateTo ( unsigned long ordinal )
2371{
9bccf70c
A
2372 return super::changePowerStateTo(ordinal);
2373}
2374
2375IOReturn IOPMrootDomain::changePowerStateToPriv ( unsigned long ordinal )
2376{
0c530ab8
A
2377 IOReturn ret;
2378
2d21ac55
A
2379 DEBUG_LOG("ChangePowerStateToPriv: power state %ld\n", ordinal);
2380
2381 if ( (getPowerState() == DOZE_STATE) && (ordinal != ON_STATE) )
2382 {
2383 return kIOReturnSuccess;
2384 }
2385
2386 if( (userDisabledAllSleep || systemBooting || systemShutdown)
2387 && (ordinal == SLEEP_STATE) )
0c530ab8 2388 {
2d21ac55
A
2389 DEBUG_LOG(" sleep denied: disableAllSleep %d, booting %d, shutdown %d\n",
2390 userDisabledAllSleep, systemBooting, systemShutdown);
0c530ab8
A
2391 super::changePowerStateToPriv(ON_STATE);
2392 }
2393
2394 if( (SLEEP_STATE == ordinal) && sleepSupportedPEFunction )
2395 {
2396
2397 // Determine if the machine supports sleep, or must doze.
2398 ret = getPlatform()->callPlatformFunction(
2399 sleepSupportedPEFunction, false,
2400 NULL, NULL, NULL, NULL);
2401
2402 // If the machine only supports doze, the callPlatformFunction call
2d21ac55 2403 // boils down to IOPMrootDomain::setSleepSupported(kPCICantSleep),
0c530ab8
A
2404 // otherwise nothing.
2405 }
2406
9bccf70c
A
2407 return super::changePowerStateToPriv(ordinal);
2408}
2409
0b4e3aa0
A
2410
2411//*********************************************************************************
2412// sysPowerDownHandler
2413//
2414// Receives a notification when the RootDomain changes state.
2415//
2416// Allows us to take action on system sleep, power down, and restart after
2417// applications have received their power change notifications and replied,
2418// but before drivers have powered down. We perform a vfs sync on power down.
2419//*********************************************************************************
2420
2421IOReturn IOPMrootDomain::sysPowerDownHandler( void * target, void * refCon,
2422 UInt32 messageType, IOService * service,
2423 void * messageArgument, vm_size_t argSize )
2424{
55e303ae
A
2425 IOReturn ret;
2426 IOPowerStateChangeNotification *params = (IOPowerStateChangeNotification *) messageArgument;
2427 IOPMrootDomain *rootDomain = OSDynamicCast(IOPMrootDomain, service);
0b4e3aa0
A
2428
2429 if(!rootDomain)
2430 return kIOReturnUnsupported;
2431
2432 switch (messageType) {
2433 case kIOMessageSystemWillSleep:
2d21ac55 2434 DEBUG_LOG("SystemWillSleep\n");
0b4e3aa0
A
2435
2436 // Interested applications have been notified of an impending power
2437 // change and have acked (when applicable).
2438 // This is our chance to save whatever state we can before powering
2439 // down.
2440 // We call sync_internal defined in xnu/bsd/vfs/vfs_syscalls.c,
2441 // via callout
2442
2443 // We will ack within 20 seconds
2444 params->returnValue = 20 * 1000 * 1000;
2d21ac55 2445#if HIBERNATION
3a60a9f5
A
2446 if (gIOHibernateState)
2447 params->returnValue += gIOHibernateFreeTime * 1000; //add in time we could spend freeing pages
2d21ac55 2448#endif
0b4e3aa0
A
2449
2450 if ( ! OSCompareAndSwap( 0, 1, &gSleepOrShutdownPending ) )
2451 {
2452 // Purposely delay the ack and hope that shutdown occurs quickly.
2453 // Another option is not to schedule the thread and wait for
2454 // ack timeout...
2455 AbsoluteTime deadline;
9bccf70c 2456 clock_interval_to_deadline( 30, kSecondScale, &deadline );
0b4e3aa0
A
2457 thread_call_enter1_delayed( rootDomain->diskSyncCalloutEntry,
2458 (thread_call_param_t)params->powerRef,
2459 deadline );
2460 }
2461 else
2462 thread_call_enter1(rootDomain->diskSyncCalloutEntry, (thread_call_param_t)params->powerRef);
2463 ret = kIOReturnSuccess;
2464 break;
fa4905b1
A
2465
2466 case kIOMessageSystemWillPowerOff:
2467 case kIOMessageSystemWillRestart:
d52fe63f 2468 ret = kIOReturnUnsupported;
fa4905b1
A
2469 break;
2470
0b4e3aa0
A
2471 default:
2472 ret = kIOReturnUnsupported;
2473 break;
2474 }
2475 return ret;
2476}
2477
2478//*********************************************************************************
2479// displayWranglerNotification
2480//
2481// Receives a notification when the IODisplayWrangler changes state.
2482//
2483// Allows us to take action on display dim/undim.
2484//
2d21ac55 2485// When the display sleeps we:
0b4e3aa0
A
2486// - Start the idle sleep timer
2487// - set the quick spin down timeout
2488//
2d21ac55 2489// On wake from display sleep:
0b4e3aa0
A
2490// - Cancel the idle sleep timer
2491// - restore the user's chosen spindown timer from the "quick" spin down value
2492//*********************************************************************************
1c79356b 2493
2d21ac55
A
2494IOReturn IOPMrootDomain::displayWranglerNotification(
2495 void * target, void * refCon,
2496 UInt32 messageType, IOService * service,
2497 void * messageArgument, vm_size_t argSize )
1c79356b 2498{
2d21ac55
A
2499#if !NO_KERNEL_HID
2500 IOPMrootDomain * rootDomain = OSDynamicCast(IOPMrootDomain, (IOService *)target);
2501 AbsoluteTime deadline;
2502 static int displayPowerState = 4;
0b4e3aa0 2503
2d21ac55 2504 if (!rootDomain)
0b4e3aa0
A
2505 return kIOReturnUnsupported;
2506
2507 switch (messageType) {
2508 case kIOMessageDeviceWillPowerOff:
2d21ac55
A
2509 DEBUG_LOG("DisplayWranglerWillPowerOff: new p-state %d\n",
2510 displayPowerState - 1);
2511
2512 // The display wrangler has dropped power because of idle display sleep
2513 // or force system sleep. We will receive 4 messages before the display
2514 // wrangler reaches its lowest state. Act only when going to state 2.
2515 //
2516 // 4->3 Display Dim
2517 // 3->2 Display Sleep
2518 // 2->1 Not visible to user
2519 // 1->0 Not visible to user
2520
2521 displayPowerState--;
2522 if ( 2 != displayPowerState )
2523 return kIOReturnUnsupported;
2524
2525 // We start a timer here if the System Sleep timer is greater than the
2526 // Display Sleep timer. We kick off this timer when the display sleeps.
2527 //
2528 // Note that, although Display Dim timings may change adaptively accordingly
2529 // to the user's activity patterns, Display Sleep _always_ occurs at the
2530 // specified interval since last user activity.
2531
2532 if ( rootDomain->extraSleepDelay )
2533 {
2534 clock_interval_to_deadline(rootDomain->extraSleepDelay*60, kSecondScale, &deadline);
0b4e3aa0
A
2535 thread_call_enter_delayed(rootDomain->extraSleepTimer, deadline);
2536 rootDomain->idleSleepPending = true;
2d21ac55
A
2537 DEBUG_LOG(" sleep timer set to expire in %ld min\n",
2538 rootDomain->extraSleepDelay);
0b4e3aa0 2539 } else {
2d21ac55
A
2540 // Accelerate disk spindown if system sleep and display sleep
2541 // sliders are set to the same value (e.g. both set to 5 min),
2542 // and display is about to go dark. Check that spin down timer
2543 // is non-zero (zero = never spin down) and system sleep is
2544 // not set to never sleep.
2545
2546 if ( (0 != rootDomain->user_spindown) && (0 != rootDomain->sleepSlider) )
2547 {
2548 DEBUG_LOG(" accelerate quick disk spindown, was %d min\n",
2549 rootDomain->user_spindown);
0b4e3aa0 2550 rootDomain->setQuickSpinDownTimeout();
2d21ac55 2551 }
0b4e3aa0
A
2552 }
2553
2d21ac55 2554 break;
0b4e3aa0
A
2555
2556 case kIOMessageDeviceHasPoweredOn:
2d21ac55
A
2557 DEBUG_LOG("DisplayWranglerHasPoweredOn: previous p-state %d\n",
2558 displayPowerState);
2559
2560 // The display wrangler has powered on either because of user activity
2561 // or wake from sleep/doze.
0b4e3aa0 2562
2d21ac55 2563 displayPowerState = 4;
0b4e3aa0 2564 rootDomain->adjustPowerState();
0b4e3aa0 2565
2d21ac55
A
2566 // cancel any pending idle sleep timers
2567 if (rootDomain->idleSleepPending)
55e303ae 2568 {
2d21ac55 2569 DEBUG_LOG(" extra-sleep timer stopped\n");
0b4e3aa0
A
2570 thread_call_cancel(rootDomain->extraSleepTimer);
2571 rootDomain->idleSleepPending = false;
2572 }
2573
2d21ac55
A
2574 // Change the spindown value back to the user's selection from our
2575 // accelerated setting.
2576 if (0 != rootDomain->user_spindown)
2577 {
2578 DEBUG_LOG(" restoring disk spindown to %d min\n",
2579 rootDomain->user_spindown);
0b4e3aa0 2580 rootDomain->restoreUserSpinDownTimeout();
2d21ac55 2581 }
0b4e3aa0
A
2582
2583 break;
2584
2585 default:
2586 break;
2587 }
2d21ac55 2588#endif
0b4e3aa0
A
2589 return kIOReturnUnsupported;
2590 }
2591
2592//*********************************************************************************
2593// displayWranglerPublished
2594//
2595// Receives a notification when the IODisplayWrangler is published.
2596// When it's published we install a power state change handler.
2597//
2598//*********************************************************************************
2599
0c530ab8
A
2600bool IOPMrootDomain::displayWranglerPublished(
2601 void * target,
2602 void * refCon,
2603 IOService * newService)
0b4e3aa0 2604{
2d21ac55 2605#if !NO_KERNEL_HID
0c530ab8
A
2606 IOPMrootDomain *rootDomain =
2607 OSDynamicCast(IOPMrootDomain, (IOService *)target);
0b4e3aa0
A
2608
2609 if(!rootDomain)
2610 return false;
2611
55e303ae
A
2612 rootDomain->wrangler = newService;
2613
2614 // we found the display wrangler, now install a handler
0c530ab8
A
2615 if( !rootDomain->wrangler->registerInterest( gIOGeneralInterest,
2616 &displayWranglerNotification, target, 0) )
2617 {
55e303ae
A
2618 return false;
2619 }
2d21ac55 2620#endif
55e303ae 2621 return true;
1c79356b
A
2622}
2623
483a1d10 2624//*********************************************************************************
0c530ab8 2625// batteryPublished
483a1d10 2626//
0c530ab8 2627// Notification on battery class IOPowerSource appearance
483a1d10 2628//
0c530ab8
A
2629//******************************************************************************
2630
2631bool IOPMrootDomain::batteryPublished(
2632 void * target,
2633 void * root_domain,
2634 IOService * resourceService )
2635{
2636 // rdar://2936060&4435589
91447636
A
2637 // All laptops have dimmable LCD displays
2638 // All laptops have batteries
2639 // So if this machine has a battery, publish the fact that the backlight
2640 // supports dimming.
2641 ((IOPMrootDomain *)root_domain)->publishFeature("DisplayDims");
0c530ab8 2642
483a1d10
A
2643 return (true);
2644}
2645
0b4e3aa0
A
2646//*********************************************************************************
2647// adjustPowerState
2648//
2649// Some condition that affects our wake/sleep/doze decision has changed.
2650//
2651// If the sleep slider is in the off position, we cannot sleep or doze.
2652// If the enclosure is open, we cannot sleep or doze.
2653// If the system is still booting, we cannot sleep or doze.
2654//
2655// In those circumstances, we prevent sleep and doze by holding power on with
2656// changePowerStateToPriv(ON).
2657//
2658// If the above conditions do not exist, and also the sleep timer has expired, we
2659// allow sleep or doze to occur with either changePowerStateToPriv(SLEEP) or
2660// changePowerStateToPriv(DOZE) depending on whether or not we already know the
2661// platform cannot sleep.
2662//
2663// In this case, sleep or doze will either occur immediately or at the next time
2664// that no children are holding the system out of idle sleep via the
2665// kIOPMPreventIdleSleep flag in their power state arrays.
2666//*********************************************************************************
2667
2668void IOPMrootDomain::adjustPowerState( void )
2669{
0c530ab8
A
2670 if ( (sleepSlider == 0)
2671 || !allowSleep
2672 || systemBooting
2d21ac55
A
2673 || systemShutdown
2674 || userDisabledAllSleep )
0c530ab8 2675 {
2d21ac55
A
2676 DEBUG_LOG("AdjustPowerState %ld -> ON: slider %ld, allowSleep %d, "
2677 "booting %d, shutdown %d, userDisabled %d\n",
2678 getPowerState(), sleepSlider, allowSleep, systemBooting,
2679 systemShutdown, userDisabledAllSleep);
2680
0b4e3aa0 2681 changePowerStateToPriv(ON_STATE);
55e303ae
A
2682 } else {
2683 if ( sleepASAP )
2684 {
2d21ac55
A
2685 DEBUG_LOG("AdjustPowerState SLEEP\n");
2686
2687 /* Convenient place to run any code at idle sleep time
2688 * IOPMrootDomain initiates an idle sleep here
2689 *
2690 * Set last sleep cause accordingly.
2691 */
2692 setProperty(kRootDomainSleepReasonKey, kIOPMIdleSleepKey);
2693
0b4e3aa0 2694 sleepASAP = false;
2d21ac55
A
2695 if ( !sleepIsSupported )
2696 {
0c530ab8 2697 setSleepSupported( kPCICantSleep );
2d21ac55
A
2698 kprintf("Sleep prevented by kIOPMPreventSystemSleep flag\n");
2699 }
0c530ab8
A
2700 changePowerStateToPriv(SLEEP_STATE);
2701 }
2702 }
2703}
2704
2d21ac55
A
2705//*********************************************************************************
2706// PMHaltWorker Class
2707//
2708//*********************************************************************************
2709
2710static unsigned int gPMHaltBusyCount;
2711static unsigned int gPMHaltIdleCount;
2712static int gPMHaltDepth;
2713static unsigned long gPMHaltEvent;
2714static IOLock * gPMHaltLock = 0;
2715static OSArray * gPMHaltArray = 0;
2716static const OSSymbol * gPMHaltClientAcknowledgeKey = 0;
2717
2718PMHaltWorker * PMHaltWorker::worker( void )
2719{
2720 PMHaltWorker * me;
2721 IOThread thread;
2722
2723 do {
2724 me = OSTypeAlloc( PMHaltWorker );
2725 if (!me || !me->init())
2726 break;
2727
2728 me->lock = IOLockAlloc();
2729 if (!me->lock)
2730 break;
2731
2732 DEBUG_LOG("PMHaltWorker %p\n", me);
2733 me->retain(); // thread holds extra retain
2734 thread = IOCreateThread( &PMHaltWorker::main, me );
2735 if (!thread)
2736 {
2737 me->release();
2738 break;
2739 }
2740 return me;
2741
2742 } while (false);
2743
2744 if (me) me->release();
2745 return 0;
2746}
2747
2748void PMHaltWorker::free( void )
2749{
2750 DEBUG_LOG("PMHaltWorker free %p\n", this);
2751 if (lock)
2752 {
2753 IOLockFree(lock);
2754 lock = 0;
2755 }
2756 return OSObject::free();
2757}
2758
2759void PMHaltWorker::main( void * arg )
2760{
2761 PMHaltWorker * me = (PMHaltWorker *) arg;
2762
2763 IOLockLock( gPMHaltLock );
2764 gPMHaltBusyCount++;
2765 me->depth = gPMHaltDepth;
2766 IOLockUnlock( gPMHaltLock );
2767
2768 while (me->depth >= 0)
2769 {
2770 PMHaltWorker::work( me );
2771
2772 IOLockLock( gPMHaltLock );
2773 if (++gPMHaltIdleCount >= gPMHaltBusyCount)
2774 {
2775 // This is the last thread to finish work on this level,
2776 // inform everyone to start working on next lower level.
2777 gPMHaltDepth--;
2778 me->depth = gPMHaltDepth;
2779 gPMHaltIdleCount = 0;
2780 thread_wakeup((event_t) &gPMHaltIdleCount);
2781 }
2782 else
2783 {
2784 // One or more threads are still working on this level,
2785 // this thread must wait.
2786 me->depth = gPMHaltDepth - 1;
2787 do {
2788 IOLockSleep(gPMHaltLock, &gPMHaltIdleCount, THREAD_UNINT);
2789 } while (me->depth != gPMHaltDepth);
2790 }
2791 IOLockUnlock( gPMHaltLock );
2792 }
2793
2794 // No more work to do, terminate thread
2795 DEBUG_LOG("All done for worker: %p (visits = %u)\n", me, me->visits);
2796 thread_wakeup( &gPMHaltDepth );
2797 me->release();
2798}
2799
2800void PMHaltWorker::work( PMHaltWorker * me )
2801{
2802 IOService * service;
2803 OSSet * inner;
2804 AbsoluteTime startTime;
2805 UInt32 deltaTime;
2806 bool timeout;
2807
2808 while (true)
2809 {
2810 service = 0;
2811 timeout = false;
2812
2813 // Claim an unit of work from the shared pool
2814 IOLockLock( gPMHaltLock );
2815 inner = (OSSet *)gPMHaltArray->getObject(me->depth);
2816 if (inner)
2817 {
2818 service = (IOService *)inner->getAnyObject();
2819 if (service)
2820 {
2821 service->retain();
2822 inner->removeObject(service);
2823 }
2824 }
2825 IOLockUnlock( gPMHaltLock );
2826 if (!service)
2827 break; // no more work at this depth
2828
2829 clock_get_uptime(&startTime);
2830
2831 if (!service->isInactive() &&
2832 service->setProperty(gPMHaltClientAcknowledgeKey, me))
2833 {
2834 IOLockLock(me->lock);
2835 me->startTime = startTime;
2836 me->service = service;
2837 me->timeout = false;
2838 IOLockUnlock(me->lock);
2839
2840 service->systemWillShutdown( gPMHaltEvent );
2841
2842 // Wait for driver acknowledgement
2843 IOLockLock(me->lock);
2844 while (service->getProperty(gPMHaltClientAcknowledgeKey))
2845 {
2846 IOLockSleep(me->lock, me, THREAD_UNINT);
2847 }
2848 me->service = 0;
2849 timeout = me->timeout;
2850 IOLockUnlock(me->lock);
2851 }
2852
2853 deltaTime = computeDeltaTimeMS(&startTime);
2854 if ((deltaTime > kPMHaltTimeoutMS) || timeout ||
2855 (gIOKitDebug & kIOLogDebugPower))
2856 {
2857 HaltRestartLog("%s driver %s (%p) took %lu ms\n",
2858 (gPMHaltEvent == kIOMessageSystemWillPowerOff) ?
2859 "PowerOff" : "Restart",
2860 service->getName(), service,
2861 deltaTime );
2862 }
2863
2864 service->release();
2865 me->visits++;
2866 }
2867}
2868
2869void PMHaltWorker::checkTimeout( PMHaltWorker * me, AbsoluteTime * now )
2870{
2871 UInt64 nano;
2872 AbsoluteTime startTime;
2873 AbsoluteTime endTime;
2874
2875 endTime = *now;
2876
2877 IOLockLock(me->lock);
2878 if (me->service && !me->timeout)
2879 {
2880 startTime = me->startTime;
2881 nano = 0;
2882 if (CMP_ABSOLUTETIME(&endTime, &startTime) > 0)
2883 {
2884 SUB_ABSOLUTETIME(&endTime, &startTime);
2885 absolutetime_to_nanoseconds(endTime, &nano);
2886 }
2887 if (nano > 3000000000ULL)
2888 {
2889 me->timeout = true;
2890 HaltRestartLog("%s still waiting on %s\n",
2891 (gPMHaltEvent == kIOMessageSystemWillPowerOff) ?
2892 "PowerOff" : "Restart",
2893 me->service->getName());
2894 }
2895 }
2896 IOLockUnlock(me->lock);
2897}
2898
2899//*********************************************************************************
2900// acknowledgeSystemWillShutdown
2901//
2902// Acknowledgement from drivers that they have prepared for shutdown/restart.
2903//*********************************************************************************
2904
2905void IOPMrootDomain::acknowledgeSystemWillShutdown( IOService * from )
2906{
2907 PMHaltWorker * worker;
2908 OSObject * prop;
2909
2910 if (!from)
2911 return;
2912
2913 //DEBUG_LOG("%s acknowledged\n", from->getName());
2914 prop = from->copyProperty( gPMHaltClientAcknowledgeKey );
2915 if (prop)
2916 {
2917 worker = (PMHaltWorker *) prop;
2918 IOLockLock(worker->lock);
2919 from->removeProperty( gPMHaltClientAcknowledgeKey );
2920 thread_wakeup((event_t) worker);
2921 IOLockUnlock(worker->lock);
2922 worker->release();
2923 }
2924 else
2925 {
2926 DEBUG_LOG("%s acknowledged without worker property\n",
2927 from->getName());
2928 }
2929}
2930
2931//*********************************************************************************
2932// notifySystemShutdown
2933//
2934// Notify all objects in PM tree that system will shutdown or restart
2935//*********************************************************************************
2936
2937static void
2938notifySystemShutdown( IOService * root, unsigned long event )
2939{
2940#define PLACEHOLDER ((OSSet *)gPMHaltArray)
2941 IORegistryIterator * iter;
2942 IORegistryEntry * entry;
2943 IOService * node;
2944 OSSet * inner;
2945 PMHaltWorker * workers[kPMHaltMaxWorkers];
2946 AbsoluteTime deadline;
2947 unsigned int totalNodes = 0;
2948 unsigned int depth;
2949 unsigned int rootDepth;
2950 unsigned int numWorkers;
2951 unsigned int count;
2952 int waitResult;
2953 void * baseFunc;
2954 bool ok;
2955
2956 DEBUG_LOG("%s event = %lx\n", __FUNCTION__, event);
2957
2958 baseFunc = OSMemberFunctionCast(void *, root, &IOService::systemWillShutdown);
2959
2960 // Iterate the entire PM tree starting from root
2961
2962 rootDepth = root->getDepth( gIOPowerPlane );
2963 if (!rootDepth) goto done;
2964
2965 // debug - for repeated test runs
2966 while (PMHaltWorker::metaClass->getInstanceCount())
2967 IOSleep(1);
2968
2969 if (!gPMHaltArray)
2970 {
2971 gPMHaltArray = OSArray::withCapacity(40);
2972 if (!gPMHaltArray) goto done;
2973 }
2974 else // debug
2975 gPMHaltArray->flushCollection();
2976
2977 if (!gPMHaltLock)
2978 {
2979 gPMHaltLock = IOLockAlloc();
2980 if (!gPMHaltLock) goto done;
2981 }
2982
2983 if (!gPMHaltClientAcknowledgeKey)
2984 {
2985 gPMHaltClientAcknowledgeKey =
2986 OSSymbol::withCStringNoCopy("PMShutdown");
2987 if (!gPMHaltClientAcknowledgeKey) goto done;
2988 }
2989
2990 gPMHaltEvent = event;
2991
2992 // Depth-first walk of PM plane
2993
2994 iter = IORegistryIterator::iterateOver(
2995 root, gIOPowerPlane, kIORegistryIterateRecursively);
2996
2997 if (iter)
2998 {
2999 while ((entry = iter->getNextObject()))
3000 {
3001 node = OSDynamicCast(IOService, entry);
3002 if (!node)
3003 continue;
3004
3005 if (baseFunc ==
3006 OSMemberFunctionCast(void *, node, &IOService::systemWillShutdown))
3007 continue;
3008
3009 depth = node->getDepth( gIOPowerPlane );
3010 if (depth <= rootDepth)
3011 continue;
3012
3013 ok = false;
3014
3015 // adjust to zero based depth
3016 depth -= (rootDepth + 1);
3017
3018 // gPMHaltArray is an array of containers, each container
3019 // refers to nodes with the same depth.
3020
3021 count = gPMHaltArray->getCount();
3022 while (depth >= count)
3023 {
3024 // expand array and insert placeholders
3025 gPMHaltArray->setObject(PLACEHOLDER);
3026 count++;
3027 }
3028 count = gPMHaltArray->getCount();
3029 if (depth < count)
3030 {
3031 inner = (OSSet *)gPMHaltArray->getObject(depth);
3032 if (inner == PLACEHOLDER)
3033 {
3034 inner = OSSet::withCapacity(40);
3035 if (inner)
3036 {
3037 gPMHaltArray->replaceObject(depth, inner);
3038 inner->release();
3039 }
3040 }
3041
3042 // PM nodes that appear more than once in the tree will have
3043 // the same depth, OSSet will refuse to add the node twice.
3044 if (inner)
3045 ok = inner->setObject(node);
3046 }
3047 if (!ok)
3048 DEBUG_LOG("Skipped PM node %s\n", node->getName());
3049 }
3050 iter->release();
3051 }
3052
3053 // debug only
3054 for (int i = 0; (inner = (OSSet *)gPMHaltArray->getObject(i)); i++)
3055 {
3056 count = 0;
3057 if (inner != PLACEHOLDER)
3058 count = inner->getCount();
3059 DEBUG_LOG("Nodes at depth %u = %u\n", i, count);
3060 }
3061
3062 // strip placeholders (not all depths are populated)
3063 numWorkers = 0;
3064 for (int i = 0; (inner = (OSSet *)gPMHaltArray->getObject(i)); )
3065 {
3066 if (inner == PLACEHOLDER)
3067 {
3068 gPMHaltArray->removeObject(i);
3069 continue;
3070 }
3071 count = inner->getCount();
3072 if (count > numWorkers)
3073 numWorkers = count;
3074 totalNodes += count;
3075 i++;
3076 }
3077
3078 if (gPMHaltArray->getCount() == 0 || !numWorkers)
3079 goto done;
3080
3081 gPMHaltBusyCount = 0;
3082 gPMHaltIdleCount = 0;
3083 gPMHaltDepth = gPMHaltArray->getCount() - 1;
3084
3085 // Create multiple workers (and threads)
3086
3087 if (numWorkers > kPMHaltMaxWorkers)
3088 numWorkers = kPMHaltMaxWorkers;
3089
3090 DEBUG_LOG("PM nodes = %u, maxDepth = %u, workers = %u\n",
3091 totalNodes, gPMHaltArray->getCount(), numWorkers);
3092
3093 for (unsigned int i = 0; i < numWorkers; i++)
3094 workers[i] = PMHaltWorker::worker();
3095
3096 // Wait for workers to exhaust all available work
3097
3098 IOLockLock(gPMHaltLock);
3099 while (gPMHaltDepth >= 0)
3100 {
3101 clock_interval_to_deadline(1000, kMillisecondScale, &deadline);
3102
3103 waitResult = IOLockSleepDeadline(
3104 gPMHaltLock, &gPMHaltDepth, deadline, THREAD_UNINT);
3105 if (THREAD_TIMED_OUT == waitResult)
3106 {
3107 AbsoluteTime now;
3108 clock_get_uptime(&now);
3109
3110 IOLockUnlock(gPMHaltLock);
3111 for (unsigned int i = 0 ; i < numWorkers; i++)
3112 {
3113 if (workers[i])
3114 PMHaltWorker::checkTimeout(workers[i], &now);
3115 }
3116 IOLockLock(gPMHaltLock);
3117 }
3118 }
3119 IOLockUnlock(gPMHaltLock);
3120
3121 // Release all workers
3122
3123 for (unsigned int i = 0; i < numWorkers; i++)
3124 {
3125 if (workers[i])
3126 workers[i]->release();
3127 // worker also retained by it's own thread
3128 }
3129
3130done:
3131 DEBUG_LOG("%s done\n", __FUNCTION__);
3132 return;
3133}
3134
3135#if DEBUG_TEST
3136// debug - exercise notifySystemShutdown()
3137bool IOPMrootDomain::serializeProperties( OSSerialize * s ) const
3138{
3139 IOPMrootDomain * root = (IOPMrootDomain *) this;
3140 notifySystemShutdown( root, kIOMessageSystemWillPowerOff );
3141 return( super::serializeProperties(s) );
3142}
3143#endif
3144
0c530ab8
A
3145/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
3146
3147
3148
3149#undef super
3150#define super OSObject
3151OSDefineMetaClassAndStructors(PMSettingObject, OSObject)
3152
3153void PMSettingObject::setPMSetting(const OSSymbol *type, OSObject *obj)
3154{
3155 (*func)(target, type, obj, refcon);
3156}
3157
3158/*
3159 * Static constructor/initializer for PMSettingObject
3160 */
3161PMSettingObject *PMSettingObject::pmSettingObject(
3162 IOPMrootDomain *parent_arg,
3163 IOPMSettingControllerCallback handler_arg,
3164 OSObject *target_arg,
3165 uintptr_t refcon_arg,
3166 uint32_t supportedPowerSources,
3167 const OSSymbol * settings[])
3168{
3169 uint32_t objCount = 0;
3170 PMSettingObject *pmso;
3171
3172 if( !parent_arg || !handler_arg || !settings ) return NULL;
3173
3174 // count OSSymbol entries in NULL terminated settings array
3175 while( settings[objCount] ) {
3176 objCount++;
3177 }
3178 if(0 == objCount) return NULL;
3179
3180 pmso = new PMSettingObject;
3181 if(!pmso || !pmso->init()) return NULL;
3182
3183 pmso->parent = parent_arg;
3184 pmso->func = handler_arg;
3185 pmso->target = target_arg;
3186 pmso->refcon = refcon_arg;
3187 pmso->releaseAtCount = objCount + 1; // release when it has count+1 retains
3188
3189 pmso->publishedFeatureID = (uint32_t *)IOMalloc(sizeof(uint32_t)*objCount);
3190 if(pmso->publishedFeatureID) {
3191 for(unsigned int i=0; i<objCount; i++) {
3192 // Since there is now at least one listener to this setting, publish
3193 // PM root domain support for it.
3194 parent_arg->publishFeature( settings[i]->getCStringNoCopy(),
3195 supportedPowerSources, &pmso->publishedFeatureID[i] );
3196 }
3197 }
3198
3199 return pmso;
3200}
3201
3202void PMSettingObject::free(void)
3203{
3204 OSCollectionIterator *settings_iter;
3205 OSSymbol *sym;
3206 OSArray *arr;
3207 int arr_idx;
3208 int i;
3209 int objCount = releaseAtCount - 1;
3210
3211 if(publishedFeatureID) {
3212 for(i=0; i<objCount; i++) {
3213 if(0 != publishedFeatureID[i]) {
3214 parent->removePublishedFeature( publishedFeatureID[i] );
0b4e3aa0
A
3215 }
3216 }
0c530ab8
A
3217
3218 IOFree(publishedFeatureID, sizeof(uint32_t) * objCount);
0b4e3aa0 3219 }
0c530ab8
A
3220
3221 IORecursiveLockLock(parent->settingsCtrlLock);
3222
3223 // Search each PM settings array in the kernel.
3224 settings_iter = OSCollectionIterator::withCollection(parent->settingsCallbacks);
3225 if(settings_iter)
3226 {
3227 while(( sym = OSDynamicCast(OSSymbol, settings_iter->getNextObject()) ))
3228 {
3229 arr = (OSArray *)parent->settingsCallbacks->getObject(sym);
3230 arr_idx = arr->getNextIndexOfObject(this, 0);
3231 if(-1 != arr_idx) {
3232 // 'this' was found in the array; remove it
3233 arr->removeObject(arr_idx);
3234 }
3235 }
3236
3237 settings_iter->release();
3238 }
3239
3240 IORecursiveLockUnlock(parent->settingsCtrlLock);
3241
3242 super::free();
3243}
3244
3245void PMSettingObject::taggedRelease(const void *tag, const int when) const
3246{
3247 // We have n+1 retains - 1 per array that this PMSettingObject is a member
3248 // of, and 1 retain to ourself. When we get a release with n+1 retains
3249 // remaining, we go ahead and free ourselves, cleaning up array pointers
3250 // in free();
3251
3252 super::taggedRelease(tag, releaseAtCount);
0b4e3aa0 3253}
1c79356b
A
3254
3255
0c530ab8 3256
1c79356b
A
3257/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
3258
3259#undef super
3260#define super IOService
3261
3262OSDefineMetaClassAndStructors(IORootParent, IOService)
3263
0b4e3aa0
A
3264// This array exactly parallels the state array for the root domain.
3265// Power state changes initiated by a device can be vetoed by a client of the device, and
3266// power state changes initiated by the parent of a device cannot be vetoed by a client of the device,
3267// so when the root domain wants a power state change that cannot be vetoed (e.g. demand sleep), it asks
3268// its parent to make the change. That is the reason for this complexity.
1c79356b 3269
0b4e3aa0 3270static IOPMPowerState patriarchPowerStates[number_of_power_states] = {
1c79356b 3271 {1,0,0,0,0,0,0,0,0,0,0,0}, // off
0b4e3aa0 3272 {1,0,RESTART_POWER,0,0,0,0,0,0,0,0,0}, // reset
1c79356b 3273 {1,0,SLEEP_POWER,0,0,0,0,0,0,0,0,0}, // sleep
0b4e3aa0 3274 {1,0,DOZE_POWER,0,0,0,0,0,0,0,0,0}, // doze
1c79356b
A
3275 {1,0,ON_POWER,0,0,0,0,0,0,0,0,0} // running
3276};
3277
1c79356b
A
3278bool IORootParent::start ( IOService * nub )
3279{
0b4e3aa0 3280 mostRecentChange = ON_STATE;
1c79356b
A
3281 super::start(nub);
3282 PMinit();
2d21ac55 3283 youAreRoot();
0b4e3aa0 3284 registerPowerDriver(this,patriarchPowerStates,number_of_power_states);
2d21ac55
A
3285 wakeSystem();
3286 powerOverrideOnPriv();
1c79356b
A
3287 return true;
3288}
3289
3290
3291void IORootParent::shutDownSystem ( void )
3292{
0b4e3aa0
A
3293 mostRecentChange = OFF_STATE;
3294 changePowerStateToPriv(OFF_STATE);
3295}
3296
3297
3298void IORootParent::restartSystem ( void )
3299{
3300 mostRecentChange = RESTART_STATE;
3301 changePowerStateToPriv(RESTART_STATE);
1c79356b
A
3302}
3303
3304
3305void IORootParent::sleepSystem ( void )
3306{
0b4e3aa0
A
3307 mostRecentChange = SLEEP_STATE;
3308 changePowerStateToPriv(SLEEP_STATE);
3309}
3310
3311
3312void IORootParent::dozeSystem ( void )
3313{
3314 mostRecentChange = DOZE_STATE;
3315 changePowerStateToPriv(DOZE_STATE);
3316}
3317
3318// Called in demand sleep when sleep discovered to be impossible after actually attaining that state.
3319// This brings the parent to doze, which allows the root to step up from sleep to doze.
3320
3321// In idle sleep, do nothing because the parent is still on and the root can freely change state.
3322
3323void IORootParent::sleepToDoze ( void )
3324{
3325 if ( mostRecentChange == SLEEP_STATE ) {
3326 changePowerStateToPriv(DOZE_STATE);
3327 }
1c79356b
A
3328}
3329
3330
3331void IORootParent::wakeSystem ( void )
3332{
0b4e3aa0
A
3333 mostRecentChange = ON_STATE;
3334 changePowerStateToPriv(ON_STATE);
1c79356b
A
3335}
3336
0c530ab8
A
3337IOReturn IORootParent::changePowerStateToPriv ( unsigned long ordinal )
3338{
3339 IOReturn ret;
3340
3341 if( (SLEEP_STATE == ordinal) && sleepSupportedPEFunction )
3342 {
3343
3344 // Determine if the machine supports sleep, or must doze.
3345 ret = getPlatform()->callPlatformFunction(
3346 sleepSupportedPEFunction, false,
3347 NULL, NULL, NULL, NULL);
3348
3349 // If the machine only supports doze, the callPlatformFunction call
3350 // boils down toIOPMrootDomain::setSleepSupported(kPCICantSleep),
3351 // otherwise nothing.
3352 }
3353
3354 return super::changePowerStateToPriv(ordinal);
3355}
3356