+void IOService::all_done( void )
+{
+ IOPMPowerStateIndex prevPowerState;
+ const IOPMPSEntry * powerStatePtr;
+ IOPMDriverCallEntry callEntry;
+ uint32_t prevMachineState = fMachineState;
+ bool actionCalled = false;
+ uint64_t ts;
+
+ fMachineState = kIOPM_Finished;
+
+ if ((fHeadNoteChangeFlags & kIOPMSynchronize) &&
+ ((prevMachineState == kIOPM_Finished) ||
+ (prevMachineState == kIOPM_SyncFinish)))
+ {
+ // Sync operation and no power change occurred.
+ // Do not inform driver and clients about this request completion,
+ // except for the originator (root domain).
+
+ PM_ACTION_2(actionPowerChangeDone,
+ fHeadNotePowerState, fHeadNoteChangeFlags);
+
+ 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)
+ {
+ // power state changed
+ if ((fHeadNoteChangeFlags & kIOPMNotDone) == 0)
+ {
+ 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);
+ actionCalled = 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-initiated power change
+ if (fHeadNoteChangeFlags & kIOPMParentInitiated)
+ {
+ if (fHeadNoteChangeFlags & kIOPMRootChangeDown)
+ ParentChangeRootChangeDown();
+
+ // power state changed
+ if ((fHeadNoteChangeFlags & kIOPMNotDone) == 0)
+ {
+ 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
+
+ OUR_PMLog(kPMLogChangeDone, fCurrentPowerState, prevPowerState);
+ PM_ACTION_2(actionPowerChangeDone,
+ fHeadNotePowerState, fHeadNoteChangeFlags);
+ actionCalled = 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 (!actionCalled)
+ {
+ 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 );
+ }
+}
+
+//*********************************************************************************
+// [private] OurChangeTellClientsPowerDown
+//
+// All applications and kernel clients have acknowledged our permission to drop
+// power. Here we notify them that we will lower the power and wait for acks.
+//*********************************************************************************
+
+void IOService::OurChangeTellClientsPowerDown( void )
+{
+ if(!IS_ROOT_DOMAIN)
+ fMachineState = kIOPM_OurChangeTellPriorityClientsPowerDown;
+ else
+ {
+ fMachineState = kIOPM_OurChangeTellUserPMPolicyPowerDown;
+ }
+ tellChangeDown1(fHeadNotePowerState);
+}
+
+//*********************************************************************************
+// [private] OurChangeTellUserPMPolicyPowerDown
+//
+// All applications and kernel clients have acknowledged our permission to drop
+// power. Here we notify power management policy in user-space and wait for acks
+// one last time before we lower power
+//*********************************************************************************
+void IOService::OurChangeTellUserPMPolicyPowerDown ( void )
+{
+ fMachineState = kIOPM_OurChangeTellPriorityClientsPowerDown;
+ fOutOfBandParameter = kNotifyApps;
+
+ tellClientsWithResponse(kIOPMMessageLastCallBeforeSleep);
+}
+
+//*********************************************************************************
+// [private] OurChangeTellPriorityClientsPowerDown
+//
+// All applications and kernel clients have acknowledged our intention to drop
+// power. Here we notify "priority" clients that we are lowering power.
+//*********************************************************************************
+
+void IOService::OurChangeTellPriorityClientsPowerDown( void )
+{
+ fMachineState = kIOPM_OurChangeNotifyInterestedDriversWillChange;
+ tellChangeDown2(fHeadNotePowerState);
+}
+
+//*********************************************************************************
+// [private] OurChangeTellCapabilityWillChange
+//
+// Extra stage for root domain to notify apps and drivers about the
+// system capability change when raising power state.
+//*********************************************************************************
+
+void IOService::OurChangeTellCapabilityWillChange( void )
+{
+ if (!IS_ROOT_DOMAIN)
+ return OurChangeNotifyInterestedDriversWillChange();
+
+ tellSystemCapabilityChange( kIOPM_OurChangeNotifyInterestedDriversWillChange );
+}
+
+//*********************************************************************************
+// [private] OurChangeNotifyInterestedDriversWillChange
+//
+// All applications and kernel clients have acknowledged our power state change.
+// Here we notify interested drivers pre-change.
+//*********************************************************************************
+
+void IOService::OurChangeNotifyInterestedDriversWillChange( void )
+{
+ IOPMrootDomain * rootDomain;
+ if ((rootDomain = getPMRootDomain()) == this)
+ {
+ if (IS_POWER_DROP)
+ {
+ rootDomain->tracePoint( kIOPMTracePointSleepWillChangeInterests );
+ }
+ else
+ rootDomain->tracePoint( kIOPMTracePointWakeWillChangeInterests );
+ }
+
+ notifyAll( kIOPM_OurChangeSetPowerState );
+}
+
+//*********************************************************************************
+// [private] OurChangeSetPowerState
+//
+// Instruct our controlling driver to program the hardware for the power state
+// change. Wait for async completions.
+//*********************************************************************************
+
+void IOService::OurChangeSetPowerState( void )
+{
+ MS_PUSH( kIOPM_OurChangeWaitForPowerSettle );
+ fMachineState = kIOPM_DriverThreadCallDone;
+ fDriverCallReason = kDriverCallSetPowerState;
+
+ if (notifyControllingDriver() == false)
+ notifyControllingDriverDone();
+}
+
+//*********************************************************************************
+// [private] OurChangeWaitForPowerSettle
+//
+// Our controlling driver has completed the power state change we initiated.
+// Wait for the driver specified settle time to expire.
+//*********************************************************************************
+
+void IOService::OurChangeWaitForPowerSettle( void )
+{
+ fMachineState = kIOPM_OurChangeNotifyInterestedDriversDidChange;
+ startSettleTimer();
+}
+
+//*********************************************************************************
+// [private] OurChangeNotifyInterestedDriversDidChange
+//
+// Power has settled on a power change we initiated. Here we notify
+// all our interested drivers post-change.
+//*********************************************************************************
+
+void IOService::OurChangeNotifyInterestedDriversDidChange( void )
+{
+ IOPMrootDomain * rootDomain;
+ if ((rootDomain = getPMRootDomain()) == this)
+ {
+ rootDomain->tracePoint( IS_POWER_DROP ?
+ kIOPMTracePointSleepDidChangeInterests :
+ kIOPMTracePointWakeDidChangeInterests );
+ }
+
+ notifyAll( kIOPM_OurChangeTellCapabilityDidChange );
+}
+
+//*********************************************************************************
+// [private] OurChangeTellCapabilityDidChange
+//
+// For root domain to notify capability power-change.
+//*********************************************************************************
+
+void IOService::OurChangeTellCapabilityDidChange( void )
+{
+ if (!IS_ROOT_DOMAIN)
+ return OurChangeFinish();
+
+ getPMRootDomain()->tracePoint( IS_POWER_DROP ?
+ kIOPMTracePointSleepCapabilityClients :
+ kIOPMTracePointWakeCapabilityClients );
+
+ tellSystemCapabilityChange( kIOPM_OurChangeFinish );
+}
+
+//*********************************************************************************
+// [private] OurChangeFinish
+//
+// Done with this self-induced power state change.
+//*********************************************************************************
+
+void IOService::OurChangeFinish( void )