CLOSE
Updated on 24 Jul, 202510 mins read 10 views

After initializing the KMDF driver and creating the device object, the next critical step is to make your driver interact with user-mode applications. This happens through I/O requests – Create, Close, Read, Write, and sometimes Device Control (IOCTL).

1 I/O Request Lifecycle in KMDF

When a user-mode application interacts with your driver (e.g., via CreateFile(), ReadFile(), or WriteFile(), the Windows I/O Manager generates IRPs (I/O Requests Packets) and delivers them to your driver.

KMDF queues receive these IRPs and route them to callback functions your register.

2 Registering Create and Close Handlers

These two handlers are used to manage file open and close events, such as when an app opens your device using CreateFile().

Registering the Callbacks:

WDF_FILEOBJECT_CONFIG fileConfig;
WDF_FILEOBJECT_CONFIG_INIT(
    &fileConfig,
    EvtDeviceFileCreate,
    EvtFileClose,
    WDF_NO_EVENT_CALLBACK
);
WdfDeviceInitSetFileObjectConfig(DeviceInit, &fileConfig, WDF_NO_OBJECT_ATTRIBUTES);

Example Callbacks:

VOID EvtDeviceFileCreate(WDFDEVICE Device, WDFREQUEST Request, WDFFILEOBJECT FileObject) {
    KdPrint(("Device opened\n"));
    WdfRequestComplete(Request, STATUS_SUCCESS);
}

VOID EvtFileClose(WDFFILEOBJECT FileObject) {
    KdPrint(("Device closed\n"));
}

3 Setting Up the I/O Queue

KMDF uses I/O queues to manage Read, Write, and DeviceIoControl operations. You create a default queue and register callbacks for the I/O operations you want to support.

Queue Setup:

WDF_IO_QUEUE_CONFIG queueConfig;
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchSequential);

queueConfig.EvtIoRead = EvtIoRead;
queueConfig.EvtIoWrite = EvtIoWrite;

WdfIoQueueCreate(device, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, &queue);

4 Handling Read and Write Requests

Once your queue is set up, you define how your driver responds to user-mode ReadFile(), or WriteFile() calls.

EvtIoWrite Example:

VOID EvtIoWrite(WDFQUEUE Queue, WDFREQUEST Request, size_t Length) {
    PVOID buffer;
    WdfRequestRetrieveInputBuffer(Request, Length, &buffer, NULL);
    KdPrint(("Write received: %s\n", (char*)buffer));
    WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, Length);
}

EvtIoRead Example:

VOID EvtIoRead(WDFQUEUE Queue, WDFREQUEST Request, size_t Length) {
    PVOID buffer;
    const char* message = "Hello from kernel!";
    size_t msgLen = strlen(message) + 1;

    WdfRequestRetrieveOutputBuffer(Request, msgLen, &buffer, NULL);
    RtlCopyMemory(buffer, message, msgLen);

    WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, msgLen);
}

I/O Dispatch Modes

KMDF queues support three main dispatch modes:

ModeDescription
SequentialRequests handled one at a time
ParallelRequests dispatched as they arrive (multi-threaded)
ManualRequests held in queue, you dequeue explicitly

Sequential is safest and simplest for beginners.

 

Leave a comment

Your email address will not be published. Required fields are marked *