对于你的问题我也用键盘驱动来对你一一说明:
普通的PS/2键盘有下面几个驱动(不考虑你的机器装过其他的键盘过滤驱动)
顶层的Kbdclass产生的设备对象
中间层的i8042prt产生的设备驱动
底层APCI产生的设备驱动你提到过设备栈问题:
一般的WDM驱动程序都是分层的,拿键盘驱动来说,
底层的APCI是总线驱动程序,PS/2接口是挂在APCI上的,总线驱动负责枚举总线上的设备并且分配相应资源,对于编程来说不太关心这个。
中间层的i8042prt:负责响应键盘的中断,然后把数据(键盘扫描码)取出来放在自己的缓冲区,在触发DPC,在DPC中调用上层设备驱动(Kbdclass)的回调函数,取走数据
上层Kbdclass:是真正的键盘驱动程序(我机器上的驱动名为KeyboardClass0),负责处理数据,把数据交给相应的请求,完成IRP。
那么设备栈是什么一个概念呢?IO_STACK_LOCATION是他的数据结构,在每个创建的IRP中都会有一个这个结构的指针。如果没有别的键盘过滤驱动程序,那么正常情况的设备栈是:
Kdbclass 的 IO_STACK_LOCATION1
i8042prt的IO_STACK_LOCATION2
在正常的键盘数据处理时,APCI一般不太关心
首先必须明白Kdbclass是位于上层应用层win32k!RawInputThread和硬件i8042之间。而win32k!RawInputThread 总是发一个 IRP_MJ_READ 的 IRP 到键盘设备栈的顶端(Kdbclass),等待着来自键盘的数据(也就是说他是一个死循环的线程,一直等待用户键盘输入)。当i8042有数据要键盘驱动取走的时候,就会触发中断,这个中断的中断服务例程是键盘驱动中的函数,于是键盘驱动就可以从i8042读取数据,经过一系列处理最终完成那个等待的 IRP。
那么现在就可以回答你的问题了:
问题1:irp中的i/o堆栈是根据设么组建的?比如说这个键盘按下的majorfunction是irp_mj_key 那么他是去找所有的dispatch函数的功能?要是这样的话,怎么判断谁在location1,谁在location2?根据什么判断的?
回答:IRP的I/O堆栈是由负责创建IRP包的模块创建的,一般用户层的请求如ReadFile,都是通过I /O管理器来完成创建的。但是并不是绝对,你也可以在你的驱动程序的分发函数中使用IoAllocateIrp创建IRP,那么这个时候你需要自己创建I/O堆栈了,那么你可能会问,创建多少个I/O设备栈,这个值由DEVICE_OBJECT中的StackSize决定(自己构建IRP的情况是很多的,如驱动和驱动之间的通信)。那么我需要纠正你的是键盘按下的是没有你说的这个:majorfunction是irp_mj_key 。用户按键就是产生中断(组合键也是一样),用户按键首先被i8042的中断捕获到,然后把相应的数据放到缓冲区中,同时在DPC中使用回调函数告诉上层的键盘驱动(Kdbclass)读取数据,然后再做一些数据的处理。那么IRP的发起者一定是上层的,如应用层调用相应的API(ReadFile,WriteFile等),或者是中间层的驱动创建IRP。而不是硬件产生。
刚开始说了应用层win32k!RawInputThread负责发起IRP并且一直死循环等待键盘驱动对IRP的完成,有键盘输入,那么就会对应的IRP完成,所以,在这里的IRP的流向是Kdbclass的Location1再到8042的Location2,一般IRP不会跨栈传递。问题2:当这个irp传到我所处理的分发函数时,我是不知道按下什么键的,对不对? 那么如果我要是想得到按键信息,必须设定IoSetCompletionRoutine(),
那么是不是在location2的要比location1的先得到按键信息?(假设都设置了这个函数)
答案:你说的必须建立在读取设备驱动的IRP,记住,越靠近底层设备的驱动程序,越早获得设备的的数据,也就是Location2要比Location1先得到数据。但是若是写的IRP那就不是这样的(你自己理解一下,比方说我要把数据写到U盘上,我编写一个文件过滤驱动,我完全可以直接返回No,那么低层的磁盘驱动根本就得不到我的数据)。
问题3:如何判断我多附加的device所在location位置?
这个就是你想编写一个过滤器驱动,然后想挂载到相应的驱动程序上面了。想查看你的驱动程序的位置,这个我没做过,但我可以给你几点意见:
方法1:用DeviceTree软件查看
方法2:在自己的设备驱动中,使用AddDevice挂载设备后,打印DEVICE_OBJECT中的StackSize,它的值就是你的设备所处的设备栈的位置。
其实从你的问题衍生出来的问题是,你如何想最早获得数据。。。自己可以想想
周末有个好心情