How to test PnP state changes in your driver

I'm still getting back into the flow of things at work, so I will start off this week with a simpler topic and move into meatier topics as get more control over my time. One thing I am constantly asked is "how do I manually test PnP state changes in my driver within the driver?"

Obviously you can test PnP state changes in the driver by running a user mode application. The DDK ships with pnpdtest.exe (which is what we use at Microsoft when we run stress on our machines). Testing within the driver is the important part of the question here. Sometimes you want to test that a PnP state change occurs while you are in a particular hardware state and it is too difficult or impossible to request this change through an application.

Fortunately, you can get the OS PnP manager to send your device stack PnP state changing PIRPs. The following irps can be sent to your driver if you are in the started state:

  • IRP_MN_QUERY_STOP_DEVICE and the subsequent IRP_MN_STOP_DEVICE + IRP_MN_START_DEVICE or IRP_MN_CANCEL_STOP_DEVICE
  • IRP_MN_SURPRISE_REMOVAL
  • IRP_MN_START_DEVICE
  • IRP_MN_QUERY_REMOVE_DEVICE and the subsequent IRP_MN_REMOVE_DEVICE or IRP_MN_CANCEL_REMOVE_DEVICE (with a caveat)

The first three irps can be sent using IoInvalidateDeviceState() (or WdfDeviceSetDeviceState() in KMDF). The caveat for IRP_MN_QUERY_REMOVE_DEVICE is that your PDO must support ejection. The following table describes which flags to set in the IRP_MN_QUERY_PNP_DEVICE_STATE PIRP after calling IoInvalidateDeviceState().

Desired PnP IRP Flags
IRP_MN_QUERY_STOP_DEVICE PNP_DEVICE_FAILED | PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED
IRP_MN_SURPRISE_REMOVAL PNP_DEVICE_REMOVED or PNP_DEVICE_FAILED
IRP_MN_START_DEVICE PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED

For IRP_MN_QUERY_REMOVE_DEVICE to be sent, you must call IoRequestDeviceEject(). I am pretty sure that the underlying PDO must report that it is ejectable for this to work, but I could be wrong and it could work for all PDOs for the purpose of getting a query remove sent to the device stack.