1 /* 2 * COPYRIGHT: GPL - See COPYING in the top level directory 3 * PROJECT: ReactOS Virtual DOS Machine 4 * FILE: subsystems/mvdm/ntvdm/bios/bios32/moubios32.c 5 * PURPOSE: VDM 32-bit PS/2 Mouse BIOS 6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org> 7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr) 8 * 9 * NOTE: Based from VirtualBox OSE ROM BIOS, and SeaBIOS. 10 */ 11 12 /* INCLUDES *******************************************************************/ 13 14 #include "ntvdm.h" 15 16 #define NDEBUG 17 #include <debug.h> 18 19 #include "emulator.h" 20 #include "cpu/cpu.h" // for EMULATOR_FLAG_CF 21 22 #include "moubios32.h" 23 #include "bios32p.h" 24 25 #include "io.h" 26 #include "hardware/mouse.h" 27 #include "hardware/ps2.h" 28 29 /* PRIVATE VARIABLES **********************************************************/ 30 31 #define MOUSE_IRQ_INT 0x74 32 33 static BOOLEAN MouseEnabled = FALSE; 34 static DWORD OldIrqHandler; 35 36 /* 37 * Far pointer to a device handler. In compatible PS/2, it is stored in the EBDA. 38 * 39 * See Ralf Brown: http://www.ctyme.com/intr/rb-1603.htm 40 * for more information. In particular: 41 * when the subroutine is called, it is given 4 WORD values on the stack; 42 * the handler should return with a FAR return without popping the stack. 43 */ 44 static ULONG DeviceHandler = 0; 45 46 /* PRIVATE FUNCTIONS **********************************************************/ 47 48 static VOID DisableMouseInt(VOID) 49 { 50 BYTE ControllerConfig; 51 52 /* Clear the mouse queue */ 53 while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231 54 55 /* Disable mouse interrupt and events */ 56 IOWriteB(PS2_CONTROL_PORT, 0x20); 57 ControllerConfig = IOReadB(PS2_DATA_PORT); 58 ControllerConfig &= ~0x02; // Turn off IRQ12 59 ControllerConfig |= 0x20; // Disable mouse clock line 60 IOWriteB(PS2_CONTROL_PORT, 0x60); 61 IOWriteB(PS2_DATA_PORT, ControllerConfig); 62 } 63 64 static VOID EnableMouseInt(VOID) 65 { 66 BYTE ControllerConfig; 67 68 /* Clear the mouse queue */ 69 while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231 70 71 /* Enable mouse interrupt and events */ 72 IOWriteB(PS2_CONTROL_PORT, 0x20); 73 ControllerConfig = IOReadB(PS2_DATA_PORT); 74 ControllerConfig |= 0x02; // Turn on IRQ12 75 ControllerConfig &= ~0x20; // Enable mouse clock line 76 IOWriteB(PS2_CONTROL_PORT, 0x60); 77 IOWriteB(PS2_DATA_PORT, ControllerConfig); 78 } 79 80 static inline 81 VOID SendMouseCommand(UCHAR Command) 82 { 83 /* Clear the mouse queue */ 84 while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231 85 86 /* Send the command */ 87 IOWriteB(PS2_CONTROL_PORT, 0xD4); 88 IOWriteB(PS2_DATA_PORT, Command); 89 } 90 91 static inline 92 UCHAR ReadMouseData(VOID) 93 { 94 PS2PortQueueRead(1); // NOTE: Should be a IOReadB! But see r67231 95 return IOReadB(PS2_DATA_PORT); 96 } 97 98 99 static 100 VOID BiosMouseEnable(VOID) 101 { 102 if (MouseEnabled) return; 103 104 MouseEnabled = TRUE; 105 106 /* Get the old IRQ handler */ 107 OldIrqHandler = ((PDWORD)BaseAddress)[MOUSE_IRQ_INT]; 108 109 /* Set the IRQ handler */ 110 //RegisterInt32(MAKELONG(FIELD_OFFSET(MOUSE_DRIVER, MouseIrqInt16Stub), MouseDataSegment), 111 // MOUSE_IRQ_INT, DosMouseIrq, NULL); 112 } 113 114 static 115 VOID BiosMouseDisable(VOID) 116 { 117 if (!MouseEnabled) return; 118 119 /* Restore the old IRQ handler */ 120 // ((PDWORD)BaseAddress)[MOUSE_IRQ_INT] = OldIrqHandler; 121 122 MouseEnabled = FALSE; 123 } 124 125 126 // Mouse IRQ 12 127 static VOID WINAPI BiosMouseIrq(LPWORD Stack) 128 { 129 DPRINT1("PS/2 Mouse IRQ! DeviceHandler = 0x%04X:0x%04X\n", 130 HIWORD(DeviceHandler), LOWORD(DeviceHandler)); 131 132 if (DeviceHandler != 0) 133 { 134 /* 135 * Prepare the stack for the mouse device handler: 136 * push Status, X and Y data, and a zero word. 137 */ 138 setSP(getSP() - sizeof(WORD)); 139 *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Status 140 setSP(getSP() - sizeof(WORD)); 141 *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // X data (high byte = 0) 142 setSP(getSP() - sizeof(WORD)); 143 *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Y data (high byte = 0) 144 setSP(getSP() - sizeof(WORD)); 145 *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Zero 146 147 /* Call the device handler */ 148 RunCallback16(&BiosContext, DeviceHandler); 149 150 /* Pop the stack */ 151 setSP(getSP() + 4*sizeof(WORD)); 152 } 153 154 PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM])); 155 } 156 157 VOID BiosMousePs2Interface(LPWORD Stack) 158 { 159 /* Disable mouse interrupt and events */ 160 DisableMouseInt(); 161 162 switch (getAL()) 163 { 164 /* Enable / Disable */ 165 case 0x00: 166 { 167 UCHAR State = getBH(); 168 169 if (State > 2) 170 { 171 /* Invalid function */ 172 setAH(0x01); 173 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 174 break; 175 } 176 177 if (State == 0x00) 178 { 179 BiosMouseDisable(); 180 181 /* Disable packet reporting */ 182 SendMouseCommand(0xF5); 183 } 184 else // if (State == 0x01) 185 { 186 /* Check for the presence of the device handler */ 187 if (DeviceHandler == 0) 188 { 189 /* No device handler installed */ 190 setAH(0x05); 191 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 192 break; 193 } 194 195 BiosMouseEnable(); 196 197 /* Enable packet reporting */ 198 SendMouseCommand(0xF4); 199 } 200 201 if (ReadMouseData() != MOUSE_ACK) 202 { 203 /* Failure */ 204 setAH(0x03); 205 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 206 break; 207 } 208 209 /* Success */ 210 setAH(0x00); 211 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 212 break; 213 } 214 215 /* Initialize */ 216 case 0x05: 217 { 218 // Fall through 219 } 220 221 /* Reset */ 222 case 0x01: 223 { 224 UCHAR Answer; 225 226 SendMouseCommand(0xFF); 227 Answer = ReadMouseData(); 228 /* A "Resend" signal (0xFE) is sent if no mouse is attached */ 229 if (Answer == 0xFE) 230 { 231 /* Resend */ 232 setAH(0x04); 233 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 234 break; 235 } 236 else if (Answer != MOUSE_ACK) 237 { 238 /* Failure */ 239 setAH(0x03); 240 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 241 break; 242 } 243 244 setBL(ReadMouseData()); // Should be MOUSE_BAT_SUCCESS 245 setBH(ReadMouseData()); // Mouse ID 246 247 /* Success */ 248 setAH(0x00); 249 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 250 break; 251 } 252 253 /* Set Sampling Rate */ 254 case 0x02: 255 { 256 UCHAR SampleRate = 0; 257 258 switch (getBH()) 259 { 260 case 0x00: SampleRate = 10; break; // 10 reports/sec 261 case 0x01: SampleRate = 20; break; // 20 " " 262 case 0x02: SampleRate = 40; break; // 40 " " 263 case 0x03: SampleRate = 60; break; // 60 " " 264 case 0x04: SampleRate = 80; break; // 80 " " 265 case 0x05: SampleRate = 100; break; // 100 " " 266 case 0x06: SampleRate = 200; break; // 200 " " 267 default: SampleRate = 0; 268 } 269 270 if (SampleRate == 0) 271 { 272 /* Invalid input */ 273 setAH(0x02); 274 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 275 break; 276 } 277 278 SendMouseCommand(0xF3); 279 if (ReadMouseData() != MOUSE_ACK) 280 { 281 /* Failure */ 282 setAH(0x03); 283 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 284 break; 285 } 286 287 SendMouseCommand(SampleRate); 288 if (ReadMouseData() != MOUSE_ACK) 289 { 290 /* Failure */ 291 setAH(0x03); 292 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 293 break; 294 } 295 296 /* Success */ 297 setAH(0x00); 298 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 299 break; 300 } 301 302 /* Set Resolution */ 303 case 0x03: 304 { 305 UCHAR Resolution = getBH(); 306 307 /* 308 * 0: 25 dpi, 1 count per millimeter 309 * 1: 50 dpi, 2 counts per millimeter 310 * 2: 100 dpi, 4 counts per millimeter 311 * 3: 200 dpi, 8 counts per millimeter 312 */ 313 if (Resolution > 3) 314 { 315 /* Invalid input */ 316 setAH(0x02); 317 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 318 break; 319 } 320 321 SendMouseCommand(0xE8); 322 if (ReadMouseData() != MOUSE_ACK) 323 { 324 /* Failure */ 325 setAH(0x03); 326 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 327 break; 328 } 329 330 SendMouseCommand(Resolution); 331 if (ReadMouseData() != MOUSE_ACK) 332 { 333 /* Failure */ 334 setAH(0x03); 335 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 336 break; 337 } 338 339 /* Success */ 340 setAH(0x00); 341 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 342 break; 343 } 344 345 /* Get Type */ 346 case 0x04: 347 { 348 SendMouseCommand(0xF2); 349 if (ReadMouseData() != MOUSE_ACK) 350 { 351 /* Failure */ 352 setAH(0x03); 353 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 354 break; 355 } 356 357 setBH(ReadMouseData()); 358 359 /* Success */ 360 setAH(0x00); 361 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 362 break; 363 } 364 365 /* Extended Commands (Return Status and Set Scaling Factor) */ 366 case 0x06: 367 { 368 UCHAR Command = getBH(); 369 370 switch (Command) 371 { 372 /* Return Status */ 373 case 0x00: 374 { 375 SendMouseCommand(0xE9); 376 if (ReadMouseData() != MOUSE_ACK) 377 { 378 /* Failure */ 379 setAH(0x03); 380 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 381 break; 382 } 383 384 setBL(ReadMouseData()); // Status 385 setCL(ReadMouseData()); // Resolution 386 setDL(ReadMouseData()); // Sample rate 387 388 /* Success */ 389 setAH(0x00); 390 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 391 break; 392 } 393 394 /* Set Scaling Factor to 1:1 */ 395 case 0x01: 396 /* Set Scaling Factor to 2:1 */ 397 case 0x02: 398 { 399 SendMouseCommand(Command == 0x01 ? 0xE6 : 0xE7); 400 if (ReadMouseData() != MOUSE_ACK) 401 { 402 /* Failure */ 403 setAH(0x03); 404 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 405 break; 406 } 407 408 /* Success */ 409 setAH(0x00); 410 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 411 break; 412 } 413 414 default: 415 { 416 /* Invalid function */ 417 setAH(0x01); 418 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 419 break; 420 } 421 } 422 423 break; 424 } 425 426 /* Set Device Handler Address */ 427 case 0x07: 428 { 429 /* ES:BX == 0000h:0000h removes the device handler */ 430 DeviceHandler = MAKELONG(getBX(), getES()); 431 432 /* Success */ 433 setAH(0x00); 434 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 435 break; 436 } 437 438 /* Write to Pointer Port */ 439 case 0x08: 440 { 441 SendMouseCommand(getBL()); 442 if (ReadMouseData() != MOUSE_ACK) 443 { 444 /* Failure */ 445 setAH(0x03); 446 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 447 break; 448 } 449 450 /* Success */ 451 setAH(0x00); 452 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 453 break; 454 } 455 456 /* Read from Pointer Port */ 457 case 0x09: 458 { 459 setBL(ReadMouseData()); 460 setCL(ReadMouseData()); 461 setDL(ReadMouseData()); 462 463 /* Success */ 464 setAH(0x00); 465 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 466 break; 467 } 468 469 default: 470 { 471 DPRINT1("INT 15h, AH = C2h, AL = %02Xh NOT IMPLEMENTED\n", 472 getAL()); 473 474 /* Unknown function */ 475 setAH(0x01); 476 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 477 } 478 } 479 480 /* Reenable mouse interrupt and events */ 481 EnableMouseInt(); 482 } 483 484 /* PUBLIC FUNCTIONS ***********************************************************/ 485 486 VOID MouseBios32Post(VOID) 487 { 488 UCHAR Answer; 489 490 /* Initialize PS/2 mouse port */ 491 // Enable the port 492 IOWriteB(PS2_CONTROL_PORT, 0xA8); 493 494 /* Detect mouse presence by attempting a reset */ 495 SendMouseCommand(0xFF); 496 Answer = ReadMouseData(); 497 /* A "Resend" signal (0xFE) is sent if no mouse is attached */ 498 if (Answer == 0xFE) 499 { 500 DPRINT1("No mouse present!\n"); 501 } 502 else if (Answer != MOUSE_ACK) 503 { 504 DPRINT1("Mouse reset failure!\n"); 505 } 506 else 507 { 508 /* Mouse present, try to completely enable it */ 509 510 // FIXME: The following is temporary until 511 // this is moved into the mouse driver!! 512 513 /* Enable packet reporting */ 514 SendMouseCommand(0xF4); 515 if (ReadMouseData() != MOUSE_ACK) 516 { 517 DPRINT1("Failed to enable mouse!\n"); 518 } 519 else 520 { 521 /* Enable mouse interrupt and events */ 522 EnableMouseInt(); 523 } 524 } 525 526 /* No mouse driver available so far */ 527 RegisterBiosInt32(0x33, NULL); 528 529 /* Set up the HW vector interrupts */ 530 EnableHwIRQ(12, BiosMouseIrq); 531 } 532 533 BOOLEAN MouseBiosInitialize(VOID) 534 { 535 return TRUE; 536 } 537 538 VOID MouseBios32Cleanup(VOID) 539 { 540 } 541