How does WdfDeviceSetFailed work? (AKA the case of the missing WDK documentation for REENUMERATE_SELF_INTERFACE_STANDARD)

KMDF is built on top of public interfaces. This means that it uses only APIs found in the WDK (or what was the DDK). This creates a dilemma. On the one hand we want to add features to the framework that are compelling and add value, but on the other we cannot rely on the OS to provide this functionality (since we must run on previous OS releases which would not have this new feature). We could selectively enable the feature if you are running on a particular OS, but that makes the driver writer's life more difficult because now you have two code paths to deal with when you try to use this new function.

One feature that we wanted to add to KMDF is the ability to rebuild the stack from the ground up without help from a user mode application. This feature is surfaced in the DDI WdfDeviceSetFailed(, WdfDeviceFailedRestart). Why would did we want this? For a variety of reasons. Consider the situation where you could not allocate a system resource (let's say a power irp) that was needed by your driver. You would want to tear down the stack and rebuild it with the hope that the resource is now available. The acquisition of this resource could occur within the framework itself or your driver. We thought long and hard about how we could implement this feature. One suggestion was to report a new PnP device state in IRP_MN_QUERY_PNP_DEVICE_STATE and have the I/O manager do the hard work for us, but this made the framework rely on a change in the OS which would not be available. What we realized is that what we wanted was functionally the same as a bus driver reporting a device as missing (e.g. a surprise removal) and then present again and that all we needed to do was instruct the bus driver to perform this sequence of events on our behalf.

But how would we tell the bus driver to perform these actions? Well, there is already a standard way for querying for functionality in a stack, IRP_MN_QUERY_INTERFACE, so all we did is define a new interface structure, REENUMERATE_SELF_INTERFACE_STANDARD, and associated GUID, GUID_REENUMERATE_SELF_INTERFACE_STANDARD. KMDF implements the querying and implementation of this interface. A KMDF FDO or filter device object will query for the interface and you can invoke it by calling WdfDeviceSetFailed as described above. Furthermore, a KMDF PDO enumerated by a WDFCHILDLIST will automatically support the implementation of the interface by default (and the bus driver can override its behavior by implementing EvtChildListDeviceReenumerated), so any KMDF bus driver can participate.

The idea was that this interface would be published in the WDK and documented so that any bus driver (like USB or PCI or a 3rd party) could provide this functionality. The good thing is that the structure made it into the header, but unfortunately the docs were not updated accordingly (although we do have a fleeting reference to it without a definition). The next release of the WDK will include documentation for the interface, but I wanted to get the word out now. The remainder of this blog entry is dedicated to the documentation of the structure. The structure, REENUMERATE_SELF_INTERFACE_STANDARD can be found in wdm.h; the GUID, GUID_REENUMERATE_SELF_INTERFACE_STANDARD, can be found in wdmguid.h Here is the structure:

 typedef
VOID
(*PREENUMERATE_SELF)(
    IN PVOID Context
    );

typedef struct _REENUMERATE_SELF_INTERFACE_STANDARD {
    //
    // generic interface header
    //
    USHORT Size;
    USHORT Version;
    PVOID Context;
    PINTERFACE_REFERENCE InterfaceReference;
    PINTERFACE_DEREFERENCE InterfaceDereference;
    //
    // Self-reenumeration interface
    //
    PREENUMERATE_SELF SurpriseRemoveAndReenumerateSelf;
} REENUMERATE_SELF_INTERFACE_STANDARD, *PREENUMERATE_SELF_INTERFACE_STANDARD;

All but the last field are a part of the standard INTERFACE header. SurpriseRemoveAndReenumerateSelf is the function provided by the PDO. You can call SurpriseRemoveAndReenumerateSelf() from IRQL <= PASSIVE_LEVEL. Note that SurpriseRemoveAndReenumerateSelf does not return an NTSTATUS. The functionality exposed by this function is not guaranteed to happen. The bus driver is not required to surprise remove and reenumerate your device. It will only do so if the parent FDO and the PDO are in the correct (where the bus driver gets to determine what that state is).