驱动程序的入口函数是DriverEntry,这个函数在驱动程序被加载的时候,会被操作系统执行一次。利用KMDF程序框架生成的工程中,Driver.C文件中,就包含了这个程序入口。
值得一提的是:#include "driver.tmh";由于*.tmh不是C/C++标准头文件,加上driver.tmh是编译时才生成;所以driver.tmh中所涉及的内容,在编辑Driver.C文件时,是无法被VS识别的;这也是诸如TraceEvents等内容不能被编辑器识别,但是编译却能成功的原因所在。
如果实在不愿意使用TraceEvents,也可以使用kdPrint,用法如下:KdPrint(("create device done!\n"));
DriverEntry的主要任务就是初始化:
1, 初始化WDF_DRIVER_CONFIG:WDF_DRIVER_CONFIG_INIT(&config,KMDFPCIEvtDeviceAdd);
系统在添加设备时,在进行设备枚举时,调用...DeviceAdd函数;来初始化WDF_DRIVER_CONFIG结构。
2, 创建WDFDRIVER对象:
status = WdfDriverCreate(DriverObject,RegistryPath,&attributes,&config, &driver);
NTSTATUS
DriverEntry(_In_ PDRIVER_OBJECT DriverObject,_In_ PUNICODE_STRING RegistryPath)
{
NTSTATUS status= STATUS_SUCCESS;
WDF_DRIVER_CONFIG config;
WDFDRIVER driver;
WDF_OBJECT_ATTRIBUTES attributes;
WPP_INIT_TRACING( DriverObject, RegistryPath );
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.EvtCleanupCallback = KMDFPCIEvtDriverContextCleanup;
WDF_DRIVER_CONFIG_INIT(&config,KMDFPCIEvtDeviceAdd);
status = WdfDriverCreate(DriverObject,RegistryPath,&attributes,&config, &driver);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfDriverCreate failed %!STATUS!", status);
WPP_CLEANUP(DriverObject);
return status;
}
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit");
return status;
}
...DeviceAdd回调函数,在设备枚举时系统自动执行。DeviceAdd函数主要执行一下操作:
1,创建并初始化WDFDEVICE对象的Context,这是一个FDO_DATA结构,可以在.h头文件中定义
typedef struct _FDO_DATA
{
ULONG Signature;
WDFDEVICE WdfDevice; // device object.
} FDO_DATA, *PFDO_DATA;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_DATA, FdoGetData)
宏WDF_DECLARE_CONTEXT_TYPE_WITH_NAME创建该类型的访问方法 。之后,可以在DeviceAdd函数中调用WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&fdoAttributes, FDO_DATA);来指定Context和对象之间的关联。
接下来, 创建Device对象:status = WdfDeviceCreate(&DeviceInit, &fdoAttributes, &device);
获得Context指针:fdoData = FdoGetData(device);
2,为PNP和电源管理回调函数设置程序入口
3,创建设备接口
status = WdfDeviceCreateDeviceInterface(device,(LPGUID) &GUID_DEVINTERFACE_KMDFPCI,NULL);
4,创建、配置I/O队列
使用默认IO队列:WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE( &ioQueueConfig, WdfIoQueueDispatchSequential);
两个参数:WDF_IO_QUEUE_CONFIG结构(ioQueueConfig)的指针;WDF_IO_QUEUE_DISPATHCH_TYPE枚举。
通过指定WdfIoqueueDispatchParallel驱动程序指示IO请求到达,KMDF就从队列调用IO请求。驱动程序可以并行处理多个请求 。
在配置IO队列以后,还需要注册IO回调函数,来支持读、写和IO控制请求;接下来在创建WDFQUEUE对象。
ioQueueConfig.EvtIoRead = PciEventRead;
ioQueueConfig.EvtIoWrite = PciEventWrite;
ioQueueConfig.EvtIoDeviceControl = PciEventDeviceControl;
status = WdfIoQueueCreate( device,&ioQueueConfig,WDF_NO_OBJECT_ATTRIBUTES,&queue);
KMDF只处理驱动程序 配置的队列请求,如果接收到其他请求,则会抛出STATUS_INVALID_DEVICE_REQUEST。
如果驱动程序支持缓冲或者直接IO请求,则可以使用Request的始发者传递的缓冲区,或者其代表的WDFMEMORY对象。使用WDFMEMOY对象,WDF框架会自动处理验证(句柄、Buffer大小)和寻址问题,确保缓冲区不会益处。
读:获得WDFMEMORY句柄:WdfRequestRetriveOutputMemory
驱动程序 获得数据 ,在从内部缓冲区复制到相关的WDFMEMORY对象中:WdfMemoryCopyFromBuffer
写:获得WDFMEMORY句柄:WdfRequestRetriveInputMemory
一般使用EdfMemoryCopyToBuffer,把(WDFMEMORY对象中)数据复制到输出缓冲区 。如果溢出,则返回错误代码。
如果需要使用缓冲区 指针,则使用WdfRequestRetriveInputBuffer,获取指针,返回需要写入的字节数;或者WdfMemoryGetBuffer获取指针和写入的字节数。
在驱动程序完满足IO请求后,他会调用WdfRequestCompleteWithInformation,使用指定的状态完成底层IO请求并传递需要读写的字节数。
5,(如果需要)创建中断对象