WDM驱动程序的基本结构和实例-《Windows内核编程》
WDM驱动的基本结构分以下几部分进行阐述:
对于WDM驱动程序来说,一般都是基于分层的,即完成一个设备的操作,至少要由两个驱动设备共同完成。WDM驱动模型是建立在NT式驱动程序模型基础之上的。
1)物理设备对象和功能设备对象
物理设备对象(Physical Device Object,PDO)和功能设备对象(Function Device Object,FDO)的关系是“附加”与“被附加”的关系。
当PC插入某个设备时,PDO会自动创建。确切的说,是由总线驱动创建的。PDO不能单独操作设备,需要配合FDO一起使用。系统会提示检测到新设备,要求安装驱动程序。需要安装的驱动程序就是WDM程序,此驱动程序负责创建FDO,并且附加到PDO上。
当一个FDO附加到PDO上时,PDO设备对象的子域AttachedDevice会记录FDO的位置。PDO被称作底层驱动或下层驱动,而FDO被称作高层驱动或上层驱动。
复杂一点的情况是,在FDO和PDO之间还会存在过滤驱动。在FDO上面的过滤驱动被称作上层过滤驱动,在FDO下层的驱动被称作下层过滤驱动。每个设备对象中,有个StackSize子域,表明操作这个设备对象需要几层才能到达最下层的物理设备。
过滤驱动可以嵌套,即可以有很多个高层过滤驱动或多个底层过滤驱动。过滤驱动不是必须的,在WDM模型中PDO和FDO是必须的。
NT设备是被动装入的,例如当有设备插入PC时,系统不会有提示,用户需要自己指定加载何种驱动;而WDM驱动则不然,当插入设备时,系统会自动创建出PDO,并提示请求用户安装FDO。
2)WDM驱动的入口程序
和NT驱动一样也是DriverEntry,但是初始化作用被分散到其他例程中。如创建设备对象的工作被放在了AddService例程中,同时,在DriverEntry中,需要设置对IRP_MJ_PNP处理的派遣函数。
WDM驱动的DriverEntry和NT式驱动的DriverEntry有以下几点不同:
(1)增加了对AddService函数的设置;AddService例程负责创建FDO,并附加到PDO上。
(2)创建设备对象的操作不在这个函数中了,而转到了AddService例程中。
(3)必须加入IRP_MJ_PNP的派遣回调函数。IRP_MJ_PNP主要是负责计算机中即插即用的处理。
3)WDM驱动的AddService例程
AddService例程是WDM独有的。在DriverEntry中需设置AddService例程的函数地址,设置的方式是驱动对象中有个DriverExtension子域,DriverExtension中有个AddService子域,将该子域指向AddService例程的函数地址即可。
和DriverEntry不同,AddService例程的名字可以任意命名:
DRIVER_ADD_DEVICE AddDevice;
NTSTATUS AddDevice(
__in struct _DRIVER_OBJECT *DriverObject, //驱动对象(I/O管理器创建的)
__in struct _DEVICE_OBJECT *PhysicalDeviceObject //设备对象(底层总线驱动创建的PDO设备对象)
//传入该参数的目的是将FDO附加在PDO上
)
{ ... }
AddService函数的功能可分为:
(1)通过IoCreateDevice等函数,创建设备对象,该设备对象就是FDO。
(2)创建FDO后,需要将FDO的地址保存下来,保存的位置是在设备扩展中。
(3)驱动程序将创建的FDO附加到PDO上,附加这个动作是利用函数IoAttachDeviceToDeviceStack实现的:
PDEVICE_OBJECT IoAttachDeviceToDeviceStack(
__in PDEVICE_OBJECT SourceDevice, //要附加到其他设备上的设备,
//将FDO附加在PDO之上时,此处即填FDO的地址
__in PDEVICE_OBJECT TargetDevice //被附加的设备,将FDO直接附加在PDO上时
//(不考虑过滤驱动),此处即时PDO的地址
);
返回值:附加后,返回附加设备的下层设备。如果中间没有过滤驱动,返回值就是PDO;如果中间有过滤驱动,返回的是过滤驱动。
当FDO附加到PDO上时,PDO会通过AttachedDevice子域知道它上层的设备是FDO(或过滤驱动);而FDO无法知道下层是什么设备,因此,需要通过设备扩展设置FDO下层的设备,例如:
//设备扩展结构体
typedefstruct _DEVICE_EXTENSION
{
PDEVICE_OBJECT fdo; //功能设备对象FDO
PDEVICE_OBJECT NextStackDevice; //FDO的下层驱动设备
UNICODE_STRING ustrDeviceName; //设备名
UNICODE_STRING ustrSymLinkName; //符号链接
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;
一般根据自己的需要定制自己的设备扩展。子域fdo是为了保存FDO的地址以备后用;子域NextStackDevice是为了定位设备的下一层设备。在附加操作完成后,需要设定符号链接,以便用户应用程序访问该设备。
4)DriverUnload例程
在NT式驱动中,DriverUnload例程主要负责做删除设备和取消符号链接。而在WDM驱动中,这部分操作被IRP_MN_REMOVE_DEVICE的处理函数所负责。因此,如果在DriverEntry中有申请内存的操作,可以在DriverUnload例程中回收这些内存,DriverUnload例程变得相对简单了。
5)对IRP_MN_REMOVE_DEVICE IRP的处理
驱动程序内部是由IRP驱动的。创建IRP的原因很多,IRP_MN_REMOVE_DEVICE这个IRP是当设备被卸载时,由即插即用管理器创建,并发送到驱动程序中的。IRP一般由两个号码指定该IRP的具体意义:一是主IRP号(Major IRP);一是辅IRP号(Minor IRP)。
每个IRP都由对应的派遣函数所处理,派遣函数是在DriverEntry中指定的。
在WDM驱动程序中,对设备的卸载一般是在对IRP_MN_REMOVE_DEVICE的处理函数中进行的。除了需要删除设备,取消符号链接外,在卸载函数中还需要将FDO从PDO上的堆栈中移除掉,使用函数IoDetachDevice:
VOID IoDetachDevice(
__inout PDEVICE_OBJECT TargetDevice //下层堆栈上的设备对象
);
调用此函数后,可将FDO从设备链上删除,但PDO还是存在的,PDO的删除是由操作系统负责的。
目前开发WDM 驱动程序的方法有三种:
①使用 Microsoft 的 Windows DDK/WDK 工具开发。
②使用 KRFTech 公司的 WinDriver 。
③使用 NuMega 公司的 DriverStudio 。
版权声明:
作者:驱动外包
链接:http://www.51qudong.net/89.html
来源:算法优化_驱动外包_直播算法优化_MAC驱动开发_Linux驱动开发_usb驱动移植外包
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论