]> git.saurik.com Git - apple/xnu.git/blobdiff - iokit/Kernel/IOPMrootDomain.cpp
xnu-2782.20.48.tar.gz
[apple/xnu.git] / iokit / Kernel / IOPMrootDomain.cpp
index c98da3b9f7dd3c76d49494e8543b6e5a22dc5d7c..73738c14ba9eaa6915d2230c0617caec22f3288a 100644 (file)
  */
 #include <libkern/c++/OSKext.h>
 #include <libkern/c++/OSMetaClass.h>
  */
 #include <libkern/c++/OSKext.h>
 #include <libkern/c++/OSMetaClass.h>
+#include <libkern/OSAtomic.h>
+#include <libkern/OSDebug.h>
 #include <IOKit/IOWorkLoop.h>
 #include <IOKit/IOCommandGate.h>
 #include <IOKit/IOPlatformExpert.h>
 #include <IOKit/IOWorkLoop.h>
 #include <IOKit/IOCommandGate.h>
 #include <IOKit/IOPlatformExpert.h>
+#include <IOKit/IOCPU.h>
 #include <IOKit/IOKitDebug.h>
 #include <IOKit/IOTimeStamp.h>
 #include <IOKit/pwr_mgt/IOPMlog.h>
 #include <IOKit/IOKitDebug.h>
 #include <IOKit/IOTimeStamp.h>
 #include <IOKit/pwr_mgt/IOPMlog.h>
 #include <IOKit/IODeviceTreeSupport.h>
 #include <IOKit/IOMessage.h>
 #include <IOKit/IOReturn.h>
 #include <IOKit/IODeviceTreeSupport.h>
 #include <IOKit/IOMessage.h>
 #include <IOKit/IOReturn.h>
+#include <IOKit/IONVRAM.h>
 #include "RootDomainUserClient.h"
 #include "IOKit/pwr_mgt/IOPowerConnection.h"
 #include "IOPMPowerStateQueue.h"
 #include <IOKit/IOCatalogue.h>
 #include "RootDomainUserClient.h"
 #include "IOKit/pwr_mgt/IOPowerConnection.h"
 #include "IOPMPowerStateQueue.h"
 #include <IOKit/IOCatalogue.h>
-#include <IOKit/IOCommand.h>    // IOServicePMPrivate
+#include <IOKit/IOReportMacros.h>
 #if HIBERNATION
 #include <IOKit/IOHibernatePrivate.h>
 #endif
 #if HIBERNATION
 #include <IOKit/IOHibernatePrivate.h>
 #endif
+#include <console/video_console.h>
 #include <sys/syslog.h>
 #include <sys/sysctl.h>
 #include <sys/syslog.h>
 #include <sys/sysctl.h>
+#include <sys/vnode.h>
+#include <sys/vnode_internal.h>
+#include <sys/fcntl.h>
+
 #include <sys/time.h>
 #include <sys/time.h>
-#include "IOServicePrivate.h"  // _IOServiceInterestNotifier
+#include "IOServicePrivate.h"   // _IOServiceInterestNotifier
 #include "IOServicePMPrivate.h"
 
 __BEGIN_DECLS
 #include "IOServicePMPrivate.h"
 
 __BEGIN_DECLS
@@ -63,157 +72,181 @@ __END_DECLS
 #endif
 
 #define kIOPMrootDomainClass    "IOPMrootDomain"
 #endif
 
 #define kIOPMrootDomainClass    "IOPMrootDomain"
+#define LOG_PREFIX              "PMRD: "
 
 
-#define LOG_PREFIX  "PMRD: "
 
 
-#define LOG(x...)   do { \
-    kprintf(LOG_PREFIX x); IOLog(x); } while (false)
+#define MSG(x...) \
+    do { kprintf(LOG_PREFIX x); IOLog(x); } while (false)
 
 
-#define KLOG(x...)  do { \
-    kprintf(LOG_PREFIX x); } while (false)
+#define LOG(x...)    \
+    do { kprintf(LOG_PREFIX x); } while (false)
 
 #define DLOG(x...)  do { \
 
 #define DLOG(x...)  do { \
-       if (kIOLogPMRootDomain & gIOKitDebug) \
-        kprintf(LOG_PREFIX x); } while (false)
+    if (kIOLogPMRootDomain & gIOKitDebug) \
+        kprintf(LOG_PREFIX x); \
+} while (false)
+
+#define DMSG(x...)  do { \
+    if (kIOLogPMRootDomain & gIOKitDebug) { \
+        kprintf(LOG_PREFIX x); IOLog(x); \
+    } \
+} while (false)
+
+
+#define _LOG(x...)
 
 #define CHECK_THREAD_CONTEXT
 #ifdef  CHECK_THREAD_CONTEXT
 static IOWorkLoop * gIOPMWorkLoop = 0;
 
 #define CHECK_THREAD_CONTEXT
 #ifdef  CHECK_THREAD_CONTEXT
 static IOWorkLoop * gIOPMWorkLoop = 0;
-#define ASSERT_GATED(x)                                     \
+#define ASSERT_GATED(                                     \
 do {                                                        \
     if (gIOPMWorkLoop && gIOPMWorkLoop->inGate() != true) { \
 do {                                                        \
     if (gIOPMWorkLoop && gIOPMWorkLoop->inGate() != true) { \
-        panic("RootDomain: not inside PM gate");               \
+        panic("RootDomain: not inside PM gate");            \
     }                                                       \
 } while(false)
 #else
     }                                                       \
 } while(false)
 #else
-#define ASSERT_GATED(x)
+#define ASSERT_GATED()
 #endif /* CHECK_THREAD_CONTEXT */
 
 #endif /* CHECK_THREAD_CONTEXT */
 
+#define CAP_LOSS(c)  \
+        (((_pendingCapability & (c)) == 0) && \
+         ((_currentCapability & (c)) != 0))
+
+#define CAP_GAIN(c)  \
+        (((_currentCapability & (c)) == 0) && \
+         ((_pendingCapability & (c)) != 0))
+
+#define CAP_CHANGE(c)    \
+        (((_currentCapability ^ _pendingCapability) & (c)) != 0)
+
+#define CAP_CURRENT(c)  \
+        ((_currentCapability & (c)) != 0)
+
+#define CAP_HIGHEST(c)  \
+        ((_highestCapability & (c)) != 0)
+
+#if defined(__i386__) || defined(__x86_64__)
+#define DARK_TO_FULL_EVALUATE_CLAMSHELL     1
+#endif
+
 // Event types for IOPMPowerStateQueue::submitPowerEvent()
 enum {
 // Event types for IOPMPowerStateQueue::submitPowerEvent()
 enum {
-    kPowerEventFeatureChanged = 1,
-    kPowerEventReceivedPowerNotification,
-    kPowerEventSystemBootCompleted,
-    kPowerEventSystemShutdown,
-    kPowerEventUserDisabledSleep,
-    kPowerEventConfigdRegisteredInterest,
-    kPowerEventAggressivenessChanged
+    kPowerEventFeatureChanged = 1,              // 1
+    kPowerEventReceivedPowerNotification,       // 2
+    kPowerEventSystemBootCompleted,             // 3
+    kPowerEventSystemShutdown,                  // 4
+    kPowerEventUserDisabledSleep,               // 5
+    kPowerEventRegisterSystemCapabilityClient,  // 6
+    kPowerEventRegisterKernelCapabilityClient,  // 7
+    kPowerEventPolicyStimulus,                  // 8
+    kPowerEventAssertionCreate,                 // 9
+    kPowerEventAssertionRelease,                // 10
+    kPowerEventAssertionSetLevel,               // 11
+    kPowerEventQueueSleepWakeUUID,              // 12
+    kPowerEventPublishSleepWakeUUID,            // 13
+    kPowerEventSetDisplayPowerOn                // 14
+};
+
+// For evaluatePolicy()
+// List of stimuli that affects the root domain policy.
+enum {
+    kStimulusDisplayWranglerSleep,      // 0
+    kStimulusDisplayWranglerWake,       // 1
+    kStimulusAggressivenessChanged,     // 2
+    kStimulusDemandSystemSleep,         // 3
+    kStimulusAllowSystemSleepChanged,   // 4
+    kStimulusDarkWakeActivityTickle,    // 5
+    kStimulusDarkWakeEntry,             // 6
+    kStimulusDarkWakeReentry,           // 7
+    kStimulusDarkWakeEvaluate,          // 8
+    kStimulusNoIdleSleepPreventers,     // 9
+    kStimulusEnterUserActiveState,      // 10
+    kStimulusLeaveUserActiveState       // 11
 };
 
 extern "C" {
 IOReturn OSKextSystemSleepOrWake( UInt32 );
 }
 };
 
 extern "C" {
 IOReturn OSKextSystemSleepOrWake( UInt32 );
 }
-
-extern const IORegistryPlane * gIOPowerPlane;
+extern "C" ppnum_t      pmap_find_phys(pmap_t pmap, addr64_t va);
+extern "C" addr64_t     kvtophys(vm_offset_t va);
+extern "C" int  stack_snapshot_from_kernel(pid_t pid, void *buf, uint32_t size, uint32_t flags, unsigned *bytesTraced);
 
 static void idleSleepTimerExpired( thread_call_param_t, thread_call_param_t );
 
 static void idleSleepTimerExpired( thread_call_param_t, thread_call_param_t );
-static void wakeupClamshellTimerExpired( thread_call_param_t us, thread_call_param_t );
 static void notifySystemShutdown( IOService * root, unsigned long event );
 static void notifySystemShutdown( IOService * root, unsigned long event );
-static bool clientMessageFilter( OSObject * object, void * context );
-static void handleAggressivesFunction( thread_call_param_t param1, thread_call_param_t param2 );
+static void handleAggressivesFunction( thread_call_param_t, thread_call_param_t );
+static void pmEventTimeStamp(uint64_t *recordTS);
 
 // "IOPMSetSleepSupported"  callPlatformFunction name
 static const OSSymbol *sleepSupportedPEFunction = NULL;
 
 // "IOPMSetSleepSupported"  callPlatformFunction name
 static const OSSymbol *sleepSupportedPEFunction = NULL;
+static const OSSymbol *sleepMessagePEFunction   = NULL;
 
 
-#define kIOSleepSupportedKey  "IOSleepSupported"
+#define kIOSleepSupportedKey        "IOSleepSupported"
+#define kIOPMSystemCapabilitiesKey  "System Capabilities"
+
+#define kIORequestWranglerIdleKey   "IORequestIdle"
+#define kDefaultWranglerIdlePeriod  25 // in milliseconds
+
+#define kIOSleepWakeDebugKey        "Persistent-memory-note"
 
 #define kRD_AllPowerSources (kIOPMSupportedOnAC \
                            | kIOPMSupportedOnBatt \
                            | kIOPMSupportedOnUPS)
 
 
 #define kRD_AllPowerSources (kIOPMSupportedOnAC \
                            | kIOPMSupportedOnBatt \
                            | kIOPMSupportedOnUPS)
 
-enum 
+enum
 {
     // not idle around autowake time, secs
     kAutoWakePreWindow  = 45,
     kAutoWakePostWindow = 15
 };
 
 {
     // not idle around autowake time, secs
     kAutoWakePreWindow  = 45,
     kAutoWakePostWindow = 15
 };
 
-#define kLocalEvalClamshellCommand        (1 << 15)
+#define kLocalEvalClamshellCommand  (1 << 15)
+#define kIdleSleepRetryInterval     (3 * 60)
+
+enum {
+    kWranglerPowerStateMin   = 0,
+    kWranglerPowerStateSleep = 2,
+    kWranglerPowerStateDim   = 3,
+    kWranglerPowerStateMax   = 4
+};
 
 enum {
 
 enum {
-    OFF_STATE       = 0,
-    RESTART_STATE   = 1,
-    SLEEP_STATE     = 2,
-    DOZE_STATE      = 3,
-    ON_STATE        = 4,
+    OFF_STATE           = 0,
+    RESTART_STATE       = 1,
+    SLEEP_STATE         = 2,
+    ON_STATE            = 3,
     NUM_POWER_STATES
 };
 
 #define ON_POWER        kIOPMPowerOn
 #define RESTART_POWER   kIOPMRestart
 #define SLEEP_POWER     kIOPMAuxPowerOn
     NUM_POWER_STATES
 };
 
 #define ON_POWER        kIOPMPowerOn
 #define RESTART_POWER   kIOPMRestart
 #define SLEEP_POWER     kIOPMAuxPowerOn
-#define DOZE_POWER      kIOPMDoze
 
 static IOPMPowerState ourPowerStates[NUM_POWER_STATES] =
 {
     {1, 0,                      0,              0,             0,0,0,0,0,0,0,0},
 
 static IOPMPowerState ourPowerStates[NUM_POWER_STATES] =
 {
     {1, 0,                      0,              0,             0,0,0,0,0,0,0,0},
-    {1, kIOPMRestartCapability,        kIOPMRestart,   RESTART_POWER, 0,0,0,0,0,0,0,0},        
+    {1, kIOPMRestartCapability, kIOPMRestart,   RESTART_POWER, 0,0,0,0,0,0,0,0},
     {1, kIOPMSleepCapability,   kIOPMSleep,     SLEEP_POWER,   0,0,0,0,0,0,0,0},
     {1, kIOPMSleepCapability,   kIOPMSleep,     SLEEP_POWER,   0,0,0,0,0,0,0,0},
-    {1, kIOPMDoze,              kIOPMDoze,      DOZE_POWER,    0,0,0,0,0,0,0,0},
     {1, kIOPMPowerOn,           kIOPMPowerOn,   ON_POWER,      0,0,0,0,0,0,0,0}
 };
 
     {1, kIOPMPowerOn,           kIOPMPowerOn,   ON_POWER,      0,0,0,0,0,0,0,0}
 };
 
-// Clients eligible to receive system power messages.
-enum {
-    kMessageClientNone = 0,
-    kMessageClientAll,
-    kMessageClientConfigd       
-};
-
-// Run states (R-state) defined within the ON power state.
-enum {
-    kRStateNormal = 0,
-    kRStateDark,
-    kRStateMaintenance,
-    kRStateCount
-};
-
-// IOService in power plane can be tagged with following flags.
-enum {
-       kServiceFlagGraphics    = 0x01,
-       kServiceFlagNoPowerUp   = 0x02,
-    kServiceFlagTopLevelPCI = 0x04
-};
-
-// Flags describing R-state features and capabilities.
-enum {
-    kRStateFlagNone             = 0x00000000,
-    kRStateFlagSuppressGraphics = 0x00000001,
-    kRStateFlagSuppressMessages = 0x00000002,
-    kRStateFlagSuppressPCICheck = 0x00000004,
-    kRStateFlagDisableIdleSleep = 0x00000008
-};
-
-#if ROOT_DOMAIN_RUN_STATES
-
-// Table of flags for each R-state.
-static uint32_t gRStateFlags[ kRStateCount ] =
-{
-    kRStateFlagNone,
-
-    /* Dark wake */
-    kRStateFlagSuppressGraphics,
-
-    /* Maintenance wake */
-    kRStateFlagSuppressGraphics |
-    kRStateFlagSuppressMessages |
-    kRStateFlagSuppressPCICheck |
-    kRStateFlagDisableIdleSleep
-};
-
-static IONotifier *     gConfigdNotifier = 0;
-
-#define kIOPMRootDomainRunStateKey          "Run State"
-#define kIOPMRootDomainWakeTypeMaintenance  "Maintenance"
-
-#endif /* ROOT_DOMAIN_RUN_STATES */
+#define kIOPMRootDomainWakeTypeSleepService     "SleepService"
+#define kIOPMRootDomainWakeTypeMaintenance      "Maintenance"
+#define kIOPMRootDomainWakeTypeSleepTimer       "SleepTimer"
+#define kIOPMrootDomainWakeTypeLowBattery       "LowBattery"
+#define kIOPMRootDomainWakeTypeUser             "User"
+#define kIOPMRootDomainWakeTypeAlarm            "Alarm"
+#define kIOPMRootDomainWakeTypeNetwork          "Network"
+#define kIOPMRootDomainWakeTypeHIDActivity      "HID Activity"
+#define kIOPMRootDomainWakeTypeNotification     "Notification"
+#define kIOPMRootDomainWakeTypeHibernateError   "HibernateError"
 
 // Special interest that entitles the interested client from receiving
 
 // Special interest that entitles the interested client from receiving
-// all system messages. Used by pmconfigd to support maintenance wake.
+// all system messages. Only used by powerd.
 //
 //
-#define kIOPMPrivilegedPowerInterest        "IOPMPrivilegedPowerInterest"
+#define kIOPMSystemCapabilityInterest       "IOPMSystemCapabilityInterest"
 
 
-static IONotifier *     gSysPowerDownNotifier = 0;
+#define WAKEEVENT_LOCK()        IOLockLock(wakeEventLock)
+#define WAKEEVENT_UNLOCK()      IOLockUnlock(wakeEventLock)
 
 /*
  * Aggressiveness
 
 /*
  * Aggressiveness
@@ -223,8 +256,6 @@ static IONotifier *     gSysPowerDownNotifier = 0;
 
 #define kAggressivesMinValue    1
 
 
 #define kAggressivesMinValue    1
 
-static uint32_t gAggressivesState = 0;
-
 enum {
     kAggressivesStateBusy           = 0x01,
     kAggressivesStateQuickSpindown  = 0x02
 enum {
     kAggressivesStateBusy           = 0x01,
     kAggressivesStateQuickSpindown  = 0x02
@@ -261,18 +292,44 @@ enum {
 enum {
     kAggressivesRecordFlagModified         = 0x00000001,
     kAggressivesRecordFlagMinValue         = 0x00000002
 enum {
     kAggressivesRecordFlagModified         = 0x00000001,
     kAggressivesRecordFlagMinValue         = 0x00000002
-    
+};
+
+// gDarkWakeFlags
+enum {
+    kDarkWakeFlagHIDTickleEarly      = 0x01, // hid tickle before gfx suppression
+    kDarkWakeFlagHIDTickleLate       = 0x02, // hid tickle after gfx suppression
+    kDarkWakeFlagHIDTickleNone       = 0x03, // hid tickle is not posted
+    kDarkWakeFlagHIDTickleMask       = 0x03,
+    kDarkWakeFlagAlarmIsDark         = 0x0100,
+    kDarkWakeFlagGraphicsPowerState1 = 0x0200,
+    kDarkWakeFlagAudioNotSuppressed  = 0x0400
 };
 
 static IOPMrootDomain * gRootDomain;
 };
 
 static IOPMrootDomain * gRootDomain;
+static IONotifier *     gSysPowerDownNotifier = 0;
 static UInt32           gSleepOrShutdownPending = 0;
 static UInt32           gWillShutdown = 0;
 static UInt32           gSleepOrShutdownPending = 0;
 static UInt32           gWillShutdown = 0;
-static uint32_t         gMessageClientType = kMessageClientNone;
+static UInt32           gPagingOff = 0;
 static UInt32           gSleepWakeUUIDIsSet = false;
 static UInt32           gSleepWakeUUIDIsSet = false;
+static uint32_t         gAggressivesState = 0;
+
+uuid_string_t bootsessionuuid_string;
+
+static uint32_t         gDarkWakeFlags = kDarkWakeFlagHIDTickleNone;
+static PMStatsStruct    gPMStats;
+
+#if HIBERNATION
+static IOPMSystemSleepPolicyHandler     gSleepPolicyHandler = 0;
+static IOPMSystemSleepPolicyVariables * gSleepPolicyVars = 0;
+static void *                           gSleepPolicyTarget;
+#endif
 
 struct timeval gIOLastSleepTime;
 struct timeval gIOLastWakeTime;
 
 
 struct timeval gIOLastSleepTime;
 struct timeval gIOLastWakeTime;
 
+static char gWakeReasonString[128];
+static bool gWakeReasonSysctlRegistered = false;
+
 // Constants used as arguments to IOPMrootDomain::informCPUStateChange
 #define kCPUUnknownIndex    9999999
 enum {
 // Constants used as arguments to IOPMrootDomain::informCPUStateChange
 #define kCPUUnknownIndex    9999999
 enum {
@@ -284,32 +341,71 @@ enum {
 const OSSymbol *gIOPMStatsApplicationResponseTimedOut;
 const OSSymbol *gIOPMStatsApplicationResponseCancel;
 const OSSymbol *gIOPMStatsApplicationResponseSlow;
 const OSSymbol *gIOPMStatsApplicationResponseTimedOut;
 const OSSymbol *gIOPMStatsApplicationResponseCancel;
 const OSSymbol *gIOPMStatsApplicationResponseSlow;
+const OSSymbol *gIOPMStatsApplicationResponsePrompt;
+const OSSymbol *gIOPMStatsDriverPSChangeSlow;
+
+#define kBadPMFeatureID     0
+
+/*
+ * PMSettingHandle
+ * Opaque handle passed to clients of registerPMSettingController()
+ */
+class PMSettingHandle : public OSObject
+{
+    OSDeclareFinalStructors( PMSettingHandle )
+    friend class PMSettingObject;
+
+private:
+    PMSettingObject *pmso;
+    void free(void);
+};
 
 
+/*
+ * PMSettingObject
+ * Internal object to track each PM setting controller
+ */
 class PMSettingObject : public OSObject
 {
 class PMSettingObject : public OSObject
 {
-    OSDeclareFinalStructors(PMSettingObject)
+    OSDeclareFinalStructors( PMSettingObject )
+    friend class IOPMrootDomain;
+
 private:
 private:
+    queue_head_t                    calloutQueue;
+    thread_t                        waitThread;
     IOPMrootDomain                  *parent;
     IOPMrootDomain                  *parent;
+    PMSettingHandle                 *pmsh;
     IOPMSettingControllerCallback   func;
     OSObject                        *target;
     uintptr_t                       refcon;
     uint32_t                        *publishedFeatureID;
     IOPMSettingControllerCallback   func;
     OSObject                        *target;
     uintptr_t                       refcon;
     uint32_t                        *publishedFeatureID;
-    int                             releaseAtCount;
+    uint32_t                        settingCount;
+    bool                            disabled;
+
+    void free(void);
+
 public:
     static PMSettingObject *pmSettingObject(
 public:
     static PMSettingObject *pmSettingObject(
-                IOPMrootDomain      *parent_arg,
+                IOPMrootDomain                  *parent_arg,
                 IOPMSettingControllerCallback   handler_arg,
                 IOPMSettingControllerCallback   handler_arg,
-                OSObject    *target_arg,
-                uintptr_t   refcon_arg,
-                uint32_t    supportedPowerSources,
-                const OSSymbol *settings[]);
-
-    void setPMSetting(const OSSymbol *type, OSObject *obj);
+                OSObject                        *target_arg,
+                uintptr_t                       refcon_arg,
+                uint32_t                        supportedPowerSources,
+                const OSSymbol                  *settings[],
+                OSObject                        **handle_obj);
+
+    void dispatchPMSetting(const OSSymbol *type, OSObject *object);
+    void clientHandleFreed(void);
+};
 
 
-    void taggedRelease(const void *tag, const int when) const;
-    void free(void);
+struct PMSettingCallEntry {
+    queue_chain_t   link;
+    thread_t        thread;
 };
 
 };
 
+#define PMSETTING_LOCK()    IOLockLock(settingsCtrlLock)
+#define PMSETTING_UNLOCK()  IOLockUnlock(settingsCtrlLock)
+#define PMSETTING_WAIT(p)   IOLockSleep(settingsCtrlLock, p, THREAD_UNINT)
+#define PMSETTING_WAKEUP(p) IOLockWakeup(settingsCtrlLock, p, true)
 
 /*
  * PMTraceWorker
 
 /*
  * PMTraceWorker
@@ -330,25 +426,82 @@ public:
     static PMTraceWorker        *tracer( IOPMrootDomain * );
     void                        tracePCIPowerChange(change_t, IOService *, uint32_t, uint32_t);
     void                        tracePoint(uint8_t phase);
     static PMTraceWorker        *tracer( IOPMrootDomain * );
     void                        tracePCIPowerChange(change_t, IOService *, uint32_t, uint32_t);
     void                        tracePoint(uint8_t phase);
+    void                        tracePoint(uint8_t phase, uint8_t data8);
+    void                        traceDetail(uint32_t detail);
     void                        traceLoginWindowPhase(uint8_t phase);
     int                         recordTopLevelPCIDevice(IOService *);
     void                        RTC_TRACE(void);
     void                        traceLoginWindowPhase(uint8_t phase);
     int                         recordTopLevelPCIDevice(IOService *);
     void                        RTC_TRACE(void);
-    virtual bool                               serialize(OSSerialize *s) const;
+    virtual bool                serialize(OSSerialize *s) const;
 
     IOPMTracePointHandler       tracePointHandler;
     void *                      tracePointTarget;
 
     IOPMTracePointHandler       tracePointHandler;
     void *                      tracePointTarget;
+    uint64_t                    getPMStatusCode();
 private:
     IOPMrootDomain              *owner;
     IOLock                      *pciMappingLock;
     OSArray                     *pciDeviceBitMappings;
 
 private:
     IOPMrootDomain              *owner;
     IOLock                      *pciMappingLock;
     OSArray                     *pciDeviceBitMappings;
 
+    uint8_t                     addedToRegistry;
     uint8_t                     tracePhase;
     uint8_t                     loginWindowPhase;
     uint8_t                     tracePhase;
     uint8_t                     loginWindowPhase;
-    uint8_t                     addedToRegistry;
-    uint8_t                     unused0;
-    uint32_t                    pciBusyBitMask;
+    uint8_t                     traceData8;
+    uint32_t                    traceData32;
+};
+
+/*
+ * PMAssertionsTracker
+ * Tracks kernel and user space PM assertions
+ */
+class PMAssertionsTracker : public OSObject
+{
+    OSDeclareFinalStructors(PMAssertionsTracker)
+public:
+    static PMAssertionsTracker  *pmAssertionsTracker( IOPMrootDomain * );
+
+    IOReturn                    createAssertion(IOPMDriverAssertionType, IOPMDriverAssertionLevel, IOService *, const char *, IOPMDriverAssertionID *);
+    IOReturn                    releaseAssertion(IOPMDriverAssertionID);
+    IOReturn                    setAssertionLevel(IOPMDriverAssertionID, IOPMDriverAssertionLevel);
+    IOReturn                    setUserAssertionLevels(IOPMDriverAssertionType);
+
+    OSArray                     *copyAssertionsArray(void);
+    IOPMDriverAssertionType     getActivatedAssertions(void);
+    IOPMDriverAssertionLevel    getAssertionLevel(IOPMDriverAssertionType);
+
+    IOReturn                    handleCreateAssertion(OSData *);
+    IOReturn                    handleReleaseAssertion(IOPMDriverAssertionID);
+    IOReturn                    handleSetAssertionLevel(IOPMDriverAssertionID, IOPMDriverAssertionLevel);
+    IOReturn                    handleSetUserAssertionLevels(void * arg0);
+    void                        publishProperties(void);
+
+private:
+    typedef struct {
+        IOPMDriverAssertionID       id;
+        IOPMDriverAssertionType     assertionBits;
+        uint64_t                    createdTime;
+        uint64_t                    modifiedTime;
+        const OSSymbol              *ownerString;
+        IOService                   *ownerService;
+        uint64_t                    registryEntryID;
+        IOPMDriverAssertionLevel    level;
+    } PMAssertStruct;
+
+    uint32_t                    tabulateProducerCount;
+    uint32_t                    tabulateConsumerCount;
+
+    PMAssertStruct              *detailsForID(IOPMDriverAssertionID, int *);
+    void                        tabulate(void);
+
+    IOPMrootDomain              *owner;
+    OSArray                     *assertionsArray;
+    IOLock                      *assertionsArrayLock;
+    IOPMDriverAssertionID       issuingUniqueID __attribute__((aligned(8))); /* aligned for atomic access */
+    IOPMDriverAssertionType     assertionsKernel;
+    IOPMDriverAssertionType     assertionsUser;
+    IOPMDriverAssertionType     assertionsCombined;
 };
 
 };
 
+OSDefineMetaClassAndFinalStructors(PMAssertionsTracker, OSObject);
+
 /*
  * PMHaltWorker
  * Internal helper object for Shutdown/Restart notifications.
 /*
  * PMHaltWorker
  * Internal helper object for Shutdown/Restart notifications.
@@ -381,6 +534,19 @@ OSDefineMetaClassAndFinalStructors( PMHaltWorker, OSObject )
 #define super IOService
 OSDefineMetaClassAndFinalStructors(IOPMrootDomain, IOService)
 
 #define super IOService
 OSDefineMetaClassAndFinalStructors(IOPMrootDomain, IOService)
 
+static void IOPMRootDomainWillShutdown(void)
+{
+    if (OSCompareAndSwap(0, 1, &gWillShutdown))
+    {
+    OSKext::willShutdown();
+    for (int i = 0; i < 100; i++)
+    {
+        if (OSCompareAndSwap(0, 1, &gSleepOrShutdownPending)) break;
+        IOSleep( 100 );
+    }
+    }
+}
+
 extern "C"
 {
     IONotifier * registerSleepWakeInterest(IOServiceInterestHandler handler, void * self, void * ref)
 extern "C"
 {
     IONotifier * registerSleepWakeInterest(IOServiceInterestHandler handler, void * self, void * ref)
@@ -402,72 +568,69 @@ extern "C"
     {
         return gRootDomain->cancelPowerChange ( (unsigned long)PMrefcon );
     }
     {
         return gRootDomain->cancelPowerChange ( (unsigned long)PMrefcon );
     }
-    
+
     IOReturn rootDomainRestart ( void )
     {
         return gRootDomain->restartSystem();
     }
     IOReturn rootDomainRestart ( void )
     {
         return gRootDomain->restartSystem();
     }
-    
+
     IOReturn rootDomainShutdown ( void )
     {
         return gRootDomain->shutdownSystem();
     }
 
     IOReturn rootDomainShutdown ( void )
     {
         return gRootDomain->shutdownSystem();
     }
 
-    void IOSystemShutdownNotification ( void )
+    void IOSystemShutdownNotification(void)
     {
     {
-       if (OSCompareAndSwap(0, 1, &gWillShutdown))
-       {
-           OSKext::willShutdown();
-           for (int i = 0; i < 100; i++)
-           {
-               if (OSCompareAndSwap(0, 1, &gSleepOrShutdownPending)) break;
-               IOSleep( 100 );
-           }
-       }
+        IOPMRootDomainWillShutdown();
+        if (OSCompareAndSwap(0, 1, &gPagingOff))
+        {
+            gRootDomain->handlePlatformHaltRestart(kPEPagingOff);
+        }
     }
 
     }
 
-    int sync_internal(void);    
+    int sync_internal(void);
 }
 
 /*
 }
 
 /*
-A device is always in the highest power state which satisfies its driver, its policy-maker, and any power domain
-children it has, but within the constraint of the power state provided by its parent.  The driver expresses its desire by
-calling changePowerStateTo(), the policy-maker expresses its desire by calling changePowerStateToPriv(), and the children
-express their desires by calling requestPowerDomainState().
-
-The Root Power Domain owns the policy for idle and demand sleep and doze for the system.  It is a power-managed IOService just
-like the others in the system.  It implements several power states which correspond to what we see as Sleep, Doze, etc.
-
-The sleep/doze policy is as follows:
-Sleep and Doze are prevented if the case is open so that nobody will think the machine is off and plug/unplug cards.
-Sleep and Doze are prevented if the sleep timeout slider in the preferences panel is at zero.
-The system cannot Sleep, but can Doze if some object in the tree is in a power state marked kIOPMPreventSystemSleep.
-
-These three conditions are enforced using the "driver clamp" by calling changePowerStateTo().  For example, if the case is
-opened, changePowerStateTo(ON_STATE) is called to hold the system on regardless of the desires of the children of the root or
-the state of the other clamp.
-
-Demand Sleep/Doze is initiated by pressing the front panel power button, closing the clamshell, or selecting the menu item.
-In this case the root's parent actually initiates the power state change so that the root has no choice and does not give
-applications the opportunity to veto the change.
-
-Idle Sleep/Doze occurs if no objects in the tree are in a state marked kIOPMPreventIdleSleep.  When this is true, the root's
-children are not holding the root on, so it sets the "policy-maker clamp" by calling changePowerStateToPriv(ON_STATE)
-to hold itself on until the sleep timer expires.  This timer is set for the difference between the sleep timeout slider and
-the larger of the display dim timeout slider and the disk spindown timeout slider in the Preferences panel.  For example, if
-the system is set to sleep after thirty idle minutes, and the display and disk are set to sleep after five idle minutes,
-when there is no longer an object in the tree holding the system out of Idle Sleep (via kIOPMPreventIdleSleep), the root
-sets its timer for 25 minutes (30 - 5).  When the timer expires, it releases its clamp and now nothing is holding it awake,
-so it falls asleep.
-
-Demand sleep is prevented when the system is booting.  When preferences are transmitted by the loginwindow at the end of
-boot, a flag is cleared, and this allows subsequent Demand Sleep.
-
-The system will not Sleep, but will Doze if some object calls setSleepSupported(kPCICantSleep) during a power change to the sleep state (this can be done by the PCI Aux Power Supply drivers, Slots99, MacRISC299, etc.).  This is not enforced with
-a clamp, but sets a flag which is noticed before actually sleeping the kernel.  If the flag is set, the root steps up
-one power state from Sleep to Doze, and any objects in the tree for which this is relevent will act appropriately (USB and
-ADB will turn on again so that they can wake the system out of Doze (keyboard/mouse activity will cause the Display Wrangler
-to be tickled)).
+A device is always in the highest power state which satisfies its driver,
+its policy-maker, and any power children it has, but within the constraint
+of the power state provided by its parent.  The driver expresses its desire by
+calling changePowerStateTo(), the policy-maker expresses its desire by calling
+changePowerStateToPriv(), and the children express their desires by calling
+requestPowerDomainState().
+
+The Root Power Domain owns the policy for idle and demand sleep for the system.
+It is a power-managed IOService just like the others in the system.
+It implements several power states which map to what we see as Sleep and On.
+
+The sleep policy is as follows:
+1. Sleep is prevented if the case is open so that nobody will think the machine
+   is off and plug/unplug cards.
+2. Sleep is prevented if the sleep timeout slider in the prefs panel is zero.
+3. System cannot Sleep if some object in the tree is in a power state marked
+   kIOPMPreventSystemSleep.
+
+These three conditions are enforced using the "driver clamp" by calling
+changePowerStateTo(). For example, if the case is opened,
+changePowerStateTo(ON_STATE) is called to hold the system on regardless
+of the desires of the children of the root or the state of the other clamp.
+
+Demand Sleep is initiated by pressing the front panel power button, closing
+the clamshell, or selecting the menu item. In this case the root's parent
+actually initiates the power state change so that the root domain has no
+choice and does not give applications the opportunity to veto the change.
+
+Idle Sleep occurs if no objects in the tree are in a state marked
+kIOPMPreventIdleSleep.  When this is true, the root's children are not holding
+the root on, so it sets the "policy-maker clamp" by calling
+changePowerStateToPriv(ON_STATE) to hold itself on until the sleep timer expires.
+This timer is set for the difference between the sleep timeout slider and the
+display dim timeout slider. When the timer expires, it releases its clamp and
+now nothing is holding it awake, so it falls asleep.
+
+Demand sleep is prevented when the system is booting.  When preferences are
+transmitted by the loginwindow at the end of boot, a flag is cleared,
+and this allows subsequent Demand Sleep.
 */
 
 //******************************************************************************
 */
 
 //******************************************************************************
@@ -487,34 +650,56 @@ IOPMrootDomain * IOPMrootDomain::construct( void )
 
 static void disk_sync_callout( thread_call_param_t p0, thread_call_param_t p1 )
 {
 
 static void disk_sync_callout( thread_call_param_t p0, thread_call_param_t p1 )
 {
-    IOService       *rootDomain = (IOService *) p0;
-    unsigned long   pmRef = (unsigned long) p1;
+    IOService * rootDomain = (IOService *) p0;
+    uint32_t    notifyRef  = (uint32_t)(uintptr_t) p1;
+    uint32_t    powerState = rootDomain->getPowerState();
 
 
-    DLOG("disk_sync_callout start\n");
+    DLOG("disk_sync_callout ps=%u\n", powerState);
 
 
-#if    HIBERNATION
-    IOHibernateSystemSleep();
+    if (ON_STATE == powerState)
+    {
+        sync_internal();
+    }
+#if HIBERNATION
+    else
+    {
+        IOHibernateSystemPostWake();
+    }
 #endif
 #endif
-    sync_internal();
-    rootDomain->allowPowerChange(pmRef);
+
+    rootDomain->allowPowerChange(notifyRef);
     DLOG("disk_sync_callout finish\n");
 }
 
 //******************************************************************************
 
     DLOG("disk_sync_callout finish\n");
 }
 
 //******************************************************************************
 
+static void hib_debugSetup_callout( thread_call_param_t p0, thread_call_param_t p1 )
+{
+    IOService * rootDomain = (IOService *) p0;
+    uint32_t    notifyRef  = (uint32_t)(uintptr_t) p1;
+
+#if    HIBERNATION
+    IOHibernateOpenForDebugData();
+#endif
+
+    rootDomain->allowPowerChange(notifyRef);
+    DLOG("hib_debugSetup_callout finish\n");
+}
+//******************************************************************************
+
 static UInt32 computeDeltaTimeMS( const AbsoluteTime * startTime )
 {
 static UInt32 computeDeltaTimeMS( const AbsoluteTime * startTime )
 {
-       AbsoluteTime    endTime;
-       UInt64                  nano = 0;
+    AbsoluteTime    endTime;
+    UInt64          nano = 0;
 
 
-       clock_get_uptime(&endTime);
-       if (CMP_ABSOLUTETIME(&endTime, startTime) > 0)
-       {
-               SUB_ABSOLUTETIME(&endTime, startTime);
-               absolutetime_to_nanoseconds(endTime, &nano);
-       }
+    clock_get_uptime(&endTime);
+    if (CMP_ABSOLUTETIME(&endTime, startTime) > 0)
+    {
+        SUB_ABSOLUTETIME(&endTime, startTime);
+        absolutetime_to_nanoseconds(endTime, &nano);
+    }
 
 
-       return (UInt32)(nano / 1000000ULL);
+    return (UInt32)(nano / 1000000ULL);
 }
 
 //******************************************************************************
 }
 
 //******************************************************************************
@@ -526,7 +711,7 @@ sysctl_sleepwaketime SYSCTL_HANDLER_ARGS
   struct proc *p = req->p;
 
   if (p == kernproc) {
   struct proc *p = req->p;
 
   if (p == kernproc) {
-    return sysctl_io_opaque(req, swt, sizeof(*swt), NULL);    
+    return sysctl_io_opaque(req, swt, sizeof(*swt), NULL);
   } else if(proc_is64bit(p)) {
     struct user64_timeval t;
     t.tv_sec = swt->tv_sec;
   } else if(proc_is64bit(p)) {
     struct user64_timeval t;
     t.tv_sec = swt->tv_sec;
@@ -541,12 +726,12 @@ sysctl_sleepwaketime SYSCTL_HANDLER_ARGS
 }
 
 static SYSCTL_PROC(_kern, OID_AUTO, sleeptime,
 }
 
 static SYSCTL_PROC(_kern, OID_AUTO, sleeptime,
-           CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_NOAUTO | CTLFLAG_KERN,
-           &gIOLastSleepTime, 0, sysctl_sleepwaketime, "S,timeval", "");
+        CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED,
+        &gIOLastSleepTime, 0, sysctl_sleepwaketime, "S,timeval", "");
 
 static SYSCTL_PROC(_kern, OID_AUTO, waketime,
 
 static SYSCTL_PROC(_kern, OID_AUTO, waketime,
-           CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_NOAUTO | CTLFLAG_KERN,
-           &gIOLastWakeTime, 0, sysctl_sleepwaketime, "S,timeval", "");
+        CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED,
+        &gIOLastWakeTime, 0, sysctl_sleepwaketime, "S,timeval", "");
 
 
 static int
 
 
 static int
@@ -556,19 +741,18 @@ sysctl_willshutdown
     int new_value, changed;
     int error = sysctl_io_number(req, gWillShutdown, sizeof(int), &new_value, &changed);
     if (changed) {
     int new_value, changed;
     int error = sysctl_io_number(req, gWillShutdown, sizeof(int), &new_value, &changed);
     if (changed) {
-       if (!gWillShutdown && (new_value == 1)) {
-           IOSystemShutdownNotification();
-       } else
-           error = EINVAL;
+    if (!gWillShutdown && (new_value == 1)) {
+        IOPMRootDomainWillShutdown();
+    } else
+        error = EINVAL;
     }
     return(error);
 }
 
 static SYSCTL_PROC(_kern, OID_AUTO, willshutdown,
     }
     return(error);
 }
 
 static SYSCTL_PROC(_kern, OID_AUTO, willshutdown,
-           CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN,
-           0, 0, sysctl_willshutdown, "I", "");
+        CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED,
+        0, 0, sysctl_willshutdown, "I", "");
 
 
-#if !CONFIG_EMBEDDED
 
 static int
 sysctl_progressmeterenable
 
 static int
 sysctl_progressmeterenable
@@ -577,10 +761,9 @@ sysctl_progressmeterenable
     int error;
     int new_value, changed;
 
     int error;
     int new_value, changed;
 
-    error = sysctl_io_number(req, vc_progress_meter_enable, sizeof(int), &new_value, &changed);
+    error = sysctl_io_number(req, vc_progressmeter_enable, sizeof(int), &new_value, &changed);
 
 
-    if (changed)
-       vc_enable_progressmeter(new_value);
+    if (changed) vc_enable_progressmeter(new_value);
 
     return (error);
 }
 
     return (error);
 }
@@ -592,60 +775,94 @@ sysctl_progressmeter
     int error;
     int new_value, changed;
 
     int error;
     int new_value, changed;
 
-    error = sysctl_io_number(req, vc_progress_meter_value, sizeof(int), &new_value, &changed);
+    error = sysctl_io_number(req, vc_progressmeter_value, sizeof(int), &new_value, &changed);
 
 
-    if (changed)
-       vc_set_progressmeter(new_value);
+    if (changed) vc_set_progressmeter(new_value);
 
     return (error);
 }
 
 static SYSCTL_PROC(_kern, OID_AUTO, progressmeterenable,
 
     return (error);
 }
 
 static SYSCTL_PROC(_kern, OID_AUTO, progressmeterenable,
-           CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN,
-           0, 0, sysctl_progressmeterenable, "I", "");
+        CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED,
+        0, 0, sysctl_progressmeterenable, "I", "");
 
 static SYSCTL_PROC(_kern, OID_AUTO, progressmeter,
 
 static SYSCTL_PROC(_kern, OID_AUTO, progressmeter,
-           CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN,
-           0, 0, sysctl_progressmeter, "I", "");
+        CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED,
+        0, 0, sysctl_progressmeter, "I", "");
 
 
-#endif
 
 
+static int
+sysctl_wakereason SYSCTL_HANDLER_ARGS
+{
+    char wr[ sizeof(gWakeReasonString) ];
+
+    wr[0] = '\0';
+    if (gRootDomain)
+        gRootDomain->copyWakeReasonString(wr, sizeof(wr));
+
+    return sysctl_io_string(req, wr, 0, 0, NULL);
+}
+
+SYSCTL_PROC(_kern, OID_AUTO, wakereason,
+    CTLTYPE_STRING| CTLFLAG_RD | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED,
+    NULL, 0, sysctl_wakereason, "A", "wakereason");
+
+static SYSCTL_INT(_debug, OID_AUTO, darkwake, CTLFLAG_RW, &gDarkWakeFlags, 0, "");
+
+static const OSSymbol * gIOPMSettingAutoWakeCalendarKey;
 static const OSSymbol * gIOPMSettingAutoWakeSecondsKey;
 static const OSSymbol * gIOPMSettingAutoWakeSecondsKey;
+static const OSSymbol * gIOPMSettingDebugWakeRelativeKey;
 static const OSSymbol * gIOPMSettingMaintenanceWakeCalendarKey;
 static const OSSymbol * gIOPMSettingMaintenanceWakeCalendarKey;
+static const OSSymbol * gIOPMSettingSleepServiceWakeCalendarKey;
+static const OSSymbol * gIOPMSettingSilentRunningKey;
+static const OSSymbol * gIOPMUserTriggeredFullWakeKey;
+static const OSSymbol * gIOPMUserIsActiveKey;
 
 //******************************************************************************
 // start
 //
 //******************************************************************************
 
 
 //******************************************************************************
 // start
 //
 //******************************************************************************
 
-#define kRootDomainSettingsCount        16
+#define kRootDomainSettingsCount        17
 
 bool IOPMrootDomain::start( IOService * nub )
 {
     OSIterator      *psIterator;
     OSDictionary    *tmpDict;
 
 bool IOPMrootDomain::start( IOService * nub )
 {
     OSIterator      *psIterator;
     OSDictionary    *tmpDict;
+    IORootParent *   patriarch;
+#if defined(__i386__) || defined(__x86_64__)
+    IONotifier   *   notifier;
+#endif
 
     super::start(nub);
 
     gRootDomain = this;
 
     super::start(nub);
 
     gRootDomain = this;
+    gIOPMSettingAutoWakeCalendarKey = OSSymbol::withCString(kIOPMSettingAutoWakeCalendarKey);
     gIOPMSettingAutoWakeSecondsKey = OSSymbol::withCString(kIOPMSettingAutoWakeSecondsKey);
     gIOPMSettingAutoWakeSecondsKey = OSSymbol::withCString(kIOPMSettingAutoWakeSecondsKey);
-    gIOPMSettingMaintenanceWakeCalendarKey =
-        OSSymbol::withCString(kIOPMSettingMaintenanceWakeCalendarKey);
+    gIOPMSettingDebugWakeRelativeKey = OSSymbol::withCString(kIOPMSettingDebugWakeRelativeKey);
+    gIOPMSettingMaintenanceWakeCalendarKey = OSSymbol::withCString(kIOPMSettingMaintenanceWakeCalendarKey);
+    gIOPMSettingSleepServiceWakeCalendarKey = OSSymbol::withCString(kIOPMSettingSleepServiceWakeCalendarKey);
+    gIOPMSettingSilentRunningKey = OSSymbol::withCStringNoCopy(kIOPMSettingSilentRunningKey);
+    gIOPMUserTriggeredFullWakeKey = OSSymbol::withCStringNoCopy(kIOPMUserTriggeredFullWakeKey);
+    gIOPMUserIsActiveKey = OSSymbol::withCStringNoCopy(kIOPMUserIsActiveKey);
 
     gIOPMStatsApplicationResponseTimedOut = OSSymbol::withCString(kIOPMStatsResponseTimedOut);
     gIOPMStatsApplicationResponseCancel = OSSymbol::withCString(kIOPMStatsResponseCancel);
     gIOPMStatsApplicationResponseSlow = OSSymbol::withCString(kIOPMStatsResponseSlow);
 
     gIOPMStatsApplicationResponseTimedOut = OSSymbol::withCString(kIOPMStatsResponseTimedOut);
     gIOPMStatsApplicationResponseCancel = OSSymbol::withCString(kIOPMStatsResponseCancel);
     gIOPMStatsApplicationResponseSlow = OSSymbol::withCString(kIOPMStatsResponseSlow);
+    gIOPMStatsApplicationResponsePrompt = OSSymbol::withCString(kIOPMStatsResponsePrompt);
+    gIOPMStatsDriverPSChangeSlow = OSSymbol::withCString(kIOPMStatsDriverPSChangeSlow);
 
     sleepSupportedPEFunction = OSSymbol::withCString("IOPMSetSleepSupported");
 
     sleepSupportedPEFunction = OSSymbol::withCString("IOPMSetSleepSupported");
+    sleepMessagePEFunction = OSSymbol::withCString("IOPMSystemSleepMessage");
 
 
-    const OSSymbol  *settingsArr[kRootDomainSettingsCount] = 
+    const OSSymbol  *settingsArr[kRootDomainSettingsCount] =
         {
             OSSymbol::withCString(kIOPMSettingSleepOnPowerButtonKey),
             gIOPMSettingAutoWakeSecondsKey,
             OSSymbol::withCString(kIOPMSettingAutoPowerSecondsKey),
         {
             OSSymbol::withCString(kIOPMSettingSleepOnPowerButtonKey),
             gIOPMSettingAutoWakeSecondsKey,
             OSSymbol::withCString(kIOPMSettingAutoPowerSecondsKey),
-            OSSymbol::withCString(kIOPMSettingAutoWakeCalendarKey),
+            gIOPMSettingAutoWakeCalendarKey,
             OSSymbol::withCString(kIOPMSettingAutoPowerCalendarKey),
             OSSymbol::withCString(kIOPMSettingAutoPowerCalendarKey),
-            OSSymbol::withCString(kIOPMSettingDebugWakeRelativeKey),
+            gIOPMSettingDebugWakeRelativeKey,
             OSSymbol::withCString(kIOPMSettingDebugPowerRelativeKey),
             OSSymbol::withCString(kIOPMSettingWakeOnRingKey),
             OSSymbol::withCString(kIOPMSettingRestartOnPowerLossKey),
             OSSymbol::withCString(kIOPMSettingDebugPowerRelativeKey),
             OSSymbol::withCString(kIOPMSettingWakeOnRingKey),
             OSSymbol::withCString(kIOPMSettingRestartOnPowerLossKey),
@@ -655,67 +872,95 @@ bool IOPMrootDomain::start( IOService * nub )
             OSSymbol::withCString(kIOPMSettingDisplaySleepUsesDimKey),
             OSSymbol::withCString(kIOPMSettingMobileMotionModuleKey),
             OSSymbol::withCString(kIOPMSettingGraphicsSwitchKey),
             OSSymbol::withCString(kIOPMSettingDisplaySleepUsesDimKey),
             OSSymbol::withCString(kIOPMSettingMobileMotionModuleKey),
             OSSymbol::withCString(kIOPMSettingGraphicsSwitchKey),
-            OSSymbol::withCString(kIOPMStateConsoleShutdown)
+            OSSymbol::withCString(kIOPMStateConsoleShutdown),
+            gIOPMSettingSilentRunningKey
         };
 
         };
 
+    PE_parse_boot_argn("darkwake", &gDarkWakeFlags, sizeof(gDarkWakeFlags));
+
     queue_init(&aggressivesQueue);
     aggressivesThreadCall = thread_call_allocate(handleAggressivesFunction, this);
     aggressivesData = OSData::withCapacity(
                         sizeof(AggressivesRecord) * (kPMLastAggressivenessType + 4));
 
     featuresDictLock = IOLockAlloc();
     queue_init(&aggressivesQueue);
     aggressivesThreadCall = thread_call_allocate(handleAggressivesFunction, this);
     aggressivesData = OSData::withCapacity(
                         sizeof(AggressivesRecord) * (kPMLastAggressivenessType + 4));
 
     featuresDictLock = IOLockAlloc();
-    settingsCtrlLock = IORecursiveLockAlloc();
+    settingsCtrlLock = IOLockAlloc();
+    wakeEventLock = IOLockAlloc();
     setPMRootDomain(this);
     setPMRootDomain(this);
-    
+
     extraSleepTimer = thread_call_allocate(
                         idleSleepTimerExpired,
                         (thread_call_param_t) this);
 
     extraSleepTimer = thread_call_allocate(
                         idleSleepTimerExpired,
                         (thread_call_param_t) this);
 
-    clamshellWakeupIgnore = thread_call_allocate(
-                        wakeupClamshellTimerExpired,
-                        (thread_call_param_t) this);
-
     diskSyncCalloutEntry = thread_call_allocate(
                         &disk_sync_callout,
                         (thread_call_param_t) this);
     diskSyncCalloutEntry = thread_call_allocate(
                         &disk_sync_callout,
                         (thread_call_param_t) this);
-    
-    canSleep = true;
+    hibDebugSetupEntry = thread_call_allocate(
+                        &hib_debugSetup_callout,
+                        (thread_call_param_t) this);
+
+#if DARK_TO_FULL_EVALUATE_CLAMSHELL
+    fullWakeThreadCall = thread_call_allocate(
+                            OSMemberFunctionCast(thread_call_func_t, this,
+                                &IOPMrootDomain::fullWakeDelayedWork),
+                            (thread_call_param_t) this);
+#endif
+
     setProperty(kIOSleepSupportedKey, true);
 
     setProperty(kIOSleepSupportedKey, true);
 
-    bzero(&pmStats, sizeof(pmStats));
+    bzero(&gPMStats, sizeof(gPMStats));
 
     pmTracer = PMTraceWorker::tracer(this);
 
 
     pmTracer = PMTraceWorker::tracer(this);
 
-    updateRunState(kRStateNormal);
+    pmAssertions = PMAssertionsTracker::pmAssertionsTracker(this);
+
     userDisabledAllSleep = false;
     userDisabledAllSleep = false;
-    allowSleep = true;
-    sleepIsSupported = true;
     systemBooting = true;
     sleepSlider = 0;
     idleSleepTimerPending = false;
     wrangler = NULL;
     systemBooting = true;
     sleepSlider = 0;
     idleSleepTimerPending = false;
     wrangler = NULL;
-    sleepASAP = false;
-    clamshellIsClosed = false;
-    clamshellExists = false;
-    ignoringClamshell = true;
-    ignoringClamshellOnWake = false;
+    clamshellClosed    = false;
+    clamshellExists    = false;
+    clamshellDisabled  = true;
     acAdaptorConnected = true;
     acAdaptorConnected = true;
+    clamshellSleepDisabled = false;
+    gWakeReasonString[0] = '\0';
+
+    // Initialize to user active.
+    // Will never transition to user inactive w/o wrangler.
+    fullWakeReason = kFullWakeReasonLocalUser;
+    userIsActive = userWasActive = true;
+    setProperty(gIOPMUserIsActiveKey, kOSBooleanTrue);
+
+    // Set the default system capabilities at boot.
+    _currentCapability = kIOPMSystemCapabilityCPU      |
+                         kIOPMSystemCapabilityGraphics |
+                         kIOPMSystemCapabilityAudio    |
+                         kIOPMSystemCapabilityNetwork;
+
+    _pendingCapability = _currentCapability;
+    _desiredCapability = _currentCapability;
+    _highestCapability = _currentCapability;
+    setProperty(kIOPMSystemCapabilitiesKey, _currentCapability, 64);
 
     queuedSleepWakeUUIDString = NULL;
 
     queuedSleepWakeUUIDString = NULL;
+    initializeBootSessionUUID();
     pmStatsAppResponses     = OSArray::withCapacity(5);
     _statsNameKey           = OSSymbol::withCString(kIOPMStatsNameKey);
     _statsPIDKey            = OSSymbol::withCString(kIOPMStatsPIDKey);
     _statsTimeMSKey         = OSSymbol::withCString(kIOPMStatsTimeMSKey);
     _statsResponseTypeKey   = OSSymbol::withCString(kIOPMStatsApplicationResponseTypeKey);
     _statsMessageTypeKey    = OSSymbol::withCString(kIOPMStatsMessageTypeKey);
     pmStatsAppResponses     = OSArray::withCapacity(5);
     _statsNameKey           = OSSymbol::withCString(kIOPMStatsNameKey);
     _statsPIDKey            = OSSymbol::withCString(kIOPMStatsPIDKey);
     _statsTimeMSKey         = OSSymbol::withCString(kIOPMStatsTimeMSKey);
     _statsResponseTypeKey   = OSSymbol::withCString(kIOPMStatsApplicationResponseTypeKey);
     _statsMessageTypeKey    = OSSymbol::withCString(kIOPMStatsMessageTypeKey);
+    _statsPowerCapsKey      = OSSymbol::withCString(kIOPMStatsPowerCapabilityKey);
 
 
+    pmStatsLock = IOLockAlloc();
     idxPMCPUClamshell = kCPUUnknownIndex;
     idxPMCPULimitedPower = kCPUUnknownIndex;
     idxPMCPUClamshell = kCPUUnknownIndex;
     idxPMCPULimitedPower = kCPUUnknownIndex;
-        
+
     tmpDict = OSDictionary::withCapacity(1);
     setProperty(kRootDomainSupportedFeatures, tmpDict);
     tmpDict->release();
     tmpDict = OSDictionary::withCapacity(1);
     setProperty(kRootDomainSupportedFeatures, tmpDict);
     tmpDict->release();
-    
+
     settingsCallbacks = OSDictionary::withCapacity(1);
 
     // Create a list of the valid PM settings that we'll relay to
     settingsCallbacks = OSDictionary::withCapacity(1);
 
     // Create a list of the valid PM settings that we'll relay to
@@ -724,8 +969,15 @@ bool IOPMrootDomain::start( IOService * nub )
                     (const OSObject **)settingsArr,
                     kRootDomainSettingsCount,
                     0);
                     (const OSObject **)settingsArr,
                     kRootDomainSettingsCount,
                     0);
-                    
+
+    // List of PM settings that should not automatically publish itself
+    // as a feature when registered by a listener.
+    noPublishPMSettings = OSArray::withObjects(
+                    (const OSObject **) &gIOPMSettingSilentRunningKey, 1, 0);
+
     fPMSettingsDict = OSDictionary::withCapacity(5);
     fPMSettingsDict = OSDictionary::withCapacity(5);
+    preventIdleSleepList = OSSet::withCapacity(8);
+    preventSystemSleepList = OSSet::withCapacity(2);
 
     PMinit();   // creates gIOPMWorkLoop
 
 
     PMinit();   // creates gIOPMWorkLoop
 
@@ -747,8 +999,6 @@ bool IOPMrootDomain::start( IOService * nub )
     patriarch->addPowerChild(this);
 
     registerPowerDriver(this, ourPowerStates, NUM_POWER_STATES);
     patriarch->addPowerChild(this);
 
     registerPowerDriver(this, ourPowerStates, NUM_POWER_STATES);
-
-    // set a clamp until we sleep
     changePowerStateToPriv(ON_STATE);
 
     // install power change handler
     changePowerStateToPriv(ON_STATE);
 
     // install power change handler
@@ -758,24 +1008,38 @@ bool IOPMrootDomain::start( IOService * nub )
     // Register for a notification when IODisplayWrangler is published
     if ((tmpDict = serviceMatching("IODisplayWrangler")))
     {
     // Register for a notification when IODisplayWrangler is published
     if ((tmpDict = serviceMatching("IODisplayWrangler")))
     {
-        _displayWranglerNotifier = addMatchingNotification( 
-                gIOPublishNotification, tmpDict, 
-                (IOServiceMatchingNotificationHandler) &displayWranglerPublished,
+        _displayWranglerNotifier = addMatchingNotification(
+                gIOPublishNotification, tmpDict,
+                (IOServiceMatchingNotificationHandler) &displayWranglerMatchPublished,
                 this, 0);
         tmpDict->release();
     }
 #endif
 
                 this, 0);
         tmpDict->release();
     }
 #endif
 
-    // Battery location published - ApplePMU support only
-    if ((tmpDict = serviceMatching("IOPMPowerSource")))
+#if defined(__i386__) || defined(__x86_64__)
+
+    if ((tmpDict = serviceMatching("IODTNVRAM")))
     {
     {
-        _batteryPublishNotifier = addMatchingNotification( 
-                gIOPublishNotification, tmpDict, 
-                (IOServiceMatchingNotificationHandler) &batteryPublished,
-                this, this);
+        notifier = addMatchingNotification(
+                gIOFirstPublishNotification, tmpDict,
+                (IOServiceMatchingNotificationHandler) &IONVRAMMatchPublished,
+                this, 0);
         tmpDict->release();
     }
 
         tmpDict->release();
     }
 
+    wranglerIdleSettings = NULL;
+    OSNumber * wranglerIdlePeriod = NULL;
+    wranglerIdleSettings = OSDictionary::withCapacity(1);
+    wranglerIdlePeriod  = OSNumber::withNumber(kDefaultWranglerIdlePeriod, 32);
+
+    if(wranglerIdleSettings && wranglerIdlePeriod)
+        wranglerIdleSettings->setObject(kIORequestWranglerIdleKey,
+                                        wranglerIdlePeriod);
+
+    if(wranglerIdlePeriod)
+        wranglerIdlePeriod->release();
+#endif
+
     const OSSymbol *ucClassName = OSSymbol::withCStringNoCopy("RootDomainUserClient");
     setProperty(gIOUserClientClassKey, (OSObject *) ucClassName);
     ucClassName->release();
     const OSSymbol *ucClassName = OSSymbol::withCStringNoCopy("RootDomainUserClient");
     setProperty(gIOUserClientClassKey, (OSObject *) ucClassName);
     ucClassName->release();
@@ -797,21 +1061,19 @@ bool IOPMrootDomain::start( IOService * nub )
     sysctl_register_oid(&sysctl__kern_sleeptime);
     sysctl_register_oid(&sysctl__kern_waketime);
     sysctl_register_oid(&sysctl__kern_willshutdown);
     sysctl_register_oid(&sysctl__kern_sleeptime);
     sysctl_register_oid(&sysctl__kern_waketime);
     sysctl_register_oid(&sysctl__kern_willshutdown);
-#if !CONFIG_EMBEDDED
     sysctl_register_oid(&sysctl__kern_progressmeterenable);
     sysctl_register_oid(&sysctl__kern_progressmeter);
     sysctl_register_oid(&sysctl__kern_progressmeterenable);
     sysctl_register_oid(&sysctl__kern_progressmeter);
-#endif /* !CONFIG_EMBEDDED */
+    sysctl_register_oid(&sysctl__kern_wakereason);
 
 
-#if    HIBERNATION
+#if HIBERNATION
     IOHibernateSystemInit(this);
 #endif
 
     IOHibernateSystemInit(this);
 #endif
 
-    registerService();                                         // let clients find us
+    registerService();                      // let clients find us
 
     return true;
 }
 
 
     return true;
 }
 
-
 //******************************************************************************
 // setProperties
 //
 //******************************************************************************
 // setProperties
 //
@@ -825,307 +1087,241 @@ IOReturn IOPMrootDomain::setProperties( OSObject * props_obj )
     OSDictionary    *dict = OSDynamicCast(OSDictionary, props_obj);
     OSBoolean       *b;
     OSNumber        *n;
     OSDictionary    *dict = OSDynamicCast(OSDictionary, props_obj);
     OSBoolean       *b;
     OSNumber        *n;
-    OSString        *str;
-    OSSymbol        *type;
+    const OSSymbol  *key;
     OSObject        *obj;
     OSObject        *obj;
-    unsigned int    i;
-
-    const OSSymbol *boot_complete_string = 
-                OSSymbol::withCString("System Boot Complete");
-    const OSSymbol *sys_shutdown_string = 
-                OSSymbol::withCString("System Shutdown");
-    const OSSymbol *stall_halt_string = 
-                OSSymbol::withCString("StallSystemAtHalt");
-    const OSSymbol *battery_warning_disabled_string = 
-                OSSymbol::withCString("BatteryWarningsDisabled");
-    const OSSymbol *idle_seconds_string = 
-                OSSymbol::withCString("System Idle Seconds");
-#if    HIBERNATION
-    const OSSymbol *hibernatemode_string = 
-                OSSymbol::withCString(kIOHibernateModeKey);
-    const OSSymbol *hibernatefile_string = 
-                OSSymbol::withCString(kIOHibernateFileKey);
-    const OSSymbol *hibernatefreeratio_string = 
-                OSSymbol::withCString(kIOHibernateFreeRatioKey);
-    const OSSymbol *hibernatefreetime_string = 
-                OSSymbol::withCString(kIOHibernateFreeTimeKey);
+    OSCollectionIterator * iter = 0;
+
+    const OSSymbol *publish_simulated_battery_string    = OSSymbol::withCString("SoftwareSimulatedBatteries");
+    const OSSymbol *boot_complete_string                = OSSymbol::withCString("System Boot Complete");
+    const OSSymbol *sys_shutdown_string                 = OSSymbol::withCString("System Shutdown");
+    const OSSymbol *stall_halt_string                   = OSSymbol::withCString("StallSystemAtHalt");
+    const OSSymbol *battery_warning_disabled_string     = OSSymbol::withCString("BatteryWarningsDisabled");
+    const OSSymbol *idle_seconds_string                 = OSSymbol::withCString("System Idle Seconds");
+    const OSSymbol *sleepdisabled_string                = OSSymbol::withCString("SleepDisabled");
+    const OSSymbol *ondeck_sleepwake_uuid_string        = OSSymbol::withCString(kIOPMSleepWakeUUIDKey);
+    const OSSymbol *loginwindow_tracepoint_string       = OSSymbol::withCString(kIOPMLoginWindowSecurityDebugKey);
+#if HIBERNATION
+    const OSSymbol *hibernatemode_string                = OSSymbol::withCString(kIOHibernateModeKey);
+    const OSSymbol *hibernatefile_string                = OSSymbol::withCString(kIOHibernateFileKey);
+    const OSSymbol *hibernatefilemin_string            = OSSymbol::withCString(kIOHibernateFileMinSizeKey);
+    const OSSymbol *hibernatefilemax_string            = OSSymbol::withCString(kIOHibernateFileMaxSizeKey);
+    const OSSymbol *hibernatefreeratio_string           = OSSymbol::withCString(kIOHibernateFreeRatioKey);
+    const OSSymbol *hibernatefreetime_string            = OSSymbol::withCString(kIOHibernateFreeTimeKey);
 #endif
 #endif
-    const OSSymbol *sleepdisabled_string =
-                OSSymbol::withCString("SleepDisabled");
-    const OSSymbol *ondeck_sleepwake_uuid_string =
-                OSSymbol::withCString(kIOPMSleepWakeUUIDKey);
-    const OSSymbol *loginwindow_tracepoint_string = 
-                OSSymbol::withCString(kIOPMLoginWindowSecurityDebugKey);
-                
-    if(!dict) 
+
+    if (!dict)
     {
         return_value = kIOReturnBadArgument;
         goto exit;
     }
 
     {
         return_value = kIOReturnBadArgument;
         goto exit;
     }
 
-    if ((n = OSDynamicCast(OSNumber, dict->getObject(idle_seconds_string))))
-    {
-        setProperty(idle_seconds_string, n);
-        idleSeconds = n->unsigned32BitValue();
-    }
-
-    if (boot_complete_string && dict->getObject(boot_complete_string)) 
-    {
-        pmPowerStateQueue->submitPowerEvent( kPowerEventSystemBootCompleted );
-    }
-
-    if( battery_warning_disabled_string
-        && dict->getObject(battery_warning_disabled_string))
-    {
-        setProperty( battery_warning_disabled_string, 
-                        dict->getObject(battery_warning_disabled_string));
-    }
-    
-    if( sys_shutdown_string 
-        && (b = OSDynamicCast(OSBoolean, dict->getObject(sys_shutdown_string)))) 
+    iter = OSCollectionIterator::withCollection(dict);
+    if (!iter)
     {
     {
-        pmPowerStateQueue->submitPowerEvent(kPowerEventSystemShutdown, (void *) b);
-    }
-    
-    if( stall_halt_string
-        && (b = OSDynamicCast(OSBoolean, dict->getObject(stall_halt_string))) ) 
-    {
-        setProperty(stall_halt_string, b);
+        return_value = kIOReturnNoMemory;
+        goto exit;
     }
 
     }
 
-#if    HIBERNATION
-    if ( hibernatemode_string
-    && (n = OSDynamicCast(OSNumber, dict->getObject(hibernatemode_string))))
+    while ((key = (const OSSymbol *) iter->getNextObject()) &&
+           (obj = dict->getObject(key)))
     {
     {
-       setProperty(hibernatemode_string, n);
-    }
-    if ( hibernatefreeratio_string
-    && (n = OSDynamicCast(OSNumber, dict->getObject(hibernatefreeratio_string))))
-    {
-        setProperty(hibernatefreeratio_string, n);
-    }
-    if ( hibernatefreetime_string
-    && (n = OSDynamicCast(OSNumber, dict->getObject(hibernatefreetime_string))))
-    {
-        setProperty(hibernatefreetime_string, n);
-    }
-    if ( hibernatefile_string
-    && (str = OSDynamicCast(OSString, dict->getObject(hibernatefile_string))))
-    {
-        setProperty(hibernatefile_string, str);
-    }
+        if (key->isEqualTo(publish_simulated_battery_string))
+        {
+            if (OSDynamicCast(OSBoolean, obj))
+                publishResource(key, kOSBooleanTrue);
+        }
+        else if (key->isEqualTo(idle_seconds_string))
+        {
+            if ((n = OSDynamicCast(OSNumber, obj)))
+            {
+                setProperty(key, n);
+                idleSeconds = n->unsigned32BitValue();
+            }
+        }
+        else if (key->isEqualTo(boot_complete_string))
+        {
+            pmPowerStateQueue->submitPowerEvent(kPowerEventSystemBootCompleted);
+        }
+        else if (key->isEqualTo(sys_shutdown_string))
+        {
+            if ((b = OSDynamicCast(OSBoolean, obj)))
+                pmPowerStateQueue->submitPowerEvent(kPowerEventSystemShutdown, (void *) b);
+        }
+        else if (key->isEqualTo(battery_warning_disabled_string))
+        {
+            setProperty(key, obj);
+        }
+#if HIBERNATION
+        else if (key->isEqualTo(hibernatemode_string) ||
+                 key->isEqualTo(hibernatefilemin_string) ||
+                 key->isEqualTo(hibernatefilemax_string) ||
+                 key->isEqualTo(hibernatefreeratio_string) ||
+                 key->isEqualTo(hibernatefreetime_string))
+        {
+            if ((n = OSDynamicCast(OSNumber, obj)))
+                setProperty(key, n);
+        }
+        else if (key->isEqualTo(hibernatefile_string))
+        {
+            OSString * str = OSDynamicCast(OSString, obj);
+            if (str) setProperty(key, str);
+        }
 #endif
 #endif
-    
-    if( sleepdisabled_string
-        && (b = OSDynamicCast(OSBoolean, dict->getObject(sleepdisabled_string))) )
-    {
-        setProperty(sleepdisabled_string, b);
-        pmPowerStateQueue->submitPowerEvent(kPowerEventUserDisabledSleep, (void *) b);
-    }
-    
-    if (ondeck_sleepwake_uuid_string
-        && (obj = dict->getObject(ondeck_sleepwake_uuid_string)))
-    {
-        // Clear the currently published UUID
-        if (kOSBooleanFalse == obj) 
+        else if (key->isEqualTo(sleepdisabled_string))
+        {
+            if ((b = OSDynamicCast(OSBoolean, obj)))
+            {
+                setProperty(key, b);
+                pmPowerStateQueue->submitPowerEvent(kPowerEventUserDisabledSleep, (void *) b);
+            }
+        }
+        else if (key->isEqualTo(ondeck_sleepwake_uuid_string))
+        {
+            obj->retain();
+            pmPowerStateQueue->submitPowerEvent(kPowerEventQueueSleepWakeUUID, (void *)obj);
+        }
+        else if (key->isEqualTo(loginwindow_tracepoint_string))
+        {
+            if (pmTracer && (n = OSDynamicCast(OSNumber, obj)))
+                pmTracer->traceLoginWindowPhase(n->unsigned8BitValue());
+        }
+        else if (key->isEqualTo(kIOPMDeepSleepEnabledKey)       ||
+                 key->isEqualTo(kIOPMDestroyFVKeyOnStandbyKey)  ||
+                 key->isEqualTo(kIOPMAutoPowerOffEnabledKey)    ||
+                 key->isEqualTo(stall_halt_string))
+        {
+            if ((b = OSDynamicCast(OSBoolean, obj)))
+                setProperty(key, b);
+        }
+        else if (key->isEqualTo(kIOPMDeepSleepDelayKey) ||
+                 key->isEqualTo(kIOPMAutoPowerOffDelayKey) ||
+                 key->isEqualTo(kIOPMAutoPowerOffTimerKey))
+        {
+            if ((n = OSDynamicCast(OSNumber, obj)))
+                setProperty(key, n);
+        }
+        else if (key->isEqualTo(kIOPMUserWakeAlarmScheduledKey))
         {
         {
-            publishSleepWakeUUID(NULL);
+            if (kOSBooleanTrue == obj)
+                OSBitOrAtomic(kIOPMAlarmBitCalendarWake, &_userScheduledAlarm);
+            else
+                OSBitAndAtomic(~kIOPMAlarmBitCalendarWake, &_userScheduledAlarm);
+            DLOG("_userScheduledAlarm = 0x%x\n", (uint32_t) _userScheduledAlarm);
         }
 
         }
 
-        // Cache UUID for an upcoming sleep/wake        
-        if ((str = OSDynamicCast(OSString, obj))) 
+        // Relay our allowed PM settings onto our registered PM clients
+        else if ((allowedPMSettings->getNextIndexOfObject(key, 0) != (unsigned int) -1))
         {
         {
-            if (queuedSleepWakeUUIDString) {
-                queuedSleepWakeUUIDString->release();
-                queuedSleepWakeUUIDString = NULL;
+            if ((gIOPMSettingAutoWakeSecondsKey == key) && ((n = OSDynamicCast(OSNumber, obj))))
+            {
+                UInt32 rsecs = n->unsigned32BitValue();
+                if (!rsecs)
+                autoWakeStart = autoWakeEnd = 0;
+                else
+                {
+                AbsoluteTime deadline;
+                clock_interval_to_deadline(rsecs + kAutoWakePostWindow, kSecondScale, &deadline);
+                autoWakeEnd = AbsoluteTime_to_scalar(&deadline);
+                if (rsecs > kAutoWakePreWindow)
+                    rsecs -= kAutoWakePreWindow;
+                else
+                    rsecs = 0;
+                clock_interval_to_deadline(rsecs, kSecondScale, &deadline);
+                autoWakeStart = AbsoluteTime_to_scalar(&deadline);
+                }
             }
             }
-            queuedSleepWakeUUIDString = str;
-            queuedSleepWakeUUIDString->retain();
-            DLOG("SleepWake UUID queued: %s\n",
-                queuedSleepWakeUUIDString->getCStringNoCopy());
-        }
-    }
-    
-    if (loginwindow_tracepoint_string
-        && (n = OSDynamicCast(OSNumber, dict->getObject(loginwindow_tracepoint_string)))
-        && pmTracer)
-    {
-        pmTracer->traceLoginWindowPhase( n->unsigned8BitValue() );
-    }
-
-    // Relay our allowed PM settings onto our registered PM clients
-    for(i = 0; i < allowedPMSettings->getCount(); i++) {
-
-        type = (OSSymbol *)allowedPMSettings->getObject(i);
-        if(!type) continue;
-
-        obj = dict->getObject(type);
-        if(!obj) continue;
-
-       if ((gIOPMSettingAutoWakeSecondsKey == type) && ((n = OSDynamicCast(OSNumber, obj))))
-       {
-           UInt32 rsecs = n->unsigned32BitValue();
-           if (!rsecs)
-               autoWakeStart = autoWakeEnd = 0;
-           else
-           {
-               AbsoluteTime deadline;
-               clock_interval_to_deadline(rsecs + kAutoWakePostWindow, kSecondScale, &deadline);
-               autoWakeEnd = AbsoluteTime_to_scalar(&deadline);
-               if (rsecs > kAutoWakePreWindow)
-                   rsecs -= kAutoWakePreWindow;
-               else
-                   rsecs = 0;
-               clock_interval_to_deadline(rsecs, kSecondScale, &deadline);
-               autoWakeStart = AbsoluteTime_to_scalar(&deadline);
-           }
-       }
-        
-        return_value = setPMSetting(type, obj);
-        
-        if(kIOReturnSuccess != return_value) goto exit;
+
+            return_value = setPMSetting(key, obj);
+            if (kIOReturnSuccess != return_value)
+                break;
+
+            if (gIOPMSettingDebugWakeRelativeKey == key)
+            {
+                if ((n = OSDynamicCast(OSNumber, obj)) &&
+                    (_debugWakeSeconds = n->unsigned32BitValue()))
+                {
+                    OSBitOrAtomic(kIOPMAlarmBitDebugWake, &_scheduledAlarms);
+                }
+                else
+                {
+                    _debugWakeSeconds = 0;
+                    OSBitAndAtomic(~kIOPMAlarmBitDebugWake, &_scheduledAlarms);
+                }
+                DLOG("_scheduledAlarms = 0x%x\n", (uint32_t) _scheduledAlarms);
+            }
+            else if (gIOPMSettingAutoWakeCalendarKey == key)
+            {
+                OSData * data;
+                if ((data = OSDynamicCast(OSData, obj)) &&
+                    (data->getLength() == sizeof(IOPMCalendarStruct)))
+                {
+                    const IOPMCalendarStruct * cs =
+                        (const IOPMCalendarStruct *) data->getBytesNoCopy();
+
+                    if (cs->year)
+                        OSBitOrAtomic(kIOPMAlarmBitCalendarWake, &_scheduledAlarms);
+                    else
+                        OSBitAndAtomic(~kIOPMAlarmBitCalendarWake, &_scheduledAlarms);
+                    DLOG("_scheduledAlarms = 0x%x\n", (uint32_t) _scheduledAlarms);
+                }
+            }
+        }
+        else
+        {
+            DLOG("setProperties(%s) not handled\n", key->getCStringNoCopy());
+        }
     }
 
 exit:
     }
 
 exit:
+    if(publish_simulated_battery_string) publish_simulated_battery_string->release();
     if(boot_complete_string) boot_complete_string->release();
     if(sys_shutdown_string) sys_shutdown_string->release();
     if(stall_halt_string) stall_halt_string->release();
     if(boot_complete_string) boot_complete_string->release();
     if(sys_shutdown_string) sys_shutdown_string->release();
     if(stall_halt_string) stall_halt_string->release();
-    if (battery_warning_disabled_string) battery_warning_disabled_string->release();
+    if(battery_warning_disabled_string) battery_warning_disabled_string->release();
     if(idle_seconds_string) idle_seconds_string->release();
     if(sleepdisabled_string) sleepdisabled_string->release();
     if(ondeck_sleepwake_uuid_string) ondeck_sleepwake_uuid_string->release();
     if(idle_seconds_string) idle_seconds_string->release();
     if(sleepdisabled_string) sleepdisabled_string->release();
     if(ondeck_sleepwake_uuid_string) ondeck_sleepwake_uuid_string->release();
-#if    HIBERNATION
+    if(loginwindow_tracepoint_string) loginwindow_tracepoint_string->release();
+#if HIBERNATION
     if(hibernatemode_string) hibernatemode_string->release();
     if(hibernatefile_string) hibernatefile_string->release();
     if(hibernatefreeratio_string) hibernatefreeratio_string->release();
     if(hibernatefreetime_string) hibernatefreetime_string->release();
 #endif
     if(hibernatemode_string) hibernatemode_string->release();
     if(hibernatefile_string) hibernatefile_string->release();
     if(hibernatefreeratio_string) hibernatefreeratio_string->release();
     if(hibernatefreetime_string) hibernatefreetime_string->release();
 #endif
+    if (iter) iter->release();
     return return_value;
 }
 
     return return_value;
 }
 
+// MARK: -
+// MARK: Aggressiveness
 
 //******************************************************************************
 
 //******************************************************************************
-// aggressivenessChanged
+// setAggressiveness
 //
 //
-// We are behind the command gate to examine changes to aggressives.
+// Override IOService::setAggressiveness()
 //******************************************************************************
 
 //******************************************************************************
 
-void IOPMrootDomain::aggressivenessChanged( void )
+IOReturn IOPMrootDomain::setAggressiveness(
+    unsigned long   type,
+    unsigned long   value )
 {
 {
-    unsigned long   minutesToSleep = 0;
-    unsigned long   minutesToDisplayDim = 0;
-
-    ASSERT_GATED();
-
-    // Fetch latest display and system sleep slider values.
-       getAggressiveness(kPMMinutesToSleep, &minutesToSleep);
-       getAggressiveness(kPMMinutesToDim,   &minutesToDisplayDim);
-    DLOG("aggressiveness changed system %u, display %u\n",
-        (uint32_t) minutesToSleep, (uint32_t) minutesToDisplayDim);
+    return setAggressiveness( type, value, 0 );
+}
 
 
-    DLOG("idle time -> %ld secs (ena %d)\n",
-        idleSeconds, (minutesToSleep != 0));
+/*
+ * Private setAggressiveness() with an internal options argument.
+ */
+IOReturn IOPMrootDomain::setAggressiveness(
+    unsigned long   type,
+    unsigned long   value,
+    IOOptionBits    options )
+{
+    AggressivesRequest *    entry;
+    AggressivesRequest *    request;
+    bool                    found = false;
 
 
-    if (0x7fffffff == minutesToSleep)
-        minutesToSleep = idleSeconds;
-
-    // How long to wait before sleeping the system once the displays turns
-    // off is indicated by 'extraSleepDelay'.
-
-    if ( minutesToSleep > minutesToDisplayDim ) {
-        extraSleepDelay = minutesToSleep - minutesToDisplayDim;
-    }
-    else {
-        extraSleepDelay = 0;
-    }
-
-    // system sleep timer was disabled, but not anymore.
-    if ( (sleepSlider == 0) && (minutesToSleep != 0) ) {
-        if (!wrangler)
-        {
-            sleepASAP = false;
-            changePowerStateToPriv(ON_STATE);
-            if (idleSeconds)
-            {
-                startIdleSleepTimer( idleSeconds );
-            }
-        }
-        else
-        {
-            // Start idle sleep timer if wrangler went to sleep
-            // while system sleep was disabled.
-
-            sleepASAP = false;
-            if (wranglerAsleep)
-            {
-                AbsoluteTime    now;
-                uint64_t        nanos;
-                uint32_t        minutesSinceDisplaySleep = 0;
-                uint32_t        sleepDelay;
-
-                clock_get_uptime(&now);
-                if (CMP_ABSOLUTETIME(&now, &wranglerSleepTime) > 0)
-                {
-                    SUB_ABSOLUTETIME(&now, &wranglerSleepTime);
-                    absolutetime_to_nanoseconds(now, &nanos);
-                    minutesSinceDisplaySleep = nanos / (60000000000ULL);
-                }
-
-                if (extraSleepDelay > minutesSinceDisplaySleep)
-                {
-                    sleepDelay = extraSleepDelay - minutesSinceDisplaySleep;
-                }
-                else
-                {
-                    // 1 min idle sleep.
-                    sleepDelay = 1;
-                }
-
-                startIdleSleepTimer(sleepDelay * 60);
-                DLOG("display slept %u min, set idle timer to %u min\n",
-                    minutesSinceDisplaySleep, sleepDelay);
-            }
-        }
-    }
-
-    sleepSlider = minutesToSleep;
-    if ( sleepSlider == 0 ) {
-        cancelIdleSleepTimer();
-        // idle sleep is now disabled
-        adjustPowerState();
-        // make sure we're powered
-        patriarch->wakeSystem();
-    }
-}
-
-
-//******************************************************************************
-// setAggressiveness
-//
-// Override IOService::setAggressiveness()
-//******************************************************************************
-
-IOReturn IOPMrootDomain::setAggressiveness(
-    unsigned long   type,
-    unsigned long   value )
-{
-    return setAggressiveness( type, value, 0 );
-}
-
-/*
- * Private setAggressiveness() with an internal options argument.
- */
-IOReturn IOPMrootDomain::setAggressiveness(
-    unsigned long   type,
-    unsigned long   value,
-    IOOptionBits    options )
-{
-    AggressivesRequest *    entry;
-    AggressivesRequest *    request;
-    bool                    found = false;
-
-    DLOG("setAggressiveness 0x%x = %u, options 0x%x\n",
-        (uint32_t) type, (uint32_t) value, (uint32_t) options);
+    DLOG("setAggressiveness(%x) 0x%x = %u\n",
+        (uint32_t) options, (uint32_t) type, (uint32_t) value);
 
     request = IONew(AggressivesRequest, 1);
     if (!request)
 
     request = IONew(AggressivesRequest, 1);
     if (!request)
@@ -1182,7 +1378,6 @@ IOReturn IOPMrootDomain::setAggressiveness(
     return kIOReturnSuccess;
 }
 
     return kIOReturnSuccess;
 }
 
-
 //******************************************************************************
 // getAggressiveness
 //
 //******************************************************************************
 // getAggressiveness
 //
@@ -1255,8 +1450,8 @@ IOReturn IOPMrootDomain::getAggressiveness (
 
     if (source)
     {
 
     if (source)
     {
-        DLOG("getAggressiveness 0x%x = %u, source %d\n",
-            (uint32_t) type, value, source);
+        DLOG("getAggressiveness(%d) 0x%x = %u\n",
+            source, (uint32_t) type, value);
         *outLevel = (unsigned long) value;
         return kIOReturnSuccess;
     }
         *outLevel = (unsigned long) value;
         return kIOReturnSuccess;
     }
@@ -1268,7 +1463,6 @@ IOReturn IOPMrootDomain::getAggressiveness (
     }
 }
 
     }
 }
 
-
 //******************************************************************************
 // joinAggressiveness
 //
 //******************************************************************************
 // joinAggressiveness
 //
@@ -1283,7 +1477,7 @@ IOReturn IOPMrootDomain::joinAggressiveness(
     if (!service || (service == this))
         return kIOReturnBadArgument;
 
     if (!service || (service == this))
         return kIOReturnBadArgument;
 
-    DLOG("joinAggressiveness %s (%p)\n", service->getName(), service);
+    DLOG("joinAggressiveness %s %p\n", service->getName(), OBFUSCATE(service));
 
     request = IONew(AggressivesRequest, 1);
     if (!request)
 
     request = IONew(AggressivesRequest, 1);
     if (!request)
@@ -1304,7 +1498,6 @@ IOReturn IOPMrootDomain::joinAggressiveness(
     return kIOReturnSuccess;
 }
 
     return kIOReturnSuccess;
 }
 
-
 //******************************************************************************
 // handleAggressivesRequests
 //
 //******************************************************************************
 // handleAggressivesRequests
 //
@@ -1370,7 +1563,7 @@ void IOPMrootDomain::handleAggressivesRequests( void )
                                     broadcast = true;
                                     record->flags |= (kAggressivesRecordFlagMinValue |
                                                       kAggressivesRecordFlagModified);
                                     broadcast = true;
                                     record->flags |= (kAggressivesRecordFlagMinValue |
                                                       kAggressivesRecordFlagModified);
-                                    DLOG("quick spindown accelerated, was %u min\n",
+                                    DLOG("disk spindown accelerated, was %u min\n",
                                         record->value);
                                 }
                             }
                                         record->value);
                                 }
                             }
@@ -1472,11 +1665,12 @@ unlock_done:
     // Submit a power event to handle those changes on the PM work loop.
 
     if (pingSelf && pmPowerStateQueue) {
     // Submit a power event to handle those changes on the PM work loop.
 
     if (pingSelf && pmPowerStateQueue) {
-        pmPowerStateQueue->submitPowerEvent( kPowerEventAggressivenessChanged );
+        pmPowerStateQueue->submitPowerEvent(
+            kPowerEventPolicyStimulus,
+            (void *) kStimulusAggressivenessChanged );
     }
 }
 
     }
 }
 
-
 //******************************************************************************
 // synchronizeAggressives
 //
 //******************************************************************************
 // synchronizeAggressives
 //
@@ -1491,6 +1685,7 @@ void IOPMrootDomain::synchronizeAggressives(
     IOService *                 service;
     AggressivesRequest *        request;
     const AggressivesRecord *   record;
     IOService *                 service;
     AggressivesRequest *        request;
     const AggressivesRecord *   record;
+    IOPMDriverCallEntry         callEntry;
     uint32_t                    value;
     int                         i;
 
     uint32_t                    value;
     int                         i;
 
@@ -1507,7 +1702,7 @@ void IOPMrootDomain::synchronizeAggressives(
 
         if (service)
         {
 
         if (service)
         {
-            if (service->assertPMThreadCall())
+            if (service->assertPMDriverCall(&callEntry))
             {
                 for (i = 0, record = array; i < count; i++, record++)
                 {
             {
                 for (i = 0, record = array; i < count; i++, record++)
                 {
@@ -1515,18 +1710,17 @@ void IOPMrootDomain::synchronizeAggressives(
                     if (record->flags & kAggressivesRecordFlagMinValue)
                         value = kAggressivesMinValue;
 
                     if (record->flags & kAggressivesRecordFlagMinValue)
                         value = kAggressivesMinValue;
 
-                    DLOG("synchronizeAggressives 0x%x = %u to %s\n",
+                    _LOG("synchronizeAggressives 0x%x = %u to %s\n",
                         record->type, value, service->getName());
                     service->setAggressiveness(record->type, value);
                 }
                         record->type, value, service->getName());
                     service->setAggressiveness(record->type, value);
                 }
-                service->deassertPMThreadCall();
+                service->deassertPMDriverCall(&callEntry);
             }
             service->release();     // retained by joinAggressiveness()
         }
     }
 }
 
             }
             service->release();     // retained by joinAggressiveness()
         }
     }
 }
 
-
 //******************************************************************************
 // broadcastAggressives
 //
 //******************************************************************************
 // broadcastAggressives
 //
@@ -1537,18 +1731,19 @@ void IOPMrootDomain::broadcastAggressives(
     const AggressivesRecord *   array,
     int                         count )
 {
     const AggressivesRecord *   array,
     int                         count )
 {
-       IORegistryIterator *        iter;
-       IORegistryEntry *           entry;
-       IOPowerConnection *         connect;
+    IORegistryIterator *        iter;
+    IORegistryEntry *           entry;
+    IOPowerConnection *         connect;
     IOService *                 service;
     const AggressivesRecord *   record;
     IOService *                 service;
     const AggressivesRecord *   record;
+    IOPMDriverCallEntry         callEntry;
     uint32_t                    value;
     int                         i;
 
     uint32_t                    value;
     int                         i;
 
-       iter = IORegistryIterator::iterateOver(
-               this, gIOPowerPlane, kIORegistryIterateRecursively);
+    iter = IORegistryIterator::iterateOver(
+            this, gIOPowerPlane, kIORegistryIterateRecursively);
     if (iter)
     if (iter)
-       {
+    {
         do
         {
             iter->reset();
         do
         {
             iter->reset();
@@ -1560,7 +1755,7 @@ void IOPMrootDomain::broadcastAggressives(
 
                 if ((service = (IOService *) connect->copyChildEntry(gIOPowerPlane)))
                 {
 
                 if ((service = (IOService *) connect->copyChildEntry(gIOPowerPlane)))
                 {
-                    if (service->assertPMThreadCall())
+                    if (service->assertPMDriverCall(&callEntry))
                     {
                         for (i = 0, record = array; i < count; i++, record++)
                         {
                     {
                         for (i = 0, record = array; i < count; i++, record++)
                         {
@@ -1569,12 +1764,12 @@ void IOPMrootDomain::broadcastAggressives(
                                 value = record->value;
                                 if (record->flags & kAggressivesRecordFlagMinValue)
                                     value = kAggressivesMinValue;
                                 value = record->value;
                                 if (record->flags & kAggressivesRecordFlagMinValue)
                                     value = kAggressivesMinValue;
-                                DLOG("broadcastAggressives %x = %u to %s\n",
+                                _LOG("broadcastAggressives %x = %u to %s\n",
                                     record->type, value, service->getName());
                                 service->setAggressiveness(record->type, value);
                             }
                         }
                                     record->type, value, service->getName());
                                 service->setAggressiveness(record->type, value);
                             }
                         }
-                        service->deassertPMThreadCall();
+                        service->deassertPMDriverCall(&callEntry);
                     }
                     service->release();
                 }
                     }
                     service->release();
                 }
@@ -1585,6 +1780,8 @@ void IOPMrootDomain::broadcastAggressives(
     }
 }
 
     }
 }
 
+// MARK: -
+// MARK: System Sleep
 
 //******************************************************************************
 // startIdleSleepTimer
 
 //******************************************************************************
 // startIdleSleepTimer
@@ -1598,14 +1795,17 @@ void IOPMrootDomain::startIdleSleepTimer( uint32_t inSeconds )
     ASSERT_GATED();
     if (inSeconds)
     {
     ASSERT_GATED();
     if (inSeconds)
     {
-        clock_interval_to_deadline(inSeconds, kSecondScale, &deadline);        
+        clock_interval_to_deadline(inSeconds, kSecondScale, &deadline);
         thread_call_enter_delayed(extraSleepTimer, deadline);
         idleSleepTimerPending = true;
         thread_call_enter_delayed(extraSleepTimer, deadline);
         idleSleepTimerPending = true;
-        DLOG("idle timer set for %u seconds\n", inSeconds);
     }
     }
+    else
+    {
+        thread_call_enter(extraSleepTimer);
+    }
+    DLOG("idle timer set for %u seconds\n", inSeconds);
 }
 
 }
 
-
 //******************************************************************************
 // cancelIdleSleepTimer
 //
 //******************************************************************************
 // cancelIdleSleepTimer
 //
@@ -1614,7 +1814,7 @@ void IOPMrootDomain::startIdleSleepTimer( uint32_t inSeconds )
 void IOPMrootDomain::cancelIdleSleepTimer( void )
 {
     ASSERT_GATED();
 void IOPMrootDomain::cancelIdleSleepTimer( void )
 {
     ASSERT_GATED();
-    if (idleSleepTimerPending) 
+    if (idleSleepTimerPending)
     {
         DLOG("idle timer cancelled\n");
         thread_call_cancel(extraSleepTimer);
     {
         DLOG("idle timer cancelled\n");
         thread_call_cancel(extraSleepTimer);
@@ -1622,7 +1822,6 @@ void IOPMrootDomain::cancelIdleSleepTimer( void )
     }
 }
 
     }
 }
 
-
 //******************************************************************************
 // idleSleepTimerExpired
 //
 //******************************************************************************
 // idleSleepTimerExpired
 //
@@ -1634,13 +1833,6 @@ static void idleSleepTimerExpired(
     ((IOPMrootDomain *)us)->handleSleepTimerExpiration();
 }
 
     ((IOPMrootDomain *)us)->handleSleepTimerExpiration();
 }
 
-static void wakeupClamshellTimerExpired(
-    thread_call_param_t us, thread_call_param_t )
-{
-    ((IOPMrootDomain *)us)->stopIgnoringClamshellEventsDuringWakeup();
-}
-
-
 //******************************************************************************
 // handleSleepTimerExpiration
 //
 //******************************************************************************
 // handleSleepTimerExpiration
 //
@@ -1674,40 +1866,80 @@ void IOPMrootDomain::handleSleepTimerExpiration( void )
         return;
     }
 
         return;
     }
 
-    // accelerate disk spin down if spin down timer is non-zero
     setQuickSpinDownTimeout();
     setQuickSpinDownTimeout();
-
-    sleepASAP = true;
-    adjustPowerState();
+    adjustPowerState(true);
 }
 
 }
 
-
 //******************************************************************************
 //******************************************************************************
-// stopIgnoringClamshellEventsDuringWakeup
+// getTimeToIdleSleep
 //
 //
+// Returns number of seconds left before going into idle sleep.
+// Caller has to make sure that idle sleep is allowed at the time of calling
+// this function
 //******************************************************************************
 
 //******************************************************************************
 
-void IOPMrootDomain::stopIgnoringClamshellEventsDuringWakeup( void )
+uint32_t IOPMrootDomain::getTimeToIdleSleep( void )
 {
 {
-    if (!getPMworkloop()->inGate())
+
+    AbsoluteTime    now, lastActivityTime;
+    uint64_t        nanos;
+    uint32_t        minutesSinceUserInactive = 0;
+    uint32_t         sleepDelay = 0;
+
+    if (sleepSlider == 0)
+        return 0xffffffff;
+
+    if (userActivityTime)
+        lastActivityTime = userActivityTime;
+    else
+        lastActivityTime = userBecameInactiveTime;
+
+    clock_get_uptime(&now);
+    if (CMP_ABSOLUTETIME(&now, &lastActivityTime) > 0)
     {
     {
-        getPMworkloop()->runAction(
-            OSMemberFunctionCast(IOWorkLoop::Action, this,
-                &IOPMrootDomain::stopIgnoringClamshellEventsDuringWakeup),
-            this);
-        return;
+        SUB_ABSOLUTETIME(&now, &lastActivityTime);
+        absolutetime_to_nanoseconds(now, &nanos);
+        minutesSinceUserInactive = nanos / (60000000000ULL);
+
+        if (minutesSinceUserInactive >= sleepSlider)
+            sleepDelay = 0;
+        else
+            sleepDelay = sleepSlider - minutesSinceUserInactive;
+    }
+    else
+    {
+        sleepDelay = sleepSlider;
     }
 
     }
 
-    ASSERT_GATED();
+    DLOG("user inactive %u min, time to idle sleep %u min\n",
+        minutesSinceUserInactive, sleepDelay);
+
+    return (sleepDelay * 60);
+}
 
 
-    // Allow clamshell-induced sleep now
-    ignoringClamshellOnWake = false;
+//******************************************************************************
+// setQuickSpinDownTimeout
+//
+//******************************************************************************
 
 
-    // Re-send clamshell event, in case it causes a sleep
-    if (clamshellIsClosed)
-        handlePowerNotification( kLocalEvalClamshellCommand );
+void IOPMrootDomain::setQuickSpinDownTimeout( void )
+{
+    ASSERT_GATED();
+    setAggressiveness(
+        kPMMinutesToSpinDown, 0, kAggressivesOptionQuickSpindownEnable );
 }
 
 }
 
+//******************************************************************************
+// restoreUserSpinDownTimeout
+//
+//******************************************************************************
+
+void IOPMrootDomain::restoreUserSpinDownTimeout( void )
+{
+    ASSERT_GATED();
+    setAggressiveness(
+        kPMMinutesToSpinDown, 0, kAggressivesOptionQuickSpindownDisable );
+}
 
 //******************************************************************************
 // sleepSystem
 
 //******************************************************************************
 // sleepSystem
@@ -1723,814 +1955,3817 @@ IOReturn IOPMrootDomain::sleepSystem( void )
 /* private */
 IOReturn IOPMrootDomain::sleepSystemOptions( OSDictionary *options )
 {
 /* private */
 IOReturn IOPMrootDomain::sleepSystemOptions( OSDictionary *options )
 {
-       /* sleepSystem is a public function, and may be called by any kernel driver.
-     * And that's bad - drivers should sleep the system by calling 
+    OSObject *obj = NULL;
+    OSString *reason = NULL;
+    /* sleepSystem is a public function, and may be called by any kernel driver.
+     * And that's bad - drivers should sleep the system by calling
      * receivePowerNotification() instead. Drivers should not use sleepSystem.
      *
      * Note that user space app calls to IOPMSleepSystem() will also travel
      * this code path and thus be correctly identified as software sleeps.
      */
      * receivePowerNotification() instead. Drivers should not use sleepSystem.
      *
      * Note that user space app calls to IOPMSleepSystem() will also travel
      * this code path and thus be correctly identified as software sleeps.
      */
-          
-    if (options && options->getObject("OSSwitch")) 
-    {
 
 
+    if (options && options->getObject("OSSwitch"))
+    {
         // Log specific sleep cause for OS Switch hibernation
         // Log specific sleep cause for OS Switch hibernation
-        return privateSleepSystem( kIOPMOSSwitchHibernationKey) ;
-
-    } else {
-
-        return privateSleepSystem( kIOPMSoftwareSleepKey);
+        return privateSleepSystem( kIOPMSleepReasonOSSwitchHibernate);
+    }
 
 
+    if (options && (obj = options->getObject("Sleep Reason")))
+    {
+        reason = OSDynamicCast(OSString, obj);
+        if (reason && reason->isEqualTo(kIOPMDarkWakeThermalEmergencyKey))
+            return privateSleepSystem(kIOPMSleepReasonDarkWakeThermalEmergency);
     }
     }
+
+    return privateSleepSystem( kIOPMSleepReasonSoftware);
 }
 
 /* private */
 }
 
 /* private */
-IOReturn IOPMrootDomain::privateSleepSystem( const char *sleepReason )
+IOReturn IOPMrootDomain::privateSleepSystem( uint32_t sleepReason )
 {
 {
-    if ( userDisabledAllSleep )
-    {
-        LOG("Sleep prevented by user disable\n");
-
-        /* Prevent sleep of all kinds if directed to by user space */
-        return kIOReturnNotPermitted;
-    }
+    /* Called from both gated and non-gated context */
 
 
-    if ( systemBooting || systemShutdown || !allowSleep )
+    if (!checkSystemSleepEnabled() || !pmPowerStateQueue)
     {
     {
-        LOG("Sleep prevented by SB %d, SS %d, AS %d\n",
-            systemBooting, systemShutdown, allowSleep);
-
-        // Unable to sleep because system is in the process of booting or
-        // shutting down, or sleep has otherwise been disallowed.
-        return kIOReturnError;
-    }
-
-    // Record sleep cause in IORegistry
-    if (sleepReason) {
-        setProperty(kRootDomainSleepReasonKey, sleepReason);
+        return kIOReturnNotPermitted;
     }
 
     }
 
-    tracePoint(kIOPMTracePointSleepStarted);
+    pmPowerStateQueue->submitPowerEvent(
+                            kPowerEventPolicyStimulus,
+                            (void *) kStimulusDemandSystemSleep,
+                            sleepReason);
 
 
-    patriarch->sleepSystem();
     return kIOReturnSuccess;
 }
 
     return kIOReturnSuccess;
 }
 
-
-//******************************************************************************
-// shutdownSystem
-//
-//******************************************************************************
-
-IOReturn IOPMrootDomain::shutdownSystem( void )
-{
-    //patriarch->shutDownSystem();
-    return kIOReturnUnsupported;
-}
-
-
-//******************************************************************************
-// restartSystem
-//
-//******************************************************************************
-
-IOReturn IOPMrootDomain::restartSystem( void )
-{
-    //patriarch->restartSystem();
-    return kIOReturnUnsupported;
-}
-
-
 //******************************************************************************
 // powerChangeDone
 //
 // This overrides powerChangeDone in IOService.
 //******************************************************************************
 // powerChangeDone
 //
 // This overrides powerChangeDone in IOService.
-//
-// Menu sleep and idle sleep move us from the ON state to the SLEEP_STATE.
-// In this case:
-// If we finished going to the SLEEP_STATE, and the platform is capable of
-// true sleep, then sleep the kernel. Otherwise switch up to the DOZE_STATE
-// which will keep almost everything as off as it can get.
 //******************************************************************************
 
 //******************************************************************************
 
-void IOPMrootDomain::powerChangeDone( unsigned long previousState )
+void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState )
 {
     ASSERT_GATED();
     DLOG("PowerChangeDone: %u->%u\n",
 {
     ASSERT_GATED();
     DLOG("PowerChangeDone: %u->%u\n",
-        (uint32_t) previousState, (uint32_t) getPowerState());
+        (uint32_t) previousPowerState, (uint32_t) getPowerState());
 
 
-    switch ( getPowerState() ) {
-        case SLEEP_STATE:
-                       if ( previousState != ON_STATE )
-                               break;
+    switch ( getPowerState() )
+    {
+        case SLEEP_STATE: {
+            if (previousPowerState != ON_STATE)
+                break;
 
 
-            if ( canSleep )
-            {
-                // re-enable this timer for next sleep
-                cancelIdleSleepTimer();
-                wranglerTickled = true;
+            acceptSystemWakeEvents(true);
 
 
-                clock_sec_t            secs;
-                               clock_usec_t    microsecs;
-                clock_get_calendar_microtime(&secs, &microsecs);
-                logtime(secs);
-                gIOLastSleepTime.tv_sec  = secs;
-                gIOLastSleepTime.tv_usec = microsecs;
-                gIOLastWakeTime.tv_sec = 0;
-                gIOLastWakeTime.tv_usec = 0;
+            // re-enable this timer for next sleep
+            cancelIdleSleepTimer();
 
 
-#if    HIBERNATION
-                LOG("System %sSleep\n", gIOHibernateState ? "Safe" : "");
+            clock_sec_t     secs;
+            clock_usec_t    microsecs;
+            clock_get_calendar_microtime(&secs, &microsecs);
+            logtime(secs);
+            gIOLastSleepTime.tv_sec  = secs;
+            gIOLastSleepTime.tv_usec = microsecs;
+            gIOLastWakeTime.tv_sec = 0;
+            gIOLastWakeTime.tv_usec = 0;
 
 
-                tracePoint(kIOPMTracePointSystemHibernatePhase);
+#if HIBERNATION
+            LOG("System %sSleep\n", gIOHibernateState ? "Safe" : "");
+
+            IOHibernateSystemHasSlept();
 
 
-                IOHibernateSystemHasSlept();
+            evaluateSystemSleepPolicyFinal();
 #else
 #else
-                LOG("System Sleep\n");
+            LOG("System Sleep\n");
 #endif
 #endif
+            if (thermalWarningState) {
+                const OSSymbol *event = OSSymbol::withCString(kIOPMThermalLevelWarningKey);
+                if (event) {
+                    systemPowerEventOccurred(event, kIOPMThermalLevelUnknown);
+                    event->release();
+                }
+            }
+            ((IOService *)this)->stop_watchdog_timer(); //14456299
+            getPlatform()->sleepKernel();
 
 
-                tracePoint(kIOPMTracePointSystemSleepPlatformPhase);
-
-                getPlatform()->sleepKernel();
+            // The CPU(s) are off at this point,
+            // Code will resume execution here upon wake.
 
 
-                // The CPU(s) are off at this point. When they're awakened by CPU interrupt,
-                // code will resume execution here.
+            clock_get_uptime(&systemWakeTime);
+            _highestCapability = 0;
 
 
-                // Now we're waking...
-                tracePoint(kIOPMTracePointSystemWakeDriversPhase);
-                
-#if    HIBERNATION
-                IOHibernateSystemWake();
+            ((IOService *)this)->start_watchdog_timer(); //14456299
+#if HIBERNATION
+            IOHibernateSystemWake();
 #endif
 
 #endif
 
-                // sleep transition complete
-                gSleepOrShutdownPending = 0;
-
-                // trip the reset of the calendar clock
-                clock_wakeup_calendar();
-
-                // get us some power
-                patriarch->wakeSystem();
+            // sleep transition complete
+            gSleepOrShutdownPending = 0;
 
 
-                // Set indicator if UUID was set - allow it to be cleared.
-                if (getProperty(kIOPMSleepWakeUUIDKey))
-                    gSleepWakeUUIDIsSet = true;
+            // trip the reset of the calendar clock
+            clock_wakeup_calendar();
 
 
-#if !ROOT_DOMAIN_RUN_STATES
-                tellClients(kIOMessageSystemWillPowerOn, clientMessageFilter);
+#if HIBERNATION
+            LOG("System %sWake\n", gIOHibernateState ? "SafeSleep " : "");
 #endif
 
 #endif
 
-#if    HIBERNATION
-                LOG("System %sWake\n", gIOHibernateState ? "SafeSleep " : "");
-#endif
+            // log system wake
+            PMDebug(kPMLogSystemWake, 0, 0);
+            lowBatteryCondition = false;
+            lastSleepReason = 0;
 
 
-                // log system wake
-                getPlatform()->PMLog(kIOPMrootDomainClass, kPMLogSystemWake, 0, 0);
+            _lastDebugWakeSeconds = _debugWakeSeconds;
+            _debugWakeSeconds = 0;
+            _scheduledAlarms = 0;
 
 #ifndef __LP64__
 
 #ifndef __LP64__
-                // tell the tree we're waking
-                systemWake();
+            systemWake();
 #endif
 
 #endif
 
-
 #if defined(__i386__) || defined(__x86_64__)
 #if defined(__i386__) || defined(__x86_64__)
-#if ROOT_DOMAIN_RUN_STATES
-                OSString * wakeType = OSDynamicCast(
-                    OSString, getProperty(kIOPMRootDomainWakeTypeKey));
-                if (wakeType && wakeType->isEqualTo(kIOPMRootDomainWakeTypeMaintenance))
+            wranglerTickled         = false;
+            graphicsSuppressed      = false;
+            darkWakePostTickle      = false;
+            darkWakeHibernateError  = false;
+            darkWakeToSleepASAP     = true;
+            logGraphicsClamp        = true;
+            sleepTimerMaintenance   = false;
+            sleepToStandby          = false;
+            wranglerTickleLatched   = false;
+            userWasActive           = false;
+            fullWakeReason = kFullWakeReasonNone;
+
+
+            OSString * wakeType = OSDynamicCast(
+                OSString, getProperty(kIOPMRootDomainWakeTypeKey));
+            OSString * wakeReason = OSDynamicCast(
+                OSString, getProperty(kIOPMRootDomainWakeReasonKey));
+
+            if (wakeReason && (wakeReason->getLength() >= 2) &&
+                gWakeReasonString[0] == '\0')
+            {
+                // Until the platform driver can claim its wake reasons
+                strlcat(gWakeReasonString, wakeReason->getCStringNoCopy(),
+                        sizeof(gWakeReasonString));
+            }
+
+            if (wakeType && wakeType->isEqualTo(kIOPMrootDomainWakeTypeLowBattery))
+            {
+                lowBatteryCondition = true;
+                darkWakeMaintenance = true;
+            }
+            else if ((gDarkWakeFlags & kDarkWakeFlagHIDTickleMask) != 0)
+            {
+                OSNumber * hibOptions = OSDynamicCast(
+                    OSNumber, getProperty(kIOHibernateOptionsKey));
+
+                if (hibernateAborted || ((hibOptions &&
+                    !(hibOptions->unsigned32BitValue() & kIOHibernateOptionDarkWake))))
                 {
                 {
-                    updateRunState(kRStateMaintenance);
-                    wranglerTickled = false;
+                    // Hibernate aborted, or EFI brought up graphics
+                    wranglerTickled = true;
+                    DLOG("hibernation aborted %d, options 0x%x\n",
+                        hibernateAborted,
+                        hibOptions ? hibOptions->unsigned32BitValue() : 0);
                 }
                 else
                 }
                 else
-#endif  /* ROOT_DOMAIN_RUN_STATES */
+                if (wakeType && (
+                    wakeType->isEqualTo(kIOPMRootDomainWakeTypeUser) ||
+                    wakeType->isEqualTo(kIOPMRootDomainWakeTypeAlarm)))
                 {
                 {
-                    updateRunState(kRStateNormal);
-                    reportUserInput();
+                    // User wake or RTC alarm
+                    wranglerTickled = true;
                 }
                 }
-#else   /* !__i386__ && !__x86_64__ */
-                // stay awake for at least 30 seconds
-                startIdleSleepTimer(30);
-                reportUserInput();
-#endif
-
-                changePowerStateToPriv(ON_STATE);
-            } else {
-                updateRunState(kRStateNormal);
-
-                // allow us to step up a power state
-                patriarch->sleepToDoze();
+                else
+                if (wakeType &&
+                    wakeType->isEqualTo(kIOPMRootDomainWakeTypeSleepTimer))
+                {
+                    // SMC standby timer trumps SleepX
+                    darkWakeMaintenance = true;
+                    sleepTimerMaintenance = true;
+                }
+                else
+                if ((_lastDebugWakeSeconds != 0) &&
+                    ((gDarkWakeFlags & kDarkWakeFlagAlarmIsDark) == 0))
+                {
+                    // SleepX before maintenance
+                    wranglerTickled = true;
+                }
+                else
+                if (wakeType &&
+                    wakeType->isEqualTo(kIOPMRootDomainWakeTypeMaintenance))
+                {
+                    darkWakeMaintenance = true;
+                }
+                else
+                if (wakeType &&
+                    wakeType->isEqualTo(kIOPMRootDomainWakeTypeSleepService))
+                {
+                    darkWakeMaintenance = true;
+                    darkWakeSleepService = true;
+                    if (kIOHibernateStateWakingFromHibernate == gIOHibernateState) {
+                        sleepToStandby = true;
+                    }
+                }
+                else
+                if (wakeType &&
+                    wakeType->isEqualTo(kIOPMRootDomainWakeTypeHibernateError))
+                {
+                    darkWakeMaintenance = true;
+                    darkWakeHibernateError = true;
+                }
+                else
+                {
+                    // Unidentified wake source, resume to full wake if debug
+                    // alarm is pending.
 
 
-                // ignore children's request for higher power during doze.
-                changePowerStateWithOverrideTo(DOZE_STATE);
+                    if (_lastDebugWakeSeconds &&
+                        (!wakeReason || wakeReason->isEqualTo("")))
+                        wranglerTickled = true;
+                }
             }
             }
-            break;
-
-        case DOZE_STATE:
-            if ( previousState != DOZE_STATE ) 
+            else
             {
             {
-                LOG("System Doze\n");
+                if (wakeType &&
+                    wakeType->isEqualTo(kIOPMRootDomainWakeTypeSleepTimer))
+                {
+                    darkWakeMaintenance = true;
+                    sleepTimerMaintenance = true;
+                }
+                else if (hibernateAborted || !wakeType ||
+                    !wakeType->isEqualTo(kIOPMRootDomainWakeTypeMaintenance) ||
+                    !wakeReason || !wakeReason->isEqualTo("RTC"))
+                {
+                    // Post a HID tickle immediately - except for RTC maintenance wake.
+                    wranglerTickled = true;
+                }
+                else
+                {
+                    darkWakeMaintenance = true;
+                }
             }
             }
-            // re-enable this timer for next sleep
-            cancelIdleSleepTimer();
-            gSleepOrShutdownPending = 0;
-
-            // Invalidate prior activity tickles to allow wake from doze.
-            if (wrangler) wrangler->changePowerStateTo(0);
-            break;
 
 
-#if ROOT_DOMAIN_RUN_STATES
-        case ON_STATE:
-            // SLEEP -> ON (Maintenance)
-            // Go back to sleep, unless cancelled by a HID event.
-
-            if ((previousState == SLEEP_STATE) &&
-                (runStateIndex == kRStateMaintenance) &&
-                !wranglerTickled)
+            if (wranglerTickled)
             {
             {
-                setProperty(kRootDomainSleepReasonKey, kIOPMMaintenanceSleepKey);
-                changePowerStateWithOverrideTo(SLEEP_STATE);
+                darkWakeToSleepASAP = false;
+                fullWakeReason = kFullWakeReasonLocalUser;
+                reportUserInput();
             }
             }
-
-            // ON -> ON triggered by R-state changes.
-
-            if ((previousState == ON_STATE) &&
-                (runStateIndex != nextRunStateIndex) &&
-                (nextRunStateIndex < kRStateCount))
+            else if (!darkWakeMaintenance)
             {
             {
-                LOG("R-state changed %u->%u\n",
-                    runStateIndex, nextRunStateIndex);
-                updateRunState(nextRunStateIndex);
-
-                DLOG("kIOMessageSystemHasPoweredOn (%u)\n",
-                    gMessageClientType);
-                tellClients(kIOMessageSystemHasPoweredOn, clientMessageFilter);
+                // Early/late tickle for non-maintenance wake.
+                if (((gDarkWakeFlags & kDarkWakeFlagHIDTickleMask) ==
+                     kDarkWakeFlagHIDTickleEarly) ||
+                    ((gDarkWakeFlags & kDarkWakeFlagHIDTickleMask) ==
+                     kDarkWakeFlagHIDTickleLate))
+                {
+                    darkWakePostTickle = true;
+                }
             }
             }
-            
-            break;
-#endif  /* ROOT_DOMAIN_RUN_STATES */
+#else   /* !__i386__ && !__x86_64__ */
+            // stay awake for at least 30 seconds
+            wranglerTickled = true;
+            fullWakeReason = kFullWakeReasonLocalUser;
+            startIdleSleepTimer(30);
+#endif
+            sleepCnt++;
+
+            changePowerStateToPriv(ON_STATE);
+        }   break;
+
     }
 }
 
     }
 }
 
-
 //******************************************************************************
 //******************************************************************************
-// wakeFromDoze
+// requestPowerDomainState
 //
 //
-// The Display Wrangler calls here when it switches to its highest state.
-// If the  system is currently dozing, allow it to wake by making sure the
-// parent is providing power.
+// Extend implementation in IOService. Running on PM work loop thread.
 //******************************************************************************
 
 //******************************************************************************
 
-void IOPMrootDomain::wakeFromDoze( void )
+IOReturn IOPMrootDomain::requestPowerDomainState (
+    IOPMPowerFlags      childDesire,
+    IOPowerConnection * childConnection,
+    unsigned long       specification )
 {
 {
-    if ( getPowerState() == DOZE_STATE )
-    {
-        changePowerStateToPriv(ON_STATE);
-        patriarch->wakeSystem();
-    }
-}
-
-
-//******************************************************************************
-// publishFeature
-//
-// Adds a new feature to the supported features dictionary
-//******************************************************************************
+    // Idle and system sleep prevention flags affects driver desire.
+    // Children desire are irrelevant so they are cleared.
 
 
-void IOPMrootDomain::publishFeature( const char * feature )
-{
-    publishFeature(feature, kRD_AllPowerSources, NULL);
+    return super::requestPowerDomainState(0, childConnection, specification);
 }
 
 }
 
-
 //******************************************************************************
 //******************************************************************************
-// publishFeature (with supported power source specified)
+// updatePreventIdleSleepList
 //
 //
-// Adds a new feature to the supported features dictionary
+// Called by IOService on PM work loop.
+// Returns true if PM policy recognized the driver's desire to prevent idle
+// sleep and updated the list of idle sleep preventers. Returns false otherwise
 //******************************************************************************
 
 //******************************************************************************
 
-void IOPMrootDomain::publishFeature(
-    const char *feature, 
-    uint32_t supportedWhere,
-    uint32_t *uniqueFeatureID)
+bool IOPMrootDomain::updatePreventIdleSleepList(
+        IOService * service, bool addNotRemove )
 {
 {
-    static uint16_t     next_feature_id = 500;
+    unsigned int oldCount, newCount;
 
 
-    OSNumber            *new_feature_data = NULL;
-    OSNumber            *existing_feature = NULL;
-    OSArray             *existing_feature_arr = NULL;
-    OSObject            *osObj = NULL;
-    uint32_t            feature_value = 0;
+    ASSERT_GATED();
 
 
-    supportedWhere &= kRD_AllPowerSources; // mask off any craziness!
+#if defined(__i386__) || defined(__x86_64__)
+    // Disregard disk I/O (besides the display wrangler) as a factor preventing
+    // idle sleep, except in the case of legacy disk I/O
+    if ((service != wrangler) && (service != this))
+    {
+        return false;
+    }
+#endif
 
 
-    if(!supportedWhere) {
-        // Feature isn't supported anywhere!
-        return;
+    oldCount = preventIdleSleepList->getCount();
+    if (addNotRemove)
+    {
+        preventIdleSleepList->setObject(service);
+        DLOG("prevent idle sleep list: %s+ (%u)\n",
+            service->getName(), preventIdleSleepList->getCount());
     }
     }
-    
-    if(next_feature_id > 5000) {
-        // Far, far too many features!
-        return;
+    else if (preventIdleSleepList->member(service))
+    {
+        preventIdleSleepList->removeObject(service);
+        DLOG("prevent idle sleep list: %s- (%u)\n",
+            service->getName(), preventIdleSleepList->getCount());
     }
     }
+    newCount = preventIdleSleepList->getCount();
 
 
-    if(featuresDictLock) IOLockLock(featuresDictLock);
+    if ((oldCount == 0) && (newCount != 0))
+    {
+        // Driver added to empty prevent list.
+        // Update the driver desire to prevent idle sleep.
+        // Driver desire does not prevent demand sleep.
 
 
-    OSDictionary *features =
-        (OSDictionary *) getProperty(kRootDomainSupportedFeatures);
-    
-    // Create new features dict if necessary
-    if ( features && OSDynamicCast(OSDictionary, features)) {
-        features = OSDictionary::withDictionary(features);
-    } else {
-        features = OSDictionary::withCapacity(1);
+        changePowerStateTo(ON_STATE);
     }
     }
-    
-    // Create OSNumber to track new feature
-    
-    next_feature_id += 1;
-    if( uniqueFeatureID ) {
-        // We don't really mind if the calling kext didn't give us a place
-        // to stash their unique id. Many kexts don't plan to unload, and thus
-        // have no need to remove themselves later.
-        *uniqueFeatureID = next_feature_id;
+    else if ((oldCount != 0) && (newCount == 0))
+    {
+        // Last driver removed from prevent list.
+        // Drop the driver clamp to allow idle sleep.
+
+        changePowerStateTo(SLEEP_STATE);
+        evaluatePolicy( kStimulusNoIdleSleepPreventers );
     }
 
     }
 
-    feature_value = (uint32_t)next_feature_id;
-    feature_value <<= 16;
-    feature_value += supportedWhere;
+#if defined(__i386__) || defined(__x86_64__)
+    if (addNotRemove && (service == wrangler) && !checkSystemCanSustainFullWake())
+    {
+        return false;   // do not idle-cancel
+    }
+#endif
 
 
-    new_feature_data = OSNumber::withNumber(
-                                (unsigned long long)feature_value, 32);
+    return true;
+}
 
 
-    // Does features object already exist?
-    if( (osObj = features->getObject(feature)) )
+//******************************************************************************
+// preventSystemSleepListUpdate
+//
+// Called by IOService on PM work loop.
+//******************************************************************************
+
+void IOPMrootDomain::updatePreventSystemSleepList(
+        IOService * service, bool addNotRemove )
+{
+    unsigned int oldCount;
+
+    ASSERT_GATED();
+    if (this == service)
+        return;
+
+    oldCount = preventSystemSleepList->getCount();
+    if (addNotRemove)
     {
     {
-        if(( existing_feature = OSDynamicCast(OSNumber, osObj) ))
-        {
-            // We need to create an OSArray to hold the now 2 elements.
-            existing_feature_arr = OSArray::withObjects(
-                            (const OSObject **)&existing_feature, 1, 2);
-        } else if(( existing_feature_arr = OSDynamicCast(OSArray, osObj) ))
-        {
-            // Add object to existing array        
-            existing_feature_arr = OSArray::withArray(
-                            existing_feature_arr,
-                            existing_feature_arr->getCount() + 1);
-        }
+        preventSystemSleepList->setObject(service);
+        DLOG("prevent system sleep list: %s+ (%u)\n",
+            service->getName(), preventSystemSleepList->getCount());
+    }
+    else if (preventSystemSleepList->member(service))
+    {
+        preventSystemSleepList->removeObject(service);
+        DLOG("prevent system sleep list: %s- (%u)\n",
+            service->getName(), preventSystemSleepList->getCount());
 
 
-        if (existing_feature_arr)
+        if ((oldCount != 0) && (preventSystemSleepList->getCount() == 0))
         {
         {
-            existing_feature_arr->setObject(new_feature_data);
-            features->setObject(feature, existing_feature_arr);
-            existing_feature_arr->release();
-            existing_feature_arr = 0;
+            // Lost all system sleep preventers.
+            // Send stimulus if system sleep was blocked, and is in dark wake.
+            evaluatePolicy( kStimulusDarkWakeEvaluate );
         }
         }
-    } else {
-        // The easy case: no previously existing features listed. We simply
-        // set the OSNumber at key 'feature' and we're on our way.
-        features->setObject(feature, new_feature_data);        
     }
     }
-    
-    new_feature_data->release();
+}
 
 
-    setProperty(kRootDomainSupportedFeatures, features);
+//******************************************************************************
+// tellChangeDown
+//
+// Override the superclass implementation to send a different message type.
+//******************************************************************************
 
 
-    features->release();
+bool IOPMrootDomain::tellChangeDown( unsigned long stateNum )
+{
+    DLOG("tellChangeDown %u->%u\n",
+        (uint32_t) getPowerState(), (uint32_t) stateNum);
 
 
-    if(featuresDictLock) IOLockUnlock(featuresDictLock);    
+    if (SLEEP_STATE == stateNum)
+    {
+        // Legacy apps were already told in the full->dark transition
+        if (!ignoreTellChangeDown)
+            tracePoint( kIOPMTracePointSleepApplications );
+        else
+            tracePoint( kIOPMTracePointSleepPriorityClients );
+    }
 
 
-    // Notify EnergySaver and all those in user space so they might
-    // re-populate their feature specific UI    
-    if(pmPowerStateQueue) {
-        pmPowerStateQueue->submitPowerEvent( kPowerEventFeatureChanged );
+    if ((SLEEP_STATE == stateNum) && !ignoreTellChangeDown)
+    {
+        userActivityAtSleep = userActivityCount;
+        hibernateAborted = false;
+        DLOG("tellChangeDown::userActivityAtSleep %d\n", userActivityAtSleep);
+
+        // Direct callout into OSKext so it can disable kext unloads
+        // during sleep/wake to prevent deadlocks.
+        OSKextSystemSleepOrWake( kIOMessageSystemWillSleep );
+
+        IOService::updateConsoleUsers(NULL, kIOMessageSystemWillSleep);
+
+        // Two change downs are sent by IOServicePM. Ignore the 2nd.
+        // But tellClientsWithResponse() must be called for both.
+        ignoreTellChangeDown = true;
     }
     }
-}
 
 
+    return super::tellClientsWithResponse( kIOMessageSystemWillSleep );
+}
 
 //******************************************************************************
 
 //******************************************************************************
-// removePublishedFeature
+// askChangeDown
 //
 //
-// Removes previously published feature
+// Override the superclass implementation to send a different message type.
+// This must be idle sleep since we don't ask during any other power change.
 //******************************************************************************
 
 //******************************************************************************
 
-IOReturn IOPMrootDomain::removePublishedFeature( uint32_t removeFeatureID )
+bool IOPMrootDomain::askChangeDown( unsigned long stateNum )
 {
 {
-    IOReturn                ret = kIOReturnError;
-    uint32_t                feature_value = 0;
-    uint16_t                feature_id = 0;
-    bool                    madeAChange = false;
-    
-    OSSymbol                *dictKey = NULL;
-    OSCollectionIterator    *dictIterator = NULL;
-    OSArray                 *arrayMember  = NULL;
-    OSNumber                *numberMember = NULL;
-    OSObject                *osObj        = NULL;
-    OSNumber                *osNum        = NULL;
-    OSArray                 *arrayMemberCopy;
+    DLOG("askChangeDown %u->%u\n",
+        (uint32_t) getPowerState(), (uint32_t) stateNum);
 
 
-    if(featuresDictLock) IOLockLock(featuresDictLock);
+    // Don't log for dark wake entry
+    if (kSystemTransitionSleep == _systemTransitionType)
+        tracePoint( kIOPMTracePointSleepApplications );
 
 
-    OSDictionary *features =
-        (OSDictionary *) getProperty(kRootDomainSupportedFeatures);
-    
-    if ( features && OSDynamicCast(OSDictionary, features) )
-    {
-        // Any modifications to the dictionary are made to the copy to prevent
-        // races & crashes with userland clients. Dictionary updated
-        // automically later.
-        features = OSDictionary::withDictionary(features);
-    } else {
-        features = NULL;
-        ret = kIOReturnNotFound;
-        goto exit;
-    }
-    
-    // We iterate 'features' dictionary looking for an entry tagged
-    // with 'removeFeatureID'. If found, we remove it from our tracking
-    // structures and notify the OS via a general interest message.
-    
-    dictIterator = OSCollectionIterator::withCollection(features);
-    if(!dictIterator) {
-        goto exit;
-    }
-    
-    while( (dictKey = OSDynamicCast(OSSymbol, dictIterator->getNextObject())) )
+    return super::tellClientsWithResponse( kIOMessageCanSystemSleep );
+}
+
+//******************************************************************************
+// askChangeDownDone
+//
+// An opportunity for root domain to cancel the power transition,
+// possibily due to an assertion created by powerd in response to
+// kIOMessageCanSystemSleep.
+//
+// Idle sleep:
+//   full -> dark wake transition
+//     1. Notify apps and powerd with kIOMessageCanSystemSleep
+//     2. askChangeDownDone()
+//   dark -> sleep transition
+//     1. Notify powerd with kIOMessageCanSystemSleep
+//     2. askChangeDownDone()
+//
+// Demand sleep:
+//   full -> dark wake transition
+//     1. Notify powerd with kIOMessageCanSystemSleep
+//     2. askChangeDownDone()
+//   dark -> sleep transition
+//     1. Notify powerd with kIOMessageCanSystemSleep
+//     2. askChangeDownDone()
+//******************************************************************************
+
+void IOPMrootDomain::askChangeDownDone(
+        IOPMPowerChangeFlags * inOutChangeFlags, bool * cancel )
+{
+    DLOG("askChangeDownDone(0x%x, %u) type %x, cap %x->%x\n",
+        *inOutChangeFlags, *cancel,
+        _systemTransitionType,
+        _currentCapability, _pendingCapability);
+
+    if ((false == *cancel) && (kSystemTransitionSleep == _systemTransitionType))
     {
     {
-        osObj = features->getObject(dictKey);
-        
-        // Each Feature is either tracked by an OSNumber
-        if( osObj && (numberMember = OSDynamicCast(OSNumber, osObj)) )
-        {
-            feature_value = numberMember->unsigned32BitValue();
-            feature_id = (uint16_t)(feature_value >> 16);
+        // Dark->Sleep transition.
+        // Check if there are any deny sleep assertions.
+        // lastSleepReason already set by handleOurPowerChangeStart()
 
 
-            if( feature_id == (uint16_t)removeFeatureID )
-            {
-                // Remove this node
-                features->removeObject(dictKey);
-                madeAChange = true;
-                break;
-            }
-        
-        // Or tracked by an OSArray of OSNumbers
-        } else if( osObj && (arrayMember = OSDynamicCast(OSArray, osObj)) )
+        if (!checkSystemCanSleep(lastSleepReason))
         {
         {
-            unsigned int arrayCount = arrayMember->getCount();
-            
-            for(unsigned int i=0; i<arrayCount; i++)
-            {
-                osNum = OSDynamicCast(OSNumber, arrayMember->getObject(i));
-                if(!osNum) {
-                    continue;
-                }
-                
-                feature_value = osNum->unsigned32BitValue();
-                feature_id = (uint16_t)(feature_value >> 16);
+            // Cancel dark wake to sleep transition.
+            // Must re-scan assertions upon entering dark wake.
 
 
-                if( feature_id == (uint16_t)removeFeatureID )
-                {
-                    // Remove this node
-                    if( 1 == arrayCount ) {
-                        // If the array only contains one element, remove
-                        // the whole thing.
-                        features->removeObject(dictKey);
-                    } else {
-                        // Otherwise remove the element from a copy of the array.
-                        arrayMemberCopy = OSArray::withArray(arrayMember);
-                        if (arrayMemberCopy)
-                        {
-                            arrayMemberCopy->removeObject(i);
-                            features->setObject(dictKey, arrayMemberCopy);
-                            arrayMemberCopy->release();
-                        }
-                    }
+            *cancel = true;
+            DLOG("cancel dark->sleep\n");
+        }
+    }
+}
 
 
-                    madeAChange = true;
-                    break;
-                }
-            }
-        }    
+//******************************************************************************
+// systemDidNotSleep
+//
+// Work common to both canceled or aborted sleep.
+//******************************************************************************
+
+void IOPMrootDomain::systemDidNotSleep( void )
+{
+    if (!wrangler)
+    {
+        if (idleSeconds)
+        {
+            // stay awake for at least idleSeconds
+            startIdleSleepTimer(idleSeconds);
+        }
     }
     }
-    
-    dictIterator->release();
-    
-    if( madeAChange )
+    else
     {
     {
-        ret = kIOReturnSuccess;    
-
-        setProperty(kRootDomainSupportedFeatures, features);
-    
-        // Notify EnergySaver and all those in user space so they might
-        // re-populate their feature specific UI    
-        if(pmPowerStateQueue) {
-            pmPowerStateQueue->submitPowerEvent( kPowerEventFeatureChanged );
+        if (sleepSlider && !userIsActive)
+        {
+            // Manually start the idle sleep timer besides waiting for
+            // the user to become inactive.
+            startIdleSleepTimer( kIdleSleepRetryInterval );
         }
         }
-    } else {
-        ret = kIOReturnNotFound;
     }
     }
-    
-exit:
-    if(features)    features->release();
-    if(featuresDictLock) IOLockUnlock(featuresDictLock);    
-    return ret;
-}
 
 
+    preventTransitionToUserActive(false);
+    IOService::setAdvisoryTickleEnable( true );
+}
 
 //******************************************************************************
 
 //******************************************************************************
-// announcePowerSourceChange
+// tellNoChangeDown
+//
+// Notify registered applications and kernel clients that we are not dropping
+// power.
 //
 //
-// Notifies "interested parties" that the battery state has changed
+// We override the superclass implementation so we can send a different message
+// type to the client or application being notified.
+//
+// This must be a vetoed idle sleep, since no other power change can be vetoed.
 //******************************************************************************
 
 //******************************************************************************
 
-void IOPMrootDomain::announcePowerSourceChange( void )
+void IOPMrootDomain::tellNoChangeDown( unsigned long stateNum )
+{
+    DLOG("tellNoChangeDown %u->%u\n",
+        (uint32_t) getPowerState(), (uint32_t) stateNum);
+
+    // Sleep canceled, clear the sleep trace point.
+    tracePoint(kIOPMTracePointSystemUp);
+
+    systemDidNotSleep();
+    return tellClients( kIOMessageSystemWillNotSleep );
+}
+
+//******************************************************************************
+// tellChangeUp
+//
+// Notify registered applications and kernel clients that we are raising power.
+//
+// We override the superclass implementation so we can send a different message
+// type to the client or application being notified.
+//******************************************************************************
+
+void IOPMrootDomain::tellChangeUp( unsigned long stateNum )
+{
+
+    DLOG("tellChangeUp %u->%u\n",
+        (uint32_t) getPowerState(), (uint32_t) stateNum);
+
+    ignoreTellChangeDown = false;
+
+    if ( stateNum == ON_STATE )
+    {
+        // Direct callout into OSKext so it can disable kext unloads
+        // during sleep/wake to prevent deadlocks.
+        OSKextSystemSleepOrWake( kIOMessageSystemHasPoweredOn );
+
+        // Notify platform that sleep was cancelled or resumed.
+        getPlatform()->callPlatformFunction(
+                        sleepMessagePEFunction, false,
+                        (void *)(uintptr_t) kIOMessageSystemHasPoweredOn,
+                        NULL, NULL, NULL);
+
+        if (getPowerState() == ON_STATE)
+        {
+            // this is a quick wake from aborted sleep
+            systemDidNotSleep();
+            tellClients( kIOMessageSystemWillPowerOn );
+        }
+
+
+        tracePoint( kIOPMTracePointWakeApplications );
+        tellClients( kIOMessageSystemHasPoweredOn );
+    }
+}
+
+//******************************************************************************
+// sysPowerDownHandler
+//
+// Perform a vfs sync before system sleep.
+//******************************************************************************
+
+IOReturn IOPMrootDomain::sysPowerDownHandler(
+    void * target, void * refCon,
+    UInt32 messageType, IOService * service,
+    void * messageArgs, vm_size_t argSize )
+{
+    IOReturn    ret = 0;
+
+    DLOG("sysPowerDownHandler message %s\n", getIOMessageString(messageType));
+
+    if (!gRootDomain)
+        return kIOReturnUnsupported;
+
+    if (messageType == kIOMessageSystemWillSleep)
+    {
+#if HIBERNATION
+        uint32_t mem_only = 0;
+        IOPowerStateChangeNotification *notify =
+                    (IOPowerStateChangeNotification *)messageArgs;
+
+       PE_parse_boot_argn("swd_mem_only", &mem_only, sizeof(mem_only));
+       if ((mem_only != 1) && (gRootDomain->sleepWakeDebugIsWdogEnabled()))
+       {
+           notify->returnValue = 30 * 1000 * 1000;
+           thread_call_enter1(
+                              gRootDomain->hibDebugSetupEntry,
+                              (thread_call_param_t)(uintptr_t) notify->powerRef);
+       }
+#endif
+    }
+    else if (messageType == kIOMessageSystemCapabilityChange)
+    {
+        IOPMSystemCapabilityChangeParameters * params =
+            (IOPMSystemCapabilityChangeParameters *) messageArgs;
+
+        // Interested applications have been notified of an impending power
+        // change and have acked (when applicable).
+        // This is our chance to save whatever state we can before powering
+        // down.
+        // We call sync_internal defined in xnu/bsd/vfs/vfs_syscalls.c,
+        // via callout
+
+        DLOG("sysPowerDownHandler cap %x -> %x (flags %x)\n",
+            params->fromCapabilities, params->toCapabilities,
+            params->changeFlags);
+
+        if ((params->changeFlags & kIOPMSystemCapabilityWillChange) &&
+            (params->fromCapabilities & kIOPMSystemCapabilityCPU) &&
+            (params->toCapabilities & kIOPMSystemCapabilityCPU) == 0)
+        {
+            // We will ack within 20 seconds
+            params->maxWaitForReply = 20 * 1000 * 1000;
+#if HIBERNATION
+            gRootDomain->evaluateSystemSleepPolicyEarly();
+
+            // add in time we could spend freeing pages
+            if (gRootDomain->hibernateMode && !gRootDomain->hibernateDisabled)
+            {
+                params->maxWaitForReply = kCapabilityClientMaxWait;
+            }
+            DLOG("sysPowerDownHandler max wait %d s\n",
+                (int) (params->maxWaitForReply / 1000 / 1000));
+#endif
+
+            // Notify platform that sleep has begun, after the early
+            // sleep policy evaluation.
+            getPlatform()->callPlatformFunction(
+                            sleepMessagePEFunction, false,
+                            (void *)(uintptr_t) kIOMessageSystemWillSleep,
+                            NULL, NULL, NULL);
+
+            if ( !OSCompareAndSwap( 0, 1, &gSleepOrShutdownPending ) )
+            {
+                // Purposely delay the ack and hope that shutdown occurs quickly.
+                // Another option is not to schedule the thread and wait for
+                // ack timeout...
+                AbsoluteTime deadline;
+                clock_interval_to_deadline( 30, kSecondScale, &deadline );
+                thread_call_enter1_delayed(
+                    gRootDomain->diskSyncCalloutEntry,
+                    (thread_call_param_t)(uintptr_t) params->notifyRef,
+                    deadline );
+            }
+            else
+                thread_call_enter1(
+                    gRootDomain->diskSyncCalloutEntry,
+                    (thread_call_param_t)(uintptr_t) params->notifyRef);
+        }
+        else
+        if ((params->changeFlags & kIOPMSystemCapabilityDidChange) &&
+            (params->toCapabilities & kIOPMSystemCapabilityCPU) &&
+            (params->fromCapabilities & kIOPMSystemCapabilityCPU) == 0)
+        {
+#if HIBERNATION
+            // We will ack within 110 seconds
+            params->maxWaitForReply = 110 * 1000 * 1000;
+
+            thread_call_enter1(
+                gRootDomain->diskSyncCalloutEntry,
+                (thread_call_param_t)(uintptr_t) params->notifyRef);
+#endif
+        }
+        ret = kIOReturnSuccess;
+    }
+
+    return ret;
+}
+
+//******************************************************************************
+// handleQueueSleepWakeUUID
+//
+// Called from IOPMrootDomain when we're initiating a sleep,
+// or indirectly from PM configd when PM decides to clear the UUID.
+// PM clears the UUID several minutes after successful wake from sleep,
+// so that we might associate App spindumps with the immediately previous
+// sleep/wake.
+//
+// @param   obj has a retain on it. We're responsible for releasing that retain.
+//******************************************************************************
+
+void IOPMrootDomain::handleQueueSleepWakeUUID(OSObject *obj)
+{
+    OSString    *str = NULL;
+
+    if (kOSBooleanFalse == obj)
+    {
+        handlePublishSleepWakeUUID(NULL);
+    }
+    else if ((str = OSDynamicCast(OSString, obj)))
+    {
+        // This branch caches the UUID for an upcoming sleep/wake
+        if (queuedSleepWakeUUIDString) {
+            queuedSleepWakeUUIDString->release();
+            queuedSleepWakeUUIDString = NULL;
+        }
+        queuedSleepWakeUUIDString = str;
+        queuedSleepWakeUUIDString->retain();
+
+        DLOG("SleepWake UUID queued: %s\n", queuedSleepWakeUUIDString->getCStringNoCopy());
+    }
+
+    if (obj) {
+        obj->release();
+    }
+    return;
+
+}
+//******************************************************************************
+// handlePublishSleepWakeUUID
+//
+// Called from IOPMrootDomain when we're initiating a sleep,
+// or indirectly from PM configd when PM decides to clear the UUID.
+// PM clears the UUID several minutes after successful wake from sleep,
+// so that we might associate App spindumps with the immediately previous
+// sleep/wake.
+//******************************************************************************
+
+void IOPMrootDomain::handlePublishSleepWakeUUID( bool shouldPublish )
+{
+   ASSERT_GATED();
+
+   /*
+    * Clear the current UUID
+    */
+   if (gSleepWakeUUIDIsSet)
+   {
+        DLOG("SleepWake UUID cleared\n");
+
+        gSleepWakeUUIDIsSet = false;
+
+        removeProperty(kIOPMSleepWakeUUIDKey);
+        messageClients(kIOPMMessageSleepWakeUUIDChange, kIOPMMessageSleepWakeUUIDCleared);
+    }
+
+    /*
+     * Optionally, publish a new UUID
+     */
+    if (queuedSleepWakeUUIDString && shouldPublish) {
+
+        OSString  *publishThisUUID = NULL;
+
+        publishThisUUID = queuedSleepWakeUUIDString;
+        publishThisUUID->retain();
+
+        if (publishThisUUID)
+        {
+            setProperty(kIOPMSleepWakeUUIDKey, publishThisUUID);
+            publishThisUUID->release();
+        }
+
+        gSleepWakeUUIDIsSet = true;
+        messageClients(kIOPMMessageSleepWakeUUIDChange, kIOPMMessageSleepWakeUUIDSet);
+
+        queuedSleepWakeUUIDString->release();
+        queuedSleepWakeUUIDString = NULL;
+    }
+}
+
+//******************************************************************************
+// initializeBootSessionUUID
+//
+// Initialize the boot session uuid at boot up and sets it into registry.
+//******************************************************************************
+
+void IOPMrootDomain::initializeBootSessionUUID(void)
+{
+    uuid_t          new_uuid;
+    uuid_string_t   new_uuid_string;
+
+    uuid_generate(new_uuid);
+    uuid_unparse_upper(new_uuid, new_uuid_string);
+    memcpy(bootsessionuuid_string, new_uuid_string, sizeof(uuid_string_t));
+
+    setProperty(kIOPMBootSessionUUIDKey, new_uuid_string);
+}
+
+//******************************************************************************
+// changePowerStateTo & changePowerStateToPriv
+//
+// Override of these methods for logging purposes.
+//******************************************************************************
+
+IOReturn IOPMrootDomain::changePowerStateTo( unsigned long ordinal )
+{
+    DLOG("changePowerStateTo(%lu)\n", ordinal);
+
+    if ((ordinal != ON_STATE) && (ordinal != SLEEP_STATE))
+        return kIOReturnUnsupported;
+
+    return super::changePowerStateTo(ordinal);
+}
+
+IOReturn IOPMrootDomain::changePowerStateToPriv( unsigned long ordinal )
+{
+    DLOG("changePowerStateToPriv(%lu)\n", ordinal);
+
+    if ((ordinal != ON_STATE) && (ordinal != SLEEP_STATE))
+        return kIOReturnUnsupported;
+
+    return super::changePowerStateToPriv(ordinal);
+}
+
+//******************************************************************************
+// activity detect
+//
+//******************************************************************************
+
+bool IOPMrootDomain::activitySinceSleep(void)
+{
+    return (userActivityCount != userActivityAtSleep);
+}
+
+bool IOPMrootDomain::abortHibernation(void)
+{
+    bool ret = activitySinceSleep();
+
+    if (ret && !hibernateAborted && checkSystemCanSustainFullWake())
+    {
+        DLOG("activitySinceSleep ABORT [%d, %d]\n", userActivityCount, userActivityAtSleep);
+        hibernateAborted = true;
+    }
+    return (ret);
+}
+
+extern "C" int
+hibernate_should_abort(void)
+{
+    if (gRootDomain)
+        return (gRootDomain->abortHibernation());
+    else
+        return (0);
+}
+
+//******************************************************************************
+// willNotifyPowerChildren
+//
+// Called after all interested drivers have all acknowledged the power change,
+// but before any power children is informed. Dispatched though a thread call,
+// so it is safe to perform work that might block on a sleeping disk. PM state
+// machine (not thread) will block w/o timeout until this function returns.
+//******************************************************************************
+
+void IOPMrootDomain::willNotifyPowerChildren( IOPMPowerStateIndex newPowerState )
+{
+#if HIBERNATION
+    if (SLEEP_STATE == newPowerState)
+    {
+        IOHibernateSystemSleep();
+        IOHibernateIOKitSleep();
+    }
+#endif
+}
+
+//******************************************************************************
+// sleepOnClamshellClosed
+//
+// contains the logic to determine if the system should sleep when the clamshell
+// is closed.
+//******************************************************************************
+
+bool IOPMrootDomain::shouldSleepOnClamshellClosed( void )
+{
+    if (!clamshellExists)
+        return false;
+
+    DLOG("clamshell closed %d, disabled %d, desktopMode %d, ac %d sleepDisabled %d\n",
+        clamshellClosed, clamshellDisabled, desktopMode, acAdaptorConnected, clamshellSleepDisabled);
+
+    return ( !clamshellDisabled && !(desktopMode && acAdaptorConnected) && !clamshellSleepDisabled );
+}
+
+void IOPMrootDomain::sendClientClamshellNotification( void )
+{
+    /* Only broadcast clamshell alert if clamshell exists. */
+    if (!clamshellExists)
+        return;
+
+    setProperty(kAppleClamshellStateKey,
+        clamshellClosed ? kOSBooleanTrue : kOSBooleanFalse);
+
+    setProperty(kAppleClamshellCausesSleepKey,
+        shouldSleepOnClamshellClosed() ? kOSBooleanTrue : kOSBooleanFalse);
+
+    /* Argument to message is a bitfiel of
+     *      ( kClamshellStateBit | kClamshellSleepBit )
+     */
+    messageClients(kIOPMMessageClamshellStateChange,
+        (void *)(uintptr_t) ( (clamshellClosed ? kClamshellStateBit : 0)
+             | ( shouldSleepOnClamshellClosed() ? kClamshellSleepBit : 0)) );
+}
+
+//******************************************************************************
+// getSleepSupported
+//
+// Deprecated
+//******************************************************************************
+
+IOOptionBits IOPMrootDomain::getSleepSupported( void )
+{
+    return( platformSleepSupport );
+}
+
+//******************************************************************************
+// setSleepSupported
+//
+// Deprecated
+//******************************************************************************
+
+void IOPMrootDomain::setSleepSupported( IOOptionBits flags )
+{
+    DLOG("setSleepSupported(%x)\n", (uint32_t) flags);
+    OSBitOrAtomic(flags, &platformSleepSupport);
+}
+
+//******************************************************************************
+// setDisableClamShellSleep
+//
+//******************************************************************************
+
+void IOPMrootDomain::setDisableClamShellSleep( bool val )
+{
+    if (gIOPMWorkLoop->inGate() == false) {
+
+       gIOPMWorkLoop->runAction(
+               OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::setDisableClamShellSleep),
+               (OSObject *)this,
+               (void *)val);
+
+       return;
+    }
+    else {
+       DLOG("setDisableClamShellSleep(%x)\n", (uint32_t) val);
+       if ( clamshellSleepDisabled != val )
+       {
+           clamshellSleepDisabled = val;
+           // If clamshellSleepDisabled is reset to 0, reevaluate if
+           // system need to go to sleep due to clamshell state
+           if ( !clamshellSleepDisabled && clamshellClosed)
+              handlePowerNotification(kLocalEvalClamshellCommand);
+       }
+    }
+}
+
+//******************************************************************************
+// wakeFromDoze
+//
+// Deprecated.
+//******************************************************************************
+
+void IOPMrootDomain::wakeFromDoze( void )
+{
+    // Preserve symbol for familes (IOUSBFamily and IOGraphics)
+}
+
+// MARK: -
+// MARK: Features
+
+//******************************************************************************
+// publishFeature
+//
+// Adds a new feature to the supported features dictionary
+//******************************************************************************
+
+void IOPMrootDomain::publishFeature( const char * feature )
+{
+    publishFeature(feature, kRD_AllPowerSources, NULL);
+}
+
+//******************************************************************************
+// publishFeature (with supported power source specified)
+//
+// Adds a new feature to the supported features dictionary
+//******************************************************************************
+
+void IOPMrootDomain::publishFeature(
+    const char *feature,
+    uint32_t supportedWhere,
+    uint32_t *uniqueFeatureID)
+{
+    static uint16_t     next_feature_id = 500;
+
+    OSNumber            *new_feature_data = NULL;
+    OSNumber            *existing_feature = NULL;
+    OSArray             *existing_feature_arr = NULL;
+    OSObject            *osObj = NULL;
+    uint32_t            feature_value = 0;
+
+    supportedWhere &= kRD_AllPowerSources; // mask off any craziness!
+
+    if(!supportedWhere) {
+        // Feature isn't supported anywhere!
+        return;
+    }
+
+    if(next_feature_id > 5000) {
+        // Far, far too many features!
+        return;
+    }
+
+    if(featuresDictLock) IOLockLock(featuresDictLock);
+
+    OSDictionary *features =
+        (OSDictionary *) getProperty(kRootDomainSupportedFeatures);
+
+    // Create new features dict if necessary
+    if ( features && OSDynamicCast(OSDictionary, features)) {
+        features = OSDictionary::withDictionary(features);
+    } else {
+        features = OSDictionary::withCapacity(1);
+    }
+
+    // Create OSNumber to track new feature
+
+    next_feature_id += 1;
+    if( uniqueFeatureID ) {
+        // We don't really mind if the calling kext didn't give us a place
+        // to stash their unique id. Many kexts don't plan to unload, and thus
+        // have no need to remove themselves later.
+        *uniqueFeatureID = next_feature_id;
+    }
+
+    feature_value = (uint32_t)next_feature_id;
+    feature_value <<= 16;
+    feature_value += supportedWhere;
+
+    new_feature_data = OSNumber::withNumber(
+                                (unsigned long long)feature_value, 32);
+
+    // Does features object already exist?
+    if( (osObj = features->getObject(feature)) )
+    {
+        if(( existing_feature = OSDynamicCast(OSNumber, osObj) ))
+        {
+            // We need to create an OSArray to hold the now 2 elements.
+            existing_feature_arr = OSArray::withObjects(
+                            (const OSObject **)&existing_feature, 1, 2);
+        } else if(( existing_feature_arr = OSDynamicCast(OSArray, osObj) ))
+        {
+            // Add object to existing array
+            existing_feature_arr = OSArray::withArray(
+                            existing_feature_arr,
+                            existing_feature_arr->getCount() + 1);
+        }
+
+        if (existing_feature_arr)
+        {
+            existing_feature_arr->setObject(new_feature_data);
+            features->setObject(feature, existing_feature_arr);
+            existing_feature_arr->release();
+            existing_feature_arr = 0;
+        }
+    } else {
+        // The easy case: no previously existing features listed. We simply
+        // set the OSNumber at key 'feature' and we're on our way.
+        features->setObject(feature, new_feature_data);
+    }
+
+    new_feature_data->release();
+
+    setProperty(kRootDomainSupportedFeatures, features);
+
+    features->release();
+
+    if(featuresDictLock) IOLockUnlock(featuresDictLock);
+
+    // Notify EnergySaver and all those in user space so they might
+    // re-populate their feature specific UI
+    if(pmPowerStateQueue) {
+        pmPowerStateQueue->submitPowerEvent( kPowerEventFeatureChanged );
+    }
+}
+
+//******************************************************************************
+// removePublishedFeature
+//
+// Removes previously published feature
+//******************************************************************************
+
+IOReturn IOPMrootDomain::removePublishedFeature( uint32_t removeFeatureID )
+{
+    IOReturn                ret = kIOReturnError;
+    uint32_t                feature_value = 0;
+    uint16_t                feature_id = 0;
+    bool                    madeAChange = false;
+
+    OSSymbol                *dictKey = NULL;
+    OSCollectionIterator    *dictIterator = NULL;
+    OSArray                 *arrayMember  = NULL;
+    OSNumber                *numberMember = NULL;
+    OSObject                *osObj        = NULL;
+    OSNumber                *osNum        = NULL;
+    OSArray                 *arrayMemberCopy;
+
+    if (kBadPMFeatureID == removeFeatureID)
+        return kIOReturnNotFound;
+
+    if(featuresDictLock) IOLockLock(featuresDictLock);
+
+    OSDictionary *features =
+        (OSDictionary *) getProperty(kRootDomainSupportedFeatures);
+
+    if ( features && OSDynamicCast(OSDictionary, features) )
+    {
+        // Any modifications to the dictionary are made to the copy to prevent
+        // races & crashes with userland clients. Dictionary updated
+        // automically later.
+        features = OSDictionary::withDictionary(features);
+    } else {
+        features = NULL;
+        ret = kIOReturnNotFound;
+        goto exit;
+    }
+
+    // We iterate 'features' dictionary looking for an entry tagged
+    // with 'removeFeatureID'. If found, we remove it from our tracking
+    // structures and notify the OS via a general interest message.
+
+    dictIterator = OSCollectionIterator::withCollection(features);
+    if(!dictIterator) {
+        goto exit;
+    }
+
+    while( (dictKey = OSDynamicCast(OSSymbol, dictIterator->getNextObject())) )
+    {
+        osObj = features->getObject(dictKey);
+
+        // Each Feature is either tracked by an OSNumber
+        if( osObj && (numberMember = OSDynamicCast(OSNumber, osObj)) )
+        {
+            feature_value = numberMember->unsigned32BitValue();
+            feature_id = (uint16_t)(feature_value >> 16);
+
+            if( feature_id == (uint16_t)removeFeatureID )
+            {
+                // Remove this node
+                features->removeObject(dictKey);
+                madeAChange = true;
+                break;
+            }
+
+        // Or tracked by an OSArray of OSNumbers
+        } else if( osObj && (arrayMember = OSDynamicCast(OSArray, osObj)) )
+        {
+            unsigned int arrayCount = arrayMember->getCount();
+
+            for(unsigned int i=0; i<arrayCount; i++)
+            {
+                osNum = OSDynamicCast(OSNumber, arrayMember->getObject(i));
+                if(!osNum) {
+                    continue;
+                }
+
+                feature_value = osNum->unsigned32BitValue();
+                feature_id = (uint16_t)(feature_value >> 16);
+
+                if( feature_id == (uint16_t)removeFeatureID )
+                {
+                    // Remove this node
+                    if( 1 == arrayCount ) {
+                        // If the array only contains one element, remove
+                        // the whole thing.
+                        features->removeObject(dictKey);
+                    } else {
+                        // Otherwise remove the element from a copy of the array.
+                        arrayMemberCopy = OSArray::withArray(arrayMember);
+                        if (arrayMemberCopy)
+                        {
+                            arrayMemberCopy->removeObject(i);
+                            features->setObject(dictKey, arrayMemberCopy);
+                            arrayMemberCopy->release();
+                        }
+                    }
+
+                    madeAChange = true;
+                    break;
+                }
+            }
+        }
+    }
+
+    dictIterator->release();
+
+    if( madeAChange )
+    {
+        ret = kIOReturnSuccess;
+
+        setProperty(kRootDomainSupportedFeatures, features);
+
+        // Notify EnergySaver and all those in user space so they might
+        // re-populate their feature specific UI
+        if(pmPowerStateQueue) {
+            pmPowerStateQueue->submitPowerEvent( kPowerEventFeatureChanged );
+        }
+    } else {
+        ret = kIOReturnNotFound;
+    }
+
+exit:
+    if(features)    features->release();
+    if(featuresDictLock) IOLockUnlock(featuresDictLock);
+    return ret;
+}
+
+//******************************************************************************
+// publishPMSetting (private)
+//
+// Should only be called by PMSettingObject to publish a PM Setting as a
+// supported feature.
+//******************************************************************************
+
+void IOPMrootDomain::publishPMSetting(
+    const OSSymbol * feature, uint32_t where, uint32_t * featureID )
+{
+    if (noPublishPMSettings &&
+        (noPublishPMSettings->getNextIndexOfObject(feature, 0) != (unsigned int)-1))
+    {
+        // Setting found in noPublishPMSettings array
+        *featureID = kBadPMFeatureID;
+        return;
+    }
+
+    publishFeature(
+        feature->getCStringNoCopy(), where, featureID);
+}
+
+//******************************************************************************
+// setPMSetting (private)
+//
+// Internal helper to relay PM settings changes from user space to individual
+// drivers. Should be called only by IOPMrootDomain::setProperties.
+//******************************************************************************
+
+IOReturn IOPMrootDomain::setPMSetting(
+    const OSSymbol  *type,
+    OSObject        *object )
+{
+    PMSettingCallEntry  *entries = 0;
+    OSArray             *chosen  = 0;
+    const OSArray       *array;
+    PMSettingObject     *pmso;
+    thread_t            thisThread;
+    int                 i, j, count, capacity;
+
+    if (NULL == type)
+        return kIOReturnBadArgument;
+
+    PMSETTING_LOCK();
+
+    // Update settings dict so changes are visible from copyPMSetting().
+    fPMSettingsDict->setObject(type, object);
+
+    // Prep all PMSetting objects with the given 'type' for callout.
+    array = (const OSArray *) settingsCallbacks->getObject(type);
+    if (!array || ((capacity = array->getCount()) == 0))
+        goto unlock_exit;
+
+    // Array to retain PMSetting objects targeted for callout.
+    chosen = OSArray::withCapacity(capacity);
+    if (!chosen)
+        goto unlock_exit;   // error
+
+    entries = IONew(PMSettingCallEntry, capacity);
+    if (!entries)
+        goto unlock_exit;   // error
+    memset(entries, 0, sizeof(PMSettingCallEntry) * capacity);
+
+    thisThread = current_thread();
+
+    for (i = 0, j = 0; i<capacity; i++)
+    {
+        pmso = (PMSettingObject *) array->getObject(i);
+        if (pmso->disabled)
+            continue;
+        entries[j].thread = thisThread;
+        queue_enter(&pmso->calloutQueue, &entries[j], PMSettingCallEntry *, link);
+        chosen->setObject(pmso);
+        j++;
+    }
+    count = j;
+    if (!count)
+        goto unlock_exit;
+
+    PMSETTING_UNLOCK();
+
+    // Call each pmso in the chosen array.
+    for (i=0; i<count; i++)
+    {
+        pmso = (PMSettingObject *) chosen->getObject(i);
+        pmso->dispatchPMSetting(type, object);
+    }
+
+    PMSETTING_LOCK();
+    for (i=0; i<count; i++)
+    {
+        pmso = (PMSettingObject *) chosen->getObject(i);
+        queue_remove(&pmso->calloutQueue, &entries[i], PMSettingCallEntry *, link);
+        if (pmso->waitThread)
+        {
+            PMSETTING_WAKEUP(pmso);
+        }
+    }
+unlock_exit:
+    PMSETTING_UNLOCK();
+
+    if (chosen)  chosen->release();
+    if (entries) IODelete(entries, PMSettingCallEntry, capacity);
+
+    return kIOReturnSuccess;
+}
+
+//******************************************************************************
+// copyPMSetting (public)
+//
+// Allows kexts to safely read setting values, without being subscribed to
+// notifications.
+//******************************************************************************
+
+OSObject * IOPMrootDomain::copyPMSetting(
+    OSSymbol *whichSetting)
+{
+    OSObject *obj = NULL;
+
+    if(!whichSetting) return NULL;
+
+    PMSETTING_LOCK();
+    obj = fPMSettingsDict->getObject(whichSetting);
+    if(obj) {
+        obj->retain();
+    }
+    PMSETTING_UNLOCK();
+
+    return obj;
+}
+
+//******************************************************************************
+// registerPMSettingController (public)
+//
+// direct wrapper to registerPMSettingController with uint32_t power source arg
+//******************************************************************************
+
+IOReturn IOPMrootDomain::registerPMSettingController(
+    const OSSymbol *                settings[],
+    IOPMSettingControllerCallback   func,
+    OSObject                        *target,
+    uintptr_t                       refcon,
+    OSObject                        **handle)
+{
+    return registerPMSettingController(
+            settings,
+            (kIOPMSupportedOnAC | kIOPMSupportedOnBatt | kIOPMSupportedOnUPS),
+            func, target, refcon, handle);
+}
+
+//******************************************************************************
+// registerPMSettingController (public)
+//
+// Kexts may register for notifications when a particular setting is changed.
+// A list of settings is available in IOPM.h.
+// Arguments:
+//  * settings - An OSArray containing OSSymbols. Caller should populate this
+//          array with a list of settings caller wants notifications from.
+//  * func - A C function callback of the type IOPMSettingControllerCallback
+//  * target - caller may provide an OSObject *, which PM will pass as an
+//          target to calls to "func"
+//  * refcon - caller may provide an void *, which PM will pass as an
+//          argument to calls to "func"
+//  * handle - This is a return argument. We will populate this pointer upon
+//          call success. Hold onto this and pass this argument to
+//          IOPMrootDomain::deRegisterPMSettingCallback when unloading your kext
+// Returns:
+//      kIOReturnSuccess on success
+//******************************************************************************
+
+IOReturn IOPMrootDomain::registerPMSettingController(
+    const OSSymbol *                settings[],
+    uint32_t                        supportedPowerSources,
+    IOPMSettingControllerCallback   func,
+    OSObject                        *target,
+    uintptr_t                       refcon,
+    OSObject                        **handle)
+{
+    PMSettingObject *pmso = NULL;
+    OSObject        *pmsh = NULL;
+    OSArray         *list = NULL;
+    int             i;
+
+    if (NULL == settings ||
+        NULL == func     ||
+        NULL == handle)
+    {
+        return kIOReturnBadArgument;
+    }
+
+    pmso = PMSettingObject::pmSettingObject(
+                (IOPMrootDomain *) this, func, target,
+                refcon, supportedPowerSources, settings, &pmsh);
+
+    if (!pmso) {
+        *handle = NULL;
+        return kIOReturnInternalError;
+    }
+
+    PMSETTING_LOCK();
+    for (i=0; settings[i]; i++)
+    {
+        list = (OSArray *) settingsCallbacks->getObject(settings[i]);
+        if (!list) {
+            // New array of callbacks for this setting
+            list = OSArray::withCapacity(1);
+            settingsCallbacks->setObject(settings[i], list);
+            list->release();
+        }
+
+        // Add caller to the callback list
+        list->setObject(pmso);
+    }
+    PMSETTING_UNLOCK();
+
+    // Return handle to the caller, the setting object is private.
+    *handle = pmsh;
+
+    return kIOReturnSuccess;
+}
+
+//******************************************************************************
+// deregisterPMSettingObject (private)
+//
+// Only called from PMSettingObject.
+//******************************************************************************
+
+void IOPMrootDomain::deregisterPMSettingObject( PMSettingObject * pmso )
+{
+    thread_t                thisThread = current_thread();
+    PMSettingCallEntry      *callEntry;
+    OSCollectionIterator    *iter;
+    OSSymbol                *sym;
+    OSArray                 *array;
+    int                     index;
+    bool                    wait;
+
+    PMSETTING_LOCK();
+
+    pmso->disabled = true;
+
+    // Wait for all callout threads to finish.
+    do {
+        wait = false;
+        queue_iterate(&pmso->calloutQueue, callEntry, PMSettingCallEntry *, link)
+        {
+            if (callEntry->thread != thisThread)
+            {
+                wait = true;
+                break;
+            }
+        }
+        if (wait)
+        {
+            assert(0 == pmso->waitThread);
+            pmso->waitThread = thisThread;
+            PMSETTING_WAIT(pmso);
+            pmso->waitThread = 0;
+        }
+    } while (wait);
+
+    // Search each PM settings array in the kernel.
+    iter = OSCollectionIterator::withCollection(settingsCallbacks);
+    if (iter)
+    {
+        while ((sym = OSDynamicCast(OSSymbol, iter->getNextObject())))
+        {
+            array = (OSArray *) settingsCallbacks->getObject(sym);
+            index = array->getNextIndexOfObject(pmso, 0);
+            if (-1 != index) {
+                array->removeObject(index);
+            }
+        }
+        iter->release();
+    }
+
+    PMSETTING_UNLOCK();
+
+    pmso->release();
+}
+
+//******************************************************************************
+// informCPUStateChange
+//
+// Call into PM CPU code so that CPU power savings may dynamically adjust for
+// running on battery, with the lid closed, etc.
+//
+// informCPUStateChange is a no-op on non x86 systems
+// only x86 has explicit support in the IntelCPUPowerManagement kext
+//******************************************************************************
+
+void IOPMrootDomain::informCPUStateChange(
+    uint32_t type,
+    uint32_t value )
+{
+#if defined(__i386__) || defined(__x86_64__)
+
+    pmioctlVariableInfo_t varInfoStruct;
+    int                 pmCPUret = 0;
+    const char          *varNameStr = NULL;
+    int32_t             *varIndex   = NULL;
+
+    if (kInformAC == type) {
+        varNameStr = kIOPMRootDomainBatPowerCString;
+        varIndex = &idxPMCPULimitedPower;
+    } else if (kInformLid == type) {
+        varNameStr = kIOPMRootDomainLidCloseCString;
+        varIndex = &idxPMCPUClamshell;
+    } else {
+        return;
+    }
+
+    // Set the new value!
+    // pmCPUControl will assign us a new ID if one doesn't exist yet
+    bzero(&varInfoStruct, sizeof(pmioctlVariableInfo_t));
+    varInfoStruct.varID         = *varIndex;
+    varInfoStruct.varType       = vBool;
+    varInfoStruct.varInitValue  = value;
+    varInfoStruct.varCurValue   = value;
+    strncpy( (char *)varInfoStruct.varName,
+             (const char *)varNameStr,
+             strlen(varNameStr) + 1 );
+
+    // Set!
+    pmCPUret = pmCPUControl( PMIOCSETVARINFO, (void *)&varInfoStruct );
+
+    // pmCPU only assigns numerical id's when a new varName is specified
+    if ((0 == pmCPUret)
+        && (*varIndex == kCPUUnknownIndex))
+    {
+        // pmCPUControl has assigned us a new variable ID.
+        // Let's re-read the structure we just SET to learn that ID.
+        pmCPUret = pmCPUControl( PMIOCGETVARNAMEINFO, (void *)&varInfoStruct );
+
+        if (0 == pmCPUret)
+        {
+            // Store it in idxPMCPUClamshell or idxPMCPULimitedPower
+            *varIndex = varInfoStruct.varID;
+        }
+    }
+
+    return;
+
+#endif /* __i386__ || __x86_64__ */
+}
+
+// MARK: -
+// MARK: Deep Sleep Policy
+
+#if HIBERNATION
+
+//******************************************************************************
+// evaluateSystemSleepPolicy
+//******************************************************************************
+
+#define kIOPlatformSystemSleepPolicyKey     "IOPlatformSystemSleepPolicy"
+
+// Sleep flags
+enum {
+    kIOPMSleepFlagHibernate         = 0x00000001,
+    kIOPMSleepFlagSleepTimerEnable  = 0x00000002
+};
+
+struct IOPMSystemSleepPolicyEntry
+{
+    uint32_t    factorMask;
+    uint32_t    factorBits;
+    uint32_t    sleepFlags;
+    uint32_t    wakeEvents;
+} __attribute__((packed));
+
+struct IOPMSystemSleepPolicyTable
+{
+    uint32_t    signature;
+    uint16_t    version;
+    uint16_t    entryCount;
+    IOPMSystemSleepPolicyEntry  entries[];
+} __attribute__((packed));
+
+enum {
+    kIOPMSleepAttributeHibernateSetup   = 0x00000001,
+    kIOPMSleepAttributeHibernateSleep   = 0x00000002
+};
+
+static uint32_t
+getSleepTypeAttributes( uint32_t sleepType )
+{
+    static const uint32_t sleepTypeAttributes[ kIOPMSleepTypeLast ] =
+    {
+    /* invalid   */ 0,
+    /* abort     */ 0,
+    /* normal    */ 0,
+    /* safesleep */ kIOPMSleepAttributeHibernateSetup,
+    /* hibernate */ kIOPMSleepAttributeHibernateSetup | kIOPMSleepAttributeHibernateSleep,
+    /* standby   */ kIOPMSleepAttributeHibernateSetup | kIOPMSleepAttributeHibernateSleep,
+    /* poweroff  */ kIOPMSleepAttributeHibernateSetup | kIOPMSleepAttributeHibernateSleep,
+    /* deepidle  */ 0
+    };
+
+    if (sleepType >= kIOPMSleepTypeLast)
+        return 0;
+
+    return sleepTypeAttributes[sleepType];
+}
+
+bool IOPMrootDomain::evaluateSystemSleepPolicy(
+    IOPMSystemSleepParameters * params, int sleepPhase, uint32_t * hibMode )
+{
+    const IOPMSystemSleepPolicyTable * pt;
+    OSObject *  prop = 0;
+    OSData *    policyData;
+    uint64_t    currentFactors = 0;
+    uint32_t    standbyDelay   = 0;
+    uint32_t    powerOffDelay  = 0;
+    uint32_t    powerOffTimer  = 0;
+    uint32_t    mismatch;
+    bool        standbyEnabled;
+    bool        powerOffEnabled;
+    bool        found = false;
+
+    // Get platform's sleep policy table
+    if (!gSleepPolicyHandler)
+    {
+        prop = getServiceRoot()->copyProperty(kIOPlatformSystemSleepPolicyKey);
+        if (!prop) goto done;
+    }
+
+    // Fetch additional settings
+    standbyEnabled = (getSleepOption(kIOPMDeepSleepDelayKey, &standbyDelay)
+        && (getProperty(kIOPMDeepSleepEnabledKey) == kOSBooleanTrue));
+    powerOffEnabled = (getSleepOption(kIOPMAutoPowerOffDelayKey, &powerOffDelay)
+        && (getProperty(kIOPMAutoPowerOffEnabledKey) == kOSBooleanTrue));
+    if (!getSleepOption(kIOPMAutoPowerOffTimerKey, &powerOffTimer))
+        powerOffTimer = powerOffDelay;
+
+    DLOG("phase %d, standby %d delay %u, poweroff %d delay %u timer %u, hibernate 0x%x\n",
+        sleepPhase, standbyEnabled, standbyDelay,
+        powerOffEnabled, powerOffDelay, powerOffTimer, *hibMode);
+
+    // pmset level overrides
+    if ((*hibMode & kIOHibernateModeOn) == 0)
+    {
+        if (!gSleepPolicyHandler)
+        {
+            standbyEnabled  = false;
+            powerOffEnabled = false;
+        }
+    }
+    else if (!(*hibMode & kIOHibernateModeSleep))
+    {
+        // Force hibernate (i.e. mode 25)
+        // If standby is enabled, force standy.
+        // If poweroff is enabled, force poweroff.
+        if (standbyEnabled)
+            currentFactors |= kIOPMSleepFactorStandbyForced;
+        else if (powerOffEnabled)
+            currentFactors |= kIOPMSleepFactorAutoPowerOffForced;
+        else
+            currentFactors |= kIOPMSleepFactorHibernateForced;
+    }
+
+    // Current factors based on environment and assertions
+    if (sleepTimerMaintenance)
+        currentFactors |= kIOPMSleepFactorSleepTimerWake;
+    if (standbyEnabled && sleepToStandby && !gSleepPolicyHandler)
+        currentFactors |= kIOPMSleepFactorSleepTimerWake;
+    if (!clamshellClosed)
+        currentFactors |= kIOPMSleepFactorLidOpen;
+    if (acAdaptorConnected)
+        currentFactors |= kIOPMSleepFactorACPower;
+    if (lowBatteryCondition)
+        currentFactors |= kIOPMSleepFactorBatteryLow;
+    if (!standbyDelay)
+        currentFactors |= kIOPMSleepFactorStandbyNoDelay;
+    if (!standbyEnabled)
+        currentFactors |= kIOPMSleepFactorStandbyDisabled;
+    if (getPMAssertionLevel(kIOPMDriverAssertionUSBExternalDeviceBit) !=
+        kIOPMDriverAssertionLevelOff)
+        currentFactors |= kIOPMSleepFactorUSBExternalDevice;
+    if (getPMAssertionLevel(kIOPMDriverAssertionBluetoothHIDDevicePairedBit) !=
+        kIOPMDriverAssertionLevelOff)
+        currentFactors |= kIOPMSleepFactorBluetoothHIDDevice;
+    if (getPMAssertionLevel(kIOPMDriverAssertionExternalMediaMountedBit) !=
+        kIOPMDriverAssertionLevelOff)
+        currentFactors |= kIOPMSleepFactorExternalMediaMounted;
+    if (getPMAssertionLevel(kIOPMDriverAssertionReservedBit5) !=
+        kIOPMDriverAssertionLevelOff)
+        currentFactors |= kIOPMSleepFactorThunderboltDevice;
+    if (_scheduledAlarms != 0)
+        currentFactors |= kIOPMSleepFactorRTCAlarmScheduled;
+    if (getPMAssertionLevel(kIOPMDriverAssertionMagicPacketWakeEnabledBit) !=
+        kIOPMDriverAssertionLevelOff)
+        currentFactors |= kIOPMSleepFactorMagicPacketWakeEnabled;
+#define TCPKEEPALIVE 1
+#if TCPKEEPALIVE
+    if (getPMAssertionLevel(kIOPMDriverAssertionNetworkKeepAliveActiveBit) !=
+        kIOPMDriverAssertionLevelOff)
+        currentFactors |= kIOPMSleepFactorNetworkKeepAliveActive;
+#endif
+    if (!powerOffEnabled)
+        currentFactors |= kIOPMSleepFactorAutoPowerOffDisabled;
+    if (desktopMode)
+        currentFactors |= kIOPMSleepFactorExternalDisplay;
+    if (userWasActive)
+        currentFactors |= kIOPMSleepFactorLocalUserActivity;
+    if (darkWakeHibernateError && !CAP_HIGHEST(kIOPMSystemCapabilityGraphics))
+        currentFactors |= kIOPMSleepFactorHibernateFailed;
+    if (thermalWarningState)
+        currentFactors |= kIOPMSleepFactorThermalWarning;
+
+    DLOG("sleep factors 0x%llx\n", currentFactors);
+
+    if (gSleepPolicyHandler)
+    {
+        uint32_t    savedHibernateMode;
+        IOReturn    result;
+
+        if (!gSleepPolicyVars)
+        {
+            gSleepPolicyVars = IONew(IOPMSystemSleepPolicyVariables, 1);
+            if (!gSleepPolicyVars)
+                goto done;
+            bzero(gSleepPolicyVars, sizeof(*gSleepPolicyVars));
+        }
+        gSleepPolicyVars->signature = kIOPMSystemSleepPolicySignature;
+        gSleepPolicyVars->version   = kIOPMSystemSleepPolicyVersion;
+        gSleepPolicyVars->currentCapability = _currentCapability;
+        gSleepPolicyVars->highestCapability = _highestCapability;
+        gSleepPolicyVars->sleepFactors      = currentFactors;
+        gSleepPolicyVars->sleepReason       = lastSleepReason;
+        gSleepPolicyVars->sleepPhase        = sleepPhase;
+        gSleepPolicyVars->standbyDelay      = standbyDelay;
+        gSleepPolicyVars->poweroffDelay     = powerOffDelay;
+        gSleepPolicyVars->scheduledAlarms   = _scheduledAlarms | _userScheduledAlarm;
+        gSleepPolicyVars->poweroffTimer     = powerOffTimer;
+
+        if (kIOPMSleepPhase0 == sleepPhase)
+        {
+            // preserve hibernateMode
+            savedHibernateMode = gSleepPolicyVars->hibernateMode;
+            gSleepPolicyVars->hibernateMode = *hibMode;
+        }
+        else if (kIOPMSleepPhase1 == sleepPhase)
+        {
+            // use original hibernateMode for phase2
+            gSleepPolicyVars->hibernateMode = *hibMode;
+        }
+
+        result = gSleepPolicyHandler(gSleepPolicyTarget, gSleepPolicyVars, params);
+
+        if (kIOPMSleepPhase0 == sleepPhase)
+        {
+            // restore hibernateMode
+            gSleepPolicyVars->hibernateMode = savedHibernateMode;
+        }
+
+        if ((result != kIOReturnSuccess) ||
+             (kIOPMSleepTypeInvalid == params->sleepType) ||
+             (params->sleepType >= kIOPMSleepTypeLast) ||
+             (kIOPMSystemSleepParametersVersion != params->version))
+        {
+            MSG("sleep policy handler error\n");
+            goto done;
+        }
+
+        if ((getSleepTypeAttributes(params->sleepType) &
+             kIOPMSleepAttributeHibernateSetup) &&
+            ((*hibMode & kIOHibernateModeOn) == 0))
+        {
+            *hibMode |= (kIOHibernateModeOn | kIOHibernateModeSleep);
+        }
+
+        DLOG("sleep params v%u, type %u, flags 0x%x, wake 0x%x, timer %u, poweroff %u\n",
+            params->version, params->sleepType, params->sleepFlags,
+            params->ecWakeEvents, params->ecWakeTimer, params->ecPoweroffTimer);
+        found = true;
+        goto done;
+    }
+
+    // Policy table is meaningless without standby enabled
+    if (!standbyEnabled)
+        goto done;
+
+    // Validate the sleep policy table
+    policyData = OSDynamicCast(OSData, prop);
+    if (!policyData || (policyData->getLength() <= sizeof(IOPMSystemSleepPolicyTable)))
+        goto done;
+
+    pt = (const IOPMSystemSleepPolicyTable *) policyData->getBytesNoCopy();
+    if ((pt->signature != kIOPMSystemSleepPolicySignature) ||
+        (pt->version != 1) || (0 == pt->entryCount))
+        goto done;
+
+    if (((policyData->getLength() - sizeof(IOPMSystemSleepPolicyTable)) !=
+         (sizeof(IOPMSystemSleepPolicyEntry) * pt->entryCount)))
+        goto done;
+
+    for (uint32_t i = 0; i < pt->entryCount; i++)
+    {
+        const IOPMSystemSleepPolicyEntry * entry = &pt->entries[i];
+        mismatch = (((uint32_t)currentFactors ^ entry->factorBits) & entry->factorMask);
+
+        DLOG("mask 0x%08x, bits 0x%08x, flags 0x%08x, wake 0x%08x, mismatch 0x%08x\n",
+            entry->factorMask, entry->factorBits,
+            entry->sleepFlags, entry->wakeEvents, mismatch);
+        if (mismatch)
+            continue;
+
+        DLOG("^ found match\n");
+        found = true;
+
+        params->version = kIOPMSystemSleepParametersVersion;
+        params->reserved1 = 1;
+        if (entry->sleepFlags & kIOPMSleepFlagHibernate)
+            params->sleepType = kIOPMSleepTypeStandby;
+        else
+            params->sleepType = kIOPMSleepTypeNormalSleep;
+
+        params->ecWakeEvents = entry->wakeEvents;
+        if (entry->sleepFlags & kIOPMSleepFlagSleepTimerEnable)
+        {
+            if (kIOPMSleepPhase2 == sleepPhase)
+            {
+                clock_sec_t now_secs = gIOLastSleepTime.tv_sec;
+
+                if (!_standbyTimerResetSeconds ||
+                    (now_secs <= _standbyTimerResetSeconds))
+                {
+                    // Reset standby timer adjustment
+                    _standbyTimerResetSeconds = now_secs;
+                    DLOG("standby delay %u, reset %u\n",
+                        standbyDelay, (uint32_t) _standbyTimerResetSeconds);
+                }
+                else if (standbyDelay)
+                {
+                    // Shorten the standby delay timer
+                    clock_sec_t elapsed = now_secs - _standbyTimerResetSeconds;
+                    if (standbyDelay > elapsed)
+                        standbyDelay -= elapsed;
+                    else
+                        standbyDelay = 1; // must be > 0
+
+                    DLOG("standby delay %u, elapsed %u\n",
+                        standbyDelay, (uint32_t) elapsed);
+                }
+            }
+            params->ecWakeTimer = standbyDelay;
+        }
+        else if (kIOPMSleepPhase2 == sleepPhase)
+        {
+            // A sleep that does not enable the sleep timer will reset
+            // the standby delay adjustment.
+            _standbyTimerResetSeconds = 0;
+        }
+        break;
+    }
+
+done:
+    if (prop)
+        prop->release();
+
+    return found;
+}
+
+static IOPMSystemSleepParameters gEarlySystemSleepParams;
+
+void IOPMrootDomain::evaluateSystemSleepPolicyEarly( void )
+{
+    // Evaluate early (priority interest phase), before drivers sleep.
+
+    DLOG("%s\n", __FUNCTION__);
+    removeProperty(kIOPMSystemSleepParametersKey);
+
+    // Full wake resets the standby timer delay adjustment
+    if (_highestCapability & kIOPMSystemCapabilityGraphics)
+        _standbyTimerResetSeconds = 0;
+
+    hibernateDisabled = false;
+    hibernateMode = 0;
+    getSleepOption(kIOHibernateModeKey, &hibernateMode);
+
+    // Save for late evaluation if sleep is aborted
+    bzero(&gEarlySystemSleepParams, sizeof(gEarlySystemSleepParams));
+
+    if (evaluateSystemSleepPolicy(&gEarlySystemSleepParams, kIOPMSleepPhase1,
+                                  &hibernateMode))
+    {
+        if (!hibernateRetry &&
+            ((getSleepTypeAttributes(gEarlySystemSleepParams.sleepType) &
+              kIOPMSleepAttributeHibernateSetup) == 0))
+        {
+            // skip hibernate setup
+            hibernateDisabled = true;
+        }
+    }
+
+    // Publish IOPMSystemSleepType
+    uint32_t sleepType = gEarlySystemSleepParams.sleepType;
+    if (sleepType == kIOPMSleepTypeInvalid)
+    {
+        // no sleep policy
+        sleepType = kIOPMSleepTypeNormalSleep;
+        if (hibernateMode & kIOHibernateModeOn)
+            sleepType = (hibernateMode & kIOHibernateModeSleep) ?
+                        kIOPMSleepTypeSafeSleep : kIOPMSleepTypeHibernate;
+    }
+    else if ((sleepType == kIOPMSleepTypeStandby) &&
+             (gEarlySystemSleepParams.ecPoweroffTimer))
+    {
+        // report the lowest possible sleep state
+        sleepType = kIOPMSleepTypePowerOff;
+    }
+
+    setProperty(kIOPMSystemSleepTypeKey, sleepType, 32);
+}
+
+void IOPMrootDomain::evaluateSystemSleepPolicyFinal( void )
+{
+    IOPMSystemSleepParameters   params;
+    OSData *                    paramsData;
+
+    // Evaluate sleep policy after sleeping drivers but before platform sleep.
+
+    DLOG("%s\n", __FUNCTION__);
+
+    bzero(&params, sizeof(params));
+    if (evaluateSystemSleepPolicy(&params, kIOPMSleepPhase2, &hibernateMode))
+    {
+        if ((hibernateDisabled || hibernateAborted) &&
+            (getSleepTypeAttributes(params.sleepType) &
+             kIOPMSleepAttributeHibernateSetup))
+        {
+            // Final evaluation picked a state requiring hibernation,
+            // but hibernate setup was skipped. Arm a short sleep using
+            // the early non-hibernate sleep parameters.
+            // Set hibernateRetry flag to force hibernate setup on the
+            // next sleep.
+
+            bcopy(&gEarlySystemSleepParams, &params, sizeof(params));
+            params.sleepType = kIOPMSleepTypeAbortedSleep;
+            params.ecWakeTimer = 1;
+            hibernateRetry = true;
+            DLOG("wake in %u secs for hibernateDisabled %d, hibernateAborted %d\n",
+                params.ecWakeTimer, hibernateDisabled, hibernateAborted);
+        }
+        else
+        {
+            hibernateRetry = false;
+        }
+
+        paramsData = OSData::withBytes(&params, sizeof(params));
+        if (paramsData)
+        {
+            setProperty(kIOPMSystemSleepParametersKey, paramsData);
+            paramsData->release();
+        }
+
+        if (getSleepTypeAttributes(params.sleepType) &
+            kIOPMSleepAttributeHibernateSleep)
+        {
+            // Disable sleep to force hibernation
+            gIOHibernateMode &= ~kIOHibernateModeSleep;
+        }
+    }
+}
+
+bool IOPMrootDomain::getHibernateSettings(
+    uint32_t *  hibernateModePtr,
+    uint32_t *  hibernateFreeRatio,
+    uint32_t *  hibernateFreeTime )
+{
+    // Called by IOHibernateSystemSleep() after evaluateSystemSleepPolicyEarly()
+    // has updated the hibernateDisabled flag.
+
+    bool ok = getSleepOption(kIOHibernateModeKey, hibernateModePtr);
+    getSleepOption(kIOHibernateFreeRatioKey, hibernateFreeRatio);
+    getSleepOption(kIOHibernateFreeTimeKey, hibernateFreeTime);
+    if (hibernateDisabled)
+        *hibernateModePtr = 0;
+    else if (gSleepPolicyHandler)
+        *hibernateModePtr = hibernateMode;
+    DLOG("hibernateMode 0x%x\n", *hibernateModePtr);
+    return ok;
+}
+
+bool IOPMrootDomain::getSleepOption( const char * key, uint32_t * option )
+{
+    OSObject *      optionsProp;
+    OSDictionary *  optionsDict;
+    OSObject *      obj = 0;
+    OSNumber *      num;
+    bool            ok = false;
+
+    optionsProp = copyProperty(kRootDomainSleepOptionsKey);
+    optionsDict = OSDynamicCast(OSDictionary, optionsProp);
+
+    if (optionsDict)
+    {
+        obj = optionsDict->getObject(key);
+        if (obj) obj->retain();
+    }
+    if (!obj)
+    {
+        obj = copyProperty(key);
+    }
+    if (obj) 
+    {
+        if ((num = OSDynamicCast(OSNumber, obj)))
+        {
+            *option = num->unsigned32BitValue();
+            ok = true;
+        }
+        else if (OSDynamicCast(OSBoolean, obj))
+        {
+            *option = (obj == kOSBooleanTrue) ? 1 : 0;
+            ok = true;
+        }
+    }
+
+    if (obj)
+        obj->release();
+    if (optionsProp)
+        optionsProp->release();
+
+    return true;
+}
+#endif /* HIBERNATION */
+
+IOReturn IOPMrootDomain::getSystemSleepType( uint32_t * sleepType )
+{
+#if HIBERNATION
+    IOPMSystemSleepParameters   params;
+    uint32_t                    hibMode = 0;
+    bool                        ok;
+
+    if (gIOPMWorkLoop->inGate() == false)
+    {
+        IOReturn ret = gIOPMWorkLoop->runAction(
+                        OSMemberFunctionCast(IOWorkLoop::Action, this,
+                            &IOPMrootDomain::getSystemSleepType),
+                        (OSObject *) this,
+                        (void *) sleepType);
+        return ret;
+    }
+
+    getSleepOption(kIOHibernateModeKey, &hibMode);
+    bzero(&params, sizeof(params));
+
+    ok = evaluateSystemSleepPolicy(&params, kIOPMSleepPhase0, &hibMode);
+    if (ok)
+    {
+        *sleepType = params.sleepType;
+        return kIOReturnSuccess;
+    }
+#endif
+
+    return kIOReturnUnsupported;
+}
+
+// MARK: -
+// MARK: Shutdown and Restart
+
+//******************************************************************************
+// handlePlatformHaltRestart
+//
+//******************************************************************************
+
+struct HaltRestartApplierContext {
+    IOPMrootDomain *    RootDomain;
+    unsigned long       PowerState;
+    IOPMPowerFlags      PowerFlags;
+    UInt32              MessageType;
+    UInt32              Counter;
+};
+
+static void
+platformHaltRestartApplier( OSObject * object, void * context )
+{
+    IOPowerStateChangeNotification  notify;
+    HaltRestartApplierContext *     ctx;
+    AbsoluteTime                    startTime;
+    UInt32                          deltaTime;
+
+    ctx = (HaltRestartApplierContext *) context;
+
+    memset(&notify, 0, sizeof(notify));
+    notify.powerRef    = (void *)(uintptr_t)ctx->Counter;
+    notify.returnValue = 0;
+    notify.stateNumber = ctx->PowerState;
+    notify.stateFlags  = ctx->PowerFlags;
+
+    clock_get_uptime(&startTime);
+    ctx->RootDomain->messageClient( ctx->MessageType, object, (void *)&notify );
+    deltaTime = computeDeltaTimeMS(&startTime);
+
+    if ((deltaTime > kPMHaltTimeoutMS) ||
+        (gIOKitDebug & kIOLogPMRootDomain))
+    {
+        _IOServiceInterestNotifier * notifier;
+        notifier = OSDynamicCast(_IOServiceInterestNotifier, object);
+
+        // IOService children of IOPMrootDomain are not instrumented.
+        // Only IORootParent currently falls under that group.
+
+        if (notifier)
+        {
+            LOG("%s handler %p took %u ms\n",
+                (ctx->MessageType == kIOMessageSystemWillPowerOff) ? "PowerOff" :
+                     (ctx->MessageType == kIOMessageSystemPagingOff) ? "PagingOff" : "Restart",
+                OBFUSCATE(notifier->handler), (uint32_t) deltaTime );
+        }
+    }
+
+    ctx->Counter++;
+}
+
+void IOPMrootDomain::handlePlatformHaltRestart( UInt32 pe_type )
+{
+    HaltRestartApplierContext   ctx;
+    AbsoluteTime                startTime;
+    UInt32                      deltaTime;
+
+    memset(&ctx, 0, sizeof(ctx));
+    ctx.RootDomain = this;
+
+    clock_get_uptime(&startTime);
+    switch (pe_type)
+    {
+        case kPEHaltCPU:
+        case kPEUPSDelayHaltCPU:
+            ctx.PowerState  = OFF_STATE;
+            ctx.MessageType = kIOMessageSystemWillPowerOff;
+            break;
+
+        case kPERestartCPU:
+            ctx.PowerState  = RESTART_STATE;
+            ctx.MessageType = kIOMessageSystemWillRestart;
+            break;
+
+        case kPEPagingOff:
+            ctx.PowerState  = ON_STATE;
+            ctx.MessageType = kIOMessageSystemPagingOff;
+            IOService::updateConsoleUsers(NULL, kIOMessageSystemPagingOff);
+#if HIBERNATION
+            IOHibernateSystemRestart();
+#endif
+            break;
+
+        default:
+            return;
+    }
+
+    // Notify legacy clients
+    applyToInterested(gIOPriorityPowerStateInterest, platformHaltRestartApplier, &ctx);
+
+    // For normal shutdown, turn off File Server Mode.
+    if (kPEHaltCPU == pe_type)
+    {
+        const OSSymbol * setting = OSSymbol::withCString(kIOPMSettingRestartOnPowerLossKey);
+        OSNumber * num = OSNumber::withNumber((unsigned long long) 0, 32);
+        if (setting && num)
+        {
+            setPMSetting(setting, num);
+            setting->release();
+            num->release();
+        }
+    }
+
+    if (kPEPagingOff != pe_type)
+    {
+        // Notify in power tree order
+        notifySystemShutdown(this, ctx.MessageType);
+    }
+
+    IOCPURunPlatformHaltRestartActions(pe_type);
+
+    deltaTime = computeDeltaTimeMS(&startTime);
+    LOG("%s all drivers took %u ms\n",
+        (ctx.MessageType == kIOMessageSystemWillPowerOff) ? "PowerOff" :
+            (ctx.MessageType == kIOMessageSystemPagingOff) ? "PagingOff" : "Restart",
+        (uint32_t) deltaTime );
+}
+
+//******************************************************************************
+// shutdownSystem
+//
+//******************************************************************************
+
+IOReturn IOPMrootDomain::shutdownSystem( void )
+{
+    return kIOReturnUnsupported;
+}
+
+//******************************************************************************
+// restartSystem
+//
+//******************************************************************************
+
+IOReturn IOPMrootDomain::restartSystem( void )
+{
+    return kIOReturnUnsupported;
+}
+
+// MARK: -
+// MARK: System Capability
+
+//******************************************************************************
+// tagPowerPlaneService
+//
+// Running on PM work loop thread.
+//******************************************************************************
+
+void IOPMrootDomain::tagPowerPlaneService(
+        IOService *     service,
+        IOPMActions *   actions )
+{
+    uint32_t    flags = 0;
+    bool        isDisplayWrangler;
+
+    memset(actions, 0, sizeof(*actions));
+    actions->target = this;
+
+    if (service == this)
+    {
+        actions->actionPowerChangeStart =
+            OSMemberFunctionCast(
+                IOPMActionPowerChangeStart, this,
+                &IOPMrootDomain::handleOurPowerChangeStart);
+
+        actions->actionPowerChangeDone =
+            OSMemberFunctionCast(
+                IOPMActionPowerChangeDone, this,
+                &IOPMrootDomain::handleOurPowerChangeDone);
+
+        actions->actionPowerChangeOverride =
+            OSMemberFunctionCast(
+                IOPMActionPowerChangeOverride, this,
+                &IOPMrootDomain::overrideOurPowerChange);
+        return;
+    }
+
+#if !NO_KERNEL_HID
+    isDisplayWrangler = (0 != service->metaCast("IODisplayWrangler"));
+    if (isDisplayWrangler)
+    {
+        wrangler = service;
+    }
+#else
+    isDisplayWrangler = false;
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+    if (isDisplayWrangler)
+        flags |= kPMActionsFlagIsDisplayWrangler;
+    if (service->getProperty("IOPMStrictTreeOrder"))
+        flags |= kPMActionsFlagIsGraphicsDevice;
+    if (service->getProperty("IOPMUnattendedWakePowerState"))
+        flags |= kPMActionsFlagIsAudioDevice;
+#endif
+
+    // Find the power connection object that is a child of the PCI host
+    // bridge, and has a graphics/audio device attached below. Mark the
+    // power branch for delayed child notifications.
+
+    if (flags)
+    {
+        IORegistryEntry * child  = service;
+        IORegistryEntry * parent = child->getParentEntry(gIOPowerPlane);
+
+        while (child != this)
+        {
+            if ((parent == pciHostBridgeDriver) ||
+                (parent == this))
+            {
+                if (OSDynamicCast(IOPowerConnection, child))
+                {
+                    IOPowerConnection * conn = (IOPowerConnection *) child;
+                    conn->delayChildNotification = true;
+                }
+                break;
+            }
+            child = parent;
+            parent = child->getParentEntry(gIOPowerPlane);
+        }
+    }
+
+    if (flags)
+    {
+        DLOG("%s tag flags %x\n", service->getName(), flags);
+        actions->parameter |= flags;
+        actions->actionPowerChangeOverride =
+            OSMemberFunctionCast(
+                IOPMActionPowerChangeOverride, this,
+                &IOPMrootDomain::overridePowerChangeForUIService);
+
+        if (flags & kPMActionsFlagIsDisplayWrangler)
+        {
+            actions->actionActivityTickle =
+                OSMemberFunctionCast(
+                    IOPMActionActivityTickle, this,
+                    &IOPMrootDomain::handleActivityTickleForDisplayWrangler);
+
+            actions->actionUpdatePowerClient =
+                OSMemberFunctionCast(
+                    IOPMActionUpdatePowerClient, this,
+                    &IOPMrootDomain::handleUpdatePowerClientForDisplayWrangler);
+        }
+        return;
+    }
+
+    // Locate the first PCI host bridge for PMTrace.
+    if (!pciHostBridgeDevice && service->metaCast("IOPCIBridge"))
+    {
+        IOService * provider = service->getProvider();
+        if (OSDynamicCast(IOPlatformDevice, provider) &&
+            provider->inPlane(gIODTPlane))
+        {
+            pciHostBridgeDevice = provider;
+            pciHostBridgeDriver = service;
+            DLOG("PMTrace found PCI host bridge %s->%s\n",
+                provider->getName(), service->getName());
+        }
+    }
+
+    // Tag top-level PCI devices. The order of PMinit() call does not
+    // change across boots and is used as the PCI bit number.
+    if (pciHostBridgeDevice && service->metaCast("IOPCIDevice"))
+    {
+        // Would prefer to check built-in property, but tagPowerPlaneService()
+        // is called before pciDevice->registerService().
+        IORegistryEntry * parent = service->getParentEntry(gIODTPlane);
+        if ((parent == pciHostBridgeDevice) && service->getProperty("acpi-device"))
+        {
+            int bit = pmTracer->recordTopLevelPCIDevice( service );
+            if (bit >= 0)
+            {
+                // Save the assigned bit for fast lookup.
+                actions->parameter |= (bit & kPMActionsPCIBitNumberMask);
+
+                actions->actionPowerChangeStart =
+                    OSMemberFunctionCast(
+                        IOPMActionPowerChangeStart, this,
+                        &IOPMrootDomain::handlePowerChangeStartForPCIDevice);
+
+                actions->actionPowerChangeDone =
+                    OSMemberFunctionCast(
+                        IOPMActionPowerChangeDone, this,
+                        &IOPMrootDomain::handlePowerChangeDoneForPCIDevice);
+            }
+        }
+    }
+}
+
+//******************************************************************************
+// PM actions for root domain
+//******************************************************************************
+
+void IOPMrootDomain::overrideOurPowerChange(
+    IOService *             service,
+    IOPMActions *           actions,
+    IOPMPowerStateIndex *   inOutPowerState,
+    IOPMPowerChangeFlags *  inOutChangeFlags,
+    IOPMRequestTag          requestTag )
+{
+    uint32_t powerState  = (uint32_t) *inOutPowerState;
+    uint32_t changeFlags = *inOutChangeFlags;
+    uint32_t currentPowerState = (uint32_t) getPowerState();
+
+    if (changeFlags & kIOPMParentInitiated)
+    {
+        // Root parent is permanently pegged at max power,
+        // a parent initiated power change is unexpected.
+        *inOutChangeFlags |= kIOPMNotDone;
+        return;
+    }
+
+    if (powerState < currentPowerState)
+    {
+        if (CAP_CURRENT(kIOPMSystemCapabilityGraphics))
+        {
+            // Root domain is dropping power state ON->SLEEP.
+            // If system is in full wake, first enter dark wake by
+            // converting the power drop to a capability change.
+            // Once in dark wake, transition to sleep state ASAP.
+
+            darkWakeToSleepASAP = true;
+
+            // Drop graphics and audio capability
+            _desiredCapability &= ~(
+                kIOPMSystemCapabilityGraphics |
+                kIOPMSystemCapabilityAudio    );
+
+            // Convert to capability change (ON->ON)
+            *inOutPowerState = ON_STATE;
+            *inOutChangeFlags |= kIOPMSynchronize;
+
+            // Revert device desire from SLEEP to ON
+            changePowerStateToPriv(ON_STATE);
+        }
+        else
+        {
+            // System is in dark wake, ok to drop power state.
+            // Broadcast root powering down to entire tree.
+            *inOutChangeFlags |= kIOPMRootChangeDown;
+        }
+    }
+    else if (powerState > currentPowerState)
+    {
+        if ((_currentCapability & kIOPMSystemCapabilityCPU) == 0)
+        {
+            // Broadcast power up when waking from sleep, but not for the
+            // initial power change at boot by checking for cpu capability.
+            *inOutChangeFlags |= kIOPMRootChangeUp;
+        }
+    }
+}
+
+void IOPMrootDomain::handleOurPowerChangeStart(
+    IOService *             service,
+    IOPMActions *           actions,
+    IOPMPowerStateIndex     powerState,
+    IOPMPowerChangeFlags *  inOutChangeFlags,
+    IOPMRequestTag          requestTag )
+{
+    uint32_t changeFlags        = *inOutChangeFlags;
+    uint32_t currentPowerState  = (uint32_t) getPowerState();
+    uint32_t sleepReason        = requestTag ? requestTag : kIOPMSleepReasonIdle;
+    bool     publishSleepReason = false;
+
+    _systemTransitionType    = kSystemTransitionNone;
+    _systemMessageClientMask = 0;
+    capabilityLoss           = false;
+
+    if (lowBatteryCondition)
+    {
+        // Low battery notification may arrive after the initial sleep request
+        // has been queued. Override the sleep reason so powerd and others can
+        // treat this as an emergency sleep.
+        sleepReason = kIOPMSleepReasonLowPower;
+    }
+
+    // 1. Explicit capability change.
+
+    if (changeFlags & kIOPMSynchronize)
+    {
+        if (powerState == ON_STATE)
+        {
+            if (changeFlags & kIOPMSyncNoChildNotify)
+                _systemTransitionType = kSystemTransitionNewCapClient;
+            else
+                _systemTransitionType = kSystemTransitionCapability;
+        }
+    }
+
+    // 2. Going to sleep (cancellation still possible).
+
+    else if (powerState < currentPowerState)
+        _systemTransitionType = kSystemTransitionSleep;
+
+    // 3. Woke from (idle or demand) sleep.
+
+    else if (!systemBooting &&
+             (changeFlags & kIOPMSelfInitiated) &&
+             (powerState > currentPowerState))
+    {
+        _systemTransitionType = kSystemTransitionWake;
+        _desiredCapability = kIOPMSystemCapabilityCPU |
+                             kIOPMSystemCapabilityNetwork;
+
+        // Early exit from dark wake to full (e.g. LID open)
+        if (kFullWakeReasonNone != fullWakeReason)
+        {
+            _desiredCapability |= (
+                kIOPMSystemCapabilityGraphics |
+                kIOPMSystemCapabilityAudio );
+        }
+#if HIBERNATION
+    IOHibernateSetWakeCapabilities(_desiredCapability);
+#endif
+    }
+
+    // Update pending wake capability at the beginning of every
+    // state transition (including synchronize). This will become
+    // the current capability at the end of the transition.
+
+    if (kSystemTransitionSleep == _systemTransitionType)
+    {
+        _pendingCapability = 0;
+        capabilityLoss = true;
+
+        // Clear previous stats
+        IOLockLock(pmStatsLock);
+        if (pmStatsAppResponses)
+        {
+            pmStatsAppResponses->release();
+            pmStatsAppResponses = OSArray::withCapacity(5);
+        }
+        IOLockUnlock(pmStatsLock);
+
+    }
+    else if (kSystemTransitionNewCapClient != _systemTransitionType)
+    {
+        _pendingCapability = _desiredCapability |
+                             kIOPMSystemCapabilityCPU |
+                             kIOPMSystemCapabilityNetwork;
+
+        if (_pendingCapability & kIOPMSystemCapabilityGraphics)
+            _pendingCapability |= kIOPMSystemCapabilityAudio;
+
+        if ((kSystemTransitionCapability == _systemTransitionType) &&
+            (_pendingCapability == _currentCapability))
+        {
+            // Cancel the PM state change.
+            _systemTransitionType = kSystemTransitionNone;
+            *inOutChangeFlags |= kIOPMNotDone;
+        }
+        if (__builtin_popcount(_pendingCapability) <
+            __builtin_popcount(_currentCapability))
+            capabilityLoss = true;
+    }
+
+    // 1. Capability change.
+
+    if (kSystemTransitionCapability == _systemTransitionType)
+    {
+        // Dark to Full transition.
+        if (CAP_GAIN(kIOPMSystemCapabilityGraphics))
+        {
+            tracePoint( kIOPMTracePointDarkWakeExit );
+
+            willEnterFullWake();
+        }
+
+        // Full to Dark transition.
+        if (CAP_LOSS(kIOPMSystemCapabilityGraphics))
+        {
+            tracePoint( kIOPMTracePointDarkWakeEntry );
+            *inOutChangeFlags |= kIOPMSyncTellPowerDown;
+            _systemMessageClientMask = kSystemMessageClientPowerd |
+                                       kSystemMessageClientLegacyApp;
+
+
+            // rdar://15971327
+            // Prevent user active transitions before notifying clients
+            // that system will sleep.
+            preventTransitionToUserActive(true);
+
+            IOService::setAdvisoryTickleEnable( false );
+
+            // Publish the sleep reason for full to dark wake
+            publishSleepReason = true;
+            lastSleepReason = fullToDarkReason = sleepReason;
+
+            // Publish a UUID for the Sleep --> Wake cycle
+            handlePublishSleepWakeUUID(true);
+        }
+    }
+
+    // 2. System sleep.
+
+    else if (kSystemTransitionSleep == _systemTransitionType)
+    {
+        // Beginning of a system sleep transition.
+        // Cancellation is still possible.
+        tracePoint( kIOPMTracePointSleepStarted, sleepReason );
+
+        _systemMessageClientMask = kSystemMessageClientAll;
+        if ((_currentCapability & kIOPMSystemCapabilityGraphics) == 0)
+            _systemMessageClientMask &= ~kSystemMessageClientLegacyApp;
+        if ((_highestCapability & kIOPMSystemCapabilityGraphics) == 0)
+            _systemMessageClientMask &= ~kSystemMessageClientKernel;
+
+        // Record the reason for dark wake back to sleep
+        // System may not have ever achieved full wake
+
+        publishSleepReason = true;
+        lastSleepReason = sleepReason;
+    }
+
+    // 3. System wake.
+
+    else if (kSystemTransitionWake == _systemTransitionType)
+    {
+        tracePoint( kIOPMTracePointWakeWillPowerOnClients );
+        // Clear stats about sleep 
+
+        if (_pendingCapability & kIOPMSystemCapabilityGraphics)
+        {
+            willEnterFullWake();
+        }
+        else
+        {
+            // Message powerd only
+            _systemMessageClientMask = kSystemMessageClientPowerd;
+            tellClients(kIOMessageSystemWillPowerOn);
+        }
+    }
+
+    // The only location where the sleep reason is published. At this point
+    // sleep can still be cancelled, but sleep reason should be published
+    // early for logging purposes.
+
+    if (publishSleepReason)
+    {
+        static const char * IOPMSleepReasons[] =
+        {
+            kIOPMClamshellSleepKey,
+            kIOPMPowerButtonSleepKey,
+            kIOPMSoftwareSleepKey,
+            kIOPMOSSwitchHibernationKey,
+            kIOPMIdleSleepKey,
+            kIOPMLowPowerSleepKey,
+            kIOPMThermalEmergencySleepKey,
+            kIOPMMaintenanceSleepKey,
+            kIOPMSleepServiceExitKey,
+            kIOPMDarkWakeThermalEmergencyKey
+        };
+
+        // Record sleep cause in IORegistry
+        uint32_t reasonIndex = sleepReason - kIOPMSleepReasonClamshell;
+        if (reasonIndex < sizeof(IOPMSleepReasons)/sizeof(IOPMSleepReasons[0])) {
+            DLOG("sleep reason %s\n", IOPMSleepReasons[reasonIndex]);
+            setProperty(kRootDomainSleepReasonKey, IOPMSleepReasons[reasonIndex]);
+        }
+    }
+
+    if ((kSystemTransitionNone != _systemTransitionType) &&
+        (kSystemTransitionNewCapClient != _systemTransitionType))
+    {
+        _systemStateGeneration++;
+        systemDarkWake = false;
+
+        DLOG("=== START (%u->%u, 0x%x) type %u, gen %u, msg %x, "
+             "dcp %x:%x:%x\n",
+            currentPowerState, (uint32_t) powerState, *inOutChangeFlags,
+            _systemTransitionType, _systemStateGeneration,
+            _systemMessageClientMask,
+            _desiredCapability, _currentCapability, _pendingCapability);
+    }
+}
+
+void IOPMrootDomain::handleOurPowerChangeDone(
+    IOService *             service,
+    IOPMActions *           actions,
+    IOPMPowerStateIndex     powerState,
+    IOPMPowerChangeFlags    changeFlags,
+    IOPMRequestTag          requestTag __unused )
+{
+    if (kSystemTransitionNewCapClient == _systemTransitionType)
+    {
+        _systemTransitionType = kSystemTransitionNone;
+        return;
+    }
+
+    if (_systemTransitionType != kSystemTransitionNone)
+    {
+        uint32_t currentPowerState = (uint32_t) getPowerState();
+
+        if (changeFlags & kIOPMNotDone)
+        {
+            // Power down was cancelled or vetoed.
+            _pendingCapability = _currentCapability;
+            lastSleepReason = 0;
+
+            if (!CAP_CURRENT(kIOPMSystemCapabilityGraphics) &&
+                 CAP_CURRENT(kIOPMSystemCapabilityCPU))
+            {
+                pmPowerStateQueue->submitPowerEvent(
+                    kPowerEventPolicyStimulus,
+                    (void *) kStimulusDarkWakeReentry,
+                    _systemStateGeneration );
+            }
+
+            // Revert device desire to max.
+            changePowerStateToPriv(ON_STATE);
+        }
+        else
+        {
+            // Send message on dark wake to full wake promotion.
+            // tellChangeUp() handles the normal SLEEP->ON case.
+
+            if (kSystemTransitionCapability == _systemTransitionType)
+            {
+                if (CAP_GAIN(kIOPMSystemCapabilityGraphics))
+                {
+                    lastSleepReason = 0; // stop logging wrangler tickles
+                    tellClients(kIOMessageSystemHasPoweredOn);
+                }
+                if (CAP_LOSS(kIOPMSystemCapabilityGraphics))
+                {
+                    // Going dark, reset full wake state
+                    // userIsActive will be cleared by wrangler powering down
+                    wranglerTickled = false;
+                    fullWakeReason = kFullWakeReasonNone;
+                }
+            }
+
+            // Reset state after exiting from dark wake.
+
+            if (CAP_GAIN(kIOPMSystemCapabilityGraphics) ||
+                CAP_LOSS(kIOPMSystemCapabilityCPU))
+            {
+                darkWakeMaintenance = false;
+                darkWakeToSleepASAP = false;
+                pciCantSleepValid   = false;
+                darkWakeSleepService = false;
+
+                if (CAP_LOSS(kIOPMSystemCapabilityCPU))
+                {
+                    // Remove the influence of display power assertion
+                    // before next system wake.
+                    if (wrangler) wrangler->changePowerStateForRootDomain(
+                                                kWranglerPowerStateMin );
+                    removeProperty(gIOPMUserTriggeredFullWakeKey);
+                }
+            }
+
+            // Entered dark mode.
+
+            if (((_pendingCapability & kIOPMSystemCapabilityGraphics) == 0) &&
+                 (_pendingCapability & kIOPMSystemCapabilityCPU))
+            {
+                // Queue an evaluation of whether to remain in dark wake,
+                // and for how long. This serves the purpose of draining
+                // any assertions from the queue.
+
+                pmPowerStateQueue->submitPowerEvent(
+                    kPowerEventPolicyStimulus,
+                    (void *) kStimulusDarkWakeEntry,
+                    _systemStateGeneration );
+            }
+        }
+
+        DLOG("=== FINISH (%u->%u, 0x%x) type %u, gen %u, msg %x, "
+             "dcp %x:%x:%x, dbgtimer %u\n",
+            currentPowerState, (uint32_t) powerState, changeFlags,
+            _systemTransitionType, _systemStateGeneration,
+            _systemMessageClientMask,
+            _desiredCapability, _currentCapability, _pendingCapability,
+            _lastDebugWakeSeconds);
+
+        if (_pendingCapability & kIOPMSystemCapabilityGraphics)
+        {
+            displayWakeCnt++;
+#if DARK_TO_FULL_EVALUATE_CLAMSHELL
+            if (clamshellExists && fullWakeThreadCall &&
+                CAP_HIGHEST(kIOPMSystemCapabilityGraphics))
+            {
+                // Not the initial graphics full power, graphics won't
+                // send a power notification to trigger a lid state
+                // evaluation.
+
+                AbsoluteTime deadline;
+                clock_interval_to_deadline(45, kSecondScale, &deadline);
+                thread_call_enter_delayed(fullWakeThreadCall, deadline);
+            }
+#endif
+        }
+        else if (CAP_GAIN(kIOPMSystemCapabilityCPU))
+            darkWakeCnt++;
+
+        // Update current system capability.
+        if (_currentCapability != _pendingCapability)
+            _currentCapability = _pendingCapability;
+
+        // Update highest system capability.
+
+        _highestCapability |= _currentCapability;
+
+        if (darkWakePostTickle &&
+            (kSystemTransitionWake == _systemTransitionType) &&
+            (gDarkWakeFlags & kDarkWakeFlagHIDTickleMask) ==
+             kDarkWakeFlagHIDTickleLate)
+        {
+            darkWakePostTickle = false;
+            reportUserInput();
+        }
+
+        // Reset tracepoint at completion of capability change,
+        // completion of wake transition, and aborted sleep transition.
+
+        if ((_systemTransitionType == kSystemTransitionCapability) ||
+            (_systemTransitionType == kSystemTransitionWake) ||
+            ((_systemTransitionType == kSystemTransitionSleep) &&
+             (changeFlags & kIOPMNotDone)))
+        {
+            setProperty(kIOPMSystemCapabilitiesKey, _currentCapability, 64);
+            tracePoint( kIOPMTracePointSystemUp, 0 );
+        }
+
+        _systemTransitionType = kSystemTransitionNone;
+        _systemMessageClientMask = 0;
+
+        logGraphicsClamp = false;
+    }
+}
+
+//******************************************************************************
+// PM actions for graphics and audio.
+//******************************************************************************
+
+void IOPMrootDomain::overridePowerChangeForUIService(
+    IOService *             service,
+    IOPMActions *           actions,
+    IOPMPowerStateIndex *   inOutPowerState,
+    IOPMPowerChangeFlags *  inOutChangeFlags )
+{
+    uint32_t powerState  = (uint32_t) *inOutPowerState;
+    uint32_t changeFlags = (uint32_t) *inOutChangeFlags;
+
+    if (kSystemTransitionNone == _systemTransitionType)
+    {
+        // Not in midst of a system transition.
+        // Do not modify power limit enable state.
+    }
+    else if ((actions->parameter & kPMActionsFlagLimitPower) == 0)
+    {
+        // Activate power limiter.
+
+        if ((actions->parameter & kPMActionsFlagIsDisplayWrangler) &&
+            ((_pendingCapability & kIOPMSystemCapabilityGraphics) == 0) &&
+            (changeFlags & kIOPMSynchronize))
+        {
+            actions->parameter |= kPMActionsFlagLimitPower;
+        }
+        else if ((actions->parameter & kPMActionsFlagIsAudioDevice) &&
+                 ((gDarkWakeFlags & kDarkWakeFlagAudioNotSuppressed) == 0) &&
+                 ((_pendingCapability & kIOPMSystemCapabilityAudio) == 0) &&
+                 (changeFlags & kIOPMSynchronize))
+        {
+            actions->parameter |= kPMActionsFlagLimitPower;
+        }
+        else if ((actions->parameter & kPMActionsFlagIsGraphicsDevice) &&
+                 (_systemTransitionType == kSystemTransitionSleep))
+        {
+            // For graphics devices, arm the limiter when entering
+            // system sleep. Not when dropping to dark wake.
+            actions->parameter |= kPMActionsFlagLimitPower;
+        }
+
+        if (actions->parameter & kPMActionsFlagLimitPower)
+        {
+            DLOG("+ plimit %s %p\n",
+                service->getName(), OBFUSCATE(service));
+        }
+    }
+    else
+    {
+        // Remove power limit.
+
+        if ((actions->parameter & (
+            kPMActionsFlagIsDisplayWrangler |
+            kPMActionsFlagIsGraphicsDevice )) &&
+            (_pendingCapability & kIOPMSystemCapabilityGraphics))
+        {
+            actions->parameter &= ~kPMActionsFlagLimitPower;
+        }
+        else if ((actions->parameter & kPMActionsFlagIsAudioDevice) &&
+                 (_pendingCapability & kIOPMSystemCapabilityAudio))
+        {
+            actions->parameter &= ~kPMActionsFlagLimitPower;
+        }
+
+        if ((actions->parameter & kPMActionsFlagLimitPower) == 0)
+        {
+            DLOG("- plimit %s %p\n",
+                service->getName(), OBFUSCATE(service));
+        }
+    }
+
+    if (actions->parameter & kPMActionsFlagLimitPower)
+    {
+        uint32_t maxPowerState = (uint32_t)(-1);
+
+        if (changeFlags & (kIOPMDomainDidChange | kIOPMDomainWillChange))
+        {
+            // Enforce limit for system power/cap transitions.
+
+            maxPowerState = 0;
+            if ((service->getPowerState() > maxPowerState) &&
+                (actions->parameter & kPMActionsFlagIsDisplayWrangler))
+            {
+                maxPowerState++;
+
+                // Remove lingering effects of any tickle before entering
+                // dark wake. It will take a new tickle to return to full
+                // wake, so the existing tickle state is useless.
+
+                if (changeFlags & kIOPMDomainDidChange)
+                    *inOutChangeFlags |= kIOPMExpireIdleTimer;
+            }
+            else if (actions->parameter & kPMActionsFlagIsGraphicsDevice)
+            {
+                maxPowerState++;
+            }
+        }
+        else
+        {
+            // Deny all self-initiated changes when power is limited.
+            // Wrangler tickle should never defeat the limiter.
+
+            maxPowerState = service->getPowerState();
+        }
+
+        if (powerState > maxPowerState)
+        {
+            DLOG("> plimit %s %p (%u->%u, 0x%x)\n",
+                service->getName(), OBFUSCATE(service), powerState, maxPowerState,
+                changeFlags);
+            *inOutPowerState = maxPowerState;
+
+            if (darkWakePostTickle &&
+                (actions->parameter & kPMActionsFlagIsDisplayWrangler) &&
+                (changeFlags & kIOPMDomainWillChange) &&
+                ((gDarkWakeFlags & kDarkWakeFlagHIDTickleMask) ==
+                 kDarkWakeFlagHIDTickleEarly))
+            {
+                darkWakePostTickle = false;
+                reportUserInput();
+            }
+        }
+
+        if (!graphicsSuppressed && (changeFlags & kIOPMDomainDidChange))
+        {
+            if (logGraphicsClamp)
+            {
+                AbsoluteTime    now;
+                uint64_t        nsec;
+
+                clock_get_uptime(&now);
+                SUB_ABSOLUTETIME(&now, &systemWakeTime);
+                absolutetime_to_nanoseconds(now, &nsec);
+                if (kIOLogPMRootDomain & gIOKitDebug)
+                    MSG("Graphics suppressed %u ms\n",
+                        ((int)((nsec) / 1000000ULL)));
+            }
+            graphicsSuppressed = true;
+        }
+    }
+}
+
+void IOPMrootDomain::handleActivityTickleForDisplayWrangler(
+    IOService *     service,
+    IOPMActions *   actions )
+{
+#if !NO_KERNEL_HID
+    // Warning: Not running in PM work loop context - don't modify state !!!
+    // Trap tickle directed to IODisplayWrangler while running with graphics
+    // capability suppressed.
+
+    assert(service == wrangler);
+
+    clock_get_uptime(&userActivityTime);
+    bool aborting = ((lastSleepReason == kIOPMSleepReasonIdle)
+                  || (lastSleepReason == kIOPMSleepReasonMaintenance));
+    if (aborting) {
+        userActivityCount++;
+        DLOG("display wrangler tickled1 %d lastSleepReason %d\n",
+            userActivityCount, lastSleepReason);
+    }
+
+    if (!wranglerTickled &&
+        ((_pendingCapability & kIOPMSystemCapabilityGraphics) == 0))
+    {
+        DLOG("display wrangler tickled\n");
+        if (kIOLogPMRootDomain & gIOKitDebug)
+            OSReportWithBacktrace("Dark wake display tickle");
+        if (pmPowerStateQueue)
+        {
+            pmPowerStateQueue->submitPowerEvent(
+                kPowerEventPolicyStimulus,
+                (void *) kStimulusDarkWakeActivityTickle,
+                true /* set wake type */ );
+        }
+    }
+#endif
+}
+
+void IOPMrootDomain::handleUpdatePowerClientForDisplayWrangler(
+    IOService *             service,
+    IOPMActions *           actions,
+    const OSSymbol *        powerClient,
+    IOPMPowerStateIndex     oldPowerState,
+    IOPMPowerStateIndex     newPowerState )
+{
+#if !NO_KERNEL_HID
+    assert(service == wrangler);
+
+    // This function implements half of the user active detection
+    // by monitoring changes to the display wrangler's device desire.
+    //
+    // User becomes active when either:
+    // 1. Wrangler's DeviceDesire increases to max, but wrangler is already
+    //    in max power state. This desire change in absence of a power state
+    //    change is detected within. This handles the case when user becomes
+    //    active while the display is already lit by setDisplayPowerOn().
+    //
+    // 2. Power state change to max, and DeviceDesire is also at max.
+    //    Handled by displayWranglerNotification().
+    //
+    // User becomes inactive when DeviceDesire drops to sleep state or below.
+
+    DLOG("wrangler %s (ps %u, %u->%u)\n",
+        powerClient->getCStringNoCopy(),
+        (uint32_t) service->getPowerState(),
+        (uint32_t) oldPowerState, (uint32_t) newPowerState);
+
+    if (powerClient == gIOPMPowerClientDevice)
+    {
+        if ((newPowerState > oldPowerState) &&
+            (newPowerState == kWranglerPowerStateMax) &&
+            (service->getPowerState() == kWranglerPowerStateMax))
+        {
+            evaluatePolicy( kStimulusEnterUserActiveState );
+        }
+        else
+        if ((newPowerState < oldPowerState) &&
+            (newPowerState <= kWranglerPowerStateSleep))
+        {
+            evaluatePolicy( kStimulusLeaveUserActiveState );
+        }
+    }
+#endif
+}
+
+//******************************************************************************
+// User active state management
+//******************************************************************************
+
+void IOPMrootDomain::preventTransitionToUserActive( bool prevent )
+{
+#if !NO_KERNEL_HID
+    _preventUserActive = prevent;
+    if (wrangler && !_preventUserActive)
+    {
+        // Allowing transition to user active, but the wrangler may have
+        // already powered ON in case of sleep cancel/revert. Poll the
+        // same conditions checked for in displayWranglerNotification()
+        // to bring the user active state up to date.
+
+        if ((wrangler->getPowerState() == kWranglerPowerStateMax) &&
+            (wrangler->getPowerStateForClient(gIOPMPowerClientDevice) ==
+             kWranglerPowerStateMax))
+        {
+            evaluatePolicy( kStimulusEnterUserActiveState );
+        }
+    }
+#endif
+}
+
+//******************************************************************************
+// Approve usage of delayed child notification by PM.
+//******************************************************************************
+
+bool IOPMrootDomain::shouldDelayChildNotification(
+    IOService * service )
+{
+    if (((gDarkWakeFlags & kDarkWakeFlagHIDTickleMask) != 0) &&
+        (kFullWakeReasonNone == fullWakeReason) &&
+        (kSystemTransitionWake == _systemTransitionType))
+    {
+        DLOG("%s: delay child notify\n", service->getName());
+        return true;
+    }
+    return false;
+}
+
+//******************************************************************************
+// PM actions for PCI device.
+//******************************************************************************
+
+void IOPMrootDomain::handlePowerChangeStartForPCIDevice(
+    IOService *             service,
+    IOPMActions *           actions,
+    IOPMPowerStateIndex     powerState,
+    IOPMPowerChangeFlags *  inOutChangeFlags )
+{
+    pmTracer->tracePCIPowerChange(
+        PMTraceWorker::kPowerChangeStart,
+        service, *inOutChangeFlags,
+        (actions->parameter & kPMActionsPCIBitNumberMask));
+}
+
+void IOPMrootDomain::handlePowerChangeDoneForPCIDevice(
+    IOService *             service,
+    IOPMActions *           actions,
+    IOPMPowerStateIndex     powerState,
+    IOPMPowerChangeFlags    changeFlags )
+{
+    pmTracer->tracePCIPowerChange(
+        PMTraceWorker::kPowerChangeCompleted,
+        service, changeFlags,
+        (actions->parameter & kPMActionsPCIBitNumberMask));
+}
+
+//******************************************************************************
+// registerInterest
+//
+// Override IOService::registerInterest() to intercept special clients.
+//******************************************************************************
+
+class IOPMServiceInterestNotifier: public _IOServiceInterestNotifier
+{
+
+    friend class IOPMrootDomain;
+    OSDeclareDefaultStructors(IOPMServiceInterestNotifier)
+
+protected:
+    uint32_t    ackTimeoutCnt;
+
+};
+
+OSDefineMetaClassAndStructors(IOPMServiceInterestNotifier, _IOServiceInterestNotifier)
+
+IONotifier * IOPMrootDomain::registerInterest(
+                const OSSymbol * typeOfInterest,
+                IOServiceInterestHandler handler,
+                void * target, void * ref )
+{
+    IOPMServiceInterestNotifier *notifier = 0;
+    bool            isSystemCapabilityClient;
+    bool            isKernelCapabilityClient;
+    IOReturn        rc = kIOReturnError;;
+
+    isSystemCapabilityClient =
+        typeOfInterest &&
+        typeOfInterest->isEqualTo(kIOPMSystemCapabilityInterest);
+
+    isKernelCapabilityClient =
+        typeOfInterest &&
+        typeOfInterest->isEqualTo(gIOPriorityPowerStateInterest);
+
+    if (isSystemCapabilityClient)
+        typeOfInterest = gIOAppPowerStateInterest;
+
+    notifier = new IOPMServiceInterestNotifier;
+    if (!notifier) return NULL;
+
+    if (notifier->init()) {
+        rc  = super::registerInterestForNotifer(notifier, typeOfInterest, handler, target, ref);
+    }
+    if (rc != kIOReturnSuccess) {
+        notifier->release();
+        notifier = 0;
+    }
+    if (pmPowerStateQueue)
+    {
+        notifier->ackTimeoutCnt = 0;
+        if (isSystemCapabilityClient)
+        {
+            notifier->retain();
+            if (pmPowerStateQueue->submitPowerEvent(
+                kPowerEventRegisterSystemCapabilityClient, notifier) == false)
+                notifier->release();
+        }
+
+        if (isKernelCapabilityClient)
+        {
+            notifier->retain();
+            if (pmPowerStateQueue->submitPowerEvent(
+                kPowerEventRegisterKernelCapabilityClient, notifier) == false)
+                notifier->release();
+        }
+    }
+
+    return notifier;
+}
+
+//******************************************************************************
+// systemMessageFilter
+//
+//******************************************************************************
+
+bool IOPMrootDomain::systemMessageFilter(
+    void * object, void * arg1, void * arg2, void * arg3 )
+{
+    const IOPMInterestContext * context = (const IOPMInterestContext *) arg1;
+    bool  isCapMsg = (context->messageType == kIOMessageSystemCapabilityChange);
+    bool  isCapClient = false;
+    bool  allow = false;
+
+    do {
+        if ((kSystemTransitionNewCapClient == _systemTransitionType) &&
+            (!isCapMsg || !_joinedCapabilityClients ||
+             !_joinedCapabilityClients->containsObject((OSObject *) object)))
+            break;
+
+        // Capability change message for app and kernel clients.
+
+        if (isCapMsg)
+        {
+            if ((context->notifyType == kNotifyPriority) ||
+                (context->notifyType == kNotifyCapabilityChangePriority))
+                isCapClient = true;
+
+            if ((context->notifyType == kNotifyCapabilityChangeApps) &&
+                (object == (void *) systemCapabilityNotifier))
+                isCapClient = true;
+        }
+
+        if (isCapClient)
+        {
+            IOPMSystemCapabilityChangeParameters * capArgs =
+                (IOPMSystemCapabilityChangeParameters *) arg2;
+
+            if (kSystemTransitionNewCapClient == _systemTransitionType)
+            {
+                capArgs->fromCapabilities = 0;
+                capArgs->toCapabilities = _currentCapability;
+                capArgs->changeFlags = 0;
+            }
+            else
+            {
+                capArgs->fromCapabilities = _currentCapability;
+                capArgs->toCapabilities = _pendingCapability;
+
+                if (context->isPreChange)
+                    capArgs->changeFlags = kIOPMSystemCapabilityWillChange;
+                else
+                    capArgs->changeFlags = kIOPMSystemCapabilityDidChange;
+            }
+
+            // Capability change messages only go to the PM configd plugin.
+            // Wait for response post-change if capabilitiy is increasing.
+            // Wait for response pre-change if capability is decreasing.
+
+            if ((context->notifyType == kNotifyCapabilityChangeApps) && arg3 &&
+                ( (capabilityLoss && context->isPreChange) ||
+                  (!capabilityLoss && !context->isPreChange) ) )
+            {
+                // app has not replied yet, wait for it
+                *((OSObject **) arg3) = kOSBooleanFalse;
+            }
+
+            allow = true;
+            break;
+        }
+
+        // Capability client will always see kIOMessageCanSystemSleep,
+        // even for demand sleep. It will also have a chance to veto
+        // sleep one last time after all clients have responded to
+        // kIOMessageSystemWillSleep
+
+        if ((kIOMessageCanSystemSleep == context->messageType) ||
+            (kIOMessageSystemWillNotSleep == context->messageType))
+        {
+            if (object == (OSObject *) systemCapabilityNotifier)
+            {
+                allow = true;
+                break;
+            }
+
+            // Not idle sleep, don't ask apps.
+            if (context->changeFlags & kIOPMSkipAskPowerDown)
+            {
+                break;
+            }
+        }
+
+        if (kIOPMMessageLastCallBeforeSleep == context->messageType)
+        {
+            if ((object == (OSObject *) systemCapabilityNotifier) &&
+                CAP_HIGHEST(kIOPMSystemCapabilityGraphics) &&
+                (fullToDarkReason == kIOPMSleepReasonIdle))
+                allow = true;
+            break;
+        }
+
+        // Reject capability change messages for legacy clients.
+        // Reject legacy system sleep messages for capability client.
+
+        if (isCapMsg || (object == (OSObject *) systemCapabilityNotifier))
+        {
+            break;
+        }
+
+        // Filter system sleep messages.
+
+        if ((context->notifyType == kNotifyApps) &&
+            (_systemMessageClientMask & kSystemMessageClientLegacyApp))
+        {
+            IOPMServiceInterestNotifier *notify;
+            allow = true;
+
+            if ((notify = OSDynamicCast(IOPMServiceInterestNotifier, (OSObject *)object))
+                && arg3) {
+
+                if (notify->ackTimeoutCnt >= 3)
+                    *((OSObject **) arg3) = kOSBooleanFalse;
+                else
+                    *((OSObject **) arg3) = kOSBooleanTrue;
+            }
+        }
+        else if ((context->notifyType == kNotifyPriority) &&
+                 (_systemMessageClientMask & kSystemMessageClientKernel))
+        {
+            allow = true;
+        }
+    }
+    while (false);
+
+    if (allow && isCapMsg && _joinedCapabilityClients)
+    {
+        _joinedCapabilityClients->removeObject((OSObject *) object);
+        if (_joinedCapabilityClients->getCount() == 0)
+        {
+            DLOG("destroyed capability client set %p\n",
+                OBFUSCATE(_joinedCapabilityClients));
+            _joinedCapabilityClients->release();
+            _joinedCapabilityClients = 0;
+        }
+    }
+
+    return allow;
+}
+
+//******************************************************************************
+// setMaintenanceWakeCalendar
+//
+//******************************************************************************
+
+IOReturn IOPMrootDomain::setMaintenanceWakeCalendar(
+    const IOPMCalendarStruct * calendar )
+{
+    OSData * data;
+    IOReturn ret = 0;
+
+    if (!calendar)
+        return kIOReturnBadArgument;
+
+    data = OSData::withBytesNoCopy((void *) calendar, sizeof(*calendar));
+    if (!data)
+        return kIOReturnNoMemory;
+
+    if (kPMCalendarTypeMaintenance == calendar->selector) {
+        ret = setPMSetting(gIOPMSettingMaintenanceWakeCalendarKey, data);
+        if (kIOReturnSuccess == ret)
+            OSBitOrAtomic(kIOPMAlarmBitMaintenanceWake, &_scheduledAlarms);
+    } else
+    if (kPMCalendarTypeSleepService == calendar->selector)
+    {
+        ret = setPMSetting(gIOPMSettingSleepServiceWakeCalendarKey, data);
+        if (kIOReturnSuccess == ret)
+            OSBitOrAtomic(kIOPMAlarmBitSleepServiceWake, &_scheduledAlarms);
+    }
+    DLOG("_scheduledAlarms = 0x%x\n", (uint32_t) _scheduledAlarms);
+
+    data->release();
+    return ret;
+}
+
+// MARK: -
+// MARK: Display Wrangler
+
+//******************************************************************************
+// displayWranglerNotification
+//
+// Handle the notification when the IODisplayWrangler changes power state.
+//******************************************************************************
+
+IOReturn IOPMrootDomain::displayWranglerNotification(
+    void * target, void * refCon,
+    UInt32 messageType, IOService * service,
+    void * messageArgument, vm_size_t argSize )
+{
+#if !NO_KERNEL_HID
+    int                                 displayPowerState;
+    IOPowerStateChangeNotification *    params =
+            (IOPowerStateChangeNotification *) messageArgument;
+
+    if ((messageType != kIOMessageDeviceWillPowerOff) &&
+        (messageType != kIOMessageDeviceHasPoweredOn))
+        return kIOReturnUnsupported;
+
+    ASSERT_GATED();
+    if (!gRootDomain)
+        return kIOReturnUnsupported;
+
+    displayPowerState = params->stateNumber;
+    DLOG("wrangler %s ps %d\n",
+         getIOMessageString(messageType), displayPowerState);
+
+    switch (messageType) {
+       case kIOMessageDeviceWillPowerOff:
+            // Display wrangler has dropped power due to display idle
+            // or force system sleep.
+            //
+            // 4 Display ON             kWranglerPowerStateMax
+            // 3 Display Dim            kWranglerPowerStateDim
+            // 2 Display Sleep          kWranglerPowerStateSleep
+            // 1 Not visible to user
+            // 0 Not visible to user    kWranglerPowerStateMin
+
+            if (displayPowerState <= kWranglerPowerStateSleep)
+                gRootDomain->evaluatePolicy( kStimulusDisplayWranglerSleep );
+            break;
+
+        case kIOMessageDeviceHasPoweredOn:
+            // Display wrangler has powered on due to user activity
+            // or wake from sleep.
+
+            if (kWranglerPowerStateMax == displayPowerState)
+            {
+                gRootDomain->evaluatePolicy( kStimulusDisplayWranglerWake );
+
+                // See comment in handleUpdatePowerClientForDisplayWrangler
+                if (service->getPowerStateForClient(gIOPMPowerClientDevice) ==
+                    kWranglerPowerStateMax)
+                {
+                    gRootDomain->evaluatePolicy( kStimulusEnterUserActiveState );
+                }
+            }
+            break;
+    }
+#endif
+    return kIOReturnUnsupported;
+}
+
+//******************************************************************************
+// displayWranglerMatchPublished
+//
+// Receives a notification when the IODisplayWrangler is published.
+// When it's published we install a power state change handler.
+//******************************************************************************
+
+bool IOPMrootDomain::displayWranglerMatchPublished(
+    void * target,
+    void * refCon,
+    IOService * newService,
+    IONotifier * notifier __unused)
+{
+#if !NO_KERNEL_HID
+    // found the display wrangler, now install a handler
+    if( !newService->registerInterest( gIOGeneralInterest,
+                            &displayWranglerNotification, target, 0) )
+    {
+        return false;
+    }
+#endif
+    return true;
+}
+
+#if defined(__i386__) || defined(__x86_64__)
+
+bool IOPMrootDomain::IONVRAMMatchPublished(
+    void * target,
+    void * refCon,
+    IOService * newService,
+    IONotifier * notifier)
+{
+    unsigned int     len = 0;
+    IOPMrootDomain *rd = (IOPMrootDomain *)target;
+    OSNumber    *statusCode = NULL;
+
+    if (PEReadNVRAMProperty(kIOSleepWakeDebugKey, NULL, &len))
+    {
+        statusCode = OSDynamicCast(OSNumber, rd->getProperty(kIOPMSleepWakeFailureCodeKey));
+        if (statusCode != NULL) {
+            if (statusCode->unsigned64BitValue() != 0) {
+                rd->swd_flags |= SWD_BOOT_BY_SW_WDOG;
+                MSG("System was rebooted due to Sleep/Wake failure\n");
+            }
+            else {
+                rd->swd_flags |= SWD_BOOT_BY_OSX_WDOG;
+                MSG("System was non-responsive and was rebooted by watchdog\n");
+            }
+        }
+
+        rd->swd_logBufMap = rd->sleepWakeDebugRetrieve();
+    }
+    if (notifier) notifier->remove();
+    return true;
+}
+
+#else
+bool IOPMrootDomain::IONVRAMMatchPublished(
+    void * target,
+    void * refCon,
+    IOService * newService,
+    IONotifier * notifier __unused)
+{
+    return false;
+}
+
+#endif
+
+//******************************************************************************
+// reportUserInput
+//
+//******************************************************************************
+
+void IOPMrootDomain::reportUserInput( void )
 {
 {
-#ifdef __ppc__
-    IORegistryEntry *_batteryRegEntry = (IORegistryEntry *) getProperty("BatteryEntry");
-
-    // (if possible) re-publish power source state under IOPMrootDomain;
-    // only do so if the battery controller publishes an IOResource 
-    // defining battery location. Called from ApplePMU battery driver.
+#if !NO_KERNEL_HID
+    OSIterator * iter;
 
 
-    if(_batteryRegEntry)
+    if(!wrangler)
     {
     {
-        OSArray             *batt_info;
-        batt_info = (OSArray *) _batteryRegEntry->getProperty(kIOBatteryInfoKey);
-        if(batt_info)
-            setProperty(kIOBatteryInfoKey, batt_info);
+        iter = getMatchingServices(serviceMatching("IODisplayWrangler"));
+        if(iter)
+        {
+            wrangler = (IOService *) iter->getNextObject();
+            iter->release();
+        }
     }
     }
+
+    if(wrangler)
+        wrangler->activityTickle(0,0);
 #endif
 }
 
 #endif
 }
 
-
 //******************************************************************************
 //******************************************************************************
-// setPMSetting (private)
-//
-// Internal helper to relay PM settings changes from user space to individual
-// drivers. Should be called only by IOPMrootDomain::setProperties.
+// latchDisplayWranglerTickle
 //******************************************************************************
 
 //******************************************************************************
 
-IOReturn IOPMrootDomain::setPMSetting(
-    const OSSymbol *type,
-    OSObject *obj)
+bool IOPMrootDomain::latchDisplayWranglerTickle( bool latch )
 {
 {
-    OSArray             *arr = NULL;
-    PMSettingObject     *p_obj = NULL;
-    int                 count;
-    int                 i;
-
-    if(NULL == type) return kIOReturnBadArgument;
-
-    IORecursiveLockLock(settingsCtrlLock);
-    
-    fPMSettingsDict->setObject(type, obj);
+#if !NO_KERNEL_HID
+    if (latch)
+    {
+        if (!(_currentCapability & kIOPMSystemCapabilityGraphics) &&
+            !(_pendingCapability & kIOPMSystemCapabilityGraphics) &&
+            !checkSystemCanSustainFullWake())
+        {
+            // Currently in dark wake, and not transitioning to full wake.
+            // Full wake is unsustainable, so latch the tickle to prevent
+            // the display from lighting up momentarily.
+            wranglerTickleLatched = true;
+        }
+        else
+        {
+            wranglerTickleLatched = false;
+        }
+    }
+    else if (wranglerTickleLatched && checkSystemCanSustainFullWake())
+    {
+        wranglerTickleLatched = false;
 
 
-    arr = (OSArray *)settingsCallbacks->getObject(type);
-    if(NULL == arr) goto exit;
-    count = arr->getCount();
-    for(i=0; i<count; i++) {
-        p_obj = (PMSettingObject *)OSDynamicCast(PMSettingObject, arr->getObject(i));
-        if(p_obj) p_obj->setPMSetting(type, obj);
+        pmPowerStateQueue->submitPowerEvent(
+            kPowerEventPolicyStimulus,
+            (void *) kStimulusDarkWakeActivityTickle );
     }
 
     }
 
-exit:
-    IORecursiveLockUnlock(settingsCtrlLock);
-    return kIOReturnSuccess;
+    return wranglerTickleLatched;
+#else
+    return false;
+#endif
 }
 
 }
 
-
 //******************************************************************************
 //******************************************************************************
-// copyPMSetting (public)
+// setDisplayPowerOn
 //
 //
-// Allows kexts to safely read setting values, without being subscribed to
-// notifications.
+// For root domain user client
 //******************************************************************************
 
 //******************************************************************************
 
-OSObject * IOPMrootDomain::copyPMSetting(
-    OSSymbol *whichSetting)
+void IOPMrootDomain::setDisplayPowerOn( uint32_t options )
 {
 {
-    OSObject *obj = NULL;
-
-    if(!whichSetting) return NULL;
-
-    IORecursiveLockLock(settingsCtrlLock);
-    obj = fPMSettingsDict->getObject(whichSetting);
-    if(obj) {
-        obj->retain();
+    if (checkSystemCanSustainFullWake())
+    {
+        pmPowerStateQueue->submitPowerEvent( kPowerEventSetDisplayPowerOn,
+                                             (void *) 0, options );
     }
     }
-    IORecursiveLockUnlock(settingsCtrlLock);
-    
-    return obj;
 }
 
 }
 
+// MARK: -
+// MARK: Battery
 
 //******************************************************************************
 
 //******************************************************************************
-// registerPMSettingController (public)
+// batteryPublished
 //
 //
-// direct wrapper to registerPMSettingController with uint32_t power source arg
+// Notification on battery class IOPowerSource appearance
 //******************************************************************************
 
 //******************************************************************************
 
-IOReturn IOPMrootDomain::registerPMSettingController(
-    const OSSymbol *                settings[],
-    IOPMSettingControllerCallback   func,
-    OSObject                        *target,
-    uintptr_t                       refcon,
-    OSObject                        **handle)
+bool IOPMrootDomain::batteryPublished(
+    void * target,
+    void * root_domain,
+    IOService * resourceService,
+    IONotifier * notifier __unused )
 {
 {
-    return registerPMSettingController( 
-            settings,
-            (kIOPMSupportedOnAC | kIOPMSupportedOnBatt | kIOPMSupportedOnUPS),
-            func, target, refcon, handle);
+    // rdar://2936060&4435589
+    // All laptops have dimmable LCD displays
+    // All laptops have batteries
+    // So if this machine has a battery, publish the fact that the backlight
+    // supports dimming.
+    ((IOPMrootDomain *)root_domain)->publishFeature("DisplayDims");
+
+    return (true);
 }
 
 }
 
+// MARK: -
+// MARK: System PM Policy
 
 //******************************************************************************
 
 //******************************************************************************
-// registerPMSettingController (public)
+// checkSystemSleepAllowed
 //
 //
-// Kexts may register for notifications when a particular setting is changed.
-// A list of settings is available in IOPM.h.
-// Arguments:
-//  * settings - An OSArray containing OSSymbols. Caller should populate this
-//          array with a list of settings caller wants notifications from.
-//  * func - A C function callback of the type IOPMSettingControllerCallback
-//  * target - caller may provide an OSObject *, which PM will pass as an 
-//          target to calls to "func"
-//  * refcon - caller may provide an void *, which PM will pass as an 
-//          argument to calls to "func"
-//  * handle - This is a return argument. We will populate this pointer upon
-//          call success. Hold onto this and pass this argument to
-//          IOPMrootDomain::deRegisterPMSettingCallback when unloading your kext
-// Returns:
-//      kIOReturnSuccess on success
 //******************************************************************************
 
 //******************************************************************************
 
-IOReturn IOPMrootDomain::registerPMSettingController(
-    const OSSymbol *                settings[],
-    uint32_t                        supportedPowerSources,
-    IOPMSettingControllerCallback   func,
-    OSObject                        *target,
-    uintptr_t                       refcon,
-    OSObject                        **handle)
+bool IOPMrootDomain::checkSystemSleepAllowed( IOOptionBits options,
+                                              uint32_t     sleepReason )
 {
 {
-    PMSettingObject     *pmso = NULL;
-    OSArray             *list = NULL;
-    IOReturn            ret = kIOReturnSuccess;
-    int                 i;
+    int err = 0;
 
 
-    if( NULL == settings ||
-        NULL == func ||
-        NULL == handle)
-    {
-        return kIOReturnBadArgument;
-    }
+    // Conditions that prevent idle and demand system sleep.
 
 
-    pmso = PMSettingObject::pmSettingObject(
-                (IOPMrootDomain *)this, func, target, 
-                refcon, supportedPowerSources, settings);
+    do {
+        if (userDisabledAllSleep)
+        {
+            err = 1;        // 1. user-space sleep kill switch
+            break;
+        }
 
 
-    if(!pmso) {
-        ret = kIOReturnInternalError;
-        goto bail_no_unlock;
-    }
+        if (systemBooting || systemShutdown || gWillShutdown)
+        {
+            err = 2;        // 2. restart or shutdown in progress
+            break;
+        }
 
 
-    IORecursiveLockLock(settingsCtrlLock);
-    for(i=0; settings[i]; i++) 
-    {
-        list = (OSArray *)settingsCallbacks->getObject(settings[i]);
-        if(!list) {
-            // New array of callbacks for this setting
-            list = OSArray::withCapacity(1);
-            settingsCallbacks->setObject(settings[i], list);
-            list->release();
+        if (options == 0)
+            break;
+
+        // Conditions above pegs the system at full wake.
+        // Conditions below prevent system sleep but does not prevent
+        // dark wake, and must be called from gated context.
+
+#if !CONFIG_SLEEP
+        err = 3;            // 3. config does not support sleep
+        break;
+#endif
+
+        if (lowBatteryCondition || thermalWarningState)
+        {
+            break;          // always sleep on low battery or when in thermal warning state
         }
 
         }
 
-        // Add caller to the callback list
-        list->setObject(pmso);
-    }
+        if (sleepReason == kIOPMSleepReasonDarkWakeThermalEmergency)
+        {
+            break;          // always sleep on dark wake thermal emergencies
+        }
+
+        if (preventSystemSleepList->getCount() != 0)
+        {
+            err = 4;        // 4. child prevent system sleep clamp
+            break;
+        }
 
 
-    IORecursiveLockUnlock(settingsCtrlLock);
-    
-    ret = kIOReturnSuccess;
+        if (getPMAssertionLevel( kIOPMDriverAssertionCPUBit ) ==
+            kIOPMDriverAssertionLevelOn)
+        {
+            err = 5;        // 5. CPU assertion
+            break;
+        }
 
 
-    // Track this instance by its OSData ptr from now on  
-    *handle = pmso;
+        if (pciCantSleepValid)
+        {
+            if (pciCantSleepFlag)
+                err = 6;    // 6. PCI card does not support PM (cached)
+            break;
+        }
+        else if (sleepSupportedPEFunction &&
+                 CAP_HIGHEST(kIOPMSystemCapabilityGraphics))
+        {
+            IOReturn ret;
+            OSBitAndAtomic(~kPCICantSleep, &platformSleepSupport);
+            ret = getPlatform()->callPlatformFunction(
+                                    sleepSupportedPEFunction, false,
+                                    NULL, NULL, NULL, NULL);
+            pciCantSleepValid = true;
+            pciCantSleepFlag  = false;
+            if ((platformSleepSupport & kPCICantSleep) ||
+                ((ret != kIOReturnSuccess) && (ret != kIOReturnUnsupported)))
+            {
+                err = 6;    // 6. PCI card does not support PM
+                pciCantSleepFlag = true;
+                break;
+            }
+        }
+    }
+    while (false);
 
 
-bail_no_unlock:
-    if(kIOReturnSuccess != ret) 
+    if (err)
     {
     {
-        // Error return case
-        if(pmso) pmso->release();
-        if(handle) *handle = NULL;
+        DLOG("System sleep prevented by %d\n", err);
+        return false;
     }
     }
-    return ret;
+    return true;
 }
 
 }
 
-
-//******************************************************************************
-// sleepOnClamshellClosed
-//
-// contains the logic to determine if the system should sleep when the clamshell
-// is closed.
-//******************************************************************************
-
-bool IOPMrootDomain::shouldSleepOnClamshellClosed( void )
+bool IOPMrootDomain::checkSystemSleepEnabled( void )
 {
 {
-    DLOG("clamshell state %d, EX %d, IG %d, IW %d, DT %d, AC %d\n",
-        clamshellIsClosed, clamshellExists, ignoringClamshell,
-        ignoringClamshellOnWake, desktopMode, acAdaptorConnected);
-
-    return ( !ignoringClamshell 
-          && !ignoringClamshellOnWake 
-          && !(desktopMode && acAdaptorConnected) );
+    return checkSystemSleepAllowed(0, 0);
 }
 
 }
 
-void IOPMrootDomain::sendClientClamshellNotification( void )
+bool IOPMrootDomain::checkSystemCanSleep( uint32_t sleepReason )
 {
 {
-    /* Only broadcast clamshell alert if clamshell exists. */
-    if (!clamshellExists)
-        return;
+    ASSERT_GATED();
+    return checkSystemSleepAllowed(1, sleepReason);
+}
 
 
-    setProperty(kAppleClamshellStateKey, 
-        clamshellIsClosed ? kOSBooleanTrue : kOSBooleanFalse);
+//******************************************************************************
+// checkSystemCanSustainFullWake
+//******************************************************************************
 
 
-    setProperty(kAppleClamshellCausesSleepKey, 
-        shouldSleepOnClamshellClosed() ? kOSBooleanTrue : kOSBooleanFalse);
+bool IOPMrootDomain::checkSystemCanSustainFullWake( void )
+{
+#if !NO_KERNEL_HID
+    if (lowBatteryCondition || thermalWarningState)
+    {
+        // Low battery wake, or received a low battery notification
+        // while system is awake. This condition will persist until
+        // the following wake.
+        return false;
+    }
 
 
-    /* Argument to message is a bitfiel of 
-     *      ( kClamshellStateBit | kClamshellSleepBit )
-     */
-    messageClients(kIOPMMessageClamshellStateChange,
-        (void *) ( (clamshellIsClosed ? kClamshellStateBit : 0)
-             | ( shouldSleepOnClamshellClosed() ? kClamshellSleepBit : 0)) );
-}
+    if (clamshellExists && clamshellClosed && !clamshellSleepDisabled)
+    {
+        // Graphics state is unknown and external display might not be probed.
+        // Do not incorporate state that requires graphics to be in max power
+        // such as desktopMode or clamshellDisabled.
 
 
+        if (!acAdaptorConnected)
+        {
+            DLOG("full wake check: no AC\n");
+            return false;
+        }
+    }
+#endif
+    return true;
+}
 
 //******************************************************************************
 
 //******************************************************************************
-// informCPUStateChange
-//
-// Call into PM CPU code so that CPU power savings may dynamically adjust for
-// running on battery, with the lid closed, etc.
+// adjustPowerState
 //
 //
-// informCPUStateChange is a no-op on non x86 systems
-// only x86 has explicit support in the IntelCPUPowerManagement kext
+// Conditions that affect our wake/sleep decision has changed.
+// If conditions dictate that the system must remain awake, clamp power
+// state to max with changePowerStateToPriv(ON). Otherwise if sleepASAP
+// is TRUE, then remove the power clamp and allow the power state to drop
+// to SLEEP_STATE.
 //******************************************************************************
 
 //******************************************************************************
 
-void IOPMrootDomain::informCPUStateChange(
-    uint32_t type, 
-    uint32_t value )
+void IOPMrootDomain::adjustPowerState( bool sleepASAP )
 {
 {
-#if defined(__i386__) || defined(__x86_64__)
+    DLOG("adjustPowerState ps %u, asap %d, slider %ld\n",
+        (uint32_t) getPowerState(), sleepASAP, sleepSlider);
 
 
-    pmioctlVariableInfo_t varInfoStruct;                            
-    int                 pmCPUret = 0;
-    const char          *varNameStr = NULL;
-    int32_t             *varIndex   = NULL;
+    ASSERT_GATED();
 
 
-    if (kInformAC == type) {
-        varNameStr = kIOPMRootDomainBatPowerCString;
-        varIndex = &idxPMCPULimitedPower;
-    } else if (kInformLid == type) {
-        varNameStr = kIOPMRootDomainLidCloseCString;
-        varIndex = &idxPMCPUClamshell;
-    } else {
-        return;
+    if ((sleepSlider == 0) || !checkSystemSleepEnabled())
+    {
+        changePowerStateToPriv(ON_STATE);
     }
     }
-    
-    // Set the new value!
-    // pmCPUControl will assign us a new ID if one doesn't exist yet
-    bzero(&varInfoStruct, sizeof(pmioctlVariableInfo_t));
-    varInfoStruct.varID         = *varIndex;
-    varInfoStruct.varType       = vBool;
-    varInfoStruct.varInitValue  = value;
-    varInfoStruct.varCurValue   = value;
-    strncpy( (char *)varInfoStruct.varName,
-             (const char *)varNameStr,
-             strlen(varNameStr) + 1 );                 
-    
-    // Set!
-    pmCPUret = pmCPUControl( PMIOCSETVARINFO, (void *)&varInfoStruct );
-
-    // pmCPU only assigns numerical id's when a new varName is specified
-    if ((0 == pmCPUret)
-        && (*varIndex == kCPUUnknownIndex))
+    else if ( sleepASAP )
     {
     {
-        // pmCPUControl has assigned us a new variable ID. 
-        // Let's re-read the structure we just SET to learn that ID.
-        pmCPUret = pmCPUControl( PMIOCGETVARNAMEINFO, (void *)&varInfoStruct );
-
-        if (0 == pmCPUret) 
-        {        
-            // Store it in idxPMCPUClamshell or idxPMCPULimitedPower
-            *varIndex = varInfoStruct.varID;
-        }
-    } 
-    
-    return;
-    
-#endif /* __i386__ || __x86_64__ */
+        changePowerStateToPriv(SLEEP_STATE);
+    }
 }
 
 }
 
-
 //******************************************************************************
 // dispatchPowerEvent
 //
 //******************************************************************************
 // dispatchPowerEvent
 //
@@ -2538,9 +5773,9 @@ void IOPMrootDomain::informCPUStateChange(
 //******************************************************************************
 
 void IOPMrootDomain::dispatchPowerEvent(
 //******************************************************************************
 
 void IOPMrootDomain::dispatchPowerEvent(
-    uint32_t event, void * arg0, void * arg1 )
+    uint32_t event, void * arg0, uint64_t arg1 )
 {
 {
-    DLOG("power event %x args %p %p\n", event, arg0, arg1);
+    DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1);
     ASSERT_GATED();
 
     switch (event)
     ASSERT_GATED();
 
     switch (event)
@@ -2552,22 +5787,46 @@ void IOPMrootDomain::dispatchPowerEvent(
         case kPowerEventReceivedPowerNotification:
             handlePowerNotification( (UInt32)(uintptr_t) arg0 );
             break;
         case kPowerEventReceivedPowerNotification:
             handlePowerNotification( (UInt32)(uintptr_t) arg0 );
             break;
-        
+
         case kPowerEventSystemBootCompleted:
             if (systemBooting)
             {
                 systemBooting = false;
         case kPowerEventSystemBootCompleted:
             if (systemBooting)
             {
                 systemBooting = false;
-                adjustPowerState();
 
 
+                if (lowBatteryCondition)
+                {
+                    privateSleepSystem (kIOPMSleepReasonLowPower);
+
+                    // The rest is unnecessary since the system is expected
+                    // to sleep immediately. The following wake will update
+                    // everything.
+                    break;
+                }
+
+                if (swd_flags & SWD_VALID_LOGS) {
+                    if (swd_flags & SWD_LOGS_IN_MEM) {
+                        sleepWakeDebugDumpFromMem(swd_logBufMap);
+                        swd_logBufMap->release();
+                        swd_logBufMap = 0;
+                    }
+                    else if (swd_flags & SWD_LOGS_IN_FILE) 
+                        sleepWakeDebugDumpFromFile();
+                }
+                else if (swd_flags & (SWD_BOOT_BY_SW_WDOG|SWD_BOOT_BY_OSX_WDOG)) {
+                    // If logs are invalid, write the failure code
+                    sleepWakeDebugDumpFromMem(NULL);
+                }
                 // If lid is closed, re-send lid closed notification
                 // now that booting is complete.
                 // If lid is closed, re-send lid closed notification
                 // now that booting is complete.
-                if( clamshellIsClosed )
+                if ( clamshellClosed )
                 {
                     handlePowerNotification(kLocalEvalClamshellCommand);
                 }
                 {
                     handlePowerNotification(kLocalEvalClamshellCommand);
                 }
+                evaluatePolicy( kStimulusAllowSystemSleepChanged );
+
             }
             break;
             }
             break;
-        
+
         case kPowerEventSystemShutdown:
             if (kOSBooleanTrue == (OSBoolean *) arg0)
             {
         case kPowerEventSystemShutdown:
             if (kOSBooleanTrue == (OSBoolean *) arg0)
             {
@@ -2575,53 +5834,124 @@ void IOPMrootDomain::dispatchPowerEvent(
                    to prevent sleep at unexpected times while loginwindow is trying
                    to shutdown apps and while the OS is trying to transition to
                    complete power of.
                    to prevent sleep at unexpected times while loginwindow is trying
                    to shutdown apps and while the OS is trying to transition to
                    complete power of.
-                   
+
                    Set to true during shutdown, as soon as loginwindow shows
                    the "shutdown countdown dialog", through individual app
                    termination, and through black screen kernel shutdown.
                  */
                    Set to true during shutdown, as soon as loginwindow shows
                    the "shutdown countdown dialog", through individual app
                    termination, and through black screen kernel shutdown.
                  */
-                LOG("systemShutdown true\n");
                 systemShutdown = true;
             } else {
                 /*
                  A shutdown was initiated, but then the shutdown
                  was cancelled, clearing systemShutdown to false here.
                 */
                 systemShutdown = true;
             } else {
                 /*
                  A shutdown was initiated, but then the shutdown
                  was cancelled, clearing systemShutdown to false here.
                 */
-                LOG("systemShutdown false\n");
-                systemShutdown = false;            
+                systemShutdown = false;
+            }
+            break;
+
+        case kPowerEventUserDisabledSleep:
+            userDisabledAllSleep = (kOSBooleanTrue == (OSBoolean *) arg0);
+            break;
+
+        case kPowerEventRegisterSystemCapabilityClient:
+            if (systemCapabilityNotifier)
+            {
+                systemCapabilityNotifier->release();
+                systemCapabilityNotifier = 0;
+            }
+            if (arg0)
+            {
+                systemCapabilityNotifier = (IONotifier *) arg0;
+                systemCapabilityNotifier->retain();
+            }
+            /* intentional fall-through */
+
+        case kPowerEventRegisterKernelCapabilityClient:
+            if (!_joinedCapabilityClients)
+                _joinedCapabilityClients = OSSet::withCapacity(8);
+            if (arg0)
+            {
+                IONotifier * notify = (IONotifier *) arg0;
+                if (_joinedCapabilityClients)
+                {
+                    _joinedCapabilityClients->setObject(notify);
+                    synchronizePowerTree( kIOPMSyncNoChildNotify );
+                }
+                notify->release();
+            }
+            break;
+
+        case kPowerEventPolicyStimulus:
+            if (arg0)
+            {
+                int stimulus = (uintptr_t) arg0;
+                evaluatePolicy( stimulus, (uint32_t) arg1 );
+            }
+            break;
+
+        case kPowerEventAssertionCreate:
+            if (pmAssertions) {
+                pmAssertions->handleCreateAssertion((OSData *)arg0);
             }
             break;
 
             }
             break;
 
-        case kPowerEventUserDisabledSleep:
-            userDisabledAllSleep = (kOSBooleanTrue == (OSBoolean *) arg0);
+
+        case kPowerEventAssertionRelease:
+            if (pmAssertions) {
+                pmAssertions->handleReleaseAssertion(arg1);
+            }
             break;
 
             break;
 
-#if ROOT_DOMAIN_RUN_STATES
-        case kPowerEventConfigdRegisteredInterest:
-            if (gConfigdNotifier)
-            {
-                gConfigdNotifier->release();
-                gConfigdNotifier = 0;
+        case kPowerEventAssertionSetLevel:
+            if (pmAssertions) {
+                pmAssertions->handleSetAssertionLevel(arg1, (IOPMDriverAssertionLevel)(uintptr_t)arg0);
             }
             }
-            if (arg0)
+            break;
+
+        case kPowerEventQueueSleepWakeUUID:
+            handleQueueSleepWakeUUID((OSObject *)arg0);
+            break;
+        case kPowerEventPublishSleepWakeUUID:
+            handlePublishSleepWakeUUID((bool)arg0);
+            break;
+
+        case kPowerEventSetDisplayPowerOn:
+            if (!wrangler) break;
+            if (arg1 != 0)
             {
             {
-                gConfigdNotifier = (IONotifier *) arg0;
+                // Force wrangler to max power state. If system is in dark wake
+                // this alone won't raise the wrangler's power state.
+
+                wrangler->changePowerStateForRootDomain(kWranglerPowerStateMax);
+
+                // System in dark wake, always requesting full wake should
+                // not have any bad side-effects, even if the request fails.
+
+                if (!CAP_CURRENT(kIOPMSystemCapabilityGraphics))
+                {
+                    setProperty(kIOPMRootDomainWakeTypeKey, kIOPMRootDomainWakeTypeNotification);
+                    requestFullWake( kFullWakeReasonDisplayOn );
+                }
             }
             }
-            break;
-#endif
+            else
+            {
+                // Relenquish desire to power up display.
+                // Must first transition to state 1 since wrangler doesn't
+                // power off the displays at state 0. At state 0 the root
+                // domain is removed from the wrangler's power client list.
 
 
-        case kPowerEventAggressivenessChanged:
-            aggressivenessChanged();
+                wrangler->changePowerStateForRootDomain(kWranglerPowerStateMin + 1);
+                wrangler->changePowerStateForRootDomain(kWranglerPowerStateMin);
+            }
             break;
     }
 }
 
             break;
     }
 }
 
-
 //******************************************************************************
 // systemPowerEventOccurred
 //
 // The power controller is notifying us of a hardware-related power management
 //******************************************************************************
 // systemPowerEventOccurred
 //
 // The power controller is notifying us of a hardware-related power management
-// event that we must handle. 
+// event that we must handle.
 //
 // systemPowerEventOccurred covers the same functionality that
 // receivePowerNotification does; it simply provides a richer API for conveying
 //
 // systemPowerEventOccurred covers the same functionality that
 // receivePowerNotification does; it simply provides a richer API for conveying
@@ -2635,9 +5965,9 @@ IOReturn IOPMrootDomain::systemPowerEventOccurred(
     IOReturn        attempt = kIOReturnSuccess;
     OSNumber        *newNumber = NULL;
 
     IOReturn        attempt = kIOReturnSuccess;
     OSNumber        *newNumber = NULL;
 
-    if (!event) 
+    if (!event)
         return kIOReturnBadArgument;
         return kIOReturnBadArgument;
-        
+
     newNumber = OSNumber::withNumber(intValue, 8*sizeof(intValue));
     if (!newNumber)
         return kIOReturnInternalError;
     newNumber = OSNumber::withNumber(intValue, 8*sizeof(intValue));
     if (!newNumber)
         return kIOReturnInternalError;
@@ -2649,14 +5979,32 @@ IOReturn IOPMrootDomain::systemPowerEventOccurred(
     return attempt;
 }
 
     return attempt;
 }
 
+void IOPMrootDomain::setThermalState(OSObject *value)
+{
+   OSNumber * num;
+
+   if (gIOPMWorkLoop->inGate() == false) {
+       gIOPMWorkLoop->runAction(
+               OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::setThermalState),
+               (OSObject *)this,
+               (void *)value);
+
+       return;
+    }
+    if (value && (num = OSDynamicCast(OSNumber, value))) {
+        thermalWarningState = ((num->unsigned32BitValue() == kIOPMThermalLevelWarning) || 
+                               (num->unsigned32BitValue() == kIOPMThermalLevelTrap)) ? 1 : 0; 
+    }
+}
+
 IOReturn IOPMrootDomain::systemPowerEventOccurred(
     const OSSymbol *event,
     OSObject *value)
 {
     OSDictionary *thermalsDict = NULL;
     bool shouldUpdate = true;
 IOReturn IOPMrootDomain::systemPowerEventOccurred(
     const OSSymbol *event,
     OSObject *value)
 {
     OSDictionary *thermalsDict = NULL;
     bool shouldUpdate = true;
-    
-    if (!event || !value) 
+
+    if (!event || !value)
         return kIOReturnBadArgument;
 
     // LOCK
         return kIOReturnBadArgument;
 
     // LOCK
@@ -2666,9 +6014,9 @@ IOReturn IOPMrootDomain::systemPowerEventOccurred(
     if (featuresDictLock) IOLockLock(featuresDictLock);
 
     thermalsDict = (OSDictionary *)getProperty(kIOPMRootDomainPowerStatusKey);
     if (featuresDictLock) IOLockLock(featuresDictLock);
 
     thermalsDict = (OSDictionary *)getProperty(kIOPMRootDomainPowerStatusKey);
-                   
+
     if (thermalsDict && OSDynamicCast(OSDictionary, thermalsDict)) {
     if (thermalsDict && OSDynamicCast(OSDictionary, thermalsDict)) {
-        thermalsDict = OSDictionary::withDictionary(thermalsDict);                        
+        thermalsDict = OSDictionary::withDictionary(thermalsDict);
     } else {
         thermalsDict = OSDictionary::withCapacity(1);
     }
     } else {
         thermalsDict = OSDictionary::withCapacity(1);
     }
@@ -2688,13 +6036,17 @@ exit:
     // UNLOCK
     if (featuresDictLock) IOLockUnlock(featuresDictLock);
 
     // UNLOCK
     if (featuresDictLock) IOLockUnlock(featuresDictLock);
 
-    if (shouldUpdate)
+    if (shouldUpdate) {
+        if (event && 
+             event->isEqualTo(kIOPMThermalLevelWarningKey)) {
+             setThermalState(value);
+        }
         messageClients (kIOPMMessageSystemPowerEventOccurred, (void *)NULL);
         messageClients (kIOPMMessageSystemPowerEventOccurred, (void *)NULL);
+    }
 
     return kIOReturnSuccess;
 }
 
 
     return kIOReturnSuccess;
 }
 
-
 //******************************************************************************
 // receivePowerNotification
 //
 //******************************************************************************
 // receivePowerNotification
 //
@@ -2706,7 +6058,7 @@ exit:
 IOReturn IOPMrootDomain::receivePowerNotification( UInt32 msg )
 {
     pmPowerStateQueue->submitPowerEvent(
 IOReturn IOPMrootDomain::receivePowerNotification( UInt32 msg )
 {
     pmPowerStateQueue->submitPowerEvent(
-        kPowerEventReceivedPowerNotification, (void *) msg );
+        kPowerEventReceivedPowerNotification, (void *)(uintptr_t) msg );
     return kIOReturnSuccess;
 }
 
     return kIOReturnSuccess;
 }
 
@@ -2727,67 +6079,80 @@ void IOPMrootDomain::handlePowerNotification( UInt32 msg )
     /*
      * Overtemp
      */
     /*
      * Overtemp
      */
-    if (msg & kIOPMOverTemp) 
+    if (msg & kIOPMOverTemp)
     {
     {
-        LOG("PowerManagement emergency overtemp signal. Going to sleep!");
-        privateSleepSystem (kIOPMThermalEmergencySleepKey);
+        MSG("PowerManagement emergency overtemp signal. Going to sleep!");
+        privateSleepSystem (kIOPMSleepReasonThermalEmergency);
     }
 
     }
 
-#ifdef __ppc__
     /*
     /*
-     * PMU Processor Speed Change
+     * Sleep if system is in dark wake
      */
      */
-    if (msg & kIOPMProcessorSpeedChange) 
+    if (msg & kIOPMDWOverTemp)
     {
     {
-        IOService *pmu = waitForService(serviceMatching("ApplePMU"));
-        pmu->callPlatformFunction("prepareForSleep", false, 0, 0, 0, 0);
-        getPlatform()->sleepKernel();
-        pmu->callPlatformFunction("recoverFromSleep", false, 0, 0, 0, 0);
+        DLOG("DarkWake thermal limits message received!\n");
+
+        // Inform cap client that we're going to sleep
+        messageClients(kIOPMMessageDarkWakeThermalEmergency);
+
     }
     }
-#endif
 
     /*
      * Sleep Now!
      */
 
     /*
      * Sleep Now!
      */
-    if (msg & kIOPMSleepNow) 
+    if (msg & kIOPMSleepNow)
     {
     {
-        privateSleepSystem (kIOPMSoftwareSleepKey);
+        privateSleepSystem (kIOPMSleepReasonSoftware);
     }
     }
-    
+
     /*
      * Power Emergency
      */
     /*
      * Power Emergency
      */
-    if (msg & kIOPMPowerEmergency) 
+    if (msg & kIOPMPowerEmergency)
     {
     {
-        privateSleepSystem (kIOPMLowPowerSleepKey);
+        lowBatteryCondition = true;
+        privateSleepSystem (kIOPMSleepReasonLowPower);
     }
 
     /*
      * Clamshell OPEN
      */
     }
 
     /*
      * Clamshell OPEN
      */
-    if (msg & kIOPMClamshellOpened) 
+    if (msg & kIOPMClamshellOpened)
     {
         // Received clamshel open message from clamshell controlling driver
         // Update our internal state and tell general interest clients
     {
         // Received clamshel open message from clamshell controlling driver
         // Update our internal state and tell general interest clients
-        clamshellIsClosed = false;
+        clamshellClosed = false;
         clamshellExists = true;
         clamshellExists = true;
-                
+
+        // Don't issue a hid tickle when lid is open and polled on wake
+        if (msg & kIOPMSetValue)
+        {
+            setProperty(kIOPMRootDomainWakeTypeKey, "Lid Open");
+            reportUserInput();
+        }
+
         // Tell PMCPU
         informCPUStateChange(kInformLid, 0);
 
         // Tell PMCPU
         informCPUStateChange(kInformLid, 0);
 
-        // Tell general interest clients        
+        // Tell general interest clients
         sendClientClamshellNotification();
         sendClientClamshellNotification();
-    } 
 
 
-    /* 
+        bool aborting =  ((lastSleepReason == kIOPMSleepReasonClamshell)
+                       || (lastSleepReason == kIOPMSleepReasonIdle)
+                       || (lastSleepReason == kIOPMSleepReasonMaintenance));
+        if (aborting) userActivityCount++;
+        DLOG("clamshell tickled %d lastSleepReason %d\n", userActivityCount, lastSleepReason);
+    }
+
+    /*
      * Clamshell CLOSED
      * Clamshell CLOSED
-     * Send the clamshell interest notification since the lid is closing. 
+     * Send the clamshell interest notification since the lid is closing.
      */
     if (msg & kIOPMClamshellClosed)
     {
         // Received clamshel open message from clamshell controlling driver
         // Update our internal state and tell general interest clients
      */
     if (msg & kIOPMClamshellClosed)
     {
         // Received clamshel open message from clamshell controlling driver
         // Update our internal state and tell general interest clients
-        clamshellIsClosed = true;
+        clamshellClosed = true;
         clamshellExists = true;
 
         // Tell PMCPU
         clamshellExists = true;
 
         // Tell PMCPU
@@ -2795,8 +6160,8 @@ void IOPMrootDomain::handlePowerNotification( UInt32 msg )
 
         // Tell general interest clients
         sendClientClamshellNotification();
 
         // Tell general interest clients
         sendClientClamshellNotification();
-        
-        // And set eval_clamshell = so we can attempt 
+
+        // And set eval_clamshell = so we can attempt
         eval_clamshell = true;
     }
 
         eval_clamshell = true;
     }
 
@@ -2805,7 +6170,7 @@ void IOPMrootDomain::handlePowerNotification( UInt32 msg )
      *
      *  -> reevaluate lid state
      */
      *
      *  -> reevaluate lid state
      */
-    if (msg & kIOPMSetDesktopMode) 
+    if (msg & kIOPMSetDesktopMode)
     {
         desktopMode = (0 != (msg & kIOPMSetValue));
         msg &= ~(kIOPMSetDesktopMode | kIOPMSetValue);
     {
         desktopMode = (0 != (msg & kIOPMSetValue));
         msg &= ~(kIOPMSetDesktopMode | kIOPMSetValue);
@@ -2813,18 +6178,15 @@ void IOPMrootDomain::handlePowerNotification( UInt32 msg )
         sendClientClamshellNotification();
 
         // Re-evaluate the lid state
         sendClientClamshellNotification();
 
         // Re-evaluate the lid state
-        if( clamshellIsClosed )
-        {
-            eval_clamshell = true;
-        }
+        eval_clamshell = true;
     }
     }
-    
+
     /*
      * AC Adaptor connected
      *
      *  -> reevaluate lid state
      */
     /*
      * AC Adaptor connected
      *
      *  -> reevaluate lid state
      */
-    if (msg & kIOPMSetACAdaptorConnected) 
+    if (msg & kIOPMSetACAdaptorConnected)
     {
         acAdaptorConnected = (0 != (msg & kIOPMSetValue));
         msg &= ~(kIOPMSetACAdaptorConnected | kIOPMSetValue);
     {
         acAdaptorConnected = (0 != (msg & kIOPMSetValue));
         msg &= ~(kIOPMSetACAdaptorConnected | kIOPMSetValue);
@@ -2839,2290 +6201,3191 @@ void IOPMrootDomain::handlePowerNotification( UInt32 msg )
         sendClientClamshellNotification();
 
         // Re-evaluate the lid state
         sendClientClamshellNotification();
 
         // Re-evaluate the lid state
-        if( clamshellIsClosed )
-        {
-            eval_clamshell = true;
+        eval_clamshell = true;
+
+        // Lack of AC may have latched a display wrangler tickle.
+        // This mirrors the hardware's USB wake event latch, where a latched
+        // USB wake event followed by an AC attach will trigger a full wake.
+        latchDisplayWranglerTickle( false );
+
+#if HIBERNATION
+        // AC presence will reset the standy timer delay adjustment.
+        _standbyTimerResetSeconds = 0;
+#endif
+        if (!userIsActive) {
+            // Reset userActivityTime when power supply is changed(rdr 13789330)
+            clock_get_uptime(&userActivityTime);
         }
     }
         }
     }
-    
+
     /*
      * Enable Clamshell (external display disappear)
      *
      *  -> reevaluate lid state
      */
     /*
      * Enable Clamshell (external display disappear)
      *
      *  -> reevaluate lid state
      */
-    if (msg & kIOPMEnableClamshell) 
+    if (msg & kIOPMEnableClamshell)
     {
         // Re-evaluate the lid state
         // System should sleep on external display disappearance
         // in lid closed operation.
     {
         // Re-evaluate the lid state
         // System should sleep on external display disappearance
         // in lid closed operation.
-        if( clamshellIsClosed && (true == ignoringClamshell) )        
+        if (true == clamshellDisabled)
         {
             eval_clamshell = true;
         }
 
         {
             eval_clamshell = true;
         }
 
-        ignoringClamshell = false;
-
+        clamshellDisabled = false;
         sendClientClamshellNotification();
     }
         sendClientClamshellNotification();
     }
-    
+
     /*
      * Disable Clamshell (external display appeared)
      * We don't bother re-evaluating clamshell state. If the system is awake,
     /*
      * Disable Clamshell (external display appeared)
      * We don't bother re-evaluating clamshell state. If the system is awake,
-     * the lid is probably open. 
+     * the lid is probably open.
      */
      */
-    if (msg & kIOPMDisableClamshell) 
+    if (msg & kIOPMDisableClamshell)
     {
     {
-        ignoringClamshell = true;
-
+        clamshellDisabled = true;
         sendClientClamshellNotification();
     }
 
     /*
      * Evaluate clamshell and SLEEP if appropiate
      */
         sendClientClamshellNotification();
     }
 
     /*
      * Evaluate clamshell and SLEEP if appropiate
      */
-    if ( eval_clamshell && shouldSleepOnClamshellClosed() ) 
+    if (eval_clamshell && clamshellClosed)
     {
     {
-
-
-        // SLEEP!
-        privateSleepSystem (kIOPMClamshellSleepKey);
+        if (shouldSleepOnClamshellClosed())
+            privateSleepSystem (kIOPMSleepReasonClamshell);
+        else
+            evaluatePolicy( kStimulusDarkWakeEvaluate );
     }
 
     /*
      * Power Button
      */
     }
 
     /*
      * Power Button
      */
-    if (msg & kIOPMPowerButton) 
+    if (msg & kIOPMPowerButton)
     {
     {
-        // toggle state of sleep/wake
-        // are we dozing?
-        if ( getPowerState() == DOZE_STATE ) 
+        if (!wranglerAsleep)
         {
         {
-#ifndef __LP64__
-            // yes, tell the tree we're waking
-            systemWake();
-#endif
-            // wake the Display Wrangler
-            reportUserInput();
-        }
-        else {
             OSString *pbs = OSString::withCString("DisablePowerButtonSleep");
             // Check that power button sleep is enabled
             if( pbs ) {
                 if( kOSBooleanTrue != getProperty(pbs))
             OSString *pbs = OSString::withCString("DisablePowerButtonSleep");
             // Check that power button sleep is enabled
             if( pbs ) {
                 if( kOSBooleanTrue != getProperty(pbs))
-                privateSleepSystem (kIOPMPowerButtonSleepKey);
+                    privateSleepSystem (kIOPMSleepReasonPowerButton);
             }
         }
             }
         }
-    }
-
-    /*
-     * Allow Sleep
-     *
-     */
-    if ( (msg & kIOPMAllowSleep) && !allowSleep ) 
-    {
-        allowSleep = true;
-        adjustPowerState();
-    }
-
-    /*
-     * Prevent Sleep
-     *
-     */
-    if (msg & kIOPMPreventSleep) {
-        allowSleep = false;
-           // are we dozing?
-        if ( getPowerState() == DOZE_STATE ) {
-#ifndef __LP64__
-            // yes, tell the tree we're waking
-            systemWake();
-#endif
-            adjustPowerState();
-            // wake the Display Wrangler
+        else
             reportUserInput();
             reportUserInput();
-        } else {
-            adjustPowerState();
-            // make sure we have power to clamp
-            patriarch->wakeSystem();
-        }
     }
 }
 
     }
 }
 
-
 //******************************************************************************
 //******************************************************************************
-// getSleepSupported
+// evaluatePolicy
 //
 //
+// Evaluate root-domain policy in response to external changes.
 //******************************************************************************
 
 //******************************************************************************
 
-IOOptionBits IOPMrootDomain::getSleepSupported( void )
+void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg )
 {
 {
-    return( platformSleepSupport );
-}
+    union {
+        struct {
+            int idleSleepEnabled    : 1;
+            int idleSleepDisabled   : 1;
+            int displaySleep        : 1;
+            int sleepDelayChanged   : 1;
+            int evaluateDarkWake    : 1;
+            int adjustPowerState    : 1;
+            int userBecameInactive  : 1;
+        } bit;
+        uint32_t u32;
+    } flags;
+
+    DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg);
 
 
+    ASSERT_GATED();
+    flags.u32 = 0;
 
 
-//******************************************************************************
-// setSleepSupported
-//
-//******************************************************************************
+    switch (stimulus)
+    {
+        case kStimulusDisplayWranglerSleep:
+            if (!wranglerAsleep)
+            {
+                // first transition to wrangler sleep or lower
+                wranglerAsleep = true;
+                flags.bit.displaySleep = true;
+            }
+            break;
 
 
-void IOPMrootDomain::setSleepSupported( IOOptionBits flags )
-{
-    DLOG("setSleepSupported(%x)\n", (uint32_t) flags);
-    OSBitOrAtomic(flags, &platformSleepSupport);
-}
+        case kStimulusDisplayWranglerWake:
+            displayIdleForDemandSleep = false;
+            wranglerAsleep = false;
+            break;
 
 
+        case kStimulusEnterUserActiveState:
+            if (_preventUserActive)
+            {
+                DLOG("user active dropped\n");
+                break;
+            }
+            if (!userIsActive)
+            {
+                userIsActive = true;
+                userWasActive = true;
 
 
-//******************************************************************************
-// requestPowerDomainState
-//
-// The root domain intercepts this call to the superclass.
-// Called on the PM work loop thread.
-//
-// If the clamp bit is not set in the desire, then the child doesn't need the power
-// state it's requesting; it just wants it. The root ignores desires but not needs.
-// If the clamp bit is not set, the root takes it that the child can tolerate no
-// power and interprets the request accordingly. If all children can thus tolerate
-// no power, we are on our way to idle sleep.
-//******************************************************************************
+                // Stay awake after dropping demand for display power on
+                if (kFullWakeReasonDisplayOn == fullWakeReason)
+                    fullWakeReason = fFullWakeReasonDisplayOnAndLocalUser;
 
 
-IOReturn IOPMrootDomain::requestPowerDomainState (
-    IOPMPowerFlags      desiredFlags,
-    IOPowerConnection * whichChild,
-    unsigned long       specification )
-{
-    OSIterator          *iter;
-    OSObject            *next;
-    IOPowerConnection   *connection;
-    IOPMPowerFlags      powerRequestFlag = 0;
-    IOPMPowerFlags      editedDesire;
+                setProperty(gIOPMUserIsActiveKey, kOSBooleanTrue);
+                messageClients(kIOPMMessageUserIsActiveChanged);
+            }
+            flags.bit.idleSleepDisabled = true;
+            break;
 
 
-    ASSERT_GATED();
+        case kStimulusLeaveUserActiveState:
+            if (userIsActive)
+            {
+                userIsActive = false;
+                clock_get_uptime(&userBecameInactiveTime);
+                flags.bit.userBecameInactive = true;
 
 
-    if (kIOLogPMRootDomain & gIOKitDebug)
-    {
-        IOService * powerChild =
-            (IOService *) whichChild->getChildEntry(gIOPowerPlane);
-        DLOG("child %p, flags %lx, spec %lx - %s\n",
-            powerChild, desiredFlags, specification,
-            powerChild ? powerChild->getName() : "?");
-    }
+                setProperty(gIOPMUserIsActiveKey, kOSBooleanFalse);
+                messageClients(kIOPMMessageUserIsActiveChanged);
+            }
+            break;
 
 
-    // Force the child's input power requirements to 0 unless the prevent
-    // idle-sleep flag is set. No input power flags map to our state 0.
-    // Our power clamp (deviceDesire) keeps the minimum power state at 2.
+        case kStimulusAggressivenessChanged:
+        {
+            unsigned long   minutesToIdleSleep  = 0;
+            unsigned long   minutesToDisplayDim = 0;
+            unsigned long   minutesDelta        = 0;
 
 
-    if (desiredFlags & kIOPMPreventIdleSleep)
-        editedDesire = kIOPMPreventIdleSleep | kIOPMPowerOn;
-    else
-        editedDesire = 0;
+            // Fetch latest display and system sleep slider values.
+            getAggressiveness(kPMMinutesToSleep, &minutesToIdleSleep);
+            getAggressiveness(kPMMinutesToDim,   &minutesToDisplayDim);
+            DLOG("aggressiveness changed: system %u->%u, display %u\n",
+                (uint32_t) sleepSlider,
+                (uint32_t) minutesToIdleSleep,
+                (uint32_t) minutesToDisplayDim);
 
 
-    // Recompute sleep supported flag (doze if not supported)
-    sleepIsSupported = true;
+            DLOG("idle time -> %ld secs (ena %d)\n",
+                idleSeconds, (minutesToIdleSleep != 0));
 
 
-    iter = getChildIterator(gIOPowerPlane);
-    if ( iter ) 
-    {
-        while ( (next = iter->getNextObject()) ) 
-        {
-            if ( (connection = OSDynamicCast(IOPowerConnection, next)) ) 
+            if (0x7fffffff == minutesToIdleSleep)
+                minutesToIdleSleep = idleSeconds;
+
+            // How long to wait before sleeping the system once
+            // the displays turns off is indicated by 'extraSleepDelay'.
+
+            if ( minutesToIdleSleep > minutesToDisplayDim )
+                minutesDelta = minutesToIdleSleep - minutesToDisplayDim;
+            else if ( minutesToIdleSleep == minutesToDisplayDim )
+                minutesDelta = 1;
+
+            if ((sleepSlider == 0) && (minutesToIdleSleep != 0))
+                flags.bit.idleSleepEnabled = true;
+
+            if ((sleepSlider != 0) && (minutesToIdleSleep == 0))
+                flags.bit.idleSleepDisabled = true;
+
+            if (((minutesDelta != extraSleepDelay) ||
+                        (userActivityTime != userActivityTime_prev)) &&
+                !flags.bit.idleSleepEnabled && !flags.bit.idleSleepDisabled)
+                flags.bit.sleepDelayChanged = true;
+
+            if (systemDarkWake && !darkWakeToSleepASAP &&
+                (flags.bit.idleSleepEnabled || flags.bit.idleSleepDisabled))
             {
             {
-                // Ignore child that are in the process of joining.
-                               if (connection->getReadyFlag() == false)
-                                       continue;
+                // Reconsider decision to remain in dark wake
+                flags.bit.evaluateDarkWake = true;
+            }
 
 
-                // Is this connection attached to the child that called
-                // requestPowerDomainState()?
+            sleepSlider = minutesToIdleSleep;
+            extraSleepDelay = minutesDelta;
+            userActivityTime_prev = userActivityTime;
+        }   break;
 
 
-                if (connection == whichChild) 
+        case kStimulusDemandSystemSleep:
+            displayIdleForDemandSleep = true;
+            if (wrangler && wranglerIdleSettings)
+            {
+                // Request wrangler idle only when demand sleep is triggered
+                // from full wake.
+                if(CAP_CURRENT(kIOPMSystemCapabilityGraphics))
                 {
                 {
-                    // OR in the child's input power requirements.
-                    powerRequestFlag |= editedDesire;
+                    wrangler->setProperties(wranglerIdleSettings);
+                    DLOG("Requested wrangler idle\n");
+                }
+            }
+            // arg = sleepReason
+            changePowerStateWithOverrideTo( SLEEP_STATE, arg );
+            break;
+
+        case kStimulusAllowSystemSleepChanged:
+            flags.bit.adjustPowerState = true;
+            break;
 
 
-                    if ( desiredFlags & kIOPMPreventSystemSleep )
-                        sleepIsSupported = false;
+        case kStimulusDarkWakeActivityTickle:
+            // arg == true implies real and not self generated wrangler tickle.
+            // Update wake type on PM work loop instead of the tickle thread to
+            // eliminate the possibility of an early tickle clobbering the wake
+            // type set by the platform driver.
+            if (arg == true)
+                setProperty(kIOPMRootDomainWakeTypeKey, kIOPMRootDomainWakeTypeHIDActivity);
+            
+            if (false == wranglerTickled)
+            {
+                if (latchDisplayWranglerTickle(true))
+                {
+                    DLOG("latched tickle\n");
+                    break;
                 }
                 }
-                else
+
+                wranglerTickled = true;
+                DLOG("Requesting full wake after dark wake activity tickle\n");
+                requestFullWake( kFullWakeReasonLocalUser );
+            }
+            break;
+
+        case kStimulusDarkWakeEntry:
+        case kStimulusDarkWakeReentry:
+            // Any system transitions since the last dark wake transition
+            // will invalid the stimulus.
+
+            if (arg == _systemStateGeneration)
+            {
+                DLOG("dark wake entry\n");
+                systemDarkWake = true;
+
+                // Keep wranglerAsleep an invariant when wrangler is absent
+                if (wrangler)
+                    wranglerAsleep = true;
+
+                if (kStimulusDarkWakeEntry == stimulus)
                 {
                 {
-                    if (kIOLogPMRootDomain & gIOKitDebug)
-                    {
-                        IOService * powerChild =
-                            (IOService *) connection->getChildEntry(gIOPowerPlane);
-                        DLOG("child %p, state %ld, noIdle %d, noSleep %d - %s\n",
-                            powerChild,
-                            connection->getDesiredDomainState(),
-                            connection->getPreventIdleSleepFlag(),
-                            connection->getPreventSystemSleepFlag(),
-                            powerChild ? powerChild->getName() : "?");
-                    }
+                    clock_get_uptime(&userBecameInactiveTime);
+                    flags.bit.evaluateDarkWake = true;
+                }
+
+                // Always accelerate disk spindown while in dark wake,
+                // even if system does not support/allow sleep.
+
+                cancelIdleSleepTimer();
+                setQuickSpinDownTimeout();
+            }
+            break;
+
+        case kStimulusDarkWakeEvaluate:
+            if (systemDarkWake)
+            {
+                flags.bit.evaluateDarkWake = true;
+            }
+            break;
+
+        case kStimulusNoIdleSleepPreventers:
+            flags.bit.adjustPowerState = true;
+            break;
+
+    } /* switch(stimulus) */
+
+    if (flags.bit.evaluateDarkWake && (kFullWakeReasonNone == fullWakeReason))
+    {
+        if (darkWakeToSleepASAP ||
+            (clamshellClosed && !(desktopMode && acAdaptorConnected)))
+        {
+            uint32_t newSleepReason;
+
+            if (CAP_HIGHEST(kIOPMSystemCapabilityGraphics))
+            {
+                // System was previously in full wake. Sleep reason from
+                // full to dark already recorded in fullToDarkReason.
+
+                if (lowBatteryCondition)
+                    newSleepReason = kIOPMSleepReasonLowPower;
+                else
+                    newSleepReason = fullToDarkReason;
+            }
+            else
+            {
+                // In dark wake from system sleep.
+
+                if (darkWakeSleepService)
+                    newSleepReason = kIOPMSleepReasonSleepServiceExit;
+                else
+                    newSleepReason = kIOPMSleepReasonMaintenance;
+            }
+
+            if (checkSystemCanSleep(newSleepReason))
+            {
+                privateSleepSystem(newSleepReason);
+            }
+        }
+        else // non-maintenance (network) dark wake
+        {
+            if (checkSystemCanSleep(kIOPMSleepReasonIdle))
+            {
+                // Release power clamp, and wait for children idle.
+                adjustPowerState(true);
+            }
+            else
+            {
+                changePowerStateToPriv(ON_STATE);
+            }
+        }
+    }
+
+    if (systemDarkWake)
+    {
+        // The rest are irrelevant while system is in dark wake.
+        flags.u32 = 0;
+    }
+
+    if ((flags.bit.displaySleep) &&
+        (kFullWakeReasonDisplayOn == fullWakeReason))
+    {
+        // kIOPMSleepReasonMaintenance?
+        changePowerStateWithOverrideTo( SLEEP_STATE, kIOPMSleepReasonMaintenance );
+    }
+
+    if (flags.bit.userBecameInactive || flags.bit.sleepDelayChanged)
+    {
+        bool cancelQuickSpindown = false;
+
+        if (flags.bit.sleepDelayChanged)
+        {
+            // Cancel existing idle sleep timer and quick disk spindown.
+            // New settings will be applied by the idleSleepEnabled flag
+            // handler below if idle sleep is enabled.
+
+            DLOG("extra sleep timer changed\n");
+            cancelIdleSleepTimer();
+            cancelQuickSpindown = true;
+        }
+        else
+        {
+            DLOG("user inactive\n");
+        }
+
+        if (!userIsActive && sleepSlider)
+        {
+            startIdleSleepTimer(getTimeToIdleSleep());
+        }
+
+        if (cancelQuickSpindown)
+            restoreUserSpinDownTimeout();
+    }
+
+    if (flags.bit.idleSleepEnabled)
+    {
+        DLOG("idle sleep timer enabled\n");
+        if (!wrangler)
+        {
+            changePowerStateToPriv(ON_STATE);
+            if (idleSeconds)
+            {
+                startIdleSleepTimer( idleSeconds );
+            }
+        }
+        else
+        {
+            // Start idle timer if prefs now allow system sleep
+            // and user is already inactive. Disk spindown is
+            // accelerated upon timer expiration.
+
+            if (!userIsActive)
+            {
+                startIdleSleepTimer(getTimeToIdleSleep());
+            }
+        }
+    }
+
+    if (flags.bit.idleSleepDisabled)
+    {
+        DLOG("idle sleep timer disabled\n");
+        cancelIdleSleepTimer();
+        restoreUserSpinDownTimeout();
+        adjustPowerState();
+    }
 
 
-                    // OR in the child's desired power state (0 or ON_STATE).
-                    powerRequestFlag |= connection->getDesiredDomainState();
+    if (flags.bit.adjustPowerState)
+    {
+        bool sleepASAP = false;
 
 
-                    if ( connection->getPreventSystemSleepFlag() )
-                        sleepIsSupported = false;
+        if (!systemBooting && (preventIdleSleepList->getCount() == 0))
+        {
+            if (!wrangler)
+            {
+                changePowerStateToPriv(ON_STATE);
+                if (idleSeconds)
+                {
+                    // stay awake for at least idleSeconds
+                    startIdleSleepTimer(idleSeconds);
                 }
             }
                 }
             }
+            else if (!extraSleepDelay && !idleSleepTimerPending && !systemDarkWake)
+            {
+                sleepASAP = true;
+            }
         }
         }
-        iter->release();
+
+        adjustPowerState(sleepASAP);
     }
     }
+}
 
 
-    DLOG("childPowerFlags 0x%lx, extraSleepDelay %ld\n",
-        powerRequestFlag, extraSleepDelay);
+//******************************************************************************
+// requestFullWake
+//
+// Request transition from dark wake to full wake
+//******************************************************************************
 
 
-    if ( !powerRequestFlag && !systemBooting ) 
+void IOPMrootDomain::requestFullWake( FullWakeReason reason )
+{
+    uint32_t        options = 0;
+    IOService *     pciRoot = 0;
+    bool            promotion = false;
+
+    // System must be in dark wake and a valid reason for entering full wake
+    if ((kFullWakeReasonNone == reason) ||
+        (kFullWakeReasonNone != fullWakeReason) ||
+        (CAP_CURRENT(kIOPMSystemCapabilityGraphics)))
     {
     {
-        if (!wrangler)
-        {
-            sleepASAP = false;
-            changePowerStateToPriv(ON_STATE);
-            if (idleSeconds)
-            {
-                // stay awake for at least idleSeconds
-                startIdleSleepTimer(idleSeconds);        
-            }
-        }
-        else if (!extraSleepDelay && !idleSleepTimerPending)
-        {
-            sleepASAP = true;
-        }
+        return;
     }
 
     }
 
-    // Drop our power clamp to SLEEP_STATE when all children became idle,
-    // and the system sleep and display sleep values are equal.
+    // Will clear reason upon exit from full wake
+    fullWakeReason = reason;
 
 
-    adjustPowerState();
+    _desiredCapability |= (kIOPMSystemCapabilityGraphics |
+                           kIOPMSystemCapabilityAudio);
 
 
-    // If our power clamp has already dropped to SLEEP_STATE, and no child
-    // is keeping us at ON_STATE, then this will trigger idle sleep.
+    if ((kSystemTransitionWake == _systemTransitionType) &&
+        !(_pendingCapability & kIOPMSystemCapabilityGraphics) &&
+        !graphicsSuppressed)
+    {
+        // Promote to full wake while waking up to dark wake due to tickle.
+        // PM will hold off notifying the graphics subsystem about system wake
+        // as late as possible, so if a HID tickle does arrive, graphics can
+        // power up on this same wake cycle. The latency to power up graphics
+        // on the next cycle can be huge on some systems. However, once any
+        // graphics suppression has taken effect, it is too late. All other
+        // graphics devices must be similarly suppressed. But the delay till
+        // the following cycle should be short.
+
+        _pendingCapability |= (kIOPMSystemCapabilityGraphics |
+                               kIOPMSystemCapabilityAudio);
+
+        // Immediately bring up audio and graphics
+        pciRoot = pciHostBridgeDriver;
+        willEnterFullWake();
+        promotion = true;
+    }
 
 
-    editedDesire |= (desiredFlags & kIOPMPreventSystemSleep);
+    // Unsafe to cancel once graphics was powered.
+    // If system woke from dark wake, the return to sleep can
+    // be cancelled. "awake -> dark -> sleep" transition
+    // can be canceled also, during the "dark --> sleep" phase
+    // *prior* to driver power down.
+    if (!CAP_HIGHEST(kIOPMSystemCapabilityGraphics) ||
+        _pendingCapability == 0) {
+        options |= kIOPMSyncCancelPowerDown;
+    }
 
 
-    return super::requestPowerDomainState(
-        editedDesire, whichChild, specification);
-}
+    synchronizePowerTree(options, pciRoot);
+    if (kFullWakeReasonLocalUser == fullWakeReason)
+    {
+        // IOGraphics doesn't light the display even though graphics is
+        // enabled in kIOMessageSystemCapabilityChange message(radar 9502104)
+        // So, do an explicit activity tickle
+        if (wrangler)
+            wrangler->activityTickle(0,0);
+    }
 
 
+    // Log a timestamp for the initial full wake request.
+    // System may not always honor this full wake request.
+    if (!CAP_HIGHEST(kIOPMSystemCapabilityGraphics))
+    {
+        AbsoluteTime    now;
+        uint64_t        nsec;
+
+        clock_get_uptime(&now);
+        SUB_ABSOLUTETIME(&now, &systemWakeTime);
+        absolutetime_to_nanoseconds(now, &nsec);
+        MSG("full wake %s (reason %u) %u ms\n",
+            promotion ? "promotion" : "request",
+            fullWakeReason, ((int)((nsec) / 1000000ULL)));
+    }
+}
 
 //******************************************************************************
 
 //******************************************************************************
-// handlePlatformHaltRestart
+// willEnterFullWake
+//
+// System will enter full wake from sleep, from dark wake, or from dark
+// wake promotion. This function aggregate things that are in common to
+// all three full wake transitions.
 //
 //
+// Assumptions: fullWakeReason was updated
 //******************************************************************************
 
 //******************************************************************************
 
-struct HaltRestartApplierContext {
-       IOPMrootDomain *        RootDomain;
-       unsigned long           PowerState;
-       IOPMPowerFlags          PowerFlags;
-       UInt32                          MessageType;
-       UInt32                          Counter;
-};
-
-static void
-platformHaltRestartApplier( OSObject * object, void * context )
+void IOPMrootDomain::willEnterFullWake( void )
 {
 {
-       IOPowerStateChangeNotification  notify;
-       HaltRestartApplierContext *             ctx;
-       AbsoluteTime                                    startTime;
-       UInt32                                                  deltaTime;
+    hibernateRetry = false;
+    sleepToStandby = false;
+    sleepTimerMaintenance = false;
 
 
-       ctx = (HaltRestartApplierContext *) context;
-       
-       memset(&notify, 0, sizeof(notify));
-    notify.powerRef    = (void *)ctx->Counter;
-    notify.returnValue = 0;
-    notify.stateNumber = ctx->PowerState;
-    notify.stateFlags  = ctx->PowerFlags;
+    _systemMessageClientMask = kSystemMessageClientPowerd |
+                               kSystemMessageClientLegacyApp;
 
 
-       clock_get_uptime(&startTime);
-    ctx->RootDomain->messageClient( ctx->MessageType, object, (void *)&notify );
-       deltaTime = computeDeltaTimeMS(&startTime);
-
-       if ((deltaTime > kPMHaltTimeoutMS) || (gIOKitDebug & kIOLogDebugPower))
-       {
-               _IOServiceInterestNotifier * notifier;
-               notifier = OSDynamicCast(_IOServiceInterestNotifier, object);
-
-               // IOService children of IOPMrootDomain are not instrumented.
-               // Only IORootParent currently falls under that group.
-
-               if (notifier)
-               {
-                       KLOG("%s handler %p took %u ms\n",
-                               (ctx->MessageType == kIOMessageSystemWillPowerOff) ?
-                                       "PowerOff" : "Restart",
-                               notifier->handler, (uint32_t) deltaTime );
-               }
-       }
+    if ((_highestCapability & kIOPMSystemCapabilityGraphics) == 0)
+    {
+        // Initial graphics full power
+        _systemMessageClientMask |= kSystemMessageClientKernel;
+
+        // Set kIOPMUserTriggeredFullWakeKey before full wake for IOGraphics
+        setProperty(gIOPMUserTriggeredFullWakeKey,
+            (kFullWakeReasonLocalUser == fullWakeReason) ?
+                kOSBooleanTrue : kOSBooleanFalse);
+    }
+#if HIBERNATION
+    IOHibernateSetWakeCapabilities(_pendingCapability);
+#endif
 
 
-       ctx->Counter++;
+    IOService::setAdvisoryTickleEnable( true );
+    tellClients(kIOMessageSystemWillPowerOn);
+    preventTransitionToUserActive(false);
 }
 
 }
 
-void IOPMrootDomain::handlePlatformHaltRestart( UInt32 pe_type )
-{
-       HaltRestartApplierContext       ctx;
-       AbsoluteTime                            startTime;
-       UInt32                                          deltaTime;
+//******************************************************************************
+// fullWakeDelayedWork
+//
+// System has already entered full wake. Invoked by a delayed thread call.
+//******************************************************************************
 
 
-       memset(&ctx, 0, sizeof(ctx));
-       ctx.RootDomain = this;
+void IOPMrootDomain::fullWakeDelayedWork( void )
+{
+#if DARK_TO_FULL_EVALUATE_CLAMSHELL
+    // Not gated, don't modify state
+    if ((kSystemTransitionNone == _systemTransitionType) &&
+        CAP_CURRENT(kIOPMSystemCapabilityGraphics))
+    {
+        receivePowerNotification( kLocalEvalClamshellCommand );
+    }
+#endif
+}
 
 
-       clock_get_uptime(&startTime);
-       switch (pe_type)
-       {
-               case kPEHaltCPU:
-        case kPEUPSDelayHaltCPU:
-                       ctx.PowerState  = OFF_STATE;
-                       ctx.MessageType = kIOMessageSystemWillPowerOff;
-                       break;
+//******************************************************************************
+// evaluateAssertions
+//
+//******************************************************************************
+void IOPMrootDomain::evaluateAssertions(IOPMDriverAssertionType newAssertions, IOPMDriverAssertionType oldAssertions)
+{
+    IOPMDriverAssertionType changedBits = newAssertions ^ oldAssertions;
 
 
-               case kPERestartCPU:
-                       ctx.PowerState  = RESTART_STATE;
-                       ctx.MessageType = kIOMessageSystemWillRestart;
-                       break;
+    messageClients(kIOPMMessageDriverAssertionsChanged);
 
 
-               default:
-                       return;
-       }
+    if (changedBits & kIOPMDriverAssertionPreventDisplaySleepBit) {
 
 
-       // Notify legacy clients
-       applyToInterested(gIOPriorityPowerStateInterest, platformHaltRestartApplier, &ctx);
+        if (wrangler) {
+            bool value = (newAssertions & kIOPMDriverAssertionPreventDisplaySleepBit) ? true : false;
 
 
-    // For UPS shutdown leave File Server Mode intact, otherwise turn it off.
-    if (kPEUPSDelayHaltCPU != pe_type)
-    {
-        const OSSymbol * setting = OSSymbol::withCString(kIOPMSettingRestartOnPowerLossKey);
-        OSNumber * num = OSNumber::withNumber((unsigned long long) 0, 32);
-        if (setting && num)
-        {
-            setPMSetting(setting, num);
-            setting->release();
-            num->release();
+            DLOG("wrangler->setIgnoreIdleTimer\(%d)\n", value);
+            wrangler->setIgnoreIdleTimer( value );
         }
     }
 
         }
     }
 
-       // Notify in power tree order
-       notifySystemShutdown(this, ctx.MessageType);
+    if (changedBits & kIOPMDriverAssertionCPUBit)
+        evaluatePolicy(kStimulusDarkWakeEvaluate);
 
 
-       deltaTime = computeDeltaTimeMS(&startTime);
-       KLOG("%s all drivers took %u ms\n",
-               (ctx.MessageType == kIOMessageSystemWillPowerOff) ?
-                       "PowerOff" : "Restart",
-               (uint32_t) deltaTime );
+    if (changedBits & kIOPMDriverAssertionReservedBit7) {
+        bool value = (newAssertions & kIOPMDriverAssertionReservedBit7) ? true : false;
+        if (value) {
+            DLOG("Driver assertion ReservedBit7 raised. Legacy IO preventing sleep\n");
+            updatePreventIdleSleepList(this, true);
+        }
+        else {
+            DLOG("Driver assertion ReservedBit7 dropped\n");
+            updatePreventIdleSleepList(this, false);
+        }
+    }
 }
 
 }
 
+// MARK: -
+// MARK: Statistics
 
 //******************************************************************************
 
 //******************************************************************************
-// registerInterest
+// pmStats
 //
 //******************************************************************************
 
 //
 //******************************************************************************
 
-IONotifier * IOPMrootDomain::registerInterest(
-                const OSSymbol * typeOfInterest,
-                IOServiceInterestHandler handler,
-                void * target, void * ref )
+void IOPMrootDomain::pmStatsRecordEvent(
+    int                 eventIndex,
+    AbsoluteTime        timestamp)
 {
 {
-    IONotifier *    notifier;
-    bool            isConfigd;
+    bool        starting = eventIndex & kIOPMStatsEventStartFlag ? true:false;
+    bool        stopping = eventIndex & kIOPMStatsEventStopFlag ? true:false;
+    uint64_t    delta;
+    uint64_t    nsec;
+    OSData *publishPMStats = NULL;
 
 
-    isConfigd = typeOfInterest &&
-                typeOfInterest->isEqualTo(kIOPMPrivilegedPowerInterest);
+    eventIndex &= ~(kIOPMStatsEventStartFlag | kIOPMStatsEventStopFlag);
 
 
-    if (isConfigd)
-        typeOfInterest = gIOAppPowerStateInterest;
+    absolutetime_to_nanoseconds(timestamp, &nsec);
 
 
-    notifier = super::registerInterest(typeOfInterest, handler, target, ref);
+    switch (eventIndex) {
+        case kIOPMStatsHibernateImageWrite:
+            if (starting)
+                gPMStats.hibWrite.start = nsec;
+            else if (stopping)
+                gPMStats.hibWrite.stop = nsec;
 
 
-#if ROOT_DOMAIN_RUN_STATES
-    if (isConfigd && notifier && pmPowerStateQueue)
-    {
-        notifier->retain();
-        if (pmPowerStateQueue->submitPowerEvent(
-                kPowerEventConfigdRegisteredInterest, notifier) == false)
-            notifier->release();
-    }
-#endif
+            if (stopping) {
+                delta = gPMStats.hibWrite.stop - gPMStats.hibWrite.start;
+                IOLog("PMStats: Hibernate write took %qd ms\n", delta/1000000ULL);
+            }
+            break;
+        case kIOPMStatsHibernateImageRead:
+            if (starting)
+                gPMStats.hibRead.start = nsec;
+            else if (stopping)
+                gPMStats.hibRead.stop = nsec;
 
 
-    return notifier;
+            if (stopping) {
+                delta = gPMStats.hibRead.stop - gPMStats.hibRead.start;
+                IOLog("PMStats: Hibernate read took %qd ms\n", delta/1000000ULL);
+
+                publishPMStats = OSData::withBytes(&gPMStats, sizeof(gPMStats));
+                setProperty(kIOPMSleepStatisticsKey, publishPMStats);
+                publishPMStats->release();
+                bzero(&gPMStats, sizeof(gPMStats));
+            }
+            break;
+    }
 }
 
 }
 
-static bool clientMessageFilter( OSObject * object, void * arg )
+/*
+ * Appends a record of the application response to
+ * IOPMrootDomain::pmStatsAppResponses
+ */
+void IOPMrootDomain::pmStatsRecordApplicationResponse(
+    const OSSymbol      *response,
+    const char          *name,
+    int                 messageType,
+    uint32_t            delay_ms,
+    int                 app_pid,
+    OSObject            *object,
+    IOPMPowerStateIndex powerState)
 {
 {
-#if ROOT_DOMAIN_RUN_STATES
-#if LOG_INTEREST_CLIENTS
-    IOPMInterestContext * context = (IOPMInterestContext *) arg;
-#endif
-    bool    allow = false;
+    OSDictionary    *responseDescription    = NULL;
+    OSNumber        *delayNum               = NULL;
+    OSNumber        *powerCaps              = NULL;
+    OSNumber        *pidNum                 = NULL;
+    OSNumber        *msgNum                 = NULL;
+    const OSSymbol  *appname;
+    const OSSymbol  *sleep = NULL, *wake = NULL;
+    IOPMServiceInterestNotifier *notify = 0;
 
 
-    switch (gMessageClientType)
+    if (object && (notify = OSDynamicCast(IOPMServiceInterestNotifier, object)))
     {
     {
-        case kMessageClientNone:
-            allow = false;
-            break;
-        
-        case kMessageClientAll:
-            allow = true;
-            break;
+        if (response->isEqualTo(gIOPMStatsApplicationResponseTimedOut)) 
+            notify->ackTimeoutCnt++;
+        else
+            notify->ackTimeoutCnt = 0;
 
 
-        case kMessageClientConfigd:
-            allow = ((object == (OSObject *) gConfigdNotifier) ||
-                     (object == (OSObject *) gSysPowerDownNotifier));
-            break;
     }
 
     }
 
-#if LOG_INTEREST_CLIENTS
-    if (allow)
-        DLOG("system message %x to %p\n",
-            context->msgType, object);
-#endif
+    if (response->isEqualTo(gIOPMStatsApplicationResponsePrompt) || 
+         (_systemTransitionType == kSystemTransitionNone) || (_systemTransitionType == kSystemTransitionNewCapClient))
+        return;
 
 
-    return allow;
-#else
-    return true;
-#endif
-}
 
 
+    responseDescription = OSDictionary::withCapacity(5);
+    if (responseDescription)
+    {
+        if (response) {
+            responseDescription->setObject(_statsResponseTypeKey, response);
+        }
 
 
-//******************************************************************************
-// tellChangeDown
-//
-// We override the superclass implementation so we can send a different message
-// type to the client or application being notified.
-//******************************************************************************
+        msgNum = OSNumber::withNumber(messageType, 32);
+        if (msgNum) {
+            responseDescription->setObject(_statsMessageTypeKey, msgNum);
+            msgNum->release();
+        }
 
 
-bool IOPMrootDomain::tellChangeDown( unsigned long stateNum )
-{
-    bool    done;
+        if (name && (strlen(name) > 0))
+        {
+            appname = OSSymbol::withCString(name);
+            if (appname) {
+                responseDescription->setObject(_statsNameKey, appname);
+                appname->release();
+            }
+        }
 
 
-    DLOG("tellChangeDown %u->%u, R-state %u\n",
-        (uint32_t) getPowerState(), (uint32_t) stateNum, runStateIndex);
+        if (app_pid != -1) {
+            pidNum = OSNumber::withNumber(app_pid, 32);
+            if (pidNum) {
+                responseDescription->setObject(_statsPIDKey, pidNum);
+                pidNum->release();
+            }
+        }
 
 
-    switch ( stateNum ) {
-        case DOZE_STATE:
-        case SLEEP_STATE:
+        delayNum = OSNumber::withNumber(delay_ms, 32);
+        if (delayNum) {
+            responseDescription->setObject(_statsTimeMSKey, delayNum);
+            delayNum->release();
+        }
 
 
-            if (!ignoreChangeDown)
-            {
-                // Direct callout into OSKext so it can disable kext unloads
-                // during sleep/wake to prevent deadlocks.
-                OSKextSystemSleepOrWake( kIOMessageSystemWillSleep );
+        if (response->isEqualTo(gIOPMStatsDriverPSChangeSlow)) {
+            powerCaps = OSNumber::withNumber(powerState, 32);
 
 
-                if ( (SLEEP_STATE == stateNum) && sleepSupportedPEFunction )
-                {
-                    // Reset PCI prevent sleep flag before calling platform driver.
-                    OSBitAndAtomic(~kPCICantSleep, &platformSleepSupport);
+#if !defined(__i386__) && !defined(__x86_64__)
+            IOLog("%s::powerStateChange type(%d) to(%lu) async took %d ms\n",
+                  name, messageType,
+                  powerState, delay_ms);
+#endif
 
 
-                    // Skip PCI check for maintenance sleep.
-                    if ((runStateFlags & kRStateFlagSuppressPCICheck) == 0)
-                    {
-                        // Determine if the machine supports sleep, or must doze.
-                        getPlatform()->callPlatformFunction(
-                                        sleepSupportedPEFunction, false,
-                                        NULL, NULL, NULL, NULL);
-                    }
+        }
+        else {
+            powerCaps = OSNumber::withNumber(_pendingCapability, 32);
+        }
+        if (powerCaps) {
+            responseDescription->setObject(_statsPowerCapsKey, powerCaps);
+            powerCaps->release();
+        }
 
 
-                    // If the machine only supports doze, the callPlatformFunction call
-                    // boils down to IOPMrootDomain::setSleepSupported(kPCICantSleep), 
-                    // otherwise nothing.
-                }
+        sleep = OSSymbol::withCString("Sleep");
+        wake = OSSymbol::withCString("Wake");
+        if (_systemTransitionType == kSystemTransitionSleep)  {
+            responseDescription->setObject(kIOPMStatsSystemTransitionKey, sleep);
+        }
+        else if (_systemTransitionType == kSystemTransitionWake) {
+            responseDescription->setObject(kIOPMStatsSystemTransitionKey, wake);
+        }
+        else if (_systemTransitionType == kSystemTransitionCapability) {
+            if (CAP_LOSS(kIOPMSystemCapabilityGraphics))
+                responseDescription->setObject(kIOPMStatsSystemTransitionKey, sleep);
+            else if (CAP_GAIN(kIOPMSystemCapabilityGraphics))
+                responseDescription->setObject(kIOPMStatsSystemTransitionKey, wake);
+        }
+        if (sleep) sleep->release();
+        if (wake) wake->release();
 
 
-                // Update canSleep and kIOSleepSupportedKey property so drivers
-                // can tell if platform is going to sleep versus doze. 
 
 
-#if CONFIG_SLEEP
-                canSleep = true;
-#else
-                canSleep = false;
-#endif
-                if (!sleepIsSupported)
-                    canSleep = false;
-                if (platformSleepSupport & kPCICantSleep)
-                    canSleep = false;
-                setProperty(kIOSleepSupportedKey, canSleep);
-                DLOG("canSleep %d\n", canSleep);
-
-                // Publish the new sleep-wake UUID
-                publishSleepWakeUUID(true);
-
-                // Two change downs are sent by IOServicePM. Ignore the 2nd.
-                ignoreChangeDown = true;
-                
-                tracePoint( kIOPMTracePointSystemSleepAppsPhase);
-            }
 
 
-            DLOG("kIOMessageSystemWillSleep (%d)\n", gMessageClientType);
-            done = super::tellClientsWithResponse(
-                    kIOMessageSystemWillSleep, clientMessageFilter);
-            break;
+        IOLockLock(pmStatsLock);
+        if (pmStatsAppResponses && pmStatsAppResponses->getCount() < 50) {
+            pmStatsAppResponses->setObject(responseDescription);
+        }
+        IOLockUnlock(pmStatsLock);
 
 
-        default:
-            done = super::tellChangeDown(stateNum);
-            break;
+        responseDescription->release();
     }
     }
-    return done;
+
+    return;
 }
 
 }
 
+// MARK: -
+// MARK: PMTraceWorker
 
 //******************************************************************************
 
 //******************************************************************************
-// askChangeDown
-//
-// We override the superclass implementation so we can send a different message
-// type to the client or application being notified.
+// TracePoint support
 //
 //
-// This must be idle sleep since we don't ask during any other power change.
 //******************************************************************************
 
 //******************************************************************************
 
-bool IOPMrootDomain::askChangeDown( unsigned long stateNum )
+#define kIOPMRegisterNVRAMTracePointHandlerKey  \
+        "IOPMRegisterNVRAMTracePointHandler"
+
+IOReturn IOPMrootDomain::callPlatformFunction(
+    const OSSymbol * functionName,
+    bool waitForFunction,
+    void * param1, void * param2,
+    void * param3, void * param4 )
+{
+    if (pmTracer && functionName &&
+        functionName->isEqualTo(kIOPMRegisterNVRAMTracePointHandlerKey) &&
+        !pmTracer->tracePointHandler && !pmTracer->tracePointTarget)
+    {
+        uint32_t    tracePointPhases, tracePointPCI;
+        uint64_t    statusCode;
+
+        pmTracer->tracePointHandler = (IOPMTracePointHandler) param1;
+        pmTracer->tracePointTarget  = (void *) param2;
+        tracePointPCI               = (uint32_t)(uintptr_t) param3;
+        tracePointPhases            = (uint32_t)(uintptr_t) param4;
+        statusCode = (((uint64_t)tracePointPCI) << 32) | tracePointPhases;
+        if ((tracePointPhases >> 24) != kIOPMTracePointSystemUp)
+        {
+            MSG("Sleep failure code 0x%08x 0x%08x\n",
+                tracePointPCI, tracePointPhases);
+        }
+        setProperty(kIOPMSleepWakeFailureCodeKey, statusCode, 64);
+        pmTracer->tracePointHandler( pmTracer->tracePointTarget, 0, 0 );
+
+        return kIOReturnSuccess;
+    }
+#if HIBERNATION
+    else if (functionName &&
+             functionName->isEqualTo(kIOPMInstallSystemSleepPolicyHandlerKey))
+    {
+        if (gSleepPolicyHandler)
+            return kIOReturnExclusiveAccess;
+        if (!param1)
+            return kIOReturnBadArgument;
+        gSleepPolicyHandler = (IOPMSystemSleepPolicyHandler) param1;
+        gSleepPolicyTarget  = (void *) param2;
+        setProperty("IOPMSystemSleepPolicyHandler", kOSBooleanTrue);
+        return kIOReturnSuccess;
+    }
+#endif
+
+    return super::callPlatformFunction(
+        functionName, waitForFunction, param1, param2, param3, param4);
+}
+
+void IOPMrootDomain::tracePoint( uint8_t point )
 {
 {
-    DLOG("askChangeDown %u->%u, R-state %u\n",
-        (uint32_t) getPowerState(), (uint32_t) stateNum, runStateIndex);
-    DLOG("kIOMessageCanSystemSleep (%d)\n", gMessageClientType);
+    if (systemBooting) return;
+
+    if (kIOPMTracePointWakeCapabilityClients == point)
+        acceptSystemWakeEvents(false);
 
 
-    return super::tellClientsWithResponse(
-                    kIOMessageCanSystemSleep,
-                    clientMessageFilter);
+    PMDebug(kPMLogSleepWakeTracePoint, point, 0);
+    pmTracer->tracePoint(point);
 }
 
 }
 
+void IOPMrootDomain::tracePoint( uint8_t point, uint8_t data )
+{
+    if (systemBooting) return;
 
 
-//******************************************************************************
-// tellNoChangeDown
-//
-// Notify registered applications and kernel clients that we are not dropping
-// power.
-//
-// We override the superclass implementation so we can send a different message
-// type to the client or application being notified.
-//
-// This must be a vetoed idle sleep, since no other power change can be vetoed.
-//******************************************************************************
+    PMDebug(kPMLogSleepWakeTracePoint, point, data);
+    pmTracer->tracePoint(point, data);
+}
+
+void IOPMrootDomain::traceDetail( uint32_t detail )
+{
+    if (!systemBooting)
+        pmTracer->traceDetail( detail );
+}
+
+
+IOReturn IOPMrootDomain::configureReport(IOReportChannelList    *channelList,
+                                    IOReportConfigureAction action,
+                                    void                   *result,
+                                    void                   *destination)
+{
+    unsigned cnt;
+    if (action != kIOReportGetDimensions) goto exit;
+
+    for (cnt = 0; cnt < channelList->nchannels; cnt++) {
+        if ( (channelList->channels[cnt].channel_id == kSleepCntChID) ||
+               (channelList->channels[cnt].channel_id == kDarkWkCntChID) ||
+               (channelList->channels[cnt].channel_id == kUserWkCntChID) ) {
+            SIMPLEREPORT_UPDATERES(kIOReportGetDimensions, result);
+        }
+    }
+
+exit:
+    return super::configureReport(channelList, action, result, destination);
+}
 
 
-void IOPMrootDomain::tellNoChangeDown( unsigned long stateNum )
+
+IOReturn IOPMrootDomain::updateReport(IOReportChannelList      *channelList,
+                                 IOReportUpdateAction      action,
+                                 void                     *result,
+                                 void                     *destination)
 {
 {
-    DLOG("tellNoChangeDown %u->%u, R-state %u\n",
-        (uint32_t) getPowerState(), (uint32_t) stateNum, runStateIndex);
+    uint32_t size2cpy;
+    void *data2cpy;
+    uint8_t buf[SIMPLEREPORT_BUFSIZE];
+    IOBufferMemoryDescriptor *dest = OSDynamicCast(IOBufferMemoryDescriptor, (OSObject *)destination);
+    unsigned cnt;
+    uint64_t ch_id;
 
 
-       // Sleep canceled, clear the sleep trace point.
-    tracePoint(kIOPMTracePointSystemUp);
+    if (action != kIOReportCopyChannelData) goto exit;
 
 
-    if (idleSeconds && !wrangler)
-    {
-        // stay awake for at least idleSeconds
-        sleepASAP = false;
-        startIdleSleepTimer(idleSeconds);
+    for (cnt = 0; cnt < channelList->nchannels; cnt++) {
+        ch_id = channelList->channels[cnt].channel_id ;
+
+        if ((ch_id == kSleepCntChID) ||
+                (ch_id == kDarkWkCntChID) || (ch_id == kUserWkCntChID)) {
+            SIMPLEREPORT_INIT(buf, sizeof(buf), getRegistryEntryID(), ch_id, kIOReportCategoryPower);
+        }
+        else continue;
+
+        if (ch_id == kSleepCntChID)
+            SIMPLEREPORT_SETVALUE(buf, sleepCnt);
+        else if (ch_id == kDarkWkCntChID)
+            SIMPLEREPORT_SETVALUE(buf, darkWakeCnt);
+        else if (ch_id == kUserWkCntChID)
+            SIMPLEREPORT_SETVALUE(buf, displayWakeCnt);
+
+        SIMPLEREPORT_UPDATEPREP(buf, data2cpy, size2cpy);
+        SIMPLEREPORT_UPDATERES(kIOReportCopyChannelData, result);
+        dest->appendBytes(data2cpy, size2cpy);
     }
     }
-    DLOG("kIOMessageSystemWillNotSleep (%d)\n", gMessageClientType);
-    return tellClients(kIOMessageSystemWillNotSleep, clientMessageFilter);
+
+exit:
+    return super::updateReport(channelList, action, result, destination);
 }
 
 
 //******************************************************************************
 }
 
 
 //******************************************************************************
-// tellChangeUp
-//
-// Notify registered applications and kernel clients that we are raising power.
+// PMTraceWorker Class
 //
 //
-// We override the superclass implementation so we can send a different message
-// type to the client or application being notified.
 //******************************************************************************
 
 //******************************************************************************
 
-void IOPMrootDomain::tellChangeUp( unsigned long stateNum )
-{
-    OSData *publishPMStats = NULL;
+#undef super
+#define super OSObject
+OSDefineMetaClassAndStructors(PMTraceWorker, OSObject)
 
 
-    DLOG("tellChangeUp %u->%u, R-state %u\n",
-        (uint32_t) getPowerState(), (uint32_t) stateNum, runStateIndex);
+#define kPMBestGuessPCIDevicesCount     25
+#define kPMMaxRTCBitfieldSize           32
 
 
-    ignoreChangeDown = false;
+PMTraceWorker *PMTraceWorker::tracer(IOPMrootDomain *owner)
+{
+    PMTraceWorker           *me;
 
 
-    if ( stateNum == ON_STATE )
+    me = OSTypeAlloc( PMTraceWorker );
+    if (!me || !me->init())
     {
     {
-        // Direct callout into OSKext so it can disable kext unloads
-        // during sleep/wake to prevent deadlocks.
-        OSKextSystemSleepOrWake( kIOMessageSystemHasPoweredOn );
-
-        if (getPowerState() == ON_STATE)
-        {
-            // this is a quick wake from aborted sleep
-            if (idleSeconds && !wrangler)
-            {
-                // stay awake for at least idleSeconds
-                sleepASAP = false;
-                startIdleSleepTimer(idleSeconds);
-            }
-            DLOG("kIOMessageSystemWillPowerOn (%d)\n", gMessageClientType);
-            tellClients(kIOMessageSystemWillPowerOn, clientMessageFilter);
-        }
-#if    HIBERNATION
-        else
-        {
-            IOHibernateSystemPostWake();
-        }
-#endif
-        tracePoint(kIOPMTracePointSystemWakeAppsPhase);
-        publishPMStats = OSData::withBytes(&pmStats, sizeof(pmStats));
-        setProperty(kIOPMSleepStatisticsKey, publishPMStats);
-        publishPMStats->release();
-        bzero(&pmStats, sizeof(pmStats));
+        return NULL;
+    }
 
 
-        if (pmStatsAppResponses) 
-        {
-            setProperty(kIOPMSleepStatisticsAppsKey, pmStatsAppResponses);
-            pmStatsAppResponses->release();
-            pmStatsAppResponses = OSArray::withCapacity(5);
-        }
-        
-        DLOG("kIOMessageSystemHasPoweredOn (%d)\n", gMessageClientType);
-        tellClients(kIOMessageSystemHasPoweredOn, clientMessageFilter);
+    DLOG("PMTraceWorker %p\n", OBFUSCATE(me));
 
 
-        tracePoint(kIOPMTracePointSystemUp);
-    }
+    // Note that we cannot instantiate the PCI device -> bit mappings here, since
+    // the IODeviceTree has not yet been created by IOPlatformExpert. We create
+    // this dictionary lazily.
+    me->owner = owner;
+    me->pciDeviceBitMappings = NULL;
+    me->pciMappingLock = IOLockAlloc();
+    me->tracePhase = kIOPMTracePointSystemUp;
+    me->loginWindowPhase = 0;
+    me->traceData32 = 0;
+    return me;
 }
 
 }
 
+void PMTraceWorker::RTC_TRACE(void)
+{
+    if (tracePointHandler && tracePointTarget)
+    {
+        uint32_t    wordA;
 
 
-//******************************************************************************
-// reportUserInput
-//
-//******************************************************************************
+        wordA = (tracePhase << 24) | (loginWindowPhase << 16) |
+                (traceData8 << 8);
 
 
-void IOPMrootDomain::reportUserInput( void )
+        tracePointHandler( tracePointTarget, traceData32, wordA );
+        _LOG("RTC_TRACE wrote 0x%08x 0x%08x\n", traceData32, wordA);
+    }
+}
+
+int PMTraceWorker::recordTopLevelPCIDevice(IOService * pciDevice)
 {
 {
-#if !NO_KERNEL_HID
-    OSIterator * iter;
+    const OSSymbol *    deviceName;
+    int                 index = -1;
 
 
-    if(!wrangler) 
+    IOLockLock(pciMappingLock);
+
+    if (!pciDeviceBitMappings)
     {
     {
-        iter = getMatchingServices(serviceMatching("IODisplayWrangler"));
-        if(iter) 
-        {
-            wrangler = (IOService *) iter->getNextObject();
-            iter->release();
-        }
+        pciDeviceBitMappings = OSArray::withCapacity(kPMBestGuessPCIDevicesCount);
+        if (!pciDeviceBitMappings)
+            goto exit;
     }
 
     }
 
-    if(wrangler)
-        wrangler->activityTickle(0,0);
-#endif
-}
+    // Check for bitmask overflow.
+    if (pciDeviceBitMappings->getCount() >= kPMMaxRTCBitfieldSize)
+        goto exit;
 
 
+    if ((deviceName = pciDevice->copyName()) &&
+        (pciDeviceBitMappings->getNextIndexOfObject(deviceName, 0) == (unsigned int)-1) &&
+        pciDeviceBitMappings->setObject(deviceName))
+    {
+        index = pciDeviceBitMappings->getCount() - 1;
+        _LOG("PMTrace PCI array: set object %s => %d\n",
+            deviceName->getCStringNoCopy(), index);
+    }
+    if (deviceName)
+        deviceName->release();
+    if (!addedToRegistry && (index >= 0))
+        addedToRegistry = owner->setProperty("PCITopLevel", this);
 
 
-//******************************************************************************
-// setQuickSpinDownTimeout
-//
-//******************************************************************************
+exit:
+    IOLockUnlock(pciMappingLock);
+    return index;
+}
 
 
-void IOPMrootDomain::setQuickSpinDownTimeout( void )
+bool PMTraceWorker::serialize(OSSerialize *s) const
 {
 {
-    ASSERT_GATED();
-    setAggressiveness(
-        kPMMinutesToSpinDown, 0, kAggressivesOptionQuickSpindownEnable );
+    bool ok = false;
+    if (pciDeviceBitMappings)
+    {
+        IOLockLock(pciMappingLock);
+        ok = pciDeviceBitMappings->serialize(s);
+        IOLockUnlock(pciMappingLock);
+    }
+    return ok;
 }
 
 }
 
+void PMTraceWorker::tracePoint(uint8_t phase)
+{
+    // clear trace detail when phase begins
+    if (tracePhase != phase)
+        traceData32 = 0;
+
+    tracePhase = phase;
 
 
-//******************************************************************************
-// restoreUserSpinDownTimeout
-//
-//******************************************************************************
+    DLOG("trace point 0x%02x\n", tracePhase);
+    RTC_TRACE();
+}
 
 
-void IOPMrootDomain::restoreUserSpinDownTimeout( void )
+void PMTraceWorker::tracePoint(uint8_t phase, uint8_t data8)
 {
 {
-    ASSERT_GATED();
-    setAggressiveness(
-        kPMMinutesToSpinDown, 0, kAggressivesOptionQuickSpindownDisable );
+    // clear trace detail when phase begins
+    if (tracePhase != phase)
+        traceData32 = 0;
+
+    tracePhase = phase;
+    traceData8 = data8;
+
+    DLOG("trace point 0x%02x 0x%02x\n", tracePhase, traceData8);
+    RTC_TRACE();
 }
 
 }
 
+void PMTraceWorker::traceDetail(uint32_t detail)
+{
+    if (kIOPMTracePointSleepPriorityClients != tracePhase)
+        return;
 
 
-//******************************************************************************
-// changePowerStateTo & changePowerStateToPriv
-//
-// Override of these methods for logging purposes.
-//******************************************************************************
+    traceData32 = detail;
+    DLOG("trace point 0x%02x detail 0x%08x\n", tracePhase, traceData32);
 
 
-IOReturn IOPMrootDomain::changePowerStateTo( unsigned long ordinal )
+    RTC_TRACE();
+}
+
+void PMTraceWorker::traceLoginWindowPhase(uint8_t phase)
 {
 {
-    return kIOReturnUnsupported;    // ignored
+    loginWindowPhase = phase;
+
+    DLOG("loginwindow tracepoint 0x%02x\n", loginWindowPhase);
+    RTC_TRACE();
 }
 
 }
 
-IOReturn IOPMrootDomain::changePowerStateToPriv( unsigned long ordinal )
+void PMTraceWorker::tracePCIPowerChange(
+    change_t type, IOService *service, uint32_t changeFlags, uint32_t bitNum)
 {
 {
-    DLOG("changePowerStateToPriv(%lu)\n", ordinal);
+    uint32_t    bitMask;
+    uint32_t    expectedFlag;
 
 
-       if ( (getPowerState() == DOZE_STATE) && (ordinal != ON_STATE) )
-       {
-               return kIOReturnSuccess;
-       }
+    // Ignore PCI changes outside of system sleep/wake.
+    if ((kIOPMTracePointSleepPowerPlaneDrivers != tracePhase) &&
+        (kIOPMTracePointWakePowerPlaneDrivers  != tracePhase))
+        return;
 
 
-    if ( (userDisabledAllSleep || systemBooting || systemShutdown) &&
-         (ordinal == SLEEP_STATE) )
+    // Only record the WillChange transition when going to sleep,
+    // and the DidChange on the way up.
+    changeFlags &= (kIOPMDomainWillChange | kIOPMDomainDidChange);
+    expectedFlag = (kIOPMTracePointSleepPowerPlaneDrivers == tracePhase) ?
+                    kIOPMDomainWillChange : kIOPMDomainDidChange;
+    if (changeFlags != expectedFlag)
+        return;
+
+    // Mark this device off in our bitfield
+    if (bitNum < kPMMaxRTCBitfieldSize)
     {
     {
-        DLOG("SLEEP rejected, forced to ON state (UD %d, SB %d, SS %d)\n",
-            userDisabledAllSleep, systemBooting, systemShutdown);
+        bitMask = (1 << bitNum);
 
 
-        super::changePowerStateToPriv(ON_STATE);
-    }
+        if (kPowerChangeStart == type)
+        {
+            traceData32 |= bitMask;
+            _LOG("PMTrace: Device %s started  - bit %2d mask 0x%08x => 0x%08x\n",
+                service->getName(), bitNum, bitMask, traceData32);
+        }
+        else
+        {
+            traceData32 &= ~bitMask;
+            _LOG("PMTrace: Device %s finished - bit %2d mask 0x%08x => 0x%08x\n",
+                service->getName(), bitNum, bitMask, traceData32);
+        }
 
 
-    return super::changePowerStateToPriv(ordinal);
+        DLOG("trace point 0x%02x detail 0x%08x\n", tracePhase, traceData32);
+        RTC_TRACE();
+    }
 }
 
 }
 
-
-//******************************************************************************
-// updateRunState
-//
-//******************************************************************************
-
-void IOPMrootDomain::updateRunState( uint32_t inRunState )
+uint64_t  PMTraceWorker::getPMStatusCode( )
 {
 {
-#if ROOT_DOMAIN_RUN_STATES
-    if (inRunState < kRStateCount)
-    {
-        runStateIndex = nextRunStateIndex = inRunState;
-        runStateFlags = gRStateFlags[inRunState];
+    return (((uint64_t)traceData32 << 32) | (tracePhase << 24) |
+            (loginWindowPhase << 16) | (traceData8 << 8));
 
 
-        setProperty(
-            kIOPMRootDomainRunStateKey,
-            (unsigned long long) inRunState, 32);
-    }
-#endif
 }
 
 }
 
+// MARK: -
+// MARK: PMHaltWorker
 
 
-#if ROOT_DOMAIN_RUN_STATES
 //******************************************************************************
 //******************************************************************************
-// tagPowerPlaneService
+// PMHaltWorker Class
 //
 //
-// Running on PM work loop thread.
 //******************************************************************************
 
 //******************************************************************************
 
-void IOPMrootDomain::tagPowerPlaneService(
-        IOService * service,
-        uint32_t *  rdFlags )
+static unsigned int     gPMHaltBusyCount;
+static unsigned int     gPMHaltIdleCount;
+static int              gPMHaltDepth;
+static unsigned long    gPMHaltEvent;
+static IOLock *         gPMHaltLock  = 0;
+static OSArray *        gPMHaltArray = 0;
+static const OSSymbol * gPMHaltClientAcknowledgeKey = 0;
+
+PMHaltWorker * PMHaltWorker::worker( void )
 {
 {
-    *rdFlags = 0;
+    PMHaltWorker *  me;
+    IOThread        thread;
 
 
-    if (service->getProperty("IOPMStrictTreeOrder") ||
-        service->metaCast("IODisplayWrangler") ||
-        OSDynamicCast(OSNumber,
-            service->getProperty("IOPMUnattendedWakePowerState")))
-    {
-        *rdFlags |= kServiceFlagGraphics;
-        DLOG("tagged device %s %x\n", service->getName(), *rdFlags);
-    }
+    do {
+        me = OSTypeAlloc( PMHaltWorker );
+        if (!me || !me->init())
+            break;
 
 
-    // Locate the first PCI host bridge.
-    if (!pciHostBridgeDevice && service->metaCast("IOPCIBridge"))
-    {
-        IOService * provider = service->getProvider();
-        if (OSDynamicCast(IOPlatformDevice, provider) &&
-            provider->inPlane(gIODTPlane))
-        {
-            pciHostBridgeDevice = provider;
-            DLOG("PMTrace found PCI host bridge %s->%s\n",
-                provider->getName(), service->getName());
-        }
-    }
+        me->lock = IOLockAlloc();
+        if (!me->lock)
+            break;
 
 
-    // Tag top-level PCI devices. The order of PMinit() call does not
-       // change across boots and is used as the PCI bit number.
-    if (pciHostBridgeDevice && service->metaCast("IOPCIDevice"))
-    {
-        // Would prefer to check built-in property, but tagPowerPlaneService()
-        // is called before pciDevice->registerService().
-        IORegistryEntry * parent = service->getParentEntry(gIODTPlane);
-        if ((parent == pciHostBridgeDevice) && service->getProperty("acpi-device"))
+        DLOG("PMHaltWorker %p\n", OBFUSCATE(me));
+        me->retain();   // thread holds extra retain
+        if (KERN_SUCCESS != kernel_thread_start(&PMHaltWorker::main, (void *) me, &thread))
         {
         {
-            int bit = pmTracer->recordTopLevelPCIDevice( service );
-            if (bit >= 0)
-            {
-                               // Save the assigned bit for fast lookup.
-                bit &= 0xff;
-                *rdFlags |= (kServiceFlagTopLevelPCI | (bit << 8));
-            }
+            me->release();
+            break;
         }
         }
-    }
-}
+        thread_deallocate(thread);
+        return me;
 
 
+    } while (false);
 
 
-//******************************************************************************
-// handleActivityTickleForService
-//
-// Called by IOService::activityTickle() for a tickle that is requesting the
-// service to raise power state. Called from driver thread.
-//******************************************************************************
+    if (me) me->release();
+    return 0;
+}
 
 
-void IOPMrootDomain::handleActivityTickleForService( IOService * service )
+void PMHaltWorker::free( void )
 {
 {
-    // Tickle directed to IODisplayWrangler while graphics is disabled.
-    // Bring graphics online.
-
-    if ((service == wrangler) &&
-        (runStateIndex > kRStateNormal) &&
-        (false == wranglerTickled))
+    DLOG("PMHaltWorker free %p\n", OBFUSCATE(this));
+    if (lock)
     {
     {
-        DLOG("display wrangler tickled\n");
-        wranglerTickled = true;
-        synchronizePowerTree();
+        IOLockFree(lock);
+        lock = 0;
     }
     }
+    return OSObject::free();
 }
 
 }
 
+void PMHaltWorker::main( void * arg, wait_result_t waitResult )
+{
+    PMHaltWorker * me = (PMHaltWorker *) arg;
+
+    IOLockLock( gPMHaltLock );
+    gPMHaltBusyCount++;
+    me->depth = gPMHaltDepth;
+    IOLockUnlock( gPMHaltLock );
 
 
-//******************************************************************************
-// handlePowerChangeStartForService
-//
-// Running on PM work loop thread.
-//******************************************************************************
+    while (me->depth >= 0)
+    {
+        PMHaltWorker::work( me );
 
 
-void IOPMrootDomain::handlePowerChangeStartForService(
-        IOService *     service,
-        uint32_t *      rdFlags,
-        uint32_t        newPowerState,
-        uint32_t        changeFlags )
+        IOLockLock( gPMHaltLock );
+        if (++gPMHaltIdleCount >= gPMHaltBusyCount)
+        {
+            // This is the last thread to finish work on this level,
+            // inform everyone to start working on next lower level.
+            gPMHaltDepth--;
+            me->depth = gPMHaltDepth;
+            gPMHaltIdleCount = 0;
+            thread_wakeup((event_t) &gPMHaltIdleCount);
+        }
+        else
+        {
+            // One or more threads are still working on this level,
+            // this thread must wait.
+            me->depth = gPMHaltDepth - 1;
+            do {
+                IOLockSleep(gPMHaltLock, &gPMHaltIdleCount, THREAD_UNINT);
+            } while (me->depth != gPMHaltDepth);
+        }
+        IOLockUnlock( gPMHaltLock );
+    }
+
+    // No more work to do, terminate thread
+    DLOG("All done for worker: %p (visits = %u)\n", OBFUSCATE(me), me->visits);
+    thread_wakeup( &gPMHaltDepth );
+    me->release();
+}
+
+void PMHaltWorker::work( PMHaltWorker * me )
 {
 {
-    if (service == this)
+    IOService *     service;
+    OSSet *         inner;
+    AbsoluteTime    startTime;
+    UInt32          deltaTime;
+    bool            timeout;
+
+    while (true)
     {
     {
-        uint32_t currentPowerState = (uint32_t) getPowerState();
-        uint32_t nextRunStateFlags;
+        service = 0;
+        timeout = false;
+
+        // Claim an unit of work from the shared pool
+        IOLockLock( gPMHaltLock );
+        inner = (OSSet *)gPMHaltArray->getObject(me->depth);
+        if (inner)
+        {
+            service = (IOService *)inner->getAnyObject();
+            if (service)
+            {
+                service->retain();
+                inner->removeObject(service);
+            }
+        }
+        IOLockUnlock( gPMHaltLock );
+        if (!service)
+            break;  // no more work at this depth
+
+        clock_get_uptime(&startTime);
 
 
-        assert(nextRunStateIndex < kRStateCount);
-        nextRunStateFlags = gRStateFlags[nextRunStateIndex];
+        if (!service->isInactive() &&
+            service->setProperty(gPMHaltClientAcknowledgeKey, me))
+        {
+            IOLockLock(me->lock);
+            me->startTime = startTime;
+            me->service   = service;
+            me->timeout   = false;
+            IOLockUnlock(me->lock);
 
 
-        gMessageClientType = kMessageClientNone;
+            service->systemWillShutdown( gPMHaltEvent );
 
 
-        // Transition towards or away from ON power state.
+            // Wait for driver acknowledgement
+            IOLockLock(me->lock);
+            while (service->getProperty(gPMHaltClientAcknowledgeKey))
+            {
+                IOLockSleep(me->lock, me, THREAD_UNINT);
+            }
+            me->service = 0;
+            timeout = me->timeout;
+            IOLockUnlock(me->lock);
+        }
 
 
-        if ((currentPowerState != newPowerState) &&
-            ((ON_STATE == newPowerState) || (ON_STATE == currentPowerState)))
+        deltaTime = computeDeltaTimeMS(&startTime);
+        if ((deltaTime > kPMHaltTimeoutMS) || timeout ||
+            (gIOKitDebug & kIOLogPMRootDomain))
         {
         {
-            if ((runStateFlags & kRStateFlagSuppressMessages) == 0)
-                gMessageClientType = kMessageClientAll;
-            else
-                gMessageClientType = kMessageClientConfigd;
+            LOG("%s driver %s (%p) took %u ms\n",
+                (gPMHaltEvent == kIOMessageSystemWillPowerOff) ?
+                    "PowerOff" : "Restart",
+                service->getName(), OBFUSCATE(service),
+                (uint32_t) deltaTime );
         }
 
         }
 
-        // Transition caused by deassertion of system notification suppression.
+        service->release();
+        me->visits++;
+    }
+}
+
+void PMHaltWorker::checkTimeout( PMHaltWorker * me, AbsoluteTime * now )
+{
+    UInt64          nano;
+    AbsoluteTime    startTime;
+    AbsoluteTime    endTime;
+
+    endTime = *now;
 
 
-        if ((ON_STATE == newPowerState) &&
-            (ON_STATE == currentPowerState) &&
-            ((runStateFlags ^ nextRunStateFlags) & kRStateFlagSuppressMessages))
+    IOLockLock(me->lock);
+    if (me->service && !me->timeout)
+    {
+        startTime = me->startTime;
+        nano = 0;
+        if (CMP_ABSOLUTETIME(&endTime, &startTime) > 0)
         {
         {
-            gMessageClientType = kMessageClientAll;
+            SUB_ABSOLUTETIME(&endTime, &startTime);
+            absolutetime_to_nanoseconds(endTime, &nano);
         }
         }
-
-        if (ON_STATE == newPowerState)
+        if (nano > 3000000000ULL)
         {
         {
-            DLOG("kIOMessageSystemWillPowerOn (%d)\n",
-                gMessageClientType);
-            tellClients(kIOMessageSystemWillPowerOn, clientMessageFilter);
+            me->timeout = true;
+            MSG("%s still waiting on %s\n",
+                (gPMHaltEvent == kIOMessageSystemWillPowerOff) ?
+                    "PowerOff" : "Restart",
+                me->service->getName());
         }
     }
         }
     }
-    
-    if (*rdFlags & kServiceFlagTopLevelPCI)
-    {
-        pmTracer->tracePCIPowerChange(
-                       PMTraceWorker::kPowerChangeStart,
-                       service, changeFlags,
-            (*rdFlags >> 8) & 0xff);
-    }
+    IOLockUnlock(me->lock);
 }
 
 
 //******************************************************************************
 }
 
 
 //******************************************************************************
-// handlePowerChangeDoneForService
+// acknowledgeSystemWillShutdown
 //
 //
-// Running on PM work loop thread.
+// Acknowledgement from drivers that they have prepared for shutdown/restart.
 //******************************************************************************
 
 //******************************************************************************
 
-void IOPMrootDomain::handlePowerChangeDoneForService(
-        IOService *     service,
-        uint32_t *      rdFlags,
-        uint32_t        newPowerState,
-        uint32_t        changeFlags )
+void IOPMrootDomain::acknowledgeSystemWillShutdown( IOService * from )
 {
 {
-    if (*rdFlags & kServiceFlagTopLevelPCI)
+    PMHaltWorker *  worker;
+    OSObject *      prop;
+
+    if (!from)
+        return;
+
+    //DLOG("%s acknowledged\n", from->getName());
+    prop = from->copyProperty( gPMHaltClientAcknowledgeKey );
+    if (prop)
+    {
+        worker = (PMHaltWorker *) prop;
+        IOLockLock(worker->lock);
+        from->removeProperty( gPMHaltClientAcknowledgeKey );
+        thread_wakeup((event_t) worker);
+        IOLockUnlock(worker->lock);
+        worker->release();
+    }
+    else
     {
     {
-        pmTracer->tracePCIPowerChange(
-                       PMTraceWorker::kPowerChangeCompleted,
-            service, changeFlags,
-            (*rdFlags >> 8) & 0xff);
+        DLOG("%s acknowledged without worker property\n",
+            from->getName());
     }
 }
 
 
 //******************************************************************************
     }
 }
 
 
 //******************************************************************************
-// overridePowerStateForService
+// notifySystemShutdown
 //
 //
-// Runs on PM work loop thread.
+// Notify all objects in PM tree that system will shutdown or restart
 //******************************************************************************
 
 //******************************************************************************
 
-void IOPMrootDomain::overridePowerStateForService(
-        IOService *     service,
-        uint32_t *      rdFlags,
-        unsigned long * powerState,
-        uint32_t        changeFlags )
+static void
+notifySystemShutdown( IOService * root, unsigned long event )
 {
 {
-    uint32_t inPowerState = (uint32_t) *powerState;
-
-    if ((service == this) && (inPowerState == ON_STATE) &&
-        (changeFlags & kIOPMSynchronize))
+#define PLACEHOLDER ((OSSet *)gPMHaltArray)
+    IORegistryIterator *    iter;
+    IORegistryEntry *       entry;
+    IOService *             node;
+    OSSet *                 inner;
+    PMHaltWorker *          workers[kPMHaltMaxWorkers];
+    AbsoluteTime            deadline;
+    unsigned int            totalNodes = 0;
+    unsigned int            depth;
+    unsigned int            rootDepth;
+    unsigned int            numWorkers;
+    unsigned int            count;
+    int                     waitResult;
+    void *                  baseFunc;
+    bool                    ok;
+
+    DLOG("%s event = %lx\n", __FUNCTION__, event);
+
+    baseFunc = OSMemberFunctionCast(void *, root, &IOService::systemWillShutdown);
+
+    // Iterate the entire PM tree starting from root
+
+    rootDepth = root->getDepth( gIOPowerPlane );
+    if (!rootDepth) goto done;
+
+    // debug - for repeated test runs
+    while (PMHaltWorker::metaClass->getInstanceCount())
+        IOSleep(1);
+
+    if (!gPMHaltArray)
     {
     {
-        DLOG("sync root domain %u->%u\n",
-            (uint32_t) getPowerState(), inPowerState);
-
-        // Root Domain is in a reduced R-state, and a HID tickle has
-        // requested a PM tree sync. Begin R-state transition.
+        gPMHaltArray = OSArray::withCapacity(40);
+        if (!gPMHaltArray) goto done;
+    }
+    else // debug
+        gPMHaltArray->flushCollection();
 
 
-        if (runStateIndex != kRStateNormal)
-        {
-            nextRunStateIndex = kRStateNormal;
-            setProperty(
-                kIOPMRootDomainRunStateKey,
-                (unsigned long long) kRStateNormal, 32);            
-        }
+    if (!gPMHaltLock)
+    {
+        gPMHaltLock = IOLockAlloc();
+        if (!gPMHaltLock) goto done;
     }
 
     }
 
-    if (*rdFlags & kServiceFlagGraphics)
+    if (!gPMHaltClientAcknowledgeKey)
     {
     {
-        DLOG("graphics device %s %u->%u (flags 0x%x)\n",
-            service->getName(), (uint32_t) service->getPowerState(),
-            inPowerState, changeFlags);
+        gPMHaltClientAcknowledgeKey =
+            OSSymbol::withCStringNoCopy("PMShutdown");
+        if (!gPMHaltClientAcknowledgeKey) goto done;
+    }
 
 
-        if (inPowerState == 0)
-        {
-            // Graphics device is powering down, apply limit preventing
-            // device from powering back up later unless we consent.
+    gPMHaltEvent = event;
 
 
-            if ((*rdFlags & kServiceFlagNoPowerUp) == 0)
-            {
-                *rdFlags |= kServiceFlagNoPowerUp;
-                DLOG("asserted power limit for %s\n",
-                    service->getName());
-            }
-        }
-        else
+    // Depth-first walk of PM plane
+
+    iter = IORegistryIterator::iterateOver(
+        root, gIOPowerPlane, kIORegistryIterateRecursively);
+
+    if (iter)
+    {
+        while ((entry = iter->getNextObject()))
         {
         {
-            uint32_t nextRunStateFlags;
+            node = OSDynamicCast(IOService, entry);
+            if (!node)
+                continue;
+
+            if (baseFunc ==
+                OSMemberFunctionCast(void *, node, &IOService::systemWillShutdown))
+                continue;
+
+            depth = node->getDepth( gIOPowerPlane );
+            if (depth <= rootDepth)
+                continue;
 
 
-            assert(nextRunStateIndex < kRStateCount);
-            nextRunStateFlags = gRStateFlags[nextRunStateIndex];
-        
-            // Graphics device is powering up. Release power limit at the
-            // did-change machine state.
+            ok = false;
 
 
-            if (changeFlags & kIOPMSynchronize)
+            // adjust to zero based depth
+            depth -= (rootDepth + 1);
+
+            // gPMHaltArray is an array of containers, each container
+            // refers to nodes with the same depth.
+
+            count = gPMHaltArray->getCount();
+            while (depth >= count)
             {
             {
-                if ((runStateFlags & kRStateFlagSuppressGraphics) &&
-                    ((nextRunStateFlags & kRStateFlagSuppressGraphics) == 0) &&
-                    (changeFlags & kIOPMDomainDidChange))
-                {
-                    // Woke up without graphics power, but
-                    // HID event has tickled display wrangler.
-                    *rdFlags &= ~kServiceFlagNoPowerUp;
-                    DLOG("removed power limit for %s\n",
-                        service->getName());
-                }
+                // expand array and insert placeholders
+                gPMHaltArray->setObject(PLACEHOLDER);
+                count++;
             }
             }
-            else if ((runStateFlags & kRStateFlagSuppressGraphics) == 0)
+            count = gPMHaltArray->getCount();
+            if (depth < count)
             {
             {
-                *rdFlags &= ~kServiceFlagNoPowerUp;
-            }
+                inner = (OSSet *)gPMHaltArray->getObject(depth);
+                if (inner == PLACEHOLDER)
+                {
+                    inner = OSSet::withCapacity(40);
+                    if (inner)
+                    {
+                        gPMHaltArray->replaceObject(depth, inner);
+                        inner->release();
+                    }
+                }
 
 
-            if (*rdFlags & kServiceFlagNoPowerUp)
-            {
-                DLOG("limited %s to power state 0\n",
-                    service->getName());
-                *powerState = 0;
+                // PM nodes that appear more than once in the tree will have
+                // the same depth, OSSet will refuse to add the node twice.
+                if (inner)
+                    ok = inner->setObject(node);
             }
             }
+            if (!ok)
+                DLOG("Skipped PM node %s\n", node->getName());
         }
         }
+        iter->release();
     }
     }
-}
 
 
+    // debug only
+    for (int i = 0; (inner = (OSSet *)gPMHaltArray->getObject(i)); i++)
+    {
+        count = 0;
+        if (inner != PLACEHOLDER)
+            count = inner->getCount();
+        DLOG("Nodes at depth %u = %u\n", i, count);
+    }
 
 
-//******************************************************************************
-// setMaintenanceWakeCalendar
-//
-//******************************************************************************
-
-IOReturn IOPMrootDomain::setMaintenanceWakeCalendar(
-    const IOPMCalendarStruct * calendar )
-{
-    OSData * data;
-    IOReturn ret;
+    // strip placeholders (not all depths are populated)
+    numWorkers = 0;
+    for (int i = 0; (inner = (OSSet *)gPMHaltArray->getObject(i)); )
+    {
+        if (inner == PLACEHOLDER)
+        {
+            gPMHaltArray->removeObject(i);
+            continue;
+        }
+        count = inner->getCount();
+        if (count > numWorkers)
+            numWorkers = count;
+        totalNodes += count;
+        i++;
+    }
 
 
-    if (!calendar)
-        return kIOReturnBadArgument;
-    
-    data = OSData::withBytesNoCopy((void *) calendar, sizeof(*calendar));
-    if (!data)
-        return kIOReturnNoMemory;
-    
-    ret = setPMSetting(gIOPMSettingMaintenanceWakeCalendarKey, data);
+    if (gPMHaltArray->getCount() == 0 || !numWorkers)
+        goto done;
 
 
-    data->release();
-    return ret;
-}
-#endif /* ROOT_DOMAIN_RUN_STATES */
+    gPMHaltBusyCount = 0;
+    gPMHaltIdleCount = 0;
+    gPMHaltDepth = gPMHaltArray->getCount() - 1;
 
 
+    // Create multiple workers (and threads)
 
 
-//******************************************************************************
-// sysPowerDownHandler
-//
-// Receives a notification when the RootDomain changes state. 
-//
-// Allows us to take action on system sleep, power down, and restart after
-// applications have received their power change notifications and replied,
-// but before drivers have powered down. We perform a vfs sync on power down.
-//******************************************************************************
+    if (numWorkers > kPMHaltMaxWorkers)
+        numWorkers = kPMHaltMaxWorkers;
 
 
-IOReturn IOPMrootDomain::sysPowerDownHandler( void * target, void * refCon,
-                                    UInt32 messageType, IOService * service,
-                                    void * messageArgument, vm_size_t argSize )
-{
-    IOReturn                             ret;
-    IOPowerStateChangeNotification      *params = (IOPowerStateChangeNotification *) messageArgument;
-    IOPMrootDomain                      *rootDomain = OSDynamicCast(IOPMrootDomain, service);
+    DLOG("PM nodes %u, maxDepth %u, workers %u\n",
+        totalNodes, gPMHaltArray->getCount(), numWorkers);
 
 
-    DLOG("sysPowerDownHandler message %x\n", (uint32_t) messageType);
+    for (unsigned int i = 0; i < numWorkers; i++)
+        workers[i] = PMHaltWorker::worker();
 
 
-    if(!rootDomain)
-        return kIOReturnUnsupported;
+    // Wait for workers to exhaust all available work
 
 
-    switch (messageType) {
-        case kIOMessageSystemWillSleep:
-            // Interested applications have been notified of an impending power
-            // change and have acked (when applicable).
-            // This is our chance to save whatever state we can before powering
-            // down.
-            // We call sync_internal defined in xnu/bsd/vfs/vfs_syscalls.c,
-            // via callout
+    IOLockLock(gPMHaltLock);
+    while (gPMHaltDepth >= 0)
+    {
+        clock_interval_to_deadline(1000, kMillisecondScale, &deadline);
 
 
-            // We will ack within 20 seconds
-            params->returnValue = 20 * 1000 * 1000;
-#if    HIBERNATION
-            if (gIOHibernateState)
-                params->returnValue += gIOHibernateFreeTime * 1000;    //add in time we could spend freeing pages
-#endif
+        waitResult = IOLockSleepDeadline(
+            gPMHaltLock, &gPMHaltDepth, deadline, THREAD_UNINT);
+        if (THREAD_TIMED_OUT == waitResult)
+        {
+            AbsoluteTime now;
+            clock_get_uptime(&now);
 
 
-            if ( ! OSCompareAndSwap( 0, 1, &gSleepOrShutdownPending ) )
+            IOLockUnlock(gPMHaltLock);
+            for (unsigned int i = 0 ; i < numWorkers; i++)
             {
             {
-                // Purposely delay the ack and hope that shutdown occurs quickly.
-                // Another option is not to schedule the thread and wait for
-                // ack timeout...
-                AbsoluteTime deadline;
-                clock_interval_to_deadline( 30, kSecondScale, &deadline );
-                thread_call_enter1_delayed( rootDomain->diskSyncCalloutEntry, 
-                                            (thread_call_param_t)params->powerRef,
-                                            deadline );
+                if (workers[i])
+                    PMHaltWorker::checkTimeout(workers[i], &now);
             }
             }
-            else
-                thread_call_enter1(rootDomain->diskSyncCalloutEntry, (thread_call_param_t)params->powerRef);
-            ret = kIOReturnSuccess;
-            break;
+            IOLockLock(gPMHaltLock);
+        }
+    }
+    IOLockUnlock(gPMHaltLock);
 
 
-        case kIOMessageSystemWillPowerOff:
-        case kIOMessageSystemWillRestart:
-            ret = kIOReturnUnsupported;
-            break;
+    // Release all workers
 
 
-        default:
-            ret = kIOReturnUnsupported;
-            break;
+    for (unsigned int i = 0; i < numWorkers; i++)
+    {
+        if (workers[i])
+            workers[i]->release();
+        // worker also retained by it's own thread
     }
     }
-    return ret;
+
+done:
+    DLOG("%s done\n", __FUNCTION__);
+    return;
 }
 
 }
 
-//******************************************************************************
-// publishSleepWakeUUID
-//
-// 
-//******************************************************************************
-void IOPMrootDomain::publishSleepWakeUUID( bool shouldPublish )
+// MARK: -
+// MARK: Kernel Assertion
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+IOPMDriverAssertionID IOPMrootDomain::createPMAssertion(
+    IOPMDriverAssertionType whichAssertionBits,
+    IOPMDriverAssertionLevel assertionLevel,
+    IOService *ownerService,
+    const char *ownerDescription)
 {
 {
-    if (shouldPublish) 
-    {
-        if (queuedSleepWakeUUIDString) 
-        {
-            if (OSCompareAndSwap(/*old*/ true, /*new*/ false, &gSleepWakeUUIDIsSet))
-            {
-                // Upon wake, it takes some time for userland to invalidate the
-                // UUID. If another sleep is initiated during that period, force
-                // a CLEAR message to balance the upcoming SET message.
+    IOReturn            ret;
+    IOPMDriverAssertionID     newAssertion;
 
 
-                messageClients( kIOPMMessageSleepWakeUUIDChange,
-                                kIOPMMessageSleepWakeUUIDCleared );
+    if (!pmAssertions)
+        return 0;
 
 
-                DLOG("SleepWake UUID forced clear\n");
-            }
+    ret = pmAssertions->createAssertion(whichAssertionBits, assertionLevel, ownerService, ownerDescription, &newAssertion);
 
 
-            setProperty(kIOPMSleepWakeUUIDKey, queuedSleepWakeUUIDString);
-            DLOG("SleepWake UUID published: %s\n", queuedSleepWakeUUIDString->getCStringNoCopy());
-            queuedSleepWakeUUIDString->release();
-            queuedSleepWakeUUIDString = NULL;
-            messageClients(kIOPMMessageSleepWakeUUIDChange, 
-                            kIOPMMessageSleepWakeUUIDSet);
-        }
-    } else {
-        if (OSCompareAndSwap(/*old*/ true, /*new*/ false, &gSleepWakeUUIDIsSet))
-        {
-            DLOG("SleepWake UUID cleared\n");
-            removeProperty(kIOPMSleepWakeUUIDKey);
-            messageClients(kIOPMMessageSleepWakeUUIDChange, 
-                            kIOPMMessageSleepWakeUUIDCleared);        
-        }
-    }
+    if (kIOReturnSuccess == ret)
+        return newAssertion;
+    else
+        return 0;
 }
 
 }
 
+IOReturn IOPMrootDomain::releasePMAssertion(IOPMDriverAssertionID releaseAssertion)
+{
+    if (!pmAssertions)
+        return kIOReturnInternalError;
 
 
-//******************************************************************************
-// displayWranglerNotification
-//
-// Receives a notification when the IODisplayWrangler changes state.
-//
-// Allows us to take action on display dim/undim.
-//
-// When the display sleeps we:
-// - Start the idle sleep timer
-// - set the quick spin down timeout
-//
-// On wake from display sleep:
-// - Cancel the idle sleep timer
-// - restore the user's chosen spindown timer from the "quick" spin down value
-//******************************************************************************
+    return pmAssertions->releaseAssertion(releaseAssertion);
+}
 
 
-IOReturn IOPMrootDomain::displayWranglerNotification(
-    void * target, void * refCon,
-    UInt32 messageType, IOService * service,
-    void * messageArgument, vm_size_t argSize )
+
+IOReturn IOPMrootDomain::setPMAssertionLevel(
+    IOPMDriverAssertionID assertionID,
+    IOPMDriverAssertionLevel assertionLevel)
 {
 {
-#if !NO_KERNEL_HID
-    int                                 displayPowerState;
-    IOPowerStateChangeNotification *    params =
-            (IOPowerStateChangeNotification *) messageArgument;
+    return pmAssertions->setAssertionLevel(assertionID, assertionLevel);
+}
 
 
-    if ((messageType != kIOMessageDeviceWillPowerOff) &&
-        (messageType != kIOMessageDeviceHasPoweredOn))
-        return kIOReturnUnsupported;
+IOPMDriverAssertionLevel IOPMrootDomain::getPMAssertionLevel(IOPMDriverAssertionType whichAssertion)
+{
+    IOPMDriverAssertionType       sysLevels;
 
 
-    ASSERT_GATED();
-    if (!gRootDomain)
-        return kIOReturnUnsupported;
+    if (!pmAssertions || whichAssertion == 0)
+        return kIOPMDriverAssertionLevelOff;
 
 
-    displayPowerState = params->stateNumber;
-    DLOG("DisplayWrangler message 0x%x, new power state %d\n",
-              (uint32_t) messageType, displayPowerState);
+    sysLevels = pmAssertions->getActivatedAssertions();
 
 
-    switch (messageType) {
-       case kIOMessageDeviceWillPowerOff:
+    // Check that every bit set in argument 'whichAssertion' is asserted
+    // in the aggregate bits.
+    if ((sysLevels & whichAssertion) == whichAssertion)
+        return kIOPMDriverAssertionLevelOn;
+    else
+        return kIOPMDriverAssertionLevelOff;
+}
 
 
-            // The display wrangler has dropped power because of idle display sleep
-            // or force system sleep.
-            //
-            // 4 Display ON
-            // 3 Display Dim
-            // 2 Display Sleep
-            // 1 Not visible to user
-            // 0 Not visible to user
+IOReturn IOPMrootDomain::setPMAssertionUserLevels(IOPMDriverAssertionType inLevels)
+{
+    if (!pmAssertions)
+        return kIOReturnNotFound;
 
 
-            if (gRootDomain->wranglerAsleep || (displayPowerState > 2))
-                break;
+    return pmAssertions->setUserAssertionLevels(inLevels);
+}
 
 
-            // Record the time the display wrangler went to sleep.
+bool IOPMrootDomain::serializeProperties( OSSerialize * s ) const
+{
+    if (pmAssertions)
+    {
+        pmAssertions->publishProperties();
+    }
+    return( IOService::serializeProperties(s) );
+}
 
 
-            gRootDomain->wranglerAsleep = true;
-            clock_get_uptime(&gRootDomain->wranglerSleepTime);
+OSObject * IOPMrootDomain::copyProperty( const char * aKey) const
+{
+    OSObject *obj = NULL;
+    obj = IOService::copyProperty(aKey);
 
 
-            // We start a timer here if the System Sleep timer is greater than the
-            // Display Sleep timer. We kick off this timer when the display sleeps.
-            //
-            // Note that, although Display Dim timings may change adaptively accordingly
-            // to the user's activity patterns, Display Sleep _always_ occurs at the
-            // specified interval since last user activity.
+    if (obj)  return obj;
 
 
-            if ( gRootDomain->extraSleepDelay )
-            {
-                gRootDomain->startIdleSleepTimer(gRootDomain->extraSleepDelay * 60);            
-            }
-            else if ( gRootDomain->sleepSlider )
-            {
-                // Accelerate disk spindown if system sleep and display sleep
-                // sliders are set to the same value (e.g. both set to 5 min),
-                // and display is about to go dark. Check that spin down timer
-                // is non-zero (zero = never spin down) and system sleep is
-                // not set to never sleep.
+    if (!strncmp(aKey, kIOPMSleepWakeWdogRebootKey,
+                        sizeof(kIOPMSleepWakeWdogRebootKey))) {
+        if (swd_flags & SWD_BOOT_BY_SW_WDOG)
+            return OSBoolean::withBoolean(true);
+        else
+            return OSBoolean::withBoolean(false);
 
 
-                gRootDomain->setQuickSpinDownTimeout();
-            }
+    }
 
 
-            break;
+    if (!strncmp(aKey, kIOPMSleepWakeWdogLogsValidKey,
+                        sizeof(kIOPMSleepWakeWdogLogsValidKey))) {
+        if (swd_flags & SWD_VALID_LOGS)
+            return OSBoolean::withBoolean(true);
+        else
+            return OSBoolean::withBoolean(false);
 
 
-        case kIOMessageDeviceHasPoweredOn:
+    }
 
 
-            // The display wrangler has powered on either because of user activity 
-            // or wake from sleep/doze.
+    /*
+     * XXX: We should get rid of "DesktopMode" property  when 'kAppleClamshellCausesSleepKey'
+     * is set properly in darwake from sleep. For that, kIOPMEnableClamshell msg has to be
+     * issued by DisplayWrangler on darkwake.
+     */
+    if (!strcmp(aKey, "DesktopMode")) {
+        if (desktopMode)
+            return OSBoolean::withBoolean(true);
+        else
+            return OSBoolean::withBoolean(false);
+    }
+    if (!strcmp(aKey, "DisplayIdleForDemandSleep")) {
+        if (displayIdleForDemandSleep) {
+            return OSBoolean::withBoolean(true);
+        }
+        else  {
+            return OSBoolean::withBoolean(false);
+        }
+    }
 
 
-            if ( 4 != displayPowerState )
-                break;
+    if (!strcmp(aKey, kIOPMDriverWakeEventsKey))
+    {
+        OSArray * array = 0;
+        WAKEEVENT_LOCK();
+        if (_systemWakeEventsArray && _systemWakeEventsArray->getCount())
+            array = OSArray::withArray(_systemWakeEventsArray);
+        WAKEEVENT_UNLOCK();
+        return array;
+    }
 
 
-            gRootDomain->wranglerAsleep = false;
-            gRootDomain->adjustPowerState();
-            gRootDomain->cancelIdleSleepTimer();
+    if (!strcmp(aKey, kIOPMSleepStatisticsAppsKey))
+    {
+        OSArray * array = 0;
+        IOLockLock(pmStatsLock);
+        if (pmStatsAppResponses && pmStatsAppResponses->getCount()) {
+            array = OSArray::withArray(pmStatsAppResponses);
+            pmStatsAppResponses->flushCollection();
+        }
+        IOLockUnlock(pmStatsLock);
+        return array;
+    }
 
 
-            // Change the spindown value back to the user's selection from our
-            // accelerated setting.
-            gRootDomain->restoreUserSpinDownTimeout();
+    return NULL;
+}
 
 
-            break;
+// MARK: -
+// MARK: Wake Event Reporting
 
 
-         default:
-             break;
-     }
-#endif
-     return kIOReturnUnsupported;
+void IOPMrootDomain::copyWakeReasonString( char * outBuf, size_t bufSize )
+{
+    WAKEEVENT_LOCK();
+    strlcpy(outBuf, gWakeReasonString, bufSize);
+    WAKEEVENT_UNLOCK();
 }
 
 }
 
-
 //******************************************************************************
 //******************************************************************************
-// displayWranglerPublished
+// acceptSystemWakeEvents
 //
 //
-// Receives a notification when the IODisplayWrangler is published.
-// When it's published we install a power state change handler.
+// Private control for the acceptance of driver wake event claims.
 //******************************************************************************
 
 //******************************************************************************
 
-bool IOPMrootDomain::displayWranglerPublished( 
-    void * target, 
-    void * refCon,
-    IOService * newService)
+void IOPMrootDomain::acceptSystemWakeEvents( bool accept )
 {
 {
-#if !NO_KERNEL_HID
-    if(!gRootDomain)
-        return false;
-
-    gRootDomain->wrangler = newService;
+    bool logWakeReason = false;
 
 
-    // we found the display wrangler, now install a handler
-    if( !gRootDomain->wrangler->registerInterest( gIOGeneralInterest, 
-                            &displayWranglerNotification, target, 0) ) 
+    WAKEEVENT_LOCK();
+    if (accept)
     {
     {
-        return false;
+        gWakeReasonString[0] = '\0';
+        if (!_systemWakeEventsArray)
+            _systemWakeEventsArray = OSArray::withCapacity(4);
+        if ((_acceptSystemWakeEvents = (_systemWakeEventsArray != 0)))
+            _systemWakeEventsArray->flushCollection();
     }
     }
-#endif
-    return true;
-}
+    else
+    {
+        _acceptSystemWakeEvents = false;
+    }
+    WAKEEVENT_UNLOCK();
 
 
+    if (logWakeReason)
+        MSG("system wake events:%s\n", gWakeReasonString);
+}
 
 //******************************************************************************
 
 //******************************************************************************
-// batteryPublished
+// claimSystemWakeEvent
 //
 //
-// Notification on battery class IOPowerSource appearance
+// For a driver to claim a device is the source/conduit of a system wake event.
 //******************************************************************************
 
 //******************************************************************************
 
-bool IOPMrootDomain::batteryPublished( 
-    void * target, 
-    void * root_domain,
-    IOService * resourceService )
-{    
-    // rdar://2936060&4435589    
-    // All laptops have dimmable LCD displays
-    // All laptops have batteries
-    // So if this machine has a battery, publish the fact that the backlight
-    // supports dimming.
-    ((IOPMrootDomain *)root_domain)->publishFeature("DisplayDims");
+void IOPMrootDomain::claimSystemWakeEvent(
+    IOService *     device,
+    IOOptionBits    flags,
+    const char *    reason,
+    OSObject *      details )
+{
+    const OSSymbol *    deviceName   = 0;
+    OSNumber *          deviceRegId  = 0;
+    OSNumber *          claimTime    = 0;
+    OSData *            flagsData    = 0;
+    OSString *          reasonString = 0;
+    OSDictionary *      d = 0;
+    uint64_t            timestamp;
+    bool                ok = false;
+
+    pmEventTimeStamp(&timestamp);
+
+    if (!device || !reason) return;
+
+    deviceName   = device->copyName(gIOServicePlane);
+    deviceRegId  = OSNumber::withNumber(device->getRegistryEntryID(), 64);
+    claimTime    = OSNumber::withNumber(timestamp, 64);
+    flagsData    = OSData::withBytes(&flags, sizeof(flags));
+    reasonString = OSString::withCString(reason);
+    d = OSDictionary::withCapacity(5 + (details ? 1 : 0));
+    if (!deviceName || !deviceRegId || !claimTime || !flagsData || !reasonString)
+        goto done;
+
+    d->setObject(gIONameKey, deviceName);
+    d->setObject(gIORegistryEntryIDKey, deviceRegId);
+    d->setObject(kIOPMWakeEventTimeKey, claimTime);
+    d->setObject(kIOPMWakeEventFlagsKey, flagsData);
+    d->setObject(kIOPMWakeEventReasonKey, reasonString);
+    if (details)
+        d->setObject(kIOPMWakeEventDetailsKey, details);
+
+    WAKEEVENT_LOCK();
+    if (!gWakeReasonSysctlRegistered)
+    {
+        // Lazy registration until the platform driver stops registering
+        // the same name.
+        gWakeReasonSysctlRegistered = true;
+    }
+    if (_acceptSystemWakeEvents)
+    {
+        ok = _systemWakeEventsArray->setObject(d);
+        if (gWakeReasonString[0] != '\0')
+            strlcat(gWakeReasonString, " ", sizeof(gWakeReasonString));
+        strlcat(gWakeReasonString, reason, sizeof(gWakeReasonString));
+    }
+    WAKEEVENT_UNLOCK();
 
 
-    return (true);
+done:
+    if (deviceName)   deviceName->release();
+    if (deviceRegId)  deviceRegId->release();
+    if (claimTime)    claimTime->release();
+    if (flagsData)    flagsData->release();
+    if (reasonString) reasonString->release();
+    if (d) d->release();
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+// MARK: -
+// MARK: PMSettingHandle
+
+OSDefineMetaClassAndStructors( PMSettingHandle, OSObject )
+
+void PMSettingHandle::free( void )
+{
+    if (pmso)
+    {
+        pmso->clientHandleFreed();
+        pmso->release();
+        pmso = 0;
+    }
+
+    OSObject::free();
 }
 
 }
 
+// MARK: -
+// MARK: PMSettingObject
+
+#undef super
+#define super OSObject
+OSDefineMetaClassAndFinalStructors( PMSettingObject, OSObject )
+
+/*
+ * Static constructor/initializer for PMSettingObject
+ */
+PMSettingObject *PMSettingObject::pmSettingObject(
+    IOPMrootDomain                      *parent_arg,
+    IOPMSettingControllerCallback       handler_arg,
+    OSObject                            *target_arg,
+    uintptr_t                           refcon_arg,
+    uint32_t                            supportedPowerSources,
+    const OSSymbol *                    settings[],
+    OSObject                            **handle_obj)
+{
+    uint32_t                            settingCount = 0;
+    PMSettingObject                     *pmso = 0;
+    PMSettingHandle                     *pmsh = 0;
+
+    if ( !parent_arg || !handler_arg || !settings || !handle_obj )
+        return NULL;
 
 
-//******************************************************************************
-// adjustPowerState
-//
-// Some condition that affects our wake/sleep/doze decision has changed.
-//
-// If the sleep slider is in the off position, we cannot sleep or doze.
-// If the enclosure is open, we cannot sleep or doze.
-// If the system is still booting, we cannot sleep or doze.
-//
-// In those circumstances, we prevent sleep and doze by holding power on with
-// changePowerStateToPriv(ON).
-//
-// If the above conditions do not exist, and also the sleep timer has expired,
-// we allow sleep or doze to occur with either changePowerStateToPriv(SLEEP) or
-// changePowerStateToPriv(DOZE) depending on whether or not we already know the
-// platform cannot sleep.
-//
-// In this case, sleep or doze will either occur immediately or at the next time
-// that no children are holding the system out of idle sleep via the 
-// kIOPMPreventIdleSleep flag in their power state arrays.
-//******************************************************************************
+    // count OSSymbol entries in NULL terminated settings array
+    while (settings[settingCount]) {
+        settingCount++;
+    }
+    if (0 == settingCount)
+        return NULL;
 
 
-void IOPMrootDomain::adjustPowerState( void )
-{
-    DLOG("adjustPowerState "
-        "PS %u, ASAP %d, SL %ld, AS %d, SB %d, SS %d, UD %d\n",
-        (uint32_t) getPowerState(), sleepASAP, sleepSlider,
-        allowSleep, systemBooting, systemShutdown, userDisabledAllSleep);
+    pmso = new PMSettingObject;
+    if (!pmso || !pmso->init())
+        goto fail;
+
+    pmsh = new PMSettingHandle;
+    if (!pmsh || !pmsh->init())
+        goto fail;
+
+    queue_init(&pmso->calloutQueue);
+    pmso->parent       = parent_arg;
+    pmso->func         = handler_arg;
+    pmso->target       = target_arg;
+    pmso->refcon       = refcon_arg;
+    pmso->settingCount = settingCount;
+
+    pmso->retain();     // handle holds a retain on pmso
+    pmsh->pmso = pmso;
+    pmso->pmsh = pmsh;
+
+    pmso->publishedFeatureID = (uint32_t *)IOMalloc(sizeof(uint32_t)*settingCount);
+    if (pmso->publishedFeatureID) {
+        for (unsigned int i=0; i<settingCount; i++) {
+            // Since there is now at least one listener to this setting, publish
+            // PM root domain support for it.
+            parent_arg->publishPMSetting( settings[i],
+                    supportedPowerSources, &pmso->publishedFeatureID[i] );
+        }
+    }
 
 
-    ASSERT_GATED();
+    *handle_obj = pmsh;
+    return pmso;
 
 
-    if ( (sleepSlider == 0) 
-        || !allowSleep 
-        || systemBooting 
-        || systemShutdown
-        || userDisabledAllSleep
-        || (runStateFlags & kRStateFlagDisableIdleSleep) )
-    {
-        changePowerStateToPriv(ON_STATE);
-    } else {
-        if ( sleepASAP ) 
-        {
-            /* Convenient place to run any code at idle sleep time
-             * IOPMrootDomain initiates an idle sleep here
-             *
-             * Set last sleep cause accordingly.
-             */
-            setProperty(kRootDomainSleepReasonKey, kIOPMIdleSleepKey);
+fail:
+    if (pmso) pmso->release();
+    if (pmsh) pmsh->release();
+    return NULL;
+}
 
 
-            tracePoint(kIOPMTracePointSleepStarted);
-    
-            sleepASAP = false;
-            changePowerStateToPriv(SLEEP_STATE);
+void PMSettingObject::free( void )
+{
+    if (publishedFeatureID) {
+        for (uint32_t i=0; i<settingCount; i++) {
+            if (publishedFeatureID[i]) {
+                parent->removePublishedFeature( publishedFeatureID[i] );
+            }
         }
         }
+
+        IOFree(publishedFeatureID, sizeof(uint32_t) * settingCount);
     }
     }
+
+    super::free();
 }
 
 }
 
-void IOPMrootDomain::pmStatsRecordEvent(
-    int                 eventIndex,
-    AbsoluteTime        timestamp)
+void PMSettingObject::dispatchPMSetting( const OSSymbol * type, OSObject * object )
 {
 {
-    bool        starting = eventIndex & kIOPMStatsEventStartFlag ? true:false;
-    bool        stopping = eventIndex & kIOPMStatsEventStopFlag ? true:false;
-    uint64_t    delta;
-    uint64_t    nsec;
+    (*func)(target, type, object, refcon);
+}
 
 
-    eventIndex &= ~(kIOPMStatsEventStartFlag | kIOPMStatsEventStopFlag);
+void PMSettingObject::clientHandleFreed( void )
+{
+    parent->deregisterPMSettingObject(this);
+}
 
 
-    absolutetime_to_nanoseconds(timestamp, &nsec);
+// MARK: -
+// MARK: PMAssertionsTracker
 
 
-    switch (eventIndex) {
-        case kIOPMStatsHibernateImageWrite:
-            if (starting)
-                pmStats.hibWrite.start = nsec;
-            else if (stopping)
-                pmStats.hibWrite.stop = nsec;
+//*********************************************************************************
+//*********************************************************************************
+//*********************************************************************************
+// class PMAssertionsTracker Implementation
 
 
-            if (stopping) {
-                delta = pmStats.hibWrite.stop - pmStats.hibWrite.start;
-                IOLog("PMStats: Hibernate write took %qd ms\n", delta/1000000ULL);
-            }
-            break;
-        case kIOPMStatsHibernateImageRead:
-            if (starting)
-                pmStats.hibRead.start = nsec;
-            else if (stopping)
-                pmStats.hibRead.stop = nsec;
+#define kAssertUniqueIDStart    500
 
 
-            if (stopping) {
-                delta = pmStats.hibRead.stop - pmStats.hibRead.start;
-                IOLog("PMStats: Hibernate read took %qd ms\n", delta/1000000ULL);
-            }
-            break;
+PMAssertionsTracker *PMAssertionsTracker::pmAssertionsTracker( IOPMrootDomain *rootDomain )
+{
+    PMAssertionsTracker    *myself;
+
+    myself = new PMAssertionsTracker;
+
+    if (myself) {
+        myself->init();
+        myself->owner = rootDomain;
+        myself->issuingUniqueID = kAssertUniqueIDStart;
+        myself->assertionsArray = OSArray::withCapacity(5);
+        myself->assertionsKernel = 0;
+        myself->assertionsUser = 0;
+        myself->assertionsCombined = 0;
+        myself->assertionsArrayLock = IOLockAlloc();
+        myself->tabulateProducerCount = myself->tabulateConsumerCount = 0;
+
+        if (!myself->assertionsArray || !myself->assertionsArrayLock)
+            myself = NULL;
     }
     }
+
+    return myself;
 }
 
 }
 
-/*
- * Appends a record of the application response to
- * IOPMrootDomain::pmStatsAppResponses
+/* tabulate
+ * - Update assertionsKernel to reflect the state of all
+ * assertions in the kernel.
+ * - Update assertionsCombined to reflect both kernel & user space.
  */
  */
-void IOPMrootDomain::pmStatsRecordApplicationResponse(
-       const OSSymbol          *response,
-       const char          *name,
-       int                 messageType,
-    uint32_t            delay_ms,
-    int                 app_pid)
+void PMAssertionsTracker::tabulate(void)
 {
 {
-    OSDictionary    *responseDescription    = NULL;
-    OSNumber        *delayNum               = NULL;
-    OSNumber        *pidNum                 = NULL;
-    OSNumber        *msgNum                 = NULL;
-    const OSSymbol  *appname;
-    const OSSymbol  *entryName;
-    OSObject        *entryType;
-    int             i;
+    int i;
+    int count;
+    PMAssertStruct      *_a = NULL;
+    OSData              *_d = NULL;
+
+    IOPMDriverAssertionType oldKernel = assertionsKernel;
+    IOPMDriverAssertionType oldCombined = assertionsCombined;
+
+    ASSERT_GATED();
 
 
-    if (!pmStatsAppResponses || pmStatsAppResponses->getCount() > 50)
+    assertionsKernel = 0;
+    assertionsCombined = 0;
+
+    if (!assertionsArray)
         return;
 
         return;
 
-    i = 0;
-    while ((responseDescription = (OSDictionary *) pmStatsAppResponses->getObject(i++)))
+    if ((count = assertionsArray->getCount()))
     {
     {
-        entryType = responseDescription->getObject(_statsResponseTypeKey);
-        entryName = (OSSymbol *) responseDescription->getObject(_statsNameKey);
-        if (entryName && (entryType == response) && entryName->isEqualTo(name))
+        for (i=0; i<count; i++)
         {
         {
-            OSNumber * entryValue;
-            entryValue = (OSNumber *) responseDescription->getObject(_statsTimeMSKey);
-            if (entryValue && (entryValue->unsigned32BitValue() < delay_ms))
-                entryValue->setValue(delay_ms);
-            return;
+            _d = OSDynamicCast(OSData, assertionsArray->getObject(i));
+            if (_d)
+            {
+                _a = (PMAssertStruct *)_d->getBytesNoCopy();
+                if (_a && (kIOPMDriverAssertionLevelOn == _a->level))
+                    assertionsKernel |= _a->assertionBits;
+            }
         }
     }
 
         }
     }
 
-    responseDescription = OSDictionary::withCapacity(5);
-    if (responseDescription) 
+    tabulateProducerCount++;
+    assertionsCombined = assertionsKernel | assertionsUser;
+
+    if ((assertionsKernel != oldKernel) ||
+        (assertionsCombined != oldCombined))
     {
     {
-        if (response) {
-            responseDescription->setObject(_statsResponseTypeKey, response);
-        }
-        
-        if (messageType != 0) {
-            msgNum = OSNumber::withNumber(messageType, 32);
-            if (msgNum) {
-                responseDescription->setObject(_statsMessageTypeKey, msgNum);
-                msgNum->release();
-            }
-        }
+        owner->evaluateAssertions(assertionsCombined, oldCombined);
+    }
+}
 
 
-        if (name && (strlen(name) > 0))
+void PMAssertionsTracker::publishProperties( void )
+{
+    OSArray             *assertionsSummary = NULL;
+
+    if (tabulateConsumerCount != tabulateProducerCount)
+    {
+        IOLockLock(assertionsArrayLock);
+
+        tabulateConsumerCount = tabulateProducerCount;
+
+        /* Publish the IOPMrootDomain property "DriverPMAssertionsDetailed"
+         */
+        assertionsSummary = copyAssertionsArray();
+        if (assertionsSummary)
         {
         {
-            appname = OSSymbol::withCString(name);
-            if (appname) {
-                responseDescription->setObject(_statsNameKey, appname);
-                appname->release();
-            }
+            owner->setProperty(kIOPMAssertionsDriverDetailedKey, assertionsSummary);
+            assertionsSummary->release();
         }
         }
-
-        if (app_pid != -1) {
-            pidNum = OSNumber::withNumber(app_pid, 32);
-            if (pidNum) {
-                responseDescription->setObject(_statsPIDKey, pidNum);
-                pidNum->release();
-            }
+        else
+        {
+            owner->removeProperty(kIOPMAssertionsDriverDetailedKey);
         }
 
         }
 
-        delayNum = OSNumber::withNumber(delay_ms, 32);
-        if (delayNum) {
-            responseDescription->setObject(_statsTimeMSKey, delayNum);
-            delayNum->release();
-        }
+        /* Publish the IOPMrootDomain property "DriverPMAssertions"
+         */
+        owner->setProperty(kIOPMAssertionsDriverKey, assertionsKernel, 64);
 
 
-        if (pmStatsAppResponses) {
-            pmStatsAppResponses->setObject(responseDescription);
+        IOLockUnlock(assertionsArrayLock);
+    }
+}
+
+PMAssertionsTracker::PMAssertStruct *PMAssertionsTracker::detailsForID(IOPMDriverAssertionID _id, int *index)
+{
+    PMAssertStruct      *_a = NULL;
+    OSData              *_d = NULL;
+    int                 found = -1;
+    int                 count = 0;
+    int                 i = 0;
+
+    if (assertionsArray
+        && (count = assertionsArray->getCount()))
+    {
+        for (i=0; i<count; i++)
+        {
+            _d = OSDynamicCast(OSData, assertionsArray->getObject(i));
+            if (_d)
+            {
+                _a = (PMAssertStruct *)_d->getBytesNoCopy();
+                if (_a && (_id == _a->id)) {
+                    found = i;
+                    break;
+                }
+            }
         }
         }
+    }
 
 
-        responseDescription->release();
+    if (-1 == found) {
+        return NULL;
+    } else {
+        if (index)
+            *index = found;
+        return _a;
     }
     }
-    return;
 }
 
 }
 
+/* PMAssertionsTracker::handleCreateAssertion
+ * Perform assertion work on the PM workloop. Do not call directly.
+ */
+IOReturn PMAssertionsTracker::handleCreateAssertion(OSData *newAssertion)
+{
+    ASSERT_GATED();
 
 
-//******************************************************************************
-// TracePoint support
-//
-//******************************************************************************
+    if (newAssertion)
+    {
+        IOLockLock(assertionsArrayLock);
+        assertionsArray->setObject(newAssertion);
+        IOLockUnlock(assertionsArrayLock);
+        newAssertion->release();
 
 
-#define kIOPMRegisterNVRAMTracePointHandlerKey \
-               "IOPMRegisterNVRAMTracePointHandler"
+        tabulate();
+    }
+    return kIOReturnSuccess;
+}
 
 
-IOReturn IOPMrootDomain::callPlatformFunction(
-    const OSSymbol * functionName,
-    bool waitForFunction,
-    void * param1, void * param2,
-    void * param3, void * param4 )
+/* PMAssertionsTracker::createAssertion
+ * createAssertion allocates memory for a new PM assertion, and affects system behavior, if
+ * appropiate.
+ */
+IOReturn PMAssertionsTracker::createAssertion(
+    IOPMDriverAssertionType which,
+    IOPMDriverAssertionLevel level,
+    IOService *serviceID,
+    const char *whoItIs,
+    IOPMDriverAssertionID *outID)
 {
 {
-    if (pmTracer && functionName &&
-        functionName->isEqualTo(kIOPMRegisterNVRAMTracePointHandlerKey) &&
-        !pmTracer->tracePointHandler && !pmTracer->tracePointTarget)
+    OSData          *dataStore = NULL;
+    PMAssertStruct  track;
+
+    // Warning: trillions and trillions of created assertions may overflow the unique ID.
+    track.id = OSIncrementAtomic64((SInt64*) &issuingUniqueID);
+    track.level = level;
+    track.assertionBits = which;
+    track.ownerString = whoItIs ? OSSymbol::withCString(whoItIs):0;
+    track.ownerService = serviceID;
+    track.registryEntryID = serviceID ? serviceID->getRegistryEntryID():0;
+    track.modifiedTime = 0;
+    pmEventTimeStamp(&track.createdTime);
+
+    dataStore = OSData::withBytes(&track, sizeof(PMAssertStruct));
+    if (!dataStore)
     {
     {
-        uint32_t    tracePointPhases, tracePointPCI;
-               uint64_t        statusCode;
+        if (track.ownerString)
+            track.ownerString->release();
+        return kIOReturnNoMemory;
+    }
 
 
-        pmTracer->tracePointHandler = (IOPMTracePointHandler) param1;
-        pmTracer->tracePointTarget  = (void *) param2;
-        tracePointPCI                          = (uint32_t)(uintptr_t) param3;
-        tracePointPhases                       = (uint32_t)(uintptr_t) param4;
-        statusCode = (((uint64_t)tracePointPCI) << 32) | tracePointPhases;
-               if ((tracePointPhases >> 24) != kIOPMTracePointSystemUp)
-        {
-            LOG("Sleep failure code 0x%08x 0x%08x\n",
-                tracePointPCI, tracePointPhases);
-        }
-               setProperty(kIOPMSleepWakeFailureCodeKey, statusCode, 64);
-        pmTracer->tracePointHandler( pmTracer->tracePointTarget, 0, 0 );
+    *outID = track.id;
 
 
-        return kIOReturnSuccess;
+    if (owner && owner->pmPowerStateQueue) {
+        owner->pmPowerStateQueue->submitPowerEvent(kPowerEventAssertionCreate, (void *)dataStore);
     }
 
     }
 
-    return super::callPlatformFunction(
-        functionName, waitForFunction, param1, param2, param3, param4);
+    return kIOReturnSuccess;
 }
 
 }
 
-void IOPMrootDomain::tracePoint( uint8_t point )
+/* PMAssertionsTracker::handleReleaseAssertion
+ * Runs in PM workloop. Do not call directly.
+ */
+IOReturn PMAssertionsTracker::handleReleaseAssertion(
+    IOPMDriverAssertionID _id)
 {
 {
-    pmTracer->tracePoint(point);
-}
+    ASSERT_GATED();
 
 
-//******************************************************************************
-// PMTraceWorker Class
-//
-//******************************************************************************
+    int             index;
+    PMAssertStruct  *assertStruct = detailsForID(_id, &index);
 
 
-#undef super
-#define super OSObject
-OSDefineMetaClassAndStructors(PMTraceWorker, OSObject)
+    if (!assertStruct)
+        return kIOReturnNotFound;
 
 
-#define kPMBestGuessPCIDevicesCount     25
-#define kPMMaxRTCBitfieldSize           32
+    IOLockLock(assertionsArrayLock);
+    if (assertStruct->ownerString)
+        assertStruct->ownerString->release();
 
 
-PMTraceWorker *PMTraceWorker::tracer(IOPMrootDomain *owner)
+    assertionsArray->removeObject(index);
+    IOLockUnlock(assertionsArrayLock);
+
+    tabulate();
+    return kIOReturnSuccess;
+}
+
+/* PMAssertionsTracker::releaseAssertion
+ * Releases an assertion and affects system behavior if appropiate.
+ * Actual work happens on PM workloop.
+ */
+IOReturn PMAssertionsTracker::releaseAssertion(
+    IOPMDriverAssertionID _id)
 {
 {
-    PMTraceWorker           *me;
-    
-    me = OSTypeAlloc( PMTraceWorker );
-    if (!me || !me->init())
-    {
-        return NULL;
+    if (owner && owner->pmPowerStateQueue) {
+        owner->pmPowerStateQueue->submitPowerEvent(kPowerEventAssertionRelease, 0, _id);
     }
     }
+    return kIOReturnSuccess;
+}
 
 
-    DLOG("PMTraceWorker %p\n", me);
+/* PMAssertionsTracker::handleSetAssertionLevel
+ * Runs in PM workloop. Do not call directly.
+ */
+IOReturn PMAssertionsTracker::handleSetAssertionLevel(
+    IOPMDriverAssertionID    _id,
+    IOPMDriverAssertionLevel _level)
+{
+    PMAssertStruct      *assertStruct = detailsForID(_id, NULL);
 
 
-    // Note that we cannot instantiate the PCI device -> bit mappings here, since
-    // the IODeviceTree has not yet been created by IOPlatformExpert. We create
-    // this dictionary lazily.
-    me->owner = owner;
-    me->pciDeviceBitMappings = NULL;
-    me->pciMappingLock = IOLockAlloc();
-    me->tracePhase = kIOPMTracePointSystemUp;
-    me->loginWindowPhase = 0;
-    me->pciBusyBitMask = 0;
-    return me;
+    ASSERT_GATED();
+
+    if (!assertStruct) {
+        return kIOReturnNotFound;
+    }
+
+    IOLockLock(assertionsArrayLock);
+    pmEventTimeStamp(&assertStruct->modifiedTime);
+    assertStruct->level = _level;
+    IOLockUnlock(assertionsArrayLock);
+
+    tabulate();
+    return kIOReturnSuccess;
 }
 
 }
 
-void PMTraceWorker::RTC_TRACE(void)
+/* PMAssertionsTracker::setAssertionLevel
+ */
+IOReturn PMAssertionsTracker::setAssertionLevel(
+    IOPMDriverAssertionID    _id,
+    IOPMDriverAssertionLevel _level)
 {
 {
-       if (tracePointHandler && tracePointTarget)
-       {
-               uint32_t    wordA;
-
-               wordA = tracePhase;                     // destined for bits 24-31
-               wordA <<= 8;
-               wordA |= loginWindowPhase;      // destined for bits 16-23
-               wordA <<= 16;
+    if (owner && owner->pmPowerStateQueue) {
+        owner->pmPowerStateQueue->submitPowerEvent(kPowerEventAssertionSetLevel,
+                (void *)(uintptr_t)_level, _id);
+    }
 
 
-        tracePointHandler( tracePointTarget, pciBusyBitMask, wordA );
-               DLOG("RTC_TRACE wrote 0x%08x 0x%08x\n", pciBusyBitMask, wordA);
-       }
+    return kIOReturnSuccess;
 }
 
 }
 
-int PMTraceWorker::recordTopLevelPCIDevice(IOService * pciDevice)
+IOReturn PMAssertionsTracker::handleSetUserAssertionLevels(void * arg0)
 {
 {
-    const OSSymbol *    deviceName;
-    int                 index = -1;
+    IOPMDriverAssertionType new_user_levels = *(IOPMDriverAssertionType *) arg0;
 
 
-    IOLockLock(pciMappingLock);
+    ASSERT_GATED();
 
 
-    if (!pciDeviceBitMappings)
+    if (new_user_levels != assertionsUser)
     {
     {
-        pciDeviceBitMappings = OSArray::withCapacity(kPMBestGuessPCIDevicesCount);
-        if (!pciDeviceBitMappings)
-            goto exit;
+        assertionsUser = new_user_levels;
+        DLOG("assertionsUser 0x%llx\n", assertionsUser);
     }
 
     }
 
-    // Check for bitmask overflow.
-    if (pciDeviceBitMappings->getCount() >= kPMMaxRTCBitfieldSize)
+    tabulate();
+    return kIOReturnSuccess;
+}
+
+IOReturn PMAssertionsTracker::setUserAssertionLevels(
+    IOPMDriverAssertionType new_user_levels)
+{
+    if (gIOPMWorkLoop) {
+        gIOPMWorkLoop->runAction(
+            OSMemberFunctionCast(
+                IOWorkLoop::Action,
+                this,
+                &PMAssertionsTracker::handleSetUserAssertionLevels),
+            this,
+            (void *) &new_user_levels, 0, 0, 0);
+    }
+
+    return kIOReturnSuccess;
+}
+
+
+OSArray *PMAssertionsTracker::copyAssertionsArray(void)
+{
+    int count;
+    int i;
+    OSArray     *outArray = NULL;
+
+    if (!assertionsArray ||
+        (0 == (count = assertionsArray->getCount())) ||
+        (NULL == (outArray = OSArray::withCapacity(count))))
+    {
         goto exit;
         goto exit;
+    }
+
+    for (i=0; i<count; i++)
+    {
+        PMAssertStruct  *_a = NULL;
+        OSData          *_d = NULL;
+        OSDictionary    *details = NULL;
+
+        _d = OSDynamicCast(OSData, assertionsArray->getObject(i));
+        if (_d && (_a = (PMAssertStruct *)_d->getBytesNoCopy()))
+        {
+            OSNumber        *_n = NULL;
+
+            details = OSDictionary::withCapacity(7);
+            if (!details)
+                continue;
+
+            outArray->setObject(details);
+            details->release();
+
+            _n = OSNumber::withNumber(_a->id, 64);
+            if (_n) {
+                details->setObject(kIOPMDriverAssertionIDKey, _n);
+                _n->release();
+            }
+            _n = OSNumber::withNumber(_a->createdTime, 64);
+            if (_n) {
+                details->setObject(kIOPMDriverAssertionCreatedTimeKey, _n);
+                _n->release();
+            }
+            _n = OSNumber::withNumber(_a->modifiedTime, 64);
+            if (_n) {
+                details->setObject(kIOPMDriverAssertionModifiedTimeKey, _n);
+                _n->release();
+            }
+            _n = OSNumber::withNumber((uintptr_t)_a->registryEntryID, 64);
+            if (_n) {
+                details->setObject(kIOPMDriverAssertionRegistryEntryIDKey, _n);
+                _n->release();
+            }
+            _n = OSNumber::withNumber(_a->level, 64);
+            if (_n) {
+                details->setObject(kIOPMDriverAssertionLevelKey, _n);
+                _n->release();
+            }
+            _n = OSNumber::withNumber(_a->assertionBits, 64);
+            if (_n) {
+                details->setObject(kIOPMDriverAssertionAssertedKey, _n);
+                _n->release();
+            }
 
 
-    if ((deviceName = pciDevice->copyName()) &&
-        (pciDeviceBitMappings->getNextIndexOfObject(deviceName, 0) == (unsigned int)-1) &&
-        pciDeviceBitMappings->setObject(deviceName))
-    {
-        index = pciDeviceBitMappings->getCount() - 1;
-        DLOG("PMTrace PCI array: set object %s => %d\n",
-            deviceName->getCStringNoCopy(), index);
+            if (_a->ownerString) {
+                details->setObject(kIOPMDriverAssertionOwnerStringKey, _a->ownerString);
+            }
+        }
     }
     }
-    if (deviceName)
-        deviceName->release();
-    if (!addedToRegistry && (index >= 0))
-        addedToRegistry = owner->setProperty("PCITopLevel", this);
 
 exit:
 
 exit:
-    IOLockUnlock(pciMappingLock);
-    return index;
+    return outArray;
 }
 
 }
 
-bool PMTraceWorker::serialize(OSSerialize *s) const
+IOPMDriverAssertionType PMAssertionsTracker::getActivatedAssertions(void)
 {
 {
-    bool ok = false;
-    if (pciDeviceBitMappings)
-    {
-        IOLockLock(pciMappingLock);
-        ok = pciDeviceBitMappings->serialize(s);
-        IOLockUnlock(pciMappingLock);
-    }
-    return ok;
+    return assertionsCombined;
 }
 
 }
 
-void PMTraceWorker::tracePoint(uint8_t phase)
+IOPMDriverAssertionLevel PMAssertionsTracker::getAssertionLevel(
+    IOPMDriverAssertionType type)
 {
 {
-    tracePhase = phase;
-
-    DLOG("IOPMrootDomain: trace point 0x%02x\n", tracePhase);
-    RTC_TRACE();
+    if (type && ((type & assertionsKernel) == assertionsKernel))
+    {
+        return kIOPMDriverAssertionLevelOn;
+    } else {
+        return kIOPMDriverAssertionLevelOff;
+    }
 }
 
 }
 
-void PMTraceWorker::traceLoginWindowPhase(uint8_t phase)
-{
-    loginWindowPhase = phase;
+//*********************************************************************************
+//*********************************************************************************
+//*********************************************************************************
 
 
-    DLOG("IOPMrootDomain: loginwindow tracepoint 0x%02x\n", loginWindowPhase);
-    RTC_TRACE();
-}
 
 
-void PMTraceWorker::tracePCIPowerChange(
-       change_t type, IOService *service, uint32_t changeFlags, uint32_t bitNum)
+static void pmEventTimeStamp(uint64_t *recordTS)
 {
 {
-    uint32_t   bitMask;
-       uint32_t        expectedFlag;
+    clock_sec_t     tsec;
+    clock_usec_t    tusec;
 
 
-       // Ignore PCI changes outside of system sleep/wake.
-    if ((kIOPMTracePointSystemSleepDriversPhase != tracePhase) &&
-        (kIOPMTracePointSystemWakeDriversPhase  != tracePhase))
+    if (!recordTS)
         return;
 
         return;
 
-       // Only record the WillChange transition when going to sleep,
-       // and the DidChange on the way up.
-       changeFlags &= (kIOPMDomainWillChange | kIOPMDomainDidChange);
-       expectedFlag = (kIOPMTracePointSystemSleepDriversPhase == tracePhase) ?
-                                       kIOPMDomainWillChange : kIOPMDomainDidChange;
-       if (changeFlags != expectedFlag)
-               return;
+    // We assume tsec fits into 32 bits; 32 bits holds enough
+    // seconds for 136 years since the epoch in 1970.
+    clock_get_calendar_microtime(&tsec, &tusec);
 
 
-    // Mark this device off in our bitfield
-    if (bitNum < kPMMaxRTCBitfieldSize)
-    {
-        bitMask = (1 << bitNum);
 
 
-        if (kPowerChangeStart == type)
-        {
-            pciBusyBitMask |= bitMask;
-            DLOG("PMTrace: Device %s started  - bit %2d mask 0x%08x => 0x%08x\n",
-                service->getName(), bitNum, bitMask, pciBusyBitMask);
-        }
-        else
-        {
-            pciBusyBitMask &= ~bitMask;
-            DLOG("PMTrace: Device %s finished - bit %2d mask 0x%08x => 0x%08x\n",
-                service->getName(), bitNum, bitMask, pciBusyBitMask);
-        }
+    // Pack the sec & microsec calendar time into a uint64_t, for fun.
+    *recordTS = 0;
+    *recordTS |= (uint32_t)tusec;
+    *recordTS |= ((uint64_t)tsec << 32);
 
 
-        RTC_TRACE();        
-    }
+    return;
 }
 
 }
 
+// MARK: -
+// MARK: IORootParent
 
 
-//******************************************************************************
-// PMHaltWorker Class
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+OSDefineMetaClassAndFinalStructors(IORootParent, IOService)
+
+// The reason that root domain needs a root parent is to facilitate demand
+// sleep, since a power change from the root parent cannot be vetoed.
 //
 //
-//******************************************************************************
+// The above statement is no longer true since root domain now performs
+// demand sleep using overrides. But root parent remains to avoid changing
+// the power tree stacking. Root parent is parked at the max power state.
 
 
-static unsigned int            gPMHaltBusyCount;
-static unsigned int            gPMHaltIdleCount;
-static int                             gPMHaltDepth;
-static unsigned long    gPMHaltEvent;
-static IOLock *                        gPMHaltLock  = 0;
-static OSArray *               gPMHaltArray = 0;
-static const OSSymbol * gPMHaltClientAcknowledgeKey = 0;
 
 
-PMHaltWorker * PMHaltWorker::worker( void )
+static IOPMPowerState patriarchPowerStates[2] =
 {
 {
-       PMHaltWorker *  me;
-       IOThread                thread;
-
-       do {
-               me = OSTypeAlloc( PMHaltWorker );
-               if (!me || !me->init())
-                       break;
+    {1,0,ON_POWER,0,0,0,0,0,0,0,0,0},
+    {1,0,ON_POWER,0,0,0,0,0,0,0,0,0},
+};
 
 
-               me->lock = IOLockAlloc();
-               if (!me->lock)
-                       break;
+void IORootParent::initialize( void )
+{
+}
 
 
-               DLOG("PMHaltWorker %p\n", me);
-               me->retain();   // thread holds extra retain
-               if (KERN_SUCCESS != kernel_thread_start(&PMHaltWorker::main, (void *) me, &thread))
-               {
-                       me->release();
-                       break;
-               }
-               thread_deallocate(thread);
-               return me;
+bool IORootParent::start( IOService * nub )
+{
+    IOService::start(nub);
+    attachToParent( getRegistryRoot(), gIOPowerPlane );
+    PMinit();
+    registerPowerDriver(this, patriarchPowerStates, 2);
+    makeUsable();
+    return true;
+}
 
 
-       } while (false);
+void IORootParent::shutDownSystem( void )
+{
+}
 
 
-       if (me) me->release();
-       return 0;
+void IORootParent::restartSystem( void )
+{
 }
 
 }
 
-void PMHaltWorker::free( void )
+void IORootParent::sleepSystem( void )
 {
 {
-       DLOG("PMHaltWorker free %p\n", this);
-       if (lock)
-       {
-               IOLockFree(lock);
-               lock = 0;
-       }
-       return OSObject::free();
 }
 
 }
 
-void PMHaltWorker::main( void * arg, wait_result_t waitResult )
+void IORootParent::dozeSystem( void )
 {
 {
-       PMHaltWorker * me = (PMHaltWorker *) arg;
-
-       IOLockLock( gPMHaltLock );
-       gPMHaltBusyCount++;
-       me->depth = gPMHaltDepth;
-       IOLockUnlock( gPMHaltLock );
-
-       while (me->depth >= 0)
-       {
-               PMHaltWorker::work( me );
-
-               IOLockLock( gPMHaltLock );
-               if (++gPMHaltIdleCount >= gPMHaltBusyCount)
-               {
-                       // This is the last thread to finish work on this level,
-                       // inform everyone to start working on next lower level.
-                       gPMHaltDepth--;
-                       me->depth = gPMHaltDepth;
-                       gPMHaltIdleCount = 0;
-                       thread_wakeup((event_t) &gPMHaltIdleCount);
-               }
-               else
-               {
-                       // One or more threads are still working on this level,
-                       // this thread must wait.
-                       me->depth = gPMHaltDepth - 1;
-                       do {
-                               IOLockSleep(gPMHaltLock, &gPMHaltIdleCount, THREAD_UNINT);
-                       } while (me->depth != gPMHaltDepth);
-               }
-               IOLockUnlock( gPMHaltLock );
-       }
+}
 
 
-       // No more work to do, terminate thread
-       DLOG("All done for worker: %p (visits = %u)\n", me, me->visits);
-       thread_wakeup( &gPMHaltDepth );
-       me->release();
+void IORootParent::sleepToDoze( void )
+{
 }
 
 }
 
-void PMHaltWorker::work( PMHaltWorker * me )
+void IORootParent::wakeSystem( void )
 {
 {
-       IOService *             service;
-       OSSet *                 inner;
-       AbsoluteTime    startTime;
-       UInt32                  deltaTime;
-       bool                    timeout;
-
-       while (true)
-       {
-               service = 0;
-               timeout = false;
-
-               // Claim an unit of work from the shared pool
-               IOLockLock( gPMHaltLock );
-               inner = (OSSet *)gPMHaltArray->getObject(me->depth);
-               if (inner)
-               {
-                       service = (IOService *)inner->getAnyObject();
-                       if (service)
-                       {
-                               service->retain();
-                               inner->removeObject(service);
-                       }
-               }
-               IOLockUnlock( gPMHaltLock );    
-               if (!service)
-                       break;  // no more work at this depth
-
-               clock_get_uptime(&startTime);
-
-               if (!service->isInactive() &&
-                       service->setProperty(gPMHaltClientAcknowledgeKey, me))
-               {
-                       IOLockLock(me->lock);
-                       me->startTime = startTime;
-                       me->service   = service;
-                       me->timeout   = false;
-                       IOLockUnlock(me->lock);
-
-                       service->systemWillShutdown( gPMHaltEvent );
-
-                       // Wait for driver acknowledgement
-                       IOLockLock(me->lock);
-                       while (service->getProperty(gPMHaltClientAcknowledgeKey))
-                       {
-                               IOLockSleep(me->lock, me, THREAD_UNINT);                        
-                       }
-                       me->service = 0;
-                       timeout = me->timeout;
-                       IOLockUnlock(me->lock);
-               }
-
-               deltaTime = computeDeltaTimeMS(&startTime);
-               if ((deltaTime > kPMHaltTimeoutMS) || timeout ||
-                       (gIOKitDebug & (kIOLogDebugPower | kIOLogPMRootDomain)))
-               {
-                       KLOG("%s driver %s (%p) took %u ms\n",
-                               (gPMHaltEvent == kIOMessageSystemWillPowerOff) ?
-                                       "PowerOff" : "Restart",
-                               service->getName(), service,
-                               (uint32_t) deltaTime );
-               }
-
-               service->release();
-               me->visits++;
-       }
 }
 
 }
 
-void PMHaltWorker::checkTimeout( PMHaltWorker * me, AbsoluteTime * now )
+OSObject * IORootParent::copyProperty( const char * aKey) const
 {
 {
-       UInt64                  nano;
-       AbsoluteTime    startTime;
-       AbsoluteTime    endTime;
-
-       endTime = *now;
-
-       IOLockLock(me->lock);
-       if (me->service && !me->timeout)
-       {
-               startTime = me->startTime;
-               nano = 0;
-           if (CMP_ABSOLUTETIME(&endTime, &startTime) > 0)
-           {
-                       SUB_ABSOLUTETIME(&endTime, &startTime);
-                       absolutetime_to_nanoseconds(endTime, &nano);
-           }
-               if (nano > 3000000000ULL)
-               {
-                       me->timeout = true;
-                       LOG("%s still waiting on %s\n",
-                               (gPMHaltEvent == kIOMessageSystemWillPowerOff) ?
-                                       "PowerOff" : "Restart",
-                               me->service->getName());
-               }
-       }
-       IOLockUnlock(me->lock);
+    return (IOService::copyProperty(aKey));
 }
 
 
 }
 
 
-//******************************************************************************
-// acknowledgeSystemWillShutdown
-//
-// Acknowledgement from drivers that they have prepared for shutdown/restart.
-//******************************************************************************
+#if defined(__i386__) || defined(__x86_64__)
+IOReturn IOPMrootDomain::restartWithStackshot()
+{
+    if ((swd_flags & SWD_WDOG_ENABLED) == 0)
+        return kIOReturnError;
 
 
-void IOPMrootDomain::acknowledgeSystemWillShutdown( IOService * from )
+    takeStackshot(true, true);
+
+    return kIOReturnSuccess;
+}
+
+void IOPMrootDomain::sleepWakeDebugTrig(bool wdogTrigger)
 {
 {
-       PMHaltWorker *  worker;
-       OSObject *              prop;
-
-       if (!from)
-               return;
-
-       //DLOG("%s acknowledged\n", from->getName());
-       prop = from->copyProperty( gPMHaltClientAcknowledgeKey );
-       if (prop)
-       {
-               worker = (PMHaltWorker *) prop;
-               IOLockLock(worker->lock);
-               from->removeProperty( gPMHaltClientAcknowledgeKey );
-               thread_wakeup((event_t) worker);
-               IOLockUnlock(worker->lock);
-               worker->release();
-       }
-       else
-       {
-               DLOG("%s acknowledged without worker property\n",
-                       from->getName());
-       }
+    takeStackshot(wdogTrigger, false);
 }
 
 }
 
+void IOPMrootDomain::takeStackshot(bool wdogTrigger, bool isOSXWatchdog)
+{
+   swd_hdr *         hdr = NULL;
+   addr64_t          data[3];
+   uint32_t          wdog_panic = 0;
+   int               cnt = 0;
+   pid_t             pid = 0;
+   uint32_t          flags;
+
+   char *            dstAddr;
+   uint32_t          size;
+   uint32_t          bytesRemaining;
+   unsigned int      len;
+   OSString *        UUIDstring = NULL;
+   uint64_t          code;
+   IOMemoryMap *     logBufMap = NULL;
+
+   swd_stackshot_hdr *stackshotHdr = NULL;
+   if ( kIOSleepWakeWdogOff & gIOKitDebug )
+      return;
+
+   if (wdogTrigger) {
+       if (PE_parse_boot_argn("swd_panic", &wdog_panic, sizeof(wdog_panic)) &&
+                  (wdog_panic == 1)) {
+           // If boot-arg is set to panic on sleep/wake hang, call panic
+           panic("Sleep/Wake hang detected\n");
+           return;
+       }
+       else if (swd_flags & SWD_BOOT_BY_SW_WDOG) {
+           // If current boot is due to this watch dog trigger restart in previous boot,
+           // then don't trigger again until at least 1 successful sleep & wake.
+           sleepCnt = displayWakeCnt = 1;
+           if (!(sleepCnt && displayWakeCnt)) {
+               IOLog("Shutting down due to repeated Sleep/Wake failures\n");
+               PEHaltRestart(kPEHaltCPU);
+               return;
+           }
+       }
+
+   }
+
+   if (sleepWakeDebugIsWdogEnabled() == false)
+       return;
+
+   if (swd_buffer == NULL) {
+      sleepWakeDebugMemAlloc();
+      if (swd_buffer == NULL) return;
+   }
+
+   if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock))
+       return;
+
+
+   hdr = (swd_hdr *)swd_buffer;
+   memset(hdr->UUID, 0x20, sizeof(hdr->UUID));
+   if ((UUIDstring = OSDynamicCast(OSString, getProperty(kIOPMSleepWakeUUIDKey))) != NULL ) {
+
+      if (wdogTrigger || (!UUIDstring->isEqualTo(hdr->UUID))) {
+         const char *str = UUIDstring->getCStringNoCopy();
+         snprintf(hdr->UUID, sizeof(hdr->UUID), "UUID: %s", str);
+      }
+      else {
+         DLOG("Data for current UUID already exists\n");
+         goto exit;
+      }
+   }
+
+   dstAddr = (char*)hdr + hdr->spindump_offset;
+   bytesRemaining = SWD_BUF_SIZE - hdr->spindump_offset;
+
+   /* if AppleOSXWatchdog triggered the stackshot, set the flag in the heaer */
+   hdr->is_osx_watchdog = isOSXWatchdog;
+
+   DLOG("Taking snapshot. bytesRemaining: %d\n", bytesRemaining);
+
+   while (bytesRemaining > sizeof(swd_stackshot_hdr)) {
+
+       stackshotHdr = (swd_stackshot_hdr *)dstAddr;
+       stackshotHdr->magic = SWD_STACKSHOTHDR_MAGIC;
+       stackshotHdr->size = 0;
+       bytesRemaining -= sizeof(swd_stackshot_hdr);
+       dstAddr += sizeof(swd_stackshot_hdr);
+
+       if (isOSXWatchdog) {
+           pid = -1;
+           size = bytesRemaining;
+           flags = STACKSHOT_SAVE_LOADINFO | STACKSHOT_SAVE_KEXT_LOADINFO;
+       }
+       else if (cnt == 0) {
+           /* 
+            * Take stackshot of all process on first sample. Size is restricted
+            * to SWD_INITIAL_STACK_SIZE
+            */
+           pid = -1;
+           size = (bytesRemaining > SWD_INITIAL_STACK_SIZE) ? SWD_INITIAL_STACK_SIZE : bytesRemaining;
+           flags = STACKSHOT_SAVE_LOADINFO | STACKSHOT_SAVE_KEXT_LOADINFO|STACKSHOT_SAVE_KERNEL_FRAMES_ONLY;
+       }
+       else {
+           /* Take sample of kernel threads only */
+           pid = 0;
+           size = bytesRemaining;
+           flags = 0;
+       }
+
+       stack_snapshot_from_kernel(pid, dstAddr, size, flags, &stackshotHdr->size);
+
+       dstAddr += stackshotHdr->size;
+       bytesRemaining -= stackshotHdr->size;
+
+       DLOG("Sample: %d size: %d bytesRemaining: %d\n", cnt, stackshotHdr->size, bytesRemaining);
+       if ((stackshotHdr->size == 0) || (++cnt == 10))
+           break;
+       IOSleep(10); // 10 ms
+   }
+
+   hdr->spindump_size = (SWD_BUF_SIZE - bytesRemaining - hdr->spindump_offset);
+
+
+   memset(hdr->cps, 0x20, sizeof(hdr->cps));
+   snprintf(hdr->cps, sizeof(hdr->cps), "\ncps: %d", ((IOService*)this)->getPowerState());
+   code = pmTracer->getPMStatusCode();
+   memset(hdr->PMStatusCode, 0x20, sizeof(hdr->PMStatusCode));
+   snprintf(hdr->PMStatusCode, sizeof(hdr->PMStatusCode), "\nCode: %08x %08x",
+           (uint32_t)((code >> 32) & 0xffffffff), (uint32_t)(code & 0xffffffff));
+   memset(hdr->reason, 0x20, sizeof(hdr->reason));
+   snprintf(hdr->reason, sizeof(hdr->reason), "\nStackshot reason: Watchdog\n\n");
+
+
+   data[0] = round_page(sizeof(swd_hdr) + hdr->spindump_size);
+   /* Header & rootdomain log is constantly changing and  is not covered by CRC */
+   data[1] = hdr->crc = crc32(0, ((char*)swd_buffer+hdr->spindump_offset), hdr->spindump_size);
+   data[2] = kvtophys((vm_offset_t)swd_buffer);
+   len = sizeof(addr64_t)*3;
+   DLOG("bytes: 0x%llx crc:0x%llx paddr:0x%llx\n",
+         data[0], data[1], data[2]);
+
+   if (PEWriteNVRAMProperty(kIOSleepWakeDebugKey, data, len) == false)
+   {
+      DLOG("Failed to update nvram boot-args\n");
+      goto exit;
+   }
 
 
-//******************************************************************************
-// notifySystemShutdown
-//
-// Notify all objects in PM tree that system will shutdown or restart
-//******************************************************************************
+exit:
 
 
-static void
-notifySystemShutdown( IOService * root, unsigned long event )
+   gRootDomain->swd_lock = 0;
+
+   if (wdogTrigger) {
+      IOLog("Restarting to collect Sleep wake debug logs\n");
+      PEHaltRestart(kPERestartCPU);
+   }
+   else {
+     logBufMap = sleepWakeDebugRetrieve();
+      if (logBufMap) {
+          sleepWakeDebugDumpFromMem(logBufMap);
+          logBufMap->release();
+          logBufMap = 0;
+      }
+   }
+}
+
+void IOPMrootDomain::sleepWakeDebugMemAlloc( )
 {
 {
-#define PLACEHOLDER ((OSSet *)gPMHaltArray)
-       IORegistryIterator *    iter;
-       IORegistryEntry *               entry;
-       IOService *                             node;
-       OSSet *                                 inner;
-       PMHaltWorker *                  workers[kPMHaltMaxWorkers];
-       AbsoluteTime                    deadline;
-       unsigned int                    totalNodes = 0;
-       unsigned int                    depth;
-       unsigned int                    rootDepth;
-       unsigned int                    numWorkers;
-       unsigned int                    count;
-       int                                             waitResult;
-       void *                                  baseFunc;
-       bool                                    ok;
-
-       DLOG("%s event = %lx\n", __FUNCTION__, event);
-
-       baseFunc = OSMemberFunctionCast(void *, root, &IOService::systemWillShutdown);
-
-       // Iterate the entire PM tree starting from root
-
-       rootDepth = root->getDepth( gIOPowerPlane );
-       if (!rootDepth) goto done;
-
-       // debug - for repeated test runs
-       while (PMHaltWorker::metaClass->getInstanceCount())
-               IOSleep(1);
-
-       if (!gPMHaltArray)
-       {
-               gPMHaltArray = OSArray::withCapacity(40);
-               if (!gPMHaltArray) goto done;
-       }
-       else // debug
-               gPMHaltArray->flushCollection();
+    vm_size_t    size = SWD_BUF_SIZE;
 
 
-       if (!gPMHaltLock)
-       {
-               gPMHaltLock = IOLockAlloc();
-               if (!gPMHaltLock) goto done;
-       }
+    swd_hdr      *hdr = NULL;
 
 
-       if (!gPMHaltClientAcknowledgeKey)
-       {
-               gPMHaltClientAcknowledgeKey =
-                       OSSymbol::withCStringNoCopy("PMShutdown");
-               if (!gPMHaltClientAcknowledgeKey) goto done;
-       }
+    IOBufferMemoryDescriptor  *memDesc = NULL;
 
 
-       gPMHaltEvent = event;
-
-       // Depth-first walk of PM plane
-
-       iter = IORegistryIterator::iterateOver(
-               root, gIOPowerPlane, kIORegistryIterateRecursively);
-
-       if (iter)
-       {
-               while ((entry = iter->getNextObject()))
-               {
-                       node = OSDynamicCast(IOService, entry);
-                       if (!node)
-                               continue;
-
-                       if (baseFunc == 
-                               OSMemberFunctionCast(void *, node, &IOService::systemWillShutdown))
-                               continue;
-
-                       depth = node->getDepth( gIOPowerPlane );
-                       if (depth <= rootDepth)
-                               continue;
-
-                       ok = false;
-
-                       // adjust to zero based depth
-                       depth -= (rootDepth + 1);
-
-                       // gPMHaltArray is an array of containers, each container
-                       // refers to nodes with the same depth.
-
-                       count = gPMHaltArray->getCount();
-                       while (depth >= count)
-                       {
-                               // expand array and insert placeholders
-                               gPMHaltArray->setObject(PLACEHOLDER);
-                               count++;
-                       }
-                       count = gPMHaltArray->getCount();
-                       if (depth < count)
-                       {
-                               inner = (OSSet *)gPMHaltArray->getObject(depth);
-                               if (inner == PLACEHOLDER)
-                               {
-                                       inner = OSSet::withCapacity(40);
-                                       if (inner)
-                                       {
-                                               gPMHaltArray->replaceObject(depth, inner);
-                                               inner->release();
-                                       }
-                               }
-
-                               // PM nodes that appear more than once in the tree will have
-                               // the same depth, OSSet will refuse to add the node twice.
-                               if (inner)
-                                       ok = inner->setObject(node);
-                       }
-                       if (!ok)
-                               DLOG("Skipped PM node %s\n", node->getName());
-               }
-               iter->release();
-       }
 
 
-       // debug only
-       for (int i = 0; (inner = (OSSet *)gPMHaltArray->getObject(i)); i++)
-       {
-               count = 0;
-               if (inner != PLACEHOLDER)
-                       count = inner->getCount();
-               DLOG("Nodes at depth %u = %u\n", i, count);
-       }
+    if ( kIOSleepWakeWdogOff & gIOKitDebug )
+      return;
 
 
-       // strip placeholders (not all depths are populated)
-       numWorkers = 0;
-       for (int i = 0; (inner = (OSSet *)gPMHaltArray->getObject(i)); )
-       {
-               if (inner == PLACEHOLDER)
-               {
-                       gPMHaltArray->removeObject(i);
-                       continue;
-               }
-               count = inner->getCount();
-               if (count > numWorkers)
-                       numWorkers = count;
-               totalNodes += count;
-               i++;
-       }
+    if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock))
+       return;
 
 
-       if (gPMHaltArray->getCount() == 0 || !numWorkers)
-               goto done;
+    // Try allocating above 4GB. If that fails, try at 2GB
+    memDesc = IOBufferMemoryDescriptor::inTaskWithPhysicalMask(
+                            kernel_task, kIOMemoryPhysicallyContiguous|kIOMemoryMapperNone,
+                            size, 0xFFFFFFFF00000000ULL);
+    if (!memDesc) {
+       memDesc = IOBufferMemoryDescriptor::inTaskWithPhysicalMask(
+                            kernel_task, kIOMemoryPhysicallyContiguous|kIOMemoryMapperNone,
+                            size, 0xFFFFFFFF10000000ULL);
+    }
+
+    if (memDesc == NULL)
+    {
+      DLOG("Failed to allocate Memory descriptor for sleepWake debug\n");
+      goto exit;
+    }
 
 
-       gPMHaltBusyCount = 0;
-       gPMHaltIdleCount = 0;
-       gPMHaltDepth = gPMHaltArray->getCount() - 1;
 
 
-       // Create multiple workers (and threads)
+    hdr = (swd_hdr *)memDesc->getBytesNoCopy();
+    memset(hdr, 0, sizeof(swd_hdr));
 
 
-       if (numWorkers > kPMHaltMaxWorkers)
-               numWorkers = kPMHaltMaxWorkers;
+    hdr->signature = SWD_HDR_SIGNATURE;
+    hdr->alloc_size = size;
 
 
-       DLOG("PM nodes = %u, maxDepth = %u, workers = %u\n",
-               totalNodes, gPMHaltArray->getCount(), numWorkers);
+    hdr->spindump_offset = sizeof(swd_hdr);
+    swd_buffer = (void *)hdr;
+    DLOG("SleepWake debug buffer size:0x%x spindump offset:0x%x\n", hdr->alloc_size, hdr->spindump_offset);
 
 
-       for (unsigned int i = 0; i < numWorkers; i++)
-               workers[i] = PMHaltWorker::worker();
+exit:
+    gRootDomain->swd_lock = 0;
+}
+
+void IOPMrootDomain::sleepWakeDebugEnableWdog()
+{
+    swd_flags |= SWD_WDOG_ENABLED;
+    if (!swd_buffer)
+        sleepWakeDebugMemAlloc();
+}
 
 
-       // Wait for workers to exhaust all available work
+bool IOPMrootDomain::sleepWakeDebugIsWdogEnabled()
+{
+    return ((swd_flags & SWD_WDOG_ENABLED) &&
+            !systemBooting && !systemShutdown);
+}
 
 
-       IOLockLock(gPMHaltLock);
-       while (gPMHaltDepth >= 0)
-       {
-               clock_interval_to_deadline(1000, kMillisecondScale, &deadline);
+errno_t IOPMrootDomain::sleepWakeDebugSaveFile(const char *name, char *buf, int len)
+{
+   struct vnode         *vp = NULL;
+   vfs_context_t        ctx = vfs_context_create(vfs_context_current());
+   kauth_cred_t         cred = vfs_context_ucred(ctx);
+   struct vnode_attr    va;
+   errno_t      error = EIO;
+
+   if (vnode_open(name, (O_CREAT | FWRITE | O_NOFOLLOW),
+                        S_IRUSR|S_IRGRP|S_IROTH, VNODE_LOOKUP_NOFOLLOW, &vp, ctx) != 0)
+   {
+      IOLog("Failed to open the file %s\n", name);
+      goto exit;
+   }
+   VATTR_INIT(&va);
+   VATTR_WANTED(&va, va_nlink);
+   /* Don't dump to non-regular files or files with links. */
+   if (vp->v_type != VREG ||
+        vnode_getattr(vp, &va, ctx) || va.va_nlink != 1) {
+        IOLog("Bailing as this is not a regular file\n");
+        goto exit;
+    }
+    VATTR_INIT(&va);
+    VATTR_SET(&va, va_data_size, 0);
+    vnode_setattr(vp, &va, ctx);
 
 
-               waitResult = IOLockSleepDeadline(
-                       gPMHaltLock, &gPMHaltDepth, deadline, THREAD_UNINT);
-               if (THREAD_TIMED_OUT == waitResult)
-               {
-                       AbsoluteTime now;
-                       clock_get_uptime(&now);
 
 
-                       IOLockUnlock(gPMHaltLock);
-                       for (unsigned int i = 0 ; i < numWorkers; i++)
-                       {
-                               if (workers[i])
-                                       PMHaltWorker::checkTimeout(workers[i], &now);
-                       }
-                       IOLockLock(gPMHaltLock);
-               }
-       }
-       IOLockUnlock(gPMHaltLock);
+    error = vn_rdwr(UIO_WRITE, vp, buf, len, 0,
+        UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, cred, (int *) 0,  vfs_context_proc(ctx));
+    if (error != 0)
+       IOLog("Failed to save sleep wake log. err 0x%x\n", error);
+    else
+       DLOG("Saved %d bytes to file %s\n",len, name);
 
 
-       // Release all workers
+exit:
+    if (vp) vnode_close(vp, FWRITE, ctx);
+    if (ctx) vfs_context_rele(ctx);
 
 
-       for (unsigned int i = 0; i < numWorkers; i++)
-       {
-               if (workers[i])
-                       workers[i]->release();
-               // worker also retained by it's own thread
-       }
+    return error;
 
 
-done:
-       DLOG("%s done\n", __FUNCTION__);
-       return;
 }
 
 }
 
+errno_t IOPMrootDomain::sleepWakeDebugCopyFile(
+                               struct vnode *srcVp, 
+                               vfs_context_t srcCtx,
+                               char *tmpBuf, uint64_t tmpBufSize,
+                               uint64_t srcOffset, 
+                               const char *dstFname, 
+                               uint64_t numBytes,
+                               uint32_t crc)
+{
+   struct vnode         *vp = NULL;
+   vfs_context_t        ctx = vfs_context_create(vfs_context_current());
+   struct vnode_attr    va;
+   errno_t      error = EIO;
+   uint64_t bytesToRead, bytesToWrite;
+   uint64_t readFileOffset, writeFileOffset, srcDataOffset; 
+   uint32_t newcrc = 0;
+
+   if (vnode_open(dstFname, (O_CREAT | FWRITE | O_NOFOLLOW), 
+                        S_IRUSR|S_IRGRP|S_IROTH, VNODE_LOOKUP_NOFOLLOW, &vp, ctx) != 0) 
+   {
+      DLOG("Failed to open the file %s\n", dstFname);
+      goto exit;
+   }
+   VATTR_INIT(&va);
+   VATTR_WANTED(&va, va_nlink);
+   /* Don't dump to non-regular files or files with links. */
+   if (vp->v_type != VREG ||
+        vnode_getattr(vp, &va, ctx) || va.va_nlink != 1) {
+        DLOG("Bailing as this is not a regular file\n");
+        goto exit;
+       }
+    VATTR_INIT(&va);   
+    VATTR_SET(&va, va_data_size, 0);
+    vnode_setattr(vp, &va, ctx);
+   
+    writeFileOffset = 0;
+    while(numBytes) {
+        bytesToRead = (round_page(numBytes) > tmpBufSize) ? tmpBufSize : round_page(numBytes);
+        readFileOffset = trunc_page(srcOffset);
+
+       DLOG("Read file (numBytes:0x%llx)\n", bytesToRead);
+       error = vn_rdwr(UIO_READ, srcVp, tmpBuf, bytesToRead, readFileOffset,
+               UIO_SYSSPACE, IO_SKIP_ENCRYPTION|IO_SYNC|IO_NODELOCKED|IO_UNIT|IO_NOCACHE, 
+               vfs_context_ucred(srcCtx), (int *) 0,
+               vfs_context_proc(srcCtx));
+       if (error) {
+           DLOG("Failed to read file(numBytes:0x%llx)\n", bytesToRead);
+           break;
+       }
+
+       srcDataOffset = (uint64_t)tmpBuf + (srcOffset - readFileOffset);
+       bytesToWrite = bytesToRead - (srcOffset - readFileOffset);
+       if (bytesToWrite > numBytes) bytesToWrite = numBytes;
+
+       if (crc) {
+           newcrc = crc32(newcrc, (void *)srcDataOffset, bytesToWrite);
+       }
+       error = vn_rdwr(UIO_WRITE, vp, (char *)srcDataOffset, bytesToWrite, writeFileOffset,
+               UIO_SYSSPACE, IO_SYNC|IO_NODELOCKED|IO_UNIT, 
+               vfs_context_ucred(ctx), (int *) 0,
+               vfs_context_proc(ctx));
+       if (error) {
+           DLOG("Failed to write file(numBytes:0x%llx)\n", bytesToWrite);
+           break;
+       }
+
+       writeFileOffset += bytesToWrite;
+       numBytes -= bytesToWrite;
+       srcOffset += bytesToWrite;
 
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+    }
+    if (crc != newcrc) {
+        swd_stackshot_hdr *shdr = (swd_stackshot_hdr *)tmpBuf;;
+
+        /* Set statckshot size to 0 if crc doesn't match */
+        shdr->magic = SWD_STACKSHOTHDR_MAGIC;
+        shdr->size = 0;
+
+        assert(tmpBufSize > sizeof(swd_stackshot_hdr));
+        bytesToWrite = round_page(sizeof(swd_stackshot_hdr));
+        vn_rdwr(UIO_WRITE, vp, (char *)tmpBuf, bytesToWrite, 0,
+               UIO_SYSSPACE, IO_SYNC|IO_NODELOCKED|IO_UNIT, 
+               vfs_context_ucred(ctx), (int *) 0,
+               vfs_context_proc(ctx));
+
+        DLOG("CRC check failed. expected:0x%x actual:0x%x\n", crc, newcrc);
+        error = EFAULT;
+    }
+exit:
+    if (vp) { 
+        error = vnode_close(vp, FWRITE, ctx);
+        DLOG("vnode_close returned 0x%x\n", error);
+    }
+    if (ctx) vfs_context_rele(ctx);
+
+    return error;
 
 
 
 
-#undef super
-#define super OSObject
-OSDefineMetaClassAndFinalStructors(PMSettingObject, OSObject)
 
 
-void PMSettingObject::setPMSetting(const OSSymbol *type, OSObject *obj)
-{
-        (*func)(target, type, obj, refcon);
 }
 
 }
 
-/* 
- * Static constructor/initializer for PMSettingObject
- */
-PMSettingObject *PMSettingObject::pmSettingObject(
-    IOPMrootDomain                      *parent_arg,
-    IOPMSettingControllerCallback       handler_arg,
-    OSObject                            *target_arg,
-    uintptr_t                           refcon_arg,
-    uint32_t                            supportedPowerSources,
-    const OSSymbol *                    settings[])
+void IOPMrootDomain::sleepWakeDebugDumpFromFile( )
 {
 {
-    uint32_t                            objCount = 0;
-    PMSettingObject                     *pmso;
 
 
-    if( !parent_arg || !handler_arg || !settings ) return NULL;
+    int             rc;
+    char                       hibernateFilename[MAXPATHLEN+1];
+    char            PMStatusCode[100];
+    void            *tmpBuf;
+    swd_hdr         *hdr = NULL;
+    uint32_t        stacksSize, logSize;
+    uint64_t        tmpBufSize;
+    uint64_t        hdrOffset, stacksOffset, logOffset;
+    errno_t         error = EIO;
+    OSObject        *obj = NULL;
+    OSString        *str = NULL;
+    OSNumber        *failStat = NULL;
+    struct vnode    *vp = NULL;
+    vfs_context_t   ctx = NULL;
+
+    struct vnode_attr           va;
+    IOBufferMemoryDescriptor    *tmpBufDesc = NULL;
+    IOHibernateImageHeader      *imageHdr;
+
+    DLOG("sleepWakeDebugDumpFromFile\n");
+    if ((swd_flags & SWD_LOGS_IN_FILE) == 0)
+        return;
+
+   if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock))
+       return;
 
 
-     // count OSSymbol entries in NULL terminated settings array
-    while( settings[objCount] ) {
-        objCount++;
-    }
-    if(0 == objCount) return NULL;
 
 
-    pmso = new PMSettingObject;
-    if(!pmso || !pmso->init()) return NULL;
-
-    pmso->parent = parent_arg;
-    pmso->func = handler_arg;
-    pmso->target = target_arg;
-    pmso->refcon = refcon_arg;
-    pmso->releaseAtCount = objCount + 1; // release when it has count+1 retains
-    pmso->publishedFeatureID = (uint32_t *)IOMalloc(sizeof(uint32_t)*objCount);
-    if(pmso->publishedFeatureID) {
-        for(unsigned int i=0; i<objCount; i++) {
-            // Since there is now at least one listener to this setting, publish
-            // PM root domain support for it.
-            parent_arg->publishFeature( settings[i]->getCStringNoCopy(), 
-                    supportedPowerSources, &pmso->publishedFeatureID[i] );
-        }
+    hibernateFilename[0] = 0;
+    if ((obj = copyProperty(kIOHibernateFileKey)))
+    {
+        if ((str = OSDynamicCast(OSString, obj)))
+            strlcpy(hibernateFilename, str->getCStringNoCopy(),
+                    sizeof(hibernateFilename));
+        obj->release();
     }
     }
-    
-    return pmso;
-}
+    if (!hibernateFilename[0]) {
+        DMSG("sleepWakeDebugDumpFromFile: Failed to hib file name\n");
+        goto exit;
+    }
+    DLOG("sleepWakeDebugDumpFromFile: Hib file name %s\n", hibernateFilename);
 
 
-void PMSettingObject::free(void)
-{
-    OSCollectionIterator    *settings_iter;
-    OSSymbol                *sym;
-    OSArray                 *arr;
-    int                     arr_idx;
-    int                     i;
-    int                     objCount = releaseAtCount - 1;
-    
-    if(publishedFeatureID) {
-        for(i=0; i<objCount; i++) {
-            if(0 != publishedFeatureID[i]) {
-                parent->removePublishedFeature( publishedFeatureID[i] );
-            }
-        }
-    
-        IOFree(publishedFeatureID, sizeof(uint32_t) * objCount);
+    /* Allocate a temp buffer to copy data between files */
+    tmpBufSize = 2*4096;
+    tmpBufDesc = IOBufferMemoryDescriptor::
+        inTaskWithOptions(kernel_task, kIODirectionOutIn | kIOMemoryMapperNone, 
+                          tmpBufSize, PAGE_SIZE);
+
+    if (!tmpBufDesc) {
+        DMSG("sleepWakeDebugDumpFromFile: Fail to allocate temp buf\n");
+        goto exit;
     }
     }
-            
-    IORecursiveLockLock(parent->settingsCtrlLock);        
-    
-    // Search each PM settings array in the kernel.
-    settings_iter = OSCollectionIterator::withCollection(parent->settingsCallbacks);
-    if(settings_iter
+
+    tmpBuf = tmpBufDesc->getBytesNoCopy();
+
+   ctx = vfs_context_create(vfs_context_current());
+    if (vnode_open(hibernateFilename, (FREAD | O_NOFOLLOW), 0,
+                   VNODE_LOOKUP_NOFOLLOW, &vp, ctx) != 0
     {
     {
-        while(( sym = OSDynamicCast(OSSymbol, settings_iter->getNextObject()) ))
-        {
-            arr = (OSArray *)parent->settingsCallbacks->getObject(sym);
-            arr_idx = arr->getNextIndexOfObject(this, 0);
-            if(-1 != arr_idx) {
-                // 'this' was found in the array; remove it                
-                arr->removeObject(arr_idx);
-            }
-        }
-    
-        settings_iter->release();
+        DMSG("sleepWakeDebugDumpFromFile: Failed to open the hibernate file %s\n", hibernateFilename);
+        goto exit;
+    }
+    VATTR_INIT(&va);
+    VATTR_WANTED(&va, va_nlink);
+    VATTR_WANTED(&va, va_data_alloc);
+    if (vp->v_type != VREG ||
+        vnode_getattr(vp, &va, ctx) || va.va_nlink != 1) {
+        DMSG("sleepWakeDebugDumpFromFile: Bailing as this is not a regular file\n");
+        goto exit;
+    }
+
+    /* Read the sleepimage file header */
+    rc = vn_rdwr(UIO_READ, vp, (char *)tmpBuf, round_page(sizeof(IOHibernateImageHeader)), 0,
+                UIO_SYSSPACE, IO_SKIP_ENCRYPTION|IO_SYNC|IO_NODELOCKED|IO_UNIT|IO_NOCACHE, 
+                vfs_context_ucred(ctx), (int *) 0,
+                vfs_context_proc(ctx));
+    if (rc != 0) {
+        DMSG("sleepWakeDebugDumpFromFile: Failed to read header size %lu(rc=%d)\n", round_page(sizeof(IOHibernateImageHeader)), rc);
+        goto exit;
     }
     }
-    
-    IORecursiveLockUnlock(parent->settingsCtrlLock);
-    
-    super::free();
-}
 
 
-void PMSettingObject::taggedRelease(const void *tag, const int when) const
-{     
-    // We have n+1 retains - 1 per array that this PMSettingObject is a member
-    // of, and 1 retain to ourself. When we get a release with n+1 retains
-    // remaining, we go ahead and free ourselves, cleaning up array pointers
-    // in free();
+    imageHdr = ((IOHibernateImageHeader *)tmpBuf);
+    if (imageHdr->signature != kIOHibernateHeaderDebugDataSignature) {
+        DMSG("sleepWakeDebugDumpFromFile: File header has unexpected value 0x%x\n", imageHdr->signature);
+        goto exit;
+    }
 
 
-    super::taggedRelease(tag, releaseAtCount);    
-}
+    /* Sleep/Wake debug header(swd_hdr) is at the beggining of the second block */
+    hdrOffset = imageHdr->deviceBlockSize;
+    if (hdrOffset + sizeof(swd_hdr) >= va.va_data_alloc) {
+        DMSG("sleepWakeDebugDumpFromFile: header is crossing file size(0x%llx)\n",  va.va_data_alloc);
+        goto exit;
+    }
 
 
+    DLOG("Reading swd_hdr len 0x%lx offset 0x%lx\n", round_page(sizeof(swd_hdr)), trunc_page(hdrOffset));
+    /* Read the sleep/wake debug header(swd_hdr) */
+    rc = vn_rdwr(UIO_READ, vp, (char *)tmpBuf, round_page(sizeof(swd_hdr)), trunc_page(hdrOffset),
+                UIO_SYSSPACE, IO_SKIP_ENCRYPTION|IO_SYNC|IO_NODELOCKED|IO_UNIT|IO_NOCACHE, 
+                vfs_context_ucred(ctx), (int *) 0,
+                vfs_context_proc(ctx));
+    if (rc != 0) {
+        DMSG("sleepWakeDebugDumpFromFile: Failed to debug read header size %lu. rc=%d\n",
+             round_page(sizeof(swd_hdr)), rc);
+        goto exit;
+    }
 
 
+    hdr = (swd_hdr *)((char *)tmpBuf + (hdrOffset - trunc_page(hdrOffset)));
+    if ((hdr->signature != SWD_HDR_SIGNATURE) || (hdr->alloc_size > SWD_BUF_SIZE) ||
+        (hdr->spindump_offset > SWD_BUF_SIZE) || (hdr->spindump_size > SWD_BUF_SIZE)) {
+        DMSG("sleepWakeDebugDumpFromFile: Invalid data in debug header. sign:0x%x size:0x%x spindump_offset:0x%x spindump_size:0x%x\n",
+             hdr->signature, hdr->alloc_size, hdr->spindump_offset, hdr->spindump_size);
+        goto exit;
+    }
+    stacksSize = hdr->spindump_size;
 
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+    /* Get stacks & log offsets in the image file */
+    stacksOffset = hdrOffset + hdr->spindump_offset;
+    logOffset = hdrOffset + offsetof(swd_hdr, UUID);
+    logSize = sizeof(swd_hdr)-offsetof(swd_hdr, UUID); 
 
 
-#undef  super
-#define super IOService
+    error = sleepWakeDebugCopyFile(vp, ctx, (char *)tmpBuf, tmpBufSize, stacksOffset,
+                                   getDumpStackFilename(hdr), stacksSize, hdr->crc);
+    if (error == EFAULT) {
+        DMSG("sleepWakeDebugDumpFromFile: Stackshot CRC doesn't match\n");
+        goto exit;
+    }
+    error = sleepWakeDebugCopyFile(vp, ctx, (char *)tmpBuf, tmpBufSize, logOffset, 
+                                   getDumpLogFilename(hdr), logSize, 0);
+    if (error) {
+        DMSG("sleepWakeDebugDumpFromFile: Failed to write the log file(0x%x)\n", error);
+        goto exit;
+    }
+exit:
+    if (error) {
+      // Write just the SleepWakeLog.dump with failure code
+      uint64_t fcode = 0;
+      const char *fname;
+      if (swd_flags & SWD_BOOT_BY_SW_WDOG) {
+          failStat = OSDynamicCast(OSNumber, getProperty(kIOPMSleepWakeFailureCodeKey));
+          fcode = failStat->unsigned64BitValue();
+          fname = kSleepWakeLogFilename;
+      }
+      else {
+          fname = kAppleOSXWatchdogLogFilename;
+      }
+      memset(PMStatusCode, 0x20, sizeof(PMStatusCode)); // Fill with spaces
+      PMStatusCode[sizeof(PMStatusCode)-1] = 0xa; // And an end-of-line at the end
+      snprintf(PMStatusCode, sizeof(PMStatusCode)-1, "Code: 0x%llx", fcode);
+      sleepWakeDebugSaveFile(fname, PMStatusCode, sizeof(PMStatusCode));
+    }
+    gRootDomain->swd_lock = 0;
 
 
-OSDefineMetaClassAndFinalStructors(IORootParent, IOService)
+    if (vp) vnode_close(vp, FREAD, ctx);
+    if (ctx) vfs_context_rele(ctx);
+    if (tmpBufDesc) tmpBufDesc->release();
 
 
-// This array exactly parallels the state array for the root domain.
-// Power state changes initiated by a device can be vetoed by a client of the device, and
-// power state changes initiated by the parent of a device cannot be vetoed by a client of the device,
-// so when the root domain wants a power state change that cannot be vetoed (e.g. demand sleep), it asks
-// its parent to make the change.  That is the reason for this complexity.
+}
 
 
-static IOPMPowerState patriarchPowerStates[NUM_POWER_STATES] =
+void IOPMrootDomain::sleepWakeDebugDumpFromMem(IOMemoryMap *logBufMap)
 {
 {
-    {1,0,0,0,0,0,0,0,0,0,0,0},              // off   (not used)
-    {1,0,RESTART_POWER,0,0,0,0,0,0,0,0,0},  // reset (not used)
-    {1,0,SLEEP_POWER,0,0,0,0,0,0,0,0,0},    // sleep
-    {1,0,DOZE_POWER,0,0,0,0,0,0,0,0,0},     // doze
-    {1,0,ON_POWER,0,0,0,0,0,0,0,0,0},       // running
-};
+   IOVirtualAddress     srcBuf = NULL;
+   char                 *stackBuf = NULL, *logOffset = NULL;
+   int                  logSize = 0;
 
 
-bool IORootParent::start( IOService * nub )
+   errno_t      error = EIO;
+   uint64_t     bufSize = 0;
+   swd_hdr      *hdr = NULL;
+   char PMStatusCode[100];
+   OSNumber  *failStat = NULL;
+
+   if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock))
+       return;
+
+   if ((logBufMap == 0) || ( (srcBuf = logBufMap->getVirtualAddress()) == 0) )
+   {
+      DLOG("Nothing saved to dump to file\n");
+      goto exit;
+   }
+
+   hdr = (swd_hdr *)srcBuf;
+   bufSize = logBufMap->getLength();
+   if (bufSize <= sizeof(swd_hdr))
+   {
+      IOLog("SleepWake log buffer contents are invalid\n");
+      goto exit;
+   }
+
+   stackBuf = (char*)hdr+hdr->spindump_offset;
+
+   error = sleepWakeDebugSaveFile(getDumpStackFilename(hdr), stackBuf, hdr->spindump_size);
+   if (error) goto exit;
+
+   logOffset = (char*)hdr+offsetof(swd_hdr, UUID);
+   logSize = sizeof(swd_hdr)-offsetof(swd_hdr, UUID);
+
+   error = sleepWakeDebugSaveFile(getDumpLogFilename(hdr), logOffset, logSize);
+   if (error) goto exit;
+
+    hdr->spindump_size = 0;
+    error = 0;
+
+exit:
+    if (error) {
+      // Write just the SleepWakeLog.dump with failure code
+      uint64_t fcode = 0;
+      const char *sname, *lname;
+      swd_stackshot_hdr shdr;
+
+        /* Try writing an empty stacks file */
+        shdr.magic = SWD_STACKSHOTHDR_MAGIC;
+        shdr.size = 0;
+
+
+      if (swd_flags & SWD_BOOT_BY_SW_WDOG) {
+          failStat = OSDynamicCast(OSNumber, getProperty(kIOPMSleepWakeFailureCodeKey));
+          fcode = failStat->unsigned64BitValue();
+          lname = kSleepWakeLogFilename;
+          sname = kSleepWakeStackFilename;
+      }
+      else {
+          lname = kAppleOSXWatchdogLogFilename;
+          sname= kAppleOSXWatchdogStackFilename;
+      }
+
+      sleepWakeDebugSaveFile(sname, (char*)(&shdr), sizeof(shdr));
+      memset(PMStatusCode, 0x20, sizeof(PMStatusCode)); // Fill with spaces
+      PMStatusCode[sizeof(PMStatusCode)-1] = 0xa; // And an end-of-line at the end
+      snprintf(PMStatusCode, sizeof(PMStatusCode)-1, "Code: 0x%llx", fcode);
+      sleepWakeDebugSaveFile(lname, PMStatusCode, sizeof(PMStatusCode));
+    }
+    gRootDomain->swd_lock = 0;
+}
+
+IOMemoryMap *IOPMrootDomain::sleepWakeDebugRetrieve( )
 {
 {
-    mostRecentChange = ON_STATE;
-    super::start(nub);
-    attachToParent( getRegistryRoot(), gIOPowerPlane );
-    PMinit();
-    registerPowerDriver(this, patriarchPowerStates, NUM_POWER_STATES);
-       wakeSystem();
-    powerOverrideOnPriv();     
-    return true;
+   IOVirtualAddress     vaddr = NULL;
+   IOMemoryDescriptor * desc = NULL;
+   IOMemoryMap *        logBufMap = NULL;
+
+   uint32_t          len;
+   addr64_t          data[3];
+   uint64_t          bufSize = 0;
+   uint64_t          crc = 0;
+   uint64_t          newcrc = 0;
+   uint64_t          paddr = 0;
+   swd_hdr           *hdr = NULL;
+   bool              ret = false;
+   char              str[20];
+
+
+   if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock))
+       return NULL;
+
+   if (!PEReadNVRAMProperty(kIOSleepWakeDebugKey, 0, &len)) {
+      DLOG("No sleepWakeDebug note to read\n");
+      goto exit;
+   }
+
+   if (len == strlen("sleepimage")) {
+       str[0] = 0;
+       PEReadNVRAMProperty(kIOSleepWakeDebugKey, str, &len);
+
+       if (!strncmp((char*)str, "sleepimage", strlen("sleepimage"))) {
+           DLOG("sleepWakeDebugRetrieve: in file logs\n");
+           swd_flags |= SWD_LOGS_IN_FILE|SWD_VALID_LOGS;
+           goto exit;
+       }
+   }
+   else if (len == sizeof(addr64_t)*3)
+       PEReadNVRAMProperty(kIOSleepWakeDebugKey, data, &len);
+   else {
+      DLOG("Invalid sleepWakeDebug note length(%d)\n", len);
+      goto exit;
+   }
+
+
+
+   DLOG("sleepWakeDebugRetrieve: data[0]:0x%llx data[1]:0x%llx data[2]:0x%llx\n",
+        data[0], data[1], data[2]);
+   DLOG("sleepWakeDebugRetrieve: in mem logs\n");
+   bufSize = data[0];
+   crc = data[1];
+   paddr = data[2];
+   if ( (bufSize <= sizeof(swd_hdr)) ||(bufSize > SWD_BUF_SIZE) || (crc == 0) )
+   {
+      IOLog("SleepWake log buffer contents are invalid\n");
+      return NULL;
+   }
+
+   DLOG("size:0x%llx crc:0x%llx paddr:0x%llx\n",
+         bufSize, crc, paddr);
+
+
+   desc = IOMemoryDescriptor::withAddressRange( paddr, bufSize,
+                          kIODirectionOutIn | kIOMemoryMapperNone, NULL);
+   if (desc == NULL)
+   {
+      IOLog("Fail to map SleepWake log buffer\n");
+      goto exit;
+   }
+
+   logBufMap = desc->map();
+
+   vaddr = logBufMap->getVirtualAddress();
+
+
+   if ( (logBufMap->getLength() <= sizeof(swd_hdr)) || (vaddr == NULL) ) {
+      IOLog("Fail to map SleepWake log buffer\n");
+      goto exit;
+   }
+
+   hdr = (swd_hdr *)vaddr;
+   if (hdr->spindump_offset+hdr->spindump_size > bufSize)
+   {
+      IOLog("SleepWake log buffer contents are invalid\n");
+      goto exit;
+   }
+
+   hdr->crc = crc;
+   newcrc = crc32(0, (void *)((char*)vaddr+hdr->spindump_offset),
+            hdr->spindump_size);
+   if (newcrc != crc) {
+      IOLog("SleepWake log buffer contents are invalid\n");
+      goto exit;
+   }
+
+   ret = true;
+   swd_flags |= SWD_LOGS_IN_MEM | SWD_VALID_LOGS;
+
+
+exit:
+   PERemoveNVRAMProperty(kIOSleepWakeDebugKey);
+   if (!ret) {
+      if (logBufMap) logBufMap->release();
+      logBufMap = 0;
+   }
+   if (desc) desc->release();
+    gRootDomain->swd_lock = 0;
+
+   return logBufMap;
 }
 
 }
 
-void IORootParent::shutDownSystem( void )
+#else
+
+void IOPMrootDomain::sleepWakeDebugTrig(bool restart)
 {
 }
 
 {
 }
 
-void IORootParent::restartSystem( void )
+void IOPMrootDomain::takeStackshot(bool restart, bool isOSXWatchdog)
 {
 {
+#pragma unused(restart)
+#pragma unused(isOSXWatchdog)
 }
 
 }
 
-void IORootParent::sleepSystem( void )
+void IOPMrootDomain::sleepWakeDebugMemAlloc( )
 {
 {
-    mostRecentChange = SLEEP_STATE;
-    changePowerStateToPriv(SLEEP_STATE);
+}
+void IOPMrootDomain::sleepWakeDebugDumpFromMem(IOMemoryMap *map)
+{
+}
+errno_t IOPMrootDomain::sleepWakeDebugCopyFile(
+                               struct vnode *srcVp, 
+                               vfs_context_t srcCtx,
+                               char *tmpBuf, uint64_t tmpBufSize,
+                               uint64_t srcOffset, 
+                               const char *dstFname, 
+                               uint64_t numBytes,
+                               uint32_t crc)
+{
+    return EIO;
 }
 
 }
 
-void IORootParent::dozeSystem( void )
+void IOPMrootDomain::sleepWakeDebugDumpFromFile()
 {
 {
-    mostRecentChange = DOZE_STATE;
-    changePowerStateToPriv(DOZE_STATE);
 }
 
 }
 
-// Called in demand sleep when sleep discovered to be impossible after actually attaining that state.
-// This brings the parent to doze, which allows the root to step up from sleep to doze.
+IOMemoryMap *IOPMrootDomain::sleepWakeDebugRetrieve( )
+{
+   return NULL;
+}
 
 
-// In idle sleep, do nothing because the parent is still on and the root can freely change state.
+void IOPMrootDomain::sleepWakeDebugEnableWdog()
+{
+}
 
 
-void IORootParent::sleepToDoze( void )
+bool IOPMrootDomain::sleepWakeDebugIsWdogEnabled()
 {
 {
-    if ( mostRecentChange == SLEEP_STATE ) {
-        changePowerStateToPriv(DOZE_STATE);
-    }
+    return false;
 }
 
 }
 
-void IORootParent::wakeSystem( void )
+errno_t IOPMrootDomain::sleepWakeDebugSaveFile(const char *name, char *buf, int len)
 {
 {
-    mostRecentChange = ON_STATE;
-    changePowerStateToPriv(ON_STATE);
+    return 0;
 }
 }
+
+#endif
+