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