+//******************************************************************************
+// handleAggressivesRequests
+//
+// Backend thread processes all incoming aggressiveness requests in the queue.
+//******************************************************************************
+
+static void
+handleAggressivesFunction(
+ thread_call_param_t param1,
+ thread_call_param_t param2 )
+{
+ if (param1)
+ {
+ ((IOPMrootDomain *) param1)->handleAggressivesRequests();
+ }
+}
+
+void IOPMrootDomain::handleAggressivesRequests( void )
+{
+ AggressivesRecord * start;
+ AggressivesRecord * record;
+ AggressivesRequest * request;
+ queue_head_t joinedQueue;
+ int i, count;
+ bool broadcast;
+ bool found;
+ bool pingSelf = false;
+
+ AGGRESSIVES_LOCK();
+
+ if ((gAggressivesState & kAggressivesStateBusy) || !aggressivesData ||
+ queue_empty(&aggressivesQueue))
+ goto unlock_done;
+
+ gAggressivesState |= kAggressivesStateBusy;
+ count = aggressivesData->getLength() / sizeof(AggressivesRecord);
+ start = (AggressivesRecord *) aggressivesData->getBytesNoCopy();
+
+ do
+ {
+ broadcast = false;
+ queue_init(&joinedQueue);
+
+ do
+ {
+ // Remove request from the incoming queue in FIFO order.
+ queue_remove_first(&aggressivesQueue, request, AggressivesRequest *, chain);
+ switch (request->dataType)
+ {
+ case kAggressivesRequestTypeRecord:
+ // Update existing record if found.
+ found = false;
+ for (i = 0, record = start; i < count; i++, record++)
+ {
+ if (record->type == request->data.record.type)
+ {
+ found = true;
+
+ if (request->options & kAggressivesOptionQuickSpindownEnable)
+ {
+ if ((record->flags & kAggressivesRecordFlagMinValue) == 0)
+ {
+ broadcast = true;
+ record->flags |= (kAggressivesRecordFlagMinValue |
+ kAggressivesRecordFlagModified);
+ DLOG("quick spindown accelerated, was %u min\n",
+ record->value);
+ }
+ }
+ else if (request->options & kAggressivesOptionQuickSpindownDisable)
+ {
+ if (record->flags & kAggressivesRecordFlagMinValue)
+ {
+ broadcast = true;
+ record->flags |= kAggressivesRecordFlagModified;
+ record->flags &= ~kAggressivesRecordFlagMinValue;
+ DLOG("disk spindown restored to %u min\n",
+ record->value);
+ }
+ }
+ else if (record->value != request->data.record.value)
+ {
+ record->value = request->data.record.value;
+ if ((record->flags & kAggressivesRecordFlagMinValue) == 0)
+ {
+ broadcast = true;
+ record->flags |= kAggressivesRecordFlagModified;
+ }
+ }
+ break;
+ }
+ }
+
+ // No matching record, append a new record.
+ if (!found &&
+ ((request->options & kAggressivesOptionQuickSpindownDisable) == 0))
+ {
+ AggressivesRecord newRecord;
+
+ newRecord.flags = kAggressivesRecordFlagModified;
+ newRecord.type = request->data.record.type;
+ newRecord.value = request->data.record.value;
+ if (request->options & kAggressivesOptionQuickSpindownEnable)
+ {
+ newRecord.flags |= kAggressivesRecordFlagMinValue;
+ DLOG("disk spindown accelerated\n");
+ }
+
+ aggressivesData->appendBytes(&newRecord, sizeof(newRecord));
+
+ // OSData may have switched to another (larger) buffer.
+ count = aggressivesData->getLength() / sizeof(AggressivesRecord);
+ start = (AggressivesRecord *) aggressivesData->getBytesNoCopy();
+ broadcast = true;
+ }
+
+ // Finished processing the request, release it.
+ IODelete(request, AggressivesRequest, 1);
+ break;
+
+ case kAggressivesRequestTypeService:
+ // synchronizeAggressives() will free request.
+ queue_enter(&joinedQueue, request, AggressivesRequest *, chain);
+ break;
+
+ default:
+ panic("bad aggressives request type %x\n", request->dataType);
+ break;
+ }
+ } while (!queue_empty(&aggressivesQueue));
+
+ // Release the lock to perform work, with busy flag set.
+ if (!queue_empty(&joinedQueue) || broadcast)
+ {
+ AGGRESSIVES_UNLOCK();
+ if (!queue_empty(&joinedQueue))
+ synchronizeAggressives(&joinedQueue, start, count);
+ if (broadcast)
+ broadcastAggressives(start, count);
+ AGGRESSIVES_LOCK();
+ }
+
+ // Remove the modified flag from all records.
+ for (i = 0, record = start; i < count; i++, record++)
+ {
+ if ((record->flags & kAggressivesRecordFlagModified) &&
+ ((record->type == kPMMinutesToDim) ||
+ (record->type == kPMMinutesToSleep)))
+ pingSelf = true;
+
+ record->flags &= ~kAggressivesRecordFlagModified;
+ }
+
+ // Check the incoming queue again since new entries may have been
+ // added while lock was released above.
+
+ } while (!queue_empty(&aggressivesQueue));
+
+ gAggressivesState &= ~kAggressivesStateBusy;
+
+unlock_done:
+ AGGRESSIVES_UNLOCK();
+
+ // Root domain is interested in system and display sleep slider changes.
+ // Submit a power event to handle those changes on the PM work loop.
+
+ if (pingSelf && pmPowerStateQueue) {
+ pmPowerStateQueue->submitPowerEvent( kPowerEventAggressivenessChanged );
+ }
+}
+
+
+//******************************************************************************
+// synchronizeAggressives
+//
+// Push all known aggressiveness records to one or more IOService.
+//******************************************************************************
+
+void IOPMrootDomain::synchronizeAggressives(
+ queue_head_t * joinedQueue,
+ const AggressivesRecord * array,
+ int count )
+{
+ IOService * service;
+ AggressivesRequest * request;
+ const AggressivesRecord * record;
+ uint32_t value;
+ int i;
+
+ while (!queue_empty(joinedQueue))
+ {
+ queue_remove_first(joinedQueue, request, AggressivesRequest *, chain);
+ if (request->dataType == kAggressivesRequestTypeService)
+ service = request->data.service;
+ else
+ service = 0;
+
+ IODelete(request, AggressivesRequest, 1);
+ request = 0;
+
+ if (service)
+ {
+ if (service->assertPMThreadCall())
+ {
+ for (i = 0, record = array; i < count; i++, record++)
+ {
+ value = record->value;
+ if (record->flags & kAggressivesRecordFlagMinValue)
+ value = kAggressivesMinValue;
+
+ DLOG("synchronizeAggressives 0x%x = %u to %s\n",
+ record->type, value, service->getName());
+ service->setAggressiveness(record->type, value);
+ }
+ service->deassertPMThreadCall();
+ }
+ service->release(); // retained by joinAggressiveness()
+ }
+ }
+}
+
+
+//******************************************************************************
+// broadcastAggressives
+//
+// Traverse PM tree and call setAggressiveness() for records that have changed.
+//******************************************************************************
+
+void IOPMrootDomain::broadcastAggressives(
+ const AggressivesRecord * array,
+ int count )
+{
+ IORegistryIterator * iter;
+ IORegistryEntry * entry;
+ IOPowerConnection * connect;
+ IOService * service;
+ const AggressivesRecord * record;
+ uint32_t value;
+ int i;
+
+ iter = IORegistryIterator::iterateOver(
+ this, gIOPowerPlane, kIORegistryIterateRecursively);
+ if (iter)
+ {
+ do
+ {
+ iter->reset();
+ while ((entry = iter->getNextObject()))
+ {
+ connect = OSDynamicCast(IOPowerConnection, entry);
+ if (!connect || !connect->getReadyFlag())
+ continue;
+
+ if ((service = (IOService *) connect->copyChildEntry(gIOPowerPlane)))
+ {
+ if (service->assertPMThreadCall())
+ {
+ for (i = 0, record = array; i < count; i++, record++)
+ {
+ if (record->flags & kAggressivesRecordFlagModified)
+ {
+ value = record->value;
+ if (record->flags & kAggressivesRecordFlagMinValue)
+ value = kAggressivesMinValue;
+ DLOG("broadcastAggressives %x = %u to %s\n",
+ record->type, value, service->getName());
+ service->setAggressiveness(record->type, value);
+ }
+ }
+ service->deassertPMThreadCall();
+ }
+ service->release();
+ }
+ }
+ }
+ while (!entry && !iter->isValid());
+ iter->release();
+ }
+}
+
+
+//******************************************************************************
+// startIdleSleepTimer
+//
+//******************************************************************************
+
+void IOPMrootDomain::startIdleSleepTimer( uint32_t inSeconds )
+{
+ AbsoluteTime deadline;
+
+ ASSERT_GATED();
+ if (inSeconds)
+ {
+ clock_interval_to_deadline(inSeconds, kSecondScale, &deadline);
+ thread_call_enter_delayed(extraSleepTimer, deadline);
+ idleSleepTimerPending = true;
+ DLOG("idle timer set for %u seconds\n", inSeconds);
+ }
+}
+
+
+//******************************************************************************
+// cancelIdleSleepTimer
+//
+//******************************************************************************
+
+void IOPMrootDomain::cancelIdleSleepTimer( void )
+{
+ ASSERT_GATED();
+ if (idleSleepTimerPending)
+ {
+ DLOG("idle timer cancelled\n");
+ thread_call_cancel(extraSleepTimer);
+ idleSleepTimerPending = false;
+ }
+}
+
+
+//******************************************************************************
+// idleSleepTimerExpired
+//
+//******************************************************************************
+
+static void idleSleepTimerExpired(
+ thread_call_param_t us, thread_call_param_t )
+{
+ ((IOPMrootDomain *)us)->handleSleepTimerExpiration();
+}
+
+static void wakeupClamshellTimerExpired(
+ thread_call_param_t us, thread_call_param_t )
+{
+ ((IOPMrootDomain *)us)->stopIgnoringClamshellEventsDuringWakeup();
+}
+
+
+//******************************************************************************
+// handleSleepTimerExpiration
+//
+// The time between the sleep idle timeout and the next longest one has elapsed.
+// It's time to sleep. Start that by removing the clamp that's holding us awake.
+//******************************************************************************
+
+void IOPMrootDomain::handleSleepTimerExpiration( void )
+{
+ if (!getPMworkloop()->inGate())
+ {
+ getPMworkloop()->runAction(
+ OSMemberFunctionCast(IOWorkLoop::Action, this,
+ &IOPMrootDomain::handleSleepTimerExpiration),
+ this);
+ return;
+ }
+
+ AbsoluteTime time;
+
+ DLOG("sleep timer expired\n");
+ ASSERT_GATED();
+
+ idleSleepTimerPending = false;
+
+ clock_get_uptime(&time);
+ if ((AbsoluteTime_to_scalar(&time) > autoWakeStart) &&
+ (AbsoluteTime_to_scalar(&time) < autoWakeEnd))
+ {
+ thread_call_enter_delayed(extraSleepTimer, *((AbsoluteTime *) &autoWakeEnd));
+ return;
+ }
+
+ // accelerate disk spin down if spin down timer is non-zero
+ setQuickSpinDownTimeout();
+
+ sleepASAP = true;
+ adjustPowerState();
+}
+
+
+//******************************************************************************
+// stopIgnoringClamshellEventsDuringWakeup
+//
+//******************************************************************************
+
+void IOPMrootDomain::stopIgnoringClamshellEventsDuringWakeup( void )
+{
+ if (!getPMworkloop()->inGate())
+ {
+ getPMworkloop()->runAction(
+ OSMemberFunctionCast(IOWorkLoop::Action, this,
+ &IOPMrootDomain::stopIgnoringClamshellEventsDuringWakeup),
+ this);
+ return;
+ }
+
+ ASSERT_GATED();
+
+ // Allow clamshell-induced sleep now
+ ignoringClamshellOnWake = false;
+
+ // Re-send clamshell event, in case it causes a sleep
+ if (clamshellIsClosed)
+ handlePowerNotification( kLocalEvalClamshellCommand );
+}
+
+
+//******************************************************************************