Create the AHCI branch for Aman's work
[reactos.git] / subsystems / mvdm / ntvdm / hardware / mouse.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/hardware/mouse.c
5 * PURPOSE: PS/2 Mouse emulation
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "ntvdm.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 #include "mouse.h"
17 #include "ps2.h"
18
19 #include "clock.h"
20 #include "video/svga.h"
21 /**/
22 #include "../console/video.h"
23 /**/
24
25 /* PRIVATE VARIABLES **********************************************************/
26
27 static const BYTE ScrollMagic[3] = { 200, 100, 80 };
28 static const BYTE ExtraButtonMagic[3] = { 200, 200, 80 };
29
30 static HANDLE MouseMutex;
31 static PHARDWARE_TIMER StreamTimer;
32 static MOUSE_PACKET LastPacket;
33 static MOUSE_MODE Mode, PreviousMode;
34 static COORD Position;
35 static BYTE Resolution; /* Completely ignored */
36 static BOOLEAN Scaling; /* Completely ignored */
37 static BOOLEAN Reporting = FALSE;
38 static BYTE MouseId;
39 static ULONG ButtonState;
40 static SHORT HorzCounter;
41 static SHORT VertCounter;
42 static CHAR ScrollCounter;
43 static BOOLEAN EventsOccurred = FALSE;
44 static BYTE DataByteWait = 0;
45 static BYTE ScrollMagicCounter = 0, ExtraButtonMagicCounter = 0;
46
47 static UINT MouseCycles = 10;
48
49 static BYTE PS2Port = 1;
50
51 /* PUBLIC VARIABLES ***********************************************************/
52
53 /* PRIVATE FUNCTIONS **********************************************************/
54
55 static VOID MouseResetConfig(VOID)
56 {
57 /* Reset the configuration to defaults */
58 MouseCycles = 10;
59 Resolution = 4;
60 Scaling = FALSE;
61 Reporting = FALSE;
62 }
63
64 static VOID MouseResetCounters(VOID)
65 {
66 /* Reset all flags and counters */
67 HorzCounter = VertCounter = ScrollCounter = 0;
68 }
69
70 static VOID MouseReset(VOID)
71 {
72 /* Reset everything */
73 MouseResetConfig();
74 MouseResetCounters();
75
76 /* Enter streaming mode and the reset the mouse ID */
77 Mode = MOUSE_STREAMING_MODE;
78 MouseId = 0;
79 ScrollMagicCounter = ExtraButtonMagicCounter = 0;
80 }
81
82 static VOID MouseGetPacket(PMOUSE_PACKET Packet)
83 {
84 /* Clear the packet */
85 RtlZeroMemory(Packet, sizeof(*Packet));
86
87 /* Acquire the mutex */
88 WaitForSingleObject(MouseMutex, INFINITE);
89
90 Packet->Flags |= MOUSE_ALWAYS_SET;
91
92 /* Set the sign flags */
93 if (HorzCounter < 0)
94 {
95 Packet->Flags |= MOUSE_X_SIGN;
96 HorzCounter = -HorzCounter;
97 }
98
99 if (VertCounter < 0)
100 {
101 Packet->Flags |= MOUSE_Y_SIGN;
102 VertCounter = -VertCounter;
103 }
104
105 /* Check for horizontal overflows */
106 if (HorzCounter > MOUSE_MAX)
107 {
108 HorzCounter = MOUSE_MAX;
109 Packet->Flags |= MOUSE_X_OVERFLOW;
110 }
111
112 /* Check for vertical overflows */
113 if (VertCounter > MOUSE_MAX)
114 {
115 VertCounter = MOUSE_MAX;
116 Packet->Flags |= MOUSE_Y_OVERFLOW;
117 }
118
119 /* Set the button flags */
120 if (ButtonState & FROM_LEFT_1ST_BUTTON_PRESSED) Packet->Flags |= MOUSE_LEFT_BUTTON;
121 if (ButtonState & FROM_LEFT_2ND_BUTTON_PRESSED) Packet->Flags |= MOUSE_MIDDLE_BUTTON;
122 if (ButtonState & RIGHTMOST_BUTTON_PRESSED) Packet->Flags |= MOUSE_RIGHT_BUTTON;
123
124 if (MouseId == 4)
125 {
126 if (ButtonState & FROM_LEFT_3RD_BUTTON_PRESSED) Packet->Extra |= MOUSE_4TH_BUTTON;
127 if (ButtonState & FROM_LEFT_4TH_BUTTON_PRESSED) Packet->Extra |= MOUSE_5TH_BUTTON;
128 }
129
130 if (MouseId >= 3)
131 {
132 /* Set the scroll counter */
133 Packet->Extra |= ((UCHAR)ScrollCounter & 0x0F);
134 }
135
136 /* Store the counters in the packet */
137 Packet->HorzCounter = LOBYTE(HorzCounter);
138 Packet->VertCounter = LOBYTE(VertCounter);
139
140 /* Reset the counters */
141 MouseResetCounters();
142
143 /* Release the mutex */
144 ReleaseMutex(MouseMutex);
145 }
146
147 static VOID MouseDispatchPacket(PMOUSE_PACKET Packet)
148 {
149 PS2QueuePush(PS2Port, Packet->Flags);
150 PS2QueuePush(PS2Port, Packet->HorzCounter);
151 PS2QueuePush(PS2Port, Packet->VertCounter);
152 if (MouseId >= 3) PS2QueuePush(PS2Port, Packet->Extra);
153 }
154
155 static VOID WINAPI MouseCommand(LPVOID Param, BYTE Command)
156 {
157 /* Check if we were waiting for a data byte */
158 if (DataByteWait)
159 {
160 PS2QueuePush(PS2Port, MOUSE_ACK);
161
162 switch (DataByteWait)
163 {
164 /* Set Resolution */
165 case 0xE8:
166 {
167 Resolution = Command;
168 break;
169 }
170
171 /* Set Sample Rate */
172 case 0xF3:
173 {
174 /* Check for the scroll wheel enabling sequence */
175 if (MouseId == 0)
176 {
177 if (Command == ScrollMagic[ScrollMagicCounter])
178 {
179 ScrollMagicCounter++;
180 if (ScrollMagicCounter == 3) MouseId = 3;
181 }
182 else
183 {
184 ScrollMagicCounter = 0;
185 }
186 }
187
188 /* Check for the 5-button enabling sequence */
189 if (MouseId == 3)
190 {
191 if (Command == ExtraButtonMagic[ExtraButtonMagicCounter])
192 {
193 ExtraButtonMagicCounter++;
194 if (ExtraButtonMagicCounter == 3) MouseId = 4;
195 }
196 else
197 {
198 ExtraButtonMagicCounter = 0;
199 }
200 }
201
202 MouseCycles = 1000 / (UINT)Command;
203 break;
204 }
205
206 default:
207 {
208 /* Shouldn't happen */
209 ASSERT(FALSE);
210 }
211 }
212
213 DataByteWait = 0;
214 return;
215 }
216
217 /* Check if we're in wrap mode */
218 if (Mode == MOUSE_WRAP_MODE)
219 {
220 /*
221 * In this mode, we just echo whatever byte we get,
222 * except for the 0xEC and 0xFF commands.
223 */
224 if (Command != 0xEC && Command != 0xFF)
225 {
226 PS2QueuePush(PS2Port, Command);
227 return;
228 }
229 }
230
231 switch (Command)
232 {
233 /* Set 1:1 Scaling */
234 case 0xE6:
235 {
236 Scaling = FALSE;
237 PS2QueuePush(PS2Port, MOUSE_ACK);
238 break;
239 }
240
241 /* Set 2:1 Scaling */
242 case 0xE7:
243 {
244 Scaling = TRUE;
245 PS2QueuePush(PS2Port, MOUSE_ACK);
246 break;
247 }
248
249 /* Set Resolution */
250 case 0xE8:
251 /* Set Sample Rate */
252 case 0xF3:
253 {
254 DataByteWait = Command;
255 PS2QueuePush(PS2Port, MOUSE_ACK);
256 break;
257 }
258
259 /* Read Status */
260 case 0xE9:
261 {
262 BYTE Status = ButtonState & 7;
263
264 if (Scaling) Status |= 1 << 4;
265 if (Reporting) Status |= 1 << 5;
266 if (Mode == MOUSE_REMOTE_MODE) Status |= 1 << 6;
267
268 PS2QueuePush(PS2Port, MOUSE_ACK);
269 PS2QueuePush(PS2Port, Status);
270 PS2QueuePush(PS2Port, Resolution);
271 PS2QueuePush(PS2Port, (BYTE)(1000 / MouseCycles));
272 break;
273 }
274
275 /* Enter Streaming Mode */
276 case 0xEA:
277 {
278 MouseResetCounters();
279 Mode = MOUSE_STREAMING_MODE;
280
281 PS2QueuePush(PS2Port, MOUSE_ACK);
282 break;
283 }
284
285 /* Read Packet */
286 case 0xEB:
287 {
288 PS2QueuePush(PS2Port, MOUSE_ACK);
289 MouseGetPacket(&LastPacket);
290 MouseDispatchPacket(&LastPacket);
291 break;
292 }
293
294 /* Return from Wrap Mode */
295 case 0xEC:
296 {
297 if (Mode == MOUSE_WRAP_MODE)
298 {
299 /* Restore the previous mode */
300 MouseResetCounters();
301 Mode = PreviousMode;
302 PS2QueuePush(PS2Port, MOUSE_ACK);
303 }
304 else
305 {
306 PS2QueuePush(PS2Port, MOUSE_ERROR);
307 }
308
309 break;
310 }
311
312 /* Enter Wrap Mode */
313 case 0xEE:
314 {
315 if (Mode != MOUSE_WRAP_MODE)
316 {
317 /* Save the previous mode */
318 PreviousMode = Mode;
319 }
320
321 MouseResetCounters();
322 Mode = MOUSE_WRAP_MODE;
323
324 PS2QueuePush(PS2Port, MOUSE_ACK);
325 break;
326 }
327
328 /* Enter Remote Mode */
329 case 0xF0:
330 {
331 MouseResetCounters();
332 Mode = MOUSE_REMOTE_MODE;
333
334 PS2QueuePush(PS2Port, MOUSE_ACK);
335 break;
336 }
337
338 /* Get Mouse ID */
339 case 0xF2:
340 {
341 PS2QueuePush(PS2Port, MOUSE_ACK);
342 PS2QueuePush(PS2Port, MouseId);
343 break;
344 }
345
346 /* Enable Reporting */
347 case 0xF4:
348 {
349 Reporting = TRUE;
350 MouseResetCounters();
351 PS2QueuePush(PS2Port, MOUSE_ACK);
352 break;
353 }
354
355 /* Disable Reporting */
356 case 0xF5:
357 {
358 Reporting = FALSE;
359 MouseResetCounters();
360 PS2QueuePush(PS2Port, MOUSE_ACK);
361 break;
362 }
363
364 /* Set Defaults */
365 case 0xF6:
366 {
367 /* Reset the configuration and counters */
368 MouseResetConfig();
369 MouseResetCounters();
370 PS2QueuePush(PS2Port, MOUSE_ACK);
371 break;
372 }
373
374 /* Resend */
375 case 0xFE:
376 {
377 PS2QueuePush(PS2Port, MOUSE_ACK);
378 MouseDispatchPacket(&LastPacket);
379 break;
380 }
381
382 /* Reset */
383 case 0xFF:
384 {
385 /* Send ACKnowledge */
386 PS2QueuePush(PS2Port, MOUSE_ACK);
387
388 MouseReset();
389
390 /* Send the Basic Assurance Test success code and the device ID */
391 PS2QueuePush(PS2Port, MOUSE_BAT_SUCCESS);
392 PS2QueuePush(PS2Port, MouseId);
393 break;
394 }
395
396 /* Unknown command */
397 default:
398 {
399 PS2QueuePush(PS2Port, MOUSE_ERROR);
400 }
401 }
402 }
403
404 static VOID FASTCALL MouseStreamingCallback(ULONGLONG ElapsedTime)
405 {
406 UNREFERENCED_PARAMETER(ElapsedTime);
407
408 /* Check if we're not in streaming mode, not reporting, or there's nothing to report */
409 if (Mode != MOUSE_STREAMING_MODE || !Reporting || !EventsOccurred) return;
410
411 MouseGetPacket(&LastPacket);
412 MouseDispatchPacket(&LastPacket);
413
414 EventsOccurred = FALSE;
415 }
416
417 /* PUBLIC FUNCTIONS ***********************************************************/
418
419 VOID MouseGetDataFast(PCOORD CurrentPosition, PBYTE CurrentButtonState)
420 {
421 WaitForSingleObject(MouseMutex, INFINITE);
422 *CurrentPosition = Position;
423 *CurrentButtonState = LOBYTE(ButtonState);
424 ReleaseMutex(MouseMutex);
425 }
426
427 VOID MouseEventHandler(PMOUSE_EVENT_RECORD MouseEvent)
428 {
429 COORD NewPosition = MouseEvent->dwMousePosition;
430 BOOLEAN DoubleWidth = FALSE, DoubleHeight = FALSE;
431
432 if (!VgaGetDoubleVisionState(&DoubleWidth, &DoubleHeight))
433 {
434 /* Text mode */
435 NewPosition.X *= 8;
436 NewPosition.Y *= 8;
437 }
438
439 /* Adjust for double vision */
440 if (DoubleWidth) NewPosition.X /= 2;
441 if (DoubleHeight) NewPosition.Y /= 2;
442
443 WaitForSingleObject(MouseMutex, INFINITE);
444
445 /* Update the counters */
446 HorzCounter += (NewPosition.X - Position.X) << DoubleWidth;
447 VertCounter += (NewPosition.Y - Position.Y) << DoubleHeight;
448
449 /* Update the position */
450 Position = NewPosition;
451
452 /* Update the button state */
453 ButtonState = MouseEvent->dwButtonState;
454
455 if (MouseEvent->dwEventFlags & MOUSE_WHEELED)
456 {
457 ScrollCounter += (SHORT)HIWORD(MouseEvent->dwButtonState);
458 }
459
460 EventsOccurred = TRUE;
461 ReleaseMutex(MouseMutex);
462 }
463
464 BOOLEAN MouseInit(BYTE PS2Connector)
465 {
466 /* Finish to plug the mouse to the specified PS/2 port */
467 PS2Port = PS2Connector;
468 PS2SetDeviceCmdProc(PS2Port, NULL, MouseCommand);
469
470 MouseMutex = CreateMutex(NULL, FALSE, NULL);
471 if (MouseMutex == NULL) return FALSE;
472
473 StreamTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED,
474 HZ_TO_NS(100),
475 MouseStreamingCallback);
476
477 MouseReset();
478 return TRUE;
479 }