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 + -
显示快捷键?