+// Subclass can override this to send a different message type. Parameter is
+// the aborted destination state number.
+//*********************************************************************************
+
+void
+IOService::tellNoChangeDown( unsigned long )
+{
+ return tellClients( kIOMessageDeviceWillNotPowerOff );
+}
+
+//*********************************************************************************
+// [public] tellChangeUp
+//
+// Notify registered applications and kernel clients that we are raising power.
+//
+// Subclass can override this to send a different message type. Parameter is
+// the aborted destination state number.
+//*********************************************************************************
+
+void
+IOService::tellChangeUp( unsigned long )
+{
+ return tellClients( kIOMessageDeviceHasPoweredOn );
+}
+
+//*********************************************************************************
+// [protected] tellClients
+//
+// Notify registered applications and kernel clients of something.
+//*********************************************************************************
+
+void
+IOService::tellClients( int messageType )
+{
+ IOPMInterestContext context;
+
+ RD_LOG("tellClients( %s )\n", getIOMessageString(messageType));
+
+ memset(&context, 0, sizeof(context));
+ context.messageType = messageType;
+ context.isPreChange = fIsPreChange;
+ context.us = this;
+ context.stateNumber = fHeadNotePowerState;
+ context.stateFlags = fHeadNotePowerArrayEntry->capabilityFlags;
+ context.changeFlags = fHeadNoteChangeFlags;
+ context.enableTracing = IS_ROOT_DOMAIN;
+ context.messageFilter = (IS_ROOT_DOMAIN) ?
+ OSMemberFunctionCast(
+ IOPMMessageFilter,
+ this,
+ &IOPMrootDomain::systemMessageFilter) : NULL;
+
+ context.notifyType = kNotifyPriority;
+ applyToInterested( gIOPriorityPowerStateInterest,
+ tellKernelClientApplier, (void *) &context );
+
+ context.notifyType = kNotifyApps;
+ applyToInterested( gIOAppPowerStateInterest,
+ tellAppClientApplier, (void *) &context );
+
+ applyToInterested( gIOGeneralInterest,
+ tellKernelClientApplier, (void *) &context );
+}
+
+//*********************************************************************************
+// [private] tellKernelClientApplier
+//
+// Message a kernel client.
+//*********************************************************************************
+
+static void
+tellKernelClientApplier( OSObject * object, void * arg )
+{
+ IOPowerStateChangeNotification notify;
+ IOPMInterestContext * context = (IOPMInterestContext *) arg;
+
+ if (context->messageFilter &&
+ !context->messageFilter(context->us, object, context, NULL, NULL)) {
+ if ((kIOLogDebugPower & gIOKitDebug) &&
+ (OSDynamicCast(_IOServiceInterestNotifier, object))) {
+ _IOServiceInterestNotifier *n = (_IOServiceInterestNotifier *) object;
+ PM_LOG("%s DROP Client %s, notifier %p, handler %p\n",
+ context->us->getName(),
+ IOService::getIOMessageString(context->messageType),
+ OBFUSCATE(object), OBFUSCATE(n->handler));
+ }
+ return;
+ }
+
+ notify.powerRef = (void *) NULL;
+ notify.returnValue = 0;
+ notify.stateNumber = context->stateNumber;
+ notify.stateFlags = context->stateFlags;
+
+ if (context->enableTracing && object) {
+ IOService::getPMRootDomain()->traceDetail(object, true);
+ }
+ context->us->messageClient(context->messageType, object, ¬ify, sizeof(notify));
+ if (context->enableTracing && object) {
+ IOService::getPMRootDomain()->traceDetail(object, false);
+ }
+
+
+
+ if ((kIOLogDebugPower & gIOKitDebug) &&
+ (OSDynamicCast(_IOServiceInterestNotifier, object))) {
+ _IOServiceInterestNotifier *n = (_IOServiceInterestNotifier *) object;
+ PM_LOG("%s MESG Client %s, notifier %p, handler %p\n",
+ context->us->getName(),
+ IOService::getIOMessageString(context->messageType),
+ OBFUSCATE(object), OBFUSCATE(n->handler));
+ }
+}
+
+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);
+ if (proc_suspended) {
+ logClientIDForNotification(object, context, "tellAppClientApplier - Suspended");
+#if !(defined(RC_HIDE_N144) || defined(RC_HIDE_N146))
+ } else if (IOService::getPMRootDomain()->isAOTMode() && get_task_suspended((task_t) proc->task)) {
+ proc_suspended = true;
+ context->skippedInDark++;
+#endif /* !(defined(RC_HIDE_N144) || defined(RC_HIDE_N146)) */
+ }
+ proc_rele(proc);
+ if (proc_suspended) {
+ return;
+ }
+ }
+ }
+ }
+
+ if (context->messageFilter &&
+ !context->messageFilter(context->us, object, context, NULL, NULL)) {
+ if (kIOLogDebugPower & gIOKitDebug) {
+ logClientIDForNotification(object, context, "DROP App");
+ }
+ return;
+ }
+ context->notSkippedInDark++;
+
+ if (kIOLogDebugPower & gIOKitDebug) {
+ logClientIDForNotification(object, context, "MESG App");
+ }
+
+ context->us->messageClient(context->messageType, object, NULL);
+}
+
+//*********************************************************************************
+// [private] checkForDone
+//*********************************************************************************
+
+bool
+IOService::checkForDone( void )
+{
+ int i = 0;
+ OSObject * theFlag;
+
+ if (fResponseArray == NULL) {
+ return true;
+ }
+
+ for (i = 0;; i++) {
+ theFlag = fResponseArray->getObject(i);
+
+ if (NULL == theFlag) {
+ break;
+ }
+
+ if (kOSBooleanTrue != theFlag) {
+ return false;
+ }
+ }
+ return true;
+}
+
+//*********************************************************************************
+// [public] responseValid
+//*********************************************************************************
+
+bool
+IOService::responseValid( uint32_t refcon, int pid )
+{
+ UInt16 serialComponent;
+ UInt16 ordinalComponent;
+ OSObject * theFlag;
+ OSObject *object = NULL;
+
+ serialComponent = (refcon >> 16) & 0xFFFF;
+ ordinalComponent = (refcon & 0xFFFF);
+
+ if (serialComponent != fSerialNumber) {
+ return false;
+ }
+
+ if (fResponseArray == NULL) {
+ return false;
+ }
+
+ theFlag = fResponseArray->getObject(ordinalComponent);
+
+ if (theFlag == NULL) {
+ return false;
+ }
+
+ if (fNotifyClientArray) {
+ object = fNotifyClientArray->getObject(ordinalComponent);
+ }
+
+ OSNumber * num;
+ if ((num = OSDynamicCast(OSNumber, theFlag))) {
+ AbsoluteTime now;
+ AbsoluteTime start;
+ uint64_t nsec;
+ char name[128];
+
+ clock_get_uptime(&now);
+ AbsoluteTime_to_scalar(&start) = num->unsigned64BitValue();
+ SUB_ABSOLUTETIME(&now, &start);
+ absolutetime_to_nanoseconds(now, &nsec);
+
+ if (pid != 0) {
+ name[0] = '\0';
+ proc_name(pid, name, sizeof(name));
+
+ if (nsec > LOG_APP_RESPONSE_TIMES) {
+ IOLog("PM response took %d ms (%d, %s)\n", NS_TO_MS(nsec),
+ pid, name);
+ }
+
+
+ if (nsec > LOG_APP_RESPONSE_MSG_TRACER) {
+ // TODO: populate the messageType argument
+ getPMRootDomain()->pmStatsRecordApplicationResponse(
+ gIOPMStatsResponseSlow,
+ name, 0, NS_TO_MS(nsec), pid, object);
+ } else {
+ getPMRootDomain()->pmStatsRecordApplicationResponse(
+ gIOPMStatsResponsePrompt,
+ name, 0, NS_TO_MS(nsec), pid, object);
+ }
+ } else {
+ getPMRootDomain()->traceAckDelay(object, 0, NS_TO_MS(nsec));
+ }
+
+ if (kIOLogDebugPower & gIOKitDebug) {
+ PM_LOG("Ack(%u) %u ms\n",
+ (uint32_t) ordinalComponent,
+ NS_TO_MS(nsec));
+ }
+ theFlag = kOSBooleanFalse;
+ } else if (object) {
+ getPMRootDomain()->pmStatsRecordApplicationResponse(
+ gIOPMStatsResponsePrompt,
+ NULL, 0, 0, pid, object);
+ }
+
+ if (kOSBooleanFalse == theFlag) {
+ fResponseArray->replaceObject(ordinalComponent, kOSBooleanTrue);
+ }
+
+ return true;
+}
+
+//*********************************************************************************
+// [public] allowPowerChange
+//
+// Our power state is about to lower, and we have notified applications
+// and kernel clients, and one of them has acknowledged. If this is the last to do
+// so, and all acknowledgements are positive, we continue with the power change.
+//*********************************************************************************
+
+IOReturn
+IOService::allowPowerChange( unsigned long refcon )
+{
+ 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 *) NULL;
+ 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;
+}
+
+//*********************************************************************************
+// cancelIdlePowerDown
+//
+// Internal method to trigger an idle cancel or revert
+//*********************************************************************************
+
+void
+IOService::cancelIdlePowerDown( IOService * service )
+{
+ IOPMRequest * request;
+
+ request = acquirePMRequest(service, kIOPMRequestTypeIdleCancel);
+ if (request) {
+ submitPMRequest(request);
+ }
+}
+
+#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.