+// Notify registered applications and kernel clients that we are definitely
+// dropping power.
+//
+// Return true if we don't have to wait for acknowledgements
+//*********************************************************************************
+
+bool IOService::tellClientsWithResponse ( int messageType )
+{
+ IOPMInterestContext context;
+ bool isRootDomain = IS_ROOT_DOMAIN;
+
+ PM_ASSERT_IN_GATE();
+ assert( fResponseArray == NULL );
+ assert( fNotifyClientArray == NULL );
+
+ RD_LOG("tellClientsWithResponse( %s, %d )\n",
+ getIOMessageString(messageType), fOutOfBandParameter);
+
+ fResponseArray = OSArray::withCapacity( 1 );
+ if (!fResponseArray)
+ goto exit;
+
+ fResponseArray->setCapacityIncrement(8);
+ if (++fSerialNumber == 0)
+ fSerialNumber++;
+
+ context.responseArray = fResponseArray;
+ context.notifyClients = 0;
+ context.serialNumber = fSerialNumber;
+ context.messageType = messageType;
+ context.notifyType = fOutOfBandParameter;
+ context.isPreChange = fIsPreChange;
+ context.enableTracing = false;
+ context.us = this;
+ context.maxTimeRequested = 0;
+ context.stateNumber = fHeadNotePowerState;
+ context.stateFlags = fHeadNotePowerArrayEntry->capabilityFlags;
+ context.changeFlags = fHeadNoteChangeFlags;
+ context.messageFilter = (isRootDomain) ?
+ OSMemberFunctionCast(
+ IOPMMessageFilter,
+ this,
+ &IOPMrootDomain::systemMessageFilter) : 0;
+
+ switch ( fOutOfBandParameter ) {
+ case kNotifyApps:
+ applyToInterested( gIOAppPowerStateInterest,
+ pmTellAppWithResponse, (void *) &context );
+
+ if (isRootDomain &&
+ (fMachineState != kIOPM_OurChangeTellClientsPowerDown) &&
+ (fMachineState != kIOPM_SyncTellClientsPowerDown))
+ {
+ // Notify capability app for tellChangeDown1()
+ // but not for askChangeDown().
+ context.notifyType = kNotifyCapabilityChangeApps;
+ context.messageType = kIOMessageSystemCapabilityChange;
+ applyToInterested( gIOAppPowerStateInterest,
+ pmTellCapabilityAppWithResponse, (void *) &context );
+ context.notifyType = fOutOfBandParameter;
+ context.messageType = messageType;
+ }
+ context.maxTimeRequested = k30seconds;
+
+ applyToInterested( gIOGeneralInterest,
+ pmTellClientWithResponse, (void *) &context );
+
+ fNotifyClientArray = context.notifyClients;
+ break;
+
+ case kNotifyPriority:
+ context.enableTracing = isRootDomain;
+ applyToInterested( gIOPriorityPowerStateInterest,
+ pmTellClientWithResponse, (void *) &context );
+
+ if (isRootDomain)
+ {
+ // Notify capability clients for tellChangeDown2().
+ context.notifyType = kNotifyCapabilityChangePriority;
+ context.messageType = kIOMessageSystemCapabilityChange;
+ applyToInterested( gIOPriorityPowerStateInterest,
+ pmTellCapabilityClientWithResponse, (void *) &context );
+ }
+ break;
+
+ case kNotifyCapabilityChangeApps:
+ applyToInterested( gIOAppPowerStateInterest,
+ pmTellCapabilityAppWithResponse, (void *) &context );
+ fNotifyClientArray = context.notifyClients;
+ context.maxTimeRequested = k30seconds;
+ break;
+
+ case kNotifyCapabilityChangePriority:
+ applyToInterested( gIOPriorityPowerStateInterest,
+ pmTellCapabilityClientWithResponse, (void *) &context );
+ break;
+ }
+
+ // do we have to wait for somebody?
+ if ( !checkForDone() )
+ {
+ OUR_PMLog(kPMLogStartAckTimer, context.maxTimeRequested, 0);
+ if (context.enableTracing)
+ getPMRootDomain()->traceDetail( context.maxTimeRequested / 1000 );
+ start_ack_timer( context.maxTimeRequested / 1000, kMillisecondScale );
+ return false;
+ }
+
+exit:
+ // everybody responded
+ if (fResponseArray)
+ {
+ fResponseArray->release();
+ fResponseArray = NULL;
+ }
+ if (fNotifyClientArray)
+ {
+ fNotifyClientArray->release();
+ fNotifyClientArray = NULL;
+ }
+
+ return true;
+}
+
+//*********************************************************************************
+// [static private] pmTellAppWithResponse
+//
+// We send a message to an application, and we expect a response, so we compute a
+// cookie we can identify the response with.
+//*********************************************************************************
+
+void IOService::pmTellAppWithResponse ( OSObject * object, void * arg )
+{
+ IOPMInterestContext * context = (IOPMInterestContext *) arg;
+ IOServicePM * pwrMgt = context->us->pwrMgt;
+ uint32_t msgIndex, msgRef, msgType;
+#if LOG_APP_RESPONSE_TIMES
+ AbsoluteTime now;
+#endif
+
+ if (!OSDynamicCast(_IOServiceInterestNotifier, object))
+ 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();
+ }
+ return;
+ }
+
+ // Create client array (for tracking purposes) only if the service
+ // has app clients. Usually only root domain does.
+ if (0 == context->notifyClients)
+ context->notifyClients = OSArray::withCapacity( 32 );
+
+ msgType = context->messageType;
+ msgIndex = context->responseArray->getCount();
+ msgRef = ((context->serialNumber & 0xFFFF) << 16) + (msgIndex & 0xFFFF);
+
+ 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();
+ }
+
+#if LOG_APP_RESPONSE_TIMES
+ OSNumber * num;
+ clock_get_uptime(&now);
+ num = OSNumber::withNumber(AbsoluteTime_to_scalar(&now), sizeof(uint64_t) * 8);
+ if (num)
+ {
+ context->responseArray->setObject(msgIndex, num);
+ num->release();
+ }
+ else
+#endif
+ context->responseArray->setObject(msgIndex, kOSBooleanFalse);
+
+ if (context->notifyClients)
+ context->notifyClients->setObject(msgIndex, object);
+
+ context->us->messageClient(msgType, object, (void *) msgRef);
+}
+
+//*********************************************************************************
+// [static private] pmTellClientWithResponse
+//
+// We send a message to an in-kernel client, and we expect a response,
+// so we compute a cookie we can identify the response with.
+//*********************************************************************************
+
+void IOService::pmTellClientWithResponse ( OSObject * object, void * arg )
+{
+ IOPowerStateChangeNotification notify;
+ IOPMInterestContext * context = (IOPMInterestContext *) arg;
+ OSObject * replied = kOSBooleanTrue;
+ _IOServiceInterestNotifier * notifier;
+ uint32_t msgIndex, msgRef, msgType;
+ IOReturn retCode;
+
+ if (context->messageFilter &&
+ !context->messageFilter(context->us, object, context, 0, 0))
+ {
+ if ((kIOLogDebugPower & gIOKitDebug) &&
+ (OSDynamicCast(_IOServiceInterestNotifier, object)))
+ {
+ _IOServiceInterestNotifier *n = (_IOServiceInterestNotifier *) object;
+ PM_LOG("%s DROP Client %s, notifier %p, handler %p\n",
+ context->us->getName(),
+ getIOMessageString(context->messageType),
+ object, n->handler);
+ }
+ return;
+ }
+
+ notifier = OSDynamicCast(_IOServiceInterestNotifier, object);
+ msgType = context->messageType;
+ msgIndex = context->responseArray->getCount();
+ msgRef = ((context->serialNumber & 0xFFFF) << 16) + (msgIndex & 0xFFFF);
+
+ IOServicePM * pwrMgt = context->us->pwrMgt;