+ bool more;
+
+ if (initialized)
+ {
+ // Work queue will immediately execute the queue'd request if possible.
+ // If execution blocks, the work queue will wait for a producer signal.
+ // Only need to signal more when completing attached requests.
+
+ more = gIOPMWorkQueue->queuePMRequest(request, pwrMgt);
+ return more;
+ }
+
+ // Calling PM without PMinit() is not allowed, fail the request.
+
+ PM_LOG("%s: PM not initialized\n", getName());
+ fAdjustPowerScheduled = false;
+ more = gIOPMFreeQueue->queuePMRequest(request);
+ if (more) gIOPMWorkQueue->incrementProducerCount();
+ return more;
+}
+
+//*********************************************************************************
+// [private] servicePMFreeQueue
+//
+// Called from IOPMCompletionQueue::checkForWork().
+//*********************************************************************************
+
+bool IOService::servicePMFreeQueue(
+ IOPMRequest * request,
+ IOPMCompletionQueue * queue )
+{
+ bool more = request->getNextRequest();
+ IOPMRequest * root = request->getRootRequest();
+
+ if (root && (root != request))
+ more = true;
+ if (more)
+ gIOPMWorkQueue->incrementProducerCount();
+
+ releasePMRequest( request );
+ return more;
+}
+
+//*********************************************************************************
+// [private] retirePMRequest
+//
+// Called by IOPMWorkQueue to retire a completed request.
+//*********************************************************************************
+
+bool IOService::retirePMRequest( IOPMRequest * request, IOPMWorkQueue * queue )
+{
+ assert(request && queue);
+
+ PM_LOG1("[- %02x] %p [%p %s] state %d, busy %d\n",
+ request->getType(), request, this, getName(),
+ fMachineState, gIOPMBusyCount);
+
+ // Catch requests created by idleTimerExpired().
+
+ if ((request->getType() == kIOPMRequestTypeActivityTickle) &&
+ (request->fArg1 == (void *) (uintptr_t) false))
+ {
+ // Idle timer power drop request completed.
+ // Restart the idle timer if deviceDesire can go lower, otherwise set
+ // a flag so we know to restart idle timer when deviceDesire goes up.
+
+ if (fDeviceDesire > 0)
+ {
+ fActivityTickleCount = 0;
+ clock_get_uptime(&fIdleTimerStartTime);
+ start_PM_idle_timer();
+ }
+ else
+ fIdleTimerStopped = true;
+ }
+
+ // If the request is linked, then Work queue has already incremented its
+ // producer count.
+
+ return (gIOPMFreeQueue->queuePMRequest( request ));
+}
+
+//*********************************************************************************
+// [private] isPMBlocked
+//
+// Check if machine state transition is blocked.
+//*********************************************************************************
+
+bool IOService::isPMBlocked ( IOPMRequest * request, int count )
+{
+ int reason = 0;
+
+ do {
+ if (kIOPM_Finished == fMachineState)
+ break;
+
+ if (kIOPM_DriverThreadCallDone == fMachineState)
+ {
+ // 5 = kDriverCallInformPreChange
+ // 6 = kDriverCallInformPostChange
+ // 7 = kDriverCallSetPowerState
+ if (fDriverCallBusy)
+ reason = 5 + fDriverCallReason;
+ break;
+ }
+
+ // Waiting on driver's setPowerState() timeout.
+ if (fDriverTimer)
+ {
+ reason = 1; break;
+ }
+
+ // Child or interested driver acks pending.
+ if (fHeadNotePendingAcks)
+ {
+ reason = 2; break;
+ }
+
+ // Waiting on apps or priority power interest clients.
+ if (fResponseArray)
+ {
+ reason = 3; break;
+ }
+
+ // Waiting on settle timer expiration.
+ if (fSettleTimeUS)
+ {
+ reason = 4; break;
+ }
+ } while (false);
+
+ fWaitReason = reason;
+
+ if (reason)
+ {
+ if (count)
+ {
+ PM_LOG1("[B %02x] %p [%p %s] state %d, reason %d\n",
+ request->getType(), request, this, getName(),
+ fMachineState, reason);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+//*********************************************************************************
+// [private] servicePMRequest
+//
+// Service a request from our work queue.
+//*********************************************************************************
+
+bool IOService::servicePMRequest( IOPMRequest * request, IOPMWorkQueue * queue )
+{
+ bool done = false;
+ int loop = 0;
+
+ assert(request && queue);
+
+ while (isPMBlocked(request, loop++) == false)
+ {
+ PM_LOG1("[W %02x] %p [%p %s] state %d\n",
+ request->getType(), request, this, getName(), fMachineState);
+
+ gIOPMRequest = request;
+ gIOPMWorkCount++;
+
+ // Every PM machine states must be handled in one of the cases below.
+
+ switch ( fMachineState )
+ {
+ case kIOPM_Finished:
+ executePMRequest( request );
+ break;
+
+ case kIOPM_OurChangeTellClientsPowerDown:
+ // Root domain might self cancel due to assertions.
+ if (IS_ROOT_DOMAIN)
+ {
+ bool cancel = (bool) fDoNotPowerDown;
+ getPMRootDomain()->askChangeDownDone(
+ &fHeadNoteChangeFlags, &cancel);
+ fDoNotPowerDown = cancel;
+ }
+
+ // askChangeDown() done, was it vetoed?
+ if (!fDoNotPowerDown)
+ {
+ if (IS_ROOT_DOMAIN) {
+ PMEventDetails *details = PMEventDetails::eventDetails(
+ kIOPMEventTypeAppNotificationsFinished,
+ NULL,
+ 0,
+ 0);
+
+ getPMRootDomain()->recordAndReleasePMEventGated( details );
+ }
+
+ // no, we can continue
+ OurChangeTellClientsPowerDown();
+ }
+ else
+ {
+ if (IS_ROOT_DOMAIN) {
+ PMEventDetails *details = PMEventDetails::eventDetails(
+ kIOPMEventTypeSleepDone,
+ NULL,
+ 1, /* reason: 1 == Ask clients succeeded */
+ kIOReturnAborted); /* result */
+
+ getPMRootDomain()->recordAndReleasePMEventGated( details );
+ }
+
+ OUR_PMLog(kPMLogIdleCancel, (uintptr_t) this, fMachineState);
+ PM_ERROR("%s: idle cancel\n", fName);
+ // yes, rescind the warning
+ tellNoChangeDown(fHeadNotePowerState);
+ // mark the change note un-actioned
+ fHeadNoteChangeFlags |= kIOPMNotDone;
+ // and we're done
+ OurChangeFinish();
+ }
+ break;
+
+ case kIOPM_OurChangeTellPriorityClientsPowerDown:
+ // tellChangeDown(kNotifyApps) done, was it cancelled?
+ if (fDoNotPowerDown)
+ {
+ if (IS_ROOT_DOMAIN) {
+ PMEventDetails *details = PMEventDetails::eventDetails(
+ kIOPMEventTypeSleepDone,
+ NULL,
+ 2, /* reason: 2 == Client cancelled wake */
+ kIOReturnAborted); /* result */
+
+ getPMRootDomain()->recordAndReleasePMEventGated( details );
+ }
+ OUR_PMLog(kPMLogIdleCancel, (uintptr_t) this, fMachineState);
+ PM_ERROR("%s: idle revert\n", fName);
+ // no, tell clients we're back in the old state
+ tellChangeUp(fCurrentPowerState);
+ // mark the change note un-actioned
+ fHeadNoteChangeFlags |= kIOPMNotDone;
+ // and we're done
+ OurChangeFinish();
+ }
+ else
+ {
+ if (IS_ROOT_DOMAIN) {
+ PMEventDetails *details = PMEventDetails::eventDetails(
+ kIOPMEventTypeAppNotificationsFinished,
+ NULL,
+ 2, /* reason: 2 == TellPriorityClientsDone */
+ kIOReturnSuccess); /* result */
+
+ getPMRootDomain()->recordAndReleasePMEventGated( details );
+ }
+ // yes, we can continue
+ OurChangeTellPriorityClientsPowerDown();
+ }
+ break;
+
+ case kIOPM_OurChangeNotifyInterestedDriversWillChange:
+ OurChangeNotifyInterestedDriversWillChange();
+ break;
+
+ case kIOPM_OurChangeSetPowerState:
+ OurChangeSetPowerState();
+ break;
+
+ case kIOPM_OurChangeWaitForPowerSettle:
+ OurChangeWaitForPowerSettle();
+ break;
+
+ case kIOPM_OurChangeNotifyInterestedDriversDidChange:
+ OurChangeNotifyInterestedDriversDidChange();
+ break;
+
+ case kIOPM_OurChangeTellCapabilityDidChange:
+ OurChangeTellCapabilityDidChange();
+ break;
+
+ case kIOPM_OurChangeFinish:
+ OurChangeFinish();
+ break;
+
+ case kIOPM_ParentChangeTellPriorityClientsPowerDown:
+ ParentChangeTellPriorityClientsPowerDown();
+ break;
+
+ case kIOPM_ParentChangeNotifyInterestedDriversWillChange:
+ ParentChangeNotifyInterestedDriversWillChange();
+ break;
+
+ case kIOPM_ParentChangeSetPowerState:
+ ParentChangeSetPowerState();
+ break;
+
+ case kIOPM_ParentChangeWaitForPowerSettle:
+ ParentChangeWaitForPowerSettle();
+ break;
+
+ case kIOPM_ParentChangeNotifyInterestedDriversDidChange:
+ ParentChangeNotifyInterestedDriversDidChange();
+ break;
+
+ case kIOPM_ParentChangeTellCapabilityDidChange:
+ ParentChangeTellCapabilityDidChange();
+ break;
+
+ case kIOPM_ParentChangeAcknowledgePowerChange:
+ ParentChangeAcknowledgePowerChange();
+ break;
+
+ case kIOPM_DriverThreadCallDone:
+ if (fDriverCallReason == kDriverCallSetPowerState)
+ notifyControllingDriverDone();
+ else
+ notifyInterestedDriversDone();
+ break;
+
+ case kIOPM_NotifyChildrenOrdered:
+ notifyChildrenOrdered();
+ break;
+
+ case kIOPM_NotifyChildrenDelayed:
+ notifyChildrenDelayed();
+ break;
+
+ case kIOPM_NotifyChildrenStart:
+ PM_LOG2("%s: kIOPM_NotifyChildrenStart done\n", getName());
+ MS_POP(); // from notifyInterestedDriversDone()
+ notifyChildren();
+ break;
+
+ case kIOPM_SyncTellClientsPowerDown:
+ // Root domain might self cancel due to assertions.
+ if (IS_ROOT_DOMAIN)
+ {
+ bool cancel = (bool) fDoNotPowerDown;
+ getPMRootDomain()->askChangeDownDone(
+ &fHeadNoteChangeFlags, &cancel);
+ fDoNotPowerDown = cancel;
+ }
+ if (!fDoNotPowerDown)
+ {
+ fMachineState = kIOPM_SyncTellPriorityClientsPowerDown;
+ fOutOfBandParameter = kNotifyApps;
+ tellChangeDown(fHeadNotePowerState);
+ }
+ else
+ {
+ OUR_PMLog(kPMLogIdleCancel, (uintptr_t) this, fMachineState);
+ PM_ERROR("%s: idle cancel\n", fName);
+ tellNoChangeDown(fHeadNotePowerState);
+ fHeadNoteChangeFlags |= kIOPMNotDone;
+ OurChangeFinish();
+ }
+ break;
+
+ case kIOPM_SyncTellPriorityClientsPowerDown:
+ if (!fDoNotPowerDown)
+ {
+ fMachineState = kIOPM_SyncNotifyWillChange;
+ fOutOfBandParameter = kNotifyPriority;
+ tellChangeDown(fHeadNotePowerState);
+ }
+ else
+ {
+ OUR_PMLog(kPMLogIdleCancel, (uintptr_t) this, fMachineState);
+ PM_ERROR("%s: idle revert\n", fName);
+ tellChangeUp(fCurrentPowerState);
+ fHeadNoteChangeFlags |= kIOPMNotDone;
+ OurChangeFinish();
+ }
+ break;
+
+ case kIOPM_SyncNotifyWillChange:
+ if (kIOPMSyncNoChildNotify & fHeadNoteChangeFlags)
+ {
+ fMachineState = kIOPM_SyncFinish;
+ continue;
+ }
+ fMachineState = kIOPM_SyncNotifyDidChange;
+ fDriverCallReason = kDriverCallInformPreChange;
+ notifyChildren();
+ break;
+
+ case kIOPM_SyncNotifyDidChange:
+ fIsPreChange = false;
+
+ if (fHeadNoteChangeFlags & kIOPMParentInitiated)
+ fMachineState = kIOPM_SyncFinish;
+ else
+ fMachineState = kIOPM_SyncTellCapabilityDidChange;
+
+ fDriverCallReason = kDriverCallInformPostChange;
+ notifyChildren();
+ break;
+
+ case kIOPM_SyncTellCapabilityDidChange:
+ tellSystemCapabilityChange( kIOPM_SyncFinish );
+ break;
+
+ case kIOPM_SyncFinish:
+ if (fHeadNoteChangeFlags & kIOPMParentInitiated)
+ ParentChangeAcknowledgePowerChange();
+ else
+ OurChangeFinish();
+ break;
+
+ case kIOPM_TellCapabilityChangeDone:
+ if (fIsPreChange)
+ {
+ if (fOutOfBandParameter == kNotifyCapabilityChangePriority)
+ {
+ MS_POP(); // tellSystemCapabilityChange()
+ continue;
+ }
+ fOutOfBandParameter = kNotifyCapabilityChangePriority;
+ }
+ else
+ {
+ if (fOutOfBandParameter == kNotifyCapabilityChangeApps)
+ {
+ MS_POP(); // tellSystemCapabilityChange()
+ continue;
+ }
+ fOutOfBandParameter = kNotifyCapabilityChangeApps;
+ }
+ tellClientsWithResponse( fOutOfBandMessage );
+ break;
+
+ default:
+ panic("servicePMWorkQueue: unknown machine state %x",
+ fMachineState);
+ }
+
+ gIOPMRequest = 0;
+
+ if (fMachineState == kIOPM_Finished)
+ {
+ done = true;
+ break;
+ }
+ }
+
+ return done;
+}
+
+//*********************************************************************************
+// [private] executePMRequest
+//*********************************************************************************
+
+void IOService::executePMRequest( IOPMRequest * request )
+{
+ assert( kIOPM_Finished == fMachineState );
+
+ switch (request->getType())
+ {
+ case kIOPMRequestTypePMStop:
+ handlePMstop( request );
+ break;
+
+ case kIOPMRequestTypeAddPowerChild1:
+ addPowerChild1( request );
+ break;