借助Microsoft提供的kmdf框架,来理解一下驱动的一般流程.
驱动的开发流程
1 驱动程序入口函数
DriverEntry 驱动程序的入口函数。 类似于 main函数
程序的标准入口函数是DriverEntry
与WDM不同,WDF的DriverEntry只负责创建和初始化WDFDRIVER对象,告诉WDF框架处理添加新设备连接的回调函数。
NTSTATUS DriverEntry(PDRIVER-OBJECT DriverObj,PUNICODE-STRING RegistryPath)
{
NTSTATUSstatus;
WDF-DRIVER-CONFIGconfig;
WDFDRIVER hDriver;
//初始化驱动配置结构,指定设备添加事件callback
WDF-DRIVER-CONFIG-INIT-NO-CONSTRAINTS(&config,MyEvtDeviceAdd);
//创建WDFDRIVER对象
status=WdfDriverCreate(DriverObj, RegistryPath,WDF-NO-OBJECT-ATTRIBUTES,
&config,//指向config结构的指针
NULL
);
return(status);
}
每当有新设备连接到系统时,WDF框架自动调用
EvtDeviceAdd设备添加callback。该回调函数初始化PNP和电源管理相关结构,设置相应的事件处理callbacks,然后创建WDFDEVICE对象和符号连接,初始化请求队列、中断处理等相关结构,设置相应的回调函数。
WDFSTATUSMyEvtDeviceAdd(WDFDRIVER Driver,PWDFDEVICE-INITDeviceInit)
{
WDFSTATUSstatus=STATUS-SUCCESS;
WDF-PNPPOWER-EVENT-CALLBACKS pnpPowerCallbacks;
WDF-OBJECT-ATTRIBUTES objAttributes;
WDFDEVICEdevice;
PMY-DEVICE-CONTEXTdevContext;
WDF-IO-QUEUE-CONFIGioCallbacks;
WDF-INTERRUPT-CONFIG interruptConfig;
//初始化PnPPowerCallbacks,设置与PnP和电源管理相关
的事件回调函数
WDF-PNPPOWER-EVENT-CALLBACKS-INIT(&pnpPowerCallbacks);
pnpPowerCallbacks.EvtDevicePrepareHardware=MyEvtPrepareHardware;
pnpPowerCallbacks.EvtDeviceReleaseHardware=MyEvtReleaseHardware;
pnpPowerCallbacks.EvtDeviceD0Entry=MyEvtDeviceD0Entry;
pnpPowerCallbacks.EvtDeviceD0Exit=MyEvtDeviceD0Exit;
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit,pnpPowerCallbacks);
//创建设备对象,初始化相关的属性
WDF-OBJECT-ATTRIBUTES-INIT(&objAttributes);
WDF-OBJECT-ATTRIBUTES-SET-CONTEXT-TYPE(&objAttributes,MY-DEVICE-CONTEXT);
//命名设备
status=WdfDeviceInitUpdateName(DeviceInit,L“\\de2vice\\WDFDEMO”)
;if(!NT-SUCCESS(status))return(status);
status=WdfDeviceCreate(&DeviceInit,&objAttributes,&device);
if(!NT-SUCCESS(status))return(status);
devContext=MyGetContextFromDevice(device);
devContext->WdfDevice=device;
//创建符号连接
status=WdfDeviceCreateSymbolicLink(device,L“\\Dos2Devices\\WDFDEMO”);
if(!NT-SUCCESS(status))
return(status);//创建和初始化请求队列
WDF-IO-QUEUE-CONFIG-INIT(&ioCallbacks,WdfIo2QueueDispatchSerial,WDF-NO-EVENT-CALLBACK,WDF-NO-EVENT-CALLBACK);
ioCallbacks.EvtIoDeviceControl=MyEvtDeviceControlIoctl;
status=WdfDeviceCreateDefaultQueue(device,&ioCallbacks,WDF-NO-OBJECT-ATTRIBUTES,NULL);
if(!NT-SUCCESS(status))return(status);
//创建和初始化中断对象,MyIsr和MyDpc分别是中断服务例程和DPC例程
WDF-INTERRUPT-CONFIG-INIT(&interruptConfig,FALSE,MyIsr,MyDpc);
interruptConfig.EvtInterruptEnable=MyEvtInterruptEnable;//中断使能
interruptConfig.EvtInterruptDisable=MyEvtInterruptDisable;//中断禁止
status=WdfInterruptCreate(device,&interruptConfig,&objAttributes,&devContext->WdfInterrupt);
//一些其他初始化操作(略)
return(status);}
开发WDF驱动程序下一步的工作就是编写各事件处理回调函数,当相应事件发生时,WDF框架会自动调用指定的回调函数进行处理。其中EvtDevicePrepareHardware回调函数在分配资源的时候被调用,框架将分配给设备的资源传递给回调函数,回调函数保存需要的资源,将共享内存映射到内核虚拟地址空间。与此对应的是EvtDeviceReleaseHardware回调函数,每当设备释放所占用的资源时,框架都将调用它。
EvtDeviceD0Entry和EvtDeviceD0Exit事件callbacks则分别在设备即将进入和离开D0电源状态时调用。EvtIoDeviceControl,EvtIoRead,EvtIoWrite等回调函数分别用来处理DeviceControl,Read,WriteI/O请求。当框架获得一个I/O请求时,它首先确定该请求应该放入哪个请求队列。如果驱动程序没有提供指定的队列,WDF框架默认将请求放入缺省请求队列会自动调用对应的回调函数。然后,框架寻找处理该请求的回调函数,如果驱动程序提供了相应的callback,则调用它处理请求。对于没有指定回调函数的I/O请求,WDF调用EvtIoStart回调函数处理。如果EvtIoStartcallback也不存在,框架将返回STATUS-NOT-SUPPORTED。
设备中断的管理由EvtInterruptEnablecallback、EvtInterruptDisablecallback、中断服务例程(ISR)和DpcForISr例程实现。WDF框架在调用EvtDeviceD0Entrycallback和注册ISR后,通过调用EvtInterruptEnable回调函数使能设备中断;而EvtInterruptDisable回调函数则在设备离开D0状态,EvtDeviceD0Exitcallback调用前获得调用,完成禁止设备中断的工作。此外,中断服务例程和DpcForIsr例程具体完成中断服务的功能,与WDM驱动程序相似。
2 创建驱动对象
WdfDriverCreate根据给定的属性配置创建driver对象.特别注意的是 这里设置了SampleDriverEvtDeviceAdd函数,也就是创建driver时,会调用的 用于添加设备对象的方法。
3 创建设备对象
SampleDriverEvtDeviceAdd //这个是wdfDriverCreate时自己指定的
在 SampleDriverEvtDeviceAdd函数中通过调用WdfDeviceCreate函数,根据配置的属性创建对应的设备
4 创建设备接口
WdfDeviceCreateDeviceInterface创建一个唯一的接口(UUID)用于应用层程序通信,调用驱动层接口。
5 创建驱动处理队列
SampleDriverQueueInitialize
The I/O dispatchcallbacks for the frameworks device object
are configured in this function.
A single default I/O Queue is configuredfor parallel request
processing, and a driver context memoryallocation is created
to hold our structureQUEUE_CONTEXT.
WdfIoQueueCreate创建一个事件队列用于处理驱动 I/O事件。需要注意的是,创建时
可以通过配置回调函数,来处理自己关心的那些事件。另外注意配置队列的分发方式:WdfIoQueueDispatchParallel
6 处理驱动的事件
SampleDriverEvtIoDeviceControl函数是在配置队列时设置的 用于处理IO控制的回调函数。大部分设备接口都是在这个函数中完成的。
7 驱动的卸载回调
驱动的卸载时调用的函数是 在driver创建时设置的,如果没有需要 可以不设置的。
attributes.EvtCleanupCallback。