+void
+IOPMrootDomain::adjustPowerState( bool sleepASAP )
+{
+ DEBUG_LOG("adjustPowerState ps %s, asap %d, idleSleepEnabled %d\n",
+ getPowerStateString((uint32_t) getPowerState()), sleepASAP, idleSleepEnabled);
+
+ ASSERT_GATED();
+
+#if !(defined(RC_HIDE_N144) || defined(RC_HIDE_N146))
+ if (_aotNow) {
+ bool exitNow;
+
+ if (AOT_STATE != getPowerState()) {
+ return;
+ }
+ WAKEEVENT_LOCK();
+ exitNow = aotShouldExit(true, false);
+ if (!exitNow
+ && !_aotTimerScheduled
+ && (kIOPMWakeEventAOTPossibleExit == (kIOPMWakeEventAOTPossibleFlags & _aotPendingFlags))) {
+ _aotTimerScheduled = true;
+ if (_aotLingerTime) {
+ _aotTimerES->setTimeout(_aotLingerTime);
+ } else {
+ _aotTimerES->setTimeout(800, kMillisecondScale);
+ }
+ }
+ WAKEEVENT_UNLOCK();
+ if (exitNow) {
+ aotExit(true);
+ } else {
+ _aotReadyToFullWake = true;
+ if (!_aotTimerScheduled) {
+ privateSleepSystem(kIOPMSleepReasonSoftware);
+ }
+ }
+ return;
+ }
+#endif /* (defined(RC_HIDE_N144) || defined(RC_HIDE_N146)) */
+
+ if ((!idleSleepEnabled) || !checkSystemSleepEnabled()) {
+ changePowerStateToPriv(getRUN_STATE());
+ } else if (sleepASAP) {
+ changePowerStateToPriv(SLEEP_STATE);
+ }
+}
+
+void
+IOPMrootDomain::handleDisplayPowerOn()
+{
+ if (!wrangler) {
+ return;
+ }
+ if (displayPowerOnRequested) {
+ if (!checkSystemCanSustainFullWake()) {
+ return;
+ }
+
+ // 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 );
+ }
+ } 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.
+
+ wrangler->changePowerStateForRootDomain(kWranglerPowerStateMin + 1);
+ wrangler->changePowerStateForRootDomain(kWranglerPowerStateMin);
+ }
+}
+
+//******************************************************************************
+// dispatchPowerEvent
+//
+// IOPMPowerStateQueue callback function. Running on PM work loop thread.
+//******************************************************************************
+
+void
+IOPMrootDomain::dispatchPowerEvent(
+ uint32_t event, void * arg0, uint64_t arg1 )
+{
+ ASSERT_GATED();
+
+ switch (event) {
+ case kPowerEventFeatureChanged:
+ DMSG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1);
+ messageClients(kIOPMMessageFeatureChange, this);
+ break;
+
+ case kPowerEventReceivedPowerNotification:
+ DMSG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1);
+ handlePowerNotification((UInt32)(uintptr_t) arg0 );
+ break;
+
+ case kPowerEventSystemBootCompleted:
+ DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1);
+ if (systemBooting) {
+ systemBooting = false;
+
+ // read noidle setting from Device Tree
+ IORegistryEntry *defaults = IORegistryEntry::fromPath("IODeviceTree:/defaults");
+ if (defaults != NULL) {
+ OSData *data = OSDynamicCast(OSData, defaults->getProperty("no-idle"));
+ if ((data != NULL) && (data->getLength() == 4)) {
+ gNoIdleFlag = *(uint32_t*)data->getBytesNoCopy();
+ DLOG("Setting gNoIdleFlag to %u from device tree\n", gNoIdleFlag);
+ }
+ defaults->release();
+ }
+ if (lowBatteryCondition) {
+ privateSleepSystem(kIOPMSleepReasonLowPower);
+
+ // The rest is unnecessary since the system is expected
+ // to sleep immediately. The following wake will update
+ // everything.
+ break;
+ }
+
+ sleepWakeDebugMemAlloc();
+ saveFailureData2File();
+
+ // If lid is closed, re-send lid closed notification
+ // now that booting is complete.
+ if (clamshellClosed) {
+ handlePowerNotification(kLocalEvalClamshellCommand);
+ }
+ evaluatePolicy( kStimulusAllowSystemSleepChanged );
+ }
+ break;
+
+ case kPowerEventSystemShutdown:
+ DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1);
+ if (kOSBooleanTrue == (OSBoolean *) arg0) {
+ /* We set systemShutdown = true during shutdown
+ * 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.
+ */
+ systemShutdown = true;
+ } else {
+ /*
+ * A shutdown was initiated, but then the shutdown
+ * was cancelled, clearing systemShutdown to false here.
+ */
+ systemShutdown = false;
+ }
+ break;
+
+ case kPowerEventUserDisabledSleep:
+ DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1);
+ userDisabledAllSleep = (kOSBooleanTrue == (OSBoolean *) arg0);
+ break;
+
+ case kPowerEventRegisterSystemCapabilityClient:
+ DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1);
+ if (systemCapabilityNotifier) {
+ systemCapabilityNotifier->release();
+ systemCapabilityNotifier = NULL;
+ }
+ if (arg0) {
+ systemCapabilityNotifier = (IONotifier *) arg0;
+ systemCapabilityNotifier->retain();
+ }
+ /* intentional fall-through */
+ [[clang::fallthrough]];
+
+ case kPowerEventRegisterKernelCapabilityClient:
+ DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1);
+ if (!_joinedCapabilityClients) {
+ _joinedCapabilityClients = OSSet::withCapacity(8);
+ }
+ if (arg0) {
+ IONotifier * notify = (IONotifier *) arg0;
+ if (_joinedCapabilityClients) {
+ _joinedCapabilityClients->setObject(notify);
+ synchronizePowerTree( kIOPMSyncNoChildNotify );
+ }
+ notify->release();
+ }
+ break;
+
+ case kPowerEventPolicyStimulus:
+ DMSG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1);
+ if (arg0) {
+ int stimulus = (uintptr_t) arg0;
+ evaluatePolicy( stimulus, (uint32_t) arg1 );
+ }
+ break;
+
+ case kPowerEventAssertionCreate:
+ DMSG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1);
+ if (pmAssertions) {
+ pmAssertions->handleCreateAssertion((OSData *)arg0);
+ }
+ break;
+
+
+ case kPowerEventAssertionRelease:
+ DMSG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1);
+ if (pmAssertions) {
+ pmAssertions->handleReleaseAssertion(arg1);
+ }
+ break;
+
+ case kPowerEventAssertionSetLevel:
+ DMSG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1);
+ if (pmAssertions) {
+ pmAssertions->handleSetAssertionLevel(arg1, (IOPMDriverAssertionLevel)(uintptr_t)arg0);
+ }
+ break;
+
+ case kPowerEventQueueSleepWakeUUID:
+ DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1);
+ handleQueueSleepWakeUUID((OSObject *)arg0);
+ break;
+ case kPowerEventPublishSleepWakeUUID:
+ DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1);
+ handlePublishSleepWakeUUID((bool)arg0);
+ break;
+
+ case kPowerEventSetDisplayPowerOn:
+ DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1);
+ if (!wrangler) {
+ break;
+ }
+ if (arg1 != 0) {
+ displayPowerOnRequested = true;
+ } else {
+ displayPowerOnRequested = false;
+ }
+ handleDisplayPowerOn();
+ break;
+ }
+}
+
+//******************************************************************************
+// systemPowerEventOccurred
+//
+// The power controller is notifying us of a hardware-related power management
+// event that we must handle.
+//
+// systemPowerEventOccurred covers the same functionality that
+// receivePowerNotification does; it simply provides a richer API for conveying
+// more information.
+//******************************************************************************
+
+IOReturn
+IOPMrootDomain::systemPowerEventOccurred(
+ const OSSymbol *event,
+ uint32_t intValue)
+{
+ IOReturn attempt = kIOReturnSuccess;
+ OSNumber *newNumber = NULL;
+
+ if (!event) {
+ return kIOReturnBadArgument;
+ }
+
+ newNumber = OSNumber::withNumber(intValue, 8 * sizeof(intValue));
+ if (!newNumber) {
+ return kIOReturnInternalError;
+ }
+
+ attempt = systemPowerEventOccurred(event, (OSObject *)newNumber);
+
+ newNumber->release();
+
+ 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;
+
+ if (!event || !value) {
+ return kIOReturnBadArgument;
+ }
+
+ // LOCK
+ // We reuse featuresDict Lock because it already exists and guards
+ // the very infrequently used publish/remove feature mechanism; so there's zero rsk
+ // of stepping on that lock.
+ if (featuresDictLock) {
+ IOLockLock(featuresDictLock);
+ }
+
+ thermalsDict = (OSDictionary *)getProperty(kIOPMRootDomainPowerStatusKey);
+
+ if (thermalsDict && OSDynamicCast(OSDictionary, thermalsDict)) {
+ thermalsDict = OSDictionary::withDictionary(thermalsDict);
+ } else {
+ thermalsDict = OSDictionary::withCapacity(1);
+ }
+
+ if (!thermalsDict) {
+ shouldUpdate = false;
+ goto exit;
+ }
+
+ thermalsDict->setObject(event, value);
+
+ setProperty(kIOPMRootDomainPowerStatusKey, thermalsDict);
+
+ thermalsDict->release();
+
+exit:
+ // UNLOCK
+ if (featuresDictLock) {
+ IOLockUnlock(featuresDictLock);
+ }
+
+ if (shouldUpdate) {
+ if (event &&
+ event->isEqualTo(kIOPMThermalLevelWarningKey)) {
+ setThermalState(value);
+ }
+ messageClients(kIOPMMessageSystemPowerEventOccurred, (void *)NULL);
+ }
+
+ return kIOReturnSuccess;
+}
+
+//******************************************************************************
+// receivePowerNotification
+//
+// The power controller is notifying us of a hardware-related power management
+// event that we must handle. This may be a result of an 'environment' interrupt
+// from the power mgt micro.
+//******************************************************************************
+
+IOReturn
+IOPMrootDomain::receivePowerNotification( UInt32 msg )
+{
+ if (msg & kIOPMPowerButton) {
+ uint32_t currentPhase = pmTracer->getTracePhase();
+ if (currentPhase != kIOPMTracePointSystemUp && currentPhase > kIOPMTracePointSystemSleep) {
+ DEBUG_LOG("power button pressed during wake. phase = %u\n", currentPhase);
+ swd_flags |= SWD_PWR_BTN_STACKSHOT;
+ thread_call_enter(powerButtonDown);
+ } else {
+ DEBUG_LOG("power button pressed when system is up\n");
+ }
+ } else if (msg & kIOPMPowerButtonUp) {
+ if (swd_flags & SWD_PWR_BTN_STACKSHOT) {
+ swd_flags &= ~SWD_PWR_BTN_STACKSHOT;
+ thread_call_enter(powerButtonUp);
+ }
+ } else {
+ pmPowerStateQueue->submitPowerEvent(
+ kPowerEventReceivedPowerNotification, (void *)(uintptr_t) msg );
+ }
+ return kIOReturnSuccess;
+}
+
+void
+IOPMrootDomain::handlePowerNotification( UInt32 msg )
+{
+ bool eval_clamshell = false;
+ bool eval_clamshell_alarm = false;
+
+ ASSERT_GATED();
+
+ /*
+ * Local (IOPMrootDomain only) eval clamshell command
+ */
+ if (msg & kLocalEvalClamshellCommand) {
+ if (isRTCAlarmWake) {
+ eval_clamshell_alarm = true;
+
+ // reset isRTCAlarmWake. This evaluation should happen only once
+ // on RTC/Alarm wake. Any clamshell events after wake should follow
+ // the regular evaluation
+ isRTCAlarmWake = false;
+ } else {
+ eval_clamshell = true;
+ }
+ }
+
+ /*
+ * Overtemp
+ */
+ if (msg & kIOPMOverTemp) {
+ MSG("PowerManagement emergency overtemp signal. Going to sleep!");
+ privateSleepSystem(kIOPMSleepReasonThermalEmergency);
+ }
+
+ /*
+ * Forward DW thermal notification to client, if system is not going to sleep
+ */
+ if ((msg & kIOPMDWOverTemp) && (_systemTransitionType != kSystemTransitionSleep)) {
+ DLOG("DarkWake thermal limits message received!\n");
+
+ messageClients(kIOPMMessageDarkWakeThermalEmergency);
+ }
+
+ /*
+ * Sleep Now!
+ */
+ if (msg & kIOPMSleepNow) {
+ privateSleepSystem(kIOPMSleepReasonSoftware);
+ }
+
+ /*
+ * Power Emergency
+ */
+ if (msg & kIOPMPowerEmergency) {
+ lowBatteryCondition = true;
+ privateSleepSystem(kIOPMSleepReasonLowPower);
+ }
+
+ /*
+ * Clamshell OPEN
+ */
+ if (msg & kIOPMClamshellOpened) {
+ DLOG("Clamshell opened\n");
+ // Received clamshel open message from clamshell controlling driver
+ // Update our internal state and tell general interest clients
+ 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();
+ }
+
+ // Tell PMCPU
+ informCPUStateChange(kInformLid, 0);
+
+ // Tell general interest clients
+ sendClientClamshellNotification();
+
+ bool aborting = ((lastSleepReason == kIOPMSleepReasonClamshell)
+ || (lastSleepReason == kIOPMSleepReasonIdle)
+ || (lastSleepReason == kIOPMSleepReasonMaintenance));
+ if (aborting) {
+ userActivityCount++;
+ }
+ DLOG("clamshell tickled %d lastSleepReason %d\n", userActivityCount, lastSleepReason);
+ }
+
+ /*
+ * Clamshell CLOSED
+ * Send the clamshell interest notification since the lid is closing.
+ */
+ if (msg & kIOPMClamshellClosed) {
+ if (clamshellClosed && clamshellExists) {
+ DLOG("Ignoring redundant Clamshell close event\n");
+ } else {
+ DLOG("Clamshell closed\n");
+ // Received clamshel open message from clamshell controlling driver
+ // Update our internal state and tell general interest clients
+ clamshellClosed = true;
+ clamshellExists = true;
+
+ // Tell PMCPU
+ informCPUStateChange(kInformLid, 1);
+
+ // Tell general interest clients
+ sendClientClamshellNotification();
+
+ // And set eval_clamshell = so we can attempt
+ eval_clamshell = true;
+ }
+ }
+
+ /*
+ * Set Desktop mode (sent from graphics)
+ *
+ * -> reevaluate lid state
+ */
+ if (msg & kIOPMSetDesktopMode) {
+ DLOG("Desktop mode\n");
+ desktopMode = (0 != (msg & kIOPMSetValue));
+ msg &= ~(kIOPMSetDesktopMode | kIOPMSetValue);
+
+ sendClientClamshellNotification();
+
+ // Re-evaluate the lid state
+ eval_clamshell = true;
+ }
+
+ /*
+ * AC Adaptor connected
+ *
+ * -> reevaluate lid state
+ */
+ if (msg & kIOPMSetACAdaptorConnected) {
+ acAdaptorConnected = (0 != (msg & kIOPMSetValue));
+ msg &= ~(kIOPMSetACAdaptorConnected | kIOPMSetValue);
+
+ // Tell CPU PM
+ informCPUStateChange(kInformAC, !acAdaptorConnected);
+
+ // Tell BSD if AC is connected
+ // 0 == external power source; 1 == on battery
+ post_sys_powersource(acAdaptorConnected ? 0:1);