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