Windows 驱动开发基础(七)WDM设备的基本结构

来源:互联网 发布:p2p监控软件 哪个最好 编辑:程序博客网 时间:2024/04/29 02:11

Windows 驱动开发基础系列,转载请标明出处:http://blog.csdn.net/ikerpeng/article/details/38822657


WDM驱动即是一种即插即用的驱动。


在WDM的结构中,一个设备对象有两部分组成:(1)物理设备对象PDO,(2)功能设备对象FDO。

当电脑上插上某个设备时,PDO会有总线驱动自动的创建。这个时候电脑也会提示安装驱动,这里需要安装的驱动就负责创建FDO并附加到PDO上面去。当一个FDO被附加到一个PDO上面的时候,就可以PDO的子域AttachedDevice就会记录FDO的位置。如下图所示:

                   

同时 FDO和PDO之间也存在着过滤驱动,同时这也构成了WDM型的驱动的分层结构!

                           


在WDM驱动里面也是包括了这几个方面:DriverEntry函数,AddDevice函数,DriverUnload函数以及对IRP_MN_REMOVE_DEVICE IRP。

DriverEntry:

extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,                                                                           IN PUNICODE_STRING pRegistryPath){         KdPrint(("Enter DriverEntry\n"));         //设置AddDevoce函数         pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;         //设置各个IRP的派遣函数         pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;         pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =         pDriverObject->MajorFunction[IRP_MJ_CREATE] =         pDriverObject->MajorFunction[IRP_MJ_READ] =         pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMDispatchRoutine;         //设置卸载例程pDriverObject->DriverUnload = HelloWDMUnload;          KdPrint(("Leave DriverEntry\n"));         return STATUS_SUCCESS;}


其中增加了对AddDevice函数的设置。因为WDM驱动是被动加载的,所以和NT不一样。但是创建设备对象已经不再这里了,而是在AddDevice里面。


AddDevice:


NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,                           IN PDEVICE_OBJECT PhysicalDeviceObject){         PAGED_CODE();         KdPrint(("Enter HelloWDMAddDevice\n"));          NTSTATUS status;         PDEVICE_OBJECT fdo;         UNICODE_STRING devName;         RtlInitUnicodeString(&devName,L"\\Device\\MyWDMDevice");/创建设备对象/         status = IoCreateDevice(                   DriverObject,                   sizeof(DEVICE_EXTENSION),                   &(UNICODE_STRING)devName,                   FILE_DEVICE_UNKNOWN,                   0,                   FALSE,                   &fdo);         if( !NT_SUCCESS(status))                   return status;         //      获取设备扩展结构PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;         pdx->fdo = fdo;         //将FDO附加到PDO上,并且返回PDO对象         pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);         UNICODE_STRING symLinkName;         RtlInitUnicodeString(&symLinkName,L"\\DosDevices\\HelloWDM");                 pdx->ustrDeviceName = devName;         pdx->ustrSymLinkName = symLinkName;         //创建符号链接         status= IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName,&(UNICODE_STRING)devName);          if( !NT_SUCCESS(status))         {                   IoDeleteSymbolicLink(&pdx->ustrSymLinkName);                   status = IoCreateSymbolicLink(&symLinkName,&devName);                   if( !NT_SUCCESS(status))                   {                            return status;                   }         }         //设置设备标志         fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;         fdo->Flags &= ~DO_DEVICE_INITIALIZING;          KdPrint(("Leave HelloWDMAddDevice\n"));         return STATUS_SUCCESS;}

函数可以以任意命名,在AddDevice里面可以分为如下几步:

1.       AddDevice中通过IoCreateDevice等函数,创建设备对象,该设备对象就是FDO

2.       创建完FDO后,需要将FDO的地址保存下来,保存的位置就是在设备扩展中。

3.       驱动程序将创建的FDO附加到PDO上,依靠IoAttachDeviceToDeviceStack函数实现。附加以后,该函数返回附加设备的下层驱动。如果中间没有过滤驱动的话,返回的就是PDO,否则返回过滤驱动。

我们可以把该返回地址记录在设备扩展结构中,以此可以访问FDO的下层设备。

4.       设置FDO设备的Flags子域。DO_BUFFERED_IO是定义设备为“缓冲内存设备”,另外~DO_DEVICE_INITIALIZING,是将Flags上的DO_DEVICE_INITIALIZING位清零。保证设备初始化完毕,这一步是必须的。


DriverUnload:

主要负责收回申请的内存(卸载设备的操作在下一步)。


IRP_MN_REMOVE_DEVICE IRP:


IRP_MN_REMOVE_DEVICE这个IRP是当设备需要被卸载的时候,由即插即用管理器创建,并发送到驱动程序中。IRP一般有两个号码指定该IRP的具体意义。一个是主版本号(Major IRP),另一个是辅IRP号(Minor IRP).

当设备被卸载的时候,会先后发送多个IRP_MJ_PNP。这些IRP的辅IRP号会有所不同。其中之一就是IRP_MN_REMOVE_DEVICE.

在WDM驱动程序中,对设备的卸载一般都是在IRP_MN_REMOVE_DEVICE的处理函数中进行卸载。

例如:

NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp){PAGED_CODE();KdPrint(("Enter HandleRemoveDevice\n"));//设置IRP的完成状态Irp->IoStatus.Status = STATUS_SUCCESS;//将IRP请求向底层驱动转发NTSTATUS status = DefaultPnpHandler(pdx, Irp);//删除符号链接IoDeleteSymbolicLink(&(UNICODE_STRING)pdx->ustrSymLinkName);  //调用IoDetachDevice()把fdo从设备栈中脱开: if (pdx->NextStackDevice)     IoDetachDevice(pdx->NextStackDevice); //删除fdo: IoDeleteDevice(pdx->fdo);KdPrint(("Leave HandleRemoveDevice\n"));return status;}


在处理IRP_MN_REMOVE_DEVICE的函数中,它的功能类似于NT驱动中的DriverUnload函数。除了删除设备,取消符号链接外,此函数中还需要将FDO从PDO上的堆栈中摘除下来。使用IoDetachDevice:

VOID 
IoDetachDevice(

IN OUT PDEVICE_OBJECT  TargetDevice
);

TargetDevice 此时,FDO从设备链上被删除,但是PDO还是存在的。PDO的删除不是由程序员负责,而是由操作系统负责。



参考文献:

《 Windows 驱动开发技术详解 》

  http://mzf2008.blog.163.com/blog/static/35599786201011973648864/

0 0
原创粉丝点击