xref: /reactos/drivers/input/i8042prt/mouse.c (revision 4561998a)
1 /*
2  * PROJECT:     ReactOS i8042 (ps/2 keyboard-mouse controller) driver
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        drivers/input/i8042prt/mouse.c
5  * PURPOSE:     Mouse specific functions
6  * PROGRAMMERS: Copyright Victor Kirhenshtein (sauros@iname.com)
7                 Copyright Jason Filby (jasonfilby@yahoo.com)
8                 Copyright Martijn Vernooij (o112w8r02@sneakemail.com)
9                 Copyright 2006-2007 Herv� Poussineau (hpoussin@reactos.org)
10                 Copyright 2008 Colin Finck (mail@colinfinck.de)
11  */
12 
13 /* INCLUDES ****************************************************************/
14 
15 #include "i8042prt.h"
16 
17 #include <debug.h>
18 
19 /* FUNCTIONS *****************************************************************/
20 
21 static KDEFERRED_ROUTINE i8042MouDpcRoutine;
22 static KDEFERRED_ROUTINE i8042DpcRoutineMouseTimeout;
23 
24 /*
25  * These functions are callbacks for filter driver custom interrupt
26  * service routines.
27  */
28 static VOID NTAPI
29 i8042MouIsrWritePort(
30 	IN PVOID Context,
31 	IN UCHAR Value)
32 {
33 	PI8042_MOUSE_EXTENSION DeviceExtension;
34 
35 	DeviceExtension = (PI8042_MOUSE_EXTENSION)Context;
36 
37 	if (DeviceExtension->MouseHook.IsrWritePort != i8042MouIsrWritePort)
38 	{
39 		DeviceExtension->MouseHook.IsrWritePort(
40 			DeviceExtension->MouseHook.CallContext,
41 			Value);
42 	}
43 	else
44 		i8042IsrWritePort(DeviceExtension->Common.PortDeviceExtension, Value, CTRL_WRITE_MOUSE);
45 }
46 
47 static VOID NTAPI
48 i8042MouQueuePacket(
49 	IN PVOID Context)
50 {
51 	PI8042_MOUSE_EXTENSION DeviceExtension;
52 
53 	DeviceExtension = (PI8042_MOUSE_EXTENSION)Context;
54 
55 	DeviceExtension->MouseComplete = TRUE;
56 	DeviceExtension->MouseInBuffer++;
57 	if (DeviceExtension->MouseInBuffer >= DeviceExtension->Common.PortDeviceExtension->Settings.MouseDataQueueSize)
58 	{
59 		WARN_(I8042PRT, "Mouse buffer overflow\n");
60 		DeviceExtension->MouseInBuffer--;
61 	}
62 
63 	TRACE_(I8042PRT, "Irq completes mouse packet\n");
64 	KeInsertQueueDpc(&DeviceExtension->DpcMouse, NULL, NULL);
65 }
66 
67 VOID
68 i8042MouHandle(
69 	IN PI8042_MOUSE_EXTENSION DeviceExtension,
70 	IN UCHAR Output)
71 {
72 	PMOUSE_INPUT_DATA MouseInput;
73 	CHAR Scroll;
74 
75 	MouseInput = DeviceExtension->MouseBuffer + DeviceExtension->MouseInBuffer;
76 
77 	switch (DeviceExtension->MouseState)
78 	{
79 		case MouseIdle:
80 			/* This bit should be 1, if not drop the packet, we
81 			 * might be lucky and get in sync again
82 			 */
83 			if (!(Output & 8)) {
84 				WARN_(I8042PRT, "Bad input, dropping..\n");
85 				return;
86 			}
87 
88 			MouseInput->Buttons = 0;
89 			MouseInput->RawButtons = 0;
90 			MouseInput->Flags = MOUSE_MOVE_RELATIVE;
91 
92 			/* Note how we ignore the overflow bits, like Windows
93 			 * is said to do. There's no reasonable thing to do
94 			 * anyway.
95 			 */
96 
97 			if (Output & 16)
98 				MouseInput->LastX = 1;
99 			else
100 				MouseInput->LastX = 0;
101 			if (Output & 32)
102 				MouseInput->LastY = 1;
103 			else
104 				MouseInput->LastY = 0;
105 
106 			if (Output & 1)
107 				MouseInput->RawButtons |= MOUSE_LEFT_BUTTON_DOWN;
108 			if (Output & 2)
109 				MouseInput->RawButtons |= MOUSE_RIGHT_BUTTON_DOWN;
110 			if (Output & 4)
111 				MouseInput->RawButtons |= MOUSE_MIDDLE_BUTTON_DOWN;
112 
113 			DeviceExtension->MouseState = XMovement;
114 			break;
115 
116 		case XMovement:
117 			if (MouseInput->LastX)
118 				MouseInput->LastX = (LONG) Output - 256;
119 			else
120 				MouseInput->LastX = Output;
121 
122 			DeviceExtension->MouseState = YMovement;
123 			break;
124 
125 		case YMovement:
126 			if (MouseInput->LastY)
127 				MouseInput->LastY = (LONG)Output - 256;
128 			else
129 				MouseInput->LastY = (LONG)Output;
130 
131 			/* Windows wants it the other way around */
132 			MouseInput->LastY = -MouseInput->LastY;
133 
134 			if (DeviceExtension->MouseType == GenericPS2 ||
135 			    DeviceExtension->MouseType == Ps2pp)
136 			{
137 				i8042MouHandleButtons(
138 					DeviceExtension,
139 					MOUSE_LEFT_BUTTON_DOWN |
140 					MOUSE_RIGHT_BUTTON_DOWN |
141 					MOUSE_MIDDLE_BUTTON_DOWN);
142 				DeviceExtension->MouseHook.QueueMousePacket(DeviceExtension->MouseHook.CallContext);
143 				DeviceExtension->MouseState = MouseIdle;
144 			}
145 			else
146 			{
147 				DeviceExtension->MouseState = ZMovement;
148 			}
149 			break;
150 
151 		case ZMovement:
152 			Scroll = Output & 0x0f;
153 			if (Scroll & 8)
154 				Scroll |= 0xf0;
155 
156 			if (Scroll)
157 			{
158 				MouseInput->RawButtons |= MOUSE_WHEEL;
159 				MouseInput->ButtonData = (USHORT)(Scroll * -WHEEL_DELTA);
160 			}
161 
162 			if (DeviceExtension->MouseType == IntellimouseExplorer)
163 			{
164 				if (Output & 16)
165 					MouseInput->RawButtons |= MOUSE_BUTTON_4_DOWN;
166 				if (Output & 32)
167 					MouseInput->RawButtons |= MOUSE_BUTTON_5_DOWN;
168 			}
169 			i8042MouHandleButtons(
170 				DeviceExtension,
171 				MOUSE_LEFT_BUTTON_DOWN |
172 				MOUSE_RIGHT_BUTTON_DOWN |
173 				MOUSE_MIDDLE_BUTTON_DOWN |
174 				MOUSE_BUTTON_4_DOWN |
175 				MOUSE_BUTTON_5_DOWN);
176 			DeviceExtension->MouseHook.QueueMousePacket(DeviceExtension->MouseHook.CallContext);
177 			DeviceExtension->MouseState = MouseIdle;
178 			break;
179 
180 		default:
181 			ERR_(I8042PRT, "Unexpected state 0x%lx!\n", DeviceExtension->MouseState);
182 			ASSERT(FALSE);
183 	}
184 }
185 
186 /*
187  * Updates ButtonFlags according to RawButtons and a saved state;
188  * Only takes in account the bits that are set in Mask
189  */
190 VOID
191 i8042MouHandleButtons(
192 	IN PI8042_MOUSE_EXTENSION DeviceExtension,
193 	IN USHORT Mask)
194 {
195 	PMOUSE_INPUT_DATA MouseInput;
196 	USHORT NewButtonData;
197 	USHORT ButtonDiff;
198 
199 	MouseInput = DeviceExtension->MouseBuffer + DeviceExtension->MouseInBuffer;
200 	NewButtonData = (USHORT)(MouseInput->RawButtons & Mask);
201 	ButtonDiff = (NewButtonData ^ DeviceExtension->MouseButtonState) & Mask;
202 
203 	/* Note that the defines are such:
204 	 * MOUSE_LEFT_BUTTON_DOWN 1
205 	 * MOUSE_LEFT_BUTTON_UP   2
206 	 */
207 	MouseInput->ButtonFlags |= (NewButtonData & ButtonDiff) |
208 		(((~(NewButtonData)) << 1) & (ButtonDiff << 1)) |
209 		(MouseInput->RawButtons & 0xfc00);
210 
211 	INFO_(I8042PRT, "Left raw/up/down: %u/%u/%u\n",
212 		MouseInput->RawButtons & MOUSE_LEFT_BUTTON_DOWN,
213 		MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_DOWN,
214 		MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_UP);
215 
216 	DeviceExtension->MouseButtonState =
217 		(DeviceExtension->MouseButtonState & ~Mask) | (NewButtonData & Mask);
218 }
219 
220 /* Does final initializations for the mouse. This method
221  * is called just before connecting the interrupt.
222  */
223 NTSTATUS
224 i8042MouInitialize(
225 	IN PI8042_MOUSE_EXTENSION DeviceExtension)
226 {
227 	NTSTATUS Status;
228 	UCHAR Value;
229 
230 	/* Enable the PS/2 mouse port */
231 	i8042Write(DeviceExtension->Common.PortDeviceExtension, DeviceExtension->Common.PortDeviceExtension->ControlPort, MOUSE_ENAB);
232 
233 	/* Enable the mouse */
234 	if(!i8042IsrWritePort(DeviceExtension->Common.PortDeviceExtension, MOU_ENAB, CTRL_WRITE_MOUSE))
235 	{
236 		WARN_(I8042PRT, "Failed to enable mouse!\n");
237 		return STATUS_IO_DEVICE_ERROR;
238 	}
239 
240 	Status = i8042ReadDataWait(DeviceExtension->Common.PortDeviceExtension, &Value);
241 	if (!NT_SUCCESS(Status))
242 	{
243 		WARN_(I8042PRT, "Failed to read the response of MOU_ENAB, status 0x%08lx\n", Status);
244 		return Status;
245 	}
246 
247 	if(Value == MOUSE_ACK)
248 	{
249 		INFO_(I8042PRT, "Mouse was enabled successfully!\n");
250 		return STATUS_SUCCESS;
251 	}
252 
253 	WARN_(I8042PRT, "Got 0x%02x instead of 0xFA\n", Value);
254 	return STATUS_IO_DEVICE_ERROR;
255 }
256 
257 static VOID NTAPI
258 i8042MouDpcRoutine(
259 	IN PKDPC Dpc,
260 	IN PVOID DeferredContext,
261 	IN PVOID SystemArgument1,
262 	IN PVOID SystemArgument2)
263 {
264 	PI8042_MOUSE_EXTENSION DeviceExtension;
265 	PPORT_DEVICE_EXTENSION PortDeviceExtension;
266 	ULONG MouseTransferred = 0;
267 	ULONG MouseInBufferCopy;
268 	KIRQL Irql;
269 	LARGE_INTEGER Timeout;
270 
271 	UNREFERENCED_PARAMETER(Dpc);
272 	UNREFERENCED_PARAMETER(SystemArgument1);
273 	UNREFERENCED_PARAMETER(SystemArgument2);
274 
275 	__analysis_assume(DeferredContext != NULL);
276 	DeviceExtension = DeferredContext;
277 	PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
278 
279 	switch (DeviceExtension->MouseTimeoutState)
280 	{
281 		case TimeoutStart:
282 		{
283 			DeviceExtension->MouseTimeoutState = NoChange;
284 			if (DeviceExtension->MouseTimeoutActive &&
285 			    !KeCancelTimer(&DeviceExtension->TimerMouseTimeout))
286 			{
287 				/* The timer fired already, give up */
288 				DeviceExtension->MouseTimeoutActive = FALSE;
289 				return;
290 			}
291 
292 			Timeout.QuadPart = -15000000; /* 1.5 seconds, should be enough */
293 
294 			KeSetTimer(
295 				&DeviceExtension->TimerMouseTimeout,
296 				Timeout,
297 				&DeviceExtension->DpcMouseTimeout);
298 			DeviceExtension->MouseTimeoutActive = TRUE;
299 			return;
300 		}
301 
302 		case TimeoutCancel:
303 		{
304 			DeviceExtension->MouseTimeoutState = NoChange;
305 			KeCancelTimer(&DeviceExtension->TimerMouseTimeout);
306 			DeviceExtension->MouseTimeoutActive = FALSE;
307 		}
308 
309 		default:
310 			;/* nothing, don't want a warning */
311 	}
312 
313 	/* Should be unlikely */
314 	if (!DeviceExtension->MouseComplete)
315 		return;
316 
317 	Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt);
318 
319 	DeviceExtension->MouseComplete = FALSE;
320 	MouseInBufferCopy = DeviceExtension->MouseInBuffer;
321 
322 	KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql);
323 
324 	TRACE_(I8042PRT, "Send a mouse packet\n");
325 
326 	if (!DeviceExtension->MouseData.ClassService)
327 		return;
328 
329 	INFO_(I8042PRT, "Sending %lu mouse move(s)\n", MouseInBufferCopy);
330 	(*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->MouseData.ClassService)(
331 		DeviceExtension->MouseData.ClassDeviceObject,
332 		DeviceExtension->MouseBuffer,
333 		DeviceExtension->MouseBuffer + MouseInBufferCopy,
334 		&MouseTransferred);
335 
336 	Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt);
337 	DeviceExtension->MouseInBuffer -= MouseTransferred;
338 	if (DeviceExtension->MouseInBuffer)
339 		RtlMoveMemory(
340 			DeviceExtension->MouseBuffer,
341 			DeviceExtension->MouseBuffer + MouseTransferred,
342 			DeviceExtension->MouseInBuffer * sizeof(MOUSE_INPUT_DATA));
343 	KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql);
344 }
345 
346 /* This timer DPC will be called when the mouse reset times out.
347  * I'll just send the 'disable mouse port' command to the controller
348  * and say the mouse doesn't exist.
349  */
350 static VOID NTAPI
351 i8042DpcRoutineMouseTimeout(
352 	IN PKDPC Dpc,
353 	IN PVOID DeferredContext,
354 	IN PVOID SystemArgument1,
355 	IN PVOID SystemArgument2)
356 {
357 	PI8042_MOUSE_EXTENSION DeviceExtension;
358 	PPORT_DEVICE_EXTENSION PortDeviceExtension;
359 	KIRQL Irql;
360 
361 	UNREFERENCED_PARAMETER(Dpc);
362 	UNREFERENCED_PARAMETER(SystemArgument1);
363 	UNREFERENCED_PARAMETER(SystemArgument2);
364 
365 	__analysis_assume(DeferredContext != NULL);
366 	DeviceExtension = DeferredContext;
367 	PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
368 
369 	Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt);
370 
371 	WARN_(I8042PRT, "Mouse initialization timeout! (substate %x)\n",
372 		DeviceExtension->MouseResetState);
373 
374 	PortDeviceExtension->Flags &= ~MOUSE_PRESENT;
375 
376 	KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql);
377 }
378 
379 /*
380  * Runs the mouse IOCTL_INTERNAL dispatch.
381  */
382 NTSTATUS NTAPI
383 i8042MouInternalDeviceControl(
384 	IN PDEVICE_OBJECT DeviceObject,
385 	IN PIRP Irp)
386 {
387 	PIO_STACK_LOCATION Stack;
388 	PI8042_MOUSE_EXTENSION DeviceExtension;
389 	NTSTATUS Status;
390 
391 	Stack = IoGetCurrentIrpStackLocation(Irp);
392 	Irp->IoStatus.Information = 0;
393 	DeviceExtension = (PI8042_MOUSE_EXTENSION)DeviceObject->DeviceExtension;
394 
395 	switch (Stack->Parameters.DeviceIoControl.IoControlCode)
396 	{
397 		case IOCTL_INTERNAL_MOUSE_CONNECT:
398 		{
399 			SIZE_T Size;
400 			PIO_WORKITEM WorkItem = NULL;
401 			PI8042_HOOK_WORKITEM WorkItemData = NULL;
402 
403 			TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_MOUSE_CONNECT\n");
404 			if (Stack->Parameters.DeviceIoControl.InputBufferLength != sizeof(CONNECT_DATA))
405 			{
406 				Status = STATUS_INVALID_PARAMETER;
407 				goto cleanup;
408 			}
409 
410 			DeviceExtension->MouseData =
411 				*((PCONNECT_DATA)Stack->Parameters.DeviceIoControl.Type3InputBuffer);
412 
413 			/* Send IOCTL_INTERNAL_I8042_HOOK_MOUSE to device stack */
414 			WorkItem = IoAllocateWorkItem(DeviceObject);
415 			if (!WorkItem)
416 			{
417 				WARN_(I8042PRT, "IoAllocateWorkItem() failed\n");
418 				Status = STATUS_INSUFFICIENT_RESOURCES;
419 				goto cleanup;
420 			}
421 			WorkItemData = ExAllocatePoolWithTag(
422 				NonPagedPool,
423 				sizeof(I8042_HOOK_WORKITEM),
424 				I8042PRT_TAG);
425 			if (!WorkItemData)
426 			{
427 				WARN_(I8042PRT, "ExAllocatePoolWithTag() failed\n");
428 				Status = STATUS_NO_MEMORY;
429 				goto cleanup;
430 			}
431 			WorkItemData->WorkItem = WorkItem;
432 			WorkItemData->Irp = Irp;
433 
434 			/* Initialize extension */
435 			DeviceExtension->Common.Type = Mouse;
436 			Size = DeviceExtension->Common.PortDeviceExtension->Settings.MouseDataQueueSize * sizeof(MOUSE_INPUT_DATA);
437 			DeviceExtension->MouseBuffer = ExAllocatePoolWithTag(
438 				NonPagedPool,
439 				Size,
440 				I8042PRT_TAG);
441 			if (!DeviceExtension->MouseBuffer)
442 			{
443 				WARN_(I8042PRT, "ExAllocatePoolWithTag() failed\n");
444 				Status = STATUS_NO_MEMORY;
445 				goto cleanup;
446 			}
447 			RtlZeroMemory(DeviceExtension->MouseBuffer, Size);
448 			DeviceExtension->MouseAttributes.InputDataQueueLength =
449 				DeviceExtension->Common.PortDeviceExtension->Settings.MouseDataQueueSize;
450 			KeInitializeDpc(
451 				&DeviceExtension->DpcMouse,
452 				i8042MouDpcRoutine,
453 				DeviceExtension);
454 			KeInitializeDpc(
455 				&DeviceExtension->DpcMouseTimeout,
456 				i8042DpcRoutineMouseTimeout,
457 				DeviceExtension);
458 			KeInitializeTimer(&DeviceExtension->TimerMouseTimeout);
459 			DeviceExtension->Common.PortDeviceExtension->MouseExtension = DeviceExtension;
460 			DeviceExtension->Common.PortDeviceExtension->Flags |= MOUSE_CONNECTED;
461 
462 			IoMarkIrpPending(Irp);
463 			DeviceExtension->MouseState = MouseResetting;
464 			DeviceExtension->MouseResetState = ExpectingReset;
465 			DeviceExtension->MouseHook.IsrWritePort = i8042MouIsrWritePort;
466 			DeviceExtension->MouseHook.QueueMousePacket = i8042MouQueuePacket;
467 			DeviceExtension->MouseHook.CallContext = DeviceExtension;
468 			IoQueueWorkItem(WorkItem,
469 				i8042SendHookWorkItem,
470 				DelayedWorkQueue,
471 				WorkItemData);
472 			Status = STATUS_PENDING;
473 			break;
474 
475 cleanup:
476 			if (DeviceExtension->MouseBuffer)
477 				ExFreePoolWithTag(DeviceExtension->MouseBuffer, I8042PRT_TAG);
478 			if (WorkItem)
479 				IoFreeWorkItem(WorkItem);
480 			if (WorkItemData)
481 				ExFreePoolWithTag(WorkItemData, I8042PRT_TAG);
482 			break;
483 		}
484 		case IOCTL_INTERNAL_MOUSE_DISCONNECT:
485 		{
486 			TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_MOUSE_DISCONNECT\n");
487 			/* MSDN says that operation is to implemented.
488 			 * To implement it, we just have to do:
489 			 * DeviceExtension->MouseData.ClassService = NULL;
490 			 */
491 			Status = STATUS_NOT_IMPLEMENTED;
492 			break;
493 		}
494 		case IOCTL_INTERNAL_I8042_HOOK_MOUSE:
495 		{
496 			PINTERNAL_I8042_HOOK_MOUSE MouseHook;
497 			TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_I8042_HOOK_MOUSE\n");
498 			if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(INTERNAL_I8042_HOOK_MOUSE))
499 			{
500 				Status = STATUS_INVALID_PARAMETER;
501 				break;
502 			}
503 			MouseHook = (PINTERNAL_I8042_HOOK_MOUSE)Stack->Parameters.DeviceIoControl.Type3InputBuffer;
504 
505 			DeviceExtension->MouseHook.Context = MouseHook->Context;
506 			if (MouseHook->IsrRoutine)
507 				DeviceExtension->MouseHook.IsrRoutine = MouseHook->IsrRoutine;
508 
509 			Status = STATUS_SUCCESS;
510 			break;
511 		}
512 		case IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER:
513 		{
514 			DPRINT1("IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER not implemented\n");
515 			Status = STATUS_NOT_IMPLEMENTED;
516 			break;
517 		}
518 		case IOCTL_INTERNAL_I8042_MOUSE_START_INFORMATION:
519 		{
520 			DPRINT1("IOCTL_INTERNAL_I8042_MOUSE_START_INFORMATION not implemented\n");
521 			Status = STATUS_NOT_IMPLEMENTED;
522 			break;
523 		}
524 		case IOCTL_MOUSE_QUERY_ATTRIBUTES:
525 		{
526 			TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_MOUSE_QUERY_ATTRIBUTES\n");
527 			if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUSE_ATTRIBUTES))
528 			{
529 				Status = STATUS_BUFFER_TOO_SMALL;
530 				break;
531 			}
532 
533 			*(PMOUSE_ATTRIBUTES) Irp->AssociatedIrp.SystemBuffer = DeviceExtension->MouseAttributes;
534 			Irp->IoStatus.Information = sizeof(MOUSE_ATTRIBUTES);
535 			Status = STATUS_SUCCESS;
536 			break;
537 		}
538 		default:
539 		{
540 			ERR_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / unknown ioctl code 0x%lx\n",
541 				Stack->Parameters.DeviceIoControl.IoControlCode);
542 			ASSERT(FALSE);
543 			return ForwardIrpAndForget(DeviceObject, Irp);
544 		}
545 	}
546 
547 	if (Status != STATUS_PENDING)
548 	{
549 		Irp->IoStatus.Status = Status;
550 		IoCompleteRequest(Irp, IO_NO_INCREMENT);
551 	}
552 	return Status;
553 }
554 
555 /* Test if packets are taking too long to come in. If they do, we
556  * might have gotten out of sync and should just drop what we have.
557  *
558  * If we want to be totally right, we'd also have to keep a count of
559  * errors, and totally reset the mouse after too much of them (can
560  * happen if the user is using a KVM switch and an OS on another port
561  * resets the mouse, or if the user hotplugs the mouse, or if we're just
562  * generally unlucky). Also note the input parsing routine where we
563  * drop invalid input packets.
564  */
565 static VOID
566 i8042MouInputTestTimeout(
567 	IN PI8042_MOUSE_EXTENSION DeviceExtension)
568 {
569 	ULARGE_INTEGER Now;
570 
571 	if (DeviceExtension->MouseState == MouseExpectingACK ||
572 	    DeviceExtension->MouseState == MouseResetting)
573 		return;
574 
575 	Now.QuadPart = KeQueryInterruptTime();
576 
577 	if (DeviceExtension->MouseState != MouseIdle) {
578 		/* Check if the last byte came too long ago */
579 		if (Now.QuadPart - DeviceExtension->MousePacketStartTime.QuadPart >
580 		    DeviceExtension->Common.PortDeviceExtension->Settings.MouseSynchIn100ns)
581 		{
582 			WARN_(I8042PRT, "Mouse input packet timeout\n");
583 			DeviceExtension->MouseState = MouseIdle;
584 		}
585 	}
586 
587 	if (DeviceExtension->MouseState == MouseIdle)
588 		DeviceExtension->MousePacketStartTime.QuadPart = Now.QuadPart;
589 }
590 
591 /*
592  * Call the customization hook. The ToReturn parameter is about wether
593  * we should go on with the interrupt. The return value is what
594  * we should return (indicating to the system wether someone else
595  * should try to handle the interrupt)
596  */
597 static BOOLEAN
598 i8042MouCallIsrHook(
599 	IN PI8042_MOUSE_EXTENSION DeviceExtension,
600 	IN UCHAR Status,
601 	IN UCHAR Input,
602 	OUT PBOOLEAN ToReturn)
603 {
604 	BOOLEAN HookReturn, HookContinue;
605 
606 	HookContinue = FALSE;
607 
608 	if (DeviceExtension->MouseHook.IsrRoutine)
609 	{
610 		HookReturn = DeviceExtension->MouseHook.IsrRoutine(
611 			DeviceExtension->MouseHook.Context,
612 			DeviceExtension->MouseBuffer + DeviceExtension->MouseInBuffer,
613 			&DeviceExtension->Common.PortDeviceExtension->Packet,
614 			Status,
615 			&Input,
616 			&HookContinue,
617 			&DeviceExtension->MouseState,
618 			&DeviceExtension->MouseResetState);
619 
620 		if (!HookContinue)
621 		{
622 			*ToReturn = HookReturn;
623 			return TRUE;
624 		}
625 	}
626 	return FALSE;
627 }
628 
629 static BOOLEAN
630 i8042MouResetIsr(
631 	IN PI8042_MOUSE_EXTENSION DeviceExtension,
632 	IN UCHAR Status,
633 	IN UCHAR Value)
634 {
635 	PPORT_DEVICE_EXTENSION PortDeviceExtension;
636 	BOOLEAN ToReturn = FALSE;
637 
638 	if (i8042MouCallIsrHook(DeviceExtension, Status, Value, &ToReturn))
639 		return ToReturn;
640 
641 	if (MouseIdle == DeviceExtension->MouseState)
642 	{
643 		/* Magic packet value that indicates a reset */
644 		if (0xAA == Value)
645 		{
646 			WARN_(I8042PRT, "Hot plugged mouse!\n");
647 			DeviceExtension->MouseState = MouseResetting;
648 			DeviceExtension->MouseResetState = ExpectingReset;
649 		}
650 		else
651 			return FALSE;
652 	}
653 	else if (MouseResetting != DeviceExtension->MouseState)
654 		return FALSE;
655 
656 	DeviceExtension->MouseTimeoutState = TimeoutStart;
657 	PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
658 
659 	switch ((ULONG)DeviceExtension->MouseResetState)
660 	{
661 		case ExpectingReset:
662 			if (MOUSE_ACK == Value)
663 			{
664 				WARN_(I8042PRT, "Dropping extra ACK\n");
665 				return TRUE;
666 			}
667 
668 			/* First, 0xFF is sent. The mouse is supposed to say AA00 if ok, FC00 if not. */
669 			if (0xAA == Value)
670 			{
671 				DeviceExtension->MouseResetState++;
672 			}
673 			else
674 			{
675 				PortDeviceExtension->Flags &= ~MOUSE_PRESENT;
676 				DeviceExtension->MouseState = MouseIdle;
677 				WARN_(I8042PRT, "Mouse returned bad reset reply: %x (expected aa)\n", Value);
678 			}
679 			return TRUE;
680 		case ExpectingResetId:
681 			if (MOUSE_ACK == Value)
682 			{
683 				WARN_(I8042PRT, "Dropping extra ACK #2\n");
684 				return TRUE;
685 			}
686 
687 			if (0x00 == Value)
688 			{
689 				DeviceExtension->MouseResetState++;
690 				DeviceExtension->MouseType = GenericPS2;
691 				DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2);
692 			}
693 			else
694 			{
695 				PortDeviceExtension->Flags &= ~MOUSE_PRESENT;
696 				DeviceExtension->MouseState = MouseIdle;
697 				WARN_(I8042PRT, "Mouse returned bad reset reply part two: %x (expected 0)\n", Value);
698 			}
699 			return TRUE;
700 		case ExpectingGetDeviceIdACK:
701 			if (MOUSE_ACK == Value)
702 			{
703 				DeviceExtension->MouseResetState++;
704 			}
705 			else if (MOUSE_NACK == Value || MOUSE_ERROR == Value)
706 			{
707 				DeviceExtension->MouseResetState++;
708 				/* Act as if 00 (normal mouse) was received */
709 				WARN_(I8042PRT, "Mouse doesn't support 0xd2, (returns %x, expected %x), faking\n", Value, MOUSE_ACK);
710 				i8042MouResetIsr(DeviceExtension, Status, 0);
711 			}
712 			return TRUE;
713 		case ExpectingGetDeviceIdValue:
714 			switch (Value)
715 			{
716 				case 0x02:
717 					DeviceExtension->MouseAttributes.MouseIdentifier =
718 						BALLPOINT_I8042_HARDWARE;
719 					break;
720 				case 0x03:
721 				case 0x04:
722 					DeviceExtension->MouseAttributes.MouseIdentifier =
723 						WHEELMOUSE_I8042_HARDWARE;
724 					break;
725 				default:
726 					DeviceExtension->MouseAttributes.MouseIdentifier =
727 						MOUSE_I8042_HARDWARE;
728 			}
729 			DeviceExtension->MouseResetState++;
730 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE8);
731 			return TRUE;
732 		case ExpectingSetResolutionDefaultACK:
733 			DeviceExtension->MouseResetState++;
734 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x00);
735 			return TRUE;
736 		case ExpectingSetResolutionDefaultValueACK:
737 			DeviceExtension->MouseResetState = ExpectingSetScaling1to1ACK;
738 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE6);
739 			return TRUE;
740 		case ExpectingSetScaling1to1ACK:
741 		case ExpectingSetScaling1to1ACK2:
742 			DeviceExtension->MouseResetState++;
743 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE6);
744 			return TRUE;
745 		case ExpectingSetScaling1to1ACK3:
746 			DeviceExtension->MouseResetState++;
747 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE9);
748 			return TRUE;
749 		case ExpectingReadMouseStatusACK:
750 			DeviceExtension->MouseResetState++;
751 			return TRUE;
752 		case ExpectingReadMouseStatusByte1:
753 			DeviceExtension->MouseLogiBuffer[0] = Value;
754 			DeviceExtension->MouseResetState++;
755 			return TRUE;
756 		case ExpectingReadMouseStatusByte2:
757 			DeviceExtension->MouseLogiBuffer[1] = Value;
758 			DeviceExtension->MouseResetState++;
759 			return TRUE;
760 		case ExpectingReadMouseStatusByte3:
761 			DeviceExtension->MouseLogiBuffer[2] = Value;
762 			/* Now MouseLogiBuffer is a set of info. If the second
763 			 * byte is 0, the mouse didn't understand the magic
764 			 * code. Otherwise, it it a Logitech and the second byte
765 			 * is the number of buttons, bit 7 of the first byte tells
766 			 * if it understands special E7 commands, the rest is an ID.
767 			 */
768 			if (DeviceExtension->MouseLogiBuffer[1])
769 			{
770 				DeviceExtension->MouseAttributes.NumberOfButtons =
771 					DeviceExtension->MouseLogiBuffer[1];
772 				DeviceExtension->MouseType = Ps2pp;
773 				DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
774 				DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK;
775 				/* TODO: Go through EnableWheel and Enable5Buttons */
776 				return TRUE;
777 			}
778 			DeviceExtension->MouseResetState = EnableWheel;
779 			i8042MouResetIsr(DeviceExtension, Status, Value);
780 			return TRUE;
781 		case EnableWheel:
782 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
783 			DeviceExtension->MouseResetState = 1001;
784 			return TRUE;
785 		case 1001:
786 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xC8);
787 			DeviceExtension->MouseResetState++;
788 			return TRUE;
789 		case 1002:
790 		case 1004:
791 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
792 			DeviceExtension->MouseResetState++;
793 			return TRUE;
794 		case 1003:
795 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x64);
796 			DeviceExtension->MouseResetState++;
797 			return TRUE;
798 		case 1005:
799 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x50);
800 			DeviceExtension->MouseResetState++;
801 			return TRUE;
802 		case 1006:
803 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2);
804 			DeviceExtension->MouseResetState++;
805 			return TRUE;
806 		case 1007:
807 			/* Ignore ACK */
808 			DeviceExtension->MouseResetState++;
809 			return TRUE;
810 		case 1008:
811 			if (0x03 == Value) {
812 				/* It's either an Intellimouse or Intellimouse Explorer. */
813 				DeviceExtension->MouseAttributes.NumberOfButtons = 3;
814 				DeviceExtension->MouseAttributes.MouseIdentifier =
815 					WHEELMOUSE_I8042_HARDWARE;
816 				DeviceExtension->MouseType = Intellimouse;
817 				DeviceExtension->MouseResetState = Enable5Buttons;
818 				i8042MouResetIsr(DeviceExtension, Status, Value);
819 			}
820 			else
821 			{
822 				/* Just set the default settings and be done */
823 				DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
824 				DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK;
825 			}
826 			return TRUE;
827 		case Enable5Buttons:
828 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
829 			DeviceExtension->MouseResetState = 1021;
830 			return TRUE;
831 		case 1022:
832 		case 1024:
833 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
834 			DeviceExtension->MouseResetState++;
835 			return TRUE;
836 		case 1021:
837 		case 1023:
838 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xC8);
839 			DeviceExtension->MouseResetState++;
840 			return TRUE;
841 		case 1025:
842 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x50);
843 			DeviceExtension->MouseResetState++;
844 			return TRUE;
845 		case 1026:
846 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2);
847 			DeviceExtension->MouseResetState++;
848 			return TRUE;
849 		case 1027:
850 			if (0x04 == Value)
851 			{
852 				DeviceExtension->MouseAttributes.NumberOfButtons = 5;
853 				DeviceExtension->MouseAttributes.MouseIdentifier =
854 					WHEELMOUSE_I8042_HARDWARE;
855 				DeviceExtension->MouseType = IntellimouseExplorer;
856 			}
857 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
858 			DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK;
859 			return TRUE;
860 		case ExpectingSetSamplingRateACK:
861 			DeviceExtension->MouseHook.IsrWritePort(
862 				DeviceExtension->MouseHook.CallContext,
863 				(UCHAR)DeviceExtension->MouseAttributes.SampleRate);
864 			DeviceExtension->MouseResetState++;
865 			return TRUE;
866 		case ExpectingSetSamplingRateValueACK:
867 			if (MOUSE_NACK == Value)
868 			{
869 				DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x3C);
870 				DeviceExtension->MouseAttributes.SampleRate = (USHORT)PortDeviceExtension->Settings.SampleRate;
871 				DeviceExtension->MouseResetState = 1040;
872 				return TRUE;
873 			}
874 		case 1040:  /* Fallthrough */
875 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE8);
876 			DeviceExtension->MouseResetState = ExpectingFinalResolutionACK;
877 			return TRUE;
878 		case ExpectingFinalResolutionACK:
879 			DeviceExtension->MouseHook.IsrWritePort(
880 				DeviceExtension->MouseHook.CallContext,
881 				(UCHAR)(PortDeviceExtension->Settings.MouseResolution & 0xff));
882 			INFO_(I8042PRT, "Mouse resolution %lu\n",
883 				PortDeviceExtension->Settings.MouseResolution);
884 			DeviceExtension->MouseResetState = ExpectingFinalResolutionValueACK;
885 			return TRUE;
886 		case ExpectingFinalResolutionValueACK:
887 			DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF4);
888 			DeviceExtension->MouseResetState = ExpectingEnableACK;
889 			return TRUE;
890 		case ExpectingEnableACK:
891 			PortDeviceExtension->Flags |= MOUSE_PRESENT;
892 			DeviceExtension->MouseState = MouseIdle;
893 			DeviceExtension->MouseTimeoutState = TimeoutCancel;
894 			INFO_(I8042PRT, "Mouse type = %u\n", DeviceExtension->MouseType);
895 			return TRUE;
896 		default:
897 			if (DeviceExtension->MouseResetState < 100 || DeviceExtension->MouseResetState > 999)
898 				ERR_(I8042PRT, "MouseResetState went out of range: %lu\n", DeviceExtension->MouseResetState);
899 			return FALSE;
900 	}
901 }
902 
903 BOOLEAN NTAPI
904 i8042MouInterruptService(
905 	IN PKINTERRUPT Interrupt,
906 	PVOID Context)
907 {
908 	PI8042_MOUSE_EXTENSION DeviceExtension;
909 	PPORT_DEVICE_EXTENSION PortDeviceExtension;
910 	ULONG Counter;
911 	UCHAR Output = 0, PortStatus = 0;
912 	NTSTATUS Status;
913 
914 	UNREFERENCED_PARAMETER(Interrupt);
915 
916 	__analysis_assume(Context != NULL);
917 	DeviceExtension = Context;
918 	PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
919 	Counter = PortDeviceExtension->Settings.PollStatusIterations;
920 
921 	while (Counter)
922 	{
923 		Status = i8042ReadStatus(PortDeviceExtension, &PortStatus);
924 		if (!NT_SUCCESS(Status))
925 		{
926 			WARN_(I8042PRT, "i8042ReadStatus() failed with status 0x%08lx\n", Status);
927 			return FALSE;
928 		}
929 		Status = i8042ReadMouseData(PortDeviceExtension, &Output);
930 		if (NT_SUCCESS(Status))
931 			break;
932 		KeStallExecutionProcessor(1);
933 		Counter--;
934 	}
935 	if (Counter == 0)
936 	{
937 		WARN_(I8042PRT, "Spurious i8042 mouse interrupt\n");
938 		return FALSE;
939 	}
940 
941 	INFO_(I8042PRT, "Got: 0x%02x\n", Output);
942 
943 	if (i8042PacketIsr(PortDeviceExtension, Output))
944 	{
945 		if (PortDeviceExtension->PacketComplete)
946 		{
947 			TRACE_(I8042PRT, "Packet complete\n");
948 			KeInsertQueueDpc(&DeviceExtension->DpcMouse, NULL, NULL);
949 		}
950 		TRACE_(I8042PRT, "Irq eaten by packet\n");
951 		return TRUE;
952 	}
953 
954 	TRACE_(I8042PRT, "Irq is mouse input\n");
955 
956 	i8042MouInputTestTimeout(DeviceExtension);
957 
958 	if (i8042MouResetIsr(DeviceExtension, PortStatus, Output))
959 	{
960 		TRACE_(I8042PRT, "Handled by ResetIsr or hooked Isr\n");
961 		if (NoChange != DeviceExtension->MouseTimeoutState) {
962 			KeInsertQueueDpc(&DeviceExtension->DpcMouse, NULL, NULL);
963 		}
964 		return TRUE;
965 	}
966 
967 	if (DeviceExtension->MouseType == Ps2pp)
968 		i8042MouHandlePs2pp(DeviceExtension, Output);
969 	else
970 		i8042MouHandle(DeviceExtension, Output);
971 
972 	return TRUE;
973 }
974