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;
    }