1 /* 2 * COPYRIGHT: GPL - See COPYING in the top level directory 3 * PROJECT: ReactOS Virtual DOS Machine 4 * FILE: subsystems/mvdm/ntvdm/hardware/ps2.c 5 * PURPOSE: PS/2 controller emulation 6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org> 7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr) 8 * 9 * DOCUMENTATION: IBM Personal System/2 Hardware Interface Technical Reference, May 1988 (Section 10) 10 * http://wiki.osdev.org/%228042%22_PS/2_Controller 11 * http://www.computer-engineering.org/ps2keyboard/ 12 */ 13 14 /* INCLUDES *******************************************************************/ 15 16 #include "ntvdm.h" 17 18 #define NDEBUG 19 #include <debug.h> 20 21 #include "emulator.h" 22 #include "ps2.h" 23 24 #include "memory.h" 25 #include "io.h" 26 #include "pic.h" 27 #include "clock.h" 28 29 /* PRIVATE VARIABLES **********************************************************/ 30 31 #define BUFFER_SIZE 32 32 33 typedef struct _PS2_PORT 34 { 35 BOOLEAN IsEnabled; 36 37 BOOLEAN QueueEmpty; 38 BYTE Queue[BUFFER_SIZE]; 39 UINT QueueStart; 40 UINT QueueEnd; 41 HANDLE QueueMutex; 42 43 LPVOID Param; 44 PS2_DEVICE_CMDPROC DeviceCommand; 45 } PS2_PORT, *PPS2_PORT; 46 47 /* 48 * Port 1: Keyboard 49 * Port 2: Mouse 50 */ 51 #define PS2_PORTS 2 52 static PS2_PORT Ports[PS2_PORTS]; 53 54 static BYTE Memory[0x20]; // PS/2 Controller internal memory 55 #define ControllerConfig Memory[0] // The configuration byte is byte 0 56 57 static BYTE StatusRegister = 0x00; // Controller status register 58 // static BYTE InputBuffer = 0x00; // PS/2 Input Buffer 59 static BYTE OutputBuffer = 0x00; // PS/2 Output Buffer 60 61 static BYTE ControllerCommand = 0x00; 62 63 static PHARDWARE_TIMER IrqTimer = NULL; 64 65 /* PRIVATE FUNCTIONS **********************************************************/ 66 67 static VOID PS2SendCommand(BYTE PS2Port, BYTE Command) 68 { 69 PPS2_PORT Port; 70 71 ASSERT(PS2Port < PS2_PORTS); 72 Port = &Ports[PS2Port]; 73 74 /* 75 * According to http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html#kccad 76 * any PS/2 command sent reenables the corresponding port. 77 */ 78 if (PS2Port == 0) 79 ControllerConfig &= ~PS2_CONFIG_KBD_DISABLE; 80 else // if (PS2Port == 1) 81 ControllerConfig &= ~PS2_CONFIG_AUX_DISABLE; 82 83 Port->IsEnabled = TRUE; 84 85 /* Call the device command */ 86 if (Port->DeviceCommand) Port->DeviceCommand(Port->Param, Command); 87 } 88 89 90 static BYTE WINAPI PS2ReadControl(USHORT Port) 91 { 92 UNREFERENCED_PARAMETER(Port); 93 94 /* 95 * Be sure the "Keyboard enable" flag is always set. 96 * On IBM PC-ATs this is the state of the hardware keyboard 97 * lock mechanism. It is not widely used, but some programs 98 * still use it, see for example: 99 * http://www.os2museum.com/wp/the-dos-4-0-shell-mouse-mystery/ 100 */ 101 StatusRegister |= PS2_STAT_KBD_ENABLE; 102 103 /* We do not have any timeouts nor parity errors */ 104 StatusRegister &= ~(PS2_STAT_PARITY_ERROR | PS2_STAT_GEN_TIMEOUT); 105 106 return StatusRegister; 107 } 108 109 static BYTE WINAPI PS2ReadData(USHORT Port) 110 { 111 UNREFERENCED_PARAMETER(Port); 112 113 /* 114 * If there is something to read (response byte from the 115 * controller or data from a PS/2 device), read it. 116 */ 117 StatusRegister &= ~PS2_STAT_OUT_BUF_FULL; 118 119 // Keep the state of the "Auxiliary output buffer full" flag 120 // in order to remember from where the data came from. 121 // StatusRegister &= ~PS2_STAT_AUX_OUT_BUF_FULL; 122 123 // FIXME: We may check there whether there is data latched in 124 // PS2 ports 1 or 2 (keyboard or mouse) and retrieve it there... 125 126 /* Always return the available byte stored in the output buffer */ 127 return OutputBuffer; 128 } 129 130 static VOID WINAPI PS2WriteControl(USHORT Port, BYTE Data) 131 { 132 UNREFERENCED_PARAMETER(Port); 133 134 switch (Data) 135 { 136 /* Read configuration byte (byte 0 from internal memory) */ 137 case 0x20: 138 /* Read byte N from internal memory */ 139 case 0x21: case 0x22: case 0x23: 140 case 0x24: case 0x25: case 0x26: case 0x27: 141 case 0x28: case 0x29: case 0x2A: case 0x2B: 142 case 0x2C: case 0x2D: case 0x2E: case 0x2F: 143 case 0x30: case 0x31: case 0x32: case 0x33: 144 case 0x34: case 0x35: case 0x36: case 0x37: 145 case 0x38: case 0x39: case 0x3A: case 0x3B: 146 case 0x3C: case 0x3D: case 0x3E: case 0x3F: 147 { 148 OutputBuffer = Memory[Data & 0x1F]; 149 StatusRegister |= PS2_STAT_OUT_BUF_FULL; 150 break; 151 } 152 153 /* Write configuration byte (byte 0 from internal memory) */ 154 case 0x60: 155 /* Write to byte N of internal memory */ 156 case 0x61: case 0x62: case 0x63: 157 case 0x64: case 0x65: case 0x66: case 0x67: 158 case 0x68: case 0x69: case 0x6A: case 0x6B: 159 case 0x6C: case 0x6D: case 0x6E: case 0x6F: 160 case 0x70: case 0x71: case 0x72: case 0x73: 161 case 0x74: case 0x75: case 0x76: case 0x77: 162 case 0x78: case 0x79: case 0x7A: case 0x7B: 163 case 0x7C: case 0x7D: case 0x7E: case 0x7F: 164 165 /* Write controller output port */ 166 case 0xD1: 167 /* Write to the first PS/2 port output buffer */ 168 case 0xD2: 169 /* Write to the second PS/2 port output buffer */ 170 case 0xD3: 171 /* Write to the second PS/2 port input buffer */ 172 case 0xD4: 173 { 174 /* These commands require a response */ 175 ControllerCommand = Data; 176 StatusRegister |= PS2_STAT_COMMAND; 177 break; 178 } 179 180 /* Disable second PS/2 port */ 181 case 0xA7: 182 { 183 ControllerConfig |= PS2_CONFIG_AUX_DISABLE; 184 Ports[1].IsEnabled = FALSE; 185 break; 186 } 187 188 /* Enable second PS/2 port */ 189 case 0xA8: 190 { 191 ControllerConfig &= ~PS2_CONFIG_AUX_DISABLE; 192 Ports[1].IsEnabled = TRUE; 193 break; 194 } 195 196 /* Test second PS/2 port */ 197 case 0xA9: 198 { 199 OutputBuffer = 0x00; // Success code 200 StatusRegister |= PS2_STAT_OUT_BUF_FULL; 201 break; 202 } 203 204 /* Test PS/2 controller */ 205 case 0xAA: 206 { 207 OutputBuffer = 0x55; // Success code 208 StatusRegister |= PS2_STAT_OUT_BUF_FULL; 209 break; 210 } 211 212 /* Test first PS/2 port */ 213 case 0xAB: 214 { 215 OutputBuffer = 0x00; // Success code 216 StatusRegister |= PS2_STAT_OUT_BUF_FULL; 217 break; 218 } 219 220 /* Disable first PS/2 port */ 221 case 0xAD: 222 { 223 ControllerConfig |= PS2_CONFIG_KBD_DISABLE; 224 Ports[0].IsEnabled = FALSE; 225 break; 226 } 227 228 /* Enable first PS/2 port */ 229 case 0xAE: 230 { 231 ControllerConfig &= ~PS2_CONFIG_KBD_DISABLE; 232 Ports[0].IsEnabled = TRUE; 233 break; 234 } 235 236 /* Read controller output port */ 237 case 0xD0: 238 { 239 /* Bit 0 always set, and bit 1 is the A20 gate state */ 240 OutputBuffer = (!!EmulatorGetA20() << 1) | PS2_OUT_CPU_NO_RESET; 241 242 /* Set IRQ1 state */ 243 if (ControllerConfig & PS2_CONFIG_KBD_INT) 244 OutputBuffer |= PS2_OUT_IRQ01; 245 else 246 OutputBuffer &= ~PS2_OUT_IRQ01; 247 248 /* Set IRQ12 state */ 249 if (ControllerConfig & PS2_CONFIG_AUX_INT) 250 OutputBuffer |= PS2_OUT_IRQ12; 251 else 252 OutputBuffer &= ~PS2_OUT_IRQ12; 253 254 /* Check whether data is already present */ 255 if (StatusRegister & PS2_STAT_OUT_BUF_FULL) 256 { 257 if (StatusRegister & PS2_STAT_AUX_OUT_BUF_FULL) 258 OutputBuffer |= PS2_OUT_AUX_DATA; 259 else 260 OutputBuffer |= PS2_OUT_KBD_DATA; 261 } 262 263 StatusRegister |= PS2_STAT_OUT_BUF_FULL; 264 break; 265 } 266 267 /* CPU Reset */ 268 case 0xF0: case 0xF2: case 0xF4: case 0xF6: 269 case 0xF8: case 0xFA: case 0xFC: case 0xFE: 270 { 271 /* Stop the VDM */ 272 EmulatorTerminate(); 273 return; 274 } 275 } 276 } 277 278 static VOID WINAPI PS2WriteData(USHORT Port, BYTE Data) 279 { 280 /* Check if the controller is waiting for a response */ 281 if (StatusRegister & PS2_STAT_COMMAND) 282 { 283 StatusRegister &= ~PS2_STAT_COMMAND; 284 285 /* Check which command it was */ 286 switch (ControllerCommand) 287 { 288 /* Write configuration byte (byte 0 from internal memory) */ 289 case 0x60: 290 { 291 ControllerConfig = Data; 292 293 /* 294 * Synchronize the enable state of the PS/2 ports 295 * with the flags in the configuration byte. 296 */ 297 Ports[0].IsEnabled = !(ControllerConfig & PS2_CONFIG_KBD_DISABLE); 298 Ports[1].IsEnabled = !(ControllerConfig & PS2_CONFIG_AUX_DISABLE); 299 300 /* 301 * Update the "System enabled" flag of the status register 302 * with bit 2 of the controller configuration byte. 303 * See: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html#kccb2 304 * for more details. 305 */ 306 if (ControllerConfig & PS2_CONFIG_SYSTEM) 307 StatusRegister |= PS2_STAT_SYSTEM; 308 else 309 StatusRegister &= ~PS2_STAT_SYSTEM; 310 311 /* 312 * Update the "Keyboard enable" flag of the status register 313 * with the "Ignore keyboard lock" flag of the controller 314 * configuration byte (if set), then reset the latter one. 315 * See: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html#kccb3 316 * for more details. 317 */ 318 if (ControllerConfig & PS2_CONFIG_NO_KEYLOCK) 319 { 320 ControllerConfig &= ~PS2_CONFIG_NO_KEYLOCK; 321 StatusRegister |= PS2_STAT_KBD_ENABLE; 322 } 323 324 break; 325 } 326 327 /* Write to byte N of internal memory */ 328 case 0x61: case 0x62: case 0x63: 329 case 0x64: case 0x65: case 0x66: case 0x67: 330 case 0x68: case 0x69: case 0x6A: case 0x6B: 331 case 0x6C: case 0x6D: case 0x6E: case 0x6F: 332 case 0x70: case 0x71: case 0x72: case 0x73: 333 case 0x74: case 0x75: case 0x76: case 0x77: 334 case 0x78: case 0x79: case 0x7A: case 0x7B: 335 case 0x7C: case 0x7D: case 0x7E: case 0x7F: 336 { 337 Memory[ControllerCommand & 0x1F] = Data; 338 break; 339 } 340 341 /* Write controller output */ 342 case 0xD1: 343 { 344 /* Check if bit 0 is unset */ 345 if (!(Data & PS2_OUT_CPU_NO_RESET)) 346 { 347 /* CPU disabled - Stop the VDM */ 348 EmulatorTerminate(); 349 return; 350 } 351 352 /* Update the A20 line setting */ 353 EmulatorSetA20(Data & PS2_OUT_A20_SET); 354 355 // FIXME: Should we need to add the status of IRQ1 and IRQ12?? 356 357 break; 358 } 359 360 /* Push the data byte into the first PS/2 port queue */ 361 case 0xD2: 362 { 363 PS2QueuePush(0, Data); 364 break; 365 } 366 367 /* Push the data byte into the second PS/2 port queue */ 368 case 0xD3: 369 { 370 PS2QueuePush(1, Data); 371 break; 372 } 373 374 /* 375 * Send a command to the second PS/2 port (by default 376 * it is a command for the first PS/2 port) 377 */ 378 case 0xD4: 379 { 380 PS2SendCommand(1, Data); 381 break; 382 } 383 } 384 385 return; 386 } 387 388 /* By default, send a command to the first PS/2 port */ 389 PS2SendCommand(0, Data); 390 } 391 392 static VOID FASTCALL GeneratePS2Irq(ULONGLONG ElapsedTime) 393 { 394 UNREFERENCED_PARAMETER(ElapsedTime); 395 396 /* 397 * Pop out fresh new data from the PS/2 port output queues, and only 398 * in case there is data ready, generate an IRQ1 or IRQ12 depending 399 * on whether interrupts are enabled for the given port. 400 * 401 * NOTE: The first PS/2 port (keyboard) has priority over the second one (mouse). 402 */ 403 if (PS2PortQueueRead(0)) 404 { 405 if (ControllerConfig & PS2_CONFIG_KBD_INT) PicInterruptRequest(1); 406 } 407 else if (PS2PortQueueRead(1)) 408 { 409 if (ControllerConfig & PS2_CONFIG_AUX_INT) PicInterruptRequest(12); 410 } 411 } 412 413 /* PUBLIC FUNCTIONS ***********************************************************/ 414 415 BOOLEAN PS2PortQueueRead(BYTE PS2Port) 416 { 417 BOOLEAN Result = FALSE; 418 PPS2_PORT Port; 419 420 // NOTE: The first PS/2 port (keyboard) has priority over the second one (mouse). 421 422 Port = &Ports[PS2Port]; 423 424 if (!Port->IsEnabled) return FALSE; 425 426 /* Make sure the queue is not empty (fast check) */ 427 if (Port->QueueEmpty) 428 { 429 /* Only the keyboard should have its last data latched */ 430 // FIXME: Alternatively this can be done in PS2ReadData when 431 // we read PS2_DATA_PORT. What is the best solution?? 432 if (PS2Port == 0) 433 { 434 OutputBuffer = Port->Queue[(Port->QueueStart - 1) % BUFFER_SIZE]; 435 StatusRegister &= ~PS2_STAT_AUX_OUT_BUF_FULL; // Clear flag: keyboard data 436 } 437 438 return FALSE; 439 } 440 441 WaitForSingleObject(Port->QueueMutex, INFINITE); 442 443 /* 444 * Recheck whether the queue is not empty (it may 445 * have changed after having grabbed the mutex). 446 */ 447 if (Port->QueueEmpty) goto Done; 448 449 /* Get the data */ 450 OutputBuffer = Port->Queue[Port->QueueStart]; 451 452 // StatusRegister &= ~(PS2_STAT_AUX_OUT_BUF_FULL | PS2_STAT_OUT_BUF_FULL); 453 454 /* Always set the "Output buffer full" flag */ 455 StatusRegister |= PS2_STAT_OUT_BUF_FULL; 456 457 /* Set the "Auxiliary output buffer full" flag according to where the data came from */ 458 if (PS2Port == 0) 459 StatusRegister &= ~PS2_STAT_AUX_OUT_BUF_FULL; // Clear flag: keyboard data 460 else // if (PS2Port == 1) 461 StatusRegister |= PS2_STAT_AUX_OUT_BUF_FULL; // Set flag: mouse data 462 463 /* Remove the value from the queue */ 464 Port->QueueStart++; 465 Port->QueueStart %= BUFFER_SIZE; 466 467 /* Check if the queue is now empty */ 468 if (Port->QueueStart == Port->QueueEnd) 469 Port->QueueEmpty = TRUE; 470 471 Result = TRUE; 472 473 Done: 474 ReleaseMutex(Port->QueueMutex); 475 return Result; 476 } 477 478 VOID PS2SetDeviceCmdProc(BYTE PS2Port, LPVOID Param, PS2_DEVICE_CMDPROC DeviceCommand) 479 { 480 ASSERT(PS2Port < PS2_PORTS); 481 Ports[PS2Port].Param = Param; 482 Ports[PS2Port].DeviceCommand = DeviceCommand; 483 } 484 485 // PS2SendToPort 486 BOOLEAN PS2QueuePush(BYTE PS2Port, BYTE Data) 487 { 488 BOOLEAN Result = FALSE; 489 PPS2_PORT Port; 490 491 ASSERT(PS2Port < PS2_PORTS); 492 Port = &Ports[PS2Port]; 493 494 if (!Port->IsEnabled) return FALSE; 495 496 WaitForSingleObject(Port->QueueMutex, INFINITE); 497 498 /* Check if the queue is full */ 499 if (!Port->QueueEmpty && (Port->QueueStart == Port->QueueEnd)) 500 goto Done; 501 502 /* Insert the value in the queue */ 503 Port->Queue[Port->QueueEnd] = Data; 504 Port->QueueEnd++; 505 Port->QueueEnd %= BUFFER_SIZE; 506 507 /* The queue is not empty anymore */ 508 Port->QueueEmpty = FALSE; 509 510 /* Schedule the IRQ */ 511 EnableHardwareTimer(IrqTimer); 512 513 Result = TRUE; 514 515 Done: 516 ReleaseMutex(Port->QueueMutex); 517 return Result; 518 } 519 520 BOOLEAN PS2Initialize(VOID) 521 { 522 /* Initialize the PS/2 ports */ 523 Ports[0].IsEnabled = FALSE; 524 Ports[0].QueueEmpty = TRUE; 525 Ports[0].QueueStart = 0; 526 Ports[0].QueueEnd = 0; 527 Ports[0].QueueMutex = CreateMutex(NULL, FALSE, NULL); 528 529 Ports[1].IsEnabled = FALSE; 530 Ports[1].QueueEmpty = TRUE; 531 Ports[1].QueueStart = 0; 532 Ports[1].QueueEnd = 0; 533 Ports[1].QueueMutex = CreateMutex(NULL, FALSE, NULL); 534 535 /* Register the I/O Ports */ 536 RegisterIoPort(PS2_CONTROL_PORT, PS2ReadControl, PS2WriteControl); 537 RegisterIoPort(PS2_DATA_PORT , PS2ReadData , PS2WriteData ); 538 539 IrqTimer = CreateHardwareTimer(HARDWARE_TIMER_ONESHOT, 540 HZ_TO_NS(100), 541 GeneratePS2Irq); 542 543 return TRUE; 544 } 545 546 VOID PS2Cleanup(VOID) 547 { 548 DestroyHardwareTimer(IrqTimer); 549 550 CloseHandle(Ports[1].QueueMutex); 551 CloseHandle(Ports[0].QueueMutex); 552 } 553 554 /* EOF */ 555