www.pudn.com > usbfx2lk_v1.1.zip > usbfx2lk_selsusp.cpp


/////////////////////////////////////////////////////////////////////////////// 
// 
//    (C) Copyright 2005 OSR Open Systems Resources, Inc. 
//    Copyright (c) 2000  Microsoft Corporation 
//    All Rights Reserved 
// 
//    This sofware is supplied for instructional purposes only. 
// 
//    OSR Open Systems Resources, Inc. (OSR) expressly disclaims any warranty 
//    for this software.  THIS SOFTWARE IS PROVIDED  "AS IS" WITHOUT WARRANTY 
//    OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, 
//    THE IMPLIED WARRANTIES OF MECHANTABILITY OR FITNESS FOR A PARTICULAR 
//    PURPOSE.  THE ENTIRE RISK ARISING FROM THE USE OF THIS SOFTWARE REMAINS 
//    WITH YOU.  OSR's entire liability and your exclusive remedy shall not 
//    exceed the price paid for this material.  In no event shall OSR or its 
//    suppliers be liable for any damages whatsoever (including, without 
//    limitation, damages for loss of business profit, business interruption, 
//    loss of business information, or any other pecuniary loss) arising out 
//    of the use or inability to use this software, even if OSR has been 
//    advised of the possibility of such damages.  Because some states/ 
//    jurisdictions do not allow the exclusion or limitation of liability for 
//    consequential or incidental damages, the above limitation may not apply 
//    to you. 
// 
//    OSR Open Systems Resources, Inc. 
//    105 Route 101A Suite 19 
//    Amherst, NH 03031  (603) 595-6500 FAX: (603) 595-6503 
//    email bugs to: bugs@osr.com 
// 
// 
//    MODULE: 
// 
//      USBFx2LK_selsusp.cpp 
// 
//    ABSTRACT: 
// 
//      This file contains the routines that handle selective suspend 
//      processing for the OSR USB FX2 Learning Kit Device 
// 
//    AUTHOR(S): 
// 
//      OSR Open Systems Resources, Inc. 
//  
/////////////////////////////////////////////////////////////////////////////// 
#include "usbfx2lk.h" 
 
#ifdef WPP_TRACING 
// 
// Include the necessary tmh file - this is  
//  just a matter of course if you're using WPP tracing. 
// 
extern "C" { 
#include "usbfx2lk_selsusp.tmh" 
} 
#endif 
 
// 
// Forward declarations 
// 
#ifndef WIN2K 
VOID SSUsbIdleCallback(PVOID Context); 
NTSTATUS SSIdleNotificationCompletionRoutine(PDEVICE_OBJECT DeviceObject, 
                                             PIRP Irp, PVOID Context); 
#endif 
 
VOID SSPowerUpCompleteUpdateSSState(PUSBFX2LK_EXT DevExt); 
VOID SSSynchronousPowerIrpCompletionFunc(PDEVICE_OBJECT DeviceObject, 
                                         UCHAR MinorFunction,  
                                         POWER_STATE PowerState, 
                                         PVOID Context, 
                                         PIO_STATUS_BLOCK IoStatus); 
VOID SSAsynchronousPowerUpIrpCompletionFunc(PDEVICE_OBJECT DeviceObject, 
                                            UCHAR MinorFunction,  
                                            POWER_STATE PowerState, 
                                            PVOID Context, 
                                            PIO_STATUS_BLOCK IoStatus); 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// EnableSelectiveSuspend 
// 
//      Enables selective suspend on the device 
// 
//  INPUTS: 
// 
//      DevExt  -  One of our device extensions 
// 
//  OUTPUTS: 
// 
//      None 
// 
//  RETURNS: 
// 
//      An appropriate NTSTATUS value 
// 
//  IRQL: 
// 
//      IRQL == PASSIVE_LEVEL 
// 
//  CONTEXT: 
// 
//      Arbitrary 
// 
//  NOTES: 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID EnableSelectiveSuspend(PUSBFX2LK_EXT DevExt) 
{ 
    KIRQL oldIrql; 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
                                ("EnableSelectiveSuspend: Entered.\n")); 
 
    // 
    // We're potentially going to change the state of the selective 
    //  suspend members of our device extension, so we must acquire 
    //  the selective suspend lock 
    // 
    KeAcquireSpinLock(&DevExt->SSLock,&oldIrql); 
 
    // 
    // ASSERT that we are not suspended or in the process of 
    //  suspending. Our logic is written such that we assume that 
    //  we are fully powered while enabling/disabling SS for the 
    //  device. This is done by the calling function calling 
    //  SSPowerDeviceIfSuspended before calling us 
    // 
    ASSERT(DevExt->SSState == SS_NOT_STARTED); 
    ASSERT(KeReadStateEvent(&DevExt->SSDeviceNotSuspendedEvent)); 
 
    // 
    // If the user doesn't ever want selective suspend, then 
    //  we can just get out of here 
    // 
    if (!DevExt->SSEnabledByUser) { 
 
        OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
            ("EnableSelectiveSuspend: SS disabled by user. Exiting\n")); 
 
        KeReleaseSpinLock(&DevExt->SSLock,oldIrql); 
        return; 
 
    } 
 
 
    // 
    // See if we've already been here before. If so, we 
    //  can just exit 
    // 
    if (DevExt->SSInitialized) { 
 
        OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
            ("EnableSelectiveSuspend: SS already initialized. Exiting\n")); 
 
        KeReleaseSpinLock(&DevExt->SSLock,oldIrql); 
        return; 
 
    } 
 
    // 
    // Default to not allowing the device to suspend right 
    //  now. Because we're called in this routine only as the  
    //  result of an I/O request (either from the User or from 
    //  the PnP manager) the I/O count is currently elevated. 
    //  When it drops to 0, we will set this to TRUE 
    // 
    DevExt->SSDeviceCanNowSuspend = FALSE; 
 
    // 
    // We're ready to selectively suspend! 
    // 
    DevExt->SSInitialized = TRUE; 
 
    KeReleaseSpinLock(&DevExt->SSLock,oldIrql); 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
                                ("EnableSelectiveSuspend: Exit.\n")); 
 
    return; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// DisableSelectiveSuspend 
