+ if (getPMRequestType() == kIOPMRequestTypeSynchronizePowerTree)
+ {
+ powerChangeDone(fCurrentPowerState);
+ }
+ else if (fAdvisoryTickleUsed)
+ {
+ // Not root domain and advisory tickle target.
+ // Re-adjust power after power tree sync at the 'did' pass
+ // to recompute desire and adjust power state between dark
+ // and full wake transitions. Root domain is responsible
+ // for calling setAdvisoryTickleEnable() before starting
+ // the kIOPMSynchronize power change.
+
+ if (!fAdjustPowerScheduled &&
+ (fHeadNoteChangeFlags & kIOPMDomainDidChange))
+ {
+ IOPMRequest * request;
+ request = acquirePMRequest( this, kIOPMRequestTypeAdjustPowerState );
+ if (request)
+ {
+ submitPMRequest( request );
+ fAdjustPowerScheduled = true;
+ }
+ }
+ }
+
+ return;
+ }
+
+ // our power change
+ if ( fHeadNoteChangeFlags & kIOPMSelfInitiated )
+ {
+ // could our driver switch to the new state?
+ if ( !( fHeadNoteChangeFlags & kIOPMNotDone) )
+ {
+ trackSystemSleepPreventers(
+ fCurrentPowerState, fHeadNotePowerState, fHeadNoteChangeFlags);
+
+ // we changed, tell our parent
+ requestDomainPower(fHeadNotePowerState);
+
+ // yes, did power raise?
+ if ( StateOrder(fCurrentPowerState) < StateOrder(fHeadNotePowerState) )
+ {
+ // yes, inform clients and apps
+ tellChangeUp (fHeadNotePowerState);
+ }
+ prevPowerState = fCurrentPowerState;
+ // either way
+ fCurrentPowerState = fHeadNotePowerState;
+ PM_LOCK();
+ if (fReportBuf) {
+ ts = mach_absolute_time();
+ STATEREPORT_SETSTATE(fReportBuf, fCurrentPowerState, ts);
+ }
+ PM_UNLOCK();
+#if PM_VARS_SUPPORT
+ fPMVars->myCurrentState = fCurrentPowerState;
+#endif
+ OUR_PMLog(kPMLogChangeDone, fCurrentPowerState, prevPowerState);
+ PM_ACTION_2(actionPowerChangeDone,
+ fHeadNotePowerState, fHeadNoteChangeFlags);
+ callAction = true;
+
+ powerStatePtr = &fPowerStates[fCurrentPowerState];
+ fCurrentCapabilityFlags = powerStatePtr->capabilityFlags;
+ if (fCurrentCapabilityFlags & kIOPMStaticPowerValid)
+ fCurrentPowerConsumption = powerStatePtr->staticPower;
+
+ if (fHeadNoteChangeFlags & kIOPMRootChangeDown)
+ {
+ // Bump tickle generation count once the entire tree is down
+ gIOPMTickleGeneration++;
+ }
+
+ // inform subclass policy-maker
+ if (fPCDFunctionOverride && fParentsKnowState &&
+ assertPMDriverCall(&callEntry, kIOPMADC_NoInactiveCheck))
+ {
+ powerChangeDone(prevPowerState);
+ deassertPMDriverCall(&callEntry);
+ }
+ }
+ else if (getPMRequestType() == kIOPMRequestTypeRequestPowerStateOverride)
+ {
+ // changePowerStateWithOverrideTo() was cancelled
+ fOverrideMaxPowerState = kIOPMPowerStateMax;
+ }
+ }
+
+ // parent's power change
+ if ( fHeadNoteChangeFlags & kIOPMParentInitiated)
+ {
+ if (fHeadNoteChangeFlags & kIOPMRootChangeDown)
+ ParentChangeRootChangeDown();
+
+ if (((fHeadNoteChangeFlags & kIOPMDomainWillChange) &&
+ (StateOrder(fCurrentPowerState) >= StateOrder(fHeadNotePowerState))) ||
+ ((fHeadNoteChangeFlags & kIOPMDomainDidChange) &&
+ (StateOrder(fCurrentPowerState) < StateOrder(fHeadNotePowerState))))
+ {
+ trackSystemSleepPreventers(
+ fCurrentPowerState, fHeadNotePowerState, fHeadNoteChangeFlags);
+
+ // did power raise?
+ if ( StateOrder(fCurrentPowerState) < StateOrder(fHeadNotePowerState) )
+ {
+ // yes, inform clients and apps
+ tellChangeUp (fHeadNotePowerState);
+ }
+ // either way
+ prevPowerState = fCurrentPowerState;
+ fCurrentPowerState = fHeadNotePowerState;
+ PM_LOCK();
+ if (fReportBuf) {
+ ts = mach_absolute_time();
+ STATEREPORT_SETSTATE(fReportBuf, fCurrentPowerState, ts);
+ }
+ PM_UNLOCK();
+#if PM_VARS_SUPPORT
+ fPMVars->myCurrentState = fCurrentPowerState;
+#endif
+ fMaxPowerState = fControllingDriver->maxCapabilityForDomainState(fHeadNoteDomainFlags);
+
+ OUR_PMLog(kPMLogChangeDone, fCurrentPowerState, prevPowerState);
+ PM_ACTION_2(actionPowerChangeDone,
+ fHeadNotePowerState, fHeadNoteChangeFlags);
+ callAction = true;
+
+ powerStatePtr = &fPowerStates[fCurrentPowerState];
+ fCurrentCapabilityFlags = powerStatePtr->capabilityFlags;
+ if (fCurrentCapabilityFlags & kIOPMStaticPowerValid)
+ fCurrentPowerConsumption = powerStatePtr->staticPower;
+
+ // inform subclass policy-maker
+ if (fPCDFunctionOverride && fParentsKnowState &&
+ assertPMDriverCall(&callEntry, kIOPMADC_NoInactiveCheck))
+ {
+ powerChangeDone(prevPowerState);
+ deassertPMDriverCall(&callEntry);
+ }
+ }
+ }
+
+ // When power rises enough to satisfy the tickle's desire for more power,
+ // the condition preventing idle-timer from dropping power is removed.
+
+ if (StateOrder(fCurrentPowerState) >= StateOrder(fIdleTimerMinPowerState))
+ {
+ fIdleTimerMinPowerState = kPowerStateZero;
+ }
+
+ if (!callAction)
+ {
+ PM_ACTION_2(actionPowerChangeDone,
+ fHeadNotePowerState, fHeadNoteChangeFlags);
+ }
+}
+
+// MARK: -
+// MARK: Power Change Initiated by Driver
+
+//*********************************************************************************
+// [private] OurChangeStart
+//
+// Begin the processing of a power change initiated by us.
+//*********************************************************************************
+
+void IOService::OurChangeStart( void )
+{
+ PM_ASSERT_IN_GATE();
+ OUR_PMLog( kPMLogStartDeviceChange, fHeadNotePowerState, fCurrentPowerState );
+
+ // fMaxPowerState is our maximum possible power state based on the current
+ // power state of our parents. If we are trying to raise power beyond the
+ // maximum, send an async request for more power to all parents.
+
+ if (!IS_PM_ROOT && (StateOrder(fMaxPowerState) < StateOrder(fHeadNotePowerState)))
+ {
+ fHeadNoteChangeFlags |= kIOPMNotDone;
+ requestDomainPower(fHeadNotePowerState);
+ OurChangeFinish();
+ return;
+ }
+
+ // Redundant power changes skips to the end of the state machine.
+
+ if (!fInitialPowerChange && (fHeadNotePowerState == fCurrentPowerState))
+ {
+ OurChangeFinish();
+ return;
+ }
+ fInitialPowerChange = false;
+
+ // Change started, but may not complete...
+ // Can be canceled (power drop) or deferred (power rise).
+
+ PM_ACTION_2(actionPowerChangeStart, fHeadNotePowerState, &fHeadNoteChangeFlags);
+
+ // Two separate paths, depending if power is being raised or lowered.
+ // Lowering power is subject to approval by clients of this service.
+
+ if (IS_POWER_DROP)
+ {
+ fDoNotPowerDown = false;
+
+ // Ask for persmission to drop power state
+ fMachineState = kIOPM_OurChangeTellClientsPowerDown;
+ fOutOfBandParameter = kNotifyApps;
+ askChangeDown(fHeadNotePowerState);
+ }
+ else
+ {
+ // This service is raising power and parents are able to support the
+ // new power state. However a parent may have already committed to
+ // drop power, which might force this object to temporarily drop power.
+ // This results in "oscillations" before the state machines converge
+ // to a steady state.
+ //
+ // To prevent this, a child must make a power reservation against all
+ // parents before raising power. If the reservation fails, indicating
+ // that the child will be unable to sustain the higher power state,
+ // then the child will signal the parent to adjust power, and the child
+ // will defer its power change.
+
+ IOReturn ret;
+
+ // Reserve parent power necessary to achieve fHeadNotePowerState.
+ ret = requestDomainPower( fHeadNotePowerState, kReserveDomainPower );
+ if (ret != kIOReturnSuccess)
+ {
+ // Reservation failed, defer power rise.
+ fHeadNoteChangeFlags |= kIOPMNotDone;
+ OurChangeFinish();
+ return;
+ }
+
+ OurChangeTellCapabilityWillChange();
+ }
+}
+
+//*********************************************************************************
+// [private] requestDomainPowerApplier
+//
+// Call requestPowerDomainState() on all power parents.
+//*********************************************************************************
+
+struct IOPMRequestDomainPowerContext {
+ IOService * child; // the requesting child
+ IOPMPowerFlags requestPowerFlags; // power flags requested by child
+};
+
+static void
+requestDomainPowerApplier(
+ IORegistryEntry * entry,
+ void * inContext )
+{
+ IOPowerConnection * connection;
+ IOService * parent;
+ IOPMRequestDomainPowerContext * context;
+
+ if ((connection = OSDynamicCast(IOPowerConnection, entry)) == 0)
+ return;
+ parent = (IOService *) connection->copyParentEntry(gIOPowerPlane);
+ if (!parent)
+ return;
+
+ assert(inContext);
+ context = (IOPMRequestDomainPowerContext *) inContext;
+
+ if (connection->parentKnowsState() && connection->getReadyFlag())
+ {
+ parent->requestPowerDomainState(
+ context->requestPowerFlags,
+ connection,
+ IOPMLowestState);
+ }
+
+ parent->release();
+}
+
+//*********************************************************************************
+// [private] requestDomainPower
+//
+// Called by a power child to broadcast its desired power state to all parents.
+// If the child self-initiates a power change, it must call this function to
+// allow its parents to adjust power state.
+//*********************************************************************************
+
+IOReturn IOService::requestDomainPower(
+ IOPMPowerStateIndex ourPowerState,
+ IOOptionBits options )
+{
+ IOPMPowerFlags requestPowerFlags;
+ IOPMPowerStateIndex maxPowerState;
+ IOPMRequestDomainPowerContext context;
+
+ PM_ASSERT_IN_GATE();
+ assert(ourPowerState < fNumberOfPowerStates);
+ if (ourPowerState >= fNumberOfPowerStates)
+ return kIOReturnBadArgument;
+ if (IS_PM_ROOT)
+ return kIOReturnSuccess;
+
+ // Fetch our input power flags for the requested power state.
+ // Parent request is stated in terms of required power flags.
+
+ requestPowerFlags = fPowerStates[ourPowerState].inputPowerFlags;
+
+ // Disregard the "previous request" for power reservation.
+
+ if (((options & kReserveDomainPower) == 0) &&
+ (fPreviousRequestPowerFlags == requestPowerFlags))
+ {
+ // skip if domain already knows our requirements
+ goto done;
+ }
+ fPreviousRequestPowerFlags = requestPowerFlags;
+
+ // The results will be collected by fHeadNoteDomainTargetFlags
+ context.child = this;
+ context.requestPowerFlags = requestPowerFlags;
+ fHeadNoteDomainTargetFlags = 0;
+ applyToParents(requestDomainPowerApplier, &context, gIOPowerPlane);
+
+ if (options & kReserveDomainPower)
+ {
+ maxPowerState = fControllingDriver->maxCapabilityForDomainState(
+ fHeadNoteDomainTargetFlags );
+
+ if (StateOrder(maxPowerState) < StateOrder(ourPowerState))
+ {
+ PM_LOG1("%s: power desired %u:0x%x got %u:0x%x\n",
+ getName(),
+ (uint32_t) ourPowerState, (uint32_t) requestPowerFlags,
+ (uint32_t) maxPowerState, (uint32_t) fHeadNoteDomainTargetFlags);
+ return kIOReturnNoPower;
+ }
+ }
+
+done:
+ return kIOReturnSuccess;
+}
+
+//*********************************************************************************
+// [private] OurSyncStart
+//*********************************************************************************
+
+void IOService::OurSyncStart( void )
+{
+ PM_ASSERT_IN_GATE();
+
+ if (fInitialPowerChange)
+ return;
+
+ PM_ACTION_2(actionPowerChangeStart, fHeadNotePowerState, &fHeadNoteChangeFlags);
+
+ if (fHeadNoteChangeFlags & kIOPMNotDone)
+ {
+ OurChangeFinish();
+ return;
+ }
+
+ if (fHeadNoteChangeFlags & kIOPMSyncTellPowerDown)
+ {
+ fDoNotPowerDown = false;
+
+ // Ask for permission to drop power state
+ fMachineState = kIOPM_SyncTellClientsPowerDown;
+ fOutOfBandParameter = kNotifyApps;
+ askChangeDown(fHeadNotePowerState);
+ }
+ else
+ {
+ // Only inform capability app and clients.
+ tellSystemCapabilityChange( kIOPM_SyncNotifyWillChange );
+ }