⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 jiurl键盘驱动 3.htm

📁 JIURL键盘驱动
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0058)http://jiurl.nease.net/cn/document/KbdDriver/JiurlKbd3.htm -->
<HTML><HEAD><TITLE>JIURL键盘驱动 3</TITLE>
<META http-equiv=Content-Type content="text/html; charset=gb2312">
<STYLE type=text/css>.title {
	FONT-WEIGHT: bold; FONT-SIZE: 21px; LINE-HEIGHT: 48px; FONT-FAMILY: "黑体", Arial, sans-serif; TEXT-DECORATION: none
}
.author {
	FONT-SIZE: 12px; LINE-HEIGHT: 16px; FONT-FAMILY: "宋体"
}
.content {
	FONT-SIZE: 14px; LINE-HEIGHT: 20px
}
</STYLE>

<META content="MSHTML 6.00.2900.2668" name=GENERATOR></HEAD>
<BODY bgColor=#f7f7f7 topMargin=5>
<DIV align=center>
<CENTER>
<TABLE height=29 cellSpacing=0 cellPadding=0 width="96%" border=0>
  <TBODY>
  <TR>
    <TD class=title width="100%" height=41>
      <P align=center><FONT face=宋体>JIURL键盘驱动 3</FONT></P></TD></TR></CENTER>
  <TR>
    <TD class=author width="100%" height=9>
      <P align=center><FONT face=宋体>作者: <A 
      href="mailto:jiurl@mail.china.com">JIURL</A> </FONT></P></TD></TR>
  <TR>
    <TD class=author width="100%" height=6>
      <P align=center><FONT 
      face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      主页: <A href="http://jiurl.yeah.net/">http://jiurl.yeah.net/</A> 
    </FONT></P></TD></TR>
  <TR>
    <TD class=author width="100%" height=2>
      <P align=center><FONT face=宋体>&nbsp;&nbsp;&nbsp; 日期: 2003-12-13</FONT> 
    </P></TD></TR></TBODY></TABLE></DIV>
<DIV align=center>
<CENTER>
<TABLE height=1 cellSpacing=0 cellPadding=0 width="96%" border=0>
  <TBODY>
  <TR>
    <TD width="100%" height=1>
      <HR color=#396da5 SIZE=3>
    </TD></TR></TBODY></TABLE></CENTER></DIV>