// 
//      Disables selective suspend on the device 
// 
//  INPUTS: 
// 
//      DevExt  -  One of our device extensions 
// 
//  OUTPUTS: 
// 
//      None 
// 
//  RETURNS: 
// 
//      An appropriate NTSTATUS value 
// 
//  IRQL: 
// 
//      IRQL == PASSIVE_LEVEL 
// 
//  CONTEXT: 
// 
//      Arbitrary 
// 
//  NOTES: 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID DisableSelectiveSuspend(PUSBFX2LK_EXT DevExt) 
{ 
    KIRQL oldIrql; 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
                                ("DisableSelectiveSuspend: Entered.\n")); 
 
    // 
    // We're potentially going to change the state of the selective 
    //  suspend members of our device extension, so we must acquire 
    //  the selective suspend lock 
    // 
    KeAcquireSpinLock(&DevExt->SSLock,&oldIrql); 
 
    // 
    // ASSERT that we are not suspended or in the process of 
    //  suspending. Our logic is written such that we assume that 
    //  we are fully powered while enabling/disabling SS for the 
    //  device. This is done by the calling function calling 
    //  SSPowerDeviceIfSuspended before calling us 
    // 
    ASSERT(DevExt->SSState == SS_NOT_STARTED); 
    ASSERT(KeReadStateEvent(&DevExt->SSDeviceNotSuspendedEvent)); 
 
    // 
    // Note that we're called here both when the user doesn't 
    //  want SS anymore AND when SS just needs to be disabled  
    //  so that the device can stop or be removed.  
    // 
 
    // 
    // See if we've already been here before. If so, we 
    //  can just exit 
    // 
    if (!DevExt->SSInitialized) { 
 
        OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
            ("DisableSelectiveSuspend: SS not initialized. Exiting\n")); 
 
        KeReleaseSpinLock(&DevExt->SSLock,oldIrql); 
        return; 
 
    } 
 
    // 
    // The device can't suspend right now... 
    // 
    DevExt->SSDeviceCanNowSuspend = FALSE; 
 
    // 
    // And it won't suspend in the future... 
    // 
    DevExt->SSInitialized = FALSE; 
 
    KeReleaseSpinLock(&DevExt->SSLock,oldIrql); 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
                                ("DisableSelectiveSuspend: Exit.\n")); 
 
    return; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// SSPowerDeviceIfSuspended 
