tinus
[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,
303 DevExt->Settings.MouseResolution & 0xff);
304 DPRINT1("%x\n", DevExt->Settings.MouseResolution);
305 DevExt->MouseResetState = ExpectingFinalResolutionValueACK;
306 return TRUE;
307 case ExpectingFinalResolutionValueACK:
308 I8042IsrWritePortMouse(DevExt, 0xf4);
309 DevExt->MouseResetState = ExpectingEnableACK;
310 return TRUE;
311 case ExpectingEnableACK:
312 DevExt->MouseState = MouseIdle;
313 DevExt->MouseTimeoutState = TimeoutCancel;
314 DPRINT("Mouse type = %d\n", DevExt->MouseType);
315 return TRUE;
316 default:
317 if (DevExt->MouseResetState < 100 ||
318 DevExt->MouseResetState > 999)
319 DPRINT1("MouseResetState went out of range: %d\n",
320 DevExt->MouseResetState);
321 return FALSE;
322 }
323 }
324
325 /*
326 * Prepare for reading the next packet and queue the dpc for handling
327 * this one.
328 *
329 * Context is the device object.
330 */
331 VOID STDCALL I8042QueueMousePacket(PVOID Context)
332 {
333 PDEVICE_OBJECT DeviceObject = Context;
334 PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
335 PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
336
337 DevExt->MouseComplete = TRUE;
338 DevExt->MouseInBuffer++;
339 if (DevExt->MouseInBuffer >
340 DevExt->MouseAttributes.InputDataQueueLength) {
341 DPRINT1("Mouse buffer overflow\n");
342 DevExt->MouseInBuffer--;
343 }
344
345 DPRINT("Irq completes mouse packet\n");
346 KeInsertQueueDpc(&DevExt->DpcMouse, DevExt, NULL);
347 }
348
349 /*
350 * Updates ButtonFlags according to RawButtons and a saved state;
351 * Only takes in account the bits that are set in Mask
352 */
353 VOID STDCALL I8042MouseHandleButtons(PDEVICE_EXTENSION DevExt,
354 USHORT Mask)
355 {
356 PMOUSE_INPUT_DATA MouseInput = DevExt->MouseBuffer +
357 DevExt->MouseInBuffer;
358 USHORT NewButtonData = MouseInput->RawButtons & Mask;
359 USHORT ButtonDiff = (NewButtonData ^ DevExt->MouseButtonState) & Mask;
360
361 /* Note that the defines are such:
362 * MOUSE_LEFT_BUTTON_DOWN 1
363 * MOUSE_LEFT_BUTTON_UP 2
364 */
365 MouseInput->ButtonFlags |= (NewButtonData & ButtonDiff) |
366 (((~(NewButtonData)) << 1) &
367 (ButtonDiff << 1)) |
368 (MouseInput->RawButtons & 0xfc00);
369
370 DPRINT("Left raw/up/down: %d/%d/%d\n", MouseInput->RawButtons & MOUSE_LEFT_BUTTON_DOWN,
371 MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_DOWN,
372 MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_UP);
373
374 DevExt->MouseButtonState = (DevExt->MouseButtonState & ~Mask) |
375 (NewButtonData & Mask);
376 }
377
378 VOID STDCALL I8042MouseHandle(PDEVICE_EXTENSION DevExt,
379 BYTE Output)
380 {
381 PMOUSE_INPUT_DATA MouseInput = DevExt->MouseBuffer +
382 DevExt->MouseInBuffer;
383 CHAR Scroll;
384
385 switch (DevExt->MouseState) {
386 case MouseIdle:
387 /* This bit should be 1, if not drop the packet, we
388 * might be lucky and get in sync again
389 */
390 if (!(Output & 8)) {
391 DPRINT1("Bad input, dropping..\n");
392 return;
393 }
394
395 MouseInput->Buttons = 0;
396 MouseInput->RawButtons = 0;
397 MouseInput->Flags = MOUSE_MOVE_RELATIVE;
398
399 /* Note how we ignore the overflow bits, like Windows
400 * is said to do. There's no reasonable thing to do
401 * anyway.
402 */
403
404 if (Output & 16)
405 MouseInput->LastX = 1;
406 else
407 MouseInput->LastX = 0;
408
409 if (Output & 32)
410 MouseInput->LastY = 1;
411 else
412 MouseInput->LastY = 0;
413
414 if (Output & 1) {
415 MouseInput->RawButtons |= MOUSE_LEFT_BUTTON_DOWN;
416 }
417
418 if (Output & 2) {
419 MouseInput->RawButtons |= MOUSE_RIGHT_BUTTON_DOWN;
420 }
421
422 if (Output & 4) {
423 MouseInput->RawButtons |= MOUSE_MIDDLE_BUTTON_DOWN;
424 }
425 DevExt->MouseState = XMovement;
426 break;
427 case XMovement:
428 if (MouseInput->LastX)
429 MouseInput->LastX = (LONG) Output - 256;
430 else
431 MouseInput->LastX = Output;
432
433 DevExt->MouseState = YMovement;
434 break;
435 case YMovement:
436 if (MouseInput->LastY)
437 MouseInput->LastY = (LONG)Output - 256;
438 else
439 MouseInput->LastY = (LONG)Output;
440
441 /* Windows wants it the other way around */
442 MouseInput->LastY = -MouseInput->LastY;
443
444 if (DevExt->MouseType == GenericPS2 ||
445 DevExt->MouseType == Ps2pp) {
446 I8042MouseHandleButtons(DevExt,
447 MOUSE_LEFT_BUTTON_DOWN |
448 MOUSE_RIGHT_BUTTON_DOWN |
449 MOUSE_MIDDLE_BUTTON_DOWN);
450 I8042QueueMousePacket(
451 DevExt->MouseObject);
452 DevExt->MouseState = MouseIdle;
453 } else {
454 DevExt->MouseState = ZMovement;
455 }
456 break;
457 case ZMovement:
458 Scroll = Output & 0x0f;
459 if (Scroll & 8)
460 Scroll |= 0xf0;
461
462 if (Scroll) {
463 MouseInput->RawButtons |= MOUSE_WHEEL;
464 MouseInput->ButtonData = (USHORT) Scroll;
465 }
466
467 if (DevExt->MouseType == IntellimouseExplorer) {
468 if (Output & 16)
469 MouseInput->RawButtons |= MOUSE_BUTTON_4_DOWN;
470
471 if (Output & 32)
472 MouseInput->RawButtons |= MOUSE_BUTTON_5_DOWN;
473 }
474 I8042MouseHandleButtons(DevExt, MOUSE_LEFT_BUTTON_DOWN |
475 MOUSE_RIGHT_BUTTON_DOWN |
476 MOUSE_MIDDLE_BUTTON_DOWN |
477 MOUSE_BUTTON_4_DOWN |
478 MOUSE_BUTTON_5_DOWN);
479 I8042QueueMousePacket(DevExt->MouseObject);
480 DevExt->MouseState = MouseIdle;
481 break;
482 default:
483 DPRINT1("Unexpected state!\n");
484 }
485 }
486
487 BOOLEAN STDCALL I8042InterruptServiceMouse(struct _KINTERRUPT *Interrupt,
488 VOID *Context)
489 {
490 BYTE Output, PortStatus;
491 NTSTATUS Status;
492 PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION) Context;
493 UINT Iterations = 0;
494
495 do {
496 Status = I8042ReadStatus(&PortStatus);
497 Status = I8042ReadData(&Output);
498 Iterations++;
499 if (STATUS_SUCCESS == Status)
500 break;
501 KeStallExecutionProcessor(1);
502 } while (Iterations < DevExt->Settings.PollStatusIterations);
503
504 if (STATUS_SUCCESS != Status) {
505 DPRINT1("Spurious I8042 mouse interrupt\n");
506 return FALSE;
507 }
508
509 DPRINT("Got: %x\n", Output);
510
511 if (I8042PacketIsr(DevExt, Output)) {
512 if (DevExt->PacketComplete) {
513 DPRINT("Packet complete\n");
514 KeInsertQueueDpc(&DevExt->DpcKbd, DevExt, NULL);
515 }
516 DPRINT("Irq eaten by packet\n");
517 return TRUE;
518 }
519
520 if (I8042MouseResetIsr(DevExt, PortStatus, &Output)) {
521 DPRINT("Handled by ResetIsr or hooked Isr\n");
522 if (NoChange != DevExt->MouseTimeoutState) {
523 KeInsertQueueDpc(&DevExt->DpcMouse, DevExt, NULL);
524 }
525 return TRUE;
526 }
527
528 if (DevExt->MouseType == Ps2pp)
529 I8042MouseHandlePs2pp(DevExt, Output);
530 else
531 I8042MouseHandle(DevExt, Output);
532
533 return TRUE;
534 }
535
536 VOID STDCALL I8042DpcRoutineMouse(PKDPC Dpc,
537 PVOID DeferredContext,
538 PVOID SystemArgument1,
539 PVOID SystemArgument2)
540 {
541 PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)SystemArgument1;
542 ULONG MouseTransferred = 0;
543 ULONG MouseInBufferCopy;
544 KIRQL Irql;
545 LARGE_INTEGER Timeout;
546
547 switch (DevExt->MouseTimeoutState) {
548 case TimeoutStart:
549 DevExt->MouseTimeoutState = NoChange;
550 if (DevExt->MouseTimeoutActive &&
551 !KeCancelTimer(&DevExt->TimerMouseTimeout)) {
552 /* The timer fired already, give up */
553 DevExt->MouseTimeoutActive = FALSE;
554 return;
555 }
556
557 Timeout.QuadPart = -15000000;
558 /* 1.5 seconds, should be enough */
559
560 KeSetTimer(&DevExt->TimerMouseTimeout,
561 Timeout,
562 &DevExt->DpcMouseTimeout);
563 DevExt->MouseTimeoutActive = TRUE;
564 return;
565 case TimeoutCancel:
566 DevExt->MouseTimeoutState = NoChange;
567 KeCancelTimer(&DevExt->TimerMouseTimeout);
568 DevExt->MouseTimeoutActive = FALSE;
569 default:
570 /* nothing, don't want a warning */ ;
571 }
572
573 /* Should be unlikely */
574 if (!DevExt->MouseComplete)
575 return;
576
577 Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
578
579 DevExt->MouseComplete = FALSE;
580 MouseInBufferCopy = DevExt->MouseInBuffer;
581
582 KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
583
584 DPRINT ("Send a mouse packet\n");
585
586 if (!DevExt->MouseData.ClassService)
587 return;
588
589 ((MOUSE_CLASS_SERVICE_CALLBACK) DevExt->MouseData.ClassService)(
590 DevExt->MouseData.ClassDeviceObject,
591 DevExt->MouseBuffer,
592 DevExt->MouseBuffer + MouseInBufferCopy,
593 &MouseTransferred);
594
595 Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
596
597 DevExt->MouseInBuffer -= MouseTransferred;
598 if (DevExt->MouseInBuffer)
599 RtlMoveMemory(DevExt->MouseBuffer,
600 DevExt->MouseBuffer+MouseTransferred,
601 DevExt->MouseInBuffer * sizeof(MOUSE_INPUT_DATA));
602
603 KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
604 }
605
606 /* This timer DPC will be called when the mouse reset times out.
607 * I'll just send the 'disable mouse port' command to the controller
608 * and say the mouse doesn't exist.
609 */
610 VOID STDCALL I8042DpcRoutineMouseTimeout(PKDPC Dpc,
611 PVOID DeferredContext,
612 PVOID SystemArgument1,
613 PVOID SystemArgument2)
614 {
615 PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)DeferredContext;
616 KIRQL Irql;
617
618 Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
619
620 DPRINT1("Mouse initialization timeout! (substate %x) "
621 "Disabling mouse.\n",
622 DevExt->MouseResetState);
623
624 if (!I8042MouseDisable(DevExt)) {
625 DPRINT1("Failed to disable mouse.\n");
626 }
627
628 DevExt->MouseExists = FALSE;
629
630 KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
631 }
632
633 /*
634 * Process the mouse internal device requests
635 * returns FALSE if it doesn't understand the
636 * call so someone else can handle it.
637 */
638 BOOLEAN STDCALL I8042StartIoMouse(PDEVICE_OBJECT DeviceObject, PIRP Irp)
639 {
640 PIO_STACK_LOCATION Stk;
641 PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
642 PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
643
644 Stk = IoGetCurrentIrpStackLocation(Irp);
645
646 switch (Stk->Parameters.DeviceIoControl.IoControlCode) {
647 case IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER:
648 I8042StartPacket(
649 DevExt,
650 DeviceObject,
651 Stk->Parameters.DeviceIoControl.Type3InputBuffer,
652 Stk->Parameters.DeviceIoControl.InputBufferLength,
653 Irp);
654 break;
655
656 default:
657 return FALSE;
658 }
659
660 return TRUE;
661 }
662
663 /*
664 * Runs the mouse IOCTL_INTERNAL dispatch.
665 * Returns NTSTATUS_INVALID_DEVICE_REQUEST if it doesn't handle this request
666 * so someone else can have a try at it.
667 */
668 NTSTATUS STDCALL I8042InternalDeviceControlMouse(PDEVICE_OBJECT DeviceObject, PIRP Irp)
669 {
670 PIO_STACK_LOCATION Stk;
671 PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
672 PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
673
674 DPRINT("InternalDeviceControl\n");
675
676 Irp->IoStatus.Information = 0;
677 Stk = IoGetCurrentIrpStackLocation(Irp);
678
679 switch (Stk->Parameters.DeviceIoControl.IoControlCode) {
680
681 case IOCTL_INTERNAL_MOUSE_CONNECT:
682 DPRINT("IOCTL_INTERNAL_MOUSE_CONNECT\n");
683 if (Stk->Parameters.DeviceIoControl.InputBufferLength <
684 sizeof(CONNECT_DATA)) {
685 DPRINT1("Mouse IOCTL_INTERNAL_MOUSE_CONNECT "
686 "invalid buffer size\n");
687 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
688 goto intcontfailure;
689 }
690
691 if (!DevExt->MouseExists) {
692 Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
693 goto intcontfailure;
694 }
695
696 if (DevExt->MouseClaimed) {
697 DPRINT1("IOCTL_INTERNAL_MOUSE_CONNECT: "
698 "Mouse is already claimed\n");
699 Irp->IoStatus.Status = STATUS_SHARING_VIOLATION;
700 goto intcontfailure;
701 }
702
703 memcpy(&DevExt->MouseData,
704 Stk->Parameters.DeviceIoControl.Type3InputBuffer,
705 sizeof(CONNECT_DATA));
706 DevExt->MouseHook.IsrWritePort = I8042IsrWritePortMouse;
707 DevExt->MouseHook.QueueMousePacket =
708 I8042QueueMousePacket;
709 DevExt->MouseHook.CallContext = DevExt;
710
711 {
712 PIO_WORKITEM WorkItem;
713 PI8042_HOOK_WORKITEM WorkItemData;
714
715 WorkItem = IoAllocateWorkItem(DeviceObject);
716 if (!WorkItem) {
717 DPRINT ("IOCTL_INTERNAL_MOUSE_CONNECT: "
718 "Can't allocate work item\n");
719 Irp->IoStatus.Status =
720 STATUS_INSUFFICIENT_RESOURCES;
721 goto intcontfailure;
722 }
723
724 WorkItemData = ExAllocatePoolWithTag(
725 NonPagedPool,
726 sizeof(I8042_HOOK_WORKITEM),
727 TAG_I8042);
728 if (!WorkItemData) {
729 DPRINT ("IOCTL_INTERNAL_MOUSE_CONNECT: "
730 "Can't allocate work item data\n");
731 Irp->IoStatus.Status =
732 STATUS_INSUFFICIENT_RESOURCES;
733 IoFreeWorkItem(WorkItem);
734 goto intcontfailure;
735 }
736 WorkItemData->WorkItem = WorkItem;
737 WorkItemData->Target =
738 DevExt->MouseData.ClassDeviceObject;
739 WorkItemData->Irp = Irp;
740
741 IoMarkIrpPending(Irp);
742 DevExt->MouseState = MouseResetting;
743 DevExt->MouseResetState = 1100;
744 IoQueueWorkItem(WorkItem,
745 I8042SendHookWorkItem,
746 DelayedWorkQueue,
747 WorkItemData);
748
749 Irp->IoStatus.Status = STATUS_PENDING;
750 }
751
752 break;
753 case IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER:
754 DPRINT("IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER\n");
755 if (Stk->Parameters.DeviceIoControl.InputBufferLength < 1) {
756 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
757 goto intcontfailure;
758 }
759 if (!DevExt->MouseInterruptObject) {
760 Irp->IoStatus.Status = STATUS_DEVICE_NOT_READY;
761 goto intcontfailure;
762 }
763
764 IoMarkIrpPending(Irp);
765 IoStartPacket(DeviceObject, Irp, NULL, NULL);
766 Irp->IoStatus.Status = STATUS_PENDING;
767
768 break;
769 case IOCTL_MOUSE_QUERY_ATTRIBUTES:
770 DPRINT("IOCTL_MOUSE_QUERY_ATTRIBUTES\n");
771 if (Stk->Parameters.DeviceIoControl.InputBufferLength <
772 sizeof(MOUSE_ATTRIBUTES)) {
773 DPRINT("Mouse IOCTL_MOUSE_QUERY_ATTRIBUTES "
774 "invalid buffer size\n");
775 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
776 goto intcontfailure;
777 }
778 memcpy(Irp->AssociatedIrp.SystemBuffer,
779 &DevExt->MouseAttributes,
780 sizeof(MOUSE_ATTRIBUTES));
781
782 Irp->IoStatus.Status = STATUS_SUCCESS;
783 break;
784 default:
785 Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
786 break;
787 }
788
789 intcontfailure:
790 return Irp->IoStatus.Status;
791 }
792
793 BOOLEAN STDCALL I8042MouseEnable(PDEVICE_EXTENSION DevExt)
794 {
795 UCHAR Value;
796 NTSTATUS Status;
797
798 DPRINT("Enabling mouse\n");
799
800 if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
801 DPRINT1("Can't read i8042 mode\n");
802 return FALSE;
803 }
804
805 Status = I8042ReadDataWait(DevExt, &Value);
806 if (Status != STATUS_SUCCESS) {
807 DPRINT1("No response after read i8042 mode\n");
808 return FALSE;
809 }
810
811 Value &= ~(0x20); // don't disable mouse
812 Value |= 0x02; // enable mouse interrupts
813
814 if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
815 DPRINT1("Can't set i8042 mode\n");
816 return FALSE;
817 }
818
819 if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
820 DPRINT1("Can't send i8042 mode\n");
821 return FALSE;
822 }
823
824 return TRUE;
825 }
826
827 BOOLEAN STDCALL I8042MouseDisable(PDEVICE_EXTENSION DevExt)
828 {
829 UCHAR Value;
830 NTSTATUS Status;
831
832 DPRINT("Disabling mouse\n");
833
834 I8042Flush(); /* Just to be (kind of) sure */
835
836 if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
837 DPRINT1("Can't read i8042 mode\n");
838 return FALSE;
839 }
840
841 Status = I8042ReadDataWait(DevExt, &Value);
842 if (Status != STATUS_SUCCESS) {
843 DPRINT1("No response after read i8042 mode\n");
844 return FALSE;
845 }
846
847 Value |= 0x20; // don't disable mouse
848 Value &= ~(0x02); // enable mouse interrupts
849
850 if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
851 DPRINT1("Can't set i8042 mode\n");
852 return FALSE;
853 }
854
855 if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
856 DPRINT1("Can't send i8042 mode\n");
857 return FALSE;
858 }
859
860 I8042Flush();
861 /* Just to be (kind of) sure; if the mouse would
862 * say something while we are disabling it, these bytes would
863 * block the keyboard.
864 */
865
866 return TRUE;
867 }