6a6fec0029fb23b6ed36e52ad755043ed6f2116c
[reactos.git] / drivers / input / psaux / mouse.c
1 #include <ddk/ntddk.h>
2 #include "../include/mouse.h"
3 #include "controller.h"
4 #include "mouse.h"
5 #include "psaux.h"
6
7 // Have we got a PS/2 mouse port?
8 BOOLEAN has_mouse = FALSE;
9
10 // This buffer holds the mouse scan codes. The PS/2 protocol sends three characters for each event.
11 unsigned mouse_buffer[3];
12 int mouse_buffer_position = 0;
13
14 // The number of mouse replies expected
15 int mouse_replies_expected = 0;
16
17 // Handle a mouse event
18
19 BOOLEAN STDCALL
20 ps2_mouse_handler(PKINTERRUPT Interrupt, PVOID ServiceContext)
21 {
22   PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)ServiceContext;
23   PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
24   int state_dx, state_dy, state_buttons;
25   unsigned scancode;
26   unsigned status = controller_read_status();
27   scancode = controller_read_input();
28
29   /*
30    * Don't handle the mouse event if we aren't connected to the mouse class 
31    * driver
32    */
33   if (DeviceExtension->ClassInformation.CallBack == NULL) 
34     {
35       return FALSE;
36     }
37
38   if ((status & CONTROLLER_STATUS_MOUSE_OUTPUT_BUFFER_FULL) != 0)
39     {
40       // mouse_handle_event(scancode); proceed to handle it
41     }
42   else
43     {
44       return FALSE; // keyboard_handle_event(scancode);
45     }
46
47   if (mouse_replies_expected > 0) 
48     {
49       if (scancode == MOUSE_ACK) 
50         {
51           mouse_replies_expected--;
52           return;
53         }
54       
55       mouse_replies_expected = 0;
56     }
57   
58   /* Add this scancode to the mouse event queue. */
59   mouse_buffer[mouse_buffer_position] = scancode;
60   mouse_buffer_position++;
61   
62   /* If the buffer is full, parse this event */
63   if (mouse_buffer_position == 3)
64     {
65       mouse_buffer_position = 0;
66
67       state_buttons = (mouse_buffer[0] & 1) * GPM_B_LEFT +
68         (mouse_buffer[0] & 2) * GPM_B_RIGHT +
69         (mouse_buffer[0] & 4) * GPM_B_MIDDLE;
70       
71       /* 
72        * Some PS/2 mice send reports with negative bit set in data[0] and zero
73        * for movement.  I think this is a bug in the mouse, but working around
74        * it only causes artifacts when the actual report is -256; they'll
75        * be treated as zero. This should be rare if the mouse sampling rate is
76        * set to a reasonable value; the default of 100 Hz is plenty.  
77        * (Stephen Tell) 
78        */
79       if (mouse_buffer[1] == 0)
80         {
81           state_dx = 0;
82         }
83       else
84         {
85           state_dx = (mouse_buffer[0] & 0x10) ? 
86             mouse_buffer[1] - 256 :
87             mouse_buffer[1];
88         }
89       
90       if (mouse_buffer[2] == 0)
91         {
92           state_dy = 0;
93         }
94       else
95         {
96           state_dy = -((mouse_buffer[0] & 0x20) ?
97                        mouse_buffer[2] - 256 :
98                        mouse_buffer[2]);
99         }
100       
101       if (((state_dx!=0) || (state_dy!=0) || (state_buttons!=0)))
102         {
103           ULONG Queue;
104           PMOUSE_INPUT_DATA Input;
105
106           /* FIXME: Implement button state, see /include/ntddmous.h */
107           
108           DeviceObject = (PDEVICE_OBJECT)ServiceContext;
109           DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
110           Queue = DeviceExtension->ActiveQueue % 2;
111           
112           if (DeviceExtension->InputDataCount[Queue] == MOUSE_BUFFER_SIZE)
113             {
114               return TRUE;
115             }
116
117           Input = &DeviceExtension->MouseInputData[Queue]
118             [DeviceExtension->InputDataCount[Queue]];
119           Input->RawButtons = state_buttons;
120           Input->ButtonData = state_buttons;
121           Input->LastX = state_dx;
122           Input->LastY = state_dy;
123           DeviceExtension->InputDataCount[Queue]++;
124           
125           KeInsertQueueDpc(&DeviceExtension->IsrDpc, DeviceObject->CurrentIrp,
126                            NULL);
127           return TRUE;
128       }
129    }
130 }
131
132 /* Write a PS/2 mouse command. */
133
134 static void mouse_write_command (int command)
135 {
136   controller_wait();
137   controller_write_command (CONTROLLER_COMMAND_WRITE_MODE);
138   controller_wait();
139   controller_write_output (command);
140 }
141
142 /* Send a byte to the PS/2 mouse & handle returned ACK. */
143
144 static void mouse_write_ack (int value)
145 {
146   controller_wait();
147   controller_write_command (CONTROLLER_COMMAND_WRITE_MOUSE);
148   controller_wait();
149   controller_write_output (value);
150
151   /* We expect an ACK in response. */
152
153   mouse_replies_expected++;
154   controller_wait ();
155 }
156  
157 /* Check if this is a dual port controller. */
158
159 BOOLEAN detect_ps2_port(void)
160 {
161   int loops;
162   BOOLEAN return_value = FALSE;
163   LARGE_INTEGER Millisecond_Timeout;
164
165   Millisecond_Timeout.QuadPart = 1;
166
167   return TRUE; // The rest of this code fails under BOCHs
168
169   /* Put the value 0x5A in the output buffer using the "WriteAuxiliary Device Output Buffer" command (0xD3).
170      Poll the Status Register for a while to see if the value really turns up in the Data Register. If the
171      KEYBOARD_STATUS_MOUSE_OBF bit is also set to 1 in the Status Register, we assume this controller has an
172      Auxiliary Port (a.k.a. Mouse Port). */
173
174   controller_wait ();
175   controller_write_command (CONTROLLER_COMMAND_WRITE_MOUSE_OUTPUT_BUFFER);
176   controller_wait ();
177
178   // 0x5A is a random dummy value
179   controller_write_output (0x5A);
180
181   for (loops = 0; loops < 10; loops++)
182   {
183     unsigned char status = controller_read_status();
184
185     if((status & CONTROLLER_STATUS_OUTPUT_BUFFER_FULL) != 0)
186     {
187       controller_read_input();
188       if ((status & CONTROLLER_STATUS_MOUSE_OUTPUT_BUFFER_FULL) != 0)
189       {
190         return_value = TRUE;
191       }
192       break;
193     }
194
195     KeDelayExecutionThread (KernelMode, FALSE, &Millisecond_Timeout);
196   }
197   
198   return return_value;
199 }
200
201 // Initialize the PS/2 mouse support
202
203 BOOLEAN mouse_init (PDEVICE_OBJECT DeviceObject)
204 {
205   PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
206   ULONG MappedIrq;
207   KIRQL Dirql;
208   KAFFINITY Affinity;
209
210   if (!detect_ps2_port ()) return FALSE;
211
212   has_mouse = TRUE;
213
214   DeviceExtension->InputDataCount[0] = 0;
215   DeviceExtension->InputDataCount[1] = 0;
216   DeviceExtension->ActiveQueue = 0;
217
218   // Enable the PS/2 mouse port
219   controller_write_command_word (CONTROLLER_COMMAND_MOUSE_ENABLE);
220
221   // 200 samples/sec
222   mouse_write_ack (MOUSE_SET_SAMPLE_RATE);
223   mouse_write_ack (200);
224
225   // 8 counts per mm
226   mouse_write_ack (MOUSE_SET_RESOLUTION);
227   mouse_write_ack (3);
228
229   // 2:1 scaling
230   mouse_write_ack (MOUSE_SET_SCALE21);
231
232   // Enable the PS/2 device
233   mouse_write_ack (MOUSE_ENABLE_DEVICE);
234
235   // Enable controller interrupts
236   mouse_write_command (MOUSE_INTERRUPTS_ON);
237
238   // Connect the interrupt for the mouse irq
239   MappedIrq = HalGetInterruptVector(Internal, 0, 0, MOUSE_IRQ, &Dirql, &Affinity);
240
241   IoConnectInterrupt(&DeviceExtension->MouseInterrupt, ps2_mouse_handler, DeviceObject, NULL, MappedIrq,
242                      Dirql, Dirql, 0, FALSE, Affinity, FALSE);
243
244   return TRUE;
245 }