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 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 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(MousePS2Port, Packet->Flags); 150 PS2QueuePush(MousePS2Port, Packet->HorzCounter); 151 PS2QueuePush(MousePS2Port, Packet->VertCounter); 152 if (MouseId >= 3) PS2QueuePush(MousePS2Port, 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 (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 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 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 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