// 
//      This routine synchronously powers the device up if it is  
//      suspended or in the process of being suspended 
// 
//  INPUTS: 
// 
//      DevExt  -  One of our device extensions 
// 
//  OUTPUTS: 
// 
//      None 
// 
//  RETURNS: 
// 
//      An appropriate NTSTATUS value 
// 
//  IRQL: 
// 
//      IRQL == PASSIVE_LEVEL 
// 
//  CONTEXT: 
// 
//      Arbitrary 
// 
//  NOTES: 
// 
//      This routine synchronously powers the device and therefore 
//      MUST be called at PASSIVE_LEVEL.  
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID SSPowerDeviceIfSuspended(PUSBFX2LK_EXT DevExt)  
{ 
 
    PAGED_CODE(); 
     
    // 
    // See if the device is currently suspended. If it  
    //  is, this routine will attempt to asynchronously power  
    //  the device back up and will return TRUE. In that 
    //  case, we want to synchronously wait for the power up  
    //  to occur. 
    // 
    // However, if this routine returns FALSE the device is  
    //  already powered up and we can simply return 
    // 
    if (SSPowerDeviceIfSuspendedAsync(DevExt)) { 
 
        // 
        // Wait for the suspend to be over 
        // 
        (VOID)OsrWaitForSingleObject(&DevExt->SSDeviceNotSuspendedEvent); 
    } 
 
    return; 
 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// SSPowerDeviceIfSuspendedAsync 
// 
//      This routine checks to see if the device is suspended or in the  
//      process of being suspsended and takes the appropriate action 
//      to get the device powered again.  
// 
//  INPUTS: 
// 
//      DevExt  -  One of our device extensions 
// 
//  OUTPUTS: 
// 
//      None 
// 
//  RETURNS: 
// 
//      TRUE if the device is suspended, FALSE if the device is  
//      fully powered 
// 
//  IRQL: 
// 
//      IRQL == PASSIVE_LEVEL 
// 
//  CONTEXT: 
// 
//      Arbitrary 
// 
//  NOTES: 
// 
// 
/////////////////////////////////////////////////////////////////////////////// 
BOOLEAN SSPowerDeviceIfSuspendedAsync(PUSBFX2LK_EXT DevExt)  
{ 
    BOOLEAN queuePowerUp = FALSE; 
    BOOLEAN powerUpQueued = FALSE; 
    KIRQL oldIrql; 
#ifndef W2K 
    PIRP idleIrp = NULL; 
#endif 
 
    PAGED_CODE(); 
 
    // 
    // We'll need the SS lock for this  
    // 
    KeAcquireSpinLock(&DevExt->SSLock,&oldIrql); 
 
    // 
    // Only if SS is setup to we need to do any  
    //  checks.  
    // 
    if (DevExt->SSInitialized) { 
 
        // 
        // Indicate that we do not want the device to suspend 
        //  because we're about to send a request to it. 
        // 
        DevExt->SSDeviceCanNowSuspend = FALSE; 
 
        // 
        // Check the state.  
        // 
        switch (DevExt->SSState) { 
 
            case SS_SUSPENDED: { 
 
                DevExt->SSState = SS_PROCESSING; 
 
                // 
                // We'll be powering up... 
                // 
                queuePowerUp = TRUE; 
                break; 
            } 
 
            case SS_PROCESSING: { 
 
                powerUpQueued = TRUE; 
 
#ifndef W2K 
 
                // 
                // On XP and later, we need to submit and idle 
                //  notification IRP to the bus driver and wait 
                //  to be called back in order to suspend. If we're 
                //  on a hub with a device that is not suspending, 
                //  this request may still be pending. If so, we 
                //  need to cancel it before we can do anything 
                // 
 
                // 
                // Grab the idle IRP out of the extension 
                // 
                idleIrp = DevExt->SSIdleIrp; 
 
                // 
                // And set it to NULL. This is the clue to the 
                //  completion routine that we will be freeing 
                //  the IRP 
                // 
                DevExt->SSIdleIrp = NULL; 
 
#endif 
                break; 
 
            } 
            case SS_NOT_STARTED: { 
 
                // 
                // Nothing to do here, we're powered up and active 
                //  already 
                // 
                break; 
            } 
            default: { 
 
                // 
                // Shouldn't be here... 
                // 
                ASSERT(FALSE); 
                break; 
 
            } 
 
        } 
 
    } else { 
 
        // 
        // If SS is not initialized, we should NEVER 
        //  be called here and not at D0. If so, then 
        //  the caller either didn't check the PnP state 
        //  before calling here or the PnP state machine 
        //  is broken. 
        // 
        ASSERT(DevExt->DevicePowerState == PowerDeviceD0); 
 
    } 
 
    KeReleaseSpinLock(&DevExt->SSLock,oldIrql); 
 
#ifndef W2K 
 
    // 
    // If we got an idle IRP, we're going to need to  
    //  cancel it 
    // 
    if (idleIrp) { 
 
        // 
        // Cancel the IRP 
        // 
        IoCancelIrp(idleIrp); 
 
        // 
        // Wait for the IRP to finish 
        // 
        (VOID)OsrWaitForSingleObject(&DevExt->SSIdleCompletionRoutineCalled); 
 
        // 
        // We got the IRP, so we free it.  
        // 
        IoFreeIrp(idleIrp); 
 
    } 
 
#endif 
 
    if (queuePowerUp) { 
 
        // 
        // We need to power the device ourselves 
        // 
        SSPowerUpDeviceAsync(DevExt); 
 
    } 
 
    // 
    // If we queued the power up IRP or there 
    //  was already a power up queued, indicate 
    //  that the device is suspended by returning 
    //  TRUE 
    // 
    return (queuePowerUp || powerUpQueued); 
 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// SSSubmissionThreadRoutine 
// 
//      This thread runs while the device is attached to the system. Its goal 
//      in life is to wake up when the device no longer has any active I/O 
//      and attempt to suspend it 
// 
//  INPUTS: 
// 
//      Context  -  One of our device extensions 
// 
//  OUTPUTS: 
// 
//      None 
// 
//  RETURNS: 
// 
//      None 
// 
//  IRQL: 
// 
//      IRQL == PASSIVE_LEVEL 
// 
//  CONTEXT: 
// 
//      System context 
// 
//  NOTES: 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID SSSubmissionThreadRoutine(PVOID Context)  
{ 
    PUSBFX2LK_EXT devExt = (PUSBFX2LK_EXT)Context; 
    NTSTATUS status; 
    PVOID waitObjects[2]; 
    LARGE_INTEGER delay; 
    LARGE_INTEGER currentTime; 
    LARGE_INTEGER timeSinceIdle; 
    KIRQL oldIrql; 
    BOOLEAN powerDeviceBackUp; 
    BOOLEAN restartQueues; 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
                            ("SSSubmissionThreadRoutine: Entered.\n")); 
 
    // 
    // Define an arbitrary thread delay that we'll add to 
    //  our loop.  
    // 
    delay.QuadPart = RELATIVE(SECONDS(1)); 
 
     
 
    // 
    // Wait on the STOP event, which is signaled when the oustanding 
    //  I/O count on the device drops to 1 and also the   
    //  thread termination event,  
    // 
    waitObjects[0] = &devExt->StopEvent; 
    waitObjects[1] = &devExt->SSSubmissionThreadTerminateEvent; 
 
    // 
    // Loop forever... 
    // 
    while (TRUE) { 
 
        // 
        // Wait on our above two events.  
        //   
        // We don't use OsrWaitForMultipleObjects here 
        //  because we expect to be just sit here for a  
        //  loooong time and the otherwise helpful debug output  
        //  produced by OsrWaitForMultipleObjects just becomes  
        //  noise in this case 
        // 
        status = KeWaitForMultipleObjects(2, 
                                          waitObjects, 
                                          WaitAny, 
                                          Executive, 
                                          KernelMode, 
                                          FALSE, 
                                          NULL, 
                                          NULL); 
 
        // 
        // We expect a wait status indicating that either  
        //  the stop event was fired OR that the  
        //  terminate thread event was fired. Anything else is an 
        //  error. 
        // 
        if (status != STATUS_WAIT_0) { 
 
            OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_SELECTIVE_SUSPEND, 
               ("EnableSelectiveSuspend: KeWaitForMultipleObjects returned "\ 
                                                    "0x%x (%s). Exiting...\n", 
                status,OsrNtStatusToString(status))); 
 
            PsTerminateSystemThread(status); 
            return; 
 
        } 
 
        // 
        // Default to not needing to power the device up 
        //  or restarting the queues, let the logic below  
        //  change this as necessary. 
        // 
        powerDeviceBackUp = FALSE; 
        restartQueues = FALSE; 
 
        // 
        // Grab the current time. We'll use this to determine if 
        //  the device has been idle long enough. 
        // 
        KeQuerySystemTime(¤tTime); 
 
 
        // 
        // We need to try to suspend the device 
        // 
        KeAcquireSpinLock(&devExt->SSLock,&oldIrql); 
 
        // 
        // If the device can suspend right now AND  
        //  we haven't been suspended, we'll do the power 
        //  down. 
        // 
        // Not that while the device is suspended, the stop 
        //  event stays fired so we'll keep coming back here 
        //  every "delay" seconds and check it. This isn't an 
        //  optimal solution, as it requires polling even if  
        //  the device is already idle or SS is disabled by the 
        //  user, but it does provide for a fairly straightforward 
        //  SS implementation. SS is very difficult to get correct, 
        //  so we have chosen to not attempt to optimize and provide 
        //  something that is safe instead. Whether or not this is  
        //  acceptable to your application is a design decision 
        //  that you will have to make. 
        // 
        if (devExt->SSDeviceCanNowSuspend &&  
            devExt->SSState == SS_NOT_STARTED) {  
 
 
            // 
            // Calculate how long it has been since the  
            //  device became idle 
            // 
            timeSinceIdle.QuadPart =  
                  currentTime.QuadPart -  
                            devExt->SSTimeOfLastActivity.QuadPart; 
 
 
            // 
            // Is the difference sufficient to constitute an idle 
            //  device? 
            // 
            if (timeSinceIdle.QuadPart >= SS_DEVICE_IDLE_DELAY) { 
             
                // 
                // We're about to power down the device, set the state 
                //  to SS_PROCESSING 
                // 
                devExt->SSState = SS_PROCESSING; 
 
                // 
                // Clear the "device is out of selective suspend" event 
                // 
                KeClearEvent(&devExt->SSDeviceNotSuspendedEvent); 
 
                // 
                // We don't need this again until the device 
                //  has suspended. The fact that we have  
                //  set the state to SS_PROCESSING will 
                //  cause any arriving operations that need the 
                //  device fully powered to queue (we'll deal 
                //  with getting them started again once we've processed  
                //  the power down). 
                // 
                // Note that we're being overly cautious and a bit 
                //  obtuse with our selective suspend algorithm here. 
                //  If an operation comes in at this point we don't  
                //  try to cancel the in progress power down. Instead, 
                //  we wait for the device to power all the way down  
                //  and then we'll power it back up again before restarting 
                //  the queues. This is a safe approach (as it eliminates 
                //  many race conditions) but it may not be ideal for  
                //  your environment. 
                // 
                KeReleaseSpinLock(&devExt->SSLock,oldIrql); 
 
                // 
                // Synchronously suspend the device. 
                // 
#ifndef W2K 
                // 
                // If we're not built for Windows 2000 compatibility, 
                //  we can't just power the device down, we need to let 
                //  the USB hus driver know about it first. In this case 
                //  we'll call SSSuspendDeviceWithCallback which will 
                //  send the appropriate IRP and power down from within 
                //  the callback 
                //  
                SSSuspendDeviceWithCallback(devExt); 
#else 
                // 
                // Otherwise, there is no concept of notifiying the bus 
                //  driver first. So, just power the device down inline 
                // 
                SSSuspendDevice(devExt); 
#endif 
 
                // 
                // Acquire our lock again and see if the state of the  
                //  world has changed. If not, then we'll set our 
                //  SS state to be SUSPENDED. If it HAS changed, then 
                //  we need to power up the device again 
                // 
                KeAcquireSpinLock(&devExt->SSLock,&oldIrql); 
 
                // 
                // Make sure that the power down actually succeeded.  
                //  If not, then there's nothing to be done 
                // 
                if (devExt->DevicePowerState != devExt->PowerDownLevel) { 
                     
                    OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_SELECTIVE_SUSPEND, 
                        ("SSSubmissionThreadRoutine: Power down of device did"\ 
                                  " not succeed. Device power state is 0x%x\n", 
                        devExt->DevicePowerState)); 
 
                    devExt->SSState = SS_NOT_STARTED; 
 
                    // 
                    // Set the "device is now out of selective suspend" event 
                    // 
                    KeSetEvent(&devExt->SSDeviceNotSuspendedEvent,  
                               EVENT_INCREMENT, 
                               FALSE); 
 
                    // 
                    // Restart the queues that were stalled  
                    //  as part of the powering down 
                    // 
                    restartQueues = TRUE; 
 
                } else { 
 
                    // 
                    // Is everyone still OK with us suspending the device? 
                    // 
                    if (devExt->SSDeviceCanNowSuspend == TRUE) { 
 
                        // 
                        // Yup, no one wants the device just yet sp 
                        //  the Device is now suspended! 
                        // 
                        devExt->SSState = SS_SUSPENDED; 
 
                    } else { 
 
                        // 
                        // Whoops. While we were powering down, a request 
                        //  came in that required access to the device. 
                        //  Power back up again once we've dropped the lock 
                        // 
                        powerDeviceBackUp = TRUE; 
 
                    } 
 
                } 
 
                // 
                // Lock acquisition above isn't leaked, it's released by 
                //  the upcoming KeRelease outside of the original IF 
                // 
 
            } 
 
        } 
 
        KeReleaseSpinLock(&devExt->SSLock,oldIrql); 
 
        if (powerDeviceBackUp) { 
 
            // 
            // While we were powering the device down,  
            //  a request came in that needed the device 
            //  to be powered. Power the device back up 
            //  here.  
            // 
             
            // 
            // We never leave the "selective suspend 
            //  processing" state in this case, which  
            //  allows us to queue all of those operations 
            //  that requested the power up. 
            // 
            // The power up code deals with getting the  
            //  queues restarted by calling OsrProcessQueuedRequests 
            // 
            ASSERT(devExt->SSState == SS_PROCESSING); 
 
            SSPowerUpDevice(devExt); 
 
        } 
 
        if (restartQueues) { 
 
            // 
            // The suspend failed for whatever reason and the 
            //  queues need to be restarted 
            // 
            OsrProcessQueuedRequests(devExt); 
 
        } 
 
 
        // 
        // Put a bit of a delay in. Because the stop 
        //  event is a notification event, we'll keep 
        //  hitting this loop while suspended so  
        //  the delay just leaves some breathing room. 
        // 
        KeDelayExecutionThread(KernelMode, 
                               FALSE, 
                               &delay); 
 
    } 
 
    return; 
 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// SSPowerUpDevice 
