--- /dev/null
+/*
+ * PROJECT: ReactOS i8042 (ps/2 keyboard-mouse controller) driver
+ * LICENSE: GPL - See COPYING in the top level directory
+ * FILE: drivers/input/i8042prt/mouse.c
+ * PURPOSE: Mouse specific functions
+ * PROGRAMMERS: Copyright Victor Kirhenshtein (sauros@iname.com)
+ Copyright Jason Filby (jasonfilby@yahoo.com)
+ Copyright Martijn Vernooij (o112w8r02@sneakemail.com)
+ Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org)
+ Copyright 2008 Colin Finck (mail@colinfinck.de)
+ */
+
+/* INCLUDES ****************************************************************/
+
+#include "i8042prt.h"
+
+#include <debug.h>
+
+/* FUNCTIONS *****************************************************************/
+
+static KDEFERRED_ROUTINE i8042MouDpcRoutine;
+static KDEFERRED_ROUTINE i8042DpcRoutineMouseTimeout;
+
+/*
+ * These functions are callbacks for filter driver custom interrupt
+ * service routines.
+ */
+static VOID NTAPI
+i8042MouIsrWritePort(
+ IN PVOID Context,
+ IN UCHAR Value)
+{
+ PI8042_MOUSE_EXTENSION DeviceExtension;
+
+ DeviceExtension = (PI8042_MOUSE_EXTENSION)Context;
+
+ if (DeviceExtension->MouseHook.IsrWritePort != i8042MouIsrWritePort)
+ {
+ DeviceExtension->MouseHook.IsrWritePort(
+ DeviceExtension->MouseHook.CallContext,
+ Value);
+ }
+ else
+ i8042IsrWritePort(DeviceExtension->Common.PortDeviceExtension, Value, CTRL_WRITE_MOUSE);
+}
+
+static VOID NTAPI
+i8042MouQueuePacket(
+ IN PVOID Context)
+{
+ PI8042_MOUSE_EXTENSION DeviceExtension;
+
+ DeviceExtension = (PI8042_MOUSE_EXTENSION)Context;
+
+ DeviceExtension->MouseComplete = TRUE;
+ DeviceExtension->MouseInBuffer++;
+ if (DeviceExtension->MouseInBuffer >= DeviceExtension->Common.PortDeviceExtension->Settings.MouseDataQueueSize)
+ {
+ WARN_(I8042PRT, "Mouse buffer overflow\n");
+ DeviceExtension->MouseInBuffer--;
+ }
+
+ TRACE_(I8042PRT, "Irq completes mouse packet\n");
+ KeInsertQueueDpc(&DeviceExtension->DpcMouse, NULL, NULL);
+}
+
+VOID
+i8042MouHandle(
+ IN PI8042_MOUSE_EXTENSION DeviceExtension,
+ IN UCHAR Output)
+{
+ PMOUSE_INPUT_DATA MouseInput;
+ CHAR Scroll;
+
+ MouseInput = DeviceExtension->MouseBuffer + DeviceExtension->MouseInBuffer;
+
+ switch (DeviceExtension->MouseState)
+ {
+ case MouseIdle:
+ /* This bit should be 1, if not drop the packet, we
+ * might be lucky and get in sync again
+ */
+ if (!(Output & 8)) {
+ WARN_(I8042PRT, "Bad input, dropping..\n");
+ return;
+ }
+
+ MouseInput->Buttons = 0;
+ MouseInput->RawButtons = 0;
+ MouseInput->Flags = MOUSE_MOVE_RELATIVE;
+
+ /* Note how we ignore the overflow bits, like Windows
+ * is said to do. There's no reasonable thing to do
+ * anyway.
+ */
+
+ if (Output & 16)
+ MouseInput->LastX = 1;
+ else
+ MouseInput->LastX = 0;
+ if (Output & 32)
+ MouseInput->LastY = 1;
+ else
+ MouseInput->LastY = 0;
+
+ if (Output & 1)
+ MouseInput->RawButtons |= MOUSE_LEFT_BUTTON_DOWN;
+ if (Output & 2)
+ MouseInput->RawButtons |= MOUSE_RIGHT_BUTTON_DOWN;
+ if (Output & 4)
+ MouseInput->RawButtons |= MOUSE_MIDDLE_BUTTON_DOWN;
+
+ DeviceExtension->MouseState = XMovement;
+ break;
+
+ case XMovement:
+ if (MouseInput->LastX)
+ MouseInput->LastX = (LONG) Output - 256;
+ else
+ MouseInput->LastX = Output;
+
+ DeviceExtension->MouseState = YMovement;
+ break;
+
+ case YMovement:
+ if (MouseInput->LastY)
+ MouseInput->LastY = (LONG)Output - 256;
+ else
+ MouseInput->LastY = (LONG)Output;
+
+ /* Windows wants it the other way around */
+ MouseInput->LastY = -MouseInput->LastY;
+
+ if (DeviceExtension->MouseType == GenericPS2 ||
+ DeviceExtension->MouseType == Ps2pp)
+ {
+ i8042MouHandleButtons(
+ DeviceExtension,
+ MOUSE_LEFT_BUTTON_DOWN |
+ MOUSE_RIGHT_BUTTON_DOWN |
+ MOUSE_MIDDLE_BUTTON_DOWN);
+ DeviceExtension->MouseHook.QueueMousePacket(DeviceExtension->MouseHook.CallContext);
+ DeviceExtension->MouseState = MouseIdle;
+ }
+ else
+ {
+ DeviceExtension->MouseState = ZMovement;
+ }
+ break;
+
+ case ZMovement:
+ Scroll = Output & 0x0f;
+ if (Scroll & 8)
+ Scroll |= 0xf0;
+
+ if (Scroll)
+ {
+ MouseInput->RawButtons |= MOUSE_WHEEL;
+ MouseInput->ButtonData = (USHORT)(Scroll * -WHEEL_DELTA);
+ }
+
+ if (DeviceExtension->MouseType == IntellimouseExplorer)
+ {
+ if (Output & 16)
+ MouseInput->RawButtons |= MOUSE_BUTTON_4_DOWN;
+ if (Output & 32)
+ MouseInput->RawButtons |= MOUSE_BUTTON_5_DOWN;
+ }
+ i8042MouHandleButtons(
+ DeviceExtension,
+ MOUSE_LEFT_BUTTON_DOWN |
+ MOUSE_RIGHT_BUTTON_DOWN |
+ MOUSE_MIDDLE_BUTTON_DOWN |
+ MOUSE_BUTTON_4_DOWN |
+ MOUSE_BUTTON_5_DOWN);
+ DeviceExtension->MouseHook.QueueMousePacket(DeviceExtension->MouseHook.CallContext);
+ DeviceExtension->MouseState = MouseIdle;
+ break;
+
+ default:
+ ERR_(I8042PRT, "Unexpected state 0x%lx!\n", DeviceExtension->MouseState);
+ ASSERT(FALSE);
+ }
+}
+
+/*
+ * Updates ButtonFlags according to RawButtons and a saved state;
+ * Only takes in account the bits that are set in Mask
+ */
+VOID
+i8042MouHandleButtons(
+ IN PI8042_MOUSE_EXTENSION DeviceExtension,
+ IN USHORT Mask)
+{
+ PMOUSE_INPUT_DATA MouseInput;
+ USHORT NewButtonData;
+ USHORT ButtonDiff;
+
+ MouseInput = DeviceExtension->MouseBuffer + DeviceExtension->MouseInBuffer;
+ NewButtonData = (USHORT)(MouseInput->RawButtons & Mask);
+ ButtonDiff = (NewButtonData ^ DeviceExtension->MouseButtonState) & Mask;
+
+ /* Note that the defines are such:
+ * MOUSE_LEFT_BUTTON_DOWN 1
+ * MOUSE_LEFT_BUTTON_UP 2
+ */
+ MouseInput->ButtonFlags |= (NewButtonData & ButtonDiff) |
+ (((~(NewButtonData)) << 1) & (ButtonDiff << 1)) |
+ (MouseInput->RawButtons & 0xfc00);
+
+ INFO_(I8042PRT, "Left raw/up/down: %u/%u/%u\n",
+ MouseInput->RawButtons & MOUSE_LEFT_BUTTON_DOWN,
+ MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_DOWN,
+ MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_UP);
+
+ DeviceExtension->MouseButtonState =
+ (DeviceExtension->MouseButtonState & ~Mask) | (NewButtonData & Mask);
+}
+
+/* Does final initializations for the mouse. This method
+ * is called just before connecting the interrupt.
+ */
+NTSTATUS
+i8042MouInitialize(
+ IN PI8042_MOUSE_EXTENSION DeviceExtension)
+{
+ NTSTATUS Status;
+ UCHAR Value;
+
+ /* Enable the PS/2 mouse port */
+ i8042Write(DeviceExtension->Common.PortDeviceExtension, DeviceExtension->Common.PortDeviceExtension->ControlPort, MOUSE_ENAB);
+
+ /* Enable the mouse */
+ if(!i8042IsrWritePort(DeviceExtension->Common.PortDeviceExtension, MOU_ENAB, CTRL_WRITE_MOUSE))
+ {
+ WARN_(I8042PRT, "Failed to enable mouse!\n");
+ return STATUS_IO_DEVICE_ERROR;
+ }
+
+ Status = i8042ReadDataWait(DeviceExtension->Common.PortDeviceExtension, &Value);
+ if (!NT_SUCCESS(Status))
+ {
+ WARN_(I8042PRT, "Failed to read the response of MOU_ENAB, status 0x%08lx\n", Status);
+ return Status;
+ }
+
+ if(Value == MOUSE_ACK)
+ {
+ INFO_(I8042PRT, "Mouse was enabled successfully!\n");
+ return STATUS_SUCCESS;
+ }
+
+ WARN_(I8042PRT, "Got 0x%02x instead of 0xFA\n", Value);
+ return STATUS_IO_DEVICE_ERROR;
+}
+
+static VOID NTAPI
+i8042MouDpcRoutine(
+ IN PKDPC Dpc,
+ IN PVOID DeferredContext,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2)
+{
+ PI8042_MOUSE_EXTENSION DeviceExtension;
+ PPORT_DEVICE_EXTENSION PortDeviceExtension;
+ ULONG MouseTransferred = 0;
+ ULONG MouseInBufferCopy;
+ KIRQL Irql;
+ LARGE_INTEGER Timeout;
+
+ UNREFERENCED_PARAMETER(Dpc);
+ UNREFERENCED_PARAMETER(SystemArgument1);
+ UNREFERENCED_PARAMETER(SystemArgument2);
+
+ __analysis_assume(DeferredContext != NULL);
+ DeviceExtension = DeferredContext;
+ PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
+
+ switch (DeviceExtension->MouseTimeoutState)
+ {
+ case TimeoutStart:
+ {
+ DeviceExtension->MouseTimeoutState = NoChange;
+ if (DeviceExtension->MouseTimeoutActive &&
+ !KeCancelTimer(&DeviceExtension->TimerMouseTimeout))
+ {
+ /* The timer fired already, give up */
+ DeviceExtension->MouseTimeoutActive = FALSE;
+ return;
+ }
+
+ Timeout.QuadPart = -15000000; /* 1.5 seconds, should be enough */
+
+ KeSetTimer(
+ &DeviceExtension->TimerMouseTimeout,
+ Timeout,
+ &DeviceExtension->DpcMouseTimeout);
+ DeviceExtension->MouseTimeoutActive = TRUE;
+ return;
+ }
+
+ case TimeoutCancel:
+ {
+ DeviceExtension->MouseTimeoutState = NoChange;
+ KeCancelTimer(&DeviceExtension->TimerMouseTimeout);
+ DeviceExtension->MouseTimeoutActive = FALSE;
+ }
+
+ default:
+ ;/* nothing, don't want a warning */
+ }
+
+ /* Should be unlikely */
+ if (!DeviceExtension->MouseComplete)
+ return;
+
+ Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt);
+
+ DeviceExtension->MouseComplete = FALSE;
+ MouseInBufferCopy = DeviceExtension->MouseInBuffer;
+
+ KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql);
+
+ TRACE_(I8042PRT, "Send a mouse packet\n");
+
+ if (!DeviceExtension->MouseData.ClassService)
+ return;
+
+ INFO_(I8042PRT, "Sending %lu mouse move(s)\n", MouseInBufferCopy);
+ (*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->MouseData.ClassService)(
+ DeviceExtension->MouseData.ClassDeviceObject,
+ DeviceExtension->MouseBuffer,
+ DeviceExtension->MouseBuffer + MouseInBufferCopy,
+ &MouseTransferred);
+
+ Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt);
+ DeviceExtension->MouseInBuffer -= MouseTransferred;
+ if (DeviceExtension->MouseInBuffer)
+ RtlMoveMemory(
+ DeviceExtension->MouseBuffer,
+ DeviceExtension->MouseBuffer + MouseTransferred,
+ DeviceExtension->MouseInBuffer * sizeof(MOUSE_INPUT_DATA));
+ KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql);
+}
+
+/* This timer DPC will be called when the mouse reset times out.
+ * I'll just send the 'disable mouse port' command to the controller
+ * and say the mouse doesn't exist.
+ */
+static VOID NTAPI
+i8042DpcRoutineMouseTimeout(
+ IN PKDPC Dpc,
+ IN PVOID DeferredContext,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2)
+{
+ PI8042_MOUSE_EXTENSION DeviceExtension;
+ PPORT_DEVICE_EXTENSION PortDeviceExtension;
+ KIRQL Irql;
+
+ UNREFERENCED_PARAMETER(Dpc);
+ UNREFERENCED_PARAMETER(SystemArgument1);
+ UNREFERENCED_PARAMETER(SystemArgument2);
+
+ __analysis_assume(DeferredContext != NULL);
+ DeviceExtension = DeferredContext;
+ PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
+
+ Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt);
+
+ WARN_(I8042PRT, "Mouse initialization timeout! (substate %x)\n",
+ DeviceExtension->MouseResetState);
+
+ PortDeviceExtension->Flags &= ~MOUSE_PRESENT;
+
+ KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql);
+}
+
+/*
+ * Runs the mouse IOCTL_INTERNAL dispatch.
+ */
+NTSTATUS NTAPI
+i8042MouInternalDeviceControl(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp)
+{
+ PIO_STACK_LOCATION Stack;
+ PI8042_MOUSE_EXTENSION DeviceExtension;
+ NTSTATUS Status;
+
+ Stack = IoGetCurrentIrpStackLocation(Irp);
+ Irp->IoStatus.Information = 0;
+ DeviceExtension = (PI8042_MOUSE_EXTENSION)DeviceObject->DeviceExtension;
+
+ switch (Stack->Parameters.DeviceIoControl.IoControlCode)
+ {
+ case IOCTL_INTERNAL_MOUSE_CONNECT:
+ {
+ SIZE_T Size;
+ PIO_WORKITEM WorkItem = NULL;
+ PI8042_HOOK_WORKITEM WorkItemData = NULL;
+
+ TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_MOUSE_CONNECT\n");
+ if (Stack->Parameters.DeviceIoControl.InputBufferLength != sizeof(CONNECT_DATA))
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ goto cleanup;
+ }
+
+ DeviceExtension->MouseData =
+ *((PCONNECT_DATA)Stack->Parameters.DeviceIoControl.Type3InputBuffer);
+
+ /* Send IOCTL_INTERNAL_I8042_HOOK_MOUSE to device stack */
+ WorkItem = IoAllocateWorkItem(DeviceObject);
+ if (!WorkItem)
+ {
+ WARN_(I8042PRT, "IoAllocateWorkItem() failed\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto cleanup;
+ }
+ WorkItemData = ExAllocatePoolWithTag(
+ NonPagedPool,
+ sizeof(I8042_HOOK_WORKITEM),
+ I8042PRT_TAG);
+ if (!WorkItemData)
+ {
+ WARN_(I8042PRT, "ExAllocatePoolWithTag() failed\n");
+ Status = STATUS_NO_MEMORY;
+ goto cleanup;
+ }
+ WorkItemData->WorkItem = WorkItem;
+ WorkItemData->Irp = Irp;
+
+ /* Initialize extension */
+ DeviceExtension->Common.Type = Mouse;
+ Size = DeviceExtension->Common.PortDeviceExtension->Settings.MouseDataQueueSize * sizeof(MOUSE_INPUT_DATA);
+ DeviceExtension->MouseBuffer = ExAllocatePoolWithTag(
+ NonPagedPool,
+ Size,
+ I8042PRT_TAG);
+ if (!DeviceExtension->MouseBuffer)
+ {
+ WARN_(I8042PRT, "ExAllocatePoolWithTag() failed\n");
+ Status = STATUS_NO_MEMORY;
+ goto cleanup;
+ }
+ RtlZeroMemory(DeviceExtension->MouseBuffer, Size);
+ DeviceExtension->MouseAttributes.InputDataQueueLength =
+ DeviceExtension->Common.PortDeviceExtension->Settings.MouseDataQueueSize;
+ KeInitializeDpc(
+ &DeviceExtension->DpcMouse,
+ i8042MouDpcRoutine,
+ DeviceExtension);
+ KeInitializeDpc(
+ &DeviceExtension->DpcMouseTimeout,
+ i8042DpcRoutineMouseTimeout,
+ DeviceExtension);
+ KeInitializeTimer(&DeviceExtension->TimerMouseTimeout);
+ DeviceExtension->Common.PortDeviceExtension->MouseExtension = DeviceExtension;
+ DeviceExtension->Common.PortDeviceExtension->Flags |= MOUSE_CONNECTED;
+
+ IoMarkIrpPending(Irp);
+ DeviceExtension->MouseState = MouseResetting;
+ DeviceExtension->MouseResetState = ExpectingReset;
+ DeviceExtension->MouseHook.IsrWritePort = i8042MouIsrWritePort;
+ DeviceExtension->MouseHook.QueueMousePacket = i8042MouQueuePacket;
+ DeviceExtension->MouseHook.CallContext = DeviceExtension;
+ IoQueueWorkItem(WorkItem,
+ i8042SendHookWorkItem,
+ DelayedWorkQueue,
+ WorkItemData);
+ Status = STATUS_PENDING;
+ break;
+
+cleanup:
+ if (DeviceExtension->MouseBuffer)
+ ExFreePoolWithTag(DeviceExtension->MouseBuffer, I8042PRT_TAG);
+ if (WorkItem)
+ IoFreeWorkItem(WorkItem);
+ if (WorkItemData)
+ ExFreePoolWithTag(WorkItemData, I8042PRT_TAG);
+ break;
+ }
+ case IOCTL_INTERNAL_MOUSE_DISCONNECT:
+ {
+ TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_MOUSE_DISCONNECT\n");
+ /* MSDN says that operation is to implemented.
+ * To implement it, we just have to do:
+ * DeviceExtension->MouseData.ClassService = NULL;
+ */
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+ case IOCTL_INTERNAL_I8042_HOOK_MOUSE:
+ {
+ PINTERNAL_I8042_HOOK_MOUSE MouseHook;
+ TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_I8042_HOOK_MOUSE\n");
+ if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(INTERNAL_I8042_HOOK_MOUSE))
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ MouseHook = (PINTERNAL_I8042_HOOK_MOUSE)Stack->Parameters.DeviceIoControl.Type3InputBuffer;
+
+ DeviceExtension->MouseHook.Context = MouseHook->Context;
+ if (MouseHook->IsrRoutine)
+ DeviceExtension->MouseHook.IsrRoutine = MouseHook->IsrRoutine;
+
+ Status = STATUS_SUCCESS;
+ break;
+ }
+ case IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER:
+ {
+ DPRINT1("IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER not implemented\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+ case IOCTL_INTERNAL_I8042_MOUSE_START_INFORMATION:
+ {
+ DPRINT1("IOCTL_INTERNAL_I8042_MOUSE_START_INFORMATION not implemented\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+ case IOCTL_MOUSE_QUERY_ATTRIBUTES:
+ {
+ TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_MOUSE_QUERY_ATTRIBUTES\n");
+ if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUSE_ATTRIBUTES))
+ {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ *(PMOUSE_ATTRIBUTES) Irp->AssociatedIrp.SystemBuffer = DeviceExtension->MouseAttributes;
+ Irp->IoStatus.Information = sizeof(MOUSE_ATTRIBUTES);
+ Status = STATUS_SUCCESS;
+ break;
+ }
+ default:
+ {
+ ERR_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / unknown ioctl code 0x%lx\n",
+ Stack->Parameters.DeviceIoControl.IoControlCode);
+ ASSERT(FALSE);
+ return ForwardIrpAndForget(DeviceObject, Irp);
+ }
+ }
+
+ if (Status != STATUS_PENDING)
+ {
+ Irp->IoStatus.Status = Status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ }
+ return Status;
+}
+
+/* Test if packets are taking too long to come in. If they do, we
+ * might have gotten out of sync and should just drop what we have.
+ *
+ * If we want to be totally right, we'd also have to keep a count of
+ * errors, and totally reset the mouse after too much of them (can
+ * happen if the user is using a KVM switch and an OS on another port
+ * resets the mouse, or if the user hotplugs the mouse, or if we're just
+ * generally unlucky). Also note the input parsing routine where we
+ * drop invalid input packets.
+ */
+static VOID
+i8042MouInputTestTimeout(
+ IN PI8042_MOUSE_EXTENSION DeviceExtension)
+{
+ ULARGE_INTEGER Now;
+
+ if (DeviceExtension->MouseState == MouseExpectingACK ||
+ DeviceExtension->MouseState == MouseResetting)
+ return;
+
+ Now.QuadPart = KeQueryInterruptTime();
+
+ if (DeviceExtension->MouseState != MouseIdle) {
+ /* Check if the last byte came too long ago */
+ if (Now.QuadPart - DeviceExtension->MousePacketStartTime.QuadPart >
+ DeviceExtension->Common.PortDeviceExtension->Settings.MouseSynchIn100ns)
+ {
+ WARN_(I8042PRT, "Mouse input packet timeout\n");
+ DeviceExtension->MouseState = MouseIdle;
+ }
+ }
+
+ if (DeviceExtension->MouseState == MouseIdle)
+ DeviceExtension->MousePacketStartTime.QuadPart = Now.QuadPart;
+}
+
+/*
+ * Call the customization hook. The ToReturn parameter is about wether
+ * we should go on with the interrupt. The return value is what
+ * we should return (indicating to the system wether someone else
+ * should try to handle the interrupt)
+ */
+static BOOLEAN
+i8042MouCallIsrHook(
+ IN PI8042_MOUSE_EXTENSION DeviceExtension,
+ IN UCHAR Status,
+ IN UCHAR Input,
+ OUT PBOOLEAN ToReturn)
+{
+ BOOLEAN HookReturn, HookContinue;
+
+ HookContinue = FALSE;
+
+ if (DeviceExtension->MouseHook.IsrRoutine)
+ {
+ HookReturn = DeviceExtension->MouseHook.IsrRoutine(
+ DeviceExtension->MouseHook.Context,
+ DeviceExtension->MouseBuffer + DeviceExtension->MouseInBuffer,
+ &DeviceExtension->Common.PortDeviceExtension->Packet,
+ Status,
+ &Input,
+ &HookContinue,
+ &DeviceExtension->MouseState,
+ &DeviceExtension->MouseResetState);
+
+ if (!HookContinue)
+ {
+ *ToReturn = HookReturn;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static BOOLEAN
+i8042MouResetIsr(
+ IN PI8042_MOUSE_EXTENSION DeviceExtension,
+ IN UCHAR Status,
+ IN UCHAR Value)
+{
+ PPORT_DEVICE_EXTENSION PortDeviceExtension;
+ BOOLEAN ToReturn = FALSE;
+
+ if (i8042MouCallIsrHook(DeviceExtension, Status, Value, &ToReturn))
+ return ToReturn;
+
+ if (MouseIdle == DeviceExtension->MouseState)
+ {
+ /* Magic packet value that indicates a reset */
+ if (0xAA == Value)
+ {
+ WARN_(I8042PRT, "Hot plugged mouse!\n");
+ DeviceExtension->MouseState = MouseResetting;
+ DeviceExtension->MouseResetState = ExpectingReset;
+ }
+ else
+ return FALSE;
+ }
+ else if (MouseResetting != DeviceExtension->MouseState)
+ return FALSE;
+
+ DeviceExtension->MouseTimeoutState = TimeoutStart;
+ PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
+
+ switch ((ULONG)DeviceExtension->MouseResetState)
+ {
+ case ExpectingReset:
+ if (MOUSE_ACK == Value)
+ {
+ WARN_(I8042PRT, "Dropping extra ACK\n");
+ return TRUE;
+ }
+
+ /* First, 0xFF is sent. The mouse is supposed to say AA00 if ok, FC00 if not. */
+ if (0xAA == Value)
+ {
+ DeviceExtension->MouseResetState++;
+ }
+ else
+ {
+ PortDeviceExtension->Flags &= ~MOUSE_PRESENT;
+ DeviceExtension->MouseState = MouseIdle;
+ WARN_(I8042PRT, "Mouse returned bad reset reply: %x (expected aa)\n", Value);
+ }
+ return TRUE;
+ case ExpectingResetId:
+ if (MOUSE_ACK == Value)
+ {
+ WARN_(I8042PRT, "Dropping extra ACK #2\n");
+ return TRUE;
+ }
+
+ if (0x00 == Value)
+ {
+ DeviceExtension->MouseResetState++;
+ DeviceExtension->MouseType = GenericPS2;
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2);
+ }
+ else
+ {
+ PortDeviceExtension->Flags &= ~MOUSE_PRESENT;
+ DeviceExtension->MouseState = MouseIdle;
+ WARN_(I8042PRT, "Mouse returned bad reset reply part two: %x (expected 0)\n", Value);
+ }
+ return TRUE;
+ case ExpectingGetDeviceIdACK:
+ if (MOUSE_ACK == Value)
+ {
+ DeviceExtension->MouseResetState++;
+ }
+ else if (MOUSE_NACK == Value || MOUSE_ERROR == Value)
+ {
+ DeviceExtension->MouseResetState++;
+ /* Act as if 00 (normal mouse) was received */
+ WARN_(I8042PRT, "Mouse doesn't support 0xd2, (returns %x, expected %x), faking\n", Value, MOUSE_ACK);
+ i8042MouResetIsr(DeviceExtension, Status, 0);
+ }
+ return TRUE;
+ case ExpectingGetDeviceIdValue:
+ switch (Value)
+ {
+ case 0x02:
+ DeviceExtension->MouseAttributes.MouseIdentifier =
+ BALLPOINT_I8042_HARDWARE;
+ break;
+ case 0x03:
+ case 0x04:
+ DeviceExtension->MouseAttributes.MouseIdentifier =
+ WHEELMOUSE_I8042_HARDWARE;
+ break;
+ default:
+ DeviceExtension->MouseAttributes.MouseIdentifier =
+ MOUSE_I8042_HARDWARE;
+ }
+ DeviceExtension->MouseResetState++;
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE8);
+ return TRUE;
+ case ExpectingSetResolutionDefaultACK:
+ DeviceExtension->MouseResetState++;
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x00);
+ return TRUE;
+ case ExpectingSetResolutionDefaultValueACK:
+ DeviceExtension->MouseResetState = ExpectingSetScaling1to1ACK;
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE6);
+ return TRUE;
+ case ExpectingSetScaling1to1ACK:
+ case ExpectingSetScaling1to1ACK2:
+ DeviceExtension->MouseResetState++;
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE6);
+ return TRUE;
+ case ExpectingSetScaling1to1ACK3:
+ DeviceExtension->MouseResetState++;
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE9);
+ return TRUE;
+ case ExpectingReadMouseStatusACK:
+ DeviceExtension->MouseResetState++;
+ return TRUE;
+ case ExpectingReadMouseStatusByte1:
+ DeviceExtension->MouseLogiBuffer[0] = Value;
+ DeviceExtension->MouseResetState++;
+ return TRUE;
+ case ExpectingReadMouseStatusByte2:
+ DeviceExtension->MouseLogiBuffer[1] = Value;
+ DeviceExtension->MouseResetState++;
+ return TRUE;
+ case ExpectingReadMouseStatusByte3:
+ DeviceExtension->MouseLogiBuffer[2] = Value;
+ /* Now MouseLogiBuffer is a set of info. If the second
+ * byte is 0, the mouse didn't understand the magic
+ * code. Otherwise, it it a Logitech and the second byte
+ * is the number of buttons, bit 7 of the first byte tells
+ * if it understands special E7 commands, the rest is an ID.
+ */
+ if (DeviceExtension->MouseLogiBuffer[1])
+ {
+ DeviceExtension->MouseAttributes.NumberOfButtons =
+ DeviceExtension->MouseLogiBuffer[1];
+ DeviceExtension->MouseType = Ps2pp;
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
+ DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK;
+ /* TODO: Go through EnableWheel and Enable5Buttons */
+ return TRUE;
+ }
+ DeviceExtension->MouseResetState = EnableWheel;
+ i8042MouResetIsr(DeviceExtension, Status, Value);
+ return TRUE;
+ case EnableWheel:
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
+ DeviceExtension->MouseResetState = 1001;
+ return TRUE;
+ case 1001:
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xC8);
+ DeviceExtension->MouseResetState++;
+ return TRUE;
+ case 1002:
+ case 1004:
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
+ DeviceExtension->MouseResetState++;
+ return TRUE;
+ case 1003:
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x64);
+ DeviceExtension->MouseResetState++;
+ return TRUE;
+ case 1005:
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x50);
+ DeviceExtension->MouseResetState++;
+ return TRUE;
+ case 1006:
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2);
+ DeviceExtension->MouseResetState++;
+ return TRUE;
+ case 1007:
+ /* Ignore ACK */
+ DeviceExtension->MouseResetState++;
+ return TRUE;
+ case 1008:
+ if (0x03 == Value) {
+ /* It's either an Intellimouse or Intellimouse Explorer. */
+ DeviceExtension->MouseAttributes.NumberOfButtons = 3;
+ DeviceExtension->MouseAttributes.MouseIdentifier =
+ WHEELMOUSE_I8042_HARDWARE;
+ DeviceExtension->MouseType = Intellimouse;
+ DeviceExtension->MouseResetState = Enable5Buttons;
+ i8042MouResetIsr(DeviceExtension, Status, Value);
+ }
+ else
+ {
+ /* Just set the default settings and be done */
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
+ DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK;
+ }
+ return TRUE;
+ case Enable5Buttons:
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
+ DeviceExtension->MouseResetState = 1021;
+ return TRUE;
+ case 1022:
+ case 1024:
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
+ DeviceExtension->MouseResetState++;
+ return TRUE;
+ case 1021:
+ case 1023:
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xC8);
+ DeviceExtension->MouseResetState++;
+ return TRUE;
+ case 1025:
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x50);
+ DeviceExtension->MouseResetState++;
+ return TRUE;
+ case 1026:
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2);
+ DeviceExtension->MouseResetState++;
+ return TRUE;
+ case 1027:
+ if (0x04 == Value)
+ {
+ DeviceExtension->MouseAttributes.NumberOfButtons = 5;
+ DeviceExtension->MouseAttributes.MouseIdentifier =
+ WHEELMOUSE_I8042_HARDWARE;
+ DeviceExtension->MouseType = IntellimouseExplorer;
+ }
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
+ DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK;
+ return TRUE;
+ case ExpectingSetSamplingRateACK:
+ DeviceExtension->MouseHook.IsrWritePort(
+ DeviceExtension->MouseHook.CallContext,
+ (UCHAR)DeviceExtension->MouseAttributes.SampleRate);
+ DeviceExtension->MouseResetState++;
+ return TRUE;
+ case ExpectingSetSamplingRateValueACK:
+ if (MOUSE_NACK == Value)
+ {
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x3C);
+ DeviceExtension->MouseAttributes.SampleRate = (USHORT)PortDeviceExtension->Settings.SampleRate;
+ DeviceExtension->MouseResetState = 1040;
+ return TRUE;
+ }
+ case 1040: /* Fallthrough */
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE8);
+ DeviceExtension->MouseResetState = ExpectingFinalResolutionACK;
+ return TRUE;
+ case ExpectingFinalResolutionACK:
+ DeviceExtension->MouseHook.IsrWritePort(
+ DeviceExtension->MouseHook.CallContext,
+ (UCHAR)(PortDeviceExtension->Settings.MouseResolution & 0xff));
+ INFO_(I8042PRT, "Mouse resolution %lu\n",
+ PortDeviceExtension->Settings.MouseResolution);
+ DeviceExtension->MouseResetState = ExpectingFinalResolutionValueACK;
+ return TRUE;
+ case ExpectingFinalResolutionValueACK:
+ DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF4);
+ DeviceExtension->MouseResetState = ExpectingEnableACK;
+ return TRUE;
+ case ExpectingEnableACK:
+ PortDeviceExtension->Flags |= MOUSE_PRESENT;
+ DeviceExtension->MouseState = MouseIdle;
+ DeviceExtension->MouseTimeoutState = TimeoutCancel;
+ INFO_(I8042PRT, "Mouse type = %u\n", DeviceExtension->MouseType);
+ return TRUE;
+ default:
+ if (DeviceExtension->MouseResetState < 100 || DeviceExtension->MouseResetState > 999)
+ ERR_(I8042PRT, "MouseResetState went out of range: %lu\n", DeviceExtension->MouseResetState);
+ return FALSE;
+ }
+}
+
+BOOLEAN NTAPI
+i8042MouInterruptService(
+ IN PKINTERRUPT Interrupt,
+ PVOID Context)
+{
+ PI8042_MOUSE_EXTENSION DeviceExtension;
+ PPORT_DEVICE_EXTENSION PortDeviceExtension;
+ ULONG Counter;
+ UCHAR Output = 0, PortStatus = 0;
+ NTSTATUS Status;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ __analysis_assume(Context != NULL);
+ DeviceExtension = Context;
+ PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
+ Counter = PortDeviceExtension->Settings.PollStatusIterations;
+
+ while (Counter)
+ {
+ Status = i8042ReadStatus(PortDeviceExtension, &PortStatus);
+ if (!NT_SUCCESS(Status))
+ {
+ WARN_(I8042PRT, "i8042ReadStatus() failed with status 0x%08lx\n", Status);
+ return FALSE;
+ }
+ Status = i8042ReadMouseData(PortDeviceExtension, &Output);
+ if (NT_SUCCESS(Status))
+ break;
+ KeStallExecutionProcessor(1);
+ Counter--;
+ }
+ if (Counter == 0)
+ {
+ WARN_(I8042PRT, "Spurious i8042 mouse interrupt\n");
+ return FALSE;
+ }
+
+ INFO_(I8042PRT, "Got: 0x%02x\n", Output);
+
+ if (i8042PacketIsr(PortDeviceExtension, Output))
+ {
+ if (PortDeviceExtension->PacketComplete)
+ {
+ TRACE_(I8042PRT, "Packet complete\n");
+ KeInsertQueueDpc(&DeviceExtension->DpcMouse, NULL, NULL);
+ }
+ TRACE_(I8042PRT, "Irq eaten by packet\n");
+ return TRUE;
+ }
+
+ TRACE_(I8042PRT, "Irq is mouse input\n");
+
+ i8042MouInputTestTimeout(DeviceExtension);
+
+ if (i8042MouResetIsr(DeviceExtension, PortStatus, Output))
+ {
+ TRACE_(I8042PRT, "Handled by ResetIsr or hooked Isr\n");
+ if (NoChange != DeviceExtension->MouseTimeoutState) {
+ KeInsertQueueDpc(&DeviceExtension->DpcMouse, NULL, NULL);
+ }
+ return TRUE;
+ }
+
+ if (DeviceExtension->MouseType == Ps2pp)
+ i8042MouHandlePs2pp(DeviceExtension, Output);
+ else
+ i8042MouHandle(DeviceExtension, Output);
+
+ return TRUE;
+}