branch update for HEAD-2003050101
[reactos.git] / drivers / input / psaux / mouse.c
1 #include <ddk/ntddk.h>
2 #include <ddk/ntddmou.h>
3 #include "controller.h"
4 #include "mouse.h"
5 #include "psaux.h"
6
7 // Have we got a PS/2 mouse port?
8 static BOOLEAN has_mouse = FALSE;
9
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;
13
14 // The number of mouse replies expected
15 static int mouse_replies_expected = 0;
16
17 // Previous button state
18 static ULONG PreviousButtons = 0;
19
20 // Handle a mouse event
21
22 BOOLEAN STDCALL
23 ps2_mouse_handler(PKINTERRUPT Interrupt, PVOID ServiceContext)
24 {
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;
30   unsigned scancode;
31   unsigned status = controller_read_status();
32   scancode = controller_read_input();
33
34   /* Don't handle the mouse event if we aren't connected to the mouse class driver */
35   if (DeviceExtension->ClassInformation.CallBack == NULL)
36   {
37     return FALSE;
38   }
39
40   if ((status & CONTROLLER_STATUS_MOUSE_OUTPUT_BUFFER_FULL) != 0)
41   {
42     // mouse_handle_event(scancode); proceed to handle it
43   }
44   else
45   {
46     return FALSE; // keyboard_handle_event(scancode);
47   }
48
49   if (mouse_replies_expected > 0)
50   {
51     if (scancode == MOUSE_ACK)
52     {
53       mouse_replies_expected--;
54       return;
55     }
56
57     mouse_replies_expected = 0;
58   }
59
60   /* Add this scancode to the mouse event queue. */
61   mouse_buffer[mouse_buffer_position] = scancode;
62   mouse_buffer_position++;
63
64   /* If the buffer is full, parse this event */
65   if (mouse_buffer_position == 3)
66   {
67     /* Set local variables for DeviceObject and DeviceExtension */
68     DeviceObject = (PDEVICE_OBJECT)ServiceContext;
69     DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
70     Queue = DeviceExtension->ActiveQueue % 2;
71
72     /* Prevent buffer overflow */
73     if (DeviceExtension->InputDataCount[Queue] == MOUSE_BUFFER_SIZE)
74     {
75       return TRUE;
76     }
77
78     Input = &DeviceExtension->MouseInputData[Queue]
79             [DeviceExtension->InputDataCount[Queue]];
80
81     mouse_buffer_position = 0;
82
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 */;
87
88     /* Determine ButtonFlags */
89     Input->ButtonFlags = 0;
90     ButtonsDiff = PreviousButtons ^ Input->RawButtons;
91
92     if (ButtonsDiff & GPM_B_LEFT)
93     {
94       if (Input->RawButtons & GPM_B_LEFT)
95       {
96         Input->ButtonFlags |= MOUSE_BUTTON_1_DOWN;
97       } else {
98         Input->ButtonFlags |= MOUSE_BUTTON_1_UP;
99       }
100     }
101
102     if (ButtonsDiff & GPM_B_RIGHT)
103     {
104       if (Input->RawButtons & GPM_B_RIGHT)
105       {
106         Input->ButtonFlags |= MOUSE_BUTTON_2_DOWN;
107       } else {
108         Input->ButtonFlags |= MOUSE_BUTTON_2_UP;
109       }
110     }
111
112     if (ButtonsDiff & GPM_B_MIDDLE)
113     {
114       if (Input->RawButtons & GPM_B_MIDDLE)
115       {
116         Input->ButtonFlags |= MOUSE_BUTTON_3_DOWN;
117       } else {
118         Input->ButtonFlags |= MOUSE_BUTTON_3_UP;
119       }
120     }
121
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) */
127
128     /* Determine LastX */
129     if (mouse_buffer[1] == 0)
130     {
131       Input->LastX = 0;
132     }
133     else
134     {
135       Input->LastX = (mouse_buffer[0] & 0x10) ? mouse_buffer[1] - 256
136                                               : mouse_buffer[1];
137     }
138
139     /* Determine LastY */
140     if (mouse_buffer[2] == 0)
141     {
142       Input->LastY = 0;
143     }
144     else
145     {
146       Input->LastY = -((mouse_buffer[0] & 0x20) ? mouse_buffer[2] - 256
147                                                 : mouse_buffer[2]);
148     }
149
150     /* Send the Input data to the Mouse Class driver */
151     DeviceExtension->InputDataCount[Queue]++;
152     KeInsertQueueDpc(&DeviceExtension->IsrDpc, DeviceObject->CurrentIrp, NULL);
153
154     /* Copy RawButtons to Previous Buttons for Input */
155     PreviousButtons = Input->RawButtons;
156
157     return TRUE;
158   }
159 }
160
161 /* Write a PS/2 mouse command. */
162
163 static void mouse_write_command (int command)
164 {
165   controller_wait();
166   controller_write_command (CONTROLLER_COMMAND_WRITE_MODE);
167   controller_wait();
168   controller_write_output (command);
169 }
170
171 /* Send a byte to the PS/2 mouse & handle returned ACK. */
172
173 static void mouse_write_ack (int value)
174 {
175   controller_wait();
176   controller_write_command (CONTROLLER_COMMAND_WRITE_MOUSE);
177   controller_wait();
178   controller_write_output (value);
179
180   /* We expect an ACK in response. */
181
182   mouse_replies_expected++;
183   controller_wait ();
184 }
185  
186 /* Check if this is a dual port controller. */
187
188 BOOLEAN detect_ps2_port(void)
189 {
190   int loops;
191   BOOLEAN return_value = FALSE;
192   LARGE_INTEGER Millisecond_Timeout;
193
194   Millisecond_Timeout.QuadPart = 1;
195
196   return TRUE; // The rest of this code fails under BOCHs
197
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). */
202
203   controller_wait ();
204   controller_write_command (CONTROLLER_COMMAND_WRITE_MOUSE_OUTPUT_BUFFER);
205   controller_wait ();
206
207   // 0x5A is a random dummy value
208   controller_write_output (0x5A);
209
210   for (loops = 0; loops < 10; loops++)
211   {
212     unsigned char status = controller_read_status();
213
214     if((status & CONTROLLER_STATUS_OUTPUT_BUFFER_FULL) != 0)
215     {
216       controller_read_input();
217       if ((status & CONTROLLER_STATUS_MOUSE_OUTPUT_BUFFER_FULL) != 0)
218       {
219         return_value = TRUE;
220       }
221       break;
222     }
223
224     KeDelayExecutionThread (KernelMode, FALSE, &Millisecond_Timeout);
225   }
226   
227   return return_value;
228 }
229
230 // Initialize the PS/2 mouse support
231
232 BOOLEAN mouse_init (PDEVICE_OBJECT DeviceObject)
233 {
234   PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
235   ULONG MappedIrq;
236   KIRQL Dirql;
237   KAFFINITY Affinity;
238
239   if (!detect_ps2_port ()) return FALSE;
240
241   has_mouse = TRUE;
242
243   DeviceExtension->InputDataCount[0] = 0;
244   DeviceExtension->InputDataCount[1] = 0;
245   DeviceExtension->ActiveQueue = 0;
246
247   // Enable the PS/2 mouse port
248   controller_write_command_word (CONTROLLER_COMMAND_MOUSE_ENABLE);
249
250   // 200 samples/sec
251   mouse_write_ack (MOUSE_SET_SAMPLE_RATE);
252   mouse_write_ack (200);
253
254   // 8 counts per mm
255   mouse_write_ack (MOUSE_SET_RESOLUTION);
256   mouse_write_ack (3);
257
258   // 2:1 scaling
259   mouse_write_ack (MOUSE_SET_SCALE21);
260
261   // Enable the PS/2 device
262   mouse_write_ack (MOUSE_ENABLE_DEVICE);
263
264   // Enable controller interrupts
265   mouse_write_command (MOUSE_INTERRUPTS_ON);
266
267   // Connect the interrupt for the mouse irq
268   MappedIrq = HalGetInterruptVector(Internal, 0, 0, MOUSE_IRQ, &Dirql, &Affinity);
269
270   IoConnectInterrupt(&DeviceExtension->MouseInterrupt, ps2_mouse_handler, DeviceObject, NULL, MappedIrq,
271                      Dirql, Dirql, 0, FALSE, Affinity, FALSE);
272
273   return TRUE;
274 }