USB Idle Request IRP Completion Routine

In many cases, a bus driver might call a driver's idle request IRP completion routine. If this occurs, a client driver must detect why the bus driver completed the IRP. The returned status code can provide this information. If the status code is not STATUS_POWER_STATE_INVALID, the driver should put its device in D0 if the device is not already in D0. If the device is still idle, the driver can submit another idle request IRP.

Note  The idle request IRP completion routine should not block waiting for a D0 power request to complete. The completion routine can be called in the context of a power IRP by the hub driver, and blocking on another power IRP in the completion routine can lead to a deadlock.

The following list indicates how a completion routine for an idle request should interpret some common status codes:

Status CodeDescription

STATUS_SUCCESS

Indicates that the device should no longer be suspended. However, drivers should verify that their devices are powered, and put them in D0 if they are not already in D0.

STATUS_CANCELLED

The bus driver completes the idle request IRP with STATUS_CANCELLED in any of the following circumstances:

  • The device driver canceled the IRP.
  • A system power state change is required.
  • On Windows XP, the device driver for one of the connected USB devices failed to put its device in D2 while executing its idle request callback routine. As a result, the bus driver completed all pending idle request IRPs.

STATUS_POWER_STATE_INVALID

Indicates that the device driver requested a D3 power state for its device. When this occurs, the bus driver completes all pending idle IRPs with STATUS_POWER_STATE_INVALID.

STATUS_DEVICE_BUSY

Indicates that the bus driver already holds an idle request IRP pending for the device. Only one idle IRP can be pending at a time for a given device. Submitting multiple idle request IRPs is an error on the part of the power policy owner, and should be addressed by the driver writer.

 

The following code example shows a sample implementation for the idle request completion routine.



/*Routine Description:

  Completion routine for idle notification IRP

Arguments:

    DeviceObject - pointer to device object
    Irp - I/O request packet
    DeviceExtension - pointer to device extension

Return Value:

    NT status value

--*/

NTSTATUS
IdleNotificationRequestComplete(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PDEVICE_EXTENSION DeviceExtension
    )
{
    NTSTATUS                ntStatus;
    POWER_STATE             powerState;
    PUSB_IDLE_CALLBACK_INFO idleCallbackInfo;

    ntStatus = Irp->IoStatus.Status;
    
    if(!NT_SUCCESS(ntStatus) && ntStatus != STATUS_NOT_SUPPORTED) 
    {

        //Idle IRP completes with error.

        switch(ntStatus) 
        {
            
        case STATUS_INVALID_DEVICE_REQUEST:

            //Invalid request.

            break;

        case STATUS_CANCELLED:

            //1. The device driver canceled the IRP. 
            //2. A system power state change is required. 

            break;

        case STATUS_POWER_STATE_INVALID:

            // Device driver requested a D3 power state for its device
            // Release the allocated resources.

            goto IdleNotificationRequestComplete_Exit;

        case STATUS_DEVICE_BUSY:

            //The bus driver already holds an idle IRP pending for the device.

            break;

        default:
            break;

        }

 
        // If IRP completes with error, issue a SetD0

        //Increment the I/O count because
        //a new IRP is dispatched for the driver.
        //This call is not shown.

        powerState.DeviceState = PowerDeviceD0;

        // Issue a new IRP
        PoRequestPowerIrp (
            DeviceExtension->PhysicalDeviceObject, 
            IRP_MN_SET_POWER, 
            powerState, 
            (PREQUEST_POWER_COMPLETE) PoIrpCompletionFunc, 
            DeviceExtension, 
            NULL);
    }

IdleNotificationRequestComplete_Exit:

    idleCallbackInfo = DeviceExtension->IdleCallbackInfo;

    DeviceExtension->IdleCallbackInfo = NULL;

    DeviceExtension->PendingIdleIrp = NULL;

    InterlockedExchange(&DeviceExtension->IdleReqPend, 0);

    if(idleCallbackInfo)
    {
        ExFreePool(idleCallbackInfo);
    }

    DeviceExtension->IdleState = IdleComplete;

    // Because the IRP was created using IoAllocateIrp,
    // the IRP needs to be released by calling IoFreeIrp.
    // Also return STATUS_MORE_PROCESSING_REQUIRED so that
    // the kernel does not reference this.

    IoFreeIrp(Irp);

    KeSetEvent(&DeviceExtension->IdleIrpCompleteEvent, IO_NO_INCREMENT, FALSE);

    return STATUS_MORE_PROCESSING_REQUIRED;
}



Related topics

USB Selective Suspend
USB Power Management

 

 

Send comments about this topic to Microsoft

Show: