xref: /reactos/subsystems/mvdm/ntvdm/hardware/mouse.c (revision 5ef2c451)
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 MouseReporting = 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 MouseDataByteWait = 0;
45 static BYTE ScrollMagicCounter = 0, ExtraButtonMagicCounter = 0;
46 
47 static UINT MouseCycles = 10;
48 
49 static BYTE MousePS2Port = 1;
50 
51 /* PUBLIC VARIABLES ***********************************************************/
52 
53 /* PRIVATE FUNCTIONS **********************************************************/
54 
MouseResetConfig(VOID)55 static VOID MouseResetConfig(VOID)
56 {
57     /* Reset the configuration to defaults */
58     MouseCycles = 10;
59     Resolution = 4;
60     Scaling = FALSE;
61     MouseReporting = FALSE;
62 }
63 
MouseResetCounters(VOID)64 static VOID MouseResetCounters(VOID)
65 {
66     /* Reset all flags and counters */
67     HorzCounter = VertCounter = ScrollCounter = 0;
68 }
69 
MouseReset(VOID)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 
MouseGetPacket(PMOUSE_PACKET Packet)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 
MouseDispatchPacket(PMOUSE_PACKET Packet)147 static VOID MouseDispatchPacket(PMOUSE_PACKET Packet)
148 {
149     PS2QueuePush(MousePS2Port, Packet->Flags);
150     PS2QueuePush(MousePS2Port, Packet->HorzCounter);
151     PS2QueuePush(MousePS2Port, Packet->VertCounter);
152     if (MouseId >= 3) PS2QueuePush(MousePS2Port, Packet->Extra);
153 }
154 
MouseCommand(LPVOID Param,BYTE Command)155 static VOID WINAPI MouseCommand(LPVOID Param, BYTE Command)
156 {
157     /* Check if we were waiting for a data byte */
158     if (MouseDataByteWait)
159     {
160         PS2QueuePush(MousePS2Port, MOUSE_ACK);
161 
162         switch (MouseDataByteWait)
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         MouseDataByteWait = 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(MousePS2Port, Command);
227             return;
228         }
229     }
230 
231     switch (Command)
232     {
233         /* Set 1:1 Scaling */
234         case 0xE6:
235         {
236             Scaling = FALSE;
237             PS2QueuePush(MousePS2Port, MOUSE_ACK);
238             break;
239         }
240 
241         /* Set 2:1 Scaling */
242         case 0xE7:
243         {
244             Scaling = TRUE;
245             PS2QueuePush(MousePS2Port, MOUSE_ACK);
246             break;
247         }
248 
249         /* Set Resolution */
250         case 0xE8:
251         /* Set Sample Rate */
252         case 0xF3:
253         {
254             MouseDataByteWait = Command;
255             PS2QueuePush(MousePS2Port, 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 (MouseReporting) Status |= 1 << 5;
266             if (Mode == MOUSE_REMOTE_MODE) Status |= 1 << 6;
267 
268             PS2QueuePush(MousePS2Port, MOUSE_ACK);
269             PS2QueuePush(MousePS2Port, Status);
270             PS2QueuePush(MousePS2Port, Resolution);
271             PS2QueuePush(MousePS2Port, (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(MousePS2Port, MOUSE_ACK);
282             break;
283         }
284 
285         /* Read Packet */
286         case 0xEB:
287         {
288             PS2QueuePush(MousePS2Port, 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(MousePS2Port, MOUSE_ACK);
303             }
304             else
305             {
306                 PS2QueuePush(MousePS2Port, 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(MousePS2Port, MOUSE_ACK);
325             break;
326         }
327 
328         /* Enter Remote Mode */
329         case 0xF0:
330         {
331             MouseResetCounters();
332             Mode = MOUSE_REMOTE_MODE;
333 
334             PS2QueuePush(MousePS2Port, MOUSE_ACK);
335             break;
336         }
337 
338         /* Get Mouse ID */
339         case 0xF2:
340         {
341             PS2QueuePush(MousePS2Port, MOUSE_ACK);
342             PS2QueuePush(MousePS2Port, MouseId);
343             break;
344         }
345 
346         /* Enable Reporting */
347         case 0xF4:
348         {
349             MouseReporting = TRUE;
350             MouseResetCounters();
351             PS2QueuePush(MousePS2Port, MOUSE_ACK);
352             break;
353         }
354 
355         /* Disable Reporting */
356         case 0xF5:
357         {
358             MouseReporting = FALSE;
359             MouseResetCounters();
360             PS2QueuePush(MousePS2Port, 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(MousePS2Port, MOUSE_ACK);
371             break;
372         }
373 
374         /* Resend */
375         case 0xFE:
376         {
377             PS2QueuePush(MousePS2Port, MOUSE_ACK);
378             MouseDispatchPacket(&LastPacket);
379             break;
380         }
381 
382         /* Reset */
383         case 0xFF:
384         {
385             /* Send ACKnowledge */
386             PS2QueuePush(MousePS2Port, MOUSE_ACK);
387 
388             MouseReset();
389 
390             /* Send the Basic Assurance Test success code and the device ID */
391             PS2QueuePush(MousePS2Port, MOUSE_BAT_SUCCESS);
392             PS2QueuePush(MousePS2Port, MouseId);
393             break;
394         }
395 
396         /* Unknown command */
397         default:
398         {
399             PS2QueuePush(MousePS2Port, MOUSE_ERROR);
400         }
401     }
402 }
403 
MouseStreamingCallback(ULONGLONG ElapsedTime)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 || !MouseReporting || !EventsOccurred) return;
410 
411     MouseGetPacket(&LastPacket);
412     MouseDispatchPacket(&LastPacket);
413 
414     EventsOccurred = FALSE;
415 }
416 
417 /* PUBLIC FUNCTIONS ***********************************************************/
418 
MouseGetDataFast(PCOORD CurrentPosition,PBYTE CurrentButtonState)419 VOID MouseGetDataFast(PCOORD CurrentPosition, PBYTE CurrentButtonState)
420 {
421     WaitForSingleObject(MouseMutex, INFINITE);
422     *CurrentPosition = Position;
423     *CurrentButtonState = LOBYTE(ButtonState);
424     ReleaseMutex(MouseMutex);
425 }
426 
MouseEventHandler(PMOUSE_EVENT_RECORD MouseEvent)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 
MouseInit(BYTE PS2Connector)464 BOOLEAN MouseInit(BYTE PS2Connector)
465 {
466     /* Finish to plug the mouse to the specified PS/2 port */
467     MousePS2Port = PS2Connector;
468     PS2SetDeviceCmdProc(MousePS2Port, 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 }
480