<DIV align=center>
<TABLE class=content height=200 cellSpacing=0 cellPadding=0 width="96%" 
border=0>
  <TBODY>
  <TR>
    <TD vAlign=top width="131%" height=200>
      <P><B>4 编译与调试环境简介</B><BR><BR>4.1 源码<BR><BR>&nbsp;&nbsp;&nbsp; 
      ps/2键盘驱动的设备栈有3层,最底层设备对象的驱动是 acpi,中间层设备对象的驱动是 i8042prt,最高层设备对象的驱动是 
      kbdclass。<BR><BR>&nbsp;&nbsp;&nbsp; DDK 所附的源码中有 i8042prt 和 kbdclass 
      的源码,分别位于 ...\NTDDK\src\input\pnpi8042 ,...\NTDDK\src\input\kbdclass 
      。<BR><BR>&nbsp;&nbsp;&nbsp; 注意,在目前DDK所附的源码中没有 acpi 的源码,不过 acpi 
      对于键盘驱动几乎没有起什么作用。在DDK中可以找到一个叫acpi的目录,但那个下面并不是acpi.sys的源码,而是acpiec.sys的源码,没有用处。<BR><BR>4.2 
      关闭文件保护替换系统中的驱动<BR><BR>&nbsp;&nbsp;&nbsp; 我们要用我们自己编译的 debug 版本的 
      i8042prt.sys 和 kbdclass.sys 替换在 ...\WINNT\system32\drivers 
      下系统原来的这两个sys文件。<BR><BR>不过由于有文件保护,我们替换之后,系统会立刻自动替换会原来的。因此我们关闭了win2k的文件保护。<BR><BR>替换驱动文件还有其他的方法,不过在这里关闭文件保护,直接替换驱动文件,对我们最合适。<BR><BR>4.3 
      改动源码以产生调试信息<BR><BR>&nbsp;&nbsp;&nbsp; 
      为了得到调试信息,我们修改了少量的源码。修改很多地方可能都可以达到这个目的。这里介绍的只是一种方法。<BR><BR>4.3.1 改动 
      kbdclass&nbsp;<BR><BR>kbdclass.h 中<BR><BR>#define DEFAULT_DEBUG_LEVEL 
      0&nbsp;<BR><BR>改为&nbsp;<BR><BR>#define DEFAULT_DEBUG_LEVEL 3 //#define 
      DEFAULT_DEBUG_LEVEL 0<BR><BR>4.3.2 改动 i8042prt<BR><BR>i8042prt.h 
      中<BR><BR>#define Print(_flags_, _x_) \<BR>if (Globals.DebugFlags &amp; 
      (_flags_) || !(_flags_)) { \<BR>DbgPrint (pDriverName); \<BR>DbgPrint _x_; 
      \<BR>}<BR>#define IsrPrint(_flags_, _x_) \<BR>if (Globals.IsrDebugFlags 
      &amp; (_flags_) || !(_flags_)) { \<BR>DbgPrint (((ULONG)(_flags_)) &gt;= 
      0x0001000 ? pIsrMou : pIsrKb); \<BR>DbgPrint _x_; 
      \<BR>}<BR><BR>改为<BR><BR>#define Print(_flags_, _x_) \<BR>{ \<BR>DbgPrint 
      (pDriverName); \<BR>DbgPrint _x_; \<BR>}<BR>#define IsrPrint(_flags_, _x_) 
      \<BR>{ \<BR>DbgPrint (((ULONG)(_flags_)) &gt;= 0x0001000 ? pIsrMou : 
      pIsrKb); \<BR>DbgPrint _x_; \<BR>}<BR><BR>由于我使用 WinDbg 
      单机调试,当我要获得按一个键的调试信息时,发现鼠标导致的调试信息不停的出现,使我没有办法在目标系统中按一个键,所以我去掉了一些打印调试信息的语句,来获得在目标系统中按一个键的机会。<BR><BR>注释掉 
      I8042MouseInterruptService,I8042MouseIsrDpc,I8xWriteDataToMouseQueue,I8xGetByteAsynchronous 
      中的所有调试打印语句。<BR><BR>4.4 源码级调试<BR><BR>&nbsp;&nbsp;&nbsp; 使用 WinDbg 
      进行源码级调试。单机使用 WinDbg 
      ,可以参考文章《借助VMware实现单机使用WinDbg》,这篇文章可以在我的主页上找到。<BR><BR><BR><B>5 
      键盘驱动概述</B><BR><BR>&nbsp;&nbsp;&nbsp; 
      键盘驱动位于应用层win32k!RawInputThread和硬件i8042之间。win32k!RawInputThread 总是发一个 
      IRP_MJ_READ 的 IRP 
      到键盘设备栈的顶端,等待着来自键盘的数据。当i8042有数据要键盘驱动取走的时候,就会触发中断,这个中断的中断服务例程是键盘驱动中的函数,于是键盘驱动就可以从i8042读取数据,经过一系列处理最终完成那个等待的 
      IRP。<BR><BR>5.1 键盘设备栈<BR><BR>&nbsp;&nbsp;&nbsp; 
      我们首先来看看键盘驱动的设备栈。对于初始化完成之后,处于运行状态,并且插有 ps/2 
      键盘的系统,我们看看它的键盘设备栈是什么样子。<BR><BR>我们通过 WinObj 之类的工具查看对象管理器命名空间 (Object 
      Manager Namespace),可以看到在 \driver\ 下的名叫 kbdclass 
      的驱动对象。在处于运行状态下的系统中,打断进入WinDbg,我们使用WinDbg 的 !drvobj 
      命令,可以得到它的设备对象列表。<BR><BR>kd&gt; !drvobj \driver\kbdclass<BR>Driver object 
      (fe4f6330) is for:<BR>\Driver\Kbdclass<BR>Driver Extension List: (id , 
      addr)<BR><BR>Device Object list:<BR>fe4f5df0&nbsp;<BR><BR>设备对象只有一个,地址为 
      fe4f5df0,使用WinDbg 的 !devobj 命令,来获得一些这个设备对象的信息。<BR><BR>kd&gt; !devobj 
      fe4f5df0<BR>Device object (fe4f5df0) is for:<BR>KeyboardClass0 
      \Driver\Kbdclass DriverObject fe4f6330<BR>Current Irp fe43e6c8 RefCount 0 
      Type 0000000b Flags 00002044<BR>DevExt fe4f5ea8 DevObjExt 
      fe4f5fd8&nbsp;<BR>ExtensionFlags (0000000000)&nbsp;<BR>AttachedTo (Lower) 
      fe4f5020 \Driver\i8042prt<BR>Device queue is busy -- Queue 
      empty.<BR><BR>看到这个设备对象有个名字叫 KeyboardClass0,我们用 WinObj 也可以在 \Device\ 
      下看到这个设备对象。<BR><BR>使用 WinDbg 的 !devstack 命令,来看看设备对象 fe4f5df0 也就是 
      \Device\KeyboardClass0 所在的设备栈<BR><BR>kd&gt; !devstack fe4f5df0<BR>!DevObj 
      !DrvObj !DevExt ObjectName<BR>&gt; fe4f5df0 \Driver\Kbdclass fe4f5ea8 
      KeyboardClass0<BR>fe4f5020 \Driver\i8042prt fe4f50d8&nbsp;<BR>fe4dd730 
      \Driver\ACPI fe507468 0000000e<BR>!DevNode fe4fed68 :<BR>DeviceInst is 
      "ACPI\PNP0303\4&amp;5289e18&amp;0"<BR>ServiceName is 
      "i8042prt"<BR><BR>我们看到<BR>设备栈最顶层的设备对象是 fe4f5df0,有名字,叫做 KeyboardClass0,属于驱动 
      Kbdclass。<BR>设备栈中间层的设备对象是 fe4f5020,没有名字,属于驱动 i8042prt。<BR>设备栈最底层的设备对象是 
      fe4dd730,有名字,0000000e,属于驱动 ACPI。<BR><BR>!DevNode 
      部分的内容和注册表有关,我们会在后面讨论。<BR>要注意的是,ACPI用于键盘驱动的那个设备对象的名字,和你计算机上安了多少外设有关,在我这里是 
      0000000e,在你那里很可能不是。<BR>我们看到的 
      \Driver\Kbdclass,\Driver\i8042prt,\Driver\ACPI 
      是驱动对象的名字。一个驱动对象就对应着一个驱动程序文件。<BR><BR>我们再看一下 \Driver\i8042prt 
      的设备对象列表。<BR><BR>kd&gt; !drvobj \driver\i8042prt<BR>Driver object 
      (fe4f69f0) is for:<BR>\Driver\i8042prt<BR>Driver Extension List: (id , 
      addr)<BR><BR>Device Object list:<BR>fe4d3ba0 
      fe4f5020&nbsp;<BR><BR>可以看到有两个设备对象。使用 !devobj 
      我们可以知道,一个是用于ps/2键盘驱动的设备对象,一个是用于ps/2鼠标驱动的设备对象。<BR><BR>acpi 
      更是有多个设备对象。<BR><BR>最早我就是看到 kbdclass 的名字,猜测这就是键盘的驱动程序,进而看到了它的设备栈,看到了 
      i8042prt,acpi,后来实践也证明了 kbdclass ,i8042prt 就是键盘驱动。<BR><BR>我们这里看到的 ps/2 
      键盘的设备栈,是没有自己另外安装其他键盘过滤程序的情况。<BR><BR>最顶层的设备对象是 驱动 Kbdclass 
      的设备对象。<BR>中间层的设备对象是 驱动 i8042prt 的一个设备对象。<BR>最底层的设备对象是 驱动 ACPI 
      的一个设备对象。<BR><BR>5.2 各层简介<BR><BR>Kbdclass<BR><BR>键盘设备栈的栈顶,应用层发出的所有 
      IRP,都首先发到这里。对于那个等待键盘数据的 IRP_MJ_READ 的 
      IRP,完全由这一层处理,不向下传。<BR><BR>i8042prt<BR><BR>对硬件i8042的所有操作都在这一层完成。键盘中断的中断服务例程也是这一层的函数。使用 
      WinDbg 的 !idt 命令可以看到这一点。<BR><BR>kd&gt; !idt<BR><BR>IDT for processor 
      #0<BR><BR>00: 804625e6 (nt!KiTrap00)<BR>01: 80462736 
      (nt!KiTrap01)<BR>...<BR>b3: fe4cf264 
      (Vector:b3,Irql:a,SyncIrql:a,Connected:TRUE,No:0,ShareVector:FALSE,Mode:Latched,ISR:i8042prt!I8042KeyboardInterruptService(fe1c1750))<BR>...<BR>ff: 
      804613f0 (nt!KiUnexpectedInterrupt207)<BR><BR>可以看到中断向量为 b3 的中断服务例程最终为 
      i8042prt!I8042KeyboardInterruptService,即 i8042prt 中的函数 
      I8042KeyboardInterruptService 
      。<BR><BR>驱动的中断和一般的中断有所不同,我们将在后面做讨论。<BR><BR>ACPI<BR><BR>这一层几乎不起什么作用。<BR><BR>5.3 
      键盘驱动的运作<BR><BR>&nbsp;&nbsp;&nbsp; 
      键盘驱动的主要工作就是,当键盘上有键按下引发中断时,键盘驱动从端口读出按键的扫描码,最终顺利的将它交给在键盘设备栈栈顶等待的那个 
      IRP_MJ_READ 的 IRP。为了完成这个任务,键盘驱动使用了两个循环使用的缓冲区。<BR><BR>i8042prt 和 kbdclass 
      各有自己的一个,循环使用的缓冲区。他们的每个单元是一个 KEYBOARD_INPUT_DATA 
      结构,用来存放一个扫描码及其相关信息。在键盘驱动的注释中,把这个循环使用的缓冲区叫做输入数据队列(input data 
      queue),i8042prt 的那个缓冲区被叫做 port keyboard input data queue,kbdclass 
      的那个缓冲区被叫做 class input data queue。输入数据队列更形象一些,我们也会使用这种叫法。<BR><BR>i8042prt 
      的自定义的设备扩展中,保存着一些指针和计数值,用来使用它的那个输入数据队列。<BR>PKEYBOARD_INPUT_DATA 类型的 
      InputData , DataIn , DataOut , DataEnd。ULONG 类型的 InputCount。<BR>InputData 
      这个指针,指向输入数据队列的开头。<BR>DataEnd 这个指针,指向输入数据队列的结尾。<BR>DataIn 
      这个指针,指向要进入队列的新数据,将要被放在队列中的位置。<BR>DataOut 
      这个指针,指向要出队列的数据,在队列中开始的位置。<BR>InputCount 这个值为输入数据队列中,数据的个数。<BR><BR>kbdclass 
      的自定义的设备扩展中,保存着一些指针和计数值,用来使用它的那个输入数据队列。<BR>PKEYBOARD_INPUT_DATA 类型的 
      InputData , DataIn , DataOut , DataEnd。ULONG 类型的 InputCount。<BR>InputData 
      这个指针,指向输入数据队列的开头。<BR>DataEnd 这个指针,指向输入数据队列的结尾。<BR>DataIn 
      这个指针,指向要进入队列的新数据,将要被放在队列中的位置。<BR>DataOut 
      这个指针,指向要出队列的数据,在队列中开始的位置。<BR>InputCount 
      这个值为输入数据队列中,数据的个数。<BR><BR>对于这两个输入数据队列,<BR>放入一个数据,这个数据应该被放在 DataIn 
      处,然后DataIn 后移一格,InputCount 加 1。当 DataIn 
      移到队列的结尾时,将从队列的开头重新开始。<BR>取出一个数据,这个数据应该从 DataOut 处取出,然后 DataOut 
      后移一格,InputCount 减 1。当 DataOut 移到队列的结尾时,将从队列的开头重新开始。<BR><BR>对于 i8042prt 
      的输入数据队列。键盘中断服务例程中,从i8042读出的按键信息,放入i8042prt的输入数据队列。上层处理输入的回调函数中,取出i8042prt的输入数据队列中的数据。<BR><BR>对于 
      kbdclass 的输入数据队列。只有当那个等待的 IRP_MJ_READ IRP 
      要求读的大小,小于i8042prt的输入队列中放入的数据时,才被使用。也就是说,只有当那个等着读的 IRP 读不完输入数据时,才使用 
      kbdclass 的输入数据队列。当那个等着读的 IRP 
      读完要求的数据后,还有剩余时,剩余的数据放入kbdclass的输入数据队列。下一次的应用层发来的 IRP_MJ_READ 
      IRP,当发现kbdclass的输入数据队列中有数据时,直接从kbdclass的输入数据队列中读出数据。<BR><BR>下面我们对键盘驱动处理键盘按键的过程做一个概括介绍<BR><BR>当键盘上一个键被按下时,产生一个 
      Make Code ,引发键盘中断。<BR><BR>当键盘上一个键被松开时,产生一个 Break 
      Code,引发键盘中断。<BR><BR>键盘中断导致键盘中断服务例程被执行。导致最终 i8042prt 
      的I8042KeyboardInterruptService 被执行。I8042KeyboardInterruptService 
      中,从端口读出按键的扫描码,放在一个 KEYBOARD_INPUT_DATA 中。将这个 KEYBOARD_INPUT_DATA 放入 
      i8042prt 的输入数据队列中,一个中断放入一个数据,DataIn 后移一格,InputCount 加 1。最后会 
      KeInsertQueueDpc 一个进行更多处理的 DPC(延迟过程调用)。<BR><BR>DPC 中,调用上层处理输入的回调函数(也就是 
      kbdclass 
      处理输入数据的函数),取走i8042prt的输入数据队列里的数据。因为设备扩展中保存着上层处理输入数据的回调函数的入口地址,所以他知道该调用谁。上层处理输入的回调函数(也就是 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -