打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
驱动开发学习笔记
1:设备对象是系统為帮组软件管理硬件而创建的数据结构,一个物理硬件可以有多个这样的数据结构。处於堆栈最底层的设备对象称為物理设备对象(PDO);
2:操作系统的pnp管理器按照设备驱动程序的要求构造了设备对象堆栈。总线驱动程序的任务就是枚举总线上的设备。并為每个设备创建一个PDO;一旦总线驱动程序检查到新硬件存在。Pnp管理器就创建一个PDO.创建完PDO后。PNP管理器参照注册表中的信息查找于这个PDO相关的过滤器和功能驱动程序。系统安装程序负责添加这些注册表项。而驱动程序包中的控制硬件安装的INF文件负责添加其他表项。这些表项定义了过滤器和功能驱动程序在堆栈中的次序。
PNP管理器先装入最底层的过滤器驱动程序并调用其AddDevice函数。这个函数创建一个FIDO。这样在过滤器驱动程序和FIDO之间建立了水平连接。然后ADDDevice把PDO连接到FIDO上。这就是设备对象之间连线的由来。PNP管理器继续向上执行。装入并调用每个底层过滤器,功能驱动程序,每个高层过滤器,直到完成整个堆栈。
3:通常IRP先被送到设备堆栈的最上层驱动程序。然后逐步的过滤到下面的驱动程序。每一层驱动程序都可以决定如何处理IRP,有时候驱动程序不做任何事情。只是向下层穿IRP。有时。驱动程序直接处理完该IRP不再向下传递。还有时。驱动程序处理了IRP又把IRP传递下去。这取决于设备以及IRP所携带的内容。
4:系统这么装入驱动程序?
既然总线驱动程序创建了PDO。然后PDO管理器根据改PDO的注册表项装入它的驱动程序。那么总线驱动程序从哪裡来???
首先,PNP管理器有一个内建的驱动程序。它与一个实际不存在的根总线相对应。根总线概念性的把计算机与所有那些不能用电子方式申明自己存在的设备连接起来。这包括主硬件总线(如PCI)。根总线驱动程序从注册表中获取有关计算机的信息。而这些关於计算机本身的注册表信息是由windows系统安装程序初始化的。安装程序通过运行一个尽心製作的硬件检测程序以及向用户提出一些适当的问题来获取这些信息。所以,根总线驱动程序有足够的信息為主总线创建PDO.然后,主总线的功能驱动程序用电子方式枚举自己的硬件。
5:有三种注册表键负责配置。它们是硬件键。类键。服务建。硬件键包括单个设备的信息。类键涉及到所有相同类型设备的共同信息。服务键包含驱动程序信息。一般注册表中都会包含一些以前用过的和现在用过的硬件信息。
设备的硬件键出现在注册表local machine分支的\system\currentControlSet\Enum子键上。Enum下的第一级子键与系统中的各种总线枚举器相对应。SetupDixXXX函数可以访问Enum键。
假设你的驱动程序使用IoRegisterDeviceInterface函数注册了一个设备接口。并且你有一个改接口的符号连接名(通过枚举该接口GUID的所有实例或从WM_DeviceChange消息的参数中获得这个名字)。
类键,所有设备类的类键都出现在HKLM\system\currentcontrolset\control\class键中。每个设备下还可以在所属类键下拥有自己的子键。该键的键名就是设备硬件键中的Driver值。这个子键的作用是把所有这些注册表项与安装设备使用的INF文件关联。
服务建:HKLM\System\CurrentControlSet\Services键中。
ImagePath:指出驱动程序执行文件的名字和路径。
6:当说系统装入一个驱动程序时。是指系统把驱动程序的映像映射到虚拟内存中。并重定位内存参考。最后调用驱动程序的主入口点。如果驱动程序已经在内存中。则装入过程仅仅是增加驱动程序映像的参考计数。
7:I/O管理器适用驱动程序对象来代表每个设备驱动程序。
WDM驱动程序可以调用IOCreateDevice函数创建设备对象。但设备对象的管理则由I/O管理器负责。
DriverObject指向与该设备对象相关的驱动程序对象。通常就是调用IOCreateDevice函数创建该设备对象的驱动程序对象。NextDevice指向属於同一个驱动程序的下一个设备对象。CurrentIrp指向最近发往驱动程序StartIO函数的I/O请求包。
Device_Object结构的Flags标誌:
这只列举主要的几个
DO_BUFFERD_IO:读写操作适用缓冲方式(系统复製缓衝区)。
DO_DIRECT_IO:读写操作适用直接方式(内存描述符表)
8:如何建立设备堆栈???
NextDevice域把所有属於特定驱动程序的设备对象水平的连接在一起。而垂直方向上的链接主要用不透明域。从PDO开始。每个设备对象都有指向上一层设备对象的指针。由於没有已公开的向下方向的指针。所以驱动程序必须自己记住下层设备对象是谁。
每个wdm驱动程序必须能处理PNP,Power,System_control这三种请求。
DriverObject->MajorFunction[IRP_MJ_PNP]=DispatchPnp;
DriverObject->MajorFunction[IRP_MJ_POWER]=DispatchPower;
DrvierObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = DispatchWmi;
9:非WDM驱动程序需要在DriverEntry例程中枚举他们的硬件,并在其硬件的所有可能实例被识别前装入内存并初始化。例如。鼠标和键盘就是这样的。但如果DriverEntry例程运行的太快。那么这些驱动程序将不能正常工作。因此,它们必须使用IORegisterDriverReinitialization函数寄存一个例程。之后I/O管理器在某个驱动程序检测到新硬件存在时再回调这个寄存例程。最后在初始化例程运行。同时也把自身寄存為下一个回调的函数。
WDM驱动程序不需要寄存再初始化例程。因為它们不需要用自己的代码去检测硬件。Pnp管理器自动把新硬件匹配到正确的WDM驱动程序上。并调用改驱动的ADDDevice例程。再由AddDevice例程做所有必要的初始化工作。
对於功能驱动程序。其AddDevice函数的基本职责是创建一个设备对象并把它连接到以pdo為底的设备堆栈中。相关步骤
(1:调用IOCreateDevice创建设备对象,并建立一个私有的设备扩展对象。
(2:注册一个多多个设备接口,以便应用程序能知道设备的存在。另外。还可以给出设备名并创建符号连接。
(3:初始化设备扩展和设备对象的flag成员。
(4:调用IoAttachDeviceToDeviceStack函数把新设备对象放到堆栈上。
10:用户模式程序可以用DefineDosDevice创建一个符号连接。如下:
BOOL  okay = DefineDosDevice(DDD_RAW_TARGET_PATH,”barf”,”\\Device\\SECTEST_0”);
在WDM中IOCreateSymbolicLink(Linkname,targname);
11:应该命名设备对象吗???
如果命名了设备对象。那么任何内核模式程序都可以打开改设备的句柄。另外。任何内核模式或用户模式都能创建连接到该设备的符号连接。并可以使用这个符号连接打开设备的句柄。
12:建立设备堆
每个过滤器驱动程序和功能驱动程序都有责任把设备对象放到设备堆栈上。从PDO开始一直向上。
PDEVICE_OBJECT fdo;
IoCreateDevice(…,&fdo);
Pdx->LowerDeviceObject = IoAttachDeviceToDeviceStack(fdo,pdo);
13:清除DO_DEVICE_INITIALIZING标誌
在AddDevice中最后一件要做的事情是清除设备对象中的DO_Device_initiatizing标誌、
Fdo->Flags&=~DO_DEVICE_INITIALIZING;
当这个标誌设置时。I/O管理器将拒绝任何打开改设备句柄的请求或向设备对象上附著其他设备对象的请求。在驱动程序完成初始化后。必须清除这个标誌。
1:创建完IRP后。你可以调用IOGetNextIrpStackLocation函数获得该IRP第一个堆栈单元的指针。然后初始化这个堆栈单元。在初始化过程的最后,你需要填充MajorFunction代码。堆栈单元初始化完成后,就可以调用IoCallDriver函数把irp发送到设备驱动程序。
PDEVICE_OBJECT DeviceObject;
PIO_STACK_LOCATION stack = IOGetNextIrpStackLoction(Irp);
Stack->MajorFunction = IRP_MJ_Xxx;
NTSTATUS status = IoCallDriver(DeviceObject,irp);
2:当有大量读写请求进入设备时,通常需要把这些请求放入一个队列中。以便使硬件访问串行化。每个设备对象都有一个请求队列对象。下面是使用这个队列的标準方法。
NTSTATUS DispatchXxx(…)
{
    ….
    IoMarkIrpPending(IRP);
    IoStartPacket(device,irp,NULL,NULL);
    Return STATUS_PENDING;
}
(1:无论何时。如果当派遣函数返回STATUS_PENDING状态代码的时候,你应该先调用这个IoMarkIrpPending函数。以帮组IO管理器避免内部竞争,我们必须在放弃IRP所有权之前做这一点。
如果设备正忙。那么IoStartPacket就把请求放到队列中。如果设备空閒,IoStartPacket将把设备置成忙并调用StartIO例程。
返回Stauts_pending通知调用者我们没有完成这个IRP;
注意一旦我们调用了IoStartPacket函数。就不要再碰IRP,因為在改函数返回前。IRP可能已经被完成并且其占用的内存可能被释放,而我们拥有的该IRP的指针也许是无效的。
3:每处理一次IRP ,I/O管理器就调用一次StartIO例程。
4:当设备完成数据传输后。它将以硬件中断形式发出通知。IoConnectInterrupt函数勾住一个中断。该函数的一个参数就是ISR的地址。因此当中断发生时。硬件抽象层(HAL)就调用你的ISR,ISR运行在DIRQL上。并由ISR专用的自旋锁保护。一个ISR最可能做的事情就是调度DPC例程(推迟过程调用)。而DPC的目的就是让你做某些事情。如调用IoCompleteRequest。而该调用不可能运行在ISR。运行在DIRQL级别上。
5:DPC例程的传统名字為DpcForlser。因為它是由ISR请求的。DPCForlsr例程在DIsplatch-level级别上获得控制。通常。它的工作就是完成IRP(导致最近的中断发生)。但一般情况下。它通过调用IoCompleteRequest函数把剩餘的工作交给完成例程来做。
IOStartNextPacket取出设备队列中的下一个IRP并发送到StartIO。False参数指出不能以通常方式取消。
IOCompleteRequest完成第一个参数指定的IRP。第二个参数是等待线程的优先级提高值。注意在调用IOCompleteRequest之前你还要填充IRP中的IoStatus块。
调用IOCompleteRequest例程是处理I/O请求的标準结束方式。在这个调用之后,I/0管理器(或是任何在开始处创建该IRP的实体)将再次拥有该IRP,最后该IRP被这个实体销毁并解除等待线程的阻塞状态。
6:完成一个IRP必须先填充 IOStatus块的status和Information成员。然后调用IoCompleteRequest例程。Status值就是Ntstatus.h中定义的状态码。而information值要取决于你完成的是何种类型的IRP以及是成功还是失败。如果IRP完成失败。你应该把Information域设置為0,如果你成功地完成了一个数据传输IRP。通常应该把Information域设置成传输的字节量。
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(IRP);
ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
If(code==IOCTL_TOASTER_BOGUS)
Return CompleteRequest(Irp,Status_invalid_device_request,0);
completeRequest函数的Information参数类型為ULONG_PTR。即该参数即可以是一个ULONG也可以是一个指针。
7:WDM使用分层设备对象结构的目的就是使IRP能方便地从一层驱动程序传递到下一层驱动程序。
 Pdx->LowerDeviceObject = IoAttachDeviceToDeviceStack(fdo,pdo);
Fdo是设备对象地址。Pdo是处於设备堆栈底部的物理设备对象地址。IoAttachDeviceToDeviceStack函数返回给你下一层设备对象的地址。当你决定把上层收到的IRP发送给自己的下层时。那么这个设备对象就是调用IOCallDrive
向下传递一个IRP你有责任初始化一个IO_Stack_Location结构。下层的驱动程序将使用改结构获取它的参数。一种方法是执行物理拷贝。
----
IoCopyCurrentIrpStackLocationToNext(IRP);
Status = IoCallDriver(pdx->LowerDeviceObject,Irp);
IoCopyCurrentIrpStackLocationToNext把当前堆栈单元的所有域,除了一个属於I/O完成例程的域。都复製到下一个堆栈单元。
如果你的驱动程序不用关心IRP传递到下层驱动程序之后的事情,你可以利用一个捷径来避免复製堆栈单元。我们就不需要安装完成例程。没有必要花费处理器时间去把你的堆栈单元内容复製到下一个堆栈单元,因為那个堆栈单元已经含有下一层驱动程序要得到的参数。以及自己下一层驱动程序可能给出的任何例程指针。因此。你可以使用下面的捷径。
NTSTATUS ForwordAndForget(PDEVICE_OBJECT fdo,PIRP Irp)
{
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
IoSkipCurrentIrpStackLocation(Irp);
Return IoCallDriver(pdx->LowerDeviceObject,Irp);

}
这个捷径存在于IoSkipCurrentIrpStackLocation函数中。它实际上是一个宏,这个宏的作用就是使堆栈指针少前进一步。而IOCallDriver函数会使堆栈指针前进一步。中和的结果就是堆栈指针不变。当下一个驱动程序的派遣例程调用IoGetCurrentIrpStackLocation时。它将收到与我们正使用的完全相同的IO_STACK_LOCATION指针,因此,它所处理的将是同一个请求。相同的主副功能代码。以及相同的参数。
8:取消I/O请求。
程序有时会取消他们原来请求的IRP。应用程序可能发出某些需要长时间才能完成的请求。然后这个应用程序结束执行。而这个IRP仍然是未完成的。这种情况在WDM模型中尤為常见。例如当新硬件插入系统时。驱动程序必须停止执行以等待配置管理器重新分配硬件资源。设备电源关闭时也是这样。為了在内核模式中取消一个请求。IRP的创建者需使用IoCancelIrp函数。如果某线程终止时。它发出的请求仍然未完成。则操作系统自动為某个IRP调用IoCancelIrp.用户模式应用程序调用CancelIo函数可以取消给定线程发出的所有未完成的异步操作。IoCancelIrp仅仅是简单的设置IRP的Cancel标誌位。然后调用Irp的取消例程(它并不知道你时候修改过IRP指针,也不知道你是否正在处理这个IRP,所以它必须依靠一个你提供的取消例程来做大部分IRP取消工作)。
9:取消自旋锁
Void StartIo(PDEVICE_OBJECT fdo,PIRP Irp)
{
KIRQL oldirql;
IoAcquireCancelSpinLock(&oldirql);
If(Irp!=fdo->CurrentIrp||Irp->Cancel)
{
          IoReleaseCancelSpinLock(oldirql);
          Return;
}
Else
{
          IoSetCancelRoutine(Irp,NULL);
          IoReleaseCancelSpinLock(oldirql);
}
}
Void OnCancel(PDEVICE_OBJECT fdo,PIRP Irp)
{
   If(fdo->CurrentiIrp==Irp)
{
   KIRQL oldirql = Irp->CancelIrql;
   IoReleaseCancelSpinLock(DISPATCH_LEVEL);
IoStartNextPacket(fdo,TRUE);
KeLowerIrql(oldiral);
}
Else
{
   KeRemoveEntryDeviceQueue(&fdo->DeviceQueue,&Irp->Tail.Overlay.DeviceQueueEntry);
IoReleaseCancelSpinLock(Irp->cancelIrpl);
}
CompleteRequest(Irp,STATUS_CANCELLED,0);
}
避免使用全局取消自旋锁
9:创建自己的IRP
有四种不同的服务函数可以用来创建IRP。但我不得不推迟到现在才讨论如何选择它们。
(1:IoBuildAsynchronousFsdRequest和IoBuildSynchronousFsdRequest函数仅能用於创建主功能码的IRP(IRP_MJ_READ,IRP_MJ_WRITE,IRP_MJ_FLUSH_BUFFER,IRP_MJ_SHUTDOWN,IRP_MJ_PNP,IRP_MJ_POWER);
IoBulidSynchronousFsdRequest(MajorFunction,DeviceObject,Buffer,Length,StartingOffset,Event,IoStatusBlock);
MajorFunction是新Irp的主功能吗,DeviceObject是该IRP最初要发送到的设备对象的地址。对於读写请求。你必须提供Buffer(PVOID),Lengh(ULONG),StartingOffset是读写操作在目标文件中的定位。对於该函数创建的其它请求。这些参数将被忽略。Event 是一个事件对象的地址。IocompleteRequest应该是操作完成时设置这个事件。IostatusBlock是一个状态块的地址。该状态块用於保存IRP结束状态和信息。在操作完成前。事件对象和状态快必须一直存在与内存中。
如果创建的是读写IRP。那么在提交该IRP前你不需要做任何事,如果创建的是其它类型的IRP你需要用附加的参数信息完成第一个堆栈单元。
提交IRP并等待其完成:
PIRP Irp = IoBuildSynchronousFsdRequest(….);
NTSTATUS status = IOCallDriver(DeviceObject,Irp);
If(Status ==STATUS_PENDING)
KeWaitForSingleObject(Event,Executive,KernelMode,False,NULL):
IRP完成后,你可以通过查看你的I/O状态快来了解该IRP的结束状态和相关信息。
10:清除
你必须事先计划好IRP占用内存的释放以及IRP的取消。当使用IOBuildSynRonOusFsdRequest创建IRP时。I/O管理器自动释放IRP占用的内存。如果是需要系统缓衝区或内存描述符的读写请求。I/O管理器也能自动清除。
系统中仅有两个实体可以取消IRP,一个是I/O管理器中称為thread rundown的代码,当线程结束仍有未处理的IRP。就执行这段代码。另一个实体就是產生该Irp的驱动程序
即插即用
1:在 WDM中。Pnp请求扮演了两个角色。在第一个角色中。这些请求指示驱动程序何时以及如何配置或取消其硬件或自身的设置。Pnp管理器使用 IRP_MN_START_DEVICE来通知功能驱动程序其硬件被赋予了设么I/O资源。以及指导功能驱动程序做任何必要的硬件或软件设置以便设备能正常工作。IRP_MN_STOP_DEVICE告诉功能驱动程序关闭设备。IRP_MN_REMOVE_DEVICE告诉功能驱动程序关闭设备并释放与之关联的设备对象。PNP 请求的第二个角色是指导驱动程序完成一系列状态装换。
2:啟动和停止设备
通过使用总线驱动程序。Pnp管理器能够自动检测硬件和分配I/O资源。大部分现代设备都有即插即用特性。可以允许系统软件自动检测并提取它们的I/O资源需求。WDM包含四种标準I/O资源类型:I/O端口,内存寄存器,DMA通道,中断请求。
当PNP管理器检测到硬件时。它首先参考注册表以了解有哪些过滤器驱动程序将管理该硬件。
开始,pnp管理器為每个设备创建一个资源需求列表并允许驱动程序过滤这个列表。一旦资源分配确定。Pnp管理器通过向每个设备发送一个带IRP_MN_START_DEVICE副功能吗的pnp请求来通知设备。通常过滤器驱动程序对这个IRP不感兴趣。所以他们使用DefaultPnpHandler方式把请求向下传,而功能驱动程序正好相反。它需要在这个IRP上做大量的工作。包括分配并配置额外的软件资源以及為设备操作做準备。这个工作需要在PASSIVE_LEVEL级上进行。并在低层驱动程序处理完该IRP后完成。为了在下传IRP_MN_START_DEVICE请求后再获得控制。派遣例程需要等待一个内核事件。该事件最终由低层驱动程序对IRP的完成操作做通知。
3:总线驱动程序利用IoStatus.Status中的设置来判断上层驱动程序是否已经处理了该IRP,对於IRP_MJ_PNP的其他几个副功能吗。总线驱动程序也做过类似的判断。IRP_MN_STOP_DEVICE设备停止请求通知你关闭设备。然后Pnp管理器重新分配I/O资源。在硬件级关闭设备将包括暂停或停止当前活动并阻止后来的中断。在软件级关闭设备将涉及释放设备啟动时配置的I/O资源。当设备将要被系统删除时。Pnp管理器向你发送副功能吗為IRP_MN_REMOVE_DEVICE的pnp请求。
NTSTATUS HandleRemoveDevice(PDEVICE_OBJECT fdo,PIRP Irp)
{
   PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
DeregisterAllInterfaces(pdx);
StopDevice(fdo,oktouch);
Irp->IoStatus.status = STATUS_SUCCESS;
NTSTATUS status = DefaultPnpHandler(fdo,Irp);
RemoveDevice(fdo);
Return status;
}
Void RemoveDevice(PDEVICE_OBJECT fdo)
{
   PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
IoDetachDevice(pdx->LowerDeviceObject);
IoDeleteDevice(fdo);
}
1:IoDetachDevice调用与AddDevice中的IoAttachDevice ToDeviceStack调用正好相反。
2:IoDeleteDevice调用与AddDevice中的IoCreateDevice调用正好相反。一旦该函数返回。设备对象将不復存在。如果你的驱动程序没有其他设备需要管理,那么很快你的驱动程序也将从内存中卸载。
有时用户可能不经过任何用户接口交互操作突然地拆卸设备。如果系统检测到这种突然的删除。它就向驱动程序发送副功能吗為IRP_MN_SURPRISE_REMOVAL的pnp请求。后面还会跟著一个IRP_MN_REMOVE_DEVICE请求。除非你以前在处理否则系统将显示一个对话框。通知用户这样做很危险。为了响应突然删除请求。设备驱动程序应该禁止所有已寄存的接口。这将给应用程序一个机会关闭设备的句柄。但应用程序必须事先关注这种通知。
NTSTATUS HandleSurpriseRemoval(PDEVICE_OBJECT fdo,PIRP irp)
{
   PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
EnableAllInterfaces(pdx,FALSE);
StopDevice(fdo,oktouch);
Irp->IoStatus.Status = STATUS_SUCCESS;
Return DefaultPnpHandler(fdo,Irp);
}
IRP_MN_SURPRISE_REMOVEAL来自何处???
简单并且直接地从计算机上拆卸设备并不能產生突然删除pnp 通知。有些总线能够察觉设备消失。例如拔掉一个USB设备将產生一个电信号。这个电信号能被总线驱动程序注意到。然而。对於大多数其他总线类型。没有任何信号能用於通知总线驱动程序。因此。Pnp管理器需要依靠其他方法来判断设备是否消失。
功能驱动程序可以通知其管理的设备的消失(如果它知道的话),通过调用IoInvalidateDeviceState函数。然后从紧接这的IRP_MN_QUERY_PNP_DEVICE_STATE中返回PNP_DEVICE_FAILED,PNP_DEVICE_REMOVED,PNP_DEVICE_DISABLED中的任一个值,比如。如果你的ISR在读通常结果為1和0混合的状态端口突然得到了全部為1的值,你的驱动程序就应该通知设备消失。更普通的情况是,总线驱动程序调用IoInvalidateDeviceRelations函数触发一个再枚举操作时报告某个设备枚举失败。另外,如果系统处於休眠或在其他低电源状态时用户拆卸了设备。那么驱动程序在接收到IRP_MN_SURRISE_REMOVAL请求前先收到了一系列电源管理IRP.这些事实说明了驱动程序应该能应付由设备突然消失所造成的错误。
3:可以使用一个DEVQUEUE来排队和取消IRP
例如你的设备用一个单独的队列来管理读写请求。你应该定义一个DEVQUEUE
4:pnp管理器在停止你的设备前总是先询问。得到允许后才向你发送IRP_MN_STOP_DEVICE请求。询问以IRP_MN_QUERY_STOP_DEVICE请求的形式出现。你可以回答成功或失败。询问的基本含义是,如果系统在几纳秒后向你发送IRP_MN_STOP_DEVICE。你能立即停止设备吗?你可以用两种稍微不同的方式处理这个询问请求。第一种方式适合可以迅速完成或者能容易地中途结束的IRP
NTSTATUS HandleQueryStop(PDEVICE_OBJECT fdo,PIRP  irp)
{
   Irp->IoStatus.Status= STATUS_SUCCESS;
   PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
If(pdx->status!=WORKING)
<--1>该语句用於这种特殊情况。Pnp管理器发Query_stop你希望忽略这样这的查询。
Return DefaultPnpHandler(fdo,Irp);
If(!OkayToStop(pdx))
{
  Return CompleteRequest(Irp,STATUS_UNSUCCESSFUL,0);
  StallRequests(&pdx->dqReadWrite);
  WaitForCurrentIrp(&pdx->dqReadWrite);
  Pdx->status = PENDINGSTOP;
  Return DefaultPnpHandler(fdo,Irp);
}
}
另一种处理QUERY_STOP的方式适合于需要长时间才能完成并且不能被中途停止的IRP,例如磁带机的备份操作就不能被中途打断。在这种情况下。你可以使用DEVQUEUE的checkbusyandstall函数。如果你的设备忙。该函数返回TRUE,在这种情况下。你还需要停止队列(检测设备状态和停止队列操作需要一个自旋锁的保护)。
即使你成功的回答了查询。但下层驱动程序可能会失败这个查询。即使所有的驱动程序都成功的回答了查询。Pnp管理器也可能决定不关闭你的设备。在这种情况下。你将收到另一个副功能吗為IRP_MN_CANCEL_STOP_DEVICE的pnp请求。它通知设备不将被关闭。之后你应该清除在查询中设置的任何state值。
与pnp管理器在停止设备前向你询问一样。它在删除设备前也会向你询问。即IRP_MN_QUERY_REMOVE_DEVICE请求。你可以回答成功或失败。与停止查询相似。如果pnp管理器中途改变想法。它就发送IRP_MN_CANCEL_REMOVE_DEVICE请求。
防止设备过早地删除的基本想法是在每一次开始处理请求时都获取删除锁。处理完成后释放删除锁。在你删除你的设备对象前。应确保删除锁未被使用。否则,你将等到这个锁的所有引用都被释放。
5:如果用户使用设备管理器删除设备。而某应用程序正拥有该设备的打开句柄。那么操作系统将拒绝删除设备并通知用户。如果设备被用户从计算机上物理的摘除并且没有使用设备管理器。那么一个良好的应用程序应该注意WM_DEVICECHANGE消息。该消息通知应用程序设备已经被卸载。应用程序接著应该关闭设备句柄。驱动程序请求。直到句柄被真正的关闭。其实这也是删除锁逻辑允许你做的。
6:设备用途通知
磁盘驱动程序(以及磁盘控制器驱动程序)有时候需要了解外来的关於它们如何被操作系统使用的信息。IRP_MN_DEVICE_NOTIFICATION请求提供了获取这些信息的手段。在IRP堆栈单元的Parameters.UsageNotification子结构中包含了这样两个参数,(InPath如果设备处於Type指定的路径中,则為TRUE,否则為FALSE,TYPE用法类型)在通知请求的子派遣例程中。你应该用一个Switch语句来区分各种通知。在大多数情况下。你将把该IRP下传。下面是这个子派遣例程的框架代码。
NTSTATUS HandleUsageNotification(PDEVICE_OBJECT fdo,PIRP Irp)
{
    PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
DEVICE_USAGE_NOTIFICATION_TYPE type = stack->Parameters.UsageNotification.Inpath;
Switch(type)
{
   Case DeviceUsageTypeHibernation:
    ---
    Irp->IoStatus.Status=STATUS_SUCCESS;
    Break:
   Case DeviceUsageTypeDumpFile:
   ---
   Irp->IoStatus.Status = STATUS_SUCCESS;
   Break;
   Case DeviceUsageTypePaging:
   ---
   Irp->IoStatus.Status = STATUS_SUCCESS;
   Break;
Default:
  Break;
}
Return DefaultPnpHandler(fdo,Irp);
}
仅当你明确的认為该通知是送往总线驱动程序的信号时才设置其Status域為STATUS_SUCCESS。如果你没有设置STATUS_SUCCESS.则总线驱动程序将假设你并不知道。因此也不处理该通知。你应该知道你的设备不能支持某种用途。例如。假设你的磁盘设备不能用来存储休眠文件。但如果该IRP指定InPath值。你应该使该IRP失败。
-----
Case DeviceUsageTypeHibernation:
    If(inPath)
     Return CompleteRequest(Irp,STATUS_UNSUCCESSFUL,0);
DeviceUsageTypePaging如果该通知為Inpath為TRUE则指出将有一个内存交换文件在这个设备上打开。如果為FALSE则指出有一个内存交换文件已被关闭。
DeviceUsageTypeDumpFile如果该通知的InPath為TRUE则指出设备已被选定用於保存系统崩溃时的DUMP文件。如果為False则取消这个设定。
DeviceUsageTypeHibernation如果该通知的InPath為TRUE则指出设备被选定用於保存休眠文件。如果為FALSE则取消这个选定。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
IRP_MN_START_DEVICE分发例程中的前进和等待IRP总结
用 VB 做 USB 通信程序及USB基础知识
DDK开发介绍-1
WDM驱动程序的组成
驱动程序开发入门(一)HelloWorld - windows驱动开发 - hell741...
Linux和Windows设备驱动架构比较
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服