Sync up to trunk head.
[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). Disabling mouse.\n",
365 DeviceExtension->MouseResetState);
366
367 i8042Flush(PortDeviceExtension);
368 i8042ChangeMode(PortDeviceExtension, CCB_MOUSE_INT_ENAB, CCB_MOUSE_DISAB);
369 i8042Flush(PortDeviceExtension);
370
371 PortDeviceExtension->Flags &= ~MOUSE_PRESENT;
372
373 KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql);
374 }
375
376 /*
377 * Runs the mouse IOCTL_INTERNAL dispatch.
378 */
379 NTSTATUS NTAPI
380 i8042MouInternalDeviceControl(
381 IN PDEVICE_OBJECT DeviceObject,
382 IN PIRP Irp)
383 {
384 PIO_STACK_LOCATION Stack;
385 PI8042_MOUSE_EXTENSION DeviceExtension;
386 NTSTATUS Status;
387
388 Stack = IoGetCurrentIrpStackLocation(Irp);
389 Irp->IoStatus.Information = 0;
390 DeviceExtension = (PI8042_MOUSE_EXTENSION)DeviceObject->DeviceExtension;
391
392 switch (Stack->Parameters.DeviceIoControl.IoControlCode)
393 {
394 case IOCTL_INTERNAL_MOUSE_CONNECT:
395 {
396 SIZE_T Size;
397 PIO_WORKITEM WorkItem = NULL;
398 PI8042_HOOK_WORKITEM WorkItemData = NULL;
399
400 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_MOUSE_CONNECT\n");
401 if (Stack->Parameters.DeviceIoControl.InputBufferLength != sizeof(CONNECT_DATA))
402 {
403 Status = STATUS_INVALID_PARAMETER;
404 goto cleanup;
405 }
406
407 DeviceExtension->MouseData =
408 *((PCONNECT_DATA)Stack->Parameters.DeviceIoControl.Type3InputBuffer);
409
410 /* Send IOCTL_INTERNAL_I8042_HOOK_MOUSE to device stack */
411 WorkItem = IoAllocateWorkItem(DeviceObject);
412 if (!WorkItem)
413 {
414 WARN_(I8042PRT, "IoAllocateWorkItem() failed\n");
415 Status = STATUS_INSUFFICIENT_RESOURCES;
416 goto cleanup;
417 }
418 WorkItemData = ExAllocatePoolWithTag(
419 NonPagedPool,
420 sizeof(I8042_HOOK_WORKITEM),
421 I8042PRT_TAG);
422 if (!WorkItemData)
423 {
424 WARN_(I8042PRT, "ExAllocatePoolWithTag() failed\n");
425 Status = STATUS_NO_MEMORY;
426 goto cleanup;
427 }
428 WorkItemData->WorkItem = WorkItem;
429 WorkItemData->Irp = Irp;
430
431 /* Initialize extension */
432 DeviceExtension->Common.Type = Mouse;
433 Size = DeviceExtension->Common.PortDeviceExtension->Settings.MouseDataQueueSize * sizeof(MOUSE_INPUT_DATA);
434 DeviceExtension->MouseBuffer = ExAllocatePoolWithTag(
435 NonPagedPool,
436 Size,
437 I8042PRT_TAG);
438 if (!DeviceExtension->MouseBuffer)
439 {
440 WARN_(I8042PRT, "ExAllocatePoolWithTag() failed\n");
441 Status = STATUS_NO_MEMORY;
442 goto cleanup;
443 }
444 RtlZeroMemory(DeviceExtension->MouseBuffer, Size);
445 DeviceExtension->MouseAttributes.InputDataQueueLength =
446 DeviceExtension->Common.PortDeviceExtension->Settings.MouseDataQueueSize;
447 KeInitializeDpc(
448 &DeviceExtension->DpcMouse,
449 i8042MouDpcRoutine,
450 DeviceExtension);
451 KeInitializeDpc(
452 &DeviceExtension->DpcMouseTimeout,
453 i8042DpcRoutineMouseTimeout,
454 DeviceExtension);
455 KeInitializeTimer(&DeviceExtension->TimerMouseTimeout);
456 DeviceExtension->Common.PortDeviceExtension->MouseExtension = DeviceExtension;
457 DeviceExtension->Common.PortDeviceExtension->Flags |= MOUSE_CONNECTED;
458
459 IoMarkIrpPending(Irp);
460 DeviceExtension->MouseState = MouseResetting;
461 DeviceExtension->MouseResetState = 1100;
462 DeviceExtension->MouseHook.IsrWritePort = i8042MouIsrWritePort;
463 DeviceExtension->MouseHook.QueueMousePacket = i8042MouQueuePacket;
464 DeviceExtension->MouseHook.CallContext = DeviceExtension;
465 IoQueueWorkItem(WorkItem,
466 i8042SendHookWorkItem,
467 DelayedWorkQueue,
468 WorkItemData);
469 Status = STATUS_PENDING;
470 break;
471
472 cleanup:
473 if (DeviceExtension->MouseBuffer)
474 ExFreePoolWithTag(DeviceExtension->MouseBuffer, I8042PRT_TAG);
475 if (WorkItem)
476 IoFreeWorkItem(WorkItem);
477 if (WorkItemData)
478 ExFreePoolWithTag(WorkItemData, I8042PRT_TAG);
479 break;
480 }
481 case IOCTL_INTERNAL_MOUSE_DISCONNECT:
482 {
483 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_MOUSE_DISCONNECT\n");
484 /* MSDN says that operation is to implemented.
485 * To implement it, we just have to do:
486 * DeviceExtension->MouseData.ClassService = NULL;
487 */
488 Status = STATUS_NOT_IMPLEMENTED;
489 break;
490 }
491 case IOCTL_INTERNAL_I8042_HOOK_MOUSE:
492 {
493 PINTERNAL_I8042_HOOK_MOUSE MouseHook;
494 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_I8042_HOOK_MOUSE\n");
495 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONNECT_DATA))
496 {
497 Status = STATUS_INVALID_PARAMETER;
498 break;
499 }
500 MouseHook = (PINTERNAL_I8042_HOOK_MOUSE)Stack->Parameters.DeviceIoControl.Type3InputBuffer;
501
502 DeviceExtension->MouseHook.Context = MouseHook->Context;
503 if (MouseHook->IsrRoutine)
504 DeviceExtension->MouseHook.IsrRoutine = MouseHook->IsrRoutine;
505
506 Status = STATUS_SUCCESS;
507 break;
508 }
509 case IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER:
510 {
511 DPRINT1("IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER not implemented\n");
512 Status = STATUS_NOT_IMPLEMENTED;
513 break;
514 }
515 case IOCTL_INTERNAL_I8042_MOUSE_START_INFORMATION:
516 {
517 DPRINT1("IOCTL_INTERNAL_I8042_MOUSE_START_INFORMATION not implemented\n");
518 Status = STATUS_NOT_IMPLEMENTED;
519 break;
520 }
521 case IOCTL_MOUSE_QUERY_ATTRIBUTES:
522 {
523 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_MOUSE_QUERY_ATTRIBUTES\n");
524 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUSE_ATTRIBUTES))
525 {
526 Status = STATUS_BUFFER_TOO_SMALL;
527 break;
528 }
529
530 *(PMOUSE_ATTRIBUTES) Irp->AssociatedIrp.SystemBuffer = DeviceExtension->MouseAttributes;
531 Irp->IoStatus.Information = sizeof(MOUSE_ATTRIBUTES);
532 Status = STATUS_SUCCESS;
533 break;
534 }
535 default:
536 {
537 ERR_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / unknown ioctl code 0x%lx\n",
538 Stack->Parameters.DeviceIoControl.IoControlCode);
539 ASSERT(FALSE);
540 return ForwardIrpAndForget(DeviceObject, Irp);
541 }
542 }
543
544 Irp->IoStatus.Status = Status;
545 if (Status != STATUS_PENDING)
546 IoCompleteRequest(Irp, IO_NO_INCREMENT);
547 return Status;
548 }
549
550 /* Test if packets are taking too long to come in. If they do, we
551 * might have gotten out of sync and should just drop what we have.
552 *
553 * If we want to be totally right, we'd also have to keep a count of
554 * errors, and totally reset the mouse after too much of them (can
555 * happen if the user is using a KVM switch and an OS on another port
556 * resets the mouse, or if the user hotplugs the mouse, or if we're just
557 * generally unlucky). Also note the input parsing routine where we
558 * drop invalid input packets.
559 */
560 static VOID
561 i8042MouInputTestTimeout(
562 IN PI8042_MOUSE_EXTENSION DeviceExtension)
563 {
564 ULARGE_INTEGER Now;
565
566 if (DeviceExtension->MouseState == MouseExpectingACK ||
567 DeviceExtension->MouseState == MouseResetting)
568 return;
569
570 Now.QuadPart = KeQueryInterruptTime();
571
572 if (DeviceExtension->MouseState != MouseIdle) {
573 /* Check if the last byte came too long ago */
574 if (Now.QuadPart - DeviceExtension->MousePacketStartTime.QuadPart >
575 DeviceExtension->Common.PortDeviceExtension->Settings.MouseSynchIn100ns)
576 {
577 WARN_(I8042PRT, "Mouse input packet timeout\n");
578 DeviceExtension->MouseState = MouseIdle;
579 }
580 }
581
582 if (DeviceExtension->MouseState == MouseIdle)
583 DeviceExtension->MousePacketStartTime.QuadPart = Now.QuadPart;
584 }
585
586 /*
587 * Call the customization hook. The ToReturn parameter is about wether
588 * we should go on with the interrupt. The return value is what
589 * we should return (indicating to the system wether someone else
590 * should try to handle the interrupt)
591 */
592 static BOOLEAN
593 i8042MouCallIsrHook(
594 IN PI8042_MOUSE_EXTENSION DeviceExtension,
595 IN UCHAR Status,
596 IN UCHAR Input,
597 OUT PBOOLEAN ToReturn)
598 {
599 BOOLEAN HookReturn, HookContinue;
600
601 HookContinue = FALSE;
602
603 if (DeviceExtension->MouseHook.IsrRoutine)
604 {
605 HookReturn = DeviceExtension->MouseHook.IsrRoutine(
606 DeviceExtension->MouseHook.Context,
607 DeviceExtension->MouseBuffer + DeviceExtension->MouseInBuffer,
608 &DeviceExtension->Common.PortDeviceExtension->Packet,
609 Status,
610 &Input,
611 &HookContinue,
612 &DeviceExtension->MouseState,
613 &DeviceExtension->MouseResetState);
614
615 if (!HookContinue)
616 {
617 *ToReturn = HookReturn;
618 return TRUE;
619 }
620 }
621 return FALSE;
622 }
623
624 static BOOLEAN
625 i8042MouResetIsr(
626 IN PI8042_MOUSE_EXTENSION DeviceExtension,
627 IN UCHAR Status,
628 IN UCHAR Value)
629 {
630 PPORT_DEVICE_EXTENSION PortDeviceExtension;
631 BOOLEAN ToReturn = FALSE;
632
633 if (i8042MouCallIsrHook(DeviceExtension, Status, Value, &ToReturn))
634 return ToReturn;
635
636 if (MouseResetting != DeviceExtension->MouseState)
637 return FALSE;
638 DeviceExtension->MouseTimeoutState = TimeoutStart;
639 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
640
641 switch ((ULONG)DeviceExtension->MouseResetState)
642 {
643 case 1100: /* the first ack, drop it. */
644 DeviceExtension->MouseResetState = ExpectingReset;
645 return TRUE;
646 case ExpectingReset:
647 /* First, 0xFF is sent. The mouse is supposed to say AA00 if ok, FC00 if not. */
648 if (0xAA == Value)
649 {
650 DeviceExtension->MouseResetState++;
651 }
652 else
653 {
654 PortDeviceExtension->Flags &= ~MOUSE_PRESENT;
655 DeviceExtension->MouseState = MouseIdle;
656 WARN_(I8042PRT, "Mouse returned bad reset reply: %x (expected aa)\n", Value);
657 }
658 return TRUE;
659 case ExpectingResetId:
660 if (0x00 == Value)
661 {
662 DeviceExtension->MouseResetState++;
663 DeviceExtension->MouseType = GenericPS2;
664 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2);
665 }
666 else
667 {
668 PortDeviceExtension->Flags &= ~MOUSE_PRESENT;
669 DeviceExtension->MouseState = MouseIdle;
670 WARN_(I8042PRT, "Mouse returned bad reset reply part two: %x (expected 0)\n", Value);
671 }
672 return TRUE;
673 case ExpectingGetDeviceIdACK:
674 if (MOUSE_ACK == Value)
675 {
676 DeviceExtension->MouseResetState++;
677 }
678 else if (MOUSE_NACK == Value || MOUSE_ERROR == Value)
679 {
680 DeviceExtension->MouseResetState++;
681 /* Act as if 00 (normal mouse) was received */
682 WARN_(I8042PRT, "Mouse doesn't support 0xd2, (returns %x, expected %x), faking\n", Value, MOUSE_ACK);
683 i8042MouResetIsr(DeviceExtension, Status, 0);
684 }
685 return TRUE;
686 case ExpectingGetDeviceIdValue:
687 switch (Value)
688 {
689 case 0x02:
690 DeviceExtension->MouseAttributes.MouseIdentifier =
691 BALLPOINT_I8042_HARDWARE;
692 break;
693 case 0x03:
694 case 0x04:
695 DeviceExtension->MouseAttributes.MouseIdentifier =
696 WHEELMOUSE_I8042_HARDWARE;
697 break;
698 default:
699 DeviceExtension->MouseAttributes.MouseIdentifier =
700 MOUSE_I8042_HARDWARE;
701 }
702 DeviceExtension->MouseResetState++;
703 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE8);
704 return TRUE;
705 case ExpectingSetResolutionDefaultACK:
706 DeviceExtension->MouseResetState++;
707 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x00);
708 return TRUE;
709 case ExpectingSetResolutionDefaultValueACK:
710 DeviceExtension->MouseResetState = ExpectingSetScaling1to1ACK;
711 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE6);
712 return TRUE;
713 case ExpectingSetScaling1to1ACK:
714 case ExpectingSetScaling1to1ACK2:
715 DeviceExtension->MouseResetState++;
716 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE6);
717 return TRUE;
718 case ExpectingSetScaling1to1ACK3:
719 DeviceExtension->MouseResetState++;
720 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE9);
721 return TRUE;
722 case ExpectingReadMouseStatusACK:
723 DeviceExtension->MouseResetState++;
724 return TRUE;
725 case ExpectingReadMouseStatusByte1:
726 DeviceExtension->MouseLogiBuffer[0] = Value;
727 DeviceExtension->MouseResetState++;
728 return TRUE;
729 case ExpectingReadMouseStatusByte2:
730 DeviceExtension->MouseLogiBuffer[1] = Value;
731 DeviceExtension->MouseResetState++;
732 return TRUE;
733 case ExpectingReadMouseStatusByte3:
734 DeviceExtension->MouseLogiBuffer[2] = Value;
735 /* Now MouseLogiBuffer is a set of info. If the second
736 * byte is 0, the mouse didn't understand the magic
737 * code. Otherwise, it it a Logitech and the second byte
738 * is the number of buttons, bit 7 of the first byte tells
739 * if it understands special E7 commands, the rest is an ID.
740 */
741 if (DeviceExtension->MouseLogiBuffer[1])
742 {
743 DeviceExtension->MouseAttributes.NumberOfButtons =
744 DeviceExtension->MouseLogiBuffer[1];
745 DeviceExtension->MouseType = Ps2pp;
746 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
747 DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK;
748 /* TODO: Go through EnableWheel and Enable5Buttons */
749 return TRUE;
750 }
751 DeviceExtension->MouseResetState = EnableWheel;
752 i8042MouResetIsr(DeviceExtension, Status, Value);
753 return TRUE;
754 case EnableWheel:
755 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
756 DeviceExtension->MouseResetState = 1001;
757 return TRUE;
758 case 1001:
759 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xC8);
760 DeviceExtension->MouseResetState++;
761 return TRUE;
762 case 1002:
763 case 1004:
764 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
765 DeviceExtension->MouseResetState++;
766 return TRUE;
767 case 1003:
768 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x64);
769 DeviceExtension->MouseResetState++;
770 return TRUE;
771 case 1005:
772 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x50);
773 DeviceExtension->MouseResetState++;
774 return TRUE;
775 case 1006:
776 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2);
777 DeviceExtension->MouseResetState++;
778 return TRUE;
779 case 1007:
780 /* Ignore ACK */
781 DeviceExtension->MouseResetState++;
782 return TRUE;
783 case 1008:
784 if (0x03 == Value) {
785 /* It's either an Intellimouse or Intellimouse Explorer. */
786 DeviceExtension->MouseAttributes.NumberOfButtons = 3;
787 DeviceExtension->MouseAttributes.MouseIdentifier =
788 WHEELMOUSE_I8042_HARDWARE;
789 DeviceExtension->MouseType = Intellimouse;
790 DeviceExtension->MouseResetState = Enable5Buttons;
791 i8042MouResetIsr(DeviceExtension, Status, Value);
792 }
793 else
794 {
795 /* Just set the default settings and be done */
796 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
797 DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK;
798 }
799 return TRUE;
800 case Enable5Buttons:
801 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
802 DeviceExtension->MouseResetState = 1021;
803 return TRUE;
804 case 1022:
805 case 1024:
806 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
807 DeviceExtension->MouseResetState++;
808 return TRUE;
809 case 1021:
810 case 1023:
811 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xC8);
812 DeviceExtension->MouseResetState++;
813 return TRUE;
814 case 1025:
815 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x50);
816 DeviceExtension->MouseResetState++;
817 return TRUE;
818 case 1026:
819 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2);
820 DeviceExtension->MouseResetState++;
821 return TRUE;
822 case 1027:
823 if (0x04 == Value)
824 {
825 DeviceExtension->MouseAttributes.NumberOfButtons = 5;
826 DeviceExtension->MouseAttributes.MouseIdentifier =
827 WHEELMOUSE_I8042_HARDWARE;
828 DeviceExtension->MouseType = IntellimouseExplorer;
829 }
830 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3);
831 DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK;
832 return TRUE;
833 case ExpectingSetSamplingRateACK:
834 DeviceExtension->MouseHook.IsrWritePort(
835 DeviceExtension->MouseHook.CallContext,
836 (UCHAR)DeviceExtension->MouseAttributes.SampleRate);
837 DeviceExtension->MouseResetState++;
838 return TRUE;
839 case ExpectingSetSamplingRateValueACK:
840 if (MOUSE_NACK == Value)
841 {
842 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x3C);
843 DeviceExtension->MouseAttributes.SampleRate = (USHORT)PortDeviceExtension->Settings.SampleRate;
844 DeviceExtension->MouseResetState = 1040;
845 return TRUE;
846 }
847 case 1040: /* Fallthrough */
848 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE8);
849 DeviceExtension->MouseResetState = ExpectingFinalResolutionACK;
850 return TRUE;
851 case ExpectingFinalResolutionACK:
852 DeviceExtension->MouseHook.IsrWritePort(
853 DeviceExtension->MouseHook.CallContext,
854 (UCHAR)(PortDeviceExtension->Settings.MouseResolution & 0xff));
855 INFO_(I8042PRT, "Mouse resolution %lu\n",
856 PortDeviceExtension->Settings.MouseResolution);
857 DeviceExtension->MouseResetState = ExpectingFinalResolutionValueACK;
858 return TRUE;
859 case ExpectingFinalResolutionValueACK:
860 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF4);
861 DeviceExtension->MouseResetState = ExpectingEnableACK;
862 return TRUE;
863 case ExpectingEnableACK:
864 DeviceExtension->MouseState = MouseIdle;
865 DeviceExtension->MouseTimeoutState = TimeoutCancel;
866 INFO_(I8042PRT, "Mouse type = %u\n", DeviceExtension->MouseType);
867 return TRUE;
868 default:
869 if (DeviceExtension->MouseResetState < 100 || DeviceExtension->MouseResetState > 999)
870 ERR_(I8042PRT, "MouseResetState went out of range: %lu\n", DeviceExtension->MouseResetState);
871 return FALSE;
872 }
873 }
874
875 BOOLEAN NTAPI
876 i8042MouInterruptService(
877 IN PKINTERRUPT Interrupt,
878 PVOID Context)
879 {
880 PI8042_MOUSE_EXTENSION DeviceExtension;
881 PPORT_DEVICE_EXTENSION PortDeviceExtension;
882 ULONG Counter;
883 UCHAR Output = 0, PortStatus = 0;
884 NTSTATUS Status;
885
886 UNREFERENCED_PARAMETER(Interrupt);
887
888 DeviceExtension = (PI8042_MOUSE_EXTENSION)Context;
889 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
890 Counter = PortDeviceExtension->Settings.PollStatusIterations;
891
892 while (Counter)
893 {
894 Status = i8042ReadStatus(PortDeviceExtension, &PortStatus);
895 if (!NT_SUCCESS(Status))
896 {
897 WARN_(I8042PRT, "i8042ReadStatus() failed with status 0x%08lx\n", Status);
898 return FALSE;
899 }
900 Status = i8042ReadMouseData(PortDeviceExtension, &Output);
901 if (NT_SUCCESS(Status))
902 break;
903 KeStallExecutionProcessor(1);
904 Counter--;
905 }
906 if (Counter == 0)
907 {
908 WARN_(I8042PRT, "Spurious i8042 mouse interrupt\n");
909 return FALSE;
910 }
911
912 INFO_(I8042PRT, "Got: 0x%02x\n", Output);
913
914 if (i8042PacketIsr(PortDeviceExtension, Output))
915 {
916 if (PortDeviceExtension->PacketComplete)
917 {
918 TRACE_(I8042PRT, "Packet complete\n");
919 KeInsertQueueDpc(&DeviceExtension->DpcMouse, NULL, NULL);
920 }
921 TRACE_(I8042PRT, "Irq eaten by packet\n");
922 return TRUE;
923 }
924
925 TRACE_(I8042PRT, "Irq is mouse input\n");
926
927 i8042MouInputTestTimeout(DeviceExtension);
928
929 if (i8042MouResetIsr(DeviceExtension, PortStatus, Output))
930 {
931 TRACE_(I8042PRT, "Handled by ResetIsr or hooked Isr\n");
932 if (NoChange != DeviceExtension->MouseTimeoutState) {
933 KeInsertQueueDpc(&DeviceExtension->DpcMouse, NULL, NULL);
934 }
935 return TRUE;
936 }
937
938 if (DeviceExtension->MouseType == Ps2pp)
939 i8042MouHandlePs2pp(DeviceExtension, Output);
940 else
941 i8042MouHandle(DeviceExtension, Output);
942
943 return TRUE;
944 }