merge ROS Shell without integrated explorer part into trunk
[reactos.git] / reactos / drivers / input / i8042prt / mouse.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/input/i8042prt/mouse.c
5 * PURPOSE: i8042 (ps/2 keyboard-mouse controller) driver
6 * mouse specifics
7 * PROGRAMMER: Victor Kirhenshtein (sauros@iname.com)
8 * Jason Filby (jasonfilby@yahoo.com)
9 * Tinus
10 */
11
12 /* INCLUDES ****************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <string.h>
16 #include <ntos/keyboard.h>
17 #include <ntos/minmax.h>
18 #include <rosrtl/string.h>
19
20 #include <ddk/ntddkbd.h>
21 #include <ddk/ntdd8042.h>
22
23 #define NDEBUG
24 #include <debug.h>
25
26 #include "i8042prt.h"
27
28 /*
29 * These functions are callbacks for filter driver custom interrupt
30 * service routines.
31 */
32 VOID STDCALL I8042IsrWritePortMouse(PVOID Context,
33 UCHAR Value)
34 {
35 I8042IsrWritePort(Context, Value, 0xD4);
36 }
37
38 NTSTATUS STDCALL I8042SynchWritePortMouse(PVOID Context,
39 UCHAR Value,
40 BOOLEAN WaitForAck)
41 {
42 return I8042SynchWritePort((PDEVICE_EXTENSION)Context,
43 0xD4,
44 Value,
45 WaitForAck);
46 }
47
48 /*
49 * Call the customization hook. The Ret2 parameter is about wether
50 * we should go on with the interrupt. The return value is what
51 * we should return (indicating to the system wether someone else
52 * should try to handle the interrupt)
53 */
54 BOOLEAN STDCALL I8042MouseCallIsrHook(PDEVICE_EXTENSION DevExt,
55 UCHAR Status,
56 PUCHAR Input,
57 PBOOLEAN ToReturn)
58 {
59 BOOLEAN HookReturn, HookContinue;
60
61 HookContinue = FALSE;
62
63 if (DevExt->MouseHook.IsrRoutine) {
64 HookReturn = DevExt->MouseHook.IsrRoutine(
65 DevExt->MouseHook.Context,
66 DevExt->MouseBuffer + DevExt->MouseInBuffer,
67 &DevExt->Packet,
68 Status,
69 Input,
70 &HookContinue,
71 &DevExt->MouseState,
72 &DevExt->MouseResetState);
73
74 if (!HookContinue) {
75 *ToReturn = HookReturn;
76 return TRUE;
77 }
78 }
79 return FALSE;
80 }
81
82 BOOLEAN STDCALL I8042MouseResetIsr(PDEVICE_EXTENSION DevExt,
83 UCHAR Status,
84 PUCHAR Value)
85 {
86 BOOLEAN ToReturn;
87
88 if (I8042MouseCallIsrHook(DevExt, Status, Value, &ToReturn))
89 return ToReturn;
90
91 if (MouseResetting != DevExt->MouseState) {
92 return FALSE;
93 }
94 DevExt->MouseTimeoutState = TimeoutStart;
95
96 switch (DevExt->MouseResetState) {
97 case 1100: /* the first ack, drop it. */
98 DevExt->MouseResetState = ExpectingReset;
99 return TRUE;
100 /* First, 0xFF is sent. The mouse is supposed to say AA00 if ok,
101 * FC00 if not.
102 */
103 case ExpectingReset:
104 if (0xAA == *Value) {
105 DevExt->MouseResetState++;
106 } else {
107 DevExt->MouseExists = FALSE;
108 DevExt->MouseState = MouseIdle;
109 DPRINT("Mouse returned bad reset reply: "
110 "%x (expected aa)\n", *Value);
111 }
112 return TRUE;
113 case ExpectingResetId:
114 if (0x00 == *Value) {
115 DevExt->MouseResetState++;
116 DevExt->MouseType = GenericPS2;
117 I8042IsrWritePortMouse(DevExt, 0xF2);
118 } else {
119 DevExt->MouseExists = FALSE;
120 DevExt->MouseState = MouseIdle;
121 DPRINT1("Mouse returned bad reset reply part two: "
122 "%x (expected 0)\n", Value);
123 }
124 return TRUE;
125 case ExpectingGetDeviceIdACK:
126 if (MOUSE_ACK == *Value) {
127 DevExt->MouseResetState++;
128 } else if (MOUSE_NACK == *Value ||
129 MOUSE_ERROR == *Value) {
130 DevExt->MouseResetState++;
131 /* Act as if 00 (normal mouse) was received */
132 DPRINT("Mouse doesn't support 0xd2, "
133 "(returns %x, expected %x), faking.\n",
134 *Value, MOUSE_ACK);
135 *Value = 0;
136 I8042MouseResetIsr(DevExt, Status, Value);
137 }
138 return TRUE;
139 case ExpectingGetDeviceIdValue:
140 switch (*Value) {
141 case 0x02:
142 DevExt->MouseAttributes.MouseIdentifier =
143 BALLPOINT_I8042_HARDWARE;
144 break;
145 case 0x03:
146 case 0x04:
147 DevExt->MouseAttributes.MouseIdentifier =
148 WHEELMOUSE_I8042_HARDWARE;
149 break;
150 default:
151 DevExt->MouseAttributes.MouseIdentifier =
152 MOUSE_I8042_HARDWARE;
153 }
154 DevExt->MouseResetState++;
155 I8042IsrWritePortMouse(DevExt, 0xE8);
156 return TRUE;
157 case ExpectingSetResolutionDefaultACK:
158 DevExt->MouseResetState++;
159 I8042IsrWritePortMouse(DevExt, 0x00);
160 return TRUE;
161 case ExpectingSetResolutionDefaultValueACK:
162 DevExt->MouseResetState = ExpectingSetScaling1to1ACK;
163 I8042IsrWritePortMouse(DevExt, 0xE6);
164 return TRUE;
165 case ExpectingSetScaling1to1ACK:
166 case ExpectingSetScaling1to1ACK2:
167 DevExt->MouseResetState++;
168 I8042IsrWritePortMouse(DevExt, 0xE6);
169 return TRUE;
170 case ExpectingSetScaling1to1ACK3:
171 DevExt->MouseResetState++;
172 I8042IsrWritePortMouse(DevExt, 0xE9);
173 return TRUE;
174 case ExpectingReadMouseStatusACK:
175 DevExt->MouseResetState++;
176 return TRUE;
177 case ExpectingReadMouseStatusByte1:
178 DevExt->MouseLogiBuffer[0] = *Value;
179 DevExt->MouseResetState++;
180 return TRUE;
181 case ExpectingReadMouseStatusByte2:
182 DevExt->MouseLogiBuffer[1] = *Value;
183 DevExt->MouseResetState++;
184 return TRUE;
185 case ExpectingReadMouseStatusByte3:
186 DevExt->MouseLogiBuffer[2] = *Value;
187 /* Now MouseLogiBuffer is a set of info. If the second
188 * byte is 0, the mouse didn't understand the magic
189 * code. Otherwise, it it a Logitech and the second byte
190 * is the number of buttons, bit 7 of the first byte tells
191 * if it understands special E7 commands, the rest is an ID.
192 */
193 if (DevExt->MouseLogiBuffer[1]) {
194 DevExt->MouseAttributes.NumberOfButtons =
195 DevExt->MouseLogiBuffer[1];
196 /* For some reason the ID is the wrong way around */
197 DevExt->MouseLogitechID =
198 ((DevExt->MouseLogiBuffer[0] >> 4) & 0x07) |
199 ((DevExt->MouseLogiBuffer[0] << 3) & 0x78);
200 DevExt->MouseType = Ps2pp;
201 I8042IsrWritePortMouse(DevExt, 0xf3);
202 DevExt->MouseResetState = ExpectingSetSamplingRateACK;
203 return TRUE;
204 /* TODO: Go through EnableWheel and Enable5Buttons */
205 }
206 DevExt->MouseResetState = EnableWheel;
207 I8042MouseResetIsr(DevExt, Status, Value);
208 return TRUE;
209 case EnableWheel:
210 I8042IsrWritePortMouse(DevExt, 0xf3);
211 DevExt->MouseResetState = 1001;
212 return TRUE;
213 case 1001:
214 I8042IsrWritePortMouse(DevExt, 0xc8);
215 DevExt->MouseResetState++;
216 return TRUE;
217 case 1002:
218 case 1004:
219 I8042IsrWritePortMouse(DevExt, 0xf3);
220 DevExt->MouseResetState++;
221 return TRUE;
222 case 1003:
223 I8042IsrWritePortMouse(DevExt, 0x64);
224 DevExt->MouseResetState++;
225 return TRUE;
226 case 1005:
227 I8042IsrWritePortMouse(DevExt, 0x50);
228 DevExt->MouseResetState++;
229 return TRUE;
230 case 1006:
231 I8042IsrWritePortMouse(DevExt, 0xf2);
232 DevExt->MouseResetState++;
233 return TRUE;
234 case 1007:
235 /* Ignore ACK */
236 DevExt->MouseResetState++;
237 return TRUE;
238 case 1008:
239 /* Now if the value == 3, it's either an Intellimouse
240 * or Intellimouse Explorer. */
241 if (0x03 == *Value) {
242 DevExt->MouseAttributes.NumberOfButtons = 3;
243 DevExt->MouseAttributes.MouseIdentifier =
244 WHEELMOUSE_I8042_HARDWARE;
245 DevExt->MouseType = Intellimouse;
246 DevExt->MouseResetState = Enable5Buttons;
247 I8042MouseResetIsr(DevExt, Status, Value);
248 return TRUE;
249 } /* Else, just set the default settings and be done */
250 I8042IsrWritePortMouse(DevExt, 0xf3);
251 DevExt->MouseResetState = ExpectingSetSamplingRateACK;
252 return TRUE;
253 case Enable5Buttons:
254 I8042IsrWritePortMouse(DevExt, 0xf3);
255 DevExt->MouseResetState = 1021;
256 return TRUE;
257 case 1022:
258 case 1024:
259 I8042IsrWritePortMouse(DevExt, 0xf3);
260 DevExt->MouseResetState++;
261 return TRUE;
262 case 1021:
263 case 1023:
264 I8042IsrWritePortMouse(DevExt, 0xc8);
265 DevExt->MouseResetState++;
266 return TRUE;
267 case 1025:
268 I8042IsrWritePortMouse(DevExt, 0x50);
269 DevExt->MouseResetState++;
270 return TRUE;
271 case 1026:
272 I8042IsrWritePortMouse(DevExt, 0xf2);
273 DevExt->MouseResetState++;
274 return TRUE;
275 case 1027:
276 if (0x04 == *Value) {
277 DevExt->MouseAttributes.NumberOfButtons = 5;
278 DevExt->MouseAttributes.MouseIdentifier =
279 WHEELMOUSE_I8042_HARDWARE;
280 DevExt->MouseType = IntellimouseExplorer;
281 }
282 I8042IsrWritePortMouse(DevExt, 0xf3);
283 DevExt->MouseResetState = ExpectingSetSamplingRateACK;
284 return TRUE;
285 case ExpectingSetSamplingRateACK:
286 I8042IsrWritePortMouse(DevExt,
287 DevExt->MouseAttributes.SampleRate);
288 DevExt->MouseResetState++;
289 return TRUE;
290 case ExpectingSetSamplingRateValueACK:
291 if (MOUSE_NACK == *Value) {
292 I8042IsrWritePortMouse(DevExt, 0x3c);
293 DevExt->MouseAttributes.SampleRate = 60;
294 DevExt->MouseResetState = 1040;
295 return TRUE;
296 }
297 case 1040: /* Fallthrough */
298 I8042IsrWritePortMouse(DevExt, 0xe8);
299 DevExt->MouseResetState = ExpectingFinalResolutionACK;
300 return TRUE;
301 case ExpectingFinalResolutionACK:
302 I8042IsrWritePortMouse(DevExt, 0x03);
303 DevExt->MouseResetState = ExpectingFinalResolutionValueACK;
304 return TRUE;
305 case ExpectingFinalResolutionValueACK:
306 I8042IsrWritePortMouse(DevExt, 0xf4);
307 DevExt->MouseResetState = ExpectingEnableACK;
308 return TRUE;
309 case ExpectingEnableACK:
310 DevExt->MouseState = MouseIdle;
311 DevExt->MouseTimeoutState = TimeoutCancel;
312 DPRINT("Mouse type = %d\n", DevExt->MouseType);
313 return TRUE;
314 default:
315 if (DevExt->MouseResetState < 100 ||
316 DevExt->MouseResetState > 999)
317 DPRINT1("MouseResetState went out of range: %d\n",
318 DevExt->MouseResetState);
319 return FALSE;
320 }
321 }
322
323 /*
324 * Prepare for reading the next packet and queue the dpc for handling
325 * this one.
326 *
327 * Context is the device object.
328 */
329 VOID STDCALL I8042QueueMousePacket(PVOID Context)
330 {
331 PDEVICE_OBJECT DeviceObject = Context;
332 PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
333 PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
334
335 DevExt->MouseComplete = TRUE;
336 DevExt->MouseInBuffer++;
337 if (DevExt->MouseInBuffer >
338 DevExt->MouseAttributes.InputDataQueueLength) {
339 DPRINT1("Mouse buffer overflow\n");
340 DevExt->MouseInBuffer--;
341 }
342
343 DPRINT("Irq completes mouse packet\n");
344 KeInsertQueueDpc(&DevExt->DpcMouse, DevExt, NULL);
345 }
346
347 /*
348 * Updates ButtonFlags according to RawButtons and a saved state;
349 * Only takes in account the bits that are set in Mask
350 */
351 VOID STDCALL I8042MouseHandleButtons(PDEVICE_EXTENSION DevExt,
352 USHORT Mask)
353 {
354 PMOUSE_INPUT_DATA MouseInput = DevExt->MouseBuffer +
355 DevExt->MouseInBuffer;
356 USHORT NewButtonData = MouseInput->RawButtons & Mask;
357 USHORT ButtonDiff = (NewButtonData ^ DevExt->MouseButtonState) & Mask;
358
359 /* Note that the defines are such:
360 * MOUSE_LEFT_BUTTON_DOWN 1
361 * MOUSE_LEFT_BUTTON_UP 2
362 */
363 MouseInput->ButtonFlags |= (NewButtonData & ButtonDiff) |
364 (((~(NewButtonData)) << 1) &
365 (ButtonDiff << 1)) |
366 (MouseInput->RawButtons & 0xfc00);
367
368 DPRINT("Left raw/up/down: %d/%d/%d\n", MouseInput->RawButtons & MOUSE_LEFT_BUTTON_DOWN,
369 MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_DOWN,
370 MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_UP);
371
372 DevExt->MouseButtonState = (DevExt->MouseButtonState & ~Mask) |
373 (NewButtonData & Mask);
374 }
375
376 VOID STDCALL I8042MouseHandle(PDEVICE_EXTENSION DevExt,
377 BYTE Output)
378 {
379 PMOUSE_INPUT_DATA MouseInput = DevExt->MouseBuffer +
380 DevExt->MouseInBuffer;
381 CHAR Scroll;
382
383 switch (DevExt->MouseState) {
384 case MouseIdle:
385 /* This bit should be 1, if not drop the packet, we
386 * might be lucky and get in sync again
387 */
388 if (!(Output & 8)) {
389 DPRINT1("Bad input, dropping..\n");
390 return;
391 }
392
393 MouseInput->Buttons = 0;
394 MouseInput->RawButtons = 0;
395 MouseInput->Flags = MOUSE_MOVE_RELATIVE;
396
397 /* Note how we ignore the overflow bits, like Windows
398 * is said to do. There's no reasonable thing to do
399 * anyway.
400 */
401
402 if (Output & 16)
403 MouseInput->LastX = 1;
404 else
405 MouseInput->LastX = 0;
406
407 if (Output & 32)
408 MouseInput->LastY = 1;
409 else
410 MouseInput->LastY = 0;
411
412 if (Output & 1) {
413 MouseInput->RawButtons |= MOUSE_LEFT_BUTTON_DOWN;
414 }
415
416 if (Output & 2) {
417 MouseInput->RawButtons |= MOUSE_RIGHT_BUTTON_DOWN;
418 }
419
420 if (Output & 4) {
421 MouseInput->RawButtons |= MOUSE_MIDDLE_BUTTON_DOWN;
422 }
423 DevExt->MouseState = XMovement;
424 break;
425 case XMovement:
426 if (MouseInput->LastX)
427 MouseInput->LastX = (LONG) Output - 256;
428 else
429 MouseInput->LastX = Output;
430
431 DevExt->MouseState = YMovement;
432 break;
433 case YMovement:
434 if (MouseInput->LastY)
435 MouseInput->LastY = (LONG)Output - 256;
436 else
437 MouseInput->LastY = (LONG)Output;
438
439 /* Windows wants it the other way around */
440 MouseInput->LastY = -MouseInput->LastY;
441
442 if (DevExt->MouseType == GenericPS2 ||
443 DevExt->MouseType == Ps2pp) {
444 I8042MouseHandleButtons(DevExt,
445 MOUSE_LEFT_BUTTON_DOWN |
446 MOUSE_RIGHT_BUTTON_DOWN |
447 MOUSE_MIDDLE_BUTTON_DOWN);
448 I8042QueueMousePacket(
449 DevExt->MouseObject);
450 DevExt->MouseState = MouseIdle;
451 } else {
452 DevExt->MouseState = ZMovement;
453 }
454 break;
455 case ZMovement:
456 Scroll = Output & 0x0f;
457 if (Scroll & 8)
458 Scroll |= 0xf0;
459
460 if (Scroll) {
461 MouseInput->RawButtons |= MOUSE_WHEEL;
462 MouseInput->ButtonData = (USHORT) Scroll;
463 }
464
465 if (DevExt->MouseType == IntellimouseExplorer) {
466 if (Output & 16)
467 MouseInput->RawButtons |= MOUSE_BUTTON_4_DOWN;
468
469 if (Output & 32)
470 MouseInput->RawButtons |= MOUSE_BUTTON_5_DOWN;
471 }
472 I8042MouseHandleButtons(DevExt, MOUSE_LEFT_BUTTON_DOWN |
473 MOUSE_RIGHT_BUTTON_DOWN |
474 MOUSE_MIDDLE_BUTTON_DOWN |
475 MOUSE_BUTTON_4_DOWN |
476 MOUSE_BUTTON_5_DOWN);
477 I8042QueueMousePacket(DevExt->MouseObject);
478 DevExt->MouseState = MouseIdle;
479 break;
480 default:
481 DPRINT1("Unexpected state!\n");
482 }
483 }
484
485 BOOLEAN STDCALL I8042InterruptServiceMouse(struct _KINTERRUPT *Interrupt,
486 VOID *Context)
487 {
488 BYTE Output, PortStatus;
489 NTSTATUS Status;
490 PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION) Context;
491 UINT Iterations = 0;
492
493 do {
494 Status = I8042ReadStatus(&PortStatus);
495 Status = I8042ReadData(&Output);
496 Iterations++;
497 if (STATUS_SUCCESS == Status)
498 break;
499 KeStallExecutionProcessor(1);
500 } while (Iterations < DevExt->Settings.PollStatusIterations);
501
502 if (STATUS_SUCCESS != Status) {
503 DPRINT1("Spurious I8042 mouse interrupt\n");
504 return FALSE;
505 }
506
507 DPRINT("Got: %x\n", Output);
508
509 if (I8042PacketIsr(DevExt, Output)) {
510 if (DevExt->PacketComplete) {
511 DPRINT("Packet complete\n");
512 KeInsertQueueDpc(&DevExt->DpcKbd, DevExt, NULL);
513 }
514 DPRINT("Irq eaten by packet\n");
515 return TRUE;
516 }
517
518 if (I8042MouseResetIsr(DevExt, PortStatus, &Output)) {
519 DPRINT("Handled by ResetIsr or hooked Isr\n");
520 if (NoChange != DevExt->MouseTimeoutState) {
521 KeInsertQueueDpc(&DevExt->DpcMouse, DevExt, NULL);
522 }
523 return TRUE;
524 }
525
526 if (DevExt->MouseType == Ps2pp)
527 I8042MouseHandlePs2pp(DevExt, Output);
528 else
529 I8042MouseHandle(DevExt, Output);
530
531 return TRUE;
532 }
533
534 VOID STDCALL I8042DpcRoutineMouse(PKDPC Dpc,
535 PVOID DeferredContext,
536 PVOID SystemArgument1,
537 PVOID SystemArgument2)
538 {
539 PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)SystemArgument1;
540 ULONG MouseTransferred = 0;
541 ULONG MouseInBufferCopy;
542 KIRQL Irql;
543 LARGE_INTEGER Timeout;
544
545 switch (DevExt->MouseTimeoutState) {
546 case TimeoutStart:
547 DevExt->MouseTimeoutState = NoChange;
548 if (DevExt->MouseTimeoutActive &&
549 !KeCancelTimer(&DevExt->TimerMouseTimeout)) {
550 /* The timer fired already, give up */
551 DevExt->MouseTimeoutActive = FALSE;
552 return;
553 }
554
555 Timeout.QuadPart = -15000000;
556 /* 1.5 seconds, should be enough */
557
558 KeSetTimer(&DevExt->TimerMouseTimeout,
559 Timeout,
560 &DevExt->DpcMouseTimeout);
561 DevExt->MouseTimeoutActive = TRUE;
562 return;
563 case TimeoutCancel:
564 DevExt->MouseTimeoutState = NoChange;
565 KeCancelTimer(&DevExt->TimerMouseTimeout);
566 DevExt->MouseTimeoutActive = FALSE;
567 default:
568 /* nothing, don't want a warning */ ;
569 }
570
571 /* Should be unlikely */
572 if (!DevExt->MouseComplete)
573 return;
574
575 Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
576
577 DevExt->MouseComplete = FALSE;
578 MouseInBufferCopy = DevExt->MouseInBuffer;
579
580 KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
581
582 DPRINT ("Send a mouse packet\n");
583
584 if (!DevExt->MouseData.ClassService)
585 return;
586
587 ((MOUSE_CLASS_SERVICE_CALLBACK) DevExt->MouseData.ClassService)(
588 DevExt->MouseData.ClassDeviceObject,
589 DevExt->MouseBuffer,
590 DevExt->MouseBuffer + MouseInBufferCopy,
591 &MouseTransferred);
592
593 Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
594
595 DevExt->MouseInBuffer -= MouseTransferred;
596 if (DevExt->MouseInBuffer)
597 RtlMoveMemory(DevExt->MouseBuffer,
598 DevExt->MouseBuffer+MouseTransferred,
599 DevExt->MouseInBuffer * sizeof(MOUSE_INPUT_DATA));
600
601 KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
602 }
603
604 /* This timer DPC will be called when the mouse reset times out.
605 * I'll just send the 'disable mouse port' command to the controller
606 * and say the mouse doesn't exist.
607 */
608 VOID STDCALL I8042DpcRoutineMouseTimeout(PKDPC Dpc,
609 PVOID DeferredContext,
610 PVOID SystemArgument1,
611 PVOID SystemArgument2)
612 {
613 PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)DeferredContext;
614 KIRQL Irql;
615
616 Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
617
618 DPRINT1("Mouse initialization timeout! (substate %x) "
619 "Disabling mouse.\n",
620 DevExt->MouseResetState);
621
622 if (!I8042MouseDisable(DevExt)) {
623 DPRINT1("Failed to disable mouse.\n");
624 }
625
626 DevExt->MouseExists = FALSE;
627
628 KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
629 }
630
631 /*
632 * Process the mouse internal device requests
633 * returns FALSE if it doesn't understand the
634 * call so someone else can handle it.
635 */
636 BOOLEAN STDCALL I8042StartIoMouse(PDEVICE_OBJECT DeviceObject, PIRP Irp)
637 {
638 PIO_STACK_LOCATION Stk;
639 PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
640 PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
641
642 Stk = IoGetCurrentIrpStackLocation(Irp);
643
644 switch (Stk->Parameters.DeviceIoControl.IoControlCode) {
645 case IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER:
646 I8042StartPacket(
647 DevExt,
648 DeviceObject,
649 Stk->Parameters.DeviceIoControl.Type3InputBuffer,
650 Stk->Parameters.DeviceIoControl.InputBufferLength,
651 Irp);
652 break;
653
654 default:
655 return FALSE;
656 }
657
658 return TRUE;
659 }
660
661 /*
662 * Runs the mouse IOCTL_INTERNAL dispatch.
663 * Returns NTSTATUS_INVALID_DEVICE_REQUEST if it doesn't handle this request
664 * so someone else can have a try at it.
665 */
666 NTSTATUS STDCALL I8042InternalDeviceControlMouse(PDEVICE_OBJECT DeviceObject, PIRP Irp)
667 {
668 PIO_STACK_LOCATION Stk;
669 PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
670 PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
671
672 DPRINT("InternalDeviceControl\n");
673
674 Irp->IoStatus.Information = 0;
675 Stk = IoGetCurrentIrpStackLocation(Irp);
676
677 switch (Stk->Parameters.DeviceIoControl.IoControlCode) {
678
679 case IOCTL_INTERNAL_MOUSE_CONNECT:
680 DPRINT("IOCTL_INTERNAL_MOUSE_CONNECT\n");
681 if (Stk->Parameters.DeviceIoControl.InputBufferLength <
682 sizeof(CONNECT_DATA)) {
683 DPRINT1("Mouse IOCTL_INTERNAL_MOUSE_CONNECT "
684 "invalid buffer size\n");
685 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
686 goto intcontfailure;
687 }
688
689 if (!DevExt->MouseExists) {
690 Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
691 goto intcontfailure;
692 }
693
694 if (DevExt->MouseClaimed) {
695 DPRINT1("IOCTL_INTERNAL_MOUSE_CONNECT: "
696 "Mouse is already claimed\n");
697 Irp->IoStatus.Status = STATUS_SHARING_VIOLATION;
698 goto intcontfailure;
699 }
700
701 memcpy(&DevExt->MouseData,
702 Stk->Parameters.DeviceIoControl.Type3InputBuffer,
703 sizeof(CONNECT_DATA));
704 DevExt->MouseHook.IsrWritePort = I8042IsrWritePortMouse;
705 DevExt->MouseHook.QueueMousePacket =
706 I8042QueueMousePacket;
707 DevExt->MouseHook.CallContext = DevExt;
708
709 {
710 PIO_WORKITEM WorkItem;
711 PI8042_HOOK_WORKITEM WorkItemData;
712
713 WorkItem = IoAllocateWorkItem(DeviceObject);
714 if (!WorkItem) {
715 DPRINT ("IOCTL_INTERNAL_MOUSE_CONNECT: "
716 "Can't allocate work item\n");
717 Irp->IoStatus.Status =
718 STATUS_INSUFFICIENT_RESOURCES;
719 goto intcontfailure;
720 }
721
722 WorkItemData = ExAllocatePoolWithTag(
723 NonPagedPool,
724 sizeof(I8042_HOOK_WORKITEM),
725 TAG_I8042);
726 if (!WorkItemData) {
727 DPRINT ("IOCTL_INTERNAL_MOUSE_CONNECT: "
728 "Can't allocate work item data\n");
729 Irp->IoStatus.Status =
730 STATUS_INSUFFICIENT_RESOURCES;
731 IoFreeWorkItem(WorkItem);
732 goto intcontfailure;
733 }
734 WorkItemData->WorkItem = WorkItem;
735 WorkItemData->Target =
736 DevExt->MouseData.ClassDeviceObject;
737 WorkItemData->Irp = Irp;
738
739 IoMarkIrpPending(Irp);
740 DevExt->MouseState = MouseResetting;
741 DevExt->MouseResetState = 1100;
742 IoQueueWorkItem(WorkItem,
743 I8042SendHookWorkItem,
744 DelayedWorkQueue,
745 WorkItemData);
746
747 Irp->IoStatus.Status = STATUS_PENDING;
748 }
749
750 break;
751 case IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER:
752 DPRINT("IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER\n");
753 if (Stk->Parameters.DeviceIoControl.InputBufferLength < 1) {
754 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
755 goto intcontfailure;
756 }
757 if (!DevExt->MouseInterruptObject) {
758 Irp->IoStatus.Status = STATUS_DEVICE_NOT_READY;
759 goto intcontfailure;
760 }
761
762 IoMarkIrpPending(Irp);
763 IoStartPacket(DeviceObject, Irp, NULL, NULL);
764 Irp->IoStatus.Status = STATUS_PENDING;
765
766 break;
767 case IOCTL_MOUSE_QUERY_ATTRIBUTES:
768 DPRINT("IOCTL_MOUSE_QUERY_ATTRIBUTES\n");
769 if (Stk->Parameters.DeviceIoControl.InputBufferLength <
770 sizeof(MOUSE_ATTRIBUTES)) {
771 DPRINT("Mouse IOCTL_MOUSE_QUERY_ATTRIBUTES "
772 "invalid buffer size\n");
773 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
774 goto intcontfailure;
775 }
776 memcpy(Irp->AssociatedIrp.SystemBuffer,
777 &DevExt->MouseAttributes,
778 sizeof(MOUSE_ATTRIBUTES));
779
780 Irp->IoStatus.Status = STATUS_SUCCESS;
781 break;
782 default:
783 Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
784 break;
785 }
786
787 intcontfailure:
788 return Irp->IoStatus.Status;
789 }
790
791 BOOLEAN STDCALL I8042MouseEnable(PDEVICE_EXTENSION DevExt)
792 {
793 UCHAR Value;
794 NTSTATUS Status;
795
796 DPRINT("Enabling mouse\n");
797
798 if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
799 DPRINT1("Can't read i8042 mode\n");
800 return FALSE;
801 }
802
803 Status = I8042ReadDataWait(DevExt, &Value);
804 if (Status != STATUS_SUCCESS) {
805 DPRINT1("No response after read i8042 mode\n");
806 return FALSE;
807 }
808
809 Value &= ~(0x20); // don't disable mouse
810 Value |= 0x02; // enable mouse interrupts
811
812 if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
813 DPRINT1("Can't set i8042 mode\n");
814 return FALSE;
815 }
816
817 if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
818 DPRINT1("Can't send i8042 mode\n");
819 return FALSE;
820 }
821
822 return TRUE;
823 }
824
825 BOOLEAN STDCALL I8042MouseDisable(PDEVICE_EXTENSION DevExt)
826 {
827 UCHAR Value;
828 NTSTATUS Status;
829
830 DPRINT("Disabling mouse\n");
831
832 I8042Flush(); /* Just to be (kind of) sure */
833
834 if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
835 DPRINT1("Can't read i8042 mode\n");
836 return FALSE;
837 }
838
839 Status = I8042ReadDataWait(DevExt, &Value);
840 if (Status != STATUS_SUCCESS) {
841 DPRINT1("No response after read i8042 mode\n");
842 return FALSE;
843 }
844
845 Value |= 0x20; // don't disable mouse
846 Value &= ~(0x02); // enable mouse interrupts
847
848 if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
849 DPRINT1("Can't set i8042 mode\n");
850 return FALSE;
851 }
852
853 if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
854 DPRINT1("Can't send i8042 mode\n");
855 return FALSE;
856 }
857
858 I8042Flush();
859 /* Just to be (kind of) sure; if the mouse would
860 * say something while we are disabling it, these bytes would
861 * block the keyboard.
862 */
863
864 return TRUE;
865 }