// 
//      This routine synchronously powers up the device 
// 
//  INPUTS: 
// 
//      DevExt  -  One of our device extensions 
// 
//  OUTPUTS: 
// 
//      None 
// 
//  RETURNS: 
// 
//      None 
// 
//  IRQL: 
// 
//      IRQL == PASSIVE_LEVEL 
// 
//  CONTEXT: 
// 
//      Arbitrary 
// 
//  NOTES: 
// 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID SSPowerUpDevice(PUSBFX2LK_EXT DevExt)  
{ 
 
    POWER_STATE devicePowerState; 
    KEVENT powerUpCompleteEvent; 
    NTSTATUS status; 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
                                ("SSPowerUpDevice: Entered\n")); 
 
    // 
    // We should not already be in D0 at this point 
    // 
    ASSERT(DevExt->DevicePowerState != PowerDeviceD0); 
 
    // 
    // Submit a device power IRP to our device 
    // 
 
    // 
    // This constitutes a new I/O on the device,  
    //  so bump the oustanding I/O count 
    // 
    OsrIncrementOutstandingIoCount(DevExt,__FILE__,__LINE__); 
 
    // 
    // Setup the power up state to be D0 
    // 
    devicePowerState.DeviceState = PowerDeviceD0; 
 
    // 
    // Initialize the power up event that our power completion 
    //  routine will signal when the power up is complete 
    // 
    KeInitializeEvent(&powerUpCompleteEvent, NotificationEvent, FALSE); 
 
    // 
    // And ask the power manager to create and send a power up D-IRP  
    //  to our PDO. Specify the synchronous power completion routine, 
    //  which will simply set the event that we pass to it when it 
    //  runs 
    // 
    status = PoRequestPowerIrp(DevExt->PhysicalDeviceObject,  
                               IRP_MN_SET_POWER,  
                               devicePowerState,  
                               SSSynchronousPowerIrpCompletionFunc, 
                               &powerUpCompleteEvent,  
                               NULL); 
 
    // 
    // We want to do this operation synchronously, so just wait 
    //  for the operation to complete 
    // 
    if (status == STATUS_PENDING) { 
 
        OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_SELECTIVE_SUSPEND, 
                ("SSPowerUpDevice: Waiting for the power irp to complete\n")); 
 
        OsrWaitForSingleObject(&powerUpCompleteEvent); 
    } 
 
    // 
    // The I/O is finished. 
    // 
    OsrDecrementOutstandingIoCount(DevExt,__FILE__,__LINE__); 
 
    OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_SELECTIVE_SUSPEND, 
        ("SSPowerUpDevice: PoRequestPowerIrp returned 0x%x (%s).\n", 
                status,OsrNtStatusToString(status))); 
 
    // 
    // A power up is complete, get the Selective Suspend 
    //  state properly updated 
    // 
    SSPowerUpCompleteUpdateSSState(DevExt); 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
                                    ("SSPowerUpDevice: Exited\n")); 
     
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// SSPowerUpCompleteUpdateSSState 
// 
//      This routine updates the Selective Suspend state of the device  
//      after a power up operation is complete 
// 
//  INPUTS: 
// 
//      DevExt  -  One of our device extensions 
// 
//  OUTPUTS: 
// 
//      None 
// 
//  RETURNS: 
// 
//      None 
// 
//  IRQL: 
// 
//      IRQL <= DISPATCH_LEVEL 
// 
//  CONTEXT: 
// 
//      Arbitrary 
// 
//  NOTES: 
// 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID SSPowerUpCompleteUpdateSSState(PUSBFX2LK_EXT DevExt)  
{ 
    KIRQL oldIrql; 
 
    // 
    // If someone has called here then we've just finished  
    //  a power up so we need to update the SS state 
    //  based on whether or not the power up actually 
    //  happened 
    // 
    KeAcquireSpinLock(&DevExt->SSLock,&oldIrql); 
     
    if (DevExt->DevicePowerState != PowerDeviceD0) { 
 
        OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_SELECTIVE_SUSPEND, 
           ("SSPowerUpDevice: Power up of device did not succeed. Device "\ 
                                                    "power state is 0x%x\n", 
           DevExt->DevicePowerState)); 
 
        // 
        // Indicate that we're suspended. 
        // 
        DevExt->SSState = SS_SUSPENDED; 
 
    } else { 
 
        // 
        // Device is now powered up! 
        // 
        DevExt->SSState = SS_NOT_STARTED; 
 
    } 
 
    // 
    // Set the "device is now out of selective suspend" event 
    // 
    KeSetEvent(&DevExt->SSDeviceNotSuspendedEvent,  
               EVENT_INCREMENT, 
               FALSE); 
 
    KeReleaseSpinLock(&DevExt->SSLock,oldIrql); 
 
    // 
    // Restart any requests that may have been queued 
    //  while the device was powering down. 
    // 
    OsrProcessQueuedRequests(DevExt); 
 
} 
 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// SSPowerUpDeviceAsync 
