reactos/drivers/input/i8042prt/mouse.c

974 lines
29 KiB
C

/*
* 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;
}