X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/6d2010ae8f7a6078e10b361c6962983bab233e0f..db6096698656d32db7df630594bd9617ee54f828:/iokit/Kernel/IOPMrootDomain.cpp diff --git a/iokit/Kernel/IOPMrootDomain.cpp b/iokit/Kernel/IOPMrootDomain.cpp index e6146bb24..dd6d10f2a 100644 --- a/iokit/Kernel/IOPMrootDomain.cpp +++ b/iokit/Kernel/IOPMrootDomain.cpp @@ -47,6 +47,7 @@ #if HIBERNATION #include #endif +#include #include #include #include @@ -78,6 +79,9 @@ __END_DECLS #define _LOG(x...) +#define DARK_WAKE_DEBUG 1 +#define SUSPEND_PM_NOTIFICATIONS_DEBUG 1 + #define CHECK_THREAD_CONTEXT #ifdef CHECK_THREAD_CONTEXT static IOWorkLoop * gIOPMWorkLoop = 0; @@ -124,7 +128,8 @@ enum { kPowerEventAssertionRelease, // 10 kPowerEventAssertionSetLevel, // 11 kPowerEventQueueSleepWakeUUID, // 12 - kPowerEventPublishSleepWakeUUID // 13 + kPowerEventPublishSleepWakeUUID, // 13 + kPowerEventSuspendClient // 14 }; // For evaluatePolicy() @@ -138,7 +143,8 @@ enum { kStimulusDarkWakeActivityTickle, // 5 kStimulusDarkWakeEntry, // 6 kStimulusDarkWakeReentry, // 7 - kStimulusDarkWakeEvaluate // 8 + kStimulusDarkWakeEvaluate, // 8 + kStimulusNoIdleSleepPreventers // 9 }; extern "C" { @@ -168,7 +174,8 @@ enum kAutoWakePostWindow = 15 }; -#define kLocalEvalClamshellCommand (1 << 15) +#define kLocalEvalClamshellCommand (1 << 15) +#define kIdleSleepRetryInterval (3 * 60) enum { OFF_STATE = 0, @@ -190,18 +197,22 @@ static IOPMPowerState ourPowerStates[NUM_POWER_STATES] = {1, kIOPMPowerOn, kIOPMPowerOn, ON_POWER, 0,0,0,0,0,0,0,0} }; +#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" // Special interest that entitles the interested client from receiving // all system messages. Only used by powerd. // #define kIOPMSystemCapabilityInterest "IOPMSystemCapabilityInterest" +#define kPMSuspendedNotificationClients "PMSuspendedNotificationClients" + /* * Aggressiveness */ @@ -257,7 +268,9 @@ enum { kDarkWakeFlagIgnoreDiskIOInDark = 0x04, // ignore disk idle in DW kDarkWakeFlagIgnoreDiskIOAlways = 0x08, // always ignore disk idle kDarkWakeFlagIgnoreDiskIOMask = 0x0C, - kDarkWakeFlagAlarmIsDark = 0x0100 + kDarkWakeFlagAlarmIsDark = 0x0100, + kDarkWakeFlagGraphicsPowerState1 = 0x0200, + kDarkWakeFlagAudioNotSuppressed = 0x0400 }; static IOPMrootDomain * gRootDomain; @@ -267,7 +280,14 @@ static UInt32 gWillShutdown = 0; static UInt32 gPagingOff = 0; static UInt32 gSleepWakeUUIDIsSet = false; static uint32_t gAggressivesState = 0; -static uint32_t gDarkWakeFlags = kDarkWakeFlagHIDTickleNone; +static uint32_t gDarkWakeFlags = kDarkWakeFlagHIDTickleNone | kDarkWakeFlagIgnoreDiskIOAlways; +static PMStatsStruct gPMStats; + +#if HIBERNATION +static IOPMSystemSleepPolicyHandler gSleepPolicyHandler = 0; +static IOPMSystemSleepPolicyVariables * gSleepPolicyVars = 0; +static void * gSleepPolicyTarget; +#endif struct timeval gIOLastSleepTime; struct timeval gIOLastWakeTime; @@ -284,6 +304,8 @@ const OSSymbol *gIOPMStatsApplicationResponseTimedOut; const OSSymbol *gIOPMStatsApplicationResponseCancel; const OSSymbol *gIOPMStatsApplicationResponseSlow; +#define kBadPMFeatureID 0 + /* * PMSettingHandle * Opaque handle passed to clients of registerPMSettingController() @@ -792,16 +814,19 @@ static SYSCTL_PROC(_kern, OID_AUTO, progressmeter, static SYSCTL_INT(_debug, OID_AUTO, darkwake, CTLFLAG_RW, &gDarkWakeFlags, 0, ""); +static const OSSymbol * gIOPMSettingAutoWakeCalendarKey; static const OSSymbol * gIOPMSettingAutoWakeSecondsKey; static const OSSymbol * gIOPMSettingDebugWakeRelativeKey; static const OSSymbol * gIOPMSettingMaintenanceWakeCalendarKey; +static const OSSymbol * gIOPMSettingSleepServiceWakeCalendarKey; +static const OSSymbol * gIOPMSettingSilentRunningKey; //****************************************************************************** // start // //****************************************************************************** -#define kRootDomainSettingsCount 16 +#define kRootDomainSettingsCount 17 bool IOPMrootDomain::start( IOService * nub ) { @@ -812,10 +837,12 @@ bool IOPMrootDomain::start( IOService * nub ) super::start(nub); gRootDomain = this; + gIOPMSettingAutoWakeCalendarKey = OSSymbol::withCString(kIOPMSettingAutoWakeCalendarKey); gIOPMSettingAutoWakeSecondsKey = OSSymbol::withCString(kIOPMSettingAutoWakeSecondsKey); gIOPMSettingDebugWakeRelativeKey = OSSymbol::withCString(kIOPMSettingDebugWakeRelativeKey); - gIOPMSettingMaintenanceWakeCalendarKey = - OSSymbol::withCString(kIOPMSettingMaintenanceWakeCalendarKey); + gIOPMSettingMaintenanceWakeCalendarKey = OSSymbol::withCString(kIOPMSettingMaintenanceWakeCalendarKey); + gIOPMSettingSleepServiceWakeCalendarKey = OSSymbol::withCString(kIOPMSettingSleepServiceWakeCalendarKey); + gIOPMSettingSilentRunningKey = OSSymbol::withCStringNoCopy(kIOPMSettingSilentRunningKey); gIOPMStatsApplicationResponseTimedOut = OSSymbol::withCString(kIOPMStatsResponseTimedOut); gIOPMStatsApplicationResponseCancel = OSSymbol::withCString(kIOPMStatsResponseCancel); @@ -829,7 +856,7 @@ bool IOPMrootDomain::start( IOService * nub ) OSSymbol::withCString(kIOPMSettingSleepOnPowerButtonKey), gIOPMSettingAutoWakeSecondsKey, OSSymbol::withCString(kIOPMSettingAutoPowerSecondsKey), - OSSymbol::withCString(kIOPMSettingAutoWakeCalendarKey), + gIOPMSettingAutoWakeCalendarKey, OSSymbol::withCString(kIOPMSettingAutoPowerCalendarKey), gIOPMSettingDebugWakeRelativeKey, OSSymbol::withCString(kIOPMSettingDebugPowerRelativeKey), @@ -841,11 +868,12 @@ bool IOPMrootDomain::start( IOService * nub ) 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( @@ -865,7 +893,7 @@ bool IOPMrootDomain::start( IOService * nub ) setProperty(kIOSleepSupportedKey, true); - bzero(&pmStats, sizeof(pmStats)); + bzero(&gPMStats, sizeof(gPMStats)); pmTracer = PMTraceWorker::tracer(this); @@ -880,6 +908,7 @@ bool IOPMrootDomain::start( IOService * nub ) clamshellExists = false; clamshellDisabled = true; acAdaptorConnected = true; + clamshellSleepDisabled = false; // Set the default system capabilities at boot. _currentCapability = kIOPMSystemCapabilityCPU | @@ -915,8 +944,15 @@ bool IOPMrootDomain::start( IOService * nub ) (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); + preventIdleSleepList = OSSet::withCapacity(8); + preventSystemSleepList = OSSet::withCapacity(2); PMinit(); // creates gIOPMWorkLoop @@ -987,6 +1023,11 @@ bool IOPMrootDomain::start( IOService * nub ) if(psIterator) { psIterator->release(); } + + + pmSuspendedCapacity = pmSuspendedSize = 0; + pmSuspendedPIDS = NULL; + sysctl_register_oid(&sysctl__kern_sleeptime); sysctl_register_oid(&sysctl__kern_waketime); @@ -1005,6 +1046,126 @@ bool IOPMrootDomain::start( IOService * nub ) return true; } + + + +void IOPMrootDomain::handleSuspendPMNotificationClient(uint32_t pid, bool doSuspend) +{ + ASSERT_GATED(); + + int index = -1; + unsigned int i; + + if (!pmSuspendedPIDS) { + pmSuspendedCapacity = 8; + pmSuspendedSize = pmSuspendedCapacity * sizeof(PMNotifySuspendedStruct); + pmSuspendedPIDS = (PMNotifySuspendedStruct *)IOMalloc(pmSuspendedSize); + bzero(pmSuspendedPIDS, pmSuspendedSize); + } + + /* Find the existing pid in the existing array */ + + for (i=0; i < pmSuspendedCapacity; i++) { + if (pmSuspendedPIDS[i].pid == pid) { + index = i; + break; + } + } + + if (-1 == index) + { + /* Find an unused slot in the suspended pids table. */ + + for (i=0; i < pmSuspendedCapacity; i++) { + if (pmSuspendedPIDS[i].refcount == 0) { + break; + } + } + + if (pmSuspendedCapacity == i) + { + /* GROW if necessary */ + + PMNotifySuspendedStruct *newSuspended = NULL; + pmSuspendedCapacity *= 2; + pmSuspendedSize = pmSuspendedCapacity * sizeof(PMNotifySuspendedStruct); + newSuspended = (PMNotifySuspendedStruct *)IOMalloc(pmSuspendedSize); + + bzero(newSuspended, pmSuspendedSize); + bcopy(pmSuspendedPIDS, newSuspended, pmSuspendedSize/2); + IOFree(pmSuspendedPIDS, pmSuspendedSize/2); + + pmSuspendedPIDS = newSuspended; + } + + index = i; + pmSuspendedPIDS[index].pid = pid; + } + + if (doSuspend) { + pmSuspendedPIDS[index].refcount++; + } else { + pmSuspendedPIDS[index].refcount--; + } + + /* + * Publish array of suspended pids in IOPMrootDomain + */ + OSArray *publish = OSArray::withCapacity(pmSuspendedCapacity); + + for (i=0; i 0) { + OSDictionary *suspended = OSDictionary::withCapacity(2); + OSNumber *n = NULL; + + n = OSNumber::withNumber(pmSuspendedPIDS[i].pid, 32); + suspended->setObject("pid", n); + n->release(); + + n = OSNumber::withNumber(pmSuspendedPIDS[i].refcount, 32); + suspended->setObject("refcount", n); + n->release(); + + publish->setObject(suspended); + suspended->release(); + + } + } + + if (0 != publish->getCount()) { + setProperty(kPMSuspendedNotificationClients, publish); + } else { + removeProperty(kPMSuspendedNotificationClients); + } + + publish->release(); + + return; +} + +bool IOPMrootDomain::pmNotificationIsSuspended(uint32_t pid) +{ + unsigned int index; + + for (index=0; index < pmSuspendedCapacity; index++) { + if (pmSuspendedPIDS[index].pid == pid) { + return pmSuspendedPIDS[index].refcount > 0; + } + } + + return false; +} + + +void IOPMrootDomain::suspendPMNotificationsForPID(uint32_t pid, bool doSuspend) +{ + if(pmPowerStateQueue) { + pmPowerStateQueue->submitPowerEvent(kPowerEventSuspendClient, (void *)pid, (uint64_t)doSuspend ); + } + return; +} + //****************************************************************************** // setProperties // @@ -1019,9 +1180,9 @@ IOReturn IOPMrootDomain::setProperties( OSObject * props_obj ) OSBoolean *b; OSNumber *n; OSDictionary *d; - OSSymbol *type; + const OSSymbol *key; OSObject *obj; - unsigned int i; + OSCollectionIterator * iter = 0; const OSSymbol *publish_simulated_battery_string = OSSymbol::withCString("SoftwareSimulatedBatteries"); const OSSymbol *boot_complete_string = OSSymbol::withCString("System Boot Complete"); @@ -1036,157 +1197,198 @@ IOReturn IOPMrootDomain::setProperties( OSObject * props_obj ) #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 - - if (!dict) +#if SUSPEND_PM_NOTIFICATIONS_DEBUG + const OSSymbol *suspendPMClient_string = OSSymbol::withCString(kPMSuspendedNotificationClients); +#endif + + if (!dict) { return_value = kIOReturnBadArgument; goto exit; } - if ((b = OSDynamicCast(OSBoolean, dict->getObject(publish_simulated_battery_string)))) + iter = OSCollectionIterator::withCollection(dict); + if (!iter) { - publishResource(publish_simulated_battery_string, kOSBooleanTrue); - } - - if ((n = OSDynamicCast(OSNumber, dict->getObject(idle_seconds_string)))) - { - setProperty(idle_seconds_string, n); - idleSeconds = n->unsigned32BitValue(); + return_value = kIOReturnNoMemory; + goto exit; } - if (boot_complete_string && dict->getObject(boot_complete_string)) - { - pmPowerStateQueue->submitPowerEvent( kPowerEventSystemBootCompleted ); - } - - if( battery_warning_disabled_string && dict->getObject(battery_warning_disabled_string)) + while ((key = (const OSSymbol *) iter->getNextObject()) && + (obj = dict->getObject(key))) { - setProperty( battery_warning_disabled_string, dict->getObject(battery_warning_disabled_string)); - } - - if (pmTimelineLogging_string && (d = OSDynamicCast(OSDictionary, dict->getObject(pmTimelineLogging_string)))) - { - if (timeline && timeline->setProperties(d)) + if (key->isEqualTo(publish_simulated_battery_string)) { - OSDictionary *tlInfo = timeline->copyInfoDictionary(); - if (tlInfo) { - setProperty(kIOPMTimelineDictionaryKey, tlInfo); - tlInfo->release(); + 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); + } + else if (key->isEqualTo(pmTimelineLogging_string)) + { + if ((d = OSDynamicCast(OSDictionary, obj)) && + timeline && timeline->setProperties(d)) + { + OSDictionary *tlInfo = timeline->copyInfoDictionary(); + if (tlInfo) { + setProperty(kIOPMTimelineDictionaryKey, tlInfo); + tlInfo->release(); + } } } - } - - if( sys_shutdown_string && (b = OSDynamicCast(OSBoolean, dict->getObject(sys_shutdown_string)))) - { - pmPowerStateQueue->submitPowerEvent(kPowerEventSystemShutdown, (void *) b); - } - - if( stall_halt_string && (b = OSDynamicCast(OSBoolean, dict->getObject(stall_halt_string))) ) - { - setProperty(stall_halt_string, b); - } - #if HIBERNATION - if ( hibernatemode_string - && (n = OSDynamicCast(OSNumber, dict->getObject(hibernatemode_string)))) - { - 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); - } - OSString *str; - if ( hibernatefile_string - && (str = OSDynamicCast(OSString, dict->getObject(hibernatefile_string)))) - { - setProperty(hibernatefile_string, str); - } -#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))) - { - if(pmPowerStateQueue) { + 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 + 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); } - - } - - if (loginwindow_tracepoint_string - && (n = OSDynamicCast(OSNumber, dict->getObject(loginwindow_tracepoint_string))) - && pmTracer) - { - pmTracer->traceLoginWindowPhase( n->unsigned8BitValue() ); - } - - if ((b = OSDynamicCast(OSBoolean, dict->getObject(kIOPMDeepSleepEnabledKey)))) - { - setProperty(kIOPMDeepSleepEnabledKey, b); - } - if ((n = OSDynamicCast(OSNumber, dict->getObject(kIOPMDeepSleepDelayKey)))) - { - setProperty(kIOPMDeepSleepDelayKey, n); - } - if ((b = OSDynamicCast(OSBoolean, dict->getObject(kIOPMDestroyFVKeyOnStandbyKey)))) - { - setProperty(kIOPMDestroyFVKeyOnStandbyKey, b); - } - - // 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)))) + 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)) { - UInt32 rsecs = n->unsigned32BitValue(); - if (!rsecs) - autoWakeStart = autoWakeEnd = 0; + if (kOSBooleanTrue == obj) + OSBitOrAtomic(kIOPMAlarmBitCalendarWake, &_userScheduledAlarm); else + OSBitAndAtomic(~kIOPMAlarmBitCalendarWake, &_userScheduledAlarm); + DLOG("_userScheduledAlarm = 0x%x\n", (uint32_t) _userScheduledAlarm); + } +#if SUSPEND_PM_NOTIFICATIONS_DEBUG + else if (key->isEqualTo(suspendPMClient_string)) + { + if ((n = OSDynamicCast(OSNumber, obj))) { - 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); + // Toggle the suspended status for pid n. + uint32_t pid_int = n->unsigned32BitValue(); + suspendPMNotificationsForPID(pid_int, !pmNotificationIsSuspended(pid_int)); } } - if (gIOPMSettingDebugWakeRelativeKey == type) +#endif + // Relay our allowed PM settings onto our registered PM clients + else if ((allowedPMSettings->getNextIndexOfObject(key, 0) != (unsigned int) -1)) { - if ((n = OSDynamicCast(OSNumber, obj))) - _debugWakeSeconds = n->unsigned32BitValue(); - else - _debugWakeSeconds = 0; + 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); + } + } + + 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()); } - - return_value = setPMSetting(type, obj); - - if(kIOReturnSuccess != return_value) goto exit; } exit: @@ -1206,6 +1408,10 @@ exit: if(hibernatefreeratio_string) hibernatefreeratio_string->release(); if(hibernatefreetime_string) hibernatefreetime_string->release(); #endif +#if SUSPEND_PM_NOTIFICATIONS_DEBUG + if(suspendPMClient_string) suspendPMClient_string->release(); +#endif + if (iter) iter->release(); return return_value; } @@ -1715,8 +1921,12 @@ void IOPMrootDomain::startIdleSleepTimer( uint32_t inSeconds ) clock_interval_to_deadline(inSeconds, kSecondScale, &deadline); 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); } //****************************************************************************** @@ -1849,9 +2059,10 @@ IOReturn IOPMrootDomain::privateSleepSystem( uint32_t sleepReason ) kIOPMOSSwitchHibernationKey, kIOPMIdleSleepKey, kIOPMLowPowerSleepKey, - kIOPMClamshellSleepKey, kIOPMThermalEmergencySleepKey, - kIOPMMaintenanceSleepKey + kIOPMMaintenanceSleepKey, + kIOPMSleepServiceExitKey, + kIOPMDarkWakeThermalEmergencyKey }; PMEventDetails *details; @@ -1866,6 +2077,9 @@ IOReturn IOPMrootDomain::privateSleepSystem( uint32_t sleepReason ) return kIOReturnNotPermitted; } + if (kIOPMSleepReasonDarkWakeThermalEmergency == sleepReason) + messageClients(kIOPMMessageDarkWakeThermalEmergency); + if (timeline) timeline->setSleepCycleInProgressFlag(true); @@ -1873,7 +2087,6 @@ IOReturn IOPMrootDomain::privateSleepSystem( uint32_t sleepReason ) if(pmPowerStateQueue) { pmPowerStateQueue->submitPowerEvent(kPowerEventPublishSleepWakeUUID, (void *)true); } - // Log the beginning of system sleep. details = PMEventDetails::eventDetails(kIOPMEventTypeSleep, NULL, @@ -1999,6 +2212,7 @@ void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) // Code will resume execution here upon wake. clock_get_uptime(&systemWakeTime); + _highestCapability = 0; #if HIBERNATION IOHibernateSystemWake(); @@ -2018,6 +2232,10 @@ void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) getPlatform()->PMLog(kIOPMrootDomainClass, kPMLogSystemWake, 0, 0); lowBatteryCondition = false; lastSleepReason = 0; + + _lastDebugWakeSeconds = _debugWakeSeconds; + _debugWakeSeconds = 0; + _scheduledAlarms = 0; // And start logging the wake event here // TODO: Publish the wakeReason string as an integer @@ -2029,7 +2247,6 @@ void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) recordAndReleasePMEvent( details ); - #ifndef __LP64__ systemWake(); #endif @@ -2041,6 +2258,9 @@ void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) logGraphicsClamp = true; logWranglerTickle = true; sleepTimerMaintenance = false; + wranglerTickleLatched = false; + darkWakeThermalAlarm = false; + darkWakeThermalEmergency = false; OSString * wakeType = OSDynamicCast( OSString, getProperty(kIOPMRootDomainWakeTypeKey)); @@ -2058,38 +2278,58 @@ void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) OSNumber * hibOptions = OSDynamicCast( OSNumber, getProperty(kIOHibernateOptionsKey)); - if (hibernateAborted || - ((hibOptions && - !(hibOptions->unsigned32BitValue() & kIOHibernateOptionDarkWake))) || - ((_debugWakeSeconds != 0) && - ((gDarkWakeFlags & kDarkWakeFlagAlarmIsDark) == 0)) || - (wakeType && ( - wakeType->isEqualTo(kIOPMRootDomainWakeTypeUser) || - wakeType->isEqualTo(kIOPMRootDomainWakeTypeAlarm)))) + if (hibernateAborted || ((hibOptions && + !(hibOptions->unsigned32BitValue() & kIOHibernateOptionDarkWake)))) { + // Hibernate aborted, or EFI brought up graphics + wranglerTickled = true; + } + else + if (wakeType && ( + wakeType->isEqualTo(kIOPMRootDomainWakeTypeUser) || + wakeType->isEqualTo(kIOPMRootDomainWakeTypeAlarm))) + { + // User wake or RTC alarm wranglerTickled = true; } else if (wakeType && - wakeType->isEqualTo(kIOPMRootDomainWakeTypeMaintenance)) + wakeType->isEqualTo(kIOPMRootDomainWakeTypeSleepTimer)) { + // SMC standby timer trumps SleepX darkWakeMaintenance = true; darkWakeToSleepASAP = true; + sleepTimerMaintenance = true; + } + else + if ((_lastDebugWakeSeconds != 0) && + ((gDarkWakeFlags & kDarkWakeFlagAlarmIsDark) == 0)) + { + // SleepX before maintenance + wranglerTickled = true; } else if (wakeType && - wakeType->isEqualTo(kIOPMRootDomainWakeTypeSleepTimer)) + wakeType->isEqualTo(kIOPMRootDomainWakeTypeMaintenance)) { darkWakeMaintenance = true; darkWakeToSleepASAP = true; - sleepTimerMaintenance = true; + } + else + if (wakeType && + wakeType->isEqualTo(kIOPMRootDomainWakeTypeSleepService)) + { + darkWakeToSleepASAP = true; +// darkWakeMaintenance = true; // ???? + darkWakeSleepService = true; } else { // Unidentified wake source, resume to full wake if debug // alarm is pending. - if (_debugWakeSeconds && (!wakeReason || wakeReason->isEqualTo(""))) + if (_lastDebugWakeSeconds && + (!wakeReason || wakeReason->isEqualTo(""))) wranglerTickled = true; else darkWakeToSleepASAP = true; @@ -2097,11 +2337,18 @@ void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) } else { - // Post a HID tickle immediately - except for maintenance wake. - - if (hibernateAborted || !wakeType || - !wakeType->isEqualTo(kIOPMRootDomainWakeTypeMaintenance)) + if (wakeType && + wakeType->isEqualTo(kIOPMRootDomainWakeTypeSleepTimer)) { + darkWakeMaintenance = true; + darkWakeToSleepASAP = 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 @@ -2132,30 +2379,17 @@ void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) changePowerStateToPriv(ON_STATE); } break; - - case ON_STATE: { - bool wasPrevented = childPreventSystemSleep; - - details = PMEventDetails::eventDetails( - kIOPMEventTypeWakeDone, - NULL, - 0, - kIOReturnSuccess); - - recordAndReleasePMEvent( details ); + case ON_STATE: { if (previousPowerState != ON_STATE) - _debugWakeSeconds = 0; - - // Update childPreventSystemSleep flag using the capability computed - // by IOSevice::rebuildChildClampBits(). - - childPreventSystemSleep = - ((currentCapability() & kIOPMChildClamp2) != 0); - - if (wasPrevented && !childPreventSystemSleep) { - evaluatePolicy( kStimulusDarkWakeEvaluate ); + details = PMEventDetails::eventDetails( + kIOPMEventTypeWakeDone, + NULL, + 0, + kIOReturnSuccess); + + recordAndReleasePMEvent( details ); } } break; } @@ -2165,9 +2399,6 @@ void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) // requestPowerDomainState // // Extend implementation in IOService. Running on PM work loop thread. -// -// Examine children desires and initiate idle-sleep if all children are idle, -// prevent idle and system sleep flags are not set. //****************************************************************************** IOReturn IOPMrootDomain::requestPowerDomainState ( @@ -2175,110 +2406,102 @@ IOReturn IOPMrootDomain::requestPowerDomainState ( IOPowerConnection * childConnection, unsigned long specification ) { - OSIterator *iter; - OSObject *next; - IOPowerConnection *connection; - IOPMPowerFlags mergedChildDesire = 0; - IOPMPowerFlags editedChildDesire; - IOPMPowerFlags thisDesire; - bool sleepASAP = false; + // Idle and system sleep prevention flags affects driver desire. + // Children desire are irrelevant so they are cleared. + + return super::requestPowerDomainState(0, childConnection, specification); +} + +//****************************************************************************** +// updatePreventIdleSleepList +// +// Called by IOService on PM work loop. +//****************************************************************************** + +void IOPMrootDomain::updatePreventIdleSleepList( + IOService * service, bool addNotRemove ) +{ + unsigned int oldCount, newCount; ASSERT_GATED(); - // Disregard disk I/O (anything besides the display wrangler) as a - // factor in preventing idle sleep - based on a runtime setting. + // Disregard disk I/O (anything besides the display wrangler) + // as a factor preventing idle sleep,except in the case of legacy disk I/O if ((gDarkWakeFlags & kDarkWakeFlagIgnoreDiskIOAlways) && - (kIOPMPreventIdleSleep & childDesire) && - (childConnection != wranglerConnection)) + addNotRemove && (service != wrangler) && (service != this)) { - childDesire &= ~kIOPMPreventIdleSleep; + return; } - // Force the child's input power requirement to 0 unless the prevent - // idle-sleep flag is set. Nil input power flags maps to our state 0. - // Our power clamp (deviceDesire) clamps the lowest power state at 2. - - editedChildDesire = 0; - if (childDesire & kIOPMPreventIdleSleep) - editedChildDesire |= (kIOPMPowerOn | kIOPMPreventIdleSleep); - if (childDesire & kIOPMPreventSystemSleep) - editedChildDesire |= (kIOPMPowerOn | kIOPMPreventSystemSleep); - - iter = getChildIterator(gIOPowerPlane); - if ( iter ) + oldCount = preventIdleSleepList->getCount(); + if (addNotRemove) { - while ( (next = iter->getNextObject()) ) - { - if ( (connection = OSDynamicCast(IOPowerConnection, next)) ) - { - // Ignore child that are in the process of joining. - if (connection->getReadyFlag() == false) - continue; + preventIdleSleepList->setObject(service); + DLOG("prevent idle sleep list: %s+ (%u)\n", + service->getName(), preventIdleSleepList->getCount()); + } + else if (preventIdleSleepList->member(service)) + { + preventIdleSleepList->removeObject(service); + DLOG("prevent idle sleep list: %s- (%u)\n", + service->getName(), preventIdleSleepList->getCount()); + } + newCount = preventIdleSleepList->getCount(); + + 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. + + changePowerStateTo(ON_STATE); + } + else if ((oldCount != 0) && (newCount == 0)) + { + // Last driver removed from prevent list. + // Drop the driver clamp to allow idle sleep. - // OR in the child's input power requirements. - // Is this connection attached to the child that called - // requestPowerDomainState()? + changePowerStateTo(SLEEP_STATE); + evaluatePolicy( kStimulusNoIdleSleepPreventers ); + } +} - if (connection == childConnection) - { - thisDesire = editedChildDesire; - } - else - { - thisDesire = 0; - if (connection->getPreventIdleSleepFlag()) - thisDesire |= (kIOPMPowerOn | kIOPMPreventIdleSleep); - if (connection->getPreventSystemSleepFlag()) - thisDesire |= (kIOPMPowerOn | kIOPMPreventSystemSleep); - } +//****************************************************************************** +// preventSystemSleepListUpdate +// +// Called by IOService on PM work loop. +//****************************************************************************** - mergedChildDesire |= thisDesire; - if (thisDesire && (kIOLogPMRootDomain & gIOKitDebug)) - { - IOService * child = - (IOService *) connection->getChildEntry(gIOPowerPlane); - LOG("child %p, noIdle %d, noSleep %d - %s\n", - child, - ((thisDesire & kIOPMPreventIdleSleep) != 0), - ((thisDesire & kIOPMPreventSystemSleep) != 0), - child ? child->getName() : "?"); - } - } - } - iter->release(); - } +void IOPMrootDomain::updatePreventSystemSleepList( + IOService * service, bool addNotRemove ) +{ + unsigned int oldCount; - DLOG("mergedChildDesire 0x%lx, extraSleepDelay %ld\n", - mergedChildDesire, extraSleepDelay); + ASSERT_GATED(); + if (this == service) + return; - if ( !mergedChildDesire && !systemBooting ) + oldCount = preventSystemSleepList->getCount(); + if (addNotRemove) { - if (!wrangler) - { - changePowerStateToPriv(ON_STATE); - if (idleSeconds) - { - // stay awake for at least idleSeconds - startIdleSleepTimer(idleSeconds); - } - } - else if (!extraSleepDelay && !idleSleepTimerPending && !systemDarkWake) + 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 ((oldCount != 0) && (preventSystemSleepList->getCount() == 0)) { - sleepASAP = true; + // Lost all system sleep preventers. + // Send stimulus if system sleep was blocked, and is in dark wake. + evaluatePolicy( kStimulusDarkWakeEvaluate ); } } - - // Drop our power clamp to SLEEP_STATE when all children became idle, - // and system sleep and display sleep slider values are equal. - - adjustPowerState(sleepASAP); - - // If our power clamp has already dropped to SLEEP_STATE, and no child - // is keeping us at ON_STATE, then the following will trigger idle sleep. - - return super::requestPowerDomainState( - editedChildDesire, childConnection, specification); } //****************************************************************************** @@ -2394,11 +2617,27 @@ void IOPMrootDomain::tellNoChangeDown( unsigned long stateNum ) DLOG("tellNoChangeDown %u->%u\n", (uint32_t) getPowerState(), (uint32_t) stateNum); - if (idleSeconds && !wrangler) + // Sleep canceled, clear the sleep trace point. + tracePoint(kIOPMTracePointSystemUp); + + if (!wrangler) + { + if (idleSeconds) + { + // stay awake for at least idleSeconds + startIdleSleepTimer(idleSeconds); + } + } + else if (sleepSlider && wranglerAsleep) { - // stay awake for at least idleSeconds - startIdleSleepTimer(idleSeconds); + // Display wrangler is already asleep, it won't trigger the next + // idle sleep attempt. Schedule a future idle sleep attempt, and + // also push out the next idle sleep attempt. + + startIdleSleepTimer( kIdleSleepRetryInterval ); } + + IOService::setAdvisoryTickleEnable( true ); return tellClients( kIOMessageSystemWillNotSleep ); } @@ -2413,7 +2652,6 @@ void IOPMrootDomain::tellNoChangeDown( unsigned long stateNum ) void IOPMrootDomain::tellChangeUp( unsigned long stateNum ) { - OSData *publishPMStats = NULL; DLOG("tellChangeUp %u->%u\n", (uint32_t) getPowerState(), (uint32_t) stateNum); @@ -2440,14 +2678,11 @@ void IOPMrootDomain::tellChangeUp( unsigned long stateNum ) // stay awake for at least idleSeconds startIdleSleepTimer(idleSeconds); } + IOService::setAdvisoryTickleEnable( true ); tellClients( kIOMessageSystemWillPowerOn ); } tracePoint( kIOPMTracePointWakeApplications ); - publishPMStats = OSData::withBytes(&pmStats, sizeof(pmStats)); - setProperty(kIOPMSleepStatisticsKey, publishPMStats); - publishPMStats->release(); - bzero(&pmStats, sizeof(pmStats)); if (pmStatsAppResponses) { @@ -2670,7 +2905,12 @@ void IOPMrootDomain::handlePublishSleepWakeUUID( bool shouldPublish ) IOReturn IOPMrootDomain::changePowerStateTo( unsigned long ordinal ) { - return kIOReturnUnsupported; // ignored + DLOG("changePowerStateTo(%lu)\n", ordinal); + + if ((ordinal != ON_STATE) && (ordinal != SLEEP_STATE)) + return kIOReturnUnsupported; + + return super::changePowerStateTo(ordinal); } IOReturn IOPMrootDomain::changePowerStateToPriv( unsigned long ordinal ) @@ -2697,7 +2937,7 @@ bool IOPMrootDomain::abortHibernation(void) { bool ret = activitySinceSleep(); - if (ret && !hibernateAborted) + if (ret && !hibernateAborted && checkSystemCanSustainFullWake()) { DLOG("activitySinceSleep ABORT [%d, %d]\n", userActivityCount, userActivityAtSleep); hibernateAborted = true; @@ -2726,10 +2966,10 @@ bool IOPMrootDomain::shouldSleepOnClamshellClosed( void ) if (!clamshellExists) return false; - DLOG("clamshell closed %d, disabled %d, desktopMode %d, ac %d\n", - clamshellClosed, clamshellDisabled, desktopMode, acAdaptorConnected); + DLOG("clamshell closed %d, disabled %d, desktopMode %d, ac %d sleepDisabled %d\n", + clamshellClosed, clamshellDisabled, desktopMode, acAdaptorConnected, clamshellSleepDisabled); - return ( !clamshellDisabled && !(desktopMode && acAdaptorConnected) ); + return ( !clamshellDisabled && !(desktopMode && acAdaptorConnected) && !clamshellSleepDisabled ); } void IOPMrootDomain::sendClientClamshellNotification( void ) @@ -2775,6 +3015,35 @@ void IOPMrootDomain::setSleepSupported( IOOptionBits 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 // @@ -2886,7 +3155,7 @@ void IOPMrootDomain::publishFeature( } 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); + features->setObject(feature, new_feature_data); } new_feature_data->release(); @@ -2925,6 +3194,9 @@ IOReturn IOPMrootDomain::removePublishedFeature( uint32_t removeFeatureID ) OSNumber *osNum = NULL; OSArray *arrayMemberCopy; + if (kBadPMFeatureID == removeFeatureID) + return kIOReturnNotFound; + if(featuresDictLock) IOLockLock(featuresDictLock); OSDictionary *features = @@ -3032,6 +3304,28 @@ exit: 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) // @@ -3365,81 +3659,99 @@ void IOPMrootDomain::informCPUStateChange( // 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; -}; - -struct IOPMSystemSleepPolicyTable -{ - uint8_t signature[4]; - uint16_t version; - uint16_t entryCount; - IOPMSystemSleepPolicyEntry entries[]; -}; - -enum { - kIOPMSleepFactorSleepTimerWake = 0x00000001, - kIOPMSleepFactorLidOpen = 0x00000002, - kIOPMSleepFactorACPower = 0x00000004, - kIOPMSleepFactorLowBattery = 0x00000008, - kIOPMSleepFactorDeepSleepNoDelay = 0x00000010, - kIOPMSleepFactorDeepSleepDemand = 0x00000020, - kIOPMSleepFactorDeepSleepDisable = 0x00000040, - kIOPMSleepFactorUSBExternalDevice = 0x00000080, - kIOPMSleepFactorBluetoothHIDDevice = 0x00000100, - kIOPMSleepFactorExternalMediaMounted = 0x00000200, - kIOPMSleepFactorDriverAssertBit5 = 0x00000400, /* Reserved for ThunderBolt */ - kIOPMSleepFactorDriverAssertBit6 = 0x00000800, - kIOPMSleepFactorDriverAssertBit7 = 0x00001000 -}; - -bool IOPMrootDomain::evaluateSystemSleepPolicy( IOPMSystemSleepParameters * p ) -{ - const IOPMSystemSleepPolicyTable * pt; - OSObject * prop = 0; - OSData * policyData; - uint32_t currentFactors; - uint32_t deepSleepDelay = 0; - bool success = false; - - if (getProperty(kIOPMDeepSleepEnabledKey) != kOSBooleanTrue) - return false; - - getSleepOption(kIOPMDeepSleepDelayKey, &deepSleepDelay); - - prop = getServiceRoot()->copyProperty(kIOPlatformSystemSleepPolicyKey); - if (!prop) - return false; - - policyData = OSDynamicCast(OSData, prop); - if (!policyData || - (policyData->getLength() < sizeof(IOPMSystemSleepPolicyTable))) - { - goto done; - } +} __attribute__((packed)); - pt = (const IOPMSystemSleepPolicyTable *) policyData->getBytesNoCopy(); - if ((pt->signature[0] != 'S') || - (pt->signature[1] != 'L') || - (pt->signature[2] != 'P') || - (pt->signature[3] != 'T') || - (pt->version != 1) || - (pt->entryCount == 0)) - { - goto done; - } +struct IOPMSystemSleepPolicyTable +{ + uint32_t signature; + uint16_t version; + uint16_t entryCount; + IOPMSystemSleepPolicyEntry entries[]; +} __attribute__((packed)); - if ((policyData->getLength() - sizeof(IOPMSystemSleepPolicyTable)) != - (sizeof(IOPMSystemSleepPolicyEntry) * pt->entryCount)) +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)) { - goto done; + // 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; } - currentFactors = 0; + // Current factors based on environment and assertions + if (sleepTimerMaintenance) + 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; @@ -3449,57 +3761,162 @@ bool IOPMrootDomain::evaluateSystemSleepPolicy( IOPMSystemSleepParameters * p ) if (getPMAssertionLevel(kIOPMDriverAssertionExternalMediaMountedBit) != kIOPMDriverAssertionLevelOff) currentFactors |= kIOPMSleepFactorExternalMediaMounted; - if (getPMAssertionLevel(kIOPMDriverAssertionReservedBit5) != /* AssertionBit5 = Thunderbolt */ + if (getPMAssertionLevel(kIOPMDriverAssertionReservedBit5) != kIOPMDriverAssertionLevelOff) - currentFactors |= kIOPMSleepFactorDriverAssertBit5; - if (getPMAssertionLevel(kIOPMDriverAssertionReservedBit7) != + currentFactors |= kIOPMSleepFactorThunderboltDevice; + if (_scheduledAlarms != 0) + currentFactors |= kIOPMSleepFactorRTCAlarmScheduled; + if (getPMAssertionLevel(kIOPMDriverAssertionMagicPacketWakeEnabledBit) != kIOPMDriverAssertionLevelOff) - currentFactors |= kIOPMSleepFactorDriverAssertBit7; - if (0 == deepSleepDelay) - currentFactors |= kIOPMSleepFactorDeepSleepNoDelay; - if (!clamshellClosed) - currentFactors |= kIOPMSleepFactorLidOpen; - if (acAdaptorConnected) - currentFactors |= kIOPMSleepFactorACPower; - if (lowBatteryCondition) - currentFactors |= kIOPMSleepFactorLowBattery; - if (sleepTimerMaintenance) - currentFactors |= kIOPMSleepFactorSleepTimerWake; + currentFactors |= kIOPMSleepFactorMagicPacketWakeEnabled; + if (!powerOffEnabled) + currentFactors |= kIOPMSleepFactorAutoPowerOffDisabled; + if (desktopMode) + currentFactors |= kIOPMSleepFactorExternalDisplay; - // pmset overrides - if ((hibernateMode & kIOHibernateModeOn) == 0) - currentFactors |= kIOPMSleepFactorDeepSleepDisable; - else if ((hibernateMode & kIOHibernateModeSleep) == 0) - currentFactors |= kIOPMSleepFactorDeepSleepDemand; - - DLOG("Sleep policy %u entries, current factors 0x%x\n", - pt->entryCount, currentFactors); + 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 ((params->sleepType >= kIOPMSleepTypeSafeSleep) && + ((*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 * policyEntry = &pt->entries[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("factor mask 0x%08x, bits 0x%08x, flags 0x%08x, wake 0x%08x\n", - policyEntry->factorMask, policyEntry->factorBits, - policyEntry->sleepFlags, policyEntry->wakeEvents); + DLOG("^ found match\n"); + found = true; - if ((currentFactors ^ policyEntry->factorBits) & policyEntry->factorMask) - continue; // mismatch, try next + params->version = kIOPMSystemSleepParametersVersion; + params->reserved1 = 1; + if (entry->sleepFlags & kIOPMSleepFlagHibernate) + params->sleepType = kIOPMSleepTypeStandby; + else + params->sleepType = kIOPMSleepTypeNormalSleep; - if (p) + params->ecWakeEvents = entry->wakeEvents; + if (entry->sleepFlags & kIOPMSleepFlagSleepTimerEnable) { - p->version = 1; - p->sleepFlags = policyEntry->sleepFlags; - p->sleepTimer = 0; - p->wakeEvents = policyEntry->wakeEvents; - if (p->sleepFlags & kIOPMSleepFlagSleepTimerEnable) + if (kIOPMSleepPhase2 == sleepPhase) { - p->sleepTimer = deepSleepDelay; + 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; } - - DLOG("matched policy entry %u\n", i); - success = true; break; } @@ -3507,28 +3924,58 @@ done: if (prop) prop->release(); - return success; + return found; } +static IOPMSystemSleepParameters gEarlySystemSleepParams; + void IOPMrootDomain::evaluateSystemSleepPolicyEarly( void ) { - IOPMSystemSleepParameters params; - - // Evaluate sleep policy before driver sleep phase. + // 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); - if (!hibernateNoDefeat && - evaluateSystemSleepPolicy(¶ms) && - ((params.sleepFlags & kIOPMSleepFlagHibernate) == 0)) + // Save for late evaluation if sleep is aborted + bzero(&gEarlySystemSleepParams, sizeof(gEarlySystemSleepParams)); + + if (evaluateSystemSleepPolicy(&gEarlySystemSleepParams, kIOPMSleepPhase1, + &hibernateMode)) + { + if (!hibernateNoDefeat && + (gEarlySystemSleepParams.sleepType == kIOPMSleepTypeNormalSleep)) + { + // Disable hibernate setup for normal sleep + hibernateDisabled = true; + } + } + + // Publish IOPMSystemSleepType + uint32_t sleepType = gEarlySystemSleepParams.sleepType; + if (sleepType == kIOPMSleepTypeInvalid) { - hibernateDisabled = true; + // 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 ) @@ -3536,27 +3983,31 @@ void IOPMrootDomain::evaluateSystemSleepPolicyFinal( void ) IOPMSystemSleepParameters params; OSData * paramsData; - // Evaluate sleep policy after drivers but before platform sleep. + // Evaluate sleep policy after sleeping drivers but before platform sleep. DLOG("%s\n", __FUNCTION__); - if (evaluateSystemSleepPolicy(¶ms)) + bzero(¶ms, sizeof(params)); + if (evaluateSystemSleepPolicy(¶ms, kIOPMSleepPhase2, &hibernateMode)) { if ((hibernateDisabled || hibernateAborted) && - (params.sleepFlags & kIOPMSleepFlagHibernate)) + (params.sleepType != kIOPMSleepTypeNormalSleep)) { - // Should hibernate but unable to or aborted. - // Arm timer for a short sleep and retry or wake fully. + // Final evaluation picked a state requiring hibernation, + // but hibernate setup was skipped. Retry using the early + // sleep parameters. - params.sleepFlags &= ~kIOPMSleepFlagHibernate; - params.sleepFlags |= kIOPMSleepFlagSleepTimerEnable; - params.sleepTimer = 1; + bcopy(&gEarlySystemSleepParams, ¶ms, sizeof(params)); + params.sleepType = kIOPMSleepTypeAbortedSleep; + params.ecWakeTimer = 1; hibernateNoDefeat = true; DLOG("wake in %u secs for hibernateDisabled %d, hibernateAborted %d\n", - params.sleepTimer, hibernateDisabled, hibernateAborted); + params.ecWakeTimer, hibernateDisabled, hibernateAborted); } else + { hibernateNoDefeat = false; + } paramsData = OSData::withBytes(¶ms, sizeof(params)); if (paramsData) @@ -3565,25 +4016,30 @@ void IOPMrootDomain::evaluateSystemSleepPolicyFinal( void ) paramsData->release(); } - if (params.sleepFlags & kIOPMSleepFlagHibernate) + if (params.sleepType >= kIOPMSleepTypeHibernate) { - // Force hibernate + // Disable safe sleep to force the hibernate path gIOHibernateMode &= ~kIOHibernateModeSleep; } } } bool IOPMrootDomain::getHibernateSettings( - uint32_t * hibernateMode, + uint32_t * hibernateModePtr, uint32_t * hibernateFreeRatio, uint32_t * hibernateFreeTime ) { - bool ok = getSleepOption(kIOHibernateModeKey, hibernateMode); + // Called by IOHibernateSystemSleep() after evaluateSystemSleepPolicyEarly() + // has updated the hibernateDisabled flag. + + bool ok = getSleepOption(kIOHibernateModeKey, hibernateModePtr); getSleepOption(kIOHibernateFreeRatioKey, hibernateFreeRatio); getSleepOption(kIOHibernateFreeTimeKey, hibernateFreeTime); if (hibernateDisabled) - *hibernateMode = 0; - DLOG("hibernateMode 0x%x\n", *hibernateMode); + *hibernateModePtr = 0; + else if (gSleepPolicyHandler) + *hibernateModePtr = hibernateMode; + DLOG("hibernateMode 0x%x\n", *hibernateModePtr); return ok; } @@ -3622,6 +4078,37 @@ bool IOPMrootDomain::getSleepOption( const char * key, uint32_t * option ) } #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(¶ms, sizeof(params)); + + ok = evaluateSystemSleepPolicy(¶ms, kIOPMSleepPhase0, &hibMode); + if (ok) + { + *sleepType = params.sleepType; + return kIOReturnSuccess; + } +#endif + + return kIOReturnUnsupported; +} + // MARK: - // MARK: Shutdown and Restart @@ -3705,6 +4192,10 @@ void IOPMrootDomain::handlePlatformHaltRestart( UInt32 pe_type ) case kPEPagingOff: ctx.PowerState = ON_STATE; ctx.MessageType = kIOMessageSystemPagingOff; + IOService::updateConsoleUsers(NULL, kIOMessageSystemPagingOff); +#if HIBERNATION + IOHibernateSystemRestart(); +#endif break; default: @@ -3803,7 +4294,6 @@ void IOPMrootDomain::tagPowerPlaneService( if (isDisplayWrangler) { wrangler = service; - wranglerConnection = (IOService *) service->getParentEntry(gIOPowerPlane); } #else isDisplayWrangler = false; @@ -4063,6 +4553,7 @@ void IOPMrootDomain::handleOurPowerChangeStart( if ((_highestCapability & kIOPMSystemCapabilityGraphics) == 0) _systemMessageClientMask |= kSystemMessageClientKernel; + IOService::setAdvisoryTickleEnable( true ); tellClients(kIOMessageSystemWillPowerOn); } @@ -4072,6 +4563,7 @@ void IOPMrootDomain::handleOurPowerChangeStart( tracePoint( kIOPMTracePointDarkWakeEntry ); *inOutChangeFlags |= kIOPMSyncTellPowerDown; _systemMessageClientMask = kSystemMessageClientUser; + IOService::setAdvisoryTickleEnable( false ); } } @@ -4109,6 +4601,7 @@ void IOPMrootDomain::handleOurPowerChangeStart( if (_pendingCapability & kIOPMSystemCapabilityGraphics) { _systemMessageClientMask = kSystemMessageClientAll; + IOService::setAdvisoryTickleEnable( true ); } else { @@ -4202,6 +4695,7 @@ void IOPMrootDomain::handleOurPowerChangeDone( darkWakeToSleepASAP = false; pciCantSleepValid = false; rejectWranglerTickle = false; + darkWakeSleepService = false; } // Entered dark mode. @@ -4211,7 +4705,7 @@ void IOPMrootDomain::handleOurPowerChangeDone( { if (((gDarkWakeFlags & kDarkWakeFlagIgnoreDiskIOInDark) == 0) && (kSystemTransitionWake == _systemTransitionType) && - (_debugWakeSeconds == 0)) + (_lastDebugWakeSeconds == 0)) { OSObject * prop = copyProperty(kIOPMRootDomainWakeTypeKey); if (prop) @@ -4248,7 +4742,7 @@ void IOPMrootDomain::handleOurPowerChangeDone( _systemTransitionType, _systemStateGeneration, _systemMessageClientMask, _desiredCapability, _currentCapability, _pendingCapability, - _debugWakeSeconds); + _lastDebugWakeSeconds); // Update current system capability. @@ -4257,10 +4751,7 @@ void IOPMrootDomain::handleOurPowerChangeDone( // Update highest system capability. - if (!CAP_CURRENT(kIOPMSystemCapabilityCPU)) - _highestCapability = 0; // reset at sleep state - else - _highestCapability |= _currentCapability; + _highestCapability |= _currentCapability; if (darkWakePostTickle && (kSystemTransitionWake == _systemTransitionType) && @@ -4281,6 +4772,20 @@ void IOPMrootDomain::handleOurPowerChangeDone( { setProperty(kIOPMSystemCapabilitiesKey, _currentCapability, 64); tracePoint( kIOPMTracePointSystemUp, 0 ); + + // kIOPMDWOverTemp notification handling was postponed + if (darkWakeThermalAlarm) + { + if (!wranglerTickled && !darkWakeThermalEmergency && + CAP_CURRENT(kIOPMSystemCapabilityCPU) && + !CAP_CURRENT(kIOPMSystemCapabilityGraphics)) + { + darkWakeThermalEmergency = true; + privateSleepSystem(kIOPMSleepReasonDarkWakeThermalEmergency); + MSG("DarkWake thermal limits breached. Going to sleep!\n"); + } + darkWakeThermalAlarm = false; + } } _systemTransitionType = kSystemTransitionNone; @@ -4313,12 +4818,15 @@ void IOPMrootDomain::overridePowerChangeForUIService( // Activate power limiter. if ((actions->parameter & kPMActionsFlagIsDisplayWrangler) && - ((_pendingCapability & kIOPMSystemCapabilityGraphics) == 0)) + ((_pendingCapability & kIOPMSystemCapabilityGraphics) == 0) && + (changeFlags & kIOPMSynchronize)) { actions->parameter |= kPMActionsFlagLimitPower; } else if ((actions->parameter & kPMActionsFlagIsAudioDevice) && - ((_pendingCapability & kIOPMSystemCapabilityAudio) == 0)) + ((gDarkWakeFlags & kDarkWakeFlagAudioNotSuppressed) == 0) && + ((_pendingCapability & kIOPMSystemCapabilityAudio) == 0) && + (changeFlags & kIOPMSynchronize)) { actions->parameter |= kPMActionsFlagLimitPower; } @@ -4369,7 +4877,8 @@ void IOPMrootDomain::overridePowerChangeForUIService( // Enforce limit for system power/cap transitions. maxPowerState = 0; - if (actions->parameter & kPMActionsFlagIsDisplayWrangler) + if ((actions->parameter & kPMActionsFlagIsDisplayWrangler) && + (service->getPowerState() > 0)) { // Forces a 3->1 transition sequence if (changeFlags & kIOPMDomainWillChange) @@ -4377,6 +4886,10 @@ void IOPMrootDomain::overridePowerChangeForUIService( else maxPowerState = 1; } + else if (actions->parameter & kPMActionsFlagIsGraphicsDevice) + { + maxPowerState = 1; + } } else { @@ -4442,9 +4955,10 @@ void IOPMrootDomain::handleActivityTickleForDisplayWrangler( } } - if (!wranglerTickled && !lowBatteryCondition && + if (!wranglerTickled && ((_pendingCapability & kIOPMSystemCapabilityGraphics) == 0)) { + setProperty(kIOPMRootDomainWakeTypeKey, kIOPMRootDomainWakeTypeHIDActivity); DLOG("display wrangler tickled\n"); if (kIOLogPMRootDomain & gIOKitDebug) OSReportWithBacktrace("Dark wake display tickle"); @@ -4695,9 +5209,20 @@ IOReturn IOPMrootDomain::setMaintenanceWakeCalendar( data = OSData::withBytesNoCopy((void *) calendar, sizeof(*calendar)); if (!data) return kIOReturnNoMemory; - - ret = setPMSetting(gIOPMSettingMaintenanceWakeCalendarKey, data); + 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; } @@ -4766,7 +5291,7 @@ IOReturn IOPMrootDomain::displayWranglerNotification( return kIOReturnUnsupported; } -//********************************************************************************* +//****************************************************************************** // displayWranglerMatchPublished // // Receives a notification when the IODisplayWrangler is published. @@ -4774,7 +5299,7 @@ IOReturn IOPMrootDomain::displayWranglerNotification( //****************************************************************************** bool IOPMrootDomain::displayWranglerMatchPublished( - void * target, + void * target, void * refCon, IOService * newService, IONotifier * notifier __unused) @@ -4815,6 +5340,42 @@ void IOPMrootDomain::reportUserInput( void ) #endif } +//****************************************************************************** +// blockDisplayWranglerTickle +//****************************************************************************** + +bool IOPMrootDomain::latchDisplayWranglerTickle( bool latch ) +{ +#if !NO_KERNEL_HID + if (latch) + { + // Not too late to prevent the display from lighting up + if (!(_currentCapability & kIOPMSystemCapabilityGraphics) && + !(_pendingCapability & kIOPMSystemCapabilityGraphics) && + !checkSystemCanSustainFullWake()) + { + wranglerTickleLatched = true; + } + else + { + wranglerTickleLatched = false; + } + } + else if (wranglerTickleLatched && checkSystemCanSustainFullWake()) + { + wranglerTickleLatched = false; + + pmPowerStateQueue->submitPowerEvent( + kPowerEventPolicyStimulus, + (void *) kStimulusDarkWakeActivityTickle ); + } + + return wranglerTickleLatched; +#else + return false; +#endif +} + // MARK: - // MARK: Battery @@ -4884,7 +5445,12 @@ bool IOPMrootDomain::checkSystemCanSleep( IOOptionBits options ) break; // always sleep on low battery } - if (childPreventSystemSleep) + if(darkWakeThermalEmergency) + { + break; // always sleep on dark wake thermal emergencies + } + + if (preventSystemSleepList->getCount() != 0) { err = 4; // 4. child prevent system sleep clamp break; @@ -4932,6 +5498,30 @@ bool IOPMrootDomain::checkSystemCanSleep( IOOptionBits options ) return true; } +//****************************************************************************** +// checkSystemCanSustainFullWake +//****************************************************************************** + +bool IOPMrootDomain::checkSystemCanSustainFullWake( void ) +{ +#if !NO_KERNEL_HID + if (lowBatteryCondition) + { + // Low battery wake, or received a low battery notification + // while system is awake. + return false; + } + + if (clamshellExists && clamshellClosed && !acAdaptorConnected && + !clamshellSleepDisabled) + { + // Lid closed on battery power + return false; + } +#endif + return true; +} + //****************************************************************************** // adjustPowerState // @@ -4986,6 +5576,16 @@ void IOPMrootDomain::dispatchPowerEvent( { systemBooting = false; + if (lowBatteryCondition) + { + privateSleepSystem (kIOPMSleepReasonLowPower); + + // The rest is unnecessary since the system is expected + // to sleep immediately. The following wake will update + // everything. + break; + } + // If lid is closed, re-send lid closed notification // now that booting is complete. if ( clamshellClosed ) @@ -5083,6 +5683,9 @@ void IOPMrootDomain::dispatchPowerEvent( case kPowerEventPublishSleepWakeUUID: handlePublishSleepWakeUUID((bool)arg0); break; + case kPowerEventSuspendClient: + handleSuspendPMNotificationClient((uintptr_t)arg0, (bool)arg1); + break; } } @@ -5201,6 +5804,27 @@ void IOPMrootDomain::handlePowerNotification( UInt32 msg ) privateSleepSystem (kIOPMSleepReasonThermalEmergency); } + if (msg & kIOPMDWOverTemp) + { + if (!CAP_CURRENT(kIOPMSystemCapabilityCPU) || + (_systemTransitionType == kSystemTransitionSleep) || + (_systemTransitionType == kSystemTransitionWake) || + (_systemTransitionType == kSystemTransitionCapability)) + { + // During early wake or when system capability is changing, + // set flag and take action at end of transition. + darkWakeThermalAlarm = true; + } + else if (!wranglerTickled && !darkWakeThermalEmergency && + !CAP_CURRENT(kIOPMSystemCapabilityGraphics)) + { + // System in steady state and in dark wake + darkWakeThermalEmergency = true; + privateSleepSystem(kIOPMSleepReasonDarkWakeThermalEmergency); + MSG("DarkWake thermal limits breached. Going to sleep!\n"); + } + } + /* * Sleep Now! */ @@ -5228,8 +5852,10 @@ void IOPMrootDomain::handlePowerNotification( UInt32 msg ) clamshellClosed = false; clamshellExists = true; + // Don't issue a hid tickle when lid is open and polled on wake if (msg & kIOPMSetValue) { + setProperty(kIOPMRootDomainWakeTypeKey, "Lid Open"); reportUserInput(); } @@ -5244,7 +5870,7 @@ void IOPMrootDomain::handlePowerNotification( UInt32 msg ) || (lastSleepReason == kIOPMSleepReasonMaintenance)); if (aborting) userActivityCount++; DLOG("clamshell tickled %d lastSleepReason %d\n", userActivityCount, lastSleepReason); - } + } /* * Clamshell CLOSED @@ -5310,6 +5936,16 @@ void IOPMrootDomain::handlePowerNotification( UInt32 msg ) { 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 } /* @@ -5351,12 +5987,11 @@ void IOPMrootDomain::handlePowerNotification( UInt32 msg ) { - // SLEEP! privateSleepSystem (kIOPMSleepReasonClamshell); } else if ( eval_clamshell ) { - evaluatePolicy(kStimulusDarkWakeEvaluate); + evaluatePolicy( kStimulusDarkWakeEvaluate ); } /* @@ -5393,6 +6028,7 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) int displaySleep : 1; int sleepDelayChanged : 1; int evaluateDarkWake : 1; + int adjustPowerState : 1; } bit; uint32_t u32; } flags; @@ -5443,6 +6079,8 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) if ( minutesToIdleSleep > minutesToDisplayDim ) minutesDelta = minutesToIdleSleep - minutesToDisplayDim; + else if( minutesToIdleSleep == minutesToDisplayDim ) + minutesDelta = 1; if ((sleepSlider == 0) && (minutesToIdleSleep != 0)) flags.bit.idleSleepEnabled = true; @@ -5470,8 +6108,7 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) break; case kStimulusAllowSystemSleepChanged: - // FIXME: de-compose to change flags. - adjustPowerState(); + flags.bit.adjustPowerState = true; break; case kStimulusDarkWakeActivityTickle: @@ -5488,6 +6125,12 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) break; } + if (latchDisplayWranglerTickle(true)) + { + DLOG("latched tickle\n"); + break; + } + _desiredCapability |= (kIOPMSystemCapabilityGraphics | kIOPMSystemCapabilityAudio); @@ -5517,6 +6160,7 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) // Notify clients about full wake. _systemMessageClientMask = kSystemMessageClientAll; + IOService::setAdvisoryTickleEnable( true ); tellClients(kIOMessageSystemWillPowerOn); } @@ -5591,6 +6235,10 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) #endif break; + case kStimulusNoIdleSleepPreventers: + flags.bit.adjustPowerState = true; + break; + } /* switch(stimulus) */ if (flags.bit.evaluateDarkWake && !wranglerTickled) @@ -5612,6 +6260,11 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) { lastSleepReason = kIOPMSleepReasonMaintenance; setProperty(kRootDomainSleepReasonKey, kIOPMMaintenanceSleepKey); + } + else if (darkWakeSleepService) + { + lastSleepReason = kIOPMSleepReasonSleepServiceExit; + setProperty(kRootDomainSleepReasonKey, kIOPMSleepServiceExitKey); } changePowerStateWithOverrideTo( SLEEP_STATE ); } @@ -5620,7 +6273,8 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) // Parked in dark wake, a tickle will return to full wake rejectWranglerTickle = false; } - } else // non-maintenance (network) dark wake + } + else // non-maintenance (network) dark wake { if (checkSystemCanSleep(true)) { @@ -5703,7 +6357,7 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) AbsoluteTime now; uint64_t nanos; uint32_t minutesSinceDisplaySleep = 0; - uint32_t sleepDelay; + uint32_t sleepDelay = 0; clock_get_uptime(&now); if (CMP_ABSOLUTETIME(&now, &wranglerSleepTime) > 0) @@ -5717,10 +6371,6 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) { sleepDelay = extraSleepDelay - minutesSinceDisplaySleep; } - else - { - sleepDelay = 1; // 1 min - } startIdleSleepTimer(sleepDelay * 60); DLOG("display slept %u min, set idle timer to %u min\n", @@ -5736,6 +6386,72 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) restoreUserSpinDownTimeout(); adjustPowerState(); } + + if (flags.bit.adjustPowerState) + { + bool sleepASAP = 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; + } + } + if(sleepASAP) + { + lastSleepReason = kIOPMSleepReasonIdle; + setProperty(kRootDomainSleepReasonKey, kIOPMIdleSleepKey); + } + + adjustPowerState(sleepASAP); + } +} + +//****************************************************************************** +// evaluateAssertions +// +//****************************************************************************** +void IOPMrootDomain::evaluateAssertions(IOPMDriverAssertionType newAssertions, IOPMDriverAssertionType oldAssertions) +{ + IOPMDriverAssertionType changedBits = newAssertions ^ oldAssertions; + + messageClients(kIOPMMessageDriverAssertionsChanged); + + if (changedBits & kIOPMDriverAssertionPreventDisplaySleepBit) { + + if (wrangler) { + + bool value = (newAssertions & kIOPMDriverAssertionPreventDisplaySleepBit) ? true : false; + DLOG("wrangler->setIgnoreIdleTimer\(%d)\n", value); + wrangler->setIgnoreIdleTimer( value ); + } + } + + if (changedBits & kIOPMDriverAssertionCPUBit) + evaluatePolicy(kStimulusDarkWakeEvaluate); + + 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: - @@ -5754,6 +6470,7 @@ void IOPMrootDomain::pmStatsRecordEvent( bool stopping = eventIndex & kIOPMStatsEventStopFlag ? true:false; uint64_t delta; uint64_t nsec; + OSData *publishPMStats = NULL; eventIndex &= ~(kIOPMStatsEventStartFlag | kIOPMStatsEventStopFlag); @@ -5762,24 +6479,29 @@ void IOPMrootDomain::pmStatsRecordEvent( switch (eventIndex) { case kIOPMStatsHibernateImageWrite: if (starting) - pmStats.hibWrite.start = nsec; + gPMStats.hibWrite.start = nsec; else if (stopping) - pmStats.hibWrite.stop = nsec; + gPMStats.hibWrite.stop = nsec; if (stopping) { - delta = pmStats.hibWrite.stop - pmStats.hibWrite.start; + delta = gPMStats.hibWrite.stop - gPMStats.hibWrite.start; IOLog("PMStats: Hibernate write took %qd ms\n", delta/1000000ULL); } break; case kIOPMStatsHibernateImageRead: if (starting) - pmStats.hibRead.start = nsec; + gPMStats.hibRead.start = nsec; else if (stopping) - pmStats.hibRead.stop = nsec; + gPMStats.hibRead.stop = nsec; if (stopping) { - delta = pmStats.hibRead.stop - pmStats.hibRead.start; + 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; } @@ -5909,6 +6631,20 @@ IOReturn IOPMrootDomain::callPlatformFunction( 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); @@ -5916,8 +6652,13 @@ IOReturn IOPMrootDomain::callPlatformFunction( void IOPMrootDomain::tracePoint( uint8_t point ) { - if (!systemBooting) - pmTracer->tracePoint(point); + if (systemBooting) return; + + pmTracer->tracePoint(point); + +#if HIBERNATION + if (kIOPMTracePointSleepPowerPlaneDrivers == point) IOHibernateIOKitSleep(); +#endif } void IOPMrootDomain::tracePoint( uint8_t point, uint8_t data ) @@ -6635,7 +7376,7 @@ IOPMDriverAssertionID IOPMrootDomain::createPMAssertion( if (!pmAssertions) return 0; - + ret = pmAssertions->createAssertion(whichAssertionBits, assertionLevel, ownerService, ownerDescription, &newAssertion); if (kIOReturnSuccess == ret) @@ -6769,7 +7510,7 @@ PMSettingObject *PMSettingObject::pmSettingObject( for (unsigned int i=0; ipublishFeature( settings[i]->getCStringNoCopy(), + parent_arg->publishPMSetting( settings[i], supportedPowerSources, &pmso->publishedFeatureID[i] ); } } @@ -7111,7 +7852,8 @@ void IOPMTimeline::setEventsTrackedCount(uint32_t newTracked) } pmTraceMemoryDescriptor = IOBufferMemoryDescriptor::withOptions( - kIOMemoryKernelUserShared | kIODirectionIn, make_buf_size); + kIOMemoryKernelUserShared | kIODirectionIn | kIOMemoryMapperNone, + make_buf_size); if (!pmTraceMemoryDescriptor) { @@ -7246,18 +7988,7 @@ void PMAssertionsTracker::tabulate(void) if ((assertionsKernel != oldKernel) || (assertionsCombined != oldCombined)) { - owner->messageClients(kIOPMMessageDriverAssertionsChanged); - - if (((assertionsCombined & kIOPMDriverAssertionPreventDisplaySleepBit) != 0) - && ((oldCombined & kIOPMDriverAssertionPreventDisplaySleepBit) == 0)) - { - /* We react to a new PreventDisplaySleep assertion by waking the display - * with an activityTickle - */ - owner->evaluatePolicy(kStimulusDarkWakeActivityTickle); - } else { - owner->evaluatePolicy(kStimulusDarkWakeEvaluate); - } + owner->evaluateAssertions(assertionsCombined, oldCombined); } }