// 
//      This routine asynchronously powers up the device 
// 
//  INPUTS: 
// 
//      DevExt  -  One of our device extensions 
// 
//  OUTPUTS: 
// 
//      None 
// 
//  RETURNS: 
// 
//      None 
// 
//  IRQL: 
// 
//      IRQL == PASSIVE_LEVEL 
// 
//  CONTEXT: 
// 
//      Arbitrary 
// 
//  NOTES: 
// 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID SSPowerUpDeviceAsync(PUSBFX2LK_EXT DevExt)  
{ 
 
    POWER_STATE devicePowerState; 
    NTSTATUS status; 
    KIRQL oldIrql; 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
                                ("SSPowerUpDeviceAsync: Entered\n")); 
 
    // 
    // We should not already be in D0 at this point 
    // 
    ASSERT(DevExt->DevicePowerState != PowerDeviceD0); 
 
    // 
    // Submit a device power IRP to our device 
    // 
 
    // 
    // This constitutes a new I/O on the device,  
    //  so bump the oustanding I/O count 
    // 
    OsrIncrementOutstandingIoCount(DevExt,__FILE__,__LINE__); 
 
    // 
    // Setup the power up state to be D0 
    // 
    devicePowerState.DeviceState = PowerDeviceD0; 
 
    // 
    // And ask the power manager to create and send a power up D-IRP  
    //  to our PDO. Specify the asynchronous power completion routine 
    //  which will deal with getting the SS state updated when the  
    //  IRP is completed. 
    // 
    status = PoRequestPowerIrp(DevExt->PhysicalDeviceObject,  
                               IRP_MN_SET_POWER,  
                               devicePowerState,  
                               SSAsynchronousPowerUpIrpCompletionFunc, 
                               DevExt,  
                               NULL); 
 
    OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_SELECTIVE_SUSPEND, 
        ("SSPowerUpDeviceAsync: PoRequestPowerIrp returned 0x%x (%s).\n", 
                status,OsrNtStatusToString(status))); 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
                                    ("SSPowerUpDeviceAsync: Exited\n")); 
     
} 
 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// SSSuspendDevice 
