+ IOPMRequest * request;
+
+ if ( !initialized )
+ {
+ // we're unloading
+ return kIOReturnSuccess;
+ }
+
+ request = acquirePMRequest( this, kIOPMRequestTypeAllowPowerChange );
+ if (!request)
+ return kIOReturnNoMemory;
+
+ request->fArg0 = (void *) refcon;
+ request->fArg1 = (void *)(uintptr_t) proc_selfpid();
+ request->fArg2 = (void *) 0;
+ submitPMRequest( request );
+
+ return kIOReturnSuccess;
+}
+
+#ifndef __LP64__
+IOReturn IOService::serializedAllowPowerChange2( unsigned long refcon )
+{
+ // [deprecated] public
+ return kIOReturnUnsupported;
+}
+#endif /* !__LP64__ */
+
+//*********************************************************************************
+// [public] cancelPowerChange
+//
+// Our power state is about to lower, and we have notified applications
+// and kernel clients, and one of them has vetoed the change. If this is the last
+// client to respond, we abandon the power change.
+//*********************************************************************************
+
+IOReturn IOService::cancelPowerChange( unsigned long refcon )
+{
+ IOPMRequest * request;
+ char name[128];
+ pid_t pid = proc_selfpid();
+
+ if ( !initialized )
+ {
+ // we're unloading
+ return kIOReturnSuccess;
+ }
+
+ name[0] = '\0';
+ proc_name(pid, name, sizeof(name));
+ PM_ERROR("PM notification cancel (pid %d, %s)\n", pid, name);
+
+ request = acquirePMRequest( this, kIOPMRequestTypeCancelPowerChange );
+ if (!request)
+ {
+ return kIOReturnNoMemory;
+ }
+
+ request->fArg0 = (void *) refcon;
+ request->fArg1 = (void *)(uintptr_t) proc_selfpid();
+ request->fArg2 = (void *) OSString::withCString(name);
+ submitPMRequest( request );
+
+ return kIOReturnSuccess;
+}
+
+#ifndef __LP64__
+IOReturn IOService::serializedCancelPowerChange2( unsigned long refcon )
+{
+ // [deprecated] public
+ return kIOReturnUnsupported;
+}
+
+//*********************************************************************************
+// PM_Clamp_Timer_Expired
+//
+// called when clamp timer expires...set power state to 0.
+//*********************************************************************************
+
+void IOService::PM_Clamp_Timer_Expired( void )
+{
+}
+
+//*********************************************************************************
+// clampPowerOn
+//
+// Set to highest available power state for a minimum of duration milliseconds
+//*********************************************************************************
+
+void IOService::clampPowerOn( unsigned long duration )
+{
+}
+#endif /* !__LP64__ */
+
+//*********************************************************************************
+// configurePowerStateReport
+//
+// Configures the IOStateReport for kPMPowerStateChannel
+//*********************************************************************************
+IOReturn IOService::configurePowerStatesReport( IOReportConfigureAction action, void *result )
+{
+
+ IOReturn rc = kIOReturnSuccess;
+ size_t reportSize;
+ unsigned long i;
+ uint64_t ts;
+
+ if (!pwrMgt)
+ return kIOReturnUnsupported;
+
+ if (!fNumberOfPowerStates)
+ return kIOReturnSuccess; // For drivers which are in power plane, but haven't called registerPowerDriver()
+ PM_LOCK();
+
+ switch (action)
+ {
+ case kIOReportEnable:
+ if (fReportBuf)
+ {
+ fReportClientCnt++;
+ break;
+ }
+ reportSize = STATEREPORT_BUFSIZE(fNumberOfPowerStates);
+ fReportBuf = IOMalloc(reportSize);
+ if (!fReportBuf) {
+ rc = kIOReturnNoMemory;
+ break;
+ }
+ memset(fReportBuf, 0, reportSize);
+
+ STATEREPORT_INIT(fNumberOfPowerStates, fReportBuf, reportSize,
+ getRegistryEntryID(), kPMPowerStatesChID, kIOReportCategoryPower);
+
+ for (i = 0; i < fNumberOfPowerStates; i++) {
+ unsigned bits = 0;
+
+ if (fPowerStates[i].capabilityFlags & kIOPMPowerOn)
+ bits |= kPMReportPowerOn;
+ if (fPowerStates[i].capabilityFlags & kIOPMDeviceUsable)
+ bits |= kPMReportDeviceUsable;
+ if (fPowerStates[i].capabilityFlags & kIOPMLowPower)
+ bits |= kPMReportLowPower;
+
+ STATEREPORT_SETSTATEID(fReportBuf, i, ((bits & 0xff) << 8) |
+ ((StateOrder(fMaxPowerState) & 0xf) << 4) | (StateOrder(i) & 0xf));
+ }
+ ts = mach_absolute_time();
+ STATEREPORT_SETSTATE(fReportBuf, fCurrentPowerState, ts);
+ break;
+
+ case kIOReportDisable:
+ if (fReportClientCnt == 0) {
+ rc = kIOReturnBadArgument;
+ break;
+ }
+ if (fReportClientCnt == 1)
+ {
+ IOFree(fReportBuf, STATEREPORT_BUFSIZE(fNumberOfPowerStates));
+ fReportBuf = NULL;
+ }
+ fReportClientCnt--;
+ break;
+
+ case kIOReportGetDimensions:
+ if (fReportBuf)
+ STATEREPORT_UPDATERES(fReportBuf, kIOReportGetDimensions, result);
+ break;
+ }
+
+ PM_UNLOCK();
+
+ return rc;
+}
+
+//*********************************************************************************
+// updatePowerStateReport
+//
+// Updates the IOStateReport for kPMPowerStateChannel
+//*********************************************************************************
+IOReturn IOService::updatePowerStatesReport( IOReportConfigureAction action, void *result, void *destination )
+{
+ uint32_t size2cpy;
+ void *data2cpy;
+ uint64_t ts;
+ IOReturn rc = kIOReturnSuccess;
+ IOBufferMemoryDescriptor *dest = OSDynamicCast(IOBufferMemoryDescriptor, (OSObject *)destination);
+
+
+ if (!pwrMgt)
+ return kIOReturnUnsupported;
+ if (!fNumberOfPowerStates)
+ return kIOReturnSuccess;
+
+ if ( !result || !dest ) return kIOReturnBadArgument;
+ PM_LOCK();
+
+ switch (action) {
+ case kIOReportCopyChannelData:
+ if ( !fReportBuf ) {
+ rc = kIOReturnNotOpen;
+ break;
+ }
+
+ ts = mach_absolute_time();
+ STATEREPORT_UPDATEPREP(fReportBuf, ts, data2cpy, size2cpy);
+ if (size2cpy > (dest->getCapacity() - dest->getLength()) ) {
+ rc = kIOReturnOverrun;
+ break;
+ }
+
+ STATEREPORT_UPDATERES(fReportBuf, kIOReportCopyChannelData, result);
+ dest->appendBytes(data2cpy, size2cpy);
+
+ default:
+ break;
+
+ }
+
+ PM_UNLOCK();
+
+ return rc;
+
+}
+
+//*********************************************************************************
+// configureSimplePowerReport
+//
+// Configures the IOSimpleReport for given channel id
+//*********************************************************************************
+IOReturn IOService::configureSimplePowerReport(IOReportConfigureAction action, void *result )
+{
+
+ IOReturn rc = kIOReturnSuccess;
+
+ if ( !pwrMgt )
+ return kIOReturnUnsupported;
+
+ if ( !fNumberOfPowerStates )
+ return rc;
+
+ switch (action)
+ {
+ case kIOReportEnable:
+ case kIOReportDisable:
+ break;
+
+ case kIOReportGetDimensions:
+ SIMPLEREPORT_UPDATERES(kIOReportGetDimensions, result);
+ break;
+ }
+
+
+ return rc;
+}
+
+//*********************************************************************************
+// updateSimplePowerReport
+//
+// Updates the IOSimpleReport for the given chanel id
+//*********************************************************************************
+IOReturn IOService::updateSimplePowerReport( IOReportConfigureAction action, void *result, void *destination )
+{
+ uint32_t size2cpy;
+ void *data2cpy;
+ uint64_t buf[SIMPLEREPORT_BUFSIZE/sizeof(uint64_t)+1]; // Force a 8-byte alignment
+ IOBufferMemoryDescriptor *dest = OSDynamicCast(IOBufferMemoryDescriptor, (OSObject *)destination);
+ IOReturn rc = kIOReturnSuccess;
+ unsigned bits = 0;
+
+
+ if ( !pwrMgt )
+ return kIOReturnUnsupported;
+ if ( !result || !dest ) return kIOReturnBadArgument;
+
+ if ( !fNumberOfPowerStates )
+ return rc;
+ PM_LOCK();
+
+ switch (action) {
+ case kIOReportCopyChannelData:
+
+ SIMPLEREPORT_INIT(buf, sizeof(buf), getRegistryEntryID(), kPMCurrStateChID, kIOReportCategoryPower);
+
+ if (fPowerStates[fCurrentPowerState].capabilityFlags & kIOPMPowerOn)
+ bits |= kPMReportPowerOn;
+ if (fPowerStates[fCurrentPowerState].capabilityFlags & kIOPMDeviceUsable)
+ bits |= kPMReportDeviceUsable;
+ if (fPowerStates[fCurrentPowerState].capabilityFlags & kIOPMLowPower)
+ bits |= kPMReportLowPower;
+
+
+ SIMPLEREPORT_SETVALUE(buf, ((bits & 0xff) << 8) | ((StateOrder(fMaxPowerState) & 0xf) << 4) |
+ (StateOrder(fCurrentPowerState) & 0xf));
+
+ SIMPLEREPORT_UPDATEPREP(buf, data2cpy, size2cpy);
+ if (size2cpy > (dest->getCapacity() - dest->getLength())) {
+ rc = kIOReturnOverrun;
+ break;
+ }
+
+ SIMPLEREPORT_UPDATERES(kIOReportCopyChannelData, result);
+ dest->appendBytes(data2cpy, size2cpy);
+
+ default:
+ break;
+
+ }
+
+ PM_UNLOCK();
+
+ return kIOReturnSuccess;
+
+}
+
+
+
+// MARK: -
+// MARK: Driver Overrides
+
+//*********************************************************************************
+// [public] setPowerState
+//
+// Does nothing here. This should be implemented in a subclass driver.
+//*********************************************************************************
+
+IOReturn IOService::setPowerState(
+ unsigned long powerStateOrdinal, IOService * whatDevice )
+{
+ return IOPMNoErr;
+}
+
+//*********************************************************************************
+// [public] maxCapabilityForDomainState
+//
+// Finds the highest power state in the array whose input power requirement
+// is equal to the input parameter. Where a more intelligent decision is
+// possible, override this in the subclassed driver.
+//*********************************************************************************
+
+IOPMPowerStateIndex IOService::getPowerStateForDomainFlags( IOPMPowerFlags flags )
+{
+ IOPMPowerStateIndex stateIndex;
+
+ if (!fNumberOfPowerStates)
+ return kPowerStateZero;
+
+ for ( int order = fNumberOfPowerStates - 1; order >= 0; order-- )
+ {
+ stateIndex = fPowerStates[order].stateOrderToIndex;
+
+ if ( (flags & fPowerStates[stateIndex].inputPowerFlags) ==
+ fPowerStates[stateIndex].inputPowerFlags )
+ {
+ return stateIndex;
+ }
+ }
+ return kPowerStateZero;
+}
+
+unsigned long IOService::maxCapabilityForDomainState( IOPMPowerFlags domainState )
+{
+ return getPowerStateForDomainFlags(domainState);
+}
+
+//*********************************************************************************
+// [public] initialPowerStateForDomainState
+//
+// Called to query the power state for the initial power transition.
+//*********************************************************************************
+
+unsigned long IOService::initialPowerStateForDomainState( IOPMPowerFlags domainState )
+{
+ if (fResetPowerStateOnWake && (domainState & kIOPMRootDomainState))
+ {
+ // Return lowest power state for any root power domain changes
+ return kPowerStateZero;
+ }
+
+ return getPowerStateForDomainFlags(domainState);
+}
+
+//*********************************************************************************
+// [public] powerStateForDomainState
+//
+// This method is not called from PM.
+//*********************************************************************************
+
+unsigned long IOService::powerStateForDomainState( IOPMPowerFlags domainState )
+{
+ return getPowerStateForDomainFlags(domainState);
+}
+
+#ifndef __LP64__
+//*********************************************************************************
+// [deprecated] didYouWakeSystem
+//
+// Does nothing here. This should be implemented in a subclass driver.
+//*********************************************************************************
+
+bool IOService::didYouWakeSystem( void )
+{
+ return false;
+}
+#endif /* !__LP64__ */
+
+//*********************************************************************************
+// [public] powerStateWillChangeTo
+//
+// Does nothing here. This should be implemented in a subclass driver.
+//*********************************************************************************
+
+IOReturn IOService::powerStateWillChangeTo( IOPMPowerFlags, unsigned long, IOService * )
+{
+ return kIOPMAckImplied;
+}
+
+//*********************************************************************************
+// [public] powerStateDidChangeTo
+//
+// Does nothing here. This should be implemented in a subclass driver.
+//*********************************************************************************
+
+IOReturn IOService::powerStateDidChangeTo( IOPMPowerFlags, unsigned long, IOService * )
+{
+ return kIOPMAckImplied;
+}
+
+//*********************************************************************************
+// [protected] powerChangeDone
+//
+// Called from PM work loop thread.
+// Does nothing here. This should be implemented in a subclass policy-maker.
+//*********************************************************************************
+
+void IOService::powerChangeDone( unsigned long )
+{
+}
+
+#ifndef __LP64__
+//*********************************************************************************
+// [deprecated] newTemperature
+//
+// Does nothing here. This should be implemented in a subclass driver.
+//*********************************************************************************
+
+IOReturn IOService::newTemperature( long currentTemp, IOService * whichZone )
+{
+ return IOPMNoErr;
+}
+#endif /* !__LP64__ */
+
+//*********************************************************************************
+// [public] systemWillShutdown
+//
+// System shutdown and restart notification.
+//*********************************************************************************
+
+void IOService::systemWillShutdown( IOOptionBits specifier )
+{
+ IOPMrootDomain * rootDomain = IOService::getPMRootDomain();
+ if (rootDomain)
+ rootDomain->acknowledgeSystemWillShutdown( this );
+}
+
+// MARK: -
+// MARK: PM State Machine
+
+//*********************************************************************************
+// [private static] acquirePMRequest
+//*********************************************************************************
+
+IOPMRequest *
+IOService::acquirePMRequest( IOService * target, IOOptionBits requestType,
+ IOPMRequest * active )
+{
+ IOPMRequest * request;
+
+ assert(target);
+
+ request = IOPMRequest::create();
+ if (request)
+ {
+ request->init( target, requestType );
+ if (active)
+ {
+ IOPMRequest * root = active->getRootRequest();
+ if (root) request->attachRootRequest(root);
+ }
+ }
+ else
+ {
+ PM_ERROR("%s: No memory for PM request type 0x%x\n",
+ target->getName(), (uint32_t) requestType);
+ }
+ return request;
+}
+
+//*********************************************************************************
+// [private static] releasePMRequest
+//*********************************************************************************
+
+void IOService::releasePMRequest( IOPMRequest * request )
+{
+ if (request)
+ {
+ request->reset();
+ request->release();
+ }
+}
+
+//*********************************************************************************
+// [private] submitPMRequest
+//*********************************************************************************
+
+void IOService::submitPMRequest( IOPMRequest * request )
+{
+ assert( request );
+ assert( gIOPMReplyQueue );
+ assert( gIOPMRequestQueue );
+
+ PM_LOG1("[+ %02lx] %p [%p %s] %p %p %p\n",
+ (long)request->getType(), OBFUSCATE(request),
+ OBFUSCATE(request->getTarget()), request->getTarget()->getName(),
+ OBFUSCATE(request->fArg0),
+ OBFUSCATE(request->fArg1), OBFUSCATE(request->fArg2));
+
+ if (request->isReplyType())
+ gIOPMReplyQueue->queuePMRequest( request );
+ else
+ gIOPMRequestQueue->queuePMRequest( request );
+}
+
+void IOService::submitPMRequest( IOPMRequest ** requests, IOItemCount count )
+{
+ assert( requests );
+ assert( count > 0 );
+ assert( gIOPMRequestQueue );
+
+ for (IOItemCount i = 0; i < count; i++)
+ {
+ IOPMRequest * req = requests[i];
+ PM_LOG1("[+ %02lx] %p [%p %s] %p %p %p\n",
+ (long)req->getType(), OBFUSCATE(req),
+ OBFUSCATE(req->getTarget()), req->getTarget()->getName(),
+ OBFUSCATE(req->fArg0),
+ OBFUSCATE(req->fArg1), OBFUSCATE(req->fArg2));
+ }
+
+ gIOPMRequestQueue->queuePMRequestChain( requests, count );
+}
+
+//*********************************************************************************
+// [private] servicePMRequestQueue
+//
+// Called from IOPMRequestQueue::checkForWork().
+//*********************************************************************************
+
+bool IOService::servicePMRequestQueue(
+ IOPMRequest * request,
+ IOPMRequestQueue * queue )
+{
+ 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(), OBFUSCATE(request),
+ OBFUSCATE(this), getName(),
+ fMachineState, gIOPMBusyCount);
+
+ // Catch requests created by idleTimerExpired().
+
+ if (request->getType() == kIOPMRequestTypeActivityTickle)
+ {
+ uint32_t tickleFlags = (uint32_t)(uintptr_t) request->fArg1;
+
+ if ((tickleFlags & kTickleTypePowerDrop) && fIdleTimerPeriod)
+ {
+ restartIdleTimer();
+ }
+ else if (tickleFlags == (kTickleTypeActivity | kTickleTypePowerRise))
+ {
+ // Invalidate any idle power drop that got queued while
+ // processing this request.
+ fIdleTimerGeneration++;
+ }
+ }
+
+ // 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
+ // 8 = kRootDomainInformPreChange
+ 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(), OBFUSCATE(request),
+ OBFUSCATE(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(), OBFUSCATE(request),
+ OBFUSCATE(this), getName(), fMachineState);
+
+ gIOPMRequest = request;
+ gIOPMWorkCount++;
+
+ // Every PM machine states must be handled in one of the cases below.
+
+ switch ( fMachineState )
+ {
+ case kIOPM_Finished:
+ start_watchdog_timer();
+
+ 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)
+ {
+ // no, we can continue
+ OurChangeTellClientsPowerDown();
+ }
+ else
+ {
+ OUR_PMLog(kPMLogIdleCancel, (uintptr_t) this, fMachineState);
+ PM_ERROR("%s: idle cancel, state %u\n", fName, fMachineState);
+ // yes, rescind the warning
+ tellNoChangeDown(fHeadNotePowerState);
+ // mark the change note un-actioned
+ fHeadNoteChangeFlags |= kIOPMNotDone;
+ // and we're done
+ OurChangeFinish();
+ }
+ break;
+
+ case kIOPM_OurChangeTellUserPMPolicyPowerDown:
+ // PMRD: tellChangeDown/kNotifyApps done, was it cancelled?
+ if (fDoNotPowerDown)
+ {
+ OUR_PMLog(kPMLogIdleCancel, (uintptr_t) this, fMachineState);
+ PM_ERROR("%s: idle cancel, state %u\n", fName, fMachineState);
+ // yes, rescind the warning
+ tellNoChangeDown(fHeadNotePowerState);
+ // mark the change note un-actioned
+ fHeadNoteChangeFlags |= kIOPMNotDone;
+ // and we're done
+ OurChangeFinish();
+ }
+ else
+ OurChangeTellUserPMPolicyPowerDown();
+ break;
+
+ case kIOPM_OurChangeTellPriorityClientsPowerDown:
+ // PMRD: LastCallBeforeSleep notify done
+ // Non-PMRD: tellChangeDown/kNotifyApps done
+ if (fDoNotPowerDown)
+ {
+ OUR_PMLog(kPMLogIdleCancel, (uintptr_t) this, fMachineState);
+ PM_ERROR("%s: idle revert, state %u\n", fName, fMachineState);
+ // 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
+ {
+ // 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:
+ switch (fDriverCallReason)
+ {
+ case kDriverCallInformPreChange:
+ case kDriverCallInformPostChange:
+ notifyInterestedDriversDone();
+ break;
+ case kDriverCallSetPowerState:
+ notifyControllingDriverDone();
+ break;
+ case kRootDomainInformPreChange:
+ notifyRootDomainDone();
+ break;
+ default:
+ panic("%s: bad call reason %x",
+ getName(), fDriverCallReason);
+ }
+ break;
+
+ case kIOPM_NotifyChildrenOrdered:
+ notifyChildrenOrdered();
+ break;
+
+ case kIOPM_NotifyChildrenDelayed:
+ notifyChildrenDelayed();
+ break;
+
+ case kIOPM_NotifyChildrenStart:
+ // pop notifyAll() state saved by notifyInterestedDriversDone()
+ MS_POP();
+ notifyRootDomain();
+ 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
+ {
+ // Cancelled by IOPMrootDomain::askChangeDownDone() or
+ // askChangeDown/kNotifyApps
+ OUR_PMLog(kPMLogIdleCancel, (uintptr_t) this, fMachineState);
+ PM_ERROR("%s: idle cancel, state %u\n", fName, fMachineState);
+ tellNoChangeDown(fHeadNotePowerState);
+ fHeadNoteChangeFlags |= kIOPMNotDone;
+ OurChangeFinish();
+ }
+ break;
+
+ case kIOPM_SyncTellPriorityClientsPowerDown:
+ // PMRD: tellChangeDown/kNotifyApps done, was it cancelled?
+ if (!fDoNotPowerDown)
+ {
+ fMachineState = kIOPM_SyncNotifyWillChange;
+ fOutOfBandParameter = kNotifyPriority;
+ tellChangeDown(fHeadNotePowerState);
+ }
+ else
+ {
+ OUR_PMLog(kPMLogIdleCancel, (uintptr_t) this, fMachineState);
+ PM_ERROR("%s: idle revert, state %u\n", fName, fMachineState);
+ 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
+ {
+ assert(IS_ROOT_DOMAIN);
+ 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)
+ {
+ stop_watchdog_timer();
+ 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;
+
+ case kIOPMRequestTypeAddPowerChild2:
+ addPowerChild2( request );
+ break;
+
+ case kIOPMRequestTypeAddPowerChild3:
+ addPowerChild3( request );
+ break;
+
+ case kIOPMRequestTypeRegisterPowerDriver:
+ handleRegisterPowerDriver( request );
+ break;
+
+ case kIOPMRequestTypeAdjustPowerState:
+ fAdjustPowerScheduled = false;
+ adjustPowerState();
+ break;
+
+ case kIOPMRequestTypePowerDomainWillChange:
+ handlePowerDomainWillChangeTo( request );
+ break;
+
+ case kIOPMRequestTypePowerDomainDidChange:
+ handlePowerDomainDidChangeTo( request );
+ break;
+
+ case kIOPMRequestTypeRequestPowerState:
+ case kIOPMRequestTypeRequestPowerStateOverride:
+ handleRequestPowerState( request );
+ break;
+
+ case kIOPMRequestTypePowerOverrideOnPriv:
+ case kIOPMRequestTypePowerOverrideOffPriv:
+ handlePowerOverrideChanged( request );
+ break;
+
+ case kIOPMRequestTypeActivityTickle:
+ handleActivityTickle( request );
+ break;
+
+ case kIOPMRequestTypeSynchronizePowerTree:
+ handleSynchronizePowerTree( request );
+ break;
+
+ case kIOPMRequestTypeSetIdleTimerPeriod:
+ {
+ fIdleTimerPeriod = (uintptr_t) request->fArg0;
+ fNextIdleTimerPeriod = fIdleTimerPeriod;
+ if ((false == fLockedFlags.PMStop) && (fIdleTimerPeriod > 0))
+ restartIdleTimer();
+ }
+ break;
+
+ case kIOPMRequestTypeIgnoreIdleTimer:
+ fIdleTimerIgnored = request->fArg0 ? 1 : 0;
+ break;
+
+ default:
+ panic("executePMRequest: unknown request type %x", request->getType());
+ }
+}
+
+//*********************************************************************************
+// [private] servicePMReplyQueue
+//*********************************************************************************
+
+bool IOService::servicePMReplyQueue( IOPMRequest * request, IOPMRequestQueue * queue )
+{
+ bool more = false;
+
+ assert( request && queue );
+ assert( request->isReplyType() );
+
+ PM_LOG1("[A %02x] %p [%p %s] state %d\n",
+ request->getType(), OBFUSCATE(request),
+ OBFUSCATE(this), getName(), fMachineState);
+
+ switch ( request->getType() )
+ {
+ case kIOPMRequestTypeAllowPowerChange:
+ case kIOPMRequestTypeCancelPowerChange:
+ // Check if we are expecting this response.
+ if (responseValid((uint32_t)(uintptr_t) request->fArg0,
+ (int)(uintptr_t) request->fArg1))
+ {
+ if (kIOPMRequestTypeCancelPowerChange == request->getType())
+ {
+ // Clients are not allowed to cancel when kIOPMSkipAskPowerDown
+ // flag is set. Only root domain will set this flag.
+ // However, there is one exception to this rule. User-space PM
+ // policy may choose to cancel sleep even after all clients have
+ // been notified that we will lower power.
+
+ if ((fMachineState == kIOPM_OurChangeTellUserPMPolicyPowerDown)
+ || (fMachineState == kIOPM_OurChangeTellPriorityClientsPowerDown)
+ || ((fHeadNoteChangeFlags & kIOPMSkipAskPowerDown) == 0))
+ {
+ fDoNotPowerDown = true;
+
+ OSString * name = (OSString *) request->fArg2;
+ getPMRootDomain()->pmStatsRecordApplicationResponse(
+ gIOPMStatsApplicationResponseCancel,
+ name ? name->getCStringNoCopy() : "", 0,
+ 0, (int)(uintptr_t) request->fArg1, 0);
+ }
+ }
+
+ if (checkForDone())
+ {
+ stop_ack_timer();
+ cleanClientResponses(false);
+ more = true;
+ }
+ }
+ // OSString containing app name in Arg2 must be released.
+ if (request->getType() == kIOPMRequestTypeCancelPowerChange)
+ {
+ OSObject * obj = (OSObject *) request->fArg2;
+ if (obj) obj->release();
+ }
+ break;
+
+ case kIOPMRequestTypeAckPowerChange:
+ more = handleAcknowledgePowerChange( request );
+ break;
+
+ case kIOPMRequestTypeAckSetPowerState:
+ if (fDriverTimer == -1)
+ {
+ // driver acked while setPowerState() call is in-flight.
+ // take this ack, return value from setPowerState() is irrelevant.
+ OUR_PMLog(kPMLogDriverAcknowledgeSet,
+ (uintptr_t) this, fDriverTimer);
+ fDriverTimer = 0;
+ }
+ else if (fDriverTimer > 0)
+ {
+ // expected ack, stop the timer
+ stop_ack_timer();
+
+#if LOG_SETPOWER_TIMES
+ uint64_t nsec = computeTimeDeltaNS(&fDriverCallStartTime);
+ if (nsec > LOG_SETPOWER_TIMES) {
+ getPMRootDomain()->pmStatsRecordApplicationResponse(
+ gIOPMStatsDriverPSChangeSlow,
+ fName, kDriverCallSetPowerState, NS_TO_MS(nsec), 0, NULL, fHeadNotePowerState);
+ }
+#endif
+ OUR_PMLog(kPMLogDriverAcknowledgeSet, (uintptr_t) this, fDriverTimer);
+ fDriverTimer = 0;
+ more = true;
+ }
+ else
+ {
+ // unexpected ack
+ OUR_PMLog(kPMLogAcknowledgeErr4, (uintptr_t) this, 0);
+ }
+ break;
+
+ case kIOPMRequestTypeInterestChanged:
+ handleInterestChanged( request );
+ more = true;
+ break;