Sending a USB Idle Request IRP
When a device goes idle, the client driver informs the bus driver by sending an idle request IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION). After the bus driver determines that it is safe to put the device in a low power state, it calls the callback routine that the client device driver passed down the stack with the idle request IRP.
In the callback routine, the client driver must cancel all pending I/O operations and wait for all USB I/O IRPs to complete. It then can issue an IRP_MN_SET_POWER request to change the WDM device power state to D2. The callback routine must wait for the D2 request to complete before returning. For more information about the idle notification callback routine, see USB Idle Notification Callback Routine.
The bus driver does not complete the idle request IRP after calling the idle notification callback routine. Instead, the bus driver holds the idle request IRP pending until one of the following conditions is true:
- An IRP_MN_SUPRISE_REMOVAL or IRP_MN_REMOVE_DEVICE IRP is received. When one of these IRPs is received the idle request IRP completes with STATUS_CANCELLED.
- The bus driver receives a request to put the device into a working power state (D0). Upon receiving this request bus driver completes the pending idle request IRP with STATUS_SUCCESS.
The following restrictions apply to the use of idle request IRPs:
- Drivers must be in device power state D0 when sending an idle request IRP.
- Drivers must send just one idle request IRP per device stack.
The following WDM example code illustrates the steps that a device driver takes to send a USB idle request IRP. Error checking has been omitted in the following code example.
Allocate and initialize the IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION IRP
irp = IoAllocateIrp (DeviceContext->TopOfStackDeviceObject->StackSize, FALSE); nextStack = IoGetNextIrpStackLocation (irp); nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION; nextStack->Parameters.DeviceIoControl.InputBufferLength = sizeof(struct _USB_IDLE_CALLBACK_INFO);
Allocate and initialize the idle request information structure (USB_IDLE_CALLBACK_INFO).
idleCallbackInfo = ExAllocatePool (NonPagedPool, sizeof(struct _USB_IDLE_CALLBACK_INFO)); idleCallbackInfo->IdleCallback = IdleNotificationCallback; // Put a pointer to the device extension in member IdleContext idleCallbackInfo->IdleContext = (PVOID) DeviceExtension; nextStack->Parameters.DeviceIoControl.Type3InputBuffer = idleCallbackInfo;
Set a completion routine.
The client driver must associate a completion routine with the idle request IRP. For more information about the idle notification completion routine and example code, see USB Idle Request IRP Completion Routine.
IoSetCompletionRoutine (irp, IdleNotificationRequestComplete, DeviceContext, TRUE, TRUE, TRUE);
Store the idle request in the device extension.
deviceExtension->PendingIdleIrp = irp;
Send the Idle request to the parent driver.
ntStatus = IoCallDriver (DeviceContext->TopOfStackDeviceObject, irp);