// 
//      This routine synchronously suspends the device 
// 
//  INPUTS: 
// 
//      DevExt  -  One of our device extensions 
// 
//  OUTPUTS: 
// 
//      None 
// 
//  RETURNS: 
// 
//      None 
// 
//  IRQL: 
// 
//      IRQL == PASSIVE_LEVEL 
// 
//  CONTEXT: 
// 
//      Arbitrary 
// 
//  NOTES: 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID SSSuspendDevice(PUSBFX2LK_EXT DevExt)  
{ 
 
    POWER_STATE devicePowerState; 
    KEVENT powerDownCompleteEvent; 
    NTSTATUS status; 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
                                    ("SSSuspendDevice: Entered\n")); 
 
    // 
    // If we haven't figured out the power state 
    //  that we're going to suspend to then we can't 
    //  suspend. This may happen while the device is first starting 
    //  up. 
    // 
    // Also, if we're already powered down there's no point in  
    //  trying to power down again.  
    // 
    if (DevExt->PowerDownLevel == PowerDeviceUnspecified || 
        DevExt->DevicePowerState != PowerDeviceD0) { 
 
        OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_SELECTIVE_SUSPEND, 
               ("SSSuspendDevice: PowerDownLevel not set or already "\ 
                "suspended, not suspending\n")); 
        return; 
 
    } 
 
    // 
    // Submit a device power IRP to our device 
    // 
 
    // 
    // This constitutes a new I/O on the device,  
    //  so bump the oustanding I/O count 
    // 
    OsrIncrementOutstandingIoCount(DevExt,__FILE__,__LINE__); 
 
    // 
    // Setup the power down state to be the state  
    //  that we determined during the QUERY_CAPABILITIES 
    //  processing 
    // 
    devicePowerState.DeviceState = (DEVICE_POWER_STATE)DevExt->PowerDownLevel; 
 
    // 
    // Initialize the power down event that our power completion 
    //  routine will signal when the power down is complete 
    // 
    KeInitializeEvent(&powerDownCompleteEvent, NotificationEvent, FALSE); 
 
    // 
    // And ask the power manager to create and send a power down D-IRP  
    //  to our PDO. We always power the device down synchronously, so 
    //  specify the synchronous power completion routine and pass 
    //  an event to be set. 
    // 
    status = PoRequestPowerIrp(DevExt->PhysicalDeviceObject,  
                               IRP_MN_SET_POWER,  
                               devicePowerState,  
                               SSSynchronousPowerIrpCompletionFunc, 
                               &powerDownCompleteEvent,  
                               NULL); 
 
    // 
    // We want to do this operation synchronously, so just wait 
    //  for the operation to complete 
    // 
    if (status == STATUS_PENDING) { 
 
        OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_SELECTIVE_SUSPEND, 
                ("SSSuspendDevice: Waiting for the power irp to complete\n")); 
 
        OsrWaitForSingleObject(&powerDownCompleteEvent); 
 
    } 
 
 
    // 
    // The I/O is finished. 
    // 
    OsrDecrementOutstandingIoCount(DevExt,__FILE__,__LINE__); 
 
    OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_SELECTIVE_SUSPEND, 
        ("SSSuspendDevice: PoRequestPowerIrp returned 0x%x (%s). Exiting...\n", 
                status,OsrNtStatusToString(status))); 
 
    // 
    // Unlike the power UP case, we don't try to determine if the power 
    //  down actually happened and update the SS state. This is because 
    //  in the power up case there's no concept of, "hey, I didn't want that 
    //  power up!" as there is in the power down case. So, it is the caller's 
    //  responsiblity to determine that if at any point during the power down 
    //  the power down request was cancelled and that the device actually 
    //  needs to be powered up again. Basically, more decisions need to be 
    //  made before we can get out of the SS_PROCESSING state so we just  
    //  return to the caller and let him deal with it. 
    // 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
                                    ("SSSuspendDevice: Exited\n")); 
     
} 
 
 
 
