ReactOS  0.4.14-dev-114-gc8cbd56
ps2.c
Go to the documentation of this file.
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 {
36 
42 
46 
47 /*
48  * Port 1: Keyboard
49  * Port 2: Mouse
50  */
51 #define PS2_PORTS 2
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 
64 
65 /* PRIVATE FUNCTIONS **********************************************************/
66 
68 {
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)
80  else // if (PS2Port == 1)
82 
83  Port->IsEnabled = TRUE;
84 
85  /* Call the device command */
86  if (Port->DeviceCommand) Port->DeviceCommand(Port->Param, Command);
87 }
88 
89 
91 {
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  */
102 
103  /* We do not have any timeouts nor parity errors */
105 
106  return StatusRegister;
107 }
108 
110 {
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  */
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 
131 {
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];
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 */
177  break;
178  }
179 
180  /* Disable second PS/2 port */
181  case 0xA7:
182  {
184  Ports[1].IsEnabled = FALSE;
185  break;
186  }
187 
188  /* Enable second PS/2 port */
189  case 0xA8:
190  {
192  Ports[1].IsEnabled = TRUE;
193  break;
194  }
195 
196  /* Test second PS/2 port */
197  case 0xA9:
198  {
199  OutputBuffer = 0x00; // Success code
201  break;
202  }
203 
204  /* Test PS/2 controller */
205  case 0xAA:
206  {
207  OutputBuffer = 0x55; // Success code
209  break;
210  }
211 
212  /* Test first PS/2 port */
213  case 0xAB:
214  {
215  OutputBuffer = 0x00; // Success code
217  break;
218  }
219 
220  /* Disable first PS/2 port */
221  case 0xAD:
222  {
224  Ports[0].IsEnabled = FALSE;
225  break;
226  }
227 
228  /* Enable first PS/2 port */
229  case 0xAE:
230  {
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 */
241 
242  /* Set IRQ1 state */
245  else
247 
248  /* Set IRQ12 state */
251  else
253 
254  /* Check whether data is already present */
256  {
259  else
261  }
262 
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 */
273  return;
274  }
275  }
276 }
277 
279 {
280  /* Check if the controller is waiting for a response */
282  {
284 
285  /* Check which command it was */
286  switch (ControllerCommand)
287  {
288  /* Write configuration byte (byte 0 from internal memory) */
289  case 0x60:
290  {
292 
293  /*
294  * Synchronize the enable state of the PS/2 ports
295  * with the flags in the configuration byte.
296  */
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  */
308  else
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  */
319  {
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 */
349  return;
350  }
351 
352  /* Update the A20 line setting */
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 
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  {
406  }
407  else if (PS2PortQueueRead(1))
408  {
410  }
411 }
412 
413 /* PUBLIC FUNCTIONS ***********************************************************/
414 
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 */
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
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 */
512 
513  Result = TRUE;
514 
515 Done:
516  ReleaseMutex(Port->QueueMutex);
517  return Result;
518 }
519 
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;
528 
529  Ports[1].IsEnabled = FALSE;
530  Ports[1].QueueEmpty = TRUE;
531  Ports[1].QueueStart = 0;
532  Ports[1].QueueEnd = 0;
534 
535  /* Register the I/O Ports */
538 
540  HZ_TO_NS(100),
542 
543  return TRUE;
544 }
545 
547 {
549 
550  CloseHandle(Ports[1].QueueMutex);
551  CloseHandle(Ports[0].QueueMutex);
552 }
553 
554 /* EOF */
static BYTE Memory[0x20]
Definition: ps2.c:54
#define PS2_DATA_PORT
Definition: ps2.h:16
static BYTE StatusRegister
Definition: ps2.c:57
CPPORT Port[4]
Definition: headless.c:34
struct _PS2_PORT PS2_PORT
#define TRUE
Definition: types.h:120
UINT QueueStart
Definition: ps2.c:39
#define CloseHandle
Definition: compat.h:398
BOOLEAN EmulatorGetA20(VOID)
Definition: memory.c:277
#define PS2_OUT_A20_SET
Definition: ps2.h:40
#define PS2_PORTS
Definition: ps2.c:51
static BYTE WINAPI PS2ReadData(USHORT Port)
Definition: ps2.c:109
#define PS2_STAT_KBD_ENABLE
Definition: ps2.h:24
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:323
#define PS2_CONFIG_KBD_DISABLE
Definition: ps2.h:34
#define PS2_STAT_COMMAND
Definition: ps2.h:23
#define PS2_CONFIG_NO_KEYLOCK
Definition: ps2.h:33
static BYTE ControllerCommand
Definition: ps2.c:61
BOOLEAN PS2QueuePush(BYTE PS2Port, BYTE Data)
Definition: ps2.c:486
Definition: shell.h:41
#define ControllerConfig
Definition: ps2.c:55
#define FASTCALL
Definition: nt_native.h:50
#define PS2_CONFIG_SYSTEM
Definition: ps2.h:32
#define PS2_OUT_KBD_DATA
Definition: ps2.h:46
BOOLEAN QueueEmpty
Definition: ps2.c:37
VOID RegisterIoPort(USHORT Port, EMULATOR_INB_PROC InHandler, EMULATOR_OUTB_PROC OutHandler)
Definition: io.c:320
static VOID FASTCALL GeneratePS2Irq(ULONGLONG ElapsedTime)
Definition: ps2.c:392
DWORD WINAPI WaitForSingleObject(IN HANDLE hHandle, IN DWORD dwMilliseconds)
Definition: synch.c:82
static VOID WINAPI PS2WriteControl(USHORT Port, BYTE Data)
Definition: ps2.c:130
#define BUFFER_SIZE
Definition: ps2.c:31
BOOLEAN IsEnabled
Definition: ps2.c:35
BYTE Queue[BUFFER_SIZE]
Definition: ps2.c:38
PS2_DEVICE_CMDPROC DeviceCommand
Definition: ps2.c:44
unsigned char BOOLEAN
PHARDWARE_TIMER CreateHardwareTimer(ULONG Flags, ULONGLONG Delay, PHARDWARE_TIMER_PROC Callback)
Definition: clock.c:144
smooth NULL
Definition: ftsmooth.c:416
_At_(*)(_In_ PWSK_CLIENT Client, _In_opt_ PUNICODE_STRING NodeName, _In_opt_ PUNICODE_STRING ServiceName, _In_opt_ ULONG NameSpace, _In_opt_ GUID *Provider, _In_opt_ PADDRINFOEXW Hints, _Outptr_ PADDRINFOEXW *Result, _In_opt_ PEPROCESS OwningProcess, _In_opt_ PETHREAD OwningThread, _Inout_ PIRP Irp Result)(Mem)) NTSTATUS(WSKAPI *PFN_WSK_GET_ADDRESS_INFO
Definition: wsk.h:426
_In_ LPGUID _In_ PVOID Data
Definition: classpnp.h:778
#define PS2_OUT_IRQ12
Definition: ps2.h:44
VOID PS2Cleanup(VOID)
Definition: ps2.c:546
BOOLEAN PS2PortQueueRead(BYTE PS2Port)
Definition: ps2.c:415
#define PS2_STAT_PARITY_ERROR
Definition: ps2.h:27
#define CreateMutex
Definition: winbase.h:3580
#define PS2_OUT_AUX_DATA
Definition: ps2.h:41
Definition: ps2.c:33
uint64_t ULONGLONG
Definition: typedefs.h:65
#define PS2_CONFIG_AUX_DISABLE
Definition: ps2.h:35
VOID EmulatorSetA20(BOOLEAN Enabled)
Definition: memory.c:272
#define WINAPI
Definition: msvc.h:8
#define PS2_CONFIG_AUX_INT
Definition: ps2.h:31
static PS2_PORT Ports[PS2_PORTS]
Definition: ps2.c:52
ASSERT((InvokeOnSuccess||InvokeOnError||InvokeOnCancel) ?(CompletionRoutine !=NULL) :TRUE)
#define HARDWARE_TIMER_ONESHOT
Definition: clock.h:16
BOOL WINAPI DECLSPEC_HOTPATCH ReleaseMutex(IN HANDLE hMutex)
Definition: synch.c:618
#define PS2_CONTROL_PORT
Definition: ps2.h:17
static BYTE OutputBuffer
Definition: ps2.c:59
static VOID WINAPI PS2WriteData(USHORT Port, BYTE Data)
Definition: ps2.c:278
unsigned char BYTE
Definition: mem.h:68
VOID EmulatorTerminate(VOID)
Definition: emulator.c:503
#define PS2_OUT_IRQ01
Definition: ps2.h:43
unsigned short USHORT
Definition: pedump.c:61
static VOID PS2SendCommand(BYTE PS2Port, BYTE Command)
Definition: ps2.c:67
unsigned int UINT
Definition: ndis.h:50
struct _PS2_PORT * PPS2_PORT
#define PS2_STAT_OUT_BUF_FULL
Definition: ps2.h:20
#define PS2_CONFIG_KBD_INT
Definition: ps2.h:30
#define PS2_OUT_CPU_NO_RESET
Definition: ps2.h:39
VOID PS2SetDeviceCmdProc(BYTE PS2Port, LPVOID Param, PS2_DEVICE_CMDPROC DeviceCommand)
Definition: ps2.c:478
#define HZ_TO_NS(Freq)
Definition: clock.h:20
LPVOID Param
Definition: ps2.c:43
VOID PicInterruptRequest(BYTE Number)
Definition: pic.c:192
BOOLEAN PS2Initialize(VOID)
Definition: ps2.c:520
VOID EnableHardwareTimer(PHARDWARE_TIMER Timer)
Definition: clock.c:161
static BYTE WINAPI PS2ReadControl(USHORT Port)
Definition: ps2.c:90
#define INFINITE
Definition: serial.h:102
#define PS2_STAT_AUX_OUT_BUF_FULL
Definition: ps2.h:25
#define PS2_STAT_GEN_TIMEOUT
Definition: ps2.h:26
HANDLE QueueMutex
Definition: ps2.c:41
VOID DestroyHardwareTimer(PHARDWARE_TIMER Timer)
Definition: clock.c:210
UINT QueueEnd
Definition: ps2.c:40
#define PS2_STAT_SYSTEM
Definition: ps2.h:22
static PHARDWARE_TIMER IrqTimer
Definition: ps2.c:63
VOID(WINAPI * PS2_DEVICE_CMDPROC)(LPVOID Param, BYTE Command)
Definition: ps2.h:48