3 ** Written by Thomas Weidenmueller (w3seek@users.sourceforge.net)
4 ** For ReactOS (www.reactos.com)
5 ** Adapted from the linux 2.6 mouse driver
9 #include <ddk/ntddmou.h>
10 #include "controller.h"
16 int InitSynaptics(PDEVICE_EXTENSION DeviceExtension
);
17 void PS2PPProcessPacket(PDEVICE_EXTENSION DeviceExtension
, PMOUSE_INPUT_DATA Input
, int *wheel
);
18 void PS2PPSet800dpi(PDEVICE_EXTENSION DeviceExtension
);
19 int PS2PPDetectModel(PDEVICE_EXTENSION DeviceExtension
, unsigned char *param
);
21 // Parse incoming packets
23 ParsePackets(PDEVICE_EXTENSION DeviceExtension
, PMOUSE_INPUT_DATA Input
)
27 unsigned char *packet
= DeviceExtension
->MouseBuffer
;
29 /* Determine the current state of the buttons */
30 Input
->RawButtons
= ((packet
[0] & 1) ? GPM_B_LEFT
: 0);
31 Input
->RawButtons
|= (((packet
[0] >> 1) & 1) ? GPM_B_RIGHT
: 0);
32 Input
->RawButtons
|= (((packet
[0] >> 2) & 1) ? GPM_B_MIDDLE
: 0);
35 * The PS2++ protocol is a little bit complex
38 if(DeviceExtension
->MouseType
== PSMOUSE_PS2PP
|| DeviceExtension
->MouseType
== PSMOUSE_PS2TPP
)
39 PS2PPProcessPacket(DeviceExtension
, Input
, &wheel
);
42 * Scroll wheel on IntelliMice, scroll buttons on NetMice
45 if(DeviceExtension
->MouseType
== PSMOUSE_IMPS
|| DeviceExtension
->MouseType
== PSMOUSE_GENPS
)
47 wheel
= (int)(-(signed char) packet
[3]);
51 * Scroll wheel and buttons on IntelliMouse Explorer
54 if(DeviceExtension
->MouseType
== PSMOUSE_IMEX
)
56 wheel
= (int)(packet
[3] & 8) - (int)(packet
[3] & 7);
57 Input
->RawButtons
|= (((packet
[3] >> 4) & 1) ? GPM_B_FOURTH
: 0);
58 Input
->RawButtons
|= (((packet
[3] >> 5) & 1) ? GPM_B_FIFTH
: 0);
62 * Extra buttons on Genius NewNet 3D
65 if(DeviceExtension
->MouseType
== PSMOUSE_GENPS
)
67 Input
->RawButtons
|= (((packet
[0] >> 6) & 1) ? GPM_B_FOURTH
: 0);
68 Input
->RawButtons
|= (((packet
[0] >> 7) & 1) ? GPM_B_FIFTH
: 0);
72 * Determine ButtonFlags
75 Input
->ButtonFlags
= 0;
76 ButtonsDiff
= DeviceExtension
->PreviousButtons
^ Input
->RawButtons
;
82 if(ButtonsDiff
& GPM_B_LEFT
)
83 Input
->ButtonFlags
|= ((Input
->RawButtons
& GPM_B_LEFT
) ? MOUSE_BUTTON_1_DOWN
: MOUSE_BUTTON_1_UP
);
85 if(ButtonsDiff
& GPM_B_RIGHT
)
86 Input
->ButtonFlags
|= ((Input
->RawButtons
& GPM_B_RIGHT
) ? MOUSE_BUTTON_2_DOWN
: MOUSE_BUTTON_2_UP
);
88 if(ButtonsDiff
& GPM_B_MIDDLE
)
89 Input
->ButtonFlags
|= ((Input
->RawButtons
& GPM_B_MIDDLE
) ? MOUSE_BUTTON_3_DOWN
: MOUSE_BUTTON_3_UP
);
91 if(ButtonsDiff
& GPM_B_FOURTH
)
92 Input
->ButtonFlags
|= ((Input
->RawButtons
& GPM_B_FOURTH
) ? MOUSE_BUTTON_4_DOWN
: MOUSE_BUTTON_4_UP
);
94 if(ButtonsDiff
& GPM_B_FIFTH
)
95 Input
->ButtonFlags
|= ((Input
->RawButtons
& GPM_B_FIFTH
) ? MOUSE_BUTTON_5_DOWN
: MOUSE_BUTTON_5_UP
);
97 /* Some PS/2 mice send reports with negative bit set in data[0] and zero for
98 * movement. I think this is a bug in the mouse, but working around it only
99 * causes artifacts when the actual report is -256; they'll be treated as zero.
100 * This should be rare if the mouse sampling rate is set to a reasonable value;
101 * the default of 100 Hz is plenty. (Stephen Tell) */
103 /* Determine LastX */
105 Input
->LastX
= ((packet
[0] & 0x10) ? (int)(packet
[1] - 256) : (int) packet
[1]);
109 /* Determine LastY */
111 Input
->LastY
= -((packet
[0] & 0x20) ? (int)(packet
[2] - 256) : (int) packet
[2]);
115 /* Copy RawButtons to Previous Buttons for Input */
116 DeviceExtension
->PreviousButtons
= Input
->RawButtons
;
118 if((wheel
!= 0) && (wheel
>= -8) && (wheel
<= 7))
120 Input
->ButtonFlags
|= MOUSE_WHEEL
;
121 Input
->ButtonData
= (UINT
)(wheel
* WHEEL_DELTA
);
127 // Handle a mouse event
129 MouseHandler(PKINTERRUPT Interrupt
, PVOID ServiceContext
)
131 PDEVICE_OBJECT DeviceObject
= (PDEVICE_OBJECT
)ServiceContext
;
132 PDEVICE_EXTENSION DeviceExtension
= DeviceObject
->DeviceExtension
;
133 PMOUSE_INPUT_DATA Input
;
137 unsigned status
= controller_read_status();
138 scancode
= controller_read_input();
140 /* Don't handle the mouse event if we aren't connected to the mouse class driver */
141 if (DeviceExtension
->ClassInformation
.CallBack
== NULL
)
146 if ((status
& CONTROLLER_STATUS_MOUSE_OUTPUT_BUFFER_FULL
) != 0)
148 // mouse_handle_event(scancode); proceed to handle it
152 return FALSE
; // keyboard_handle_event(scancode);
155 /* Add this scancode to the mouse event queue. */
156 DeviceExtension
->MouseBuffer
[DeviceExtension
->MouseBufferPosition
] = scancode
;
157 DeviceExtension
->MouseBufferPosition
++;
159 /* If the buffer is full, parse this event */
160 if (DeviceExtension
->MouseBufferPosition
== DeviceExtension
->MouseBufferSize
)
162 Queue
= DeviceExtension
->ActiveQueue
% 2;
164 /* Reset the buffer state. */
165 DeviceExtension
->MouseBufferPosition
= 0;
167 /* Prevent buffer overflow */
168 if (DeviceExtension
->InputDataCount
[Queue
] == MOUSE_BUFFER_SIZE
)
173 Input
= &DeviceExtension
->MouseInputData
[Queue
]
174 [DeviceExtension
->InputDataCount
[Queue
]];
176 ret
= ParsePackets(DeviceExtension
, Input
);
178 /* Send the Input data to the Mouse Class driver */
179 DeviceExtension
->InputDataCount
[Queue
]++;
180 KeInsertQueueDpc(&DeviceExtension
->IsrDpc
, DeviceObject
->CurrentIrp
, NULL
);
188 // Write a PS/2 mouse command.
189 static void mouse_write_command(int command
)
192 controller_write_command(CONTROLLER_COMMAND_WRITE_MODE
);
194 controller_write_output(command
);
198 // Sends a byte to the mouse
199 static int SendByte(PDEVICE_EXTENSION DeviceExtension
, unsigned char byte
)
201 int timeout
= 100; /* 100 msec */
203 unsigned char status
;
204 LARGE_INTEGER Millisecond_Timeout
;
206 Millisecond_Timeout
.QuadPart
= 1;
208 DeviceExtension
->ack
= 0;
209 DeviceExtension
->acking
= 1;
212 controller_write_command(CONTROLLER_COMMAND_WRITE_MOUSE
);
214 controller_write_output(byte
);
215 while ((DeviceExtension
->ack
== 0) && timeout
--)
217 status
= controller_read_status();
218 if((status
& CONTROLLER_STATUS_OUTPUT_BUFFER_FULL
) != 0)
220 scancode
= controller_read_input();
225 case PSMOUSE_RET_ACK
:
226 DeviceExtension
->ack
= 1;
228 case PSMOUSE_RET_NAK
:
229 DeviceExtension
->ack
= -1;
232 DeviceExtension
->ack
= 1; /* Workaround for mice which don't ACK the Get ID command */
233 if (DeviceExtension
->RepliesExpected
)
234 DeviceExtension
->pkt
[--DeviceExtension
->RepliesExpected
] = scancode
;
237 return (int)(-(DeviceExtension
->ack
<= 0));
240 KeDelayExecutionThread (KernelMode
, FALSE
, &Millisecond_Timeout
);
242 return (int)(-(DeviceExtension
->ack
<= 0));
246 // Send a PS/2 command to the mouse.
247 int SendCommand(PDEVICE_EXTENSION DeviceExtension
, unsigned char *param
, int command
)
249 LARGE_INTEGER Millisecond_Timeout
;
250 unsigned char status
;
252 int timeout
= 500; /* 500 msec */
253 int send
= (command
>> 12) & 0xf;
254 int receive
= (command
>> 8) & 0xf;
257 Millisecond_Timeout
.QuadPart
= 1;
259 DeviceExtension
->RepliesExpected
= receive
;
260 if (command
== PSMOUSE_CMD_RESET_BAT
)
261 timeout
= 2000; /* 2 sec */
264 if (SendByte(DeviceExtension
, command
& 0xff))
265 return (int)(DeviceExtension
->RepliesExpected
= 0) - 1;
267 for (i
= 0; i
< send
; i
++)
268 if (SendByte(DeviceExtension
, param
[i
]))
269 return (int)(DeviceExtension
->RepliesExpected
= 0) - 1;
271 while (DeviceExtension
->RepliesExpected
&& timeout
--)
273 if (DeviceExtension
->RepliesExpected
== 1 && command
== PSMOUSE_CMD_RESET_BAT
)
276 if (DeviceExtension
->RepliesExpected
== 1 && command
== PSMOUSE_CMD_GETID
&&
277 DeviceExtension
->pkt
[1] != 0xab && DeviceExtension
->pkt
[1] != 0xac)
279 DeviceExtension
->RepliesExpected
= 0;
283 status
= controller_read_status();
284 if((status
& CONTROLLER_STATUS_OUTPUT_BUFFER_FULL
) != 0)
286 scancode
= controller_read_input();
289 DeviceExtension
->pkt
[--DeviceExtension
->RepliesExpected
] = scancode
;
293 KeDelayExecutionThread (KernelMode
, FALSE
, &Millisecond_Timeout
);
296 for (i
= 0; i
< receive
; i
++)
297 param
[i
] = DeviceExtension
->pkt
[(receive
- 1) - i
];
299 if (DeviceExtension
->RepliesExpected
)
300 return (int)(DeviceExtension
->RepliesExpected
= 0) - 1;
306 // changes the resolution of the mouse
307 void SetResolution(PDEVICE_EXTENSION DeviceExtension
)
309 unsigned char param
[1];
311 if(DeviceExtension
->MouseType
== PSMOUSE_PS2PP
&& DeviceExtension
->Resolution
> 400)
313 PS2PPSet800dpi(DeviceExtension
);
317 if(!DeviceExtension
->Resolution
|| DeviceExtension
->Resolution
>= 200)
319 else if(DeviceExtension
->Resolution
>= 100)
321 else if(DeviceExtension
->Resolution
>= 50)
323 else if(DeviceExtension
->Resolution
)
326 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_SETRES
);
330 // Detect mouse models
331 int TestMouseExtensions(PDEVICE_EXTENSION DeviceExtension
)
333 unsigned char param
[4];
340 DeviceExtension
->MouseModel
= 0;
343 * Try Synaptics TouchPad magic ID
346 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_SETRES
);
347 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_SETRES
);
348 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_SETRES
);
349 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_SETRES
);
350 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_GETINFO
);
353 // vendor = Synaptics
355 if(!InitSynaptics(DeviceExtension
))
356 return PSMOUSE_SYNAPTICS
;
362 * Try Genius NetMouse magic init.
365 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_SETRES
);
366 SendCommand(DeviceExtension
, NULL
, PSMOUSE_CMD_SETSCALE11
);
367 SendCommand(DeviceExtension
, NULL
, PSMOUSE_CMD_SETSCALE11
);
368 SendCommand(DeviceExtension
, NULL
, PSMOUSE_CMD_SETSCALE11
);
369 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_GETINFO
);
370 if(param
[0] == 0x00 && param
[1] == 0x33 && param
[2] == 0x55)
373 // name = Wheel Mouse
374 // Features = 4th, 5th, Wheel
375 return PSMOUSE_GENPS
;
379 * Try Logitech magic ID.
382 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_SETRES
);
383 SendCommand(DeviceExtension
, NULL
, PSMOUSE_CMD_SETSCALE11
);
384 SendCommand(DeviceExtension
, NULL
, PSMOUSE_CMD_SETSCALE11
);
385 SendCommand(DeviceExtension
, NULL
, PSMOUSE_CMD_SETSCALE11
);
387 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_GETINFO
);
390 type
= PS2PPDetectModel(DeviceExtension
, param
);
396 * Try IntelliMouse magic init.
399 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_SETRATE
);
401 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_SETRATE
);
403 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_SETRATE
);
404 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_GETID
);
410 * Try IntelliMouse/Explorer magic init.
413 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_SETRATE
);
415 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_SETRATE
);
417 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_SETRATE
);
418 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_GETID
);
421 // name = Explorer Mouse
422 // Features = 4th, 5th, Wheel
426 // name = Wheel Mouse
431 * Okay, all failed, we have a standard mouse here. The number of the buttons
432 * is still a question, though. We assume 3.
438 // Detect if mouse is just a standard ps/2 mouse
439 int TestMouse(PDEVICE_EXTENSION DeviceExtension
)
441 unsigned char param
[4];
443 param
[0] = param
[1] = 0xa5;
446 * First, we check if it's a mouse. It should send 0x00 or 0x03
447 * in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer.
449 if(SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_GETID
))
452 if(param
[0] != 0x00 && param
[0] != 0x03 && param
[0] != 0x04)
456 * Then we reset and disable the mouse so that it doesn't generate events.
459 if(DeviceExtension
->NoExtensions
)
461 return DeviceExtension
->MouseType
= PSMOUSE_PS2
;
464 if(SendCommand(DeviceExtension
, NULL
, PSMOUSE_CMD_RESET_DIS
))
467 return DeviceExtension
->MouseType
= TestMouseExtensions(DeviceExtension
);
471 // Initialize and enable the mouse
472 void InitializeMouse(PDEVICE_EXTENSION DeviceExtension
)
474 unsigned char param
[4];
477 * We set the mouse report rate to a highest possible value.
478 * We try 100 first in case mouse fails to set 200.
482 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_SETRATE
);
485 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_SETRATE
);
487 SetResolution(DeviceExtension
);
488 SendCommand(DeviceExtension
, NULL
, PSMOUSE_CMD_SETSCALE11
);
491 * We set the mouse into streaming mode.
494 SendCommand(DeviceExtension
, param
, PSMOUSE_CMD_SETSTREAM
);
497 * Last, we enable the mouse so that we get reports from it.
500 if(SendCommand(DeviceExtension
, NULL
, PSMOUSE_CMD_ENABLE
))
501 DbgPrint("mouse.c: Failed to enable mouse!\n");
505 // Load settings from registry (by Filip Navara)
506 BOOL
LoadMouseSettings(PDEVICE_EXTENSION DeviceExtension
, PUNICODE_STRING RegistryPath
)
509 * Get the parameters from registry
511 ULONG DefaultMouseResolution
= 0;
512 ULONG DisableExtensionDetection
= 1;
513 UNICODE_STRING ParametersPath
;
514 RTL_QUERY_REGISTRY_TABLE Parameters
[3];
515 PWSTR ParametersKeyPath
= L
"\\Parameters";
518 RtlZeroMemory(Parameters
, sizeof(Parameters
));
521 * Formulate path to registry
523 RtlInitUnicodeString(&ParametersPath
, NULL
);
524 ParametersPath
.MaximumLength
= RegistryPath
->Length
+
525 (wcslen(ParametersKeyPath
) * sizeof(WCHAR
)) + sizeof(UNICODE_NULL
);
526 ParametersPath
.Buffer
= ExAllocatePool(PagedPool
,
527 ParametersPath
.MaximumLength
);
528 if (!ParametersPath
.Buffer
)
530 DPRINT("Can't allocate buffer for parameters\n");
533 RtlZeroMemory(ParametersPath
.Buffer
, ParametersPath
.MaximumLength
);
534 RtlAppendUnicodeToString(&ParametersPath
, RegistryPath
->Buffer
);
535 RtlAppendUnicodeToString(&ParametersPath
, ParametersKeyPath
);
536 DPRINT("Parameters Path: %wZ\n", &ParametersPath
);
538 Parameters
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
539 Parameters
[0].Name
= L
"Resolution";
540 Parameters
[0].EntryContext
= &DeviceExtension
->Resolution
;
541 Parameters
[0].DefaultType
= REG_DWORD
;
542 Parameters
[0].DefaultData
= &DefaultMouseResolution
;
543 Parameters
[0].DefaultLength
= sizeof(ULONG
);
545 Parameters
[1].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
546 Parameters
[1].Name
= L
"DisableExtensionDetection";
547 Parameters
[1].EntryContext
= &DeviceExtension
->NoExtensions
;
548 Parameters
[1].DefaultType
= REG_DWORD
;
549 Parameters
[1].DefaultData
= &DisableExtensionDetection
;
550 Parameters
[1].DefaultLength
= sizeof(ULONG
);
552 Status
= RtlQueryRegistryValues(
553 RTL_REGISTRY_ABSOLUTE
| RTL_REGISTRY_OPTIONAL
,
554 ParametersPath
.Buffer
,
559 if (!NT_SUCCESS(Status
))
561 DPRINT("RtlQueryRegistryValues failed (0x%x)\n", Status
);
568 // Initialize the PS/2 mouse support
569 BOOLEAN
SetupMouse(PDEVICE_OBJECT DeviceObject
, PUNICODE_STRING RegistryPath
)
571 PDEVICE_EXTENSION DeviceExtension
= DeviceObject
->DeviceExtension
;
575 LARGE_INTEGER Millisecond_Timeout
;
577 Millisecond_Timeout
.QuadPart
= 1;
580 DeviceExtension
->NoExtensions
= 0;
581 DeviceExtension
->InputDataCount
[0] = 0;
582 DeviceExtension
->InputDataCount
[1] = 0;
583 DeviceExtension
->ActiveQueue
= 0;
584 DeviceExtension
->MouseBufferPosition
= 0;
585 DeviceExtension
->MouseBufferSize
= 3;
586 DeviceExtension
->Resolution
= 0;
587 DeviceExtension
->RepliesExpected
= 0;
588 DeviceExtension
->PreviousButtons
= 0;
589 DeviceExtension
->SmartScroll
= 1;
590 DeviceExtension
->ack
= 0;
591 DeviceExtension
->acking
= 0;
593 LoadMouseSettings(DeviceExtension
, RegistryPath
);
595 // Enable the PS/2 mouse port
596 controller_write_command_word (CONTROLLER_COMMAND_MOUSE_ENABLE
);
598 //InitializeMouse(DeviceExtension);
599 DeviceExtension
->MouseType
= TestMouse(DeviceExtension
);
601 if(DeviceExtension
->MouseType
> 0)
603 /* set the incoming buffer to 4 if needed */
604 DeviceExtension
->MouseBufferSize
+= (DeviceExtension
->MouseType
>= PSMOUSE_GENPS
);
606 DPRINT("Detected Mouse: 0x%x\n", DeviceExtension
->MouseType
);
608 InitializeMouse(DeviceExtension
);
610 // Enable controller interrupts
611 mouse_write_command (MOUSE_INTERRUPTS_ON
);
613 // Connect the interrupt for the mouse irq
614 MappedIrq
= HalGetInterruptVector(Internal
, 0, 0, MOUSE_IRQ
, &Dirql
, &Affinity
);
616 IoConnectInterrupt(&DeviceExtension
->MouseInterrupt
, MouseHandler
, DeviceObject
, NULL
, MappedIrq
,
617 Dirql
, Dirql
, 0, FALSE
, Affinity
, FALSE
);
621 /* FIXME - this fixes the crash if no mouse was detected */
623 // Connect the interrupt for the mouse irq
624 MappedIrq
= HalGetInterruptVector(Internal
, 0, 0, MOUSE_IRQ
, &Dirql
, &Affinity
);
626 IoConnectInterrupt(&DeviceExtension
->MouseInterrupt
, MouseHandler
, DeviceObject
, NULL
, MappedIrq
,
627 Dirql
, Dirql
, 0, FALSE
, Affinity
, FALSE
);
634 // Check if this is a dual port controller.
635 BOOLEAN
DetectPS2Port(void)
639 BOOLEAN return_value
= FALSE
;
640 LARGE_INTEGER Millisecond_Timeout
;
642 Millisecond_Timeout
.QuadPart
= 1;
644 //return TRUE; // The rest of this code fails under BOCHs
646 /* Put the value 0x5A in the output buffer using the "WriteAuxiliary Device Output Buffer" command (0xD3).
647 Poll the Status Register for a while to see if the value really turns up in the Data Register. If the
648 KEYBOARD_STATUS_MOUSE_OBF bit is also set to 1 in the Status Register, we assume this controller has an
649 Auxiliary Port (a.k.a. Mouse Port). */
652 controller_write_command(CONTROLLER_COMMAND_WRITE_MOUSE_OUTPUT_BUFFER
);
655 /* 0x5A is a random dummy value */
656 controller_write_output(0x5A);
658 for (loops
= 0; loops
< 10; loops
++)
660 unsigned char status
= controller_read_status();
662 if((status
& CONTROLLER_STATUS_OUTPUT_BUFFER_FULL
) != 0)
664 scancode
= controller_read_input();
665 if((status
& CONTROLLER_STATUS_MOUSE_OUTPUT_BUFFER_FULL
) != 0)
672 KeDelayExecutionThread(KernelMode
, FALSE
, &Millisecond_Timeout
);