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
i8042MouIsrWritePort(IN PVOID Context,IN UCHAR Value)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
i8042MouQueuePacket(IN PVOID Context)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
i8042MouHandle(IN PI8042_MOUSE_EXTENSION DeviceExtension,IN UCHAR Output)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
i8042MouHandleButtons(IN PI8042_MOUSE_EXTENSION DeviceExtension,IN USHORT Mask)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
i8042MouInitialize(IN PI8042_MOUSE_EXTENSION DeviceExtension)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
i8042MouDpcRoutine(IN PKDPC Dpc,IN PVOID DeferredContext,IN PVOID SystemArgument1,IN PVOID SystemArgument2)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
i8042DpcRoutineMouseTimeout(IN PKDPC Dpc,IN PVOID DeferredContext,IN PVOID SystemArgument1,IN PVOID SystemArgument2)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
i8042MouInternalDeviceControl(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)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
i8042MouInputTestTimeout(IN PI8042_MOUSE_EXTENSION DeviceExtension)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
i8042MouCallIsrHook(IN PI8042_MOUSE_EXTENSION DeviceExtension,IN UCHAR Status,IN UCHAR Input,OUT PBOOLEAN ToReturn)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
i8042MouResetIsr(IN PI8042_MOUSE_EXTENSION DeviceExtension,IN UCHAR Status,IN UCHAR Value)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
i8042MouInterruptService(IN PKINTERRUPT Interrupt,PVOID Context)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