注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

从C开始

 
 
 

日志

 
 

WDM式驱动程序的基本结构  

2010-12-09 19:36:48|  分类: 驱动编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

WDM式驱动程序的基本结构

         对于WDM驱动程序来说,一般都是基于分层的。也就是说,完成一个设备的操作,至少要由两个驱动设备共同完成。

 

1. 物理设备对象与功能设备对象

WDM模型中,完成一个设备的操作,至少要有两个设备对象共同完成。其中,一个是物理设备对象(Physical Device Object,即PDO),另一个是功能设备对象(Function Device Object,即FDO)。其关系是“附加”与“被附加”的关系。

PC插入某个设备时,PDO是由总线驱动创建的。PDO不能单独操作设备,需要配合FDO一起使用。系统会提示检测到新设备,要求安装设备驱动程序。需要安装的驱动程序指的就是WDM程序,此程序负责创建FDO,并且附加到PDO之上。

当一个FDO附加到PDO上的时候,PDO设备对象的子域AttachedDevice会记录FDO的位置。PDO被称作是底层驱动或者下层驱动,而FDO被称为是高层驱动或者上层驱动。这里的上层指的是接近发出I/O请求的地方,而下层指的是靠近物理设备的地方。

WDM式驱动程序的基本结构 - Fly - 从C开始
 

         这是最简单的一种情况,事实要比这个复杂一些。在FDOPDO之间还会存在过滤驱动。在FDO上面的过滤驱动被称作上层过滤驱动。在FDO下层的驱动,被称作下层过滤驱动。另外,每个设备对象中,有个StackSize子域,表明操作这个设备对象需要几层才能到达最下层的物理设备。

WDM式驱动程序的基本结构 - Fly - 从C开始
 

 

 

2.WDM驱动的入口函数

         NT驱动一样,WDM驱动的入口函数也是DriverEntry,但是初始化作用被分散到其他例程中。例如,创建设备对象的责任就被放在了AddDevice例程中。同时,在DriverEntry中,需要设置对IRP_MJ_PNP处理的派遣函数。

如下面的代码:

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;

}

 

从上述的代码中可以看出,WDM驱动的DriverEntryNT式驱动的DriverEntry有以下几点不同:

1.       增加了对AddDevice函数的设置。因为NT驱动是主动加载设备的,也就是驱动一旦加载就创建设备。而WDM驱动是被动加载设备的。操作系统必须加载PDO以后,调用驱动程序的AddDevice例程,AddDevice例程负责创建FDO,并且附加到PDO之上。

2.       创建设备对象已经不在这个函数中了,而在AddDevice例程中创建。

3.       必须加入IRP_MJ_PNP的派遣回调函数。IPR_MJ_PNP主要负责计算机中即插即用的处理,在WDM驱动中加入了很多即插即用的处理。

 

 

3.WMD驱动的AddDevice例程

AddDevice例程用来创建驱动的设备对象。在DriverEntry中,需要设置AddDevice例程的函数地址。设置的方式如下:

pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;

 

实例代码如下:

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函数有两个参数,一个是驱动对象DriverObject,另一个是设备对象PhysicalDeviceObject。驱动对象是I/O管理器创建的驱动对象。设备对象是由底层总线驱动创建的PDO设备对象。传进该参数的目的就是让我们创建的FDO附加到PDO之上。

 

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位清零。保证设备初始化完毕,这一步是必须的。

 

 

4.DriverUnload例程

       NT驱动中,DriverUnload例程主要负责做删除设备和取消符号链接。而在WDM驱动中,这部分操作被IRP_MN_REMOVE_DEVICE IRP处理函数所负责。而DriverUnload例程显得变得相对简单。如果在DriverEntry中有申请内存的操作,可以在DriverUnload例程中回收这些内存。

        

 

5.      IRP_MN_REMOVE_DEVICE的处理

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函数。除了删除设备,取消符号链接外,此函数中还需要将FDOPDO上的堆栈中摘除下来。使用IoDetachDevice

VOID 
IoDetachDevice(

IN OUT PDEVICE_OBJECT  TargetDevice
);

TargetDevice 指向下层堆栈的设备对象。

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

 

 

6. 驱动程序的层次结构

WDM式驱动程序的基本结构 - Fly - 从C开始
 

WDM式驱动程序的基本结构 - Fly - 从C开始
 

  评论这张
 
阅读(1612)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018