#include <IOKit/pwr_mgt/IOPMPrivate.h>
#include <sys/proc.h>
+#include <sys/proc_internal.h>
#include <libkern/OSDebug.h>
// Required for notification instrumentation
static IOPMWorkQueue * gIOPMWorkQueue = 0;
static IOPMCompletionQueue * gIOPMFreeQueue = 0;
static IOPMRequest * gIOPMRequest = 0;
-static IOPlatformExpert * gPlatform = 0;
static IOService * gIOPMRootNode = 0;
+static IOPlatformExpert * gPlatform = 0;
static const OSSymbol * gIOPMPowerClientDevice = 0;
static const OSSymbol * gIOPMPowerClientDriver = 0;
#define PM_LOCK_WAKEUP(event) IOLockWakeup(fPMLock, event, false)
#define ns_per_us 1000
-#define k30seconds (30*1000000)
+#define k30Seconds (30*1000000)
#define kMinAckTimeoutTicks (10*1000000)
#define kIOPMTardyAckSPSKey "IOPMTardyAckSetPowerState"
#define kIOPMTardyAckPSCKey "IOPMTardyAckPowerStateChange"
do { if (fPMActions.a) { \
(fPMActions.a)(fPMActions.target, this, &fPMActions, x, y); } \
} while (false)
+
+static OSNumber * copyClientIDForNotification(
+ OSObject *object,
+ IOPMInterestContext *context);
+
+static void logClientIDForNotification(
+ OSObject *object,
+ IOPMInterestContext *context,
+ const char *logString);
+
//*********************************************************************************
// PM machine states
PM_ASSERT_IN_GATE();
PM_LOG2("%s: %p %s start\n", getName(), this, __FUNCTION__);
+ // remove driver from prevent system sleep lists
+ getPMRootDomain()->updatePreventIdleSleepList(this, false);
+ getPMRootDomain()->updatePreventSystemSleepList(this, false);
+
// remove the property
removeProperty(kPwrMgtKey);
}
}
-//*********************************************************************************
-// [private] rebuildChildClampBits
-//
-// The ChildClamp bits (kIOPMChildClamp & kIOPMChildClamp2) in our capabilityFlags
-// indicate that one of our children (or grandchildren or great-grandchildren ...)
-// doesn't support idle or system sleep in its current state. Since we don't track
-// the origin of each bit, every time any child changes state we have to clear
-// these bits and rebuild them.
-//*********************************************************************************
+//******************************************************************************
+// [private] trackSystemSleepPreventers
+//******************************************************************************
-void IOService::rebuildChildClampBits ( void )
+void IOService::trackSystemSleepPreventers(
+ IOPMPowerStateIndex oldPowerState,
+ IOPMPowerStateIndex newPowerState,
+ IOPMPowerChangeFlags changeFlags __unused )
{
- unsigned long i;
- OSIterator * iter;
- OSObject * next;
- IOPowerConnection * connection;
- unsigned long powerState;
-
- // A child's desires has changed. We need to rebuild the child-clamp bits in
- // our power state array. Start by clearing the bits in each power state.
-
- for ( i = 0; i < fNumberOfPowerStates; i++ )
- {
- fPowerStates[i].capabilityFlags &= ~(kIOPMChildClamp | kIOPMChildClamp2);
- }
+ IOPMPowerFlags oldCapability, newCapability;
- if (!inPlane(gIOPowerPlane))
- return;
+ oldCapability = fPowerStates[oldPowerState].capabilityFlags &
+ (kIOPMPreventIdleSleep | kIOPMPreventSystemSleep);
+ newCapability = fPowerStates[newPowerState].capabilityFlags &
+ (kIOPMPreventIdleSleep | kIOPMPreventSystemSleep);
- // Loop through the children. When we encounter the calling child, save the
- // computed state as this child's desire. And set the ChildClamp bits in any
- // of our states that some child has clamp on.
+ if (fHeadNoteChangeFlags & kIOPMInitialPowerChange)
+ oldCapability = 0;
+ if (oldCapability == newCapability)
+ return;
- iter = getChildIterator(gIOPowerPlane);
- if ( iter )
+ if ((oldCapability ^ newCapability) & kIOPMPreventIdleSleep)
{
- while ( (next = iter->getNextObject()) )
+#if SUPPORT_IDLE_CANCEL
+ if ((oldCapability & kIOPMPreventIdleSleep) == 0)
{
- if ( (connection = OSDynamicCast(IOPowerConnection, next)) )
- {
- if (connection->getReadyFlag() == false)
- {
- PM_LOG3("[%s] %s: connection not ready\n",
- getName(), __FUNCTION__);
- continue;
- }
+ IOPMRequest * cancelRequest;
- powerState = connection->getDesiredDomainState();
- if (powerState < fNumberOfPowerStates)
- {
- if ( connection->getPreventIdleSleepFlag() )
- fPowerStates[powerState].capabilityFlags |= kIOPMChildClamp;
- if ( connection->getPreventSystemSleepFlag() )
- fPowerStates[powerState].capabilityFlags |= kIOPMChildClamp2;
- }
+ cancelRequest = acquirePMRequest( this, kIOPMRequestTypeIdleCancel );
+ if (cancelRequest)
+ {
+ getPMRootDomain()->submitPMRequest( cancelRequest );
}
}
- iter->release();
+#endif
+
+ getPMRootDomain()->updatePreventIdleSleepList(this,
+ ((oldCapability & kIOPMPreventIdleSleep) == 0));
+ }
+
+ if ((oldCapability ^ newCapability) & kIOPMPreventSystemSleep)
+ {
+
+ getPMRootDomain()->updatePreventSystemSleepList(this,
+ ((oldCapability & kIOPMPreventSystemSleep) == 0));
}
}
IOPMPowerFlags outputPowerFlags;
IOService * child;
IOPMRequest * subRequest;
- bool preventIdle, preventSleep;
bool adjustPower = false;
if (!initialized)
child = (IOService *) childConnection->getChildEntry(gIOPowerPlane);
assert(child);
- preventIdle = ((childRequestPowerFlags & kIOPMPreventIdleSleep) != 0);
- preventSleep = ((childRequestPowerFlags & kIOPMPreventSystemSleep) != 0);
- childRequestPowerFlags &= ~(kIOPMPreventIdleSleep | kIOPMPreventSystemSleep);
-
// Merge in the power flags contributed by this power parent
// at its current or impending power state.
// prevent idle/sleep flags towards the root domain.
if (!childConnection->childHasRequestedPower() ||
- (ps != childConnection->getDesiredDomainState()) ||
- (childConnection->getPreventIdleSleepFlag() != preventIdle) ||
- (childConnection->getPreventSystemSleepFlag() != preventSleep))
+ (ps != childConnection->getDesiredDomainState()))
adjustPower = true;
#if ENABLE_DEBUG_LOGS
#endif
// Record the child's desires on the connection.
-#if SUPPORT_IDLE_CANCEL
- bool attemptCancel = (preventIdle && !childConnection->getPreventIdleSleepFlag());
-#endif
childConnection->setChildHasRequestedPower();
childConnection->setDesiredDomainState( ps );
- childConnection->setPreventIdleSleepFlag( preventIdle );
- childConnection->setPreventSystemSleepFlag( preventSleep );
// Schedule a request to re-evaluate all children desires and
// adjust power state. Submit a request if one wasn't pending,
}
}
-#if SUPPORT_IDLE_CANCEL
- if (attemptCancel)
- {
- subRequest = acquirePMRequest( this, kIOPMRequestTypeIdleCancel );
- if (subRequest)
- {
- submitPMRequest( subRequest );
- }
- }
-#endif
-
return kIOReturnSuccess;
}
return kIOReturnSuccess;
}
+IOReturn IOService::setIgnoreIdleTimer( bool ignore )
+{
+ if (!initialized)
+ return IOPMNotYetInitialized;
+
+ OUR_PMLog(kIOPMRequestTypeIgnoreIdleTimer, ignore, 0);
+
+ IOPMRequest * request =
+ acquirePMRequest( this, kIOPMRequestTypeIgnoreIdleTimer );
+ if (!request)
+ return kIOReturnNoMemory;
+
+ request->fArg0 = (void *) ignore;
+ submitPMRequest( request );
+
+ return kIOReturnSuccess;
+}
+
//******************************************************************************
// [public] nextIdleTimeout
//
// Device was active - do not drop power, restart timer.
fDeviceWasActive = false;
}
- else
+ else if (!fIdleTimerIgnored)
{
// No device activity - drop power state by one level.
// Decrement the cached tickle power state when possible.
if (fInitialSetPowerState)
{
+ fInitialSetPowerState = false;
+ fHeadNoteChangeFlags |= kIOPMInitialPowerChange;
+
// Driver specified flag to skip the inital setPowerState()
if (fHeadNotePowerArrayEntry->capabilityFlags & kIOPMInitialDeviceState)
{
return false;
}
- fInitialSetPowerState = false;
}
param = (DriverCallParam *) fDriverCallParamPtr;
// could our driver switch to the new state?
if ( !( fHeadNoteChangeFlags & kIOPMNotDone) )
{
+ trackSystemSleepPreventers(
+ fCurrentPowerState, fHeadNotePowerState, fHeadNoteChangeFlags);
+
// we changed, tell our parent
requestDomainPower(fHeadNotePowerState);
((fHeadNoteChangeFlags & kIOPMDomainDidChange) &&
(fCurrentPowerState < fHeadNotePowerState)))
{
+ trackSystemSleepPreventers(
+ fCurrentPowerState, fHeadNotePowerState, fHeadNoteChangeFlags);
+
// did power raise?
if ( fCurrentPowerState < fHeadNotePowerState )
{
IOPMPowerStateIndex ourPowerState,
IOOptionBits options )
{
- const IOPMPSEntry * powerStateEntry;
IOPMPowerFlags requestPowerFlags;
IOPMPowerStateIndex maxPowerState;
IOPMRequestDomainPowerContext context;
// Fetch the input power flags for the requested power state.
// Parent request is stated in terms of required power flags.
- powerStateEntry = &fPowerStates[ourPowerState];
- requestPowerFlags = powerStateEntry->inputPowerFlags;
-
- if (powerStateEntry->capabilityFlags & (kIOPMChildClamp | kIOPMPreventIdleSleep))
- requestPowerFlags |= kIOPMPreventIdleSleep;
- if (powerStateEntry->capabilityFlags & (kIOPMChildClamp2 | kIOPMPreventSystemSleep))
- requestPowerFlags |= kIOPMPreventSystemSleep;
+ requestPowerFlags = fPowerStates[ourPowerState].inputPowerFlags;
// Disregard the "previous request" for power reservation.
(flag = context->responseArray->getObject(clientIndex)) &&
(flag != kOSBooleanTrue))
{
- OSString * clientID = 0;
- context->us->messageClient(context->messageType, object, &clientID);
- PM_ERROR(context->errorLog, clientID ? clientID->getCStringNoCopy() : "");
+ OSString *logClientID = NULL;
+ OSNumber *clientID = copyClientIDForNotification(object, context);
+
+ if (clientID) {
+ logClientID = IOCopyLogNameForPID(clientID->unsigned32BitValue());
+ clientID->release();
+ }
+
+ PM_ERROR(context->errorLog, logClientID ? logClientID->getCStringNoCopy() : "");
// TODO: record message type if possible
IOService::getPMRootDomain()->pmStatsRecordApplicationResponse(
gIOPMStatsApplicationResponseTimedOut,
- clientID ? clientID->getCStringNoCopy() : "",
+ logClientID ? logClientID->getCStringNoCopy() : "",
0, (30*1000), -1);
- if (clientID)
- clientID->release();
+ if (logClientID)
+ logClientID->release();
}
}
}
context.notifyType = fOutOfBandParameter;
context.messageType = messageType;
}
- context.maxTimeRequested = k30seconds;
+ context.maxTimeRequested = k30Seconds;
applyToInterested( gIOGeneralInterest,
pmTellClientWithResponse, (void *) &context );
applyToInterested( gIOAppPowerStateInterest,
pmTellCapabilityAppWithResponse, (void *) &context );
fNotifyClientArray = context.notifyClients;
- context.maxTimeRequested = k30seconds;
+ context.maxTimeRequested = k30Seconds;
break;
case kNotifyCapabilityChangePriority:
IOPMInterestContext * context = (IOPMInterestContext *) arg;
IOServicePM * pwrMgt = context->us->pwrMgt;
uint32_t msgIndex, msgRef, msgType;
+ OSNumber *clientID = NULL;
+ proc_t proc = NULL;
+ boolean_t proc_suspended = FALSE;
#if LOG_APP_RESPONSE_TIMES
AbsoluteTime now;
#endif
if (!OSDynamicCast(_IOServiceInterestNotifier, object))
return;
+ if (context->us == getPMRootDomain())
+ {
+ if ((clientID = copyClientIDForNotification(object, context)))
+ {
+ uint32_t clientPID = clientID->unsigned32BitValue();
+ clientID->release();
+ proc = proc_find(clientPID);
+
+ if (proc)
+ {
+ proc_suspended = get_task_pidsuspended((task_t) proc->task);
+ proc_rele(proc);
+
+ if (proc_suspended)
+ {
+ logClientIDForNotification(object, context, "PMTellAppWithResponse - Suspended");
+ return;
+ }
+ }
+ }
+ }
+
if (context->messageFilter &&
!context->messageFilter(context->us, object, context, 0, 0))
{
if (kIOLogDebugPower & gIOKitDebug)
{
- // Log client pid/name and client array index.
- OSString * clientID = 0;
- context->us->messageClient(kIOMessageCopyClientID, object, &clientID);
- PM_LOG("%s DROP App %s, %s\n",
- context->us->getName(),
- getIOMessageString(context->messageType),
- clientID ? clientID->getCStringNoCopy() : "");
- if (clientID) clientID->release();
+ logClientIDForNotification(object, context, "DROP App");
}
return;
}
OUR_PMLog(kPMLogAppNotify, msgType, msgRef);
if (kIOLogDebugPower & gIOKitDebug)
{
- // Log client pid/name and client array index.
- OSString * clientID = 0;
- context->us->messageClient(kIOMessageCopyClientID, object, &clientID);
- PM_LOG("%s MESG App(%u) %s, %s\n",
- context->us->getName(),
- msgIndex, getIOMessageString(msgType),
- clientID ? clientID->getCStringNoCopy() : "");
- if (clientID) clientID->release();
+ logClientIDForNotification(object, context, "MESG App");
}
#if LOG_APP_RESPONSE_TIMES
getPMRootDomain()->traceDetail( detail );
}
- retCode = context->us->messageClient(msgType, object, (void *) ¬ify);
- if ( kIOReturnSuccess == retCode )
+ retCode = context->us->messageClient(msgType, object, (void *) ¬ify, sizeof(notify));
+
+ if (kIOReturnSuccess == retCode)
{
- if ( 0 == notify.returnValue )
- {
- // client doesn't want time to respond
+ if (0 == notify.returnValue) {
OUR_PMLog(kPMLogClientAcknowledge, msgRef, (uintptr_t) object);
- }
- else
- {
+ } else {
replied = kOSBooleanFalse;
if ( notify.returnValue > context->maxTimeRequested )
{
context->maxTimeRequested = notify.returnValue;
}
}
- }
- else
- {
+ } else {
// not a client of ours
// so we won't be waiting for response
OUR_PMLog(kPMLogClientAcknowledge, msgRef, 0);
if (kIOLogDebugPower & gIOKitDebug)
{
// Log client pid/name and client array index.
- OSString * clientID = 0;
+ OSNumber * clientID = NULL;
+ OSString * clientIDString = NULL;;
context->us->messageClient(kIOMessageCopyClientID, object, &clientID);
+ if (clientID) {
+ clientIDString = IOCopyLogNameForPID(clientID->unsigned32BitValue());
+ }
+
PM_LOG("%s MESG App(%u) %s, wait %u, %s\n",
context->us->getName(),
msgIndex, getIOMessageString(msgType),
(replied != kOSBooleanTrue),
- clientID ? clientID->getCStringNoCopy() : "");
+ clientIDString ? clientIDString->getCStringNoCopy() : "");
if (clientID) clientID->release();
+ if (clientIDString) clientIDString->release();
}
msgArg.notifyRef = msgRef;
notify.stateNumber = context->stateNumber;
notify.stateFlags = context->stateFlags;
- context->us->messageClient(context->messageType, object, ¬ify);
+ context->us->messageClient(context->messageType, object, ¬ify, sizeof(notify));
if ((kIOLogDebugPower & gIOKitDebug) &&
(OSDynamicCast(_IOServiceInterestNotifier, object)))
}
}
-//*********************************************************************************
-// [private] tellAppClientApplier
-//
-// Message a registered application.
-//*********************************************************************************
+static OSNumber * copyClientIDForNotification(
+ OSObject *object,
+ IOPMInterestContext *context)
+{
+ OSNumber *clientID = NULL;
+ context->us->messageClient(kIOMessageCopyClientID, object, &clientID);
+ return clientID;
+}
+
+static void logClientIDForNotification(
+ OSObject *object,
+ IOPMInterestContext *context,
+ const char *logString)
+{
+ OSString *logClientID = NULL;
+ OSNumber *clientID = copyClientIDForNotification(object, context);
+
+ if (logString)
+ {
+ if (clientID)
+ logClientID = IOCopyLogNameForPID(clientID->unsigned32BitValue());
+
+ PM_LOG("%s %s %s, %s\n",
+ context->us->getName(), logString,
+ IOService::getIOMessageString(context->messageType),
+ logClientID ? logClientID->getCStringNoCopy() : "");
+
+ if (logClientID)
+ logClientID->release();
+ }
+
+ if (clientID)
+ clientID->release();
+
+ return;
+}
+
static void tellAppClientApplier ( OSObject * object, void * arg )
{
IOPMInterestContext * context = (IOPMInterestContext *) arg;
+ OSNumber * clientID = NULL;
+ proc_t proc = NULL;
+ boolean_t proc_suspended = FALSE;
+
+ if (context->us == IOService::getPMRootDomain())
+ {
+ if ((clientID = copyClientIDForNotification(object, context)))
+ {
+ uint32_t clientPID = clientID->unsigned32BitValue();
+ clientID->release();
+ proc = proc_find(clientPID);
+
+ if (proc)
+ {
+ proc_suspended = get_task_pidsuspended((task_t) proc->task);
+ proc_rele(proc);
+
+ if (proc_suspended)
+ {
+ logClientIDForNotification(object, context, "tellAppClientApplier - Suspended");
+ return;
+ }
+ }
+ }
+ }
if (context->messageFilter &&
!context->messageFilter(context->us, object, context, 0, 0))
{
if (kIOLogDebugPower & gIOKitDebug)
{
- // Log client pid/name and client array index.
- OSString * clientID = 0;
- context->us->messageClient(kIOMessageCopyClientID, object, &clientID);
- PM_LOG("%s DROP App %s, %s\n",
- context->us->getName(),
- IOService::getIOMessageString(context->messageType),
- clientID ? clientID->getCStringNoCopy() : "");
- if (clientID) clientID->release();
+ logClientIDForNotification(object, context, "DROP App");
}
return;
}
if (kIOLogDebugPower & gIOKitDebug)
{
- // Log client pid/name and client array index.
- OSString * clientID = 0;
- context->us->messageClient(kIOMessageCopyClientID, object, &clientID);
- PM_LOG("%s MESG App %s, %s\n",
- context->us->getName(),
- IOService::getIOMessageString(context->messageType),
- clientID ? clientID->getCStringNoCopy() : "");
- if (clientID) clientID->release();
+ logClientIDForNotification(object, context, "MESG App");
}
context->us->messageClient(context->messageType, object, 0);
int i = 0;
OSObject * theFlag;
- if ( fResponseArray == NULL )
- {
+ if (fResponseArray == NULL) {
return true;
}
- for ( i = 0; ; i++ )
- {
+ for (i = 0; ; i++) {
theFlag = fResponseArray->getObject(i);
- if ( theFlag == NULL )
- {
+
+ if (NULL == theFlag) {
break;
}
- if ( kOSBooleanTrue != theFlag )
- {
+
+ if (kOSBooleanTrue != theFlag) {
return false;
}
}
case kIOPMRequestTypeAdjustPowerState:
fAdjustPowerScheduled = false;
- rebuildChildClampBits();
adjustPowerState();
break;
}
break;
+ case kIOPMRequestTypeIgnoreIdleTimer:
+ fIdleTimerIgnored = request->fArg0 ? 1 : 0;
+ break;
+
default:
panic("executePMRequest: unknown request type %x", request->getType());
}