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