📄 keyboard.c
字号:
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/input/i8042prt/keyboard.c
* PURPOSE: i8042 (ps/2 keyboard-mouse controller) driver
* keyboard specifics
* PROGRAMMER: Victor Kirhenshtein (sauros@iname.com)
* Jason Filby (jasonfilby@yahoo.com)
* Tinus
*/
/* INCLUDES ****************************************************************/
#include "i8042prt.h"
#ifdef __REACTOS__
#include "kdfuncs.h"
#endif
#ifndef NDEBUG
#define NDEBUG
#endif
#include <debug.h>
/* GLOBALS *******************************************************************/
static UCHAR TypematicTable[] = {
0x00, 0x00, 0x00, 0x05, 0x08, 0x0B, 0x0D, 0x0F, 0x10, 0x12, /* 0-9 */
0x13, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1A, /* 10-19 */
0x1B, 0x1C, 0x1C, 0x1C, 0x1D, 0x1D, 0x1E };
typedef struct _LOCAL_KEYBOARD_INDICATOR_TRANSLATION {
USHORT NumberOfIndicatorKeys;
INDICATOR_LIST IndicatorList[3];
} LOCAL_KEYBOARD_INDICATOR_TRANSLATION, *PLOCAL_KEYBOARD_INDICATOR_TRANSLATION;
static LOCAL_KEYBOARD_INDICATOR_TRANSLATION IndicatorTranslation = { 3, {
{0x3A, KEYBOARD_CAPS_LOCK_ON},
{0x45, KEYBOARD_NUM_LOCK_ON},
{0x46, KEYBOARD_SCROLL_LOCK_ON}}};
static IO_WORKITEM_ROUTINE I8042DebugWorkItem;
static VOID STDCALL I8042DebugWorkItem(PDEVICE_OBJECT DeviceObject,
PVOID Context);
/* FUNCTIONS *****************************************************************/
/*
* These functions are callbacks for filter driver custom interrupt
* service routines.
*/
VOID STDCALL I8042IsrWritePortKbd(PVOID Context,
UCHAR Value)
{
I8042IsrWritePort(Context, Value, 0);
}
static VOID STDCALL I8042QueueKeyboardPacket(PVOID Context)
{
PDEVICE_OBJECT DeviceObject = Context;
PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
DevExt->KeyComplete = TRUE;
DevExt->KeysInBuffer++;
if (DevExt->KeysInBuffer >
DevExt->KeyboardAttributes.InputDataQueueLength) {
DPRINT1("Keyboard buffer overflow\n");
DevExt->KeysInBuffer--;
}
DPRINT("Irq completes key\n");
KeInsertQueueDpc(&DevExt->DpcKbd, DevExt, NULL);
}
/*
* These functions are callbacks for filter driver custom
* initialization routines.
*/
NTSTATUS STDCALL I8042SynchWritePortKbd(PVOID Context,
UCHAR Value,
BOOLEAN WaitForAck)
{
return I8042SynchWritePort((PDEVICE_EXTENSION)Context,
0,
Value,
WaitForAck);
}
BOOLEAN STDCALL I8042InterruptServiceKbd(struct _KINTERRUPT *Interrupt,
VOID * Context)
{
UCHAR Output;
UCHAR PortStatus;
NTSTATUS Status;
PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION) Context;
BOOLEAN HookContinue = FALSE, HookReturn;
ULONG Iterations = 0;
KEYBOARD_INPUT_DATA *InputData =
DevExt->KeyboardBuffer + DevExt->KeysInBuffer;
do {
Status = I8042ReadStatus(&PortStatus);
DPRINT("PortStatus: %x\n", PortStatus);
Status = I8042ReadData(&Output);
Iterations++;
if (STATUS_SUCCESS == Status)
break;
KeStallExecutionProcessor(1);
} while (Iterations < DevExt->Settings.PollStatusIterations);
if (STATUS_SUCCESS != Status) {
DPRINT("Spurious I8042 interrupt\n");
return FALSE;
}
DPRINT("Got: %x\n", Output);
if (DevExt->KeyboardHook.IsrRoutine) {
HookReturn = DevExt->KeyboardHook.IsrRoutine(
DevExt->KeyboardHook.Context,
InputData,
&DevExt->Packet,
PortStatus,
&Output,
&HookContinue,
&DevExt->KeyboardScanState);
if (!HookContinue)
return HookReturn;
}
if (I8042PacketIsr(DevExt, Output)) {
if (DevExt->PacketComplete) {
DPRINT("Packet complete\n");
KeInsertQueueDpc(&DevExt->DpcKbd, DevExt, NULL);
}
DPRINT("Irq eaten by packet\n");
return TRUE;
}
DPRINT("Irq is keyboard input\n");
if (Normal == DevExt->KeyboardScanState) {
switch (Output) {
case 0xe0:
DevExt->KeyboardScanState = GotE0;
return TRUE;
case 0xe1:
DevExt->KeyboardScanState = GotE1;
return TRUE;
default:
;/* continue */
}
}
InputData->Flags = 0;
switch (DevExt->KeyboardScanState) {
case GotE0:
InputData->Flags |= KEY_E0;
break;
case GotE1:
InputData->Flags |= KEY_E1;
break;
default:
;
}
DevExt->KeyboardScanState = Normal;
if (Output & 0x80)
InputData->Flags |= KEY_BREAK;
else
InputData->Flags |= KEY_MAKE;
InputData->MakeCode = Output & 0x7f;
I8042QueueKeyboardPacket(DevExt->KeyboardObject);
return TRUE;
}
VOID STDCALL I8042DpcRoutineKbd(PKDPC Dpc,
PVOID DeferredContext,
PVOID SystemArgument1,
PVOID SystemArgument2)
{
PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)SystemArgument1;
ULONG KeysTransferred = 0;
ULONG KeysInBufferCopy;
KIRQL Irql;
I8042PacketDpc(DevExt);
if (!DevExt->KeyComplete)
return;
/* We got the interrupt as it was being enabled, too bad */
if (!DevExt->HighestDIRQLInterrupt)
return;
Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
DevExt->KeyComplete = FALSE;
KeysInBufferCopy = DevExt->KeysInBuffer;
KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
/* Test for TAB (debugging) */
if (DevExt->Settings.CrashSysRq) {
PKEYBOARD_INPUT_DATA InputData = DevExt->KeyboardBuffer +
KeysInBufferCopy - 1;
if (InputData->MakeCode == 0x0F) {
DPRINT("Tab!\n");
DevExt->TabPressed = !(InputData->Flags & KEY_BREAK);
} else if (DevExt->TabPressed) {
DPRINT ("Queueing work item %x\n", DevExt->DebugWorkItem);
DevExt->DebugKey = InputData->MakeCode;
DevExt->TabPressed = FALSE;
IoQueueWorkItem(DevExt->DebugWorkItem,
&(I8042DebugWorkItem),
DelayedWorkQueue,
DevExt);
}
}
DPRINT ("Send a key\n");
if (!DevExt->KeyboardData.ClassService)
return;
((PSERVICE_CALLBACK_ROUTINE) DevExt->KeyboardData.ClassService)(
DevExt->KeyboardData.ClassDeviceObject,
DevExt->KeyboardBuffer,
DevExt->KeyboardBuffer + KeysInBufferCopy,
&KeysTransferred);
Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
DevExt->KeysInBuffer -= KeysTransferred;
KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
}
/* You have to send the rate/delay in a somewhat awkward format */
static UCHAR I8042GetTypematicByte(USHORT Rate, USHORT Delay)
{
UCHAR ret;
if (Rate < 3) {
ret = 0x0;
} else if (Rate > 26) {
ret = 0x1F;
} else {
ret = TypematicTable[Rate];
}
if (Delay < 375) {
;
} else if (Delay < 625) {
ret |= 0x20;
} else if (Delay < 875) {
ret |= 0x40;
} else {
ret |= 0x60;
}
return ret;
}
/*
* Process the keyboard internal device requests
* returns FALSE if it doesn't understand the
* call so someone else can handle it.
*/
BOOLEAN STDCALL I8042StartIoKbd(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
PIO_STACK_LOCATION Stk;
PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
Stk = IoGetCurrentIrpStackLocation(Irp);
switch (Stk->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_INTERNAL_I8042_KEYBOARD_WRITE_BUFFER:
I8042StartPacket(
DevExt,
DeviceObject,
Stk->Parameters.DeviceIoControl.Type3InputBuffer,
Stk->Parameters.DeviceIoControl.InputBufferLength,
Irp);
break;
case IOCTL_KEYBOARD_SET_INDICATORS:
DevExt->PacketBuffer[0] = 0xED;
DevExt->PacketBuffer[1] = 0;
if (DevExt->KeyboardIndicators.LedFlags & KEYBOARD_CAPS_LOCK_ON)
DevExt->PacketBuffer[1] |= 0x04;
if (DevExt->KeyboardIndicators.LedFlags & KEYBOARD_NUM_LOCK_ON)
DevExt->PacketBuffer[1] |= 0x02;
if (DevExt->KeyboardIndicators.LedFlags & KEYBOARD_SCROLL_LOCK_ON)
DevExt->PacketBuffer[1] |= 0x01;
I8042StartPacket(DevExt,
DeviceObject,
DevExt->PacketBuffer,
2,
Irp);
break;
case IOCTL_KEYBOARD_SET_TYPEMATIC:
DevExt->PacketBuffer[0] = 0xF3;
DevExt->PacketBuffer[1] = I8042GetTypematicByte(
DevExt->KeyboardTypematic.Rate,
DevExt->KeyboardTypematic.Delay);
I8042StartPacket(DevExt,
DeviceObject,
DevExt->PacketBuffer,
2,
Irp);
break;
default:
return FALSE;
}
return TRUE;
}
/*
* Runs the keyboard IOCTL_INTERNAL dispatch.
* Returns NTSTATUS_INVALID_DEVICE_REQUEST if it doesn't handle this request
* so someone else can have a try at it.
*/
NTSTATUS STDCALL I8042InternalDeviceControlKbd(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
PIO_STACK_LOCATION Stk;
PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
DPRINT("InternalDeviceControl\n");
Irp->IoStatus.Information = 0;
Stk = IoGetCurrentIrpStackLocation(Irp);
switch (Stk->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_INTERNAL_KEYBOARD_CONNECT:
DPRINT("IOCTL_INTERNAL_KEYBOARD_CONNECT\n");
if (Stk->Parameters.DeviceIoControl.InputBufferLength <
sizeof(CONNECT_DATA)) {
DPRINT1("Keyboard IOCTL_INTERNAL_KEYBOARD_CONNECT "
"invalid buffer size\n");
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
goto intcontfailure;
}
if (!DevExt->KeyboardExists) {
Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
goto intcontfailure;
}
if (DevExt->KeyboardClaimed) {
DPRINT1("IOCTL_INTERNAL_KEYBOARD_CONNECT: "
"Keyboard is already claimed\n");
Irp->IoStatus.Status = STATUS_SHARING_VIOLATION;
goto intcontfailure;
}
memcpy(&DevExt->KeyboardData,
Stk->Parameters.DeviceIoControl.Type3InputBuffer,
sizeof(CONNECT_DATA));
DevExt->KeyboardHook.IsrWritePort = I8042IsrWritePortKbd;
DevExt->KeyboardHook.QueueKeyboardPacket =
I8042QueueKeyboardPacket;
DevExt->KeyboardHook.CallContext = DevExt;
{
PIO_WORKITEM WorkItem;
PI8042_HOOK_WORKITEM WorkItemData;
WorkItem = IoAllocateWorkItem(DeviceObject);
if (!WorkItem) {
DPRINT ("IOCTL_INTERNAL_KEYBOARD_CONNECT: "
"Can't allocate work item\n");
Irp->IoStatus.Status =
STATUS_INSUFFICIENT_RESOURCES;
goto intcontfailure;
}
WorkItemData = ExAllocatePoolWithTag(
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -