4222cc5ed8af81161140ff05c5d539fb88914e7a
[reactos.git] / reactos / subsystems / mvdm / ntvdm / hardware / ps2.c
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 */