/*
* Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
*
- * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. The rights granted to you under the
- * License may not be used to create, or enable the creation or
- * redistribution of, unlawful or unlicensed copies of an Apple operating
- * system, or to circumvent, violate, or enable the circumvention or
- * violation of, any terms of an Apple operating system software license
- * agreement.
- *
- * Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
* limitations under the License.
- *
- * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
#include <IOKit/assert.h>
#include <IOKit/pwr_mgt/IOPowerConnection.h>
#include <IOKit/pwr_mgt/RootDomain.h>
+
// Required for notification instrumentation
#include "IOServicePrivate.h"
pm_vars->responseFlags = NULL;
pm_vars->doNotPowerDown = true;
pm_vars->PMcommandGate = NULL;
- priv->ackTimer = thread_call_allocate((thread_call_func_t)ack_timer_expired, (thread_call_param_t)this);
- priv->settleTimer = thread_call_allocate((thread_call_func_t)settle_timer_expired, (thread_call_param_t)this);
+ priv->ackTimer = thread_call_allocate(
+ (thread_call_func_t)ack_timer_expired,
+ (thread_call_param_t)this);
+ priv->settleTimer = thread_call_allocate(
+ (thread_call_func_t)settle_timer_expired,
+ (thread_call_param_t)this);
+
initialized = true;
}
}
// Put ourselves into a usable power state.
// We must be in an "on" power state, as our children must be able to access
// our hardware after joining the power plane.
- makeUsable();
+ temporaryMakeUsable();
// make a nub
connection = new IOPowerConnection;
}
-//*********************************************************************************
+//******************************************************************************
// removePowerChild
//
-//*********************************************************************************
+//******************************************************************************
IOReturn IOService::removePowerChild ( IOPowerConnection * theNub )
{
IORegistryEntry *theChild;
- OSIterator *iter;
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogRemoveChild,0,0);
if (priv->head_note_pendingAcks != 0 )
{
// that's one fewer ack to worry about
- priv->head_note_pendingAcks -= 1;
+ OSAddAtomic(-1, (SInt32*)&priv->head_note_pendingAcks);
// is that the last?
if ( priv->head_note_pendingAcks == 0 )
{
return IOPMNoErr;
}
- // Perhaps the departing child was holding up idle or system sleep - we need to re-evaluate our
- // childrens' requests. Clear and re-calculate our kIOPMChildClamp and kIOPMChildClamp2 bits.
+ // Perhaps the departing child was holding up idle or system sleep - we
+ // need to re-evaluate our childrens' requests.
+ // Clear and re-calculate our kIOPMChildClamp and kIOPMChildClamp2 bits.
rebuildChildClampBits();
- if(!priv->clampOn)
- {
- // count children
- iter = getChildIterator(gIOPowerPlane);
- if ( !iter || !iter->getNextObject() )
- {
- // paired to match the makeUsable() call in addPowerChild()
- changePowerStateToPriv(0);
- }
- if(iter) iter->release();
- }
-
- // this may be different now
+ // Change state if we can now tolerate lower power
computeDesiredState();
- // change state if we can now tolerate lower power
changeState();
return IOPMNoErr;
// mark it acked
ackingObject->timer = 0;
// that's one fewer to worry about
- priv->head_note_pendingAcks -= 1;
+ OSAddAtomic(-1, (SInt32*)&priv->head_note_pendingAcks);
// is that the last?
if ( priv->head_note_pendingAcks == 0 )
{
if ( ((IOPowerConnection *)whichObject)->getAwaitingAck() )
{
// that's one fewer to worry about
- priv->head_note_pendingAcks -= 1;
+ OSAddAtomic(-1, (SInt32*)&priv->head_note_pendingAcks);
((IOPowerConnection *)whichObject)->setAwaitingAck(false);
theChild = (IOService *)whichObject->copyChildEntry(gIOPowerPlane);
if ( theChild )
void IOService::driver_acked ( void )
{
+
switch (priv->machine_state) {
case kIOPM_OurChangeWaitForPowerSettle:
OurChangeWaitForPowerSettle();
return IOPMNoErr;
}
+//******************************************************************************
+// temporaryMakeUsable
+//
+// Private function, called by IOService::addPowerChild to ensure that the
+// device is temporarily in a usable power state so that attached power
+// children may properly initialize.
+//******************************************************************************
+
+IOReturn IOService::temporaryMakeUsable ( void )
+{
+ IOReturn ret = kIOReturnSuccess;
+ unsigned long tempDesire;
+
+ pm_vars->thePlatform->PMLog( pm_vars->ourName,
+ PMlogMakeUsable,
+ PMlogMakeUsable,
+ priv->deviceDesire);
+
+ if ( pm_vars->theControllingDriver == NULL )
+ {
+ priv->need_to_become_usable = true;
+ return IOPMNoErr;
+ }
+ tempDesire = priv->deviceDesire;
+ priv->deviceDesire = pm_vars->theNumberOfPowerStates - 1;
+ computeDesiredState();
+ if ( inPlane(gIOPowerPlane) && (pm_vars->parentsKnowState) )
+ {
+ ret = changeState();
+ }
+ priv->deviceDesire = tempDesire;
+ return ret;
+}
+
//*********************************************************************************
// currentCapability
}
if ( pm_vars->myCurrentState > 0 )
{
+
+ unsigned long newState = pm_vars->myCurrentState - 1;
+
IOUnlock(priv->activityLock);
- changePowerStateToPriv(pm_vars->myCurrentState - 1);
- start_PM_idle_timer();
+ changePowerStateToPriv(newState);
+ if ( newState >= priv->ourDesiredPowerState )
+ start_PM_idle_timer();
return;
}
IOUnlock(priv->activityLock);
// needn't perform the previous change, so we collapse the list a little.
//*********************************************************************************
-IOReturn IOService::enqueuePowerChange ( unsigned long flags, unsigned long whatStateOrdinal, unsigned long domainState, IOPowerConnection * whichParent, unsigned long singleParentState )
+IOReturn IOService::enqueuePowerChange (
+ unsigned long flags,
+ unsigned long whatStateOrdinal,
+ unsigned long domainState,
+ IOPowerConnection * whichParent,
+ unsigned long singleParentState )
{
long newNote;
long previousNote;
OSObject * next;
IOPowerConnection * connection;
- // To prevent acknowledgePowerChange from finishing the change note and removing it from the queue if
- // some driver calls it, we inflate the number of pending acks so it cannot become zero. We'll fix it later.
-
- priv->head_note_pendingAcks =1;
+ // To prevent acknowledgePowerChange from finishing the change note and
+ // removing it from the queue if
+ // some driver calls it, we inflate the number of pending acks so it
+ // cannot become zero. We'll fix it later.
+
+ if(!acquire_lock()) return IOPMAckImplied;
- // OK, we will go through the lists of interested drivers and power domain children
- // and notify each one of this change.
+ OSAddAtomic(1, (SInt32*)&priv->head_note_pendingAcks);
+ // OK, we will go through the lists of interested drivers and
+ // power domain children and notify each one of this change.
+
nextObject = priv->interestedDrivers->firstInList();
while ( nextObject != NULL ) {
- priv->head_note_pendingAcks +=1;
- if (! inform(nextObject, is_prechange) )
+
+ OSAddAtomic(1, (SInt32*)&priv->head_note_pendingAcks);
+
+ IOUnlock(priv->our_lock);
+
+ inform(nextObject, is_prechange);
+
+ if(!acquire_lock())
{
+ goto exit;
}
+
nextObject = priv->interestedDrivers->nextInList(nextObject);
}
- if (! acquire_lock() ) {
- return IOPMNoErr;
- }
// did they all ack?
if ( priv->head_note_pendingAcks > 1 ) {
// no
// summing their power consumption
pm_vars->thePowerStates[priv->head_note_state].staticPower = 0;
- if ( iter )
+ if ( iter && acquire_lock())
{
while ( (next = iter->getNextObject()) )
{
if ( (connection = OSDynamicCast(IOPowerConnection,next)) )
{
- priv->head_note_pendingAcks +=1;
+ OSAddAtomic(1, (SInt32*)&priv->head_note_pendingAcks);
+
+ IOUnlock(priv->our_lock);
+
notifyChild(connection, is_prechange);
+
+ if(!acquire_lock())
+ {
+ goto exit;
+ }
}
}
iter->release();
+ IOUnlock(priv->our_lock);
}
if (! acquire_lock() ) {
return IOPMNoErr;
}
// now make this real
- priv->head_note_pendingAcks -= 1;
+ OSAddAtomic(-1, (SInt32*)&priv->head_note_pendingAcks);
+
// is it all acked?
if (priv->head_note_pendingAcks == 0 ) {
// yes, all acked
// not all acked
IOUnlock(priv->our_lock);
+
+exit: // unable to acquire_lock exit case
+
return IOPMWillAckLater;
}
// The child has been detached since we grabbed the child iterator.
// Decrement pending_acks, already incremented in notifyAll,
// to account for this unexpected departure.
- priv->head_note_pendingAcks--;
+
+ if( acquire_lock() )
+ {
+ OSAddAtomic(-1, (SInt32*)&priv->head_note_pendingAcks);
+ IOUnlock(priv->our_lock);
+ }
return true;
}
if ( k == IOPMAckImplied )
{
// yes
- priv->head_note_pendingAcks--;
+ if( acquire_lock() )
+ {
+ OSAddAtomic(-1, (SInt32*)&priv->head_note_pendingAcks);
+ IOUnlock(priv->our_lock);
+ }
+
theNub->setAwaitingAck(false);
childPower = theChild->currentPowerConsumption();
if ( childPower == kIOPMUnknown )
return true;
}
- // no, did the return code ack?
- if ( k ==IOPMAckImplied )
+ if ( (k ==IOPMAckImplied) // no, did the return code ack?
+ || (k < 0) ) // somebody goofed
{
// yes
nextObject->timer = 0;
- priv->head_note_pendingAcks -= 1;
- return true;
- }
- if ( k<0 )
- {
- // somebody goofed
- nextObject->timer = 0;
- priv-> head_note_pendingAcks -= 1;
+
+ if( acquire_lock() )
+ {
+ OSAddAtomic(-1, (SInt32*)&priv->head_note_pendingAcks);
+ IOUnlock(priv->our_lock);
+ }
return true;
}
{
priv->machine_state = kIOPM_OurChangeWaitForPowerSettle;
+ IOLockLock(priv->our_lock);
+
if ( instruct_driver(priv->head_note_state) == IOPMAckImplied )
{
// it's done, carry on
+ IOLockUnlock(priv->our_lock);
OurChangeWaitForPowerSettle();
} else {
// it's not, wait for it
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogStartAckTimer,0,0);
start_ack_timer();
+ IOLockUnlock(priv->our_lock);
// execution will resume via ack_timer_ticked()
}
}
{
priv->machine_state = kIOPM_ParentDownWaitForPowerSettle_Delayed;
+ IOLockLock(priv->our_lock);
+
if ( instruct_driver(priv->head_note_state) == IOPMAckImplied )
{
// it's done, carry on
+ IOLockUnlock(priv->our_lock);
return ParentDownWaitForPowerSettleAndNotifyDidChange_Immediate();
}
// it's not, wait for it
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogStartAckTimer,0,0);
start_ack_timer();
+ IOLockUnlock(priv->our_lock);
return IOPMWillAckLater;
}
{
priv-> machine_state = kIOPM_ParentDownWaitForPowerSettle_Delayed;
+ IOLockLock(priv->our_lock);
+
if ( instruct_driver(priv->head_note_state) == IOPMAckImplied )
{
// it's done, carry on
+ IOLockUnlock(priv->our_lock);
ParentDownWaitForPowerSettle_Delayed();
} else {
// it's not, wait for it
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogStartAckTimer,0,0);
start_ack_timer();
+ IOLockUnlock(priv->our_lock);
}
}
{
priv->machine_state = kIOPM_ParentUpWaitForSettleTime_Delayed;
+ IOLockLock(priv->our_lock);
+
if ( instruct_driver(priv->head_note_state) == IOPMAckImplied )
{
// it did it, carry on
+ IOLockUnlock(priv->our_lock);
ParentUpWaitForSettleTime_Delayed();
} else {
// it didn't, wait for it
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogStartAckTimer,0,0);
start_ack_timer();
+ IOLockUnlock(priv->our_lock);
}
}
{
priv->machine_state = kIOPM_ParentUpWaitForSettleTime_Delayed;
+ IOLockLock(priv->our_lock);
+
if ( instruct_driver(priv->head_note_state) == IOPMAckImplied )
{
// it did it, carry on
+ IOLockUnlock(priv->our_lock);
return ParentUpWaitForSettleTime_Immediate();
}
else {
// it didn't, wait for it
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogStartAckTimer,0,0);
start_ack_timer();
+ IOLockUnlock(priv->our_lock);
return IOPMWillAckLater;
}
}
start_our_change(priv->head_note);
} else {
nub = priv->changeList->changeNote[priv->head_note].parent;
+ if (nub) nub->retain(); // might be released by start_parent_change()
if ( start_parent_change(priv->head_note) == IOPMAckImplied )
{
parent = (IOService *)nub->copyParentEntry(gIOPowerPlane);
parent->release();
}
}
+ if (nub) nub->release();
}
} else {
IOLockUnlock(priv->queue_lock);
//
//*********************************************************************************
-void IOService::all_acked ( void )
+
+
+void IOService::all_acked( void )
{
switch (priv->machine_state) {
case kIOPM_OurChangeSetPowerState:
{
pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogIntDriverTardy,0,0);
//kprintf("interested driver tardy: %s\n",nextObject->whatObject->getName());
- priv->head_note_pendingAcks -= 1;
+ OSAddAtomic(-1, (SInt32*)&priv->head_note_pendingAcks);
}
}
nextObject = priv->interestedDrivers->nextInList(nextObject);
case kIOPM_ParentDownSetPowerState_Delayed:
case kIOPM_ParentUpSetPowerState_Delayed:
// one for this child and one to prevent
- priv->head_note_pendingAcks += 2;
+ OSAddAtomic(2, (SInt32*)&priv->head_note_pendingAcks);
// incoming acks from changing our state
IOUnlock(priv->our_lock);
notifyChild(newObject, true);
if (! acquire_lock() )
{
// put it back
- --priv->head_note_pendingAcks;
+ OSAddAtomic(-1, (SInt32*)&priv->head_note_pendingAcks);
return IOPMNoErr;
}
// are we still waiting for acks?
- if ( --priv->head_note_pendingAcks == 0 )
+ OSAddAtomic(-1, (SInt32*)&priv->head_note_pendingAcks);
+ if ( priv->head_note_pendingAcks == 0 )
{
// no, stop the timer
stop_ack_timer();
case kIOPM_ParentDownAcknowledgeChange_Delayed:
case kIOPM_ParentUpAcknowledgePowerChange_Delayed:
// one for this child and one to prevent
- priv->head_note_pendingAcks += 2;
+ OSAddAtomic(2, (SInt32*)&priv->head_note_pendingAcks);
// incoming acks from changing our state
IOUnlock(priv->our_lock);
notifyChild(newObject, false);
if (! acquire_lock() )
{
// put it back
- --priv->head_note_pendingAcks;
+ OSAddAtomic(-1, (SInt32*)&priv->head_note_pendingAcks);
return IOPMNoErr;
}
// are we still waiting for acks?
- if ( --priv->head_note_pendingAcks == 0 )
+ OSAddAtomic(-1, (SInt32*)&priv->head_note_pendingAcks);
+ if ( priv->head_note_pendingAcks == 0 )
{
// no, stop the timer
stop_ack_timer();
case kIOPM_ParentDownSetPowerState_Delayed:
case kIOPM_ParentUpSetPowerState_Delayed:
// one for this driver and one to prevent
- priv->head_note_pendingAcks += 2;
+ OSAddAtomic(2, (SInt32*)&priv->head_note_pendingAcks);
// incoming acks from changing our state
IOUnlock(priv->our_lock);
// inform the driver
if (! acquire_lock() )
{
// put it back
- --priv->head_note_pendingAcks;
+ OSAddAtomic(-1, (SInt32*)&priv->head_note_pendingAcks);
return IOPMNoErr;
}
// are we still waiting for acks?
- if ( --priv->head_note_pendingAcks == 0 )
+ OSAddAtomic(-1, (SInt32*)&priv->head_note_pendingAcks);
+ if ( priv->head_note_pendingAcks == 0 )
{
// no, stop the timer
stop_ack_timer();
case kIOPM_ParentDownAcknowledgeChange_Delayed:
case kIOPM_ParentUpAcknowledgePowerChange_Delayed:
// one for this driver and one to prevent
- priv->head_note_pendingAcks += 2;
+ OSAddAtomic(2, (SInt32*)&priv->head_note_pendingAcks);
// incoming acks from changing our state
IOUnlock(priv->our_lock);
// inform the driver
inform(newObject, false);
if (! acquire_lock() ) {
// put it back
- --priv->head_note_pendingAcks;
+ OSAddAtomic(-1, (SInt32*)&priv->head_note_pendingAcks);
return IOPMNoErr;
}
// are we still waiting for acks?
- if ( --priv->head_note_pendingAcks == 0 ) {
+ OSAddAtomic(-1, (SInt32*)&priv->head_note_pendingAcks);
+ if ( priv->head_note_pendingAcks == 0 ) {
// no, stop the timer
stop_ack_timer();
IOUnlock(priv->our_lock);
priv->driver_timer = -1;
// yes, instruct it
+ IOLockUnlock(priv->our_lock);
OUR_PMLog( kPMLogProgramHardware, (UInt32) this, newState);
delay = pm_vars->theControllingDriver->setPowerState( newState,this );
OUR_PMLog((UInt32) -kPMLogProgramHardware, (UInt32) this, (UInt32) delay);
+ IOLockLock(priv->our_lock);
// it finished
if ( delay == IOPMAckImplied )