X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/6601e61aa18bf4f09af135ff61fc7f4771d23b06..0c530ab8987f0ae6a1a3d9284f40182b88852816:/iokit/Kernel/IOServicePM.cpp?ds=inline diff --git a/iokit/Kernel/IOServicePM.cpp b/iokit/Kernel/IOServicePM.cpp index a20e0a6a0..66e271062 100644 --- a/iokit/Kernel/IOServicePM.cpp +++ b/iokit/Kernel/IOServicePM.cpp @@ -38,6 +38,7 @@ #include #include + // Required for notification instrumentation #include "IOServicePrivate.h" @@ -281,8 +282,13 @@ void IOService::PMinit ( void ) 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; } } @@ -585,7 +591,7 @@ IOReturn IOService::addPowerChild ( IOService * theChild ) // 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; @@ -631,14 +637,13 @@ IOReturn IOService::addPowerChild ( IOService * theChild ) } -//********************************************************************************* +//****************************************************************************** // removePowerChild // -//********************************************************************************* +//****************************************************************************** IOReturn IOService::removePowerChild ( IOPowerConnection * theNub ) { IORegistryEntry *theChild; - OSIterator *iter; pm_vars->thePlatform->PMLog(pm_vars->ourName,PMlogRemoveChild,0,0); @@ -664,7 +669,7 @@ IOReturn IOService::removePowerChild ( IOPowerConnection * theNub ) 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 ) { @@ -693,25 +698,13 @@ IOReturn IOService::removePowerChild ( IOPowerConnection * theNub ) 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; @@ -946,7 +939,7 @@ IOReturn IOService::acknowledgePowerChange ( IOService * whichObject ) // 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 ) { @@ -968,7 +961,7 @@ IOReturn IOService::acknowledgePowerChange ( IOService * whichObject ) 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 ) @@ -1051,6 +1044,7 @@ IOReturn IOService::acknowledgeSetPowerState ( void ) void IOService::driver_acked ( void ) { + switch (priv->machine_state) { case kIOPM_OurChangeWaitForPowerSettle: OurChangeWaitForPowerSettle(); @@ -1458,6 +1452,40 @@ IOReturn IOService::makeUsable ( void ) 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 @@ -1862,9 +1890,13 @@ void IOService::PM_idle_timer_expiration ( void ) } 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); @@ -2097,7 +2129,12 @@ IOReturn IOService::powerOverrideOffPriv ( void ) // 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; @@ -2186,26 +2223,35 @@ IOReturn IOService::notifyAll ( bool is_prechange ) 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 @@ -2220,24 +2266,34 @@ IOReturn IOService::notifyAll ( bool is_prechange ) // 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 @@ -2248,6 +2304,9 @@ IOReturn IOService::notifyAll ( bool is_prechange ) // not all acked IOUnlock(priv->our_lock); + +exit: // unable to acquire_lock exit case + return IOPMWillAckLater; } @@ -2272,7 +2331,12 @@ bool IOService::notifyChild ( IOPowerConnection * theNub, bool is_prechange ) // 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; } @@ -2291,7 +2355,12 @@ bool IOService::notifyChild ( IOPowerConnection * theNub, bool is_prechange ) 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 ) @@ -2344,19 +2413,17 @@ bool IOService::inform ( IOPMinformee * nextObject, bool is_prechange ) 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; } @@ -2453,14 +2520,18 @@ void IOService::OurChangeSetPowerState ( void ) { 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() } } @@ -2640,14 +2711,18 @@ IOReturn IOService::ParentDownSetPowerState_Immediate ( void ) { 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; } @@ -2668,14 +2743,18 @@ void IOService::ParentDownSetPowerState_Delayed ( void ) { 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); } } @@ -2820,14 +2899,18 @@ void IOService::ParentUpSetPowerState_Delayed ( void ) { 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); } } @@ -2847,15 +2930,19 @@ IOReturn IOService::ParentUpSetPowerState_Immediate ( void ) { 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; } } @@ -3069,6 +3156,7 @@ void IOService::all_done ( void ) 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); @@ -3078,6 +3166,7 @@ void IOService::all_done ( void ) parent->release(); } } + if (nub) nub->release(); } } else { IOLockUnlock(priv->queue_lock); @@ -3095,7 +3184,9 @@ void IOService::all_done ( void ) // //********************************************************************************* -void IOService::all_acked ( void ) + + +void IOService::all_acked( void ) { switch (priv->machine_state) { case kIOPM_OurChangeSetPowerState: @@ -3280,7 +3371,7 @@ void IOService::ack_timer_ticked ( void ) { 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); @@ -3458,18 +3549,19 @@ IOReturn IOService::add_child_to_active_change ( IOPowerConnection * newObject ) 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(); @@ -3484,18 +3576,19 @@ IOReturn IOService::add_child_to_active_change ( IOPowerConnection * newObject ) 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(); @@ -3533,7 +3626,7 @@ IOReturn IOService::add_driver_to_active_change ( IOPMinformee * newObject ) 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 @@ -3541,11 +3634,12 @@ IOReturn IOService::add_driver_to_active_change ( IOPMinformee * newObject ) 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(); @@ -3560,18 +3654,19 @@ IOReturn IOService::add_driver_to_active_change ( IOPMinformee * newObject ) 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); @@ -3881,9 +3976,11 @@ IOReturn IOService::instruct_driver ( unsigned long newState ) 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 )