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:
Mode | Description |
---|---|
Sequential | Requests handled one at a time |
Parallel | Requests dispatched as they arrive (multi-threaded) |
Manual | Requests 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 *