#ifndef W2K 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// SSSuspendDeviceWithCallback 
// 
//      This routine requests an idle notification callback from the bus driver 
//      and uses it to synchronously power down the device  
// 
//  INPUTS: 
// 
//      DevExt  -  One of our device extensions 
// 
//  OUTPUTS: 
// 
//      None 
// 
//  RETURNS: 
// 
//      None 
// 
//  IRQL: 
// 
//      IRQL == PASSIVE_LEVEL 
// 
//  CONTEXT: 
// 
//      Arbitrary 
// 
//  NOTES: 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID SSSuspendDeviceWithCallback(PUSBFX2LK_EXT DevExt)  
{ 
     
    PIRP idleCallbackIrp; 
    PIO_STACK_LOCATION nextStack; 
    NTSTATUS status; 
    KIRQL oldIrql; 
    PVOID waitObjects[2]; 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
                        ("SSSuspendDeviceWithCallback: Entered\n")); 
     
    // 
    // We're built for XP and later, so we need to  
    //  submit an idle callback request and wait 
    //  for the callback to be called by the bus 
    //  driver before powering down the device 
    // 
 
    // 
    // There's one REALLY annoying side effect of us  
    //  having to go through this path: 
    // 
    // ** There's no guarantee that the IRP won't complete 
    // **  in error before, DURING, or after the idle  
    // **  callback running. Also, in the successful 
    // **  case, the IRP's completion routine doesn't 
    // **  run until the device is powered back up again 
    // 
    // So, the idle callback may or may not ever run and 
    //  the IRP's completion routine may run now or  
    //  48 hours in the future. However, we still want 
    //  to supply seemingly synchronous power down behaviour 
    //  even when we require the use of the idle callback  
    //  (again, SS is hard enough so we're not going to  
    //  optimize). 
    // 
    // We'll use 2 events to do this. One that will be fired  
    //  within the callback (which, in the normal case, will  
    //  run within a relatively short time) and one event for 
    //  the running of the IRP's completion routine.  
    // 
    // If the event from the callback routine gets fired,  
    //  then the device is powered down and we're set. If the 
    //  event from the completion routine gets fired, then the 
    //  idle IRP was aborted by the bus driver for some reason. 
    // 
 
    // 
    // Set the events to a known state 
    // 
    KeClearEvent(&DevExt->SSIdleCallbackCalled); 
    KeClearEvent(&DevExt->SSIdleCompletionRoutineCalled); 
     
 
    // 
    // Fill in the idle callback structure that we need 
    //  to pass along with the IRP 
    // 
    DevExt->SSIdleCallbackInfo.IdleCallback = SSUsbIdleCallback; 
    DevExt->SSIdleCallbackInfo.IdleContext  = DevExt; 
 
    // 
    // This constitutes a new I/O on the device,  
    //  so bump the oustanding I/O count 
    // 
    OsrIncrementOutstandingIoCount(DevExt,__FILE__,__LINE__); 
 
 
    // 
    // And allocate an IRP of the appropriate size 
    // 
    idleCallbackIrp = IoAllocateIrp(DevExt->DeviceToSendIrpsTo->StackSize, 
                                    FALSE); 
 
    if (!idleCallbackIrp) { 
 
        OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_SELECTIVE_SUSPEND, 
         ("SSSuspendDeviceWithCallback: IoAllocateIrp returned NULL. Exiting...\n")); 
        return; 
 
    } 
 
    // 
    // Get the next stack location for the IRP and  
    //  set it up to be an idle notification request 
    // 
    nextStack = IoGetNextIrpStackLocation(idleCallbackIrp); 
 
    nextStack->MajorFunction =  
                    IRP_MJ_INTERNAL_DEVICE_CONTROL; 
 
    nextStack->Parameters.DeviceIoControl.IoControlCode =  
                    IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION; 
 
    nextStack->Parameters.DeviceIoControl.Type3InputBuffer = 
                    &DevExt->SSIdleCallbackInfo; 
 
    nextStack->Parameters.DeviceIoControl.InputBufferLength = 
                    sizeof(USB_IDLE_CALLBACK_INFO); 
 
    // 
    // Setup our completion routine that will free the memory 
    //  for this IRP 
    // 
    IoSetCompletionRoutine(idleCallbackIrp,  
                           SSIdleNotificationCompletionRoutine, 
                           DevExt,  
                           TRUE,  
                           TRUE,  
                           TRUE); 
 
    OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_SELECTIVE_SUSPEND, 
            ("SSSuspendDeviceWithCallback: Submitting idle IRP 0x%p.\n",  
                idleCallbackIrp)); 
 
    // 
    // Set this IRP up in our device extension. We need to 
    //  keep track of this IRP because it will pend 
    //  forever if we are on a hub with a device that  
    //  isn't suspending. 
    // 
    KeAcquireSpinLock(&DevExt->SSLock,&oldIrql); 
     
    // 
    // Shouldn't already have a request pending 
    // 
    ASSERT(DevExt->SSIdleIrp == NULL); 
 
    // 
    // There's another race condition that we have to contend 
    //  with in this case. SSPowerDeviceIfSuspended may have  
    //  already executed and looked to see if there is  
    //  an Idle IRP pending. Because we haven't set the 
    //  field yet, it will think that the device is just 
    //  in the middle of powering up and will simply  
    //  wait for the power up event. That means that we  
    //  could sit here waiting for the idle notification 
    //  callback to occur (which may not EVER happen) while 
    //  the rest of our code is waiting for a device power 
    //  up to happen, leading to a livelock in the driver.  
    // 
    // We'll cover this race condition by adding an extra 
    //  check here to make sure that the device hasn't  
    //  become busy while we were building the idle IRP.  
    //  If it has, we'll just get out of here... 
    // 
    if (DevExt->SSDeviceCanNowSuspend == FALSE) { 
 
        OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_SELECTIVE_SUSPEND, 
            ("SSSuspendDeviceWithCallback: Device became busy while building "\ 
                    "IDLE IRP. Exiting.\n")); 
 
        // 
        // Free the IRP we built  
        // 
        IoFreeIrp(idleCallbackIrp); 
 
        KeReleaseSpinLock(&DevExt->SSLock,oldIrql); 
 
        // 
        // The I/O is finished. 
        // 
        OsrDecrementOutstandingIoCount(DevExt,__FILE__,__LINE__); 
 
        return; 
 
    } 
 
    //  
    // Setup a pointer to the idle IRP in the  
    //  device extension 
    // 
    DevExt->SSIdleIrp = idleCallbackIrp; 
 
    KeReleaseSpinLock(&DevExt->SSLock,oldIrql); 
 
    // 
    // And submit the IRP down to the lower driver.  
    // 
    status = IoCallDriver(DevExt->DeviceToSendIrpsTo, 
                          idleCallbackIrp); 
 
    if (NT_SUCCESS(status)) { 
 
        // 
        // If we got a successful status, wait for the  
        //  idle callback to fire and the device to  
        //  power down OR for the IRP's completion routine  
        //  to be called because there was some sort of error 
        //  that occurred before the device could idle 
        // 
        OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_SELECTIVE_SUSPEND, 
            ("SSSuspendDeviceWithCallback: Waiting for suspend idle callback "\ 
                            "or completion routine to fire...\n")); 
 
        waitObjects[0] = &DevExt->SSIdleCallbackCalled; 
        waitObjects[1] = &DevExt->SSIdleCompletionRoutineCalled; 
 
        // 
        // Wait on our above two events.  
        // 
        status = OsrWaitForMultipleObjects(2, 
                                           waitObjects, 
                                           WaitAny, 
                                           NULL); 
 
        if (status == STATUS_WAIT_0) { 
 
            OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_SELECTIVE_SUSPEND, 
                ("SSSuspendDeviceWithCallback: Idle callback fired!\n")); 
 
        } else  if (status == STATUS_WAIT_1) { 
 
            OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_SELECTIVE_SUSPEND, 
                ("SSSuspendDeviceWithCallback: Completion routine fired!\n")); 
 
        } else { 
 
            OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_SELECTIVE_SUSPEND, 
                ("SSSuspendDeviceWithCallback: KeWaitForMultipleObjects "\ 
                                    "returned unknown status 0x%x (%s).\n", 
                status,OsrNtStatusToString(status))); 
 
        } 
 
 
    } else { 
 
        // 
        // The IRP was failed before the idle callback was  
        //  queued. Not much we can do, the completion routine 
        //  will free the IRP 
        // 
        OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_SELECTIVE_SUSPEND, 
            ("SSSuspendDeviceWithCallback: Idle request failed, bus returned "\ 
                                                     "0x%x (%s). Exiting...\n", 
                    status,OsrNtStatusToString(status))); 
 
 
    } 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
                                ("SSSuspendDeviceWithCallback: Exited\n")); 
 
} 
 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// SSUsbIdleCallback 
