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