+ pmAssertions = PMAssertionsTracker::pmAssertionsTracker(this);
+
+ userDisabledAllSleep = false;
+ systemBooting = true;
+ idleSleepEnabled = false;
+ sleepSlider = 0;
+ idleSleepTimerPending = false;
+ wrangler = NULL;
+ clamshellClosed = false;
+ clamshellExists = false;
+#if DISPLAY_WRANGLER_PRESENT
+ clamshellDisabled = true;
+#else
+ clamshellDisabled = false;
+#endif
+ clamshellIgnoreClose = false;
+ acAdaptorConnected = true;
+ clamshellSleepDisableMask = 0;
+ gWakeReasonString[0] = '\0';
+
+ // Initialize to user active.
+ // Will never transition to user inactive w/o wrangler.
+ fullWakeReason = kFullWakeReasonLocalUser;
+ userIsActive = userWasActive = true;
+ clock_get_uptime(&gUserActiveAbsTime);
+ setProperty(gIOPMUserIsActiveKey.get(), 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;
+ 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);
+ _statsPowerCapsKey = OSSymbol::withCString(kIOPMStatsPowerCapabilityKey);
+ assertOnWakeSecs = -1;// Invalid value to prevent updates
+
+ pmStatsLock = IOLockAlloc();
+ idxPMCPUClamshell = kCPUUnknownIndex;
+ idxPMCPULimitedPower = kCPUUnknownIndex;
+
+ tmpDict = OSDictionary::withCapacity(1);
+ setProperty(kRootDomainSupportedFeatures, tmpDict.get());
+
+ // Set a default "SystemPowerProfileOverrideDict" for platform
+ // drivers without any overrides.
+ if (!propertyExists(kIOPMSystemDefaultOverrideKey)) {
+ tmpDict = OSDictionary::withCapacity(1);
+ setProperty(kIOPMSystemDefaultOverrideKey, tmpDict.get());
+ }
+
+ settingsCallbacks = OSDictionary::withCapacity(1);
+
+ // Create a list of the valid PM settings that we'll relay to
+ // interested clients in setProperties() => setPMSetting()
+ allowedPMSettings = OSArray::withObjects(
+ (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 **)noPublishSettingsArr,
+ kRootDomainNoPublishSettingsCount,
+ 0);
+
+ fPMSettingsDict = OSDictionary::withCapacity(5);
+ preventIdleSleepList = OSSet::withCapacity(8);
+ preventSystemSleepList = OSSet::withCapacity(2);
+
+ PMinit(); // creates gIOPMWorkLoop
+ gIOPMWorkLoop = getIOPMWorkloop();
+
+ // Create IOPMPowerStateQueue used to queue external power
+ // events, and to handle those events on the PM work loop.
+ pmPowerStateQueue = IOPMPowerStateQueue::PMPowerStateQueue(
+ this, OSMemberFunctionCast(IOEventSource::Action, this,
+ &IOPMrootDomain::dispatchPowerEvent));
+ gIOPMWorkLoop->addEventSource(pmPowerStateQueue);
+
+ _aotMode = 0;
+ _aotTimerES = IOTimerEventSource::timerEventSource(this,
+ OSMemberFunctionCast(IOTimerEventSource::Action,
+ this, &IOPMrootDomain::aotEvaluate));
+ gIOPMWorkLoop->addEventSource(_aotTimerES.get());
+
+ // create our power parent
+ gPatriarch = new IORootParent;
+ gPatriarch->init();
+ gPatriarch->attach(this);
+ gPatriarch->start(this);
+ gPatriarch->addPowerChild(this);
+
+ registerPowerDriver(this, ourPowerStates, NUM_POWER_STATES);
+ changePowerStateWithTagToPriv(ON_STATE, kCPSReasonInit);
+
+ // install power change handler
+ gSysPowerDownNotifier = registerPrioritySleepWakeInterest( &sysPowerDownHandler, this, NULL);
+
+#if DISPLAY_WRANGLER_PRESENT
+ wranglerIdleSettings = OSDictionary::withCapacity(1);
+ OSSharedPtr<OSNumber> wranglerIdlePeriod = OSNumber::withNumber(kDefaultWranglerIdlePeriod, 32);
+
+ if (wranglerIdleSettings && wranglerIdlePeriod) {
+ wranglerIdleSettings->setObject(kIORequestWranglerIdleKey,
+ wranglerIdlePeriod.get());
+ }
+
+#endif /* DISPLAY_WRANGLER_PRESENT */
+
+ lowLatencyAudioNotifierDict = OSDictionary::withCapacity(2);
+ lowLatencyAudioNotifyStateSym = OSSymbol::withCString("LowLatencyAudioNotifyState");
+ lowLatencyAudioNotifyTimestampSym = OSSymbol::withCString("LowLatencyAudioNotifyTimestamp");
+ lowLatencyAudioNotifyStateVal = OSNumber::withNumber(0ull, 32);
+ lowLatencyAudioNotifyTimestampVal = OSNumber::withNumber(0ull, 64);
+
+ if (lowLatencyAudioNotifierDict && lowLatencyAudioNotifyStateSym && lowLatencyAudioNotifyTimestampSym &&
+ lowLatencyAudioNotifyStateVal && lowLatencyAudioNotifyTimestampVal) {
+ lowLatencyAudioNotifierDict->setObject(lowLatencyAudioNotifyStateSym.get(), lowLatencyAudioNotifyStateVal.get());
+ lowLatencyAudioNotifierDict->setObject(lowLatencyAudioNotifyTimestampSym.get(), lowLatencyAudioNotifyTimestampVal.get());
+ }
+
+ OSSharedPtr<const OSSymbol> ucClassName = OSSymbol::withCStringNoCopy("RootDomainUserClient");
+ setProperty(gIOUserClientClassKey, const_cast<OSObject *>(static_cast<const OSObject *>(ucClassName.get())));
+
+ // IOBacklightDisplay can take a long time to load at boot, or it may
+ // not load at all if you're booting with clamshell closed. We publish
+ // 'DisplayDims' here redundantly to get it published early and at all.
+ OSSharedPtr<OSDictionary> matching;
+ matching = serviceMatching("IOPMPowerSource");
+ psIterator = getMatchingServices(matching.get());
+
+ if (psIterator && psIterator->getNextObject()) {
+ // There's at least one battery on the system, so we publish
+ // 'DisplayDims' support for the LCD.
+ publishFeature("DisplayDims");
+ }
+
+ // read swd_panic boot-arg
+ PE_parse_boot_argn("swd_panic", &gSwdPanic, sizeof(gSwdPanic));
+ gWillShutdownSysctlRegistered = true;
+
+#if HIBERNATION
+#if defined(__arm64__)
+#endif /* defined(__arm64__) */
+ IOHibernateSystemInit(this);
+#endif
+
+ registerService(); // let clients find us
+
+ return true;
+}
+
+//******************************************************************************
+// setProperties
+//
+// Receive a setProperty call
+// The "System Boot" property means the system is completely booted.
+//******************************************************************************
+
+IOReturn
+IOPMrootDomain::setProperties( OSObject * props_obj )
+{
+ IOReturn return_value = kIOReturnSuccess;
+ OSDictionary *dict = OSDynamicCast(OSDictionary, props_obj);
+ OSBoolean *b = NULL;
+ OSNumber *n = NULL;
+ const OSSymbol *key = NULL;
+ OSObject *obj = NULL;
+ OSSharedPtr<OSCollectionIterator> iter;
+
+ if (!dict) {
+ return kIOReturnBadArgument;
+ }
+
+ bool clientEntitled = false;
+ {
+ OSSharedPtr<OSObject> obj = IOUserClient::copyClientEntitlement(current_task(), kRootDomainEntitlementSetProperty);
+ clientEntitled = (obj == kOSBooleanTrue);
+ }
+
+ if (!clientEntitled) {
+ const char * errorSuffix = NULL;
+
+ // IOPMSchedulePowerEvent() clients may not be entitled, but must be root.
+ // That API can set 6 possible keys that are checked below.
+ if ((dict->getCount() == 1) &&
+ (dict->getObject(gIOPMSettingAutoWakeSecondsKey.get()) ||
+ dict->getObject(gIOPMSettingAutoPowerSecondsKey.get()) ||
+ dict->getObject(gIOPMSettingAutoWakeCalendarKey.get()) ||
+ dict->getObject(gIOPMSettingAutoPowerCalendarKey.get()) ||
+ dict->getObject(gIOPMSettingDebugWakeRelativeKey.get()) ||
+ dict->getObject(gIOPMSettingDebugPowerRelativeKey.get()))) {
+ return_value = IOUserClient::clientHasPrivilege(current_task(), kIOClientPrivilegeAdministrator);
+ if (return_value != kIOReturnSuccess) {
+ errorSuffix = "privileged";
+ }
+ } else {
+ return_value = kIOReturnNotPermitted;
+ errorSuffix = "entitled";
+ }
+
+ if (return_value != kIOReturnSuccess) {
+ OSSharedPtr<OSString> procName(IOCopyLogNameForPID(proc_selfpid()), OSNoRetain);
+ DLOG("%s failed, process %s is not %s\n", __func__,
+ procName ? procName->getCStringNoCopy() : "", errorSuffix);
+ return return_value;
+ }
+ }
+
+ OSSharedPtr<const OSSymbol> publish_simulated_battery_string = OSSymbol::withCString("SoftwareSimulatedBatteries");
+ OSSharedPtr<const OSSymbol> boot_complete_string = OSSymbol::withCString("System Boot Complete");
+ OSSharedPtr<const OSSymbol> sys_shutdown_string = OSSymbol::withCString("System Shutdown");
+ OSSharedPtr<const OSSymbol> stall_halt_string = OSSymbol::withCString("StallSystemAtHalt");
+ OSSharedPtr<const OSSymbol> battery_warning_disabled_string = OSSymbol::withCString("BatteryWarningsDisabled");
+ OSSharedPtr<const OSSymbol> idle_seconds_string = OSSymbol::withCString("System Idle Seconds");
+ OSSharedPtr<const OSSymbol> sleepdisabled_string = OSSymbol::withCString("SleepDisabled");
+ OSSharedPtr<const OSSymbol> ondeck_sleepwake_uuid_string = OSSymbol::withCString(kIOPMSleepWakeUUIDKey);
+ OSSharedPtr<const OSSymbol> loginwindow_progress_string = OSSymbol::withCString(kIOPMLoginWindowProgressKey);
+ OSSharedPtr<const OSSymbol> coredisplay_progress_string = OSSymbol::withCString(kIOPMCoreDisplayProgressKey);
+ OSSharedPtr<const OSSymbol> coregraphics_progress_string = OSSymbol::withCString(kIOPMCoreGraphicsProgressKey);
+#if DEBUG || DEVELOPMENT
+ OSSharedPtr<const OSSymbol> clamshell_close_string = OSSymbol::withCString("IOPMTestClamshellClose");
+ OSSharedPtr<const OSSymbol> clamshell_open_string = OSSymbol::withCString("IOPMTestClamshellOpen");
+ OSSharedPtr<const OSSymbol> ac_detach_string = OSSymbol::withCString("IOPMTestACDetach");
+ OSSharedPtr<const OSSymbol> ac_attach_string = OSSymbol::withCString("IOPMTestACAttach");
+ OSSharedPtr<const OSSymbol> desktopmode_set_string = OSSymbol::withCString("IOPMTestDesktopModeSet");
+ OSSharedPtr<const OSSymbol> desktopmode_remove_string = OSSymbol::withCString("IOPMTestDesktopModeRemove");
+#endif
+
+#if HIBERNATION
+ OSSharedPtr<const OSSymbol> hibernatemode_string = OSSymbol::withCString(kIOHibernateModeKey);
+ OSSharedPtr<const OSSymbol> hibernatefile_string = OSSymbol::withCString(kIOHibernateFileKey);
+ OSSharedPtr<const OSSymbol> hibernatefilemin_string = OSSymbol::withCString(kIOHibernateFileMinSizeKey);
+ OSSharedPtr<const OSSymbol> hibernatefilemax_string = OSSymbol::withCString(kIOHibernateFileMaxSizeKey);
+ OSSharedPtr<const OSSymbol> hibernatefreeratio_string = OSSymbol::withCString(kIOHibernateFreeRatioKey);
+ OSSharedPtr<const OSSymbol> hibernatefreetime_string = OSSymbol::withCString(kIOHibernateFreeTimeKey);
+#endif
+
+ iter = OSCollectionIterator::withCollection(dict);
+ if (!iter) {
+ return_value = kIOReturnNoMemory;
+ goto exit;
+ }
+
+ while ((key = (const OSSymbol *) iter->getNextObject()) &&
+ (obj = dict->getObject(key))) {
+ if (key->isEqualTo(publish_simulated_battery_string.get())) {
+ if (OSDynamicCast(OSBoolean, obj)) {
+ publishResource(key, kOSBooleanTrue);
+ }
+ } else if (key->isEqualTo(idle_seconds_string.get())) {
+ if ((n = OSDynamicCast(OSNumber, obj))) {
+ setProperty(key, n);
+ idleSeconds = n->unsigned32BitValue();
+ }
+ } else if (key->isEqualTo(boot_complete_string.get())) {
+ pmPowerStateQueue->submitPowerEvent(kPowerEventSystemBootCompleted);
+ } else if (key->isEqualTo(sys_shutdown_string.get())) {
+ if ((b = OSDynamicCast(OSBoolean, obj))) {
+ pmPowerStateQueue->submitPowerEvent(kPowerEventSystemShutdown, (void *) b);
+ }
+ } else if (key->isEqualTo(battery_warning_disabled_string.get())) {
+ setProperty(key, obj);
+ }
+#if HIBERNATION
+ else if (key->isEqualTo(hibernatemode_string.get()) ||
+ key->isEqualTo(hibernatefilemin_string.get()) ||
+ key->isEqualTo(hibernatefilemax_string.get()) ||
+ key->isEqualTo(hibernatefreeratio_string.get()) ||
+ key->isEqualTo(hibernatefreetime_string.get())) {
+ if ((n = OSDynamicCast(OSNumber, obj))) {
+ setProperty(key, n);
+ }
+ } else if (key->isEqualTo(hibernatefile_string.get())) {
+ OSString * str = OSDynamicCast(OSString, obj);
+ if (str) {
+ setProperty(key, str);
+ }
+ }
+#endif
+ else if (key->isEqualTo(sleepdisabled_string.get())) {
+ if ((b = OSDynamicCast(OSBoolean, obj))) {
+ setProperty(key, b);
+ pmPowerStateQueue->submitPowerEvent(kPowerEventUserDisabledSleep, (void *) b);
+ }
+ } else if (key->isEqualTo(ondeck_sleepwake_uuid_string.get())) {
+ obj->retain();
+ pmPowerStateQueue->submitPowerEvent(kPowerEventQueueSleepWakeUUID, (void *)obj);
+ } else if (key->isEqualTo(loginwindow_progress_string.get())) {
+ if (pmTracer && (n = OSDynamicCast(OSNumber, obj))) {
+ uint32_t data = n->unsigned32BitValue();
+ pmTracer->traceComponentWakeProgress(kIOPMLoginWindowProgress, data);
+ kdebugTrace(kPMLogComponentWakeProgress, 0, kIOPMLoginWindowProgress, data);
+ }
+ } else if (key->isEqualTo(coredisplay_progress_string.get())) {
+ if (pmTracer && (n = OSDynamicCast(OSNumber, obj))) {
+ uint32_t data = n->unsigned32BitValue();
+ pmTracer->traceComponentWakeProgress(kIOPMCoreDisplayProgress, data);
+ kdebugTrace(kPMLogComponentWakeProgress, 0, kIOPMCoreDisplayProgress, data);
+ }
+ } else if (key->isEqualTo(coregraphics_progress_string.get())) {
+ if (pmTracer && (n = OSDynamicCast(OSNumber, obj))) {
+ uint32_t data = n->unsigned32BitValue();
+ pmTracer->traceComponentWakeProgress(kIOPMCoreGraphicsProgress, data);
+ kdebugTrace(kPMLogComponentWakeProgress, 0, kIOPMCoreGraphicsProgress, data);
+ }
+ } else if (key->isEqualTo(kIOPMDeepSleepEnabledKey) ||
+ key->isEqualTo(kIOPMDestroyFVKeyOnStandbyKey) ||
+ key->isEqualTo(kIOPMAutoPowerOffEnabledKey) ||
+ key->isEqualTo(stall_halt_string.get())) {
+ if ((b = OSDynamicCast(OSBoolean, obj))) {
+ setProperty(key, b);
+ }
+ } else if (key->isEqualTo(kIOPMDeepSleepDelayKey) ||
+ key->isEqualTo(kIOPMDeepSleepTimerKey) ||
+ key->isEqualTo(kIOPMAutoPowerOffDelayKey) ||
+ key->isEqualTo(kIOPMAutoPowerOffTimerKey)) {
+ if ((n = OSDynamicCast(OSNumber, obj))) {
+ setProperty(key, n);
+ }
+ } else if (key->isEqualTo(kIOPMUserWakeAlarmScheduledKey)) {
+ if (kOSBooleanTrue == obj) {
+ OSBitOrAtomic(kIOPMAlarmBitCalendarWake, &_userScheduledAlarmMask);
+ } else {
+ OSBitAndAtomic(~kIOPMAlarmBitCalendarWake, &_userScheduledAlarmMask);
+ }
+ DLOG("_userScheduledAlarmMask 0x%x\n", (uint32_t) _userScheduledAlarmMask);
+ }
+#if DEBUG || DEVELOPMENT
+ else if (key->isEqualTo(clamshell_close_string.get())) {
+ DLOG("SetProperties: setting clamshell close\n");
+ UInt32 msg = kIOPMClamshellClosed;
+ pmPowerStateQueue->submitPowerEvent(kPowerEventReceivedPowerNotification, (void *)(uintptr_t)msg);
+ } else if (key->isEqualTo(clamshell_open_string.get())) {
+ DLOG("SetProperties: setting clamshell open\n");
+ UInt32 msg = kIOPMClamshellOpened;
+ pmPowerStateQueue->submitPowerEvent(kPowerEventReceivedPowerNotification, (void *)(uintptr_t)msg);
+ } else if (key->isEqualTo(ac_detach_string.get())) {
+ DLOG("SetProperties: setting ac detach\n");
+ UInt32 msg = kIOPMSetACAdaptorConnected;
+ pmPowerStateQueue->submitPowerEvent(kPowerEventReceivedPowerNotification, (void *)(uintptr_t)msg);
+ } else if (key->isEqualTo(ac_attach_string.get())) {
+ DLOG("SetProperties: setting ac attach\n");
+ UInt32 msg = kIOPMSetACAdaptorConnected | kIOPMSetValue;
+ pmPowerStateQueue->submitPowerEvent(kPowerEventReceivedPowerNotification, (void *)(uintptr_t)msg);
+ } else if (key->isEqualTo(desktopmode_set_string.get())) {
+ DLOG("SetProperties: setting desktopmode");
+ UInt32 msg = kIOPMSetDesktopMode | kIOPMSetValue;
+ pmPowerStateQueue->submitPowerEvent(kPowerEventReceivedPowerNotification, (void *)(uintptr_t)msg);
+ } else if (key->isEqualTo(desktopmode_remove_string.get())) {
+ DLOG("SetProperties: removing desktopmode\n");
+ UInt32 msg = kIOPMSetDesktopMode;
+ pmPowerStateQueue->submitPowerEvent(kPowerEventReceivedPowerNotification, (void *)(uintptr_t)msg);
+ }
+#endif
+ // Relay our allowed PM settings onto our registered PM clients
+ else if ((allowedPMSettings->getNextIndexOfObject(key, 0) != (unsigned int) -1)) {
+ return_value = setPMSetting(key, obj);
+ if (kIOReturnSuccess != return_value) {
+ break;
+ }
+ } else {
+ DLOG("setProperties(%s) not handled\n", key->getCStringNoCopy());
+ }
+ }
+
+exit:
+ return return_value;
+}
+
+// MARK: -
+// MARK: Aggressiveness
+
+//******************************************************************************
+// 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;
+
+ if ((type > UINT_MAX) || (value > UINT_MAX)) {
+ return kIOReturnBadArgument;
+ }
+
+ if (type == kPMMinutesToDim || type == kPMMinutesToSleep) {
+ DLOG("setAggressiveness(%x) %s = %u\n",
+ (uint32_t) options, getAggressivenessTypeString((uint32_t) type), (uint32_t) value);
+ } else {
+ DEBUG_LOG("setAggressiveness(%x) %s = %u\n",
+ (uint32_t) options, getAggressivenessTypeString((uint32_t) type), (uint32_t) value);
+ }
+
+ request = IONew(AggressivesRequest, 1);
+ if (!request) {
+ return kIOReturnNoMemory;
+ }
+
+ memset(request, 0, sizeof(*request));
+ request->options = options;
+ request->dataType = kAggressivesRequestTypeRecord;
+ request->data.record.type = (uint32_t) type;
+ request->data.record.value = (uint32_t) value;
+
+ AGGRESSIVES_LOCK();
+
+ // Update disk quick spindown flag used by getAggressiveness().
+ // Never merge requests with quick spindown flags set.
+
+ if (options & kAggressivesOptionQuickSpindownEnable) {
+ gAggressivesState |= kAggressivesStateQuickSpindown;
+ } else if (options & kAggressivesOptionQuickSpindownDisable) {
+ gAggressivesState &= ~kAggressivesStateQuickSpindown;
+ } else {
+ // Coalesce requests with identical aggressives types.
+ // Deal with callers that calls us too "aggressively".
+
+ queue_iterate(&aggressivesQueue, entry, AggressivesRequest *, chain)
+ {
+ if ((entry->dataType == kAggressivesRequestTypeRecord) &&
+ (entry->data.record.type == type) &&
+ ((entry->options & kAggressivesOptionQuickSpindownMask) == 0)) {
+ entry->data.record.value = (uint32_t) value;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ queue_enter(&aggressivesQueue, request, AggressivesRequest *, chain);
+ }
+
+ AGGRESSIVES_UNLOCK();
+
+ if (found) {
+ IODelete(request, AggressivesRequest, 1);
+ }
+
+ if (options & kAggressivesOptionSynchronous) {
+ handleAggressivesRequests(); // not truly synchronous
+ } else {
+ thread_call_enter(aggressivesThreadCall);
+ }
+
+ return kIOReturnSuccess;
+}
+
+//******************************************************************************
+// getAggressiveness
+//
+// Override IOService::setAggressiveness()
+// Fetch the aggressiveness factor with the given type.
+//******************************************************************************
+
+IOReturn
+IOPMrootDomain::getAggressiveness(
+ unsigned long type,
+ unsigned long * outLevel )
+{
+ uint32_t value = 0;
+ int source = 0;
+
+ if (!outLevel || (type > UINT_MAX)) {
+ return kIOReturnBadArgument;
+ }
+
+ AGGRESSIVES_LOCK();
+
+ // Disk quick spindown in effect, report value = 1
+
+ if ((gAggressivesState & kAggressivesStateQuickSpindown) &&
+ (type == kPMMinutesToSpinDown)) {
+ value = kAggressivesMinValue;
+ source = 1;
+ }
+
+ // Consult the pending request queue.
+
+ if (!source) {
+ AggressivesRequest * entry;
+
+ queue_iterate(&aggressivesQueue, entry, AggressivesRequest *, chain)
+ {
+ if ((entry->dataType == kAggressivesRequestTypeRecord) &&
+ (entry->data.record.type == type) &&
+ ((entry->options & kAggressivesOptionQuickSpindownMask) == 0)) {
+ value = entry->data.record.value;
+ source = 2;
+ break;
+ }
+ }
+ }
+
+ // Consult the backend records.
+
+ if (!source && aggressivesData) {
+ AggressivesRecord * record;
+ int i, count;
+
+ count = aggressivesData->getLength() / sizeof(AggressivesRecord);
+ record = (AggressivesRecord *) aggressivesData->getBytesNoCopy();
+
+ for (i = 0; i < count; i++, record++) {
+ if (record->type == type) {
+ value = record->value;
+ source = 3;
+ break;
+ }
+ }
+ }
+
+ AGGRESSIVES_UNLOCK();
+
+ if (source) {
+ *outLevel = (unsigned long) value;
+ return kIOReturnSuccess;
+ } else {
+ DLOG("getAggressiveness type 0x%x not found\n", (uint32_t) type);
+ *outLevel = 0; // default return = 0, driver may not check for error
+ return kIOReturnInvalid;
+ }
+}
+
+//******************************************************************************
+// joinAggressiveness
+//
+// Request from IOService to join future aggressiveness broadcasts.
+//******************************************************************************
+
+IOReturn
+IOPMrootDomain::joinAggressiveness(
+ IOService * service )
+{
+ AggressivesRequest * request;
+
+ if (!service || (service == this)) {
+ return kIOReturnBadArgument;
+ }
+
+ DEBUG_LOG("joinAggressiveness %s %p\n", service->getName(), OBFUSCATE(service));
+
+ request = IONew(AggressivesRequest, 1);
+ if (!request) {
+ return kIOReturnNoMemory;
+ }
+
+ memset(request, 0, sizeof(*request));
+ request->dataType = kAggressivesRequestTypeService;
+ request->data.service.reset(service, OSRetain); // released by synchronizeAggressives()
+
+ AGGRESSIVES_LOCK();
+ queue_enter(&aggressivesQueue, request, AggressivesRequest *, chain);
+ AGGRESSIVES_UNLOCK();
+
+ thread_call_enter(aggressivesThreadCall);
+
+ return kIOReturnSuccess;
+}
+
+//******************************************************************************
+// handleAggressivesRequests
+//
+// Backend thread processes all incoming aggressiveness requests in the queue.