30 KiB
DMF_BusFilter
Module Summary
Implements a bus filter driver pattern that intercepts the IRP_MN_QUERY_DEVICE_RELATIONS/BusRelations completion path of a
WDF FDO and splices a proxy WDM filter device object onto every child PDO reported by the underlying bus driver. The proxy filter
sits between the bus driver's PDO and the child devnode's FDO, giving the client driver visibility into PnP, power, and I/O
traffic for each child device.
This is a standalone dispatch-table-hooking library. It does not use the standard DMFMODULE handle or DMF_*_Create pattern.
Clients interact through a plain DMF_BusFilter_CONFIG structure and three lifecycle entry points.
Typical client driver usage flow:
- DriverEntry – fill in
DMF_BusFilter_CONFIGand callDMF_BusFilter_InitializeafterWdfDriverCreatereturns. - EvtDriverDeviceAdd – register
DMF_BusFilter_DeviceAddas theEvtDriverDeviceAddcallback (or call it directly from a wrapper). - EvtDeviceReleaseHardware or EvtCleanupCallback – call
DMF_BusFilter_DeviceCleanupto drain and destroy any remaining proxy child devices and prevent resource leaks.
Available only in kernel-mode builds (DMF_KERNEL_MODE).
Module Configuration
DMF_BusFilter_CONFIG_INIT
VOID
DMF_BusFilter_CONFIG_INIT(
_Out_ DMF_BusFilter_CONFIG* BusFilterConfig,
_In_ PDRIVER_OBJECT DriverObject
);
Zero-initializes a DMF_BusFilter_CONFIG structure and sets the required DriverObject field. The client must call this macro
before setting any optional callback pointers and before passing the config to DMF_BusFilter_Initialize. All optional callback
pointers default to NULL. DeviceType defaults to 0, which causes the library to inherit the device type from each child PDO
at attach time.
DMF_BusFilter_CONFIG
typedef struct
{
//
// The WDM DRIVER_OBJECT for this driver. Required.
//
_In_ DRIVER_OBJECT* DriverObject;
//
// Device type for proxy child filter DOs.
// 0 causes the library to inherit the type from the PDO (recommended).
//
_In_ DEVICE_TYPE DeviceType;
//
// Device characteristics flags for proxy child filter DOs.
// FILE_AUTOGENERATED_DEVICE_NAME, FILE_CHARACTERISTIC_TS_DEVICE,
// FILE_CHARACTERISTIC_WEBDAV_DEVICE, FILE_DEVICE_IS_MOUNTED, and
// FILE_VIRTUAL_VOLUME are masked off by the library before use.
//
_In_ ULONG DeviceCharacteristics;
//
// Called before the bus filter FDO is created.
//
_In_opt_ EVT_DMF_BusFilter_PreBusDeviceAdd* EvtPreBusDeviceAdd;
//
// Called after the bus filter FDO has been successfully created.
//
_In_opt_ EVT_DMF_BusFilter_PostBusDeviceAdd* EvtPostBusDeviceAdd;
//
// Called when a proxy child filter device object has been attached to a PDO.
//
_In_opt_ EVT_DMF_BusFilter_DeviceAdd* EvtDeviceAdd;
//
// Called when a proxy child device is about to be destroyed.
//
_In_opt_ EVT_DMF_BusFilter_DeviceRemove* EvtDeviceRemove;
//
// Called after IRP_MN_START_DEVICE has been forwarded and the lower stack returned success.
//
_In_opt_ EVT_DMF_BusFilter_DeviceStarted* EvtDeviceStarted;
//
// Called when IRP_MN_DEVICE_ENUMERATED reaches the proxy child device.
//
_In_opt_ EVT_DMF_BusFilter_DeviceEnumerated* EvtDeviceEnumerated;
//
// Called when IRP_MN_QUERY_ID reaches the proxy child device.
//
_In_opt_ EVT_DMF_BusFilter_DeviceQueryId* EvtDeviceQueryId;
//
// Called when IRP_MN_QUERY_INTERFACE reaches the proxy child device.
//
_In_opt_ EVT_DMF_BusFilter_DeviceQueryInterface* EvtDeviceQueryInterface;
} DMF_BusFilter_CONFIG;
| Member | Description |
|---|---|
| DriverObject | The WDM DRIVER_OBJECT for this driver, as received by DriverEntry. Required; DMF_BusFilter_Initialize returns STATUS_INVALID_PARAMETER if this is NULL. |
| DeviceType | Device type passed to IoCreateDevice for each proxy child filter DO. If 0, the library inherits the type from the PDO at attach time (recommended for most bus filter scenarios). |
| DeviceCharacteristics | Characteristics flags passed to IoCreateDevice. FILE_AUTOGENERATED_DEVICE_NAME, FILE_CHARACTERISTIC_TS_DEVICE, FILE_CHARACTERISTIC_WEBDAV_DEVICE, FILE_DEVICE_IS_MOUNTED, and FILE_VIRTUAL_VOLUME are always masked off by the library. |
| EvtPreBusDeviceAdd | Optional callback invoked before the bus filter FDO is created. See EVT_DMF_BusFilter_PreBusDeviceAdd. |
| EvtPostBusDeviceAdd | Optional callback invoked after the bus filter FDO has been successfully created. See EVT_DMF_BusFilter_PostBusDeviceAdd. |
| EvtDeviceAdd | Optional callback invoked after a proxy child filter DO has been attached to a newly discovered PDO. See EVT_DMF_BusFilter_DeviceAdd. |
| EvtDeviceRemove | Optional callback invoked just before a proxy child device is destroyed. See EVT_DMF_BusFilter_DeviceRemove. |
| EvtDeviceStarted | Optional callback invoked after IRP_MN_START_DEVICE has been forwarded successfully to the lower stack. See EVT_DMF_BusFilter_DeviceStarted. |
| EvtDeviceEnumerated | Optional callback invoked when IRP_MN_DEVICE_ENUMERATED reaches the proxy child device. See EVT_DMF_BusFilter_DeviceEnumerated. |
| EvtDeviceQueryId | Optional callback invoked when IRP_MN_QUERY_ID reaches the proxy child device. See EVT_DMF_BusFilter_DeviceQueryId. |
| EvtDeviceQueryInterface | Optional callback invoked when IRP_MN_QUERY_INTERFACE reaches the proxy child device. See EVT_DMF_BusFilter_DeviceQueryInterface. |
Module Callbacks
EVT_DMF_BusFilter_PreBusDeviceAdd
_IRQL_requires_same_
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
EVT_DMF_BusFilter_PreBusDeviceAdd(
_In_ WDFDRIVER Driver,
_Inout_ PWDFDEVICE_INIT DeviceInit,
_Out_ WDF_OBJECT_ATTRIBUTES* Attributes,
_Outptr_result_maybenull_ PDMFDEVICE_INIT* DmfDeviceInit
);
Called by DMF_BusFilter_DeviceAdd before the bus filter FDO is created. The client may customize DeviceInit (e.g. set PnP
capabilities or power policy) and optionally allocate a PDMFDEVICE_INIT if it wants to use DMF modules on the bus FDO. If the
client allocates *DmfDeviceInit it must not free it; the library takes ownership regardless of success or failure.
Returns
STATUS_SUCCESS, or an error status. A non-success value aborts DMF_BusFilter_DeviceAdd.
Parameters
| Parameter | Description |
|---|---|
| Driver | The WDFDRIVER for this driver. |
| DeviceInit | The WDFDEVICE_INIT being prepared for the bus filter FDO. The client may call WdfDeviceInitSet* routines on it. |
| Attributes | WDF_OBJECT_ATTRIBUTES to apply to the created device. The client may set a context type or parent here. |
| DmfDeviceInit | On output: optionally set to a PDMFDEVICE_INIT if the client uses DMF modules on the bus FDO. Pass NULL if not needed. |
EVT_DMF_BusFilter_PostBusDeviceAdd
_IRQL_requires_same_
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
EVT_DMF_BusFilter_PostBusDeviceAdd(
_In_ WDFDEVICE Device,
_In_opt_ PDMFDEVICE_INIT DmfDeviceInit
);
Called by DMF_BusFilter_DeviceAdd after the bus filter FDO has been successfully created. The client may perform any
post-creation initialization that requires a valid WDFDEVICE handle, such as creating I/O queues or registering interfaces.
Returns
STATUS_SUCCESS, or an error status. A non-success value aborts DMF_BusFilter_DeviceAdd and causes the device object to be
deleted.
Parameters
| Parameter | Description |
|---|---|
| Device | The newly created bus filter WDFDEVICE. |
| DmfDeviceInit | The PDMFDEVICE_INIT that was optionally allocated in EVT_DMF_BusFilter_PreBusDeviceAdd, or NULL if DMF module support was not requested. |
EVT_DMF_BusFilter_DeviceAdd
_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_same_
NTSTATUS
EVT_DMF_BusFilter_DeviceAdd(
_In_ WDFDEVICE Device,
_In_ DMFBUSCHILDDEVICE ChildDevice
);
Called when a proxy WDM filter device object has been created and attached to a newly discovered child PDO. The filter is already
in the stack (attached above the PDO) when this callback fires. The client may associate per-child state or WDF objects with the
ChildDevice handle at this point.
If the callback returns a non-success status, the library immediately detaches and deletes the proxy filter device; the
ChildDevice handle becomes invalid after the callback returns.
Returns
STATUS_SUCCESS, or an error status to abort creation of this proxy child and roll back the attach.
Parameters
| Parameter | Description |
|---|---|
| Device | The parent bus filter WDFDEVICE. |
| ChildDevice | Opaque DMFBUSCHILDDEVICE handle representing the newly created proxy child. Valid only for the duration of this callback (and subsequent callbacks) until EVT_DMF_BusFilter_DeviceRemove returns. |
Remarks
- The IRQL annotation is
APC_LEVELbecause this callback is invoked fromRelations_AddDevice, which runs atPASSIVE_LEVELbut holds no spinlock. The annotation is intentionally conservative; in practice this callback fires atPASSIVE_LEVEL.
EVT_DMF_BusFilter_DeviceRemove
_IRQL_requires_max_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID
EVT_DMF_BusFilter_DeviceRemove(
_In_ WDFDEVICE Device,
_In_ DMFBUSCHILDDEVICE ChildDevice
);
Called when a proxy child device is about to be destroyed. This fires both during the normal IRP_MN_REMOVE_DEVICE path and
during forced teardown in DMF_BusFilter_DeviceCleanup. At this point in-flight I/O to the child has already been drained via
the per-child IO_REMOVE_LOCK, so the child is quiescent.
The callback must not attempt to forward any IRPs to the child or access the child's WDM stack; both are being torn down. After
this callback returns the ChildDevice handle is deleted by the library.
Returns
None.
Parameters
| Parameter | Description |
|---|---|
| Device | The parent bus filter WDFDEVICE. |
| ChildDevice | Opaque handle for the proxy child being removed. Do not use after this callback returns. |
EVT_DMF_BusFilter_DeviceStarted
_IRQL_requires_max_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID
EVT_DMF_BusFilter_DeviceStarted(
_In_ DMFBUSCHILDDEVICE ChildDevice,
_In_ IRP* Irp
);
Called after the library has successfully forwarded IRP_MN_START_DEVICE to the lower stack and the lower driver returned
success. The IRP is still held by the library and will be completed after this callback returns; the client must not complete it.
The client may use this callback to perform any work that requires the child device to be in the started state, such as sending I/O or enabling device interfaces.
Returns
None.
Parameters
| Parameter | Description |
|---|---|
| ChildDevice | Opaque handle for the proxy child that has started. |
| Irp | The IRP_MN_START_DEVICE IRP. For inspection only; the client must not complete it. |
EVT_DMF_BusFilter_DeviceEnumerated
_IRQL_requires_max_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID
EVT_DMF_BusFilter_DeviceEnumerated(
_In_ DMFBUSCHILDDEVICE ChildDevice,
_In_ IRP* Irp
);
Called when IRP_MN_DEVICE_ENUMERATED reaches the proxy child device. This minor code is sent by the PnP manager after the
device has been enumerated and its instance ID has been assigned. It is informational; the library always forwards the IRP to the
lower stack after this callback returns.
The client must not complete the IRP.
Returns
None.
Parameters
| Parameter | Description |
|---|---|
| ChildDevice | Opaque handle for the proxy child being enumerated. |
| Irp | The IRP_MN_DEVICE_ENUMERATED IRP. For inspection only; the client must not complete it. |
EVT_DMF_BusFilter_DeviceQueryId
_IRQL_requires_max_(PASSIVE_LEVEL)
_IRQL_requires_same_
BOOLEAN
EVT_DMF_BusFilter_DeviceQueryId(
_In_ DMFBUSCHILDDEVICE ChildDevice,
_In_ IRP* Irp
);
Called when IRP_MN_QUERY_ID reaches the proxy child device. The client may inspect or modify the IRP stack location and set
IoStatus fields to supply identity strings (hardware ID, instance ID, etc.).
Return value contract (load-bearing):
TRUE– The client has fully handled the IRP (setIoStatus.StatusandInformation). The library completes the IRP without forwarding it to the lower stack.FALSE– The client did not handle the IRP. The library forwards it synchronously to the lower driver withIoForwardIrpSynchronously, then completes it with the lower driver's status.
Returns
TRUE if the client handled the IRP; FALSE to let the library forward it to the lower stack.
Parameters
| Parameter | Description |
|---|---|
| ChildDevice | Opaque handle for the proxy child. |
| Irp | The IRP_MN_QUERY_ID IRP. |
EVT_DMF_BusFilter_DeviceQueryInterface
_IRQL_requires_max_(PASSIVE_LEVEL)
_IRQL_requires_same_
BOOLEAN
EVT_DMF_BusFilter_DeviceQueryInterface(
_In_ DMFBUSCHILDDEVICE ChildDevice,
_In_ IRP* Irp
);
Called when IRP_MN_QUERY_INTERFACE reaches the proxy child device. The client may fill in the interface struct pointed to by
the IRP stack location's Parameters.QueryInterface field.
Return value contract (load-bearing):
TRUE– The client has fully handled the IRP (setIoStatus.Statusand filled in the interface). The library completes the IRP without forwarding it to the lower stack.FALSE– The client did not handle the IRP. The library forwards it synchronously to the lower driver withIoForwardIrpSynchronously, then completes it with the lower driver's status.
Returns
TRUE if the client handled the IRP; FALSE to let the library forward it to the lower stack.
Parameters
| Parameter | Description |
|---|---|
| ChildDevice | Opaque handle for the proxy child. |
| Irp | The IRP_MN_QUERY_INTERFACE IRP. |
Module Methods
DMF_BusFilter_Initialize
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS
DMF_BusFilter_Initialize(
_In_ DMF_BusFilter_CONFIG* BusFilterConfig
);
Initializes the bus filter library for the current driver. Must be called once from DriverEntry, after WdfDriverCreate
returns, and before the first call to DMF_BusFilter_DeviceAdd.
Internally this routine hooks the driver's entire MajorFunction dispatch table so that IRPs sent to proxy child device objects
are intercepted and routed through the library's dispatch handler. It also stores a copy of the client's configuration for use
in subsequent callbacks.
Returns
STATUS_SUCCESS, or:
| Status | Meaning |
|---|---|
STATUS_INVALID_PARAMETER |
BusFilterConfig or BusFilterConfig->DriverObject is NULL. |
STATUS_NOT_SUPPORTED |
WdfGetDriver returned NULL (WDF is not yet ready; call after WdfDriverCreate). |
| Any WDF context-allocation failure | Returned from WdfObjectAllocateContext. |
Parameters
| Parameter | Description |
|---|---|
| BusFilterConfig | Pointer to a fully populated DMF_BusFilter_CONFIG. Initialize with DMF_BusFilter_CONFIG_INIT before setting callbacks. The caller retains ownership; the library copies the config internally. |
Remarks
- Call
DMF_BusFilter_CONFIG_INITto zero-initialize the config before setting optional fields. - The library hooks
DriverObject->MajorFunctionfor all major function codes. IRPs targeting devices that do not carry the internalGUID_DMF_BUSFILTER_SIGNATUREtag are delegated to the previously installed handler transparently.
DMF_BusFilter_DeviceAdd
EVT_WDF_DRIVER_DEVICE_ADD DMF_BusFilter_DeviceAdd;
WDF EvtDriverDeviceAdd callback implementation for the bus filter FDO. The client should register this function as its
EvtDriverDeviceAdd (or call it directly from a wrapper). It creates the bus filter WDF device, installs the bus-relations IRP
preprocessor, and initializes the internal child device list.
DMF_BusFilter_Initialize must have been called before this function.
Returns
STATUS_SUCCESS, or a failure status from WdfDeviceCreate or supporting framework calls. On failure, any partially created
device is cleaned up.
Parameters
| Parameter | Description |
|---|---|
| Driver | The WDFDRIVER. |
| DeviceInit | The WDFDEVICE_INIT prepared by the framework. |
DMF_BusFilter_DeviceCleanup
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID
DMF_BusFilter_DeviceCleanup(
_In_ WDFDEVICE Device
);
Tears down all proxy child filter devices still associated with the bus device. The client must call this from its
EvtDeviceReleaseHardware or EvtCleanupCallback to prevent WDM device object leaks. Without this call, any child devices
remaining in the internal list at unload time will be leaked and will fault when the I/O manager later dereferences them.
Under normal PnP operation the child list is already empty when this function runs (IRP_MN_REMOVE_DEVICE has been dispatched
to every child). This function is a safety net for abnormal paths such as surprise removal of the parent before all children
received REMOVE.
The function first waits for any outstanding bus-relations work items to drain (preventing a race with a pending passive-level
Relations_AddDevice call), then for each remaining child: acquires the per-child IO_REMOVE_LOCK to drain in-flight I/O,
calls EvtDeviceRemove (if set), and detaches and deletes the proxy filter device in the mandatory order.
If a child's IO_REMOVE_LOCK cannot be acquired (STATUS_DELETE_PENDING), it means a concurrent IRP_MN_REMOVE_DEVICE is
already processing that child; this function skips it and lets the REMOVE path handle teardown.
Returns
None.
Parameters
| Parameter | Description |
|---|---|
| Device | The bus filter WDFDEVICE whose proxy children should be cleaned up. |
DMF_BusFilter_WdmDeviceObjectGet
PDEVICE_OBJECT
DMF_BusFilter_WdmDeviceObjectGet(
_In_ DMFBUSCHILDDEVICE ChildDevice
);
Returns the WDM DEVICE_OBJECT that represents the proxy child filter device itself (the filter DO created by IoCreateDevice
inside this library). Use this to obtain the raw WDM object when the WDF handle is not sufficient.
Returns
The proxy filter DEVICE_OBJECT, or NULL if the context is missing.
Parameters
| Parameter | Description |
|---|---|
| ChildDevice | Opaque handle for a proxy child device. |
DMF_BusFilter_WdmAttachedDeviceGet
PDEVICE_OBJECT
DMF_BusFilter_WdmAttachedDeviceGet(
_In_ DMFBUSCHILDDEVICE ChildDevice
);
Returns the WDM DEVICE_OBJECT to which the proxy child filter is attached (i.e. the result of
IoAttachDeviceToDeviceStackSafe — typically the PDO itself or the topmost device in the PDO's stack at attach time). This is
the object that IRPs are forwarded to when the filter does not handle them.
Returns
The attached (lower) DEVICE_OBJECT, or NULL if the child has an unrecognised signature or a missing context.
Parameters
| Parameter | Description |
|---|---|
| ChildDevice | Opaque handle for a proxy child device. |
DMF_BusFilter_WdmPhysicalDeviceGet
PDEVICE_OBJECT
DMF_BusFilter_WdmPhysicalDeviceGet(
_In_ DMFBUSCHILDDEVICE ChildDevice
);
Returns the Physical Device Object (PDO) associated with a proxy child device. This is the PDO reported by the bus driver in
the DEVICE_RELATIONS array that caused this proxy child to be created.
Returns
The PDO DEVICE_OBJECT, or NULL if the child has an unrecognised signature or a missing context.
Parameters
| Parameter | Description |
|---|---|
| ChildDevice | Opaque handle for a proxy child device. |
Module IOCTLs
- None
Module Remarks
DMF_BusFilter_Initializemust be called beforeDMF_BusFilter_DeviceAdd. IfDeviceAddis called without a prior successfulInitialize, it returnsSTATUS_INVALID_DEVICE_STATE.DMF_BusFilter_DeviceCleanupmust be called fromEvtDeviceReleaseHardwareorEvtCleanupCallback. Omitting this call leaks WDM device objects that remain in the child list at driver unload.- The mandatory WDM filter teardown order for each proxy child is: (1)
IoReleaseRemoveLockAndWaitto drain in-flight I/O, (2)IoDetachDeviceto detach the filter from the PDO stack, (3)IoDeleteDeviceto lazy-free the device object. Nothing may access the device extension afterIoDeleteDevicereturns. - The bus-relations completion routine (
DMF_BusFilter_QueryBusRelationsCompleted) may fire atDISPATCH_LEVEL. BecauseIoCreateDeviceandIoAttachDeviceToDeviceStackSaferequirePASSIVE_LEVEL, the library automatically defers child creation to a passive-level work item in that case. - The completion routine returns
STATUS_MORE_PROCESSING_REQUIREDto hold the IRP (and therefore the bus driver's references on each PDO inDEVICE_RELATIONS) alive until the passive-level worker has finished attaching all filter devices. DeviceTypeset to0inDMF_BusFilter_CONFIGcauses the library to inherit the device type from each child PDO at attach time. A filter whose device type differs from the stack it joins can cause I/O manager policy mismatches.- All
EVT_*callback pointers inDMF_BusFilter_CONFIGare optional. The library silently skipsNULLcallbacks.
Module Children
- None
Module Implementation Details
Device Stack Placement
The library attaches one proxy WDM filter DO above each PDO reported by the bus driver. The resulting per-child devnode stack looks like:
flowchart TD
FDO["Child FDO\n(loaded later by PnP)"]
FilterDO["Proxy Filter DO\n(created by this library,\ntagged with GUID_DMF_BUSFILTER_SIGNATURE)"]
PDO["Child PDO\n(reported by bus driver)"]
FDO --> FilterDO --> PDO
BusFDO["Bus Filter FDO\n(DMF_BusFilter_DeviceAdd)"]
BusFDO -. "preprocesses\nQUERY_DEVICE_RELATIONS" .-> PDO
The bus filter FDO hooks IRP_MN_QUERY_DEVICE_RELATIONS/BusRelations via a WDF IRP preprocessor callback. When the
completion fires it calls IoAttachDeviceToDeviceStackSafe once per PDO in DEVICE_RELATIONS that has not been seen before.
Dispatch-Table Hooking and Child Identification
DMF_BusFilter_Initialize replaces every slot of DriverObject->MajorFunction with the library's own
DMF_BusFilter_DispatchHandler. The original per-slot function pointer is saved in the driver-level context
(BusFilter_Context.MajorDispatchFunctions).
For each incoming IRP, DispatchHandler inspects the target device object's extension for the tag GUID
GUID_DMF_BUSFILTER_SIGNATURE ({678CBB8D-019F-4D07-912A-73E2E568B148}). IRPs targeting non-child devices (e.g. the WDF FDO)
are delegated to the saved original handler transparently. IRPs targeting proxy children are handled by the library.
Bus-Relations Attach Window and Work-Item Rundown
The only safe window for a bus filter to attach to a PDO is between the bus driver populating DEVICE_RELATIONS and the PnP
manager dispatching IRP_MN_START_DEVICE to the child FDO. Attaching outside this window races PDO creation or FDO
construction.
The parent bus device tracks outstanding passive-level workers with an atomic counter (OutstandingWorkItems) and a kernel
event (WorkItemsIdle, signaled when the counter reaches zero). DMF_BusFilter_DeviceCleanup waits on WorkItemsIdle before
draining the child list, preventing a race between a concurrent Relations_AddDevice worker and list teardown.
flowchart TD
BusRelComp["BusRelations completion\n(may be DISPATCH_LEVEL)"]
Check{"IRQL >
PASSIVE_LEVEL?"}
WorkItem["Queue passive-level\nwork item"]
Inline["Process inline\nat PASSIVE_LEVEL"]
AddDevice["Relations_AddDevice\nper PDO in DEVICE_RELATIONS\n(IoCreateDevice +\nIoAttachDeviceToDeviceStackSafe +\nEvtDeviceAdd)"]
CompleteIrp["IoCompleteRequest\n(resume IRP propagation)"]
BusRelComp --> Check
Check -- Yes --> WorkItem --> AddDevice
Check -- No --> Inline --> AddDevice
AddDevice --> CompleteIrp
Per-Child IO_REMOVE_LOCK Protocol
Each proxy child device extension contains an IO_REMOVE_LOCK. DispatchHandler acquires the lock for every IRP that enters
the filter. Release rules:
IRP_MN_REMOVE_DEVICE— released insideRelations_RemoveDeviceviaIoReleaseRemoveLockAndWait, which also drains all other in-flight holders. The dispatcher must not release the lock again after this call.- All other PnP minors — released with
IoReleaseRemoveLockafterDispatchPnpreturns. - Non-PnP IRPs — released with
IoReleaseRemoveLockbeforeIoCallDriverso the lock does not span an asynchronous IRP completion.
Examples
The following sketch shows the minimum wiring for a client driver.
//
// Global configuration block (or stored in driver context).
//
static DMF_BusFilter_CONFIG g_BusFilterConfig;
//
// Optional: per-child callback.
//
static
NTSTATUS
MyDriver_EvtDeviceAdd(
_In_ WDFDEVICE Device,
_In_ DMFBUSCHILDDEVICE ChildDevice
)
{
UNREFERENCED_PARAMETER(Device);
// Retrieve the raw PDO for this child if needed.
PDEVICE_OBJECT pdo = DMF_BusFilter_WdmPhysicalDeviceGet(ChildDevice);
UNREFERENCED_PARAMETER(pdo);
return STATUS_SUCCESS;
}
static
VOID
MyDriver_EvtDeviceRemove(
_In_ WDFDEVICE Device,
_In_ DMFBUSCHILDDEVICE ChildDevice
)
{
UNREFERENCED_PARAMETER(Device);
UNREFERENCED_PARAMETER(ChildDevice);
}
//
// Called when the bus filter FDO's hardware is being released.
//
static
NTSTATUS
MyDriver_EvtDeviceReleaseHardware(
_In_ WDFDEVICE Device,
_In_ WDFCMRESLIST ResourcesTranslated
)
{
UNREFERENCED_PARAMETER(ResourcesTranslated);
// Mandatory: drain and destroy all remaining proxy children.
DMF_BusFilter_DeviceCleanup(Device);
return STATUS_SUCCESS;
}
//
// DriverEntry
//
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
NTSTATUS ntStatus;
WDF_DRIVER_CONFIG driverConfig;
WDF_DRIVER_CONFIG_INIT(&driverConfig, DMF_BusFilter_DeviceAdd);
ntStatus = WdfDriverCreate(DriverObject,
RegistryPath,
WDF_NO_OBJECT_ATTRIBUTES,
&driverConfig,
WDF_NO_HANDLE);
if (!NT_SUCCESS(ntStatus))
{
return ntStatus;
}
// Initialize the bus filter library after WdfDriverCreate.
//
DMF_BusFilter_CONFIG_INIT(&g_BusFilterConfig, DriverObject);
g_BusFilterConfig.EvtDeviceAdd = MyDriver_EvtDeviceAdd;
g_BusFilterConfig.EvtDeviceRemove = MyDriver_EvtDeviceRemove;
return DMF_BusFilter_Initialize(&g_BusFilterConfig);
}
To Do
Module Category
Driver Patterns