In my last post I described why a WDFREQUEST is unique to a particular WDFDEVICE. There is one particular programming pattern where this is not the behavior you want. This pattern is when you have each PDO accepting IO requests which it then forwards on to the parent WDFDEVICE for processing. One great in box example of this is usbhub.sys. Each usbhub PDO receives URBs which are then forwarded to the parent FDO and the FDO is where all IO processing occurs.
If you want to apply this pattern to a KMDF driver written to a v1.7 or earlier and take advantage of WDFQUEUEs you had to send the requests from the PDO to the FDO with WdfRequestSend so that they were represented to the FDO. The easiest way to do this is to create a WDFIOTARGET for the FDO itself and then have each PDO send IO to that WDFIOTARGET as shown in the following 3 code snippets.
NTSTATUS EvtDriverDeviceAdd(WDFDRIVER Driver, PWDFDEVICE_INIT DeviceInit) { WDFDEVICE device; WDF_OBJECT_ATTRIBUTES woa; WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&woa, FDO_EXTENSION); // ...initialize the DeviceInit... status = WdfDeviceCreate(&DeviceInit, &woa, &device); if (!NT_SUCCESS(status)) { return status; } PFDO_EXTENSION pFdoExt = GetFdoExt(device); status = WdfIoTargetCreate(device, WDF_NO_OBJECT_ATTRIBUTES, &pFdoExt->SelfTarget); if (!NT_SUCCESS(status)) { return status; } // open the WDFIOTARGET to point our own PDEVICE_OBJECT WDF_IO_TARGET_OPEN_PARAMS openParams; WDF_IO_TARGET_OPEN_PARAMS_INIT_EXISTING_DEVICE(openParams, WdfDeviceWdmGetDeviceObject(device)); status = WdfIoTargetOpen(pFdoExt->SelfTarget, &openParams); if (!NT_SUCCESS(status)) { return status; } ... return status; }
NTSTATUS EvtChildListCreateDevice( WDFCHILDLIST ChildList, PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription, PWDFDEVICE_INIT ChildInit ) { WDFDEVICE pdo; NTSTATUS status; WDF_OBJECT_ATTRIBUTES woa; WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&woa, PDO_EXTENSION); status = WdfDeviceCreate(&ChildInit, &woa, &device); if (!NT_SUCCESS(status)) { return status; } PPDO_EXTENSION pPdoExt = GetPdoExt(device); PFDO_EXTENSION pFdoExt = GetFdoExt(WdfPdoGetParent(device)); pPdoExt->ParentTarget = pFdoExt->SelfTarget; ... return status; }
typedef VOID EvtIoDefault(WDFQUEUE Queue, WDFREQUEST Request)
{ // ... extract Request type ... if (RequestShouldBeSentToParent()) { WdfRequestFormatRequestUsingCurrentType(Request); WdfRequestSend(Request, GetPdoExt(WdfIoQueueGetDevice(Queue))->ParentTarget); } }
As you can see, this is a bit cumbersome! While it works, it is not ideal. The KMDF team addressed this issue in v1.9 by adding 2 new DDIs that must be used together
Let’s now rewrite the 3 code snippets to make use of these new DDIs, new code in red
NTSTATUS EvtDriverDeviceAdd(WDFDRIVER Driver, PWDFDEVICE_INIT DeviceInit) { WDFDEVICE device; WDF_OBJECT_ATTRIBUTES woa; WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&woa, FDO_EXTENSION); // ...initialize the DeviceInit... status = WdfDeviceCreate(&DeviceInit, &woa, &device); if (!NT_SUCCESS(status)) { return status; } PFDO_EXTENSION pFdoExt = GetFdoExt(device); status = WdfIoTargetCreate(device, WDF_NO_OBJECT_ATTRIBUTES, &pFdoExt->SelfTarget); if (!NT_SUCCESS(status)) { return status; } // open the WDFIOTARGET to point our own PDEVICE_OBJECT WDF_IO_TARGET_OPEN_PARAMS openParams; WDF_IO_TARGET_OPEN_PARAMS_INIT_EXISTING_DEVICE(openParams, WdfDeviceWdmGetDeviceObject(device)); status = WdfIoTargetOpen(pFdoExt->SelfTarget, &openParams); if (!NT_SUCCESS(status)) { return status; } // initialize a WDF_IO_QUEUE_CONFIG to your needs status = WdfIoQueueCreate(device, ..., &pFdoExt->>ChildProcessingQueue); if (!NT_SUCCESS(status)) { return status; } ... return status; }
NTSTATUS EvtChildListCreateDevice( WDFCHILDLIST ChildList, PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription, PWDFDEVICE_INIT ChildInit ) { WDFDEVICE pdo; NTSTATUS status; WDF_OBJECT_ATTRIBUTES woa; WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&woa, PDO_EXTENSION); WdfPdoInitAllowForwardingRequestToParent(ChildInit); status = WdfDeviceCreate(&ChildInit, &woa, &device); if (!NT_SUCCESS(status)) { return status; } PPDO_EXTENSION pPdoExt = GetPdoExt(device); PFDO_EXTENSION pFdoExt = GetFdoExt(WdfPdoGetParent(device)); pPdoExt->ParentTarget = pFdoExt->SelfTarget; ... return status; }
VOID EvtIoDefault(WDFQUEUE Queue, WDFREQUEST Request)
{ // ... extract Request type ... if (RequestShouldBeSentToParent()) { WdfRequestFormatRequestUsingCurrentType(Request); WdfRequestSend(Request, GetPdoExt(WdfIoQueueGetDevice(Queue))->ParentTarget); WDF_REQUEST_FORWARD_OPTIONS options; WDF_REQUEST_FORWARD_OPTIONS_INIT(&options); options.Flags = WDF_REQUEST_FORWARD_OPTIONS_FLAGS;
status = WdfRequestForwardToParentDeviceIoQueue( Request, GetFdoExt(WdfPdoGetParent(WdfIoQueueGetDevice(Queue)))->ChildProcessingQueue, &options); } }