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

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


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


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

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

                   Windows 驱动开发基础(七)WDM设备的基本结构_第1张图片

同时 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/

你可能感兴趣的:(windows,驱动开发,7)