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