Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974/* * 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 NTAPIi8042MouIsrWritePort( 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 NTAPIi8042MouQueuePacket( 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);}
VOIDi8042MouHandle( 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 */VOIDi8042MouHandleButtons( 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. */NTSTATUSi8042MouInitialize( 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 NTAPIi8042MouDpcRoutine( 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 NTAPIi8042DpcRoutineMouseTimeout( 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 NTAPIi8042MouInternalDeviceControl( 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 VOIDi8042MouInputTestTimeout( 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 BOOLEANi8042MouCallIsrHook( 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 BOOLEANi8042MouResetIsr( 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 NTAPIi8042MouInterruptService( 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;}