KMDF驱动程序模型:一个即插即用的KMDF驱动程序应该包含
一个DriverEntry例程
一个EvtDriverDeviceAdd例程
一个或者多个I/O队列
一个或者多个I/O回调例程
支持即插即用和电源管理回调例程
支持的WMI回调例程
其他回调例程,如对象清除,中断例程、DMA例程等
1.DriverEntry例程
DriverEntry例程负责驱动程序的初始化,类似linux下的module_init模块。所有驱动必须包含DriverEntry例程,,驱动程序第一次加载时,调用DriverEntry例程。在DriverEntry例程中,主要完成创建驱动对象和设置EvtDriverDeviceAdd例程地址。
驱动对象结构体WDFDRIVER需要配置结构体WDF_DRIVER_CONFIG用来完成初始化,WDF_DRIVER_CONFIG结构体如下:
typedef struct WDF_DRIVER_CONFIG{
ULONG Size;
PFN_WDF_DRIVER_DEVICE_ADD EvtDriverDeviceAdd;
PFN_WDF_DRIVER_UNLOAD EvtDriverUnload;
ULONG DriverInitFlags;
ULONG DriverPoolTag;
}
EvtDriverDeviceAdd: 设备对象添加例程
EvtDriverUnload : 设备对象卸载例程删除DriverEntry例程分配给驱动的资源
DriverInitFlags : 初始化标志位
驱动对象配置结构初始化函数如下
VOID
WDF_DRIVER_CONFIG_INIT(
OUT PWDF_DRIVER_CONFIG config,
IN PFN_WDF_DRIVER _DEVICE_ADD EvtDriverDeviceAdd,
);
驱动对象创建函数如下
NTSTATUS
wdfDriverCreate(
IN PDRIVER_OBJECT DRIVEROBJECT,
IN PCUNICODE_STRING RegistryPath,
IN OPTIONAL PWDF_OBJECT_ATTRIBUTES DriverAttributes,
IN PWDF_DRIVER_CONFIG Driver_Config,
OUT OPTIONAL WDFDRIVER* Driver
);
DriverAttributes :对象属性
KMDF驱动的一个基本DriverEntr例程示例
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
WDF_DRIVER_CONFIG config; // 配置信息
NTSTATUS status;
WDF_DRIVER_CONFIG_INIT(&config, EvtDeviceAdd);
status = WdfDriverCreate( DriverObject,RegistryPath,WDF_NO_OBJECT_ATTRIBUTES, &config,WDF_NO_HANDLE);
// 创建驱动对象
return status;
}
2.DeviceAdd例程
通过DriverEntry例程,内核完成驱动对象的创建和设置,并且将AddDeivce例程地址装入,在驱动初始化完成后,内核的Pnp管理器就会调用驱动程序的DeviceAdd例程来初始化驱动程序所控制的设备。在DeviceAdd历程中,驱动创建一个设备对象作为目标I/O设备,并将设备对象依附到设备堆栈中。当系统运行时,任何一个新设备枚举时,系统都将会调用DeviceAdd程序。
在KMDF驱动中,DeviceAddd需要完成以下任务
创建设备对象
一个或者多个I/O队列和设备GUID接口
设置各种事件的回调例程
设备对象创建函数如下
NTSTATUS
WdfDeviceCreate(
IN OUT PWDFDEVICE_INIT* DeviceInit,
IN OPTIONAL PWDF_OBJECT_ATTRIBUTES DeviceAttributes,
OUT WDFDEVICE* Device
);
创建设备GUID接口
NTSTATUS
WdfDeviceCreateDeviceInterface(
IN WDFDEIVCE device,
IN CONST GUID*InterfaceClassGUID,
IN OPTIONAL PCUNICODE_STRING ReferenceString
);
DeviceAdd例程示例
NTSTATUS
EvtDeviceAdd(
IN WDFDRIVER Driver,
IN PWDFDEVICE_INIT DeviceInit
)
{
NTSTATUS status;
WDFDEVICE device;
WDF_IO_QUEUE_CONFIG ioQueueConfig;
//例程的首句PAGED_CODE,表示该例程的代码占用分页内存。
//只能在PASSIVE_LEVEL中断级别调用该例程,否则会蓝屏。
//如不说明,则占用系统的非分页内存,要珍惜使用。
PAGED_CODE();
//创建设备,没有对象属性和设备对象环境变量结构
status = WdfDeviceCreate(&DeviceInit, WDF_NO_OBJECT_ATTRIBUTES, &device);
if (!NT_SUCCESS(status)) {
return status;
}
//初始化缺省队列配置,设置I/O请求分发处理方式为串行。
//对这个实例而言,选择串行或并行都可以,但不能选手工。
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, WdfIoQueueDispatchSequential);
//设置EvtIoDeviceControl例程,处理应用程序的DeviceIoControl()函数调用
ioQueueConfig.EvtIoDeviceControl = CharSample_EvtIoDeviceControl;
//创建队列
status = WdfIoQueueCreate(device, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, NULL);
if (!NT_SUCCESS(status)) {
return status;
}
//创建设备GUID接口
status = WdfDeviceCreateDeviceInterface(device, (LPGUID) &CharSample_DEVINTERFACE_GUID, NULL);
if (!NT_SUCCESS(status)) {
}
return status;
}
3. I/O处理例程
I/O处理例程完成驱动和应用之间的通信,包括Create、Close、Cleanup、Read、Write和DeviceControl
Read、Write和DeviceControl由队列管理,可以是默认队列,也可以由自己创建。
Create例程处理应用程序的CreateFile函数调用,Close例程处理应用程序的CloseHandle调用。
设置Creat、Close、Cleanup例程的结构体WDF_FILEOBJECT_CONFIG
typedef struct WDF_FILEOBJECT_CONFIG(
ULONG Size;
PFN_WDF_DEVICE_FILE_CREATE EvtDeviceFileCreate;
PFN_WDF_FILE_CLOSE EvtFileClose;
PFN_WDF_FILE_CLEANUP EvtFileCleanup;
WDF_TRI_STATE AutoForwordCleanupClose;
WDF_FILEOBJECT_CLASS FileObjectClass;
);
//配置该结构体的函数为:
VOID
WDP_FILEOBJECT_CONFIG_INIT(
OUT PWDF_FILEOBJECT_CONFIG FileEventCallbacks,
IN OPTIONAL PFN_WDF_DEVICE_FILE_CREATE EvtDeviceFileCreate,
IN OPTIONAL PFN_WDF_FILE_CLOSE EvtFileClose,
IN OPTIONAL PFN_WDF_FILE_CLEANUP EvtFileCleanu
);
//设置该结构体的函数:
VOID
WdfDeviceInitSetFileObjectConfig(
IN PWDFDEVICE_INIT DeviceInit,
IN PWDF_FILEOBJECT_CONFIG FileObjectConfig,
IN OPTIONAL PWDF_OBJECT_ATTRIBUTES FileObjectAttributes
);
Read、Write和DeviceControl分别处理应用程序的ReadFile、WriteFile和DeviceControl函数调用。
VOID
CharSample_EvtIoDeviceControl(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t OutputBufferLength,
IN size_t InputBufferLength,
IN ULONG IoControlCode
)
{
NTSTATUS status;
PVOID buffer;
CHAR n,c[]="零一二三四五六七八九";
PAGED_CODE();
switch(IoControlCode) {
case CharSample_IOCTL_800:
if (InputBufferLength == 0 || OutputBufferLength < 2)
{ //检查输入、输出参数有效性
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
}
else
{
//输入缓冲区地址可通过调用WdfRequestRetrieveInputBuffer函数获得
//输出缓冲区地址可通过调用WdfRequestRetrieveOutputBuffer函数获得
//获取输入缓冲区地址buffer
//要求1字节空间
status = WdfRequestRetrieveInputBuffer(Request, 1, &buffer, NULL);
if (!NT_SUCCESS(status)) {
WdfRequestComplete(Request, STATUS_UNSUCCESSFUL);
break;
}
//这里buffer表示输入缓冲区地址
//输入n=应用程序传给驱动程序的数字ASCII码
n = *(CHAR *)buffer;
if ((n>='0') && (n<='9'))
{ //若为数字,则处理
n-='0'; //n=数字(0-9)
//获取输出缓冲区地址buffer
status = WdfRequestRetrieveOutputBuffer(Request, 2, &buffer, NULL);
if (!NT_SUCCESS(status)) {
WdfRequestComplete(Request, STATUS_UNSUCCESSFUL);
break;
}
//这里buffer表示输出缓冲区地址
//输出:从中文数组c[]中取出对应的数字的中文码,拷贝到输出缓冲区
strncpy((PCHAR)buffer,&c[n*2],2);
//完成I/O请求,驱动程序传给应用程序的数据长度为2字节(一个中文)
WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, 2);
}
else //否则返回无效参数
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
}
break;
default :
status = STATUS_INVALID_DEVICE_REQUEST;
WdfRequestCompleteWithInformation(Request, status, 0);
break;
}
return;
}
对于大部分驱动,WDM的默认配置已经可以完成即插即用和电源管理,这些程序不需要关于即插即用和电源管理的编程处理
参考文献:Windows设备驱动程序WDM开发第二章KMDF驱动程序框架