reactos操作系统实现(99)
下面来分析键盘的中断处理函数的实现,如下: #001 BOOLEAN NTAPI #002 i8042KbdInterruptService( #003 IN PKINTERRUPT Interrupt, #004 PVOID Context) #005 { #006 PI8042_KEYBOARD_EXTENSION DeviceExtension; #007 PPORT_DEVICE_EXTENSION PortDeviceExtension; #008 PKEYBOARD_INPUT_DATA InputData; #009 ULONG Counter; #010 UCHAR PortStatus,Output; #011 BOOLEAN ToReturn = FALSE; #012 NTSTATUS Status; #013
这个上下文参数,其实就是设备驱动程序里的设备扩展对象。 #014 DeviceExtension = (PI8042_KEYBOARD_EXTENSION)Context;
这里获取端口扩展属性。 #015 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
计算缓冲区里放入新按键位置。 #016 InputData = DeviceExtension->KeyboardBuffer + DeviceExtension->KeysInBuffer;
读取端口轮询的最大次数。 #017 Counter = PortDeviceExtension->Settings.PollStatusIterations; #018 #019 while (Counter) #020 {
通过键盘端口读取状态。 #021 Status = i8042ReadStatus(PortDeviceExtension,&PortStatus); #022 if (!NT_SUCCESS(Status)) #023 { #024 WARN_(I8042PRT,"i8042ReadStatus() failed with status 0x%08lx/n",Status); #025 return FALSE; #026 }
读取键盘输入的数据。 #027 Status = i8042ReadKeyboardData(PortDeviceExtension,&Output); #028 if (NT_SUCCESS(Status)) #029 break;
暂停一会再进入下一次尝试读取。 #030 KeStallExecutionProcessor(1); #031 Counter--; #032 }
轮询次数计数为0,那么说明读取键盘硬件失败。 #033 if (Counter == 0) #034 { #035 WARN_(I8042PRT,"Spurious i8042 keyboard interrupt/n"); #036 return FALSE; #037 } #038 #039 INFO_(I8042PRT,"Got: 0x%02x/n",Output); #040
判断是否是CTRL + SCROLL组合键。 #041 if (PortDeviceExtension->Settings.CrashOnCtrlScroll) #042 { #043 /* Test for CTRL + SCROLL LOCK twice */ #044 static const UCHAR ScanCodes[] = { 0xe0,0x1d,0x46,0xc6,0 }; #045 #046 if (Output == ScanCodes[DeviceExtension->ComboPosition]) #047 { #048 DeviceExtension->ComboPosition++; #049 if (ScanCodes[DeviceExtension->ComboPosition] == 0) #050 KeBugCheck(MANUALLY_INITIATED_CRASH); #051 } #052 else if (Output == ScanCodes[0]) #053 DeviceExtension->ComboPosition = 1; #054 else #055 DeviceExtension->ComboPosition = 0; #056 } #057
调用ISR的回调函数,如果已经处理这个按键就返回。 #058 if (i8042KbdCallIsrHook(DeviceExtension,PortStatus,Output,&ToReturn)) #059 return ToReturn; #060
处理键盘的数据是否需要重新输出到键盘。 #061 if (i8042PacketIsr(PortDeviceExtension,Output)) #062 { #063 if (PortDeviceExtension->PacketComplete) #064 { #065 TRACE_(I8042PRT,"Packet complete/n"); #066 KeInsertQueueDpc(&DeviceExtension->DpcKeyboard,NULL,NULL); #067 } #068 TRACE_(I8042PRT,"Irq eaten by packet/n"); #069 return TRUE; #070 } #071 #072 TRACE_(I8042PRT,"Irq is keyboard input/n"); #073
到这里,已经说明是键盘输入数据。 #074 if (DeviceExtension->KeyboardScanState == Normal) #075 {
判断扫描码是E0码,还是E1码。 #076 switch (Output) #077 { #078 case 0xe0: #079 DeviceExtension->KeyboardScanState = GotE0; #080 return TRUE; #081 case 0xe1: #082 DeviceExtension->KeyboardScanState = GotE1; #083 return TRUE; #084 default: #085 break; #086 } #087 } #088
更新输入的数据。 #089 /* Update InputData */ #090 InputData->Flags = 0;
置位是E0码,还是E1码。 #091 switch (DeviceExtension->KeyboardScanState) #092 { #093 case GotE0: #094 InputData->Flags |= KEY_E0; #095 break; #096 case GotE1: #097 InputData->Flags |= KEY_E1; #098 break; #099 default: #100 break; #101 }
设置按键是按下码,还是弹起码。 当键盘上有键被按下,松开,按住,键盘将产生扫描码( Scan Code ),这些扫描码将被 i8048 直接得到。扫描码有两种,Make Code 和 Break Code。当一个键被按下或按住时产生的是 Make Code ,当一个键被松开产生的是 Break Code。每个键被分配了唯一的 Make Code 和 Break Code ,这样主机通过扫描码就可以知道是哪一个键。简单的说就是按下键,产生一个 Make Code。松开键,产生一个 Break Code。 #102 DeviceExtension->KeyboardScanState = Normal; #103 if (Output & 0x80) #104 InputData->Flags |= KEY_BREAK; #105 else #106 InputData->Flags |= KEY_MAKE;
只需要保存低7F值。 #107 InputData->MakeCode = Output & 0x7f; #108 InputData->Reserved = 0; #109
调用键盘队列返回包。其实这个函数是把数据放到环形缓冲区里,然后插入一个事件到DPC队列里,再由DPC队列来完成每个IRP包。 #110 DeviceExtension->KeyboardHook.QueueKeyboardPacket(DeviceExtension->KeyboardHook.CallContext); #111 #112 return TRUE; #113 } #114 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |