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; }
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/