记录一个驱动队列使用遇到的问题
1、背景
按照 《Windows设备驱动程序WDF开发》教程第三章,敲写代码,中间按照自己的理解调整了些代码结构。我们来看样例中device.c中这个函数的实现如下:(这个在初始化设备的时候把创建队列的具体实现逻辑包含进来来了,个人认为应该把创建队列的实现放到queue.c 中。)
NTSTATUS
QueueSample_EvtDeviceAdd(IN WDFDRIVER Driver,IN PWDFDEVICE_INIT DeviceInit)
{NTSTATUS status;WDF_OBJECT_ATTRIBUTES deviceAttributes;WDFDEVICE device;PDEVICE_CONTEXT pDeviceContext;WDF_IO_QUEUE_CONFIG ioQueueConfig;WDFQUEUE queue;PAGED_CODE();//初始化设备对象环境变量结构WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);//// Set WDFDEVICE synchronization scope. By opting for device level// synchronization scope, all the queue and timer callbacks are// synchronized with the device-level spinlock.////采用设备对象范围下的同步,因为I/O调用例程、取消例程和//定时器回调例程都要用到设备对象环境变量,需要实现//对其的保护性访问deviceAttributes.SynchronizationScope = WdfSynchronizationScopeDevice;status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);if (!NT_SUCCESS(status)) {return status;}//获取设备对象环境变量结构地址指针pDeviceContext = GetDeviceContext(device);//创建一个一次性的定时器status = QueueSample_TimerCreate(&pDeviceContext->Timer, device);if (!NT_SUCCESS(status)) {DbgPrint("QueueSample: Error creating timer 0x%x\n",status);return status;}//初始化非缺省队列配置,设置I/O请求分发处理方式为串行WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, WdfIoQueueDispatchSequential);//设置EvtIoDeviceControl例程,处理应用程序的DeviceIoControl()函数调用ioQueueConfig.EvtIoDeviceControl = QueueSample_EvtIoDeviceControl;//创建非缺省串行队列status = WdfIoQueueCreate(device, &ioQueueConfig,WDF_NO_OBJECT_ATTRIBUTES, &queue);if (!NT_SUCCESS(status)) {return status;}//对于非缺省队列,必须指定I/O请求类型//这里指定WdfRequestTypeDeviceControl,//是因为该队列要处理DeviceControl例程status = WdfDeviceConfigureRequestDispatching(device,queue,WdfRequestTypeDeviceControl);if(!NT_SUCCESS (status)){return status;}//初始化非缺省队列配置,设置I/O请求分发处理方式为手工WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig,WdfIoQueueDispatchManual);//禁止队列电源管理ioQueueConfig.PowerManaged = WdfFalse;//设置手工队列中的I/O请求取消例程ioQueueConfig.EvtIoCanceledOnQueue = QueueSample_EvtRequestCancel;//创建非缺省手工队列status = WdfIoQueueCreate(device,&ioQueueConfig,WDF_NO_OBJECT_ATTRIBUTES,&pDeviceContext->Queue);if (!NT_SUCCESS(status)) {return status;}status = WdfDeviceCreateDeviceInterface(device, (LPGUID) &QueueSample_DEVINTERFACE_GUID, NULL);if (!NT_SUCCESS(status)) {}return status;
}
于是乎我调整了对应的函数代码:
NTSTATUS
QueueSampleCreateDevice(_Inout_ PWDFDEVICE_INIT DeviceInit)
/*++Routine Description:Worker routine called to create a device and its software resources.Arguments:DeviceInit - Pointer to an opaque init structure. Memory for thisstructure will be freed by the framework when the WdfDeviceCreatesucceeds. So don't access the structure after that point.Return Value:NTSTATUS--*/
{WDF_OBJECT_ATTRIBUTES deviceAttributes;PDEVICE_CONTEXT deviceContext;WDFDEVICE device;NTSTATUS status;PAGED_CODE();WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);deviceAttributes.SynchronizationScope = WdfSynchronizationScopeDevice;status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);if (NT_SUCCESS(status)) {//// Get a pointer to the device context structure that we just associated// with the device object. We define this structure in the device.h// header file. DeviceGetContext is an inline function generated by// using the WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro in device.h.// This function will do the type checking and return the device context.// If you pass a wrong object handle it will return NULL and assert if// run under framework verifier mode.//deviceContext = DeviceGetContext(device);//// Initialize the context.//status = QueueSample_TimerCreate(&deviceContext->Timer, device);if (!NT_SUCCESS(status)){TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "QueueSample_TimerCreate failed %!STATUS!", status);return status;}//创建一个非缺省的手工队列status = QueueSampleQueueInitialize(device, &deviceContext->Queue);if (!NT_SUCCESS(status)) {TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "QueueSampleQueueInitialize failed %!STATUS!", status);return status;}//// Create a device interface so that applications can find and talk// to us.//status = WdfDeviceCreateDeviceInterface(device,&GUID_DEVINTERFACE_QueueSample,NULL // ReferenceString);if (!NT_SUCCESS(status)) {TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "WdfDeviceCreateDeviceInterface failed %!STATUS!", status);return status;}}return status;
}
而且把QueueSampleQueueInitialize 这个函数的实现放到了queue.c 中,同样初始化了device的设备上下文。
NTSTATUS
QueueSampleQueueInitialize(_In_ WDFDEVICE Device,_Out_ WDFQUEUE* Queue)
/*++Routine Description:Creates the I/O queues for this device.First creates a default queue to receive DeviceControl requests,then creates a manual queue for deferred processing.Arguments:Device - Handle to a framework device object.Queue - Pointer to receive the manual queue handle.Return Value:NTSTATUS--*/
{NTSTATUS status;WDF_IO_QUEUE_CONFIG queueConfig;WDFQUEUE defaultQueue;PAGED_CODE();//// Create a manual queue for deferred processing//WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,WdfIoQueueDispatchManual);//该队列不随设备的电源状态变化而变化queueConfig.PowerManaged = WdfFalse;queueConfig.EvtIoDeviceControl = QueueSample_EvtIoDeviceControl;queueConfig.EvtIoCanceledOnQueue = QueueSample_EvtRequestCancel;status = WdfIoQueueCreate(Device,&queueConfig,WDF_NO_OBJECT_ATTRIBUTES,Queue);if(!NT_SUCCESS(status)) {TraceEvents(TRACE_LEVEL_ERROR, TRACE_QUEUE, "WdfIoQueueCreate for manual queue failed %!STATUS!", status);return status;}TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_QUEUE, "Both default and manual queues created successfully");return status;
}
于是乎,我的Copy版本的QueueSample驱动就编译写好了,心里美滋滋的,好家伙,我也可以优化教程上的代码了。不出意外,意外出现了。当我安装驱动的时候,虽然安装的时候成功了,却启动不起来,具体现象描述如下:
这真是赢沟里翻了船,我就调整了下代码结构,这就出大事了!!!!!,一脸懵逼。
于是乎我去仔细读书上的例程,这个Example主要演示将WDFQueue从一个队列转发到另一个手工队列,通过创建WDFTimer来手工处理这个手工队列的WdfRequest。明白了这个目标,再去看我调整的代码。好家伙:我的device的IO处理请求处理队列也是用的我的创建的手工队列。
问题根源:在 WDF 中,每个设备必须至少有一个队列来接收 I/O 请求。如果你不创建默认队列,系统不知道如何将 DeviceControl 请求路由到你的驱动程序,这会导致 STATUS_INVALID_PARAMETER 错误。
所以调整后的代码如下:
NTSTATUS
QueueSampleQueueInitialize(_In_ WDFDEVICE Device,_Out_ WDFQUEUE* Queue)
/*++Routine Description:Creates the I/O queues for this device.First creates a default queue to receive DeviceControl requests,then creates a manual queue for deferred processing.Arguments:Device - Handle to a framework device object.Queue - Pointer to receive the manual queue handle.Return Value:NTSTATUS--*/
{NTSTATUS status;WDF_IO_QUEUE_CONFIG queueConfig;WDFQUEUE defaultQueue;PAGED_CODE();//在 WDF 中,每个设备必须至少有一个队列来接收 I / O 请求。// 如果你不创建默认队列,系统不知道如何将 DeviceControl 请求路由到你的驱动程序// 这会导致 STATUS_INVALID_PARAMETER 错误。// //// Create a default queue to receive DeviceControl requests//WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig,WdfIoQueueDispatchSequential);queueConfig.EvtIoDeviceControl = QueueSample_EvtIoDeviceControl;status = WdfIoQueueCreate(Device,&queueConfig,WDF_NO_OBJECT_ATTRIBUTES,&defaultQueue);if (!NT_SUCCESS(status)) {TraceEvents(TRACE_LEVEL_ERROR, TRACE_QUEUE, "WdfIoQueueCreate for default queue failed %!STATUS!", status);return status;}//// Create a manual queue for deferred processing//WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,WdfIoQueueDispatchManual);//该队列不随设备的电源状态变化而变化queueConfig.PowerManaged = WdfFalse;queueConfig.EvtIoCanceledOnQueue = QueueSample_EvtRequestCancel;status = WdfIoQueueCreate(Device,&queueConfig,WDF_NO_OBJECT_ATTRIBUTES,Queue);if(!NT_SUCCESS(status)) {TraceEvents(TRACE_LEVEL_ERROR, TRACE_QUEUE, "WdfIoQueueCreate for manual queue failed %!STATUS!", status);return status;}TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_QUEUE, "Both default and manual queues created successfully");return status;
}
由于之前的驱动已经安装了,我们修改后需要先将驱动卸载再继续安装,实验成功。
这编文章只列出了我调整的KMDF代码块儿。纸上得来终觉浅,绝知此事要躬行! 发现问题,才能解决问题,有所收获!