* Sync up to trunk head (r64939).
[reactos.git] / drivers / input / i8042prt / mouse.c
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%u!\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 Irp->IoStatus.Status = Status;
548 if (Status != STATUS_PENDING)
549 IoCompleteRequest(Irp, IO_NO_INCREMENT);
550 return Status;
551 }
552
553 /* Test if packets are taking too long to come in. If they do, we
554 * might have gotten out of sync and should just drop what we have.
555 *
556 * If we want to be totally right, we'd also have to keep a count of
557 * errors, and totally reset the mouse after too much of them (can
558 * happen if the user is using a KVM switch and an OS on another port
559 * resets the mouse, or if the user hotplugs the mouse, or if we're just
560 * generally unlucky). Also note the input parsing routine where we
561 * drop invalid input packets.
562 */
563 static VOID
564 i8042MouInputTestTimeout(
565 IN PI8042_MOUSE_EXTENSION DeviceExtension)
566 {
567 ULARGE_INTEGER Now;
568
569 if (DeviceExtension->MouseState == MouseExpectingACK ||
570 DeviceExtension->MouseState == MouseResetting)
571 return;
572
573 Now.QuadPart = KeQueryInterruptTime();
574
575 if (DeviceExtension->MouseState != MouseIdle) {
576 /* Check if the last byte came too long ago */
577 if (Now.QuadPart - DeviceExtension->MousePacketStartTime.QuadPart >
578 DeviceExtension->Common.PortDeviceExtension->Settings.MouseSynchIn100ns)
579 {
580 WARN_(I8042PRT, "Mouse input packet timeout\n");
581 DeviceExtension->MouseState = MouseIdle;
582 }
583 }
584
585 if (DeviceExtension->MouseState == MouseIdle)
586 DeviceExtension->MousePacketStartTime.QuadPart = Now.QuadPart;
587 }
588
589 /*
590 * Call the customization hook. The ToReturn parameter is about wether
591 * we should go on with the interrupt. The return value is what
592 * we should return (indicating to the system wether someone else
593 * should try to handle the interrupt)
594 */
595 static BOOLEAN
596 i8042MouCallIsrHook(
597 IN PI8042_MOUSE_EXTENSION DeviceExtension,
598 IN UCHAR Status,
599 IN UCHAR Input,
600 OUT PBOOLEAN ToReturn)
601 {
602 BOOLEAN HookReturn, HookContinue;
603
604 HookContinue = FALSE;
605
606 if (DeviceExtension->MouseHook.IsrRoutine)
607 {
608 HookReturn = DeviceExtension->MouseHook.IsrRoutine(
609 DeviceExtension->MouseHook.Context,
610 DeviceExtension->MouseBuffer + DeviceExtension->MouseInBuffer,
611 &DeviceExtension->Common.PortDeviceExtension->Packet,
612 Status,
613 &Input,
614 &HookContinue,
615 &DeviceExtension->MouseState,
616 &DeviceExtension->MouseResetState);
617
618 if (!HookContinue)
619 {
620 *ToReturn = HookReturn;
621 return TRUE;
622 }
623 }
624 return FALSE;
625 }
626
627 static BOOLEAN
628 i8042MouResetIsr(
629 IN PI8042_MOUSE_EXTENSION DeviceExtension,
630 IN UCHAR Status,
631 IN UCHAR Value)
632 {
633 PPORT_DEVICE_EXTENSION PortDeviceExtension;
634 BOOLEAN ToReturn = FALSE;
635
636 if (i8042MouCallIsrHook(DeviceExtension, Status, Value, &ToReturn))
637 return ToReturn;
638
639 if (MouseIdle == DeviceExtension->MouseState)
640 {
641 /* Magic packet value that indicates a reset */
642 if (0xAA == Value)
643 {
644 WARN_(I8042PRT, "Hot plugged mouse!\n");
645 DeviceExtension->MouseState = MouseResetting;
646 DeviceExtension->MouseResetState = ExpectingReset;
647 }
648 else
649 return FALSE;
650 }
651 else if (MouseResetting != DeviceExtension->MouseState)
652 return FALSE;
653
654 DeviceExtension->MouseTimeoutState = TimeoutStart;
655 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
656
657 switch ((ULONG)DeviceExtension->MouseResetState)
658 {
659 case ExpectingReset:
660 if (MOUSE_ACK == Value)
661 {
662 WARN_(I8042PRT, "Dropping extra ACK\n");
663 return TRUE;
664 }
665
666 /* First, 0xFF is sent. The mouse is supposed to say AA00 if ok, FC00 if not. */
667 if (0xAA == Value)
668 {
669 DeviceExtension->MouseResetState++;
670 }
671 else
672 {
673 PortDeviceExtension->Flags &= ~MOUSE_PRESENT;
674 DeviceExtension->MouseState = MouseIdle;
675 WARN_(I8042PRT, "Mouse returned bad reset reply: %x (expected aa)\n", Value);
676 }
677 return TRUE;
678 case ExpectingResetId:
679 if (MOUSE_ACK == Value)
680 {
681 WARN_(I8042PRT, "Dropping extra ACK #2\n");
682 return TRUE;
683 }
684
685 if (0x00 == Value)
686 {
687 DeviceExtension->MouseResetState++;
688 DeviceExtension->MouseType = GenericPS2;
689 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2);
690 }
691 else
692 {
693 PortDeviceExtension->Flags &= ~MOUSE_PRESENT;
694 DeviceExtension->MouseState = MouseIdle;
695 WARN_(I8042PRT, "Mouse returned bad reset reply part two: %x (expected 0)\n", Value);
696 }
697 return TRUE;
698 case ExpectingGetDeviceIdACK:
699 if (MOUSE_ACK == Value)
700 {
701 DeviceExtension->MouseResetState++;
702 }
703 else if (MOUSE_NACK == Value || MOUSE_ERROR == Value)
704 {
705 DeviceExtension->MouseResetState++;
706 /* Act as if 00 (normal mouse) was received */
707 WARN_(I8042PRT, "Mouse doesn't support 0xd2, (returns %x, expected %x), faking\n", Value, MOUSE_ACK);
708 i8042MouResetIsr(DeviceExtension, Status, 0);
709 }
710 return TRUE;
711 case ExpectingGetDeviceIdValue:
712 switch (Value)
713 {
714 case 0x02:
715 DeviceExtension->MouseAttributes.MouseIdentifier =
716 BALLPOINT_I8042_HARDWARE;
717 break;
718 case 0x03:
719 case 0x04:
720 DeviceExtension->MouseAttributes.MouseIdentifier =
721 WHEELMOUSE_I8042_HARDWARE;
722 break;
723 default:
724 DeviceExtension->MouseAttributes.MouseIdentifier =
725 MOUSE_I8042_HARDWARE;
726 }
727 DeviceExtension->MouseResetState++;
728 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE8);
729 return TRUE;
730 case ExpectingSetResolutionDefaultACK:
731 DeviceExtension->MouseResetState++;
732 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x00);
733 return TRUE;
734 case ExpectingSetResolutionDefaultValueACK:
735 DeviceExtension->MouseResetState = ExpectingSetScaling1to1ACK;
736 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE6);
737 return TRUE;
738 case ExpectingSetScaling1to1ACK:
739 case ExpectingSetScaling1to1ACK2:
740 DeviceExtension->MouseResetState++;
741 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE6);
742 return TRUE;
743 case ExpectingSetScaling1to1ACK3:
744 DeviceExtension->MouseResetState++;
745 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE9);
746 return TRUE;
747 case ExpectingReadMouseStatusACK:
748 DeviceExtension->MouseResetState++;
749 return TRUE;
750 case ExpectingReadMouseStatusByte1:
751 DeviceExtension->MouseLogiBuffer[0] = Value;
752 DeviceExtension->MouseResetState++;
753 return TRUE;
754 case ExpectingReadMouseStatusByte2:
755 DeviceExtension->MouseLogiBuffer[1] = Value;
756 DeviceExtension->MouseResetState++;
757 return TRUE;
758 case ExpectingReadMouseStatusByte3:
759 DeviceExtension->MouseLogiBuffer[2] = Value;
760 /* Now MouseLogiBuffer is a set of info. If the second
761 * byte is 0, the mouse didn't understand the magic
762 * code. Otherwise, it it a Logitech and the second byte
763 * is the number of buttons, bit 7 of the first byte tells
764 * if it understands special E7 commands, the rest is an ID.
765 */
766 if (DeviceExtension->MouseLogiBuffer[1])
767 {
768 DeviceExtension->MouseAttributes.NumberOfButtons =
769 DeviceExtension->MouseLogiBuffer[1];
770 DeviceExtension->MouseType = Ps2pp;
771 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
772 DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK;
773 /* TODO: Go through EnableWheel and Enable5Buttons */
774 return TRUE;
775 }
776 DeviceExtension->MouseResetState = EnableWheel;
777 i8042MouResetIsr(DeviceExtension, Status, Value);
778 return TRUE;
779 case EnableWheel:
780 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
781 DeviceExtension->MouseResetState = 1001;
782 return TRUE;
783 case 1001:
784 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xC8);
785 DeviceExtension->MouseResetState++;
786 return TRUE;
787 case 1002:
788 case 1004:
789 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
790 DeviceExtension->MouseResetState++;
791 return TRUE;
792 case 1003:
793 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x64);
794 DeviceExtension->MouseResetState++;
795 return TRUE;
796 case 1005:
797 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x50);
798 DeviceExtension->MouseResetState++;
799 return TRUE;
800 case 1006:
801 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2);
802 DeviceExtension->MouseResetState++;
803 return TRUE;
804 case 1007:
805 /* Ignore ACK */
806 DeviceExtension->MouseResetState++;
807 return TRUE;
808 case 1008:
809 if (0x03 == Value) {
810 /* It's either an Intellimouse or Intellimouse Explorer. */
811 DeviceExtension->MouseAttributes.NumberOfButtons = 3;
812 DeviceExtension->MouseAttributes.MouseIdentifier =
813 WHEELMOUSE_I8042_HARDWARE;
814 DeviceExtension->MouseType = Intellimouse;
815 DeviceExtension->MouseResetState = Enable5Buttons;
816 i8042MouResetIsr(DeviceExtension, Status, Value);
817 }
818 else
819 {
820 /* Just set the default settings and be done */
821 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
822 DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK;
823 }
824 return TRUE;
825 case Enable5Buttons:
826 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
827 DeviceExtension->MouseResetState = 1021;
828 return TRUE;
829 case 1022:
830 case 1024:
831 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
832 DeviceExtension->MouseResetState++;
833 return TRUE;
834 case 1021:
835 case 1023:
836 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xC8);
837 DeviceExtension->MouseResetState++;
838 return TRUE;
839 case 1025:
840 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x50);
841 DeviceExtension->MouseResetState++;
842 return TRUE;
843 case 1026:
844 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2);
845 DeviceExtension->MouseResetState++;
846 return TRUE;
847 case 1027:
848 if (0x04 == Value)
849 {
850 DeviceExtension->MouseAttributes.NumberOfButtons = 5;
851 DeviceExtension->MouseAttributes.MouseIdentifier =
852 WHEELMOUSE_I8042_HARDWARE;
853 DeviceExtension->MouseType = IntellimouseExplorer;
854 }
855 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
856 DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK;
857 return TRUE;
858 case ExpectingSetSamplingRateACK:
859 DeviceExtension->MouseHook.IsrWritePort(
860 DeviceExtension->MouseHook.CallContext,
861 (UCHAR)DeviceExtension->MouseAttributes.SampleRate);
862 DeviceExtension->MouseResetState++;
863 return TRUE;
864 case ExpectingSetSamplingRateValueACK:
865 if (MOUSE_NACK == Value)
866 {
867 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x3C);
868 DeviceExtension->MouseAttributes.SampleRate = (USHORT)PortDeviceExtension->Settings.SampleRate;
869 DeviceExtension->MouseResetState = 1040;
870 return TRUE;
871 }
872 case 1040: /* Fallthrough */
873 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE8);
874 DeviceExtension->MouseResetState = ExpectingFinalResolutionACK;
875 return TRUE;
876 case ExpectingFinalResolutionACK:
877 DeviceExtension->MouseHook.IsrWritePort(
878 DeviceExtension->MouseHook.CallContext,
879 (UCHAR)(PortDeviceExtension->Settings.MouseResolution & 0xff));
880 INFO_(I8042PRT, "Mouse resolution %lu\n",
881 PortDeviceExtension->Settings.MouseResolution);
882 DeviceExtension->MouseResetState = ExpectingFinalResolutionValueACK;
883 return TRUE;
884 case ExpectingFinalResolutionValueACK:
885 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF4);
886 DeviceExtension->MouseResetState = ExpectingEnableACK;
887 return TRUE;
888 case ExpectingEnableACK:
889 PortDeviceExtension->Flags |= MOUSE_PRESENT;
890 DeviceExtension->MouseState = MouseIdle;
891 DeviceExtension->MouseTimeoutState = TimeoutCancel;
892 INFO_(I8042PRT, "Mouse type = %u\n", DeviceExtension->MouseType);
893 return TRUE;
894 default:
895 if (DeviceExtension->MouseResetState < 100 || DeviceExtension->MouseResetState > 999)
896 ERR_(I8042PRT, "MouseResetState went out of range: %lu\n", DeviceExtension->MouseResetState);
897 return FALSE;
898 }
899 }
900
901 BOOLEAN NTAPI
902 i8042MouInterruptService(
903 IN PKINTERRUPT Interrupt,
904 PVOID Context)
905 {
906 PI8042_MOUSE_EXTENSION DeviceExtension;
907 PPORT_DEVICE_EXTENSION PortDeviceExtension;
908 ULONG Counter;
909 UCHAR Output = 0, PortStatus = 0;
910 NTSTATUS Status;
911
912 UNREFERENCED_PARAMETER(Interrupt);
913
914 __analysis_assume(Context != NULL);
915 DeviceExtension = Context;
916 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
917 Counter = PortDeviceExtension->Settings.PollStatusIterations;
918
919 while (Counter)
920 {
921 Status = i8042ReadStatus(PortDeviceExtension, &PortStatus);
922 if (!NT_SUCCESS(Status))
923 {
924 WARN_(I8042PRT, "i8042ReadStatus() failed with status 0x%08lx\n", Status);
925 return FALSE;
926 }
927 Status = i8042ReadMouseData(PortDeviceExtension, &Output);
928 if (NT_SUCCESS(Status))
929 break;
930 KeStallExecutionProcessor(1);
931 Counter--;
932 }
933 if (Counter == 0)
934 {
935 WARN_(I8042PRT, "Spurious i8042 mouse interrupt\n");
936 return FALSE;
937 }
938
939 INFO_(I8042PRT, "Got: 0x%02x\n", Output);
940
941 if (i8042PacketIsr(PortDeviceExtension, Output))
942 {
943 if (PortDeviceExtension->PacketComplete)
944 {
945 TRACE_(I8042PRT, "Packet complete\n");
946 KeInsertQueueDpc(&DeviceExtension->DpcMouse, NULL, NULL);
947 }
948 TRACE_(I8042PRT, "Irq eaten by packet\n");
949 return TRUE;
950 }
951
952 TRACE_(I8042PRT, "Irq is mouse input\n");
953
954 i8042MouInputTestTimeout(DeviceExtension);
955
956 if (i8042MouResetIsr(DeviceExtension, PortStatus, Output))
957 {
958 TRACE_(I8042PRT, "Handled by ResetIsr or hooked Isr\n");
959 if (NoChange != DeviceExtension->MouseTimeoutState) {
960 KeInsertQueueDpc(&DeviceExtension->DpcMouse, NULL, NULL);
961 }
962 return TRUE;
963 }
964
965 if (DeviceExtension->MouseType == Ps2pp)
966 i8042MouHandlePs2pp(DeviceExtension, Output);
967 else
968 i8042MouHandle(DeviceExtension, Output);
969
970 return TRUE;
971 }