Dynamic Enumeration Made Simpler in WDF
Introduction
The Windows driver foundation (WDF) is a library that provides a set of DDIs (Device Driver Interface) that makes the life of a Windows driver writer so easy. This article describes functionalities provided by WDF to support dynamic enumeration of devices, which is a normal functionality of a bus driver.
Writing bus driver in WDM involves handling IRPs for bus driver FDO as well as IRPs for PDOs created by bus driver.
WDF applies an object-oriented approach to do this and introduces a data structure called “WDFCHILDLIST,” to simplify the overall process of dynamic enumeration. This document provides some initial guidelines for using WDF to implement dynamic bus enumerator.
This article assumes that user has previous experience with WDM concepts.
Dynamic Enumeration in WDM
Take a brief look at dynamic enumerators implemented using WDM:
- A WDM bus driver enumerates its children in response to an IRP_MN_QUERY_DEVICE_RELATIONS request with a relation type of BusRelations.
- After enumerating PDO, Bus Driver needs to handle IRP_MN_QUERY_ID sent by the PnP Manager to successfully install FDO for the child.
The PnP Manager issues this request in several forms to determine which device identifiers it will use to locate the INF file for a child device. Bus Driver responds by returning (in IoStatus.Information) a MULTI_SZ value containing the requisite device identifiers.
- Handle PnP IRPs for bus driver FDO as well as child PDO. The common task across all bus drivers is to maintain the child-parent relationship for devices enumerated by it. It involves functionalities such as:
- Maintain unique identification for child PDOs.
- Delete all child PDOs while handling IRP_MN_SURPRISE_REMOVAL/IRP_MN_REMOVE_DEVICE for bus driver FDO.
- After every modification in the child list, call IoInvalidateDeviceRelations to notify the PnP manager that the relations for a device (such as bus relations, ejection relations, removal relations, and the target device relation) have changed.
- The major task is to handle IRP_MN_QUERY_DEVICE_RELATIONS to provide information about child PDOs.
Dynamic Enumeration in WDF
To simplify Bus Driver’s task, WDF provides a WDFCHILDLIST object. Logically, you can explain the WDFCHILDLIST object as follows:
- The framework creates an empty default WDFCHILDLIST object for every FDO and sets the FDO as the parent of the WDFCHILDLIST. The child-list object maintains information about the child devices that are enumerated by a parent device.
- So, dynamic enumeration now will happen in the following way:
- The Bus Driver calls WdfFdoInitSetDefaultChildListConfig from EvtDriverDeviceAdd to configure the child list and register the EvtChildListXxx callbacks before it creates the FDO for the bus.
For example, inside EvtDriverDeviceAdd of the bus driver:
- Enumerate the child devices after creating the FDO and use WdfChildListXxx methods to populate the child list:
- Implement EvtChildListCreateDevice, which creates the PDO for a child device. The framework passes a pointer to a WDFDEVICE_INIT structure when it invokes this callback. The callback fills in the structure and calls WdfDeviceCreate to create the child PDO.
- Implement other EvtChildListXxx callbacks as required to support the device’s children.
- A driver that performs dynamic enumeration uses WdfChildListXxx methods to change its child list. Following is list of DDIs provided for child list handling:
- WdfChildListAddOrUpdateChildDescriptionAsPresent
- WdfChildListUpdateAllChildDescriptionsAsPresent
- WdfChildListBeginIteration
- WdfChildListBeginScan
- WdfChildListEndIteration
- WdfChildListEndScan
- WdfChildListRequestChildEject
- WdfChildListGetDevice
- WdfChildListRetrieveAddressDescription
- WdfChildListRetrieveNextDevice
- WdfChildListRetrievePdo
// Initialize WDF_CHILD_LIST_CONFIG to set // EvtChildListCreateDevice and // IndentificationDescriptionSize WDF_CHILD_LIST_CONFIG_INIT(&config, sizeof(PDO_IDENTIFICATION_DESCRIPTION), // callback to create a child device. Bus_EvtDeviceListCreatePdo ); // Configure bus driver's default child list WdfFdoInitSetDefaultChildListConfig(DeviceInit, &config, WDF_NO_OBJECT_ATTRIBUTES);
// obtain handle to default child list list = WdfFdoGetDefaultChildList(Device); PDO_IDENTIFICATION_DESCRIPTION description; WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT( description.Header, sizeof(description)) ); description.SwitchNumber = i; WdfChildListBeginScan(list); // enumerate new PDO, by adding new identification // description status = WdfChildListAddOrUpdateChildDescriptionAsPresent( list, &description.Header, NULL ); WdfChildListEndScan(list); if (!NT_SUCCESS(status) && status != STATUS_OBJECT_NAME_EXISTS){return status; }
NTSTATUS SampleDriver_EvtChildListCreatePdo( WDFCHILDLIST DeviceList, PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription, PWDFDEVICE_INIT ChildInit ) { // Call WdfPdoInitXxx DDIs to initialize PDO // Provide DeviceID, HardwareIDs, CompatibleIDs, // and InstanceId RtlInitUnicodeString(&deviceId, HardwareIds); status = WdfPdoInitAssignDeviceID(DeviceInit, &deviceId); b& // Create PDO status = WdfDeviceCreate(&DeviceInit, &pdoAttributes, &hChild); b& }