Freigeben über


Formatting a WDFREQUEST for any IRP_MJ code

In KMDF, the WDFIOTARGET object provides an abstraction for sending I/O to another
PDEVICE_OBJECT and tracking all pending I/O.
The WDFIOTARGET also provides formatting for specific types of I/O: read, write, IOCTL, and internal IOCTL.
The WDFUSBDEVICE and WDFUSBPIPE objects are WDFIOTARGET objects with specific
domain knowledge about USB and know how to format USB specific requests. All of
these formatting functions also maintain a reference count on the WDFMEMORYs that
are used in the format to make sure that the memory remains valid for the lifetime
of the I/O.

But what if you wanted to format a request with a format that KMDF does not current support?

KMDF suports formatting a request with any format. You still get the WDFIOTARGET
functionality of tracking I/O, but you may lose the additional reference count
on any memory used in that I/O. There is a very generic way of specifying your
own format as well as a more specific way which is applicable to internal IOCTLs.

KMDF allows you to specify the format of the WDFREQUEST with the
WdfRequestWdmFormatUsingStackLocation() DDI. You format an
IO_STACK_LOCATION and KMDF will copy the contents
from your buffer into the WDFREQUEST's next stack location. This looks very much
like WDM code except that you are not retrieving the next stack location by calling
IoGetNextIrpStackLocation, instead you are letting
KMDF set it. Since the
IO_STACK_LOCATION structure has no concept of a WDFMEMORY,
KMDF cannot determine which fields in the stack location buffer correspond to
a WDFMEMORY object, so no reference counts are taken. You are responsible for
making sure that the buffer provided as a part of the format remains valid for the
lifetime of the I/O.
Here is a code snippet on how to format a WDFREQUEST for a
IRP_MJ_PNP query capabilities and sending it down the stack:

 
    NTSTATUS GetCaps(WDFDEVICE Device)
    {
        WDFREQUEST request;
        WDFIOTARGET target;
        WDF_REQUEST_SEND_OPTIONS options;
        WDF_REQUEST_REUSE_PARAMS reuse;
        IO_STACK_LOCATION stack;
        DEVICE_CAPABILITIES caps;
        NTSTATUS status;

        target = WdfDeviceGetIoTarget(Device);

        status = WdfRequestCreate(
            WDF_NO_OBJECT_ATTRIBUTES, target, &request);
        if (!NT_SUCCESS(status)) {
            return status;
        }

        // All pnp IRPs must be initialized with STATUS_NOT_SUPPORTED
        WDF_REQUEST_REUSE_PARAMS_INIT(
            &reuse, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_NOT_SUPPORTED);
        WdfRequestReuse(request, &reuse);

        // Initialize device capabilities according to the DDK docs
        RtlZeroMemory(&caps, sizeof(DEVICE_CAPABILITIES));
        caps.Size = sizeof(DEVICE_CAPABILITIES);
        caps.Version  =  1;
        caps.Address  = (ULONG) -1;
        caps.UINumber = (ULONG) -1;

        RtlZeroMemory(&stack, sizeof(stack));
        stack.MajorFunction = IRP_MJ_PNP;
        stack.MinorFunction = IRP_MN_QUERY_CAPABILITIES;
        stack.Parameters.DeviceCapabilities.Capabilities = ∩︀

        WdfRequestWdmFormatUsingStackLocation(request, &stack); 

        WDF_REQUEST_SEND_OPTIONS_INIT(&options, WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);

        WdfRequestSend(request, target, &options);
        status = WdfRequestGetStatus(request);

        WdfObjectDelete(request);

        return status;
    }

This example is a little more complex then I would like it to be, but I wanted to
use a real world example. The example is sending synchronous I/O and the pattern
for sending asychronous I/O is about the same. Note that KMDF does not make any copies of the buffers
embedded in the provided IO_STACK_LOCATION, so do not provide embed a buffer that
is a local variable on the stack for a WDFREQUEST that will be completed asynchronously!
If you have a WDFREQUEST from an EvtIoXxx dispatch routine, you can use that
in your format call (assuming that you have made sure previously that your device's StackSize
is appropriately large enough to fwd I/O to another device) instead of allocating your
own WDFREQUEST.

One interesting subset of formatting your own I/O is if you are on a protocol stack that
uses request blocks to do its work. Instances of request blocks are URB (USB), SRB (SCSI, disk),
IRB (1394). Let's call a generic request block an XRB. XRBs are usually sent to
the bus driver using an interrnal IOCTL. In this case you can use
WdfIoTargetFormatRequestForInternalIoctlOthers() to
format the request and then send it. This is better then calling
WdfRequestWdmFormatUsingStackLocation in this
particular case because WdfIoTargetFormatRequestForInternalIoctlOthers()
will maintain the refcount on the WDFMEMORY for you, freeing you of that burden.

 
    NTSTATUS SendXrb(WDFDEVICE Device, WDFREQUEST Request)
    {
        WDF_OBJECT_ATTRIBUTES woa;
        NTSTATUS status;
        WDFMEMORY memory;
        PXRB pXrb;

        WDF_OBJECT_ATTRIBUTES_INIT(&woa);
        woa.ParentObject = Request;

        status = WdfMemoryCreate(WDF_NO_OBJECT_ATTRIBUTES,
                                 NonPagedPool,
                                 0,
                                 sizeof(XRB),
                                 &memory,
                                 (PVOID*) &pXrb);
        if (!NT_SUCCESS(status)) {
            return status;
        }

        [...Format the XRB...]

        // Parameters.Others.Argument3 is used by the IOCTL value
        status = WdfIoTargetFormatRequestForInternalIoctlOthers(
            WdfDeviceGetIoTarget(Device),
            Request,
            IOCTL_SUBMIT_XRB,
            memory, NULL,        // Parameters.Others.Argument1
            WDF_NO_HANDLE, NULL, // Parameters.Others.Argument2
            WDF_NO_HANDLE, NULL   // Parameters.Others.Argument4
            );

        if (NT_SUCCESS(status)) {
            WdfRequestSetCompletionRoutine(Request, XrbCompletionRoutine, NULL);

            // send async
            if (WdfRequestSend(Request,
                               WdfDeviceGetIoTarget(Device),
                               WDF_NO_SEND_OPTIONS) == FALSE) {
                // send failed
                WdfObjectDelete(memory);
                status = WdfRequestGetStatus(Request);
            }
            else {
                status = STATUS_PENDING;
            }
        }

        if (!NT_SUCCESS(status)) {
            WdfObjectDelete(memory);
        }

        return status;
    }

Comments

  • Anonymous
    August 16, 2006
    Good Article! Seems I have used a silly method to put the SRB in the IO_STACK_LOCATION. :-(
  • Anonymous
    April 21, 2010
    I need help to remove the veil. I am trying to get more detailed access to a disk from an upper filter to the class. I get get the various io properties from the class, but can't get at the details like "task file registers" to solicit some detailed info from specific disks. The above code gives me the promise of communicating with this driver, but I simply can't figure out how to connect the dots. (either srb based or CF/ATA) I guess the questions are as follows:
  1. Should I assume that the ata minifilter is running on an XP system    or should I be using the srb and miniscsi interface?
  2. Is the GetIoTarget going to get me the correct resource for this    operation? (assuming I get the device from pnp D0Entry or like callback in the filter driver)
  3. What IOCTL major and minor codes do I use if any?
  4. Are there any more or less veiled KMDF calls or the like that I   have not run across yet? Thanks!