2 #include <ddk/ntddmou.h>
3 #include "controller.h"
7 // Have we got a PS/2 mouse port?
8 static BOOLEAN has_mouse = FALSE;
10 // This buffer holds the mouse scan codes. The PS/2 protocol sends three characters for each event.
11 static unsigned mouse_buffer[3];
12 static int mouse_buffer_position = 0;
14 // The number of mouse replies expected
15 static int mouse_replies_expected = 0;
17 // Previous button state
18 static ULONG PreviousButtons = 0;
20 // Handle a mouse event
23 ps2_mouse_handler(PKINTERRUPT Interrupt, PVOID ServiceContext)
25 PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)ServiceContext;
26 PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
27 PMOUSE_INPUT_DATA Input;
28 ULONG Queue, ButtonsDiff;
29 int state_dx, state_dy;
31 unsigned status = controller_read_status();
32 scancode = controller_read_input();
34 /* Don't handle the mouse event if we aren't connected to the mouse class driver */
35 if (DeviceExtension->ClassInformation.CallBack == NULL)
40 if ((status & CONTROLLER_STATUS_MOUSE_OUTPUT_BUFFER_FULL) != 0)
42 // mouse_handle_event(scancode); proceed to handle it
46 return FALSE; // keyboard_handle_event(scancode);
49 if (mouse_replies_expected > 0)
51 if (scancode == MOUSE_ACK)
53 mouse_replies_expected--;
57 mouse_replies_expected = 0;
60 /* Add this scancode to the mouse event queue. */
61 mouse_buffer[mouse_buffer_position] = scancode;
62 mouse_buffer_position++;
64 /* If the buffer is full, parse this event */
65 if (mouse_buffer_position == 3)
67 /* Set local variables for DeviceObject and DeviceExtension */
68 DeviceObject = (PDEVICE_OBJECT)ServiceContext;
69 DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
70 Queue = DeviceExtension->ActiveQueue % 2;
72 /* Prevent buffer overflow */
73 if (DeviceExtension->InputDataCount[Queue] == MOUSE_BUFFER_SIZE)
78 Input = &DeviceExtension->MouseInputData[Queue]
79 [DeviceExtension->InputDataCount[Queue]];
81 mouse_buffer_position = 0;
83 /* Determine the current state of the buttons */
84 Input->RawButtons = (mouse_buffer[0] & 1) /* * GPM_B_LEFT */ +
85 (mouse_buffer[0] & 2) /* * GPM_B_RIGHT */ +
86 (mouse_buffer[0] & 4) /* * GPM_B_MIDDLE */;
88 /* Determine ButtonFlags */
89 Input->ButtonFlags = 0;
90 ButtonsDiff = PreviousButtons ^ Input->RawButtons;
92 if (ButtonsDiff & GPM_B_LEFT)
94 if (Input->RawButtons & GPM_B_LEFT)
96 Input->ButtonFlags |= MOUSE_BUTTON_1_DOWN;
98 Input->ButtonFlags |= MOUSE_BUTTON_1_UP;
102 if (ButtonsDiff & GPM_B_RIGHT)
104 if (Input->RawButtons & GPM_B_RIGHT)
106 Input->ButtonFlags |= MOUSE_BUTTON_2_DOWN;
108 Input->ButtonFlags |= MOUSE_BUTTON_2_UP;
112 if (ButtonsDiff & GPM_B_MIDDLE)
114 if (Input->RawButtons & GPM_B_MIDDLE)
116 Input->ButtonFlags |= MOUSE_BUTTON_3_DOWN;
118 Input->ButtonFlags |= MOUSE_BUTTON_3_UP;
122 /* Some PS/2 mice send reports with negative bit set in data[0] and zero for
123 * movement. I think this is a bug in the mouse, but working around it only
124 * causes artifacts when the actual report is -256; they'll be treated as zero.
125 * This should be rare if the mouse sampling rate is set to a reasonable value;
126 * the default of 100 Hz is plenty. (Stephen Tell) */
128 /* Determine LastX */
129 if (mouse_buffer[1] == 0)
135 Input->LastX = (mouse_buffer[0] & 0x10) ? mouse_buffer[1] - 256
139 /* Determine LastY */
140 if (mouse_buffer[2] == 0)
146 Input->LastY = -((mouse_buffer[0] & 0x20) ? mouse_buffer[2] - 256
150 /* Send the Input data to the Mouse Class driver */
151 DeviceExtension->InputDataCount[Queue]++;
152 KeInsertQueueDpc(&DeviceExtension->IsrDpc, DeviceObject->CurrentIrp, NULL);
154 /* Copy RawButtons to Previous Buttons for Input */
155 PreviousButtons = Input->RawButtons;
161 /* Write a PS/2 mouse command. */
163 static void mouse_write_command (int command)
166 controller_write_command (CONTROLLER_COMMAND_WRITE_MODE);
168 controller_write_output (command);
171 /* Send a byte to the PS/2 mouse & handle returned ACK. */
173 static void mouse_write_ack (int value)
176 controller_write_command (CONTROLLER_COMMAND_WRITE_MOUSE);
178 controller_write_output (value);
180 /* We expect an ACK in response. */
182 mouse_replies_expected++;
186 /* Check if this is a dual port controller. */
188 BOOLEAN detect_ps2_port(void)
191 BOOLEAN return_value = FALSE;
192 LARGE_INTEGER Millisecond_Timeout;
194 Millisecond_Timeout.QuadPart = 1;
196 return TRUE; // The rest of this code fails under BOCHs
198 /* Put the value 0x5A in the output buffer using the "WriteAuxiliary Device Output Buffer" command (0xD3).
199 Poll the Status Register for a while to see if the value really turns up in the Data Register. If the
200 KEYBOARD_STATUS_MOUSE_OBF bit is also set to 1 in the Status Register, we assume this controller has an
201 Auxiliary Port (a.k.a. Mouse Port). */
204 controller_write_command (CONTROLLER_COMMAND_WRITE_MOUSE_OUTPUT_BUFFER);
207 // 0x5A is a random dummy value
208 controller_write_output (0x5A);
210 for (loops = 0; loops < 10; loops++)
212 unsigned char status = controller_read_status();
214 if((status & CONTROLLER_STATUS_OUTPUT_BUFFER_FULL) != 0)
216 controller_read_input();
217 if ((status & CONTROLLER_STATUS_MOUSE_OUTPUT_BUFFER_FULL) != 0)
224 KeDelayExecutionThread (KernelMode, FALSE, &Millisecond_Timeout);
230 // Initialize the PS/2 mouse support
232 BOOLEAN mouse_init (PDEVICE_OBJECT DeviceObject)
234 PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
239 if (!detect_ps2_port ()) return FALSE;
243 DeviceExtension->InputDataCount[0] = 0;
244 DeviceExtension->InputDataCount[1] = 0;
245 DeviceExtension->ActiveQueue = 0;
247 // Enable the PS/2 mouse port
248 controller_write_command_word (CONTROLLER_COMMAND_MOUSE_ENABLE);
251 mouse_write_ack (MOUSE_SET_SAMPLE_RATE);
252 mouse_write_ack (200);
255 mouse_write_ack (MOUSE_SET_RESOLUTION);
259 mouse_write_ack (MOUSE_SET_SCALE21);
261 // Enable the PS/2 device
262 mouse_write_ack (MOUSE_ENABLE_DEVICE);
264 // Enable controller interrupts
265 mouse_write_command (MOUSE_INTERRUPTS_ON);
267 // Connect the interrupt for the mouse irq
268 MappedIrq = HalGetInterruptVector(Internal, 0, 0, MOUSE_IRQ, &Dirql, &Affinity);
270 IoConnectInterrupt(&DeviceExtension->MouseInterrupt, ps2_mouse_handler, DeviceObject, NULL, MappedIrq,
271 Dirql, Dirql, 0, FALSE, Affinity, FALSE);