+ // the return value from setPowerState()
+ result = param->Result;
+
+ if ((result == IOPMAckImplied) || (result < 0))
+ {
+ fDriverTimer = 0;
+ }
+ else if (fDriverTimer)
+ {
+ assert(fDriverTimer == -1);
+
+ // Driver has not acked, and has returned a positive result.
+ // Enforce a minimum permissible timeout value.
+ // Make the min value large enough so timeout is less likely
+ // to occur if a driver misinterpreted that the return value
+ // should be in microsecond units. And make it large enough
+ // to be noticeable if a driver neglects to ack.
+
+ if (result < kMinAckTimeoutTicks)
+ result = kMinAckTimeoutTicks;
+
+ fDriverTimer = (result / (ACK_TIMER_PERIOD / ns_per_us)) + 1;
+ }
+ // else, child has already acked and driver_timer reset to 0.
+
+ fDriverCallParamCount = 0;
+
+ if ( fDriverTimer )
+ {
+ OUR_PMLog(kPMLogStartAckTimer, 0, 0);
+ start_ack_timer();
+ }
+ }
+
+ MS_POP(); // pushed by OurChangeSetPowerState()
+ fIsPreChange = false;
+}
+
+//*********************************************************************************
+// [private] all_done
+//
+// A power change is done.
+//*********************************************************************************
+
+void IOService::all_done( void )
+{
+ IOPMPowerStateIndex prevPowerState;
+ const IOPMPSEntry * powerStatePtr;
+ IOPMDriverCallEntry callEntry;
+ uint32_t prevMachineState = fMachineState;
+ bool callAction = 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 )
+ {
+ // 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 );
+ }
+}
+
+//*********************************************************************************
+// [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 )
+{
+ all_done();
+}
+
+// MARK: -
+// MARK: Power Change Initiated by Parent
+
+//*********************************************************************************
+// [private] ParentChangeStart
+//
+// Here we begin the processing of a power change initiated by our parent.
+//*********************************************************************************
+
+IOReturn IOService::ParentChangeStart( void )
+{
+ PM_ASSERT_IN_GATE();
+ OUR_PMLog( kPMLogStartParentChange, fHeadNotePowerState, fCurrentPowerState );
+
+ // Root power domain has transitioned to its max power state
+ if ((fHeadNoteChangeFlags & (kIOPMDomainDidChange | kIOPMRootChangeUp)) ==
+ (kIOPMDomainDidChange | kIOPMRootChangeUp))
+ {
+ // Restart the idle timer stopped by ParentChangeRootChangeDown()
+ if (fIdleTimerPeriod && fIdleTimerStopped)
+ {
+ restartIdleTimer();
+ }
+ }
+
+ // Power domain is forcing us to lower power
+ if ( StateOrder(fHeadNotePowerState) < StateOrder(fCurrentPowerState) )
+ {
+ PM_ACTION_2(actionPowerChangeStart, fHeadNotePowerState, &fHeadNoteChangeFlags);
+
+ // Tell apps and kernel clients
+ fInitialPowerChange = false;
+ fMachineState = kIOPM_ParentChangeTellPriorityClientsPowerDown;
+ tellChangeDown1(fHeadNotePowerState);
+ return IOPMWillAckLater;
+ }
+
+ // Power domain is allowing us to raise power up to fHeadNotePowerState
+ if ( StateOrder(fHeadNotePowerState) > StateOrder(fCurrentPowerState) )
+ {
+ if ( StateOrder(fDesiredPowerState) > StateOrder(fCurrentPowerState) )
+ {
+ if ( StateOrder(fDesiredPowerState) < StateOrder(fHeadNotePowerState) )
+ {
+ // We power up, but not all the way
+ fHeadNotePowerState = fDesiredPowerState;
+ fHeadNotePowerArrayEntry = &fPowerStates[fDesiredPowerState];
+ OUR_PMLog(kPMLogAmendParentChange, fHeadNotePowerState, 0);
+ }
+ } else {
+ // We don't need to change
+ fHeadNotePowerState = fCurrentPowerState;
+ fHeadNotePowerArrayEntry = &fPowerStates[fCurrentPowerState];
+ OUR_PMLog(kPMLogAmendParentChange, fHeadNotePowerState, 0);
+ }
+ }
+
+ if ( fHeadNoteChangeFlags & kIOPMDomainDidChange )
+ {
+ if ( StateOrder(fHeadNotePowerState) > StateOrder(fCurrentPowerState) )
+ {
+ PM_ACTION_2(actionPowerChangeStart,
+ fHeadNotePowerState, &fHeadNoteChangeFlags);
+
+ // Parent did change up - start our change up
+ fInitialPowerChange = false;
+ ParentChangeTellCapabilityWillChange();
+ return IOPMWillAckLater;
+ }
+ else if (fHeadNoteChangeFlags & kIOPMRootBroadcastFlags)
+ {
+ // No need to change power state, but broadcast change
+ // to our children.
+ fMachineState = kIOPM_SyncNotifyDidChange;
+ fDriverCallReason = kDriverCallInformPreChange;
+ notifyChildren();
+ return IOPMWillAckLater;
+ }
+ }
+
+ all_done();
+ return IOPMAckImplied;
+}
+
+//******************************************************************************
+// [private] ParentChangeRootChangeDown
+//
+// Root domain has finished the transition to the system sleep state. And all
+// drivers in the power plane should have powered down. Cancel the idle timer,
+// and also reset the device desire for those drivers that don't want power
+// automatically restored on wake.
+//******************************************************************************
+
+void IOService::ParentChangeRootChangeDown( void )
+{
+ // Always stop the idle timer before root power down
+ if (fIdleTimerPeriod && !fIdleTimerStopped)
+ {
+ fIdleTimerStopped = true;
+ if (fIdleTimer && thread_call_cancel(fIdleTimer))
+ release();
+ }
+
+ if (fResetPowerStateOnWake)
+ {
+ // Reset device desire down to the lowest power state.
+ // Advisory tickle desire is intentionally untouched since
+ // it has no effect until system is promoted to full wake.
+
+ if (fDeviceDesire != kPowerStateZero)
+ {
+ updatePowerClient(gIOPMPowerClientDevice, kPowerStateZero);
+ computeDesiredState(kPowerStateZero, true);
+ PM_LOG1("%s: tickle desire removed\n", fName);
+ }
+
+ // Invalidate tickle cache so the next tickle will issue a request
+ IOLockLock(fActivityLock);
+ fDeviceWasActive = false;
+ fActivityTicklePowerState = kInvalidTicklePowerState;
+ IOLockUnlock(fActivityLock);
+
+ fIdleTimerMinPowerState = kPowerStateZero;
+ }
+ else if (fAdvisoryTickleUsed)
+ {
+ // Less aggressive mechanism to accelerate idle timer expiration
+ // before system sleep. May not always allow the driver to wake
+ // up from system sleep in the min power state.
+
+ AbsoluteTime now;
+ uint64_t nsec;
+ bool dropTickleDesire = false;
+
+ if (fIdleTimerPeriod && !fIdleTimerIgnored &&
+ (fIdleTimerMinPowerState == kPowerStateZero) &&
+ (fDeviceDesire != kPowerStateZero))
+ {
+ IOLockLock(fActivityLock);
+
+ if (!fDeviceWasActive)
+ {
+ // No tickles since the last idle timer expiration.
+ // Safe to drop the device desire to zero.
+ dropTickleDesire = true;
+ }
+ else
+ {
+ // Was tickled since the last idle timer expiration,
+ // but not in the last minute.
+ clock_get_uptime(&now);
+ SUB_ABSOLUTETIME(&now, &fDeviceActiveTimestamp);
+ absolutetime_to_nanoseconds(now, &nsec);
+ if (nsec >= kNoTickleCancelWindow)
+ {
+ dropTickleDesire = true;
+ }
+ }
+
+ if (dropTickleDesire)
+ {
+ // Force the next tickle to raise power state
+ fDeviceWasActive = false;
+ fActivityTicklePowerState = kInvalidTicklePowerState;
+ }
+
+ IOLockUnlock(fActivityLock);
+ }
+
+ if (dropTickleDesire)
+ {
+ // Advisory tickle desire is intentionally untouched since
+ // it has no effect until system is promoted to full wake.
+
+ updatePowerClient(gIOPMPowerClientDevice, kPowerStateZero);
+ computeDesiredState(kPowerStateZero, true);
+ PM_LOG1("%s: tickle desire dropped\n", fName);
+ }
+ }
+}