i8042prt.c
来自「一个类似windows」· C语言 代码 · 共 925 行 · 第 1/2 页
C
925 行
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/input/i8042prt/i8042prt.c
* PURPOSE: i8042 (ps/2 keyboard-mouse controller) driver
* PROGRAMMER: Victor Kirhenshtein (sauros@iname.com)
* Jason Filby (jasonfilby@yahoo.com)
* Tinus
*/
/* INCLUDES ****************************************************************/
#define NDEBUG
#include <debug.h>
#include "i8042prt.h"
/* GLOBALS *******************************************************************/
/*
* Driver data
*/
#define I8042_TIMEOUT 500000
#define I8042_MAX_COMMAND_LENGTH 16
#define I8042_MAX_UPWARDS_STACK 5
UNICODE_STRING I8042RegistryPath;
/* FUNCTIONS *****************************************************************/
/*
* FUNCTION: Write data to a port, waiting first for it to become ready
*/
BOOLEAN I8042Write(PDEVICE_EXTENSION DevExt, PUCHAR addr, UCHAR data)
{
ULONG ResendIterations = DevExt->Settings.PollingIterations;
while ((KBD_IBF & READ_PORT_UCHAR(I8042_CTRL_PORT)) &&
(ResendIterations--))
{
KeStallExecutionProcessor(50);
}
if (ResendIterations) {
WRITE_PORT_UCHAR(addr,data);
DPRINT("Sent %x to %x\n", data, addr);
return TRUE;
}
return FALSE;
}
#if 0 /* function is not needed */
/*
* FUNCTION: Write data to a port, without waiting first
*/
static BOOLEAN I8042WriteNoWait(PDEVICE_EXTENSION DevExt, int addr, UCHAR data)
{
WRITE_PORT_UCHAR((PUCHAR)addr,data);
DPRINT("Sent %x to %x\n", data, addr);
return TRUE;
}
#endif
/*
* FUNCTION: Read data from port 0x60
*/
NTSTATUS I8042ReadData(UCHAR *Data)
{
UCHAR Status;
Status=READ_PORT_UCHAR(I8042_CTRL_PORT);
// If data is available
if ((Status & KBD_OBF)) {
Data[0]=READ_PORT_UCHAR((PUCHAR)I8042_DATA_PORT);
DPRINT("Read: %x (status: %x)\n", Data[0], Status);
// If the data is valid (not timeout, not parity error)
if (0 == (Status & KBD_PERR))
return STATUS_SUCCESS;
}
return STATUS_UNSUCCESSFUL;
}
NTSTATUS I8042ReadStatus(UCHAR *Status)
{
Status[0]=READ_PORT_UCHAR(I8042_CTRL_PORT);
return STATUS_SUCCESS;
}
/*
* FUNCTION: Read data from port 0x60
*/
NTSTATUS I8042ReadDataWait(PDEVICE_EXTENSION DevExt, UCHAR *Data)
{
ULONG Counter = DevExt->Settings.PollingIterations;
NTSTATUS Status;
while (Counter--) {
Status = I8042ReadData(Data);
if (STATUS_SUCCESS == Status)
return Status;
KeStallExecutionProcessor(50);
}
// Timed out
return STATUS_IO_TIMEOUT;
}
VOID I8042Flush()
{
UCHAR Ignore;
while (STATUS_SUCCESS == I8042ReadData(&Ignore)) {
DPRINT("Data flushed\n"); /* drop */
}
}
VOID STDCALL I8042IsrWritePort(PDEVICE_EXTENSION DevExt,
UCHAR Value,
UCHAR SelectCmd)
{
if (SelectCmd)
if (!I8042Write(DevExt, I8042_CTRL_PORT, SelectCmd))
return;
I8042Write(DevExt, I8042_DATA_PORT, Value);
}
/*
* These functions are callbacks for filter driver custom
* initialization routines.
*/
NTSTATUS STDCALL I8042SynchWritePort(PDEVICE_EXTENSION DevExt,
UCHAR Port,
UCHAR Value,
BOOLEAN WaitForAck)
{
NTSTATUS Status;
UCHAR Ack;
ULONG ResendIterations = DevExt->Settings.ResendIterations + 1;
do {
if (Port)
if (!I8042Write(DevExt, I8042_DATA_PORT, Port))
{
DPRINT1("Failed to write Port\n");
return STATUS_IO_TIMEOUT;
}
if (!I8042Write(DevExt, I8042_DATA_PORT, Value))
{
DPRINT1("Failed to write Value\n");
return STATUS_IO_TIMEOUT;
}
if (WaitForAck) {
Status = I8042ReadDataWait(DevExt, &Ack);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to read Ack\n");
return Status;
}
if (Ack == KBD_ACK)
return STATUS_SUCCESS;
if (Ack != KBD_RESEND)
{
DPRINT1("Unexpected Ack 0x%x\n", Ack);
return STATUS_UNEXPECTED_IO_ERROR;
}
} else {
return STATUS_SUCCESS;
}
DPRINT("Reiterating\n");
ResendIterations--;
} while (ResendIterations);
return STATUS_IO_TIMEOUT;
}
/*
* This one reads a value from the port; You don't have to specify
* which one, it'll always be from the one you talked to, so one function
* is enough this time. Note how MSDN specifies the
* WaitForAck parameter to be ignored.
*/
static NTSTATUS STDCALL I8042SynchReadPort(PVOID Context,
PUCHAR Value,
BOOLEAN WaitForAck)
{
PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)Context;
return I8042ReadDataWait(DevExt, Value);
}
/* Write the current byte of the packet. Returns FALSE in case
* of problems.
*/
static BOOLEAN STDCALL I8042PacketWrite(PDEVICE_EXTENSION DevExt)
{
UCHAR Port = DevExt->PacketPort;
if (Port) {
if (!I8042Write(DevExt,
I8042_CTRL_PORT,
Port)) {
/* something is really wrong! */
DPRINT1("Failed to send packet byte!\n");
return FALSE;
}
}
return I8042Write(DevExt,
I8042_DATA_PORT,
DevExt->Packet.Bytes[DevExt->Packet.CurrentByte]);
}
/*
* This function starts a packet. It must be called with the
* correct DIRQL.
*/
NTSTATUS STDCALL I8042StartPacket(PDEVICE_EXTENSION DevExt,
PDEVICE_OBJECT Device,
PUCHAR Bytes,
ULONG ByteCount,
PIRP Irp)
{
KIRQL Irql;
NTSTATUS Status;
PFDO_DEVICE_EXTENSION FdoDevExt = Device->DeviceExtension;
Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
DevExt->CurrentIrp = Irp;
DevExt->CurrentIrpDevice = Device;
if (Idle != DevExt->Packet.State) {
Status = STATUS_DEVICE_BUSY;
goto startpacketdone;
}
DevExt->Packet.Bytes = Bytes;
DevExt->Packet.CurrentByte = 0;
DevExt->Packet.ByteCount = ByteCount;
DevExt->Packet.State = SendingBytes;
DevExt->PacketResult = Status = STATUS_PENDING;
if (Mouse == FdoDevExt->Type)
DevExt->PacketPort = 0xD4;
else
DevExt->PacketPort = 0;
if (!I8042PacketWrite(DevExt)) {
Status = STATUS_IO_TIMEOUT;
DevExt->Packet.State = Idle;
DevExt->PacketResult = STATUS_ABANDONED;
goto startpacketdone;
}
DevExt->Packet.CurrentByte++;
startpacketdone:
KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
if (STATUS_PENDING != Status) {
DevExt->CurrentIrp = NULL;
DevExt->CurrentIrpDevice = NULL;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
return Status;
}
BOOLEAN STDCALL I8042PacketIsr(PDEVICE_EXTENSION DevExt,
UCHAR Output)
{
if (Idle == DevExt->Packet.State)
return FALSE;
switch (Output) {
case KBD_RESEND:
DevExt->PacketResends++;
if (DevExt->PacketResends > DevExt->Settings.ResendIterations) {
DevExt->Packet.State = Idle;
DevExt->PacketComplete = TRUE;
DevExt->PacketResult = STATUS_IO_TIMEOUT;
DevExt->PacketResends = 0;
return TRUE;
}
DevExt->Packet.CurrentByte--;
break;
case KBD_NACK:
DevExt->Packet.State = Idle;
DevExt->PacketComplete = TRUE;
DevExt->PacketResult = STATUS_UNEXPECTED_IO_ERROR;
DevExt->PacketResends = 0;
return TRUE;
default:
DevExt->PacketResends = 0;
}
if (DevExt->Packet.CurrentByte >= DevExt->Packet.ByteCount) {
DevExt->Packet.State = Idle;
DevExt->PacketComplete = TRUE;
DevExt->PacketResult = STATUS_SUCCESS;
return TRUE;
}
if (!I8042PacketWrite(DevExt)) {
DevExt->Packet.State = Idle;
DevExt->PacketComplete = TRUE;
DevExt->PacketResult = STATUS_IO_TIMEOUT;
return TRUE;
}
DevExt->Packet.CurrentByte++;
return TRUE;
}
VOID I8042PacketDpc(PDEVICE_EXTENSION DevExt)
{
BOOLEAN FinishIrp = FALSE;
NTSTATUS Result = STATUS_INTERNAL_ERROR; /* Shouldn't happen */
KIRQL Irql;
/* If the interrupt happens before this is setup, the key
* was already in the buffer. Too bad! */
if (!DevExt->HighestDIRQLInterrupt)
return;
Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
if (Idle == DevExt->Packet.State &&
DevExt->PacketComplete) {
FinishIrp = TRUE;
Result = DevExt->PacketResult;
DevExt->PacketComplete = FALSE;
}
KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt,
Irql);
if (!FinishIrp)
return;
if (DevExt->CurrentIrp) {
DevExt->CurrentIrp->IoStatus.Status = Result;
IoCompleteRequest(DevExt->CurrentIrp, IO_NO_INCREMENT);
IoStartNextPacket(DevExt->CurrentIrpDevice, FALSE);
DevExt->CurrentIrp = NULL;
DevExt->CurrentIrpDevice = NULL;
}
}
VOID STDCALL I8042SendHookWorkItem(PDEVICE_OBJECT DeviceObject,
PVOID Context)
{
KEVENT Event;
IO_STATUS_BLOCK IoStatus;
NTSTATUS Status;
PDEVICE_EXTENSION DevExt;
PFDO_DEVICE_EXTENSION FdoDevExt;
PIRP NewIrp;
PI8042_HOOK_WORKITEM WorkItemData = (PI8042_HOOK_WORKITEM)Context;
ULONG IoControlCode;
PVOID InputBuffer;
ULONG InputBufferLength;
BOOLEAN IsKbd;
DPRINT("HookWorkItem\n");
FdoDevExt = (PFDO_DEVICE_EXTENSION)
DeviceObject->DeviceExtension;
DevExt = FdoDevExt->PortDevExt;
if (WorkItemData->Target == DevExt->KeyboardData.ClassDeviceObject) {
IoControlCode = IOCTL_INTERNAL_I8042_HOOK_KEYBOARD;
InputBuffer = &DevExt->KeyboardHook;
InputBufferLength = sizeof(INTERNAL_I8042_HOOK_KEYBOARD);
IsKbd = TRUE;
DPRINT ("is for keyboard.\n");
} else if (WorkItemData->Target == DevExt->MouseData.ClassDeviceObject){
IoControlCode = IOCTL_INTERNAL_I8042_HOOK_MOUSE;
InputBuffer = &DevExt->MouseHook;
InputBufferLength = sizeof(INTERNAL_I8042_HOOK_MOUSE);
IsKbd = FALSE;
DPRINT ("is for mouse.\n");
} else {
DPRINT1("I8042SendHookWorkItem: Can't find DeviceObject\n");
WorkItemData->Irp->IoStatus.Status = STATUS_INTERNAL_ERROR;
goto hookworkitemdone;
}
KeInitializeEvent(&Event, NotificationEvent, FALSE);
NewIrp = IoBuildDeviceIoControlRequest(
IoControlCode,
WorkItemData->Target,
InputBuffer,
InputBufferLength,
NULL,
0,
TRUE,
&Event,
&IoStatus);
if (!NewIrp) {
DPRINT("IOCTL_INTERNAL_(device)_CONNECT: "
"Can't allocate IRP\n");
WorkItemData->Irp->IoStatus.Status =
STATUS_INSUFFICIENT_RESOURCES;
goto hookworkitemdone;
}
#if 0
Status = IoCallDriver(
WorkItemData->Target,
NewIrp);
if (STATUS_PENDING == Status)
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
#endif
if (IsKbd) {
/* Call the hooked initialization if it exists */
if (DevExt->KeyboardHook.InitializationRoutine) {
Status = DevExt->KeyboardHook.InitializationRoutine(
DevExt->KeyboardHook.Context,
DevExt,
I8042SynchReadPort,
I8042SynchWritePortKbd,
FALSE);
if (!NT_SUCCESS(Status)) {
WorkItemData->Irp->IoStatus.Status = Status;
goto hookworkitemdone;
}
}
/* TODO: Now would be the right time to enable the interrupt */
DevExt->KeyboardClaimed = TRUE;
} else {
/* Mouse doesn't have this, but we need to send a
* reset to start the detection.
*/
KIRQL Irql;
Irql = KeAcquireInterruptSpinLock(
DevExt->HighestDIRQLInterrupt);
I8042Write(DevExt, I8042_CTRL_PORT, 0xD4);
I8042Write(DevExt, I8042_DATA_PORT, 0xFF);
KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?