// 
//      This routine is the USB idle callback that does the actually 
//      suspending of the device when built for XP and later 
// 
//  INPUTS: 
// 
//      Context  -  One of our device extensions 
// 
//  OUTPUTS: 
// 
//      None 
// 
//  RETURNS: 
// 
//      None 
// 
//  IRQL: 
// 
//      IRQL == PASSIVE_LEVEL 
// 
//  CONTEXT: 
// 
//      Arbitrary 
// 
//  NOTES: 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID SSUsbIdleCallback(PVOID Context) 
{ 
    PUSBFX2LK_EXT devExt = (PUSBFX2LK_EXT)Context; 
         
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
            ("SSUsbIdleCallback: Entered\n")); 
 
 
    // 
    // And now power the device down 
    //  
    SSSuspendDevice(devExt); 
 
    KeSetEvent(&devExt->SSIdleCallbackCalled,  
               EVENT_INCREMENT, 
               FALSE); 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
            ("SSUsbIdleCallback: Exited\n")); 
 
 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// SSIdleNotificationCompletionRoutine 
// 
//      This routine is the completion routine for the IRP used to submit the 
//      USB idle callback 
// 
//  INPUTS: 
// 
//      DeviceObject - We allocated the IRP, so this is NULL 
// 
//      Irp          - The idle notification IRP 
// 
//      Context      -  One of our device extensions 
// 
//  OUTPUTS: 
// 
//      None 
// 
//  RETURNS: 
// 
//      STATUS_MORE_PROCESSING_REQUIRED 
// 
//  IRQL: 
// 
//      IRQL <= DISPATCH_LEVEL 
// 
//  CONTEXT: 
// 
//      Arbitrary 
// 
//  NOTES: 
// 
/////////////////////////////////////////////////////////////////////////////// 
NTSTATUS SSIdleNotificationCompletionRoutine(PDEVICE_OBJECT DeviceObject, 
                                             PIRP Irp, PVOID Context) 
{ 
    PUSBFX2LK_EXT devExt = (PUSBFX2LK_EXT)Context; 
    KIRQL oldIrql; 
    PIRP idleIrp; 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
            ("SSIdleNotificationCompletionRoutine: Entered\n")); 
 
    KeAcquireSpinLock(&devExt->SSLock,&oldIrql); 
     
    // 
    // Let's see if someone else didn't get to this 
    //  first 
    // 
    idleIrp = devExt->SSIdleIrp; 
 
    devExt->SSIdleIrp = NULL; 
 
    KeReleaseSpinLock(&devExt->SSLock,oldIrql); 
 
    if (idleIrp) { 
 
        // 
        // If we got the IRP, we free it. Otherwise let the 
        //  person who cancelled the IRP free it.  
        // 
        IoFreeIrp(idleIrp); 
 
    } 
 
    KeSetEvent(&devExt->SSIdleCompletionRoutineCalled,  
               EVENT_INCREMENT, 
               FALSE); 
 
 
    // 
    // The I/O is finished. 
    // 
    OsrDecrementOutstandingIoCount(devExt,__FILE__,__LINE__); 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
            ("SSIdleNotificationCompletionRoutine: Exited\n")); 
 
    return STATUS_MORE_PROCESSING_REQUIRED; 
 
} 
 
 
#endif 
 
 
 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// SSSynchronousPowerIrpCompletionFunc 
// 
//      Generic SYNCHRONOUS selective suspend D-IRP power completion routine 
// 
//  INPUTS: 
// 
//      DeviceObject  - Not used 
//       
//      MinorFunction - Not used 
// 
//      PowerState    - Not used 
// 
//      Context       - An event to signal the completion of the D-IRP 
// 
//      IoStatus      - Not used 
// 
//  OUTPUTS: 
// 
//      None 
// 
//  RETURNS: 
// 
//      None 
// 
//  IRQL: 
// 
//      IRQL <= DISPATCH_LEVEL 
// 
//  CONTEXT: 
// 
//      Arbitrary 
// 
//  NOTES: 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID SSSynchronousPowerIrpCompletionFunc(PDEVICE_OBJECT DeviceObject, 
                                         UCHAR MinorFunction,  
                                         POWER_STATE PowerState, 
                                         PVOID Context, 
                                         PIO_STATUS_BLOCK IoStatus) 
{ 
 
    PKEVENT completeEvent = (PKEVENT)Context; 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
            ("SSSynchronousPowerIrpCompletionFunc: Entered\n")); 
 
    KeSetEvent(completeEvent, EVENT_INCREMENT, FALSE); 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
            ("SSSynchronousPowerIrpCompletionFunc: Exiting\n")); 
 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// SSAsynchronousPowerUpIrpCompletionFunc 
// 
//      ASYNCHRONOUS selective suspend for power up D-IRP completion  
//      routine 
// 
//  INPUTS: 
// 
//      DeviceObject  - Not used 
//       
//      MinorFunction - Not used 
// 
//      PowerState    - Not used 
// 
//      Context       - One of our device extensions 
// 
//      IoStatus      - Not used 
// 
//  OUTPUTS: 
// 
//      None 
// 
//  RETURNS: 
// 
//      None 
// 
//  IRQL: 
// 
//      IRQL <= DISPATCH_LEVEL 
// 
//  CONTEXT: 
// 
//      Arbitrary 
// 
//  NOTES: 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID SSAsynchronousPowerUpIrpCompletionFunc(PDEVICE_OBJECT DeviceObject, 
                                            UCHAR MinorFunction,  
                                            POWER_STATE PowerState, 
                                            PVOID Context, 
                                            PIO_STATUS_BLOCK IoStatus) 
{ 
 
    PUSBFX2LK_EXT devExt = (PUSBFX2LK_EXT)Context; 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
            ("SSAsynchronousPowerUpIrpCompletionFunc: Entered\n")); 
 
    // 
    // The I/O is finished. 
    // 
    OsrDecrementOutstandingIoCount(devExt,__FILE__,__LINE__); 
 
    // 
    // A power up is complete, get the Selective Suspend 
    //  state properly updated 
    // 
    SSPowerUpCompleteUpdateSSState(devExt); 
 
    OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND, 
            ("SSAsynchronousPowerUpIrpCompletionFunc: Exiting\n")); 
 
}