ReactOS 0.4.15-dev-7942-gd23573b
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
33typedef struct _PS2_PORT
34{
36
42
46
47/*
48 * Port 1: Keyboard
49 * Port 2: Mouse
50 */
51#define PS2_PORTS 2
53
54static BYTE Memory[0x20]; // PS/2 Controller internal memory
55#define ControllerConfig Memory[0] // The configuration byte is byte 0
56
57static BYTE StatusRegister = 0x00; // Controller status register
58// static BYTE InputBuffer = 0x00; // PS/2 Input Buffer
59static BYTE OutputBuffer = 0x00; // PS/2 Output Buffer
60
61static 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)
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
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 */
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
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 {
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
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 {
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 */
241
242 /* Set IRQ1 state */
245 else
246 OutputBuffer &= ~PS2_OUT_IRQ01;
247
248 /* Set IRQ12 state */
251 else
252 OutputBuffer &= ~PS2_OUT_IRQ12;
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 {
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 {
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
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 */
319 {
320 ControllerConfig &= ~PS2_CONFIG_NO_KEYLOCK;
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 {
381 break;
382 }
383 }
384
385 return;
386 }
387
388 /* By default, send a command to the first PS/2 port */
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{
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
473Done:
474 ReleaseMutex(Port->QueueMutex);
475 return Result;
476}
477
479{
480 ASSERT(PS2Port < PS2_PORTS);
481 Ports[PS2Port].Param = Param;
482 Ports[PS2Port].DeviceCommand = DeviceCommand;
483}
484
485// PS2SendToPort
487{
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
515Done:
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 */
unsigned char BOOLEAN
#define HARDWARE_TIMER_ONESHOT
Definition: clock.h:16
#define HZ_TO_NS(Freq)
Definition: clock.h:20
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define CloseHandle
Definition: compat.h:739
#define INFINITE
Definition: serial.h:102
VOID EmulatorTerminate(VOID)
Definition: emulator.c:503
CPPORT Port[4]
Definition: headless.c:35
#define ASSERT(a)
Definition: mode.c:44
unsigned int UINT
Definition: ndis.h:50
#define FASTCALL
Definition: nt_native.h:50
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:317
unsigned short USHORT
Definition: pedump.c:61
static BYTE WINAPI PS2ReadControl(USHORT Port)
Definition: ps2.c:90
VOID PS2SetDeviceCmdProc(BYTE PS2Port, LPVOID Param, PS2_DEVICE_CMDPROC DeviceCommand)
Definition: ps2.c:478
BOOLEAN PS2PortQueueRead(BYTE PS2Port)
Definition: ps2.c:415
static BYTE ControllerCommand
Definition: ps2.c:61
BOOLEAN PS2QueuePush(BYTE PS2Port, BYTE Data)
Definition: ps2.c:486
static VOID FASTCALL GeneratePS2Irq(ULONGLONG ElapsedTime)
Definition: ps2.c:392
static BYTE WINAPI PS2ReadData(USHORT Port)
Definition: ps2.c:109
struct _PS2_PORT * PPS2_PORT
static VOID WINAPI PS2WriteControl(USHORT Port, BYTE Data)
Definition: ps2.c:130
#define BUFFER_SIZE
Definition: ps2.c:31
#define PS2_PORTS
Definition: ps2.c:51
struct _PS2_PORT PS2_PORT
static VOID PS2SendCommand(BYTE PS2Port, BYTE Command)
Definition: ps2.c:67
static PHARDWARE_TIMER IrqTimer
Definition: ps2.c:63
#define ControllerConfig
Definition: ps2.c:55
static VOID WINAPI PS2WriteData(USHORT Port, BYTE Data)
Definition: ps2.c:278
static PS2_PORT Ports[PS2_PORTS]
Definition: ps2.c:52
BOOLEAN PS2Initialize(VOID)
Definition: ps2.c:520
VOID PS2Cleanup(VOID)
Definition: ps2.c:546
static BYTE OutputBuffer
Definition: ps2.c:59
static BYTE StatusRegister
Definition: ps2.c:57
#define PS2_OUT_AUX_DATA
Definition: ps2.h:41
#define PS2_STAT_OUT_BUF_FULL
Definition: ps2.h:20
#define PS2_OUT_A20_SET
Definition: ps2.h:40
#define PS2_CONFIG_NO_KEYLOCK
Definition: ps2.h:33
#define PS2_STAT_PARITY_ERROR
Definition: ps2.h:27
#define PS2_STAT_GEN_TIMEOUT
Definition: ps2.h:26
#define PS2_OUT_KBD_DATA
Definition: ps2.h:46
#define PS2_STAT_SYSTEM
Definition: ps2.h:22
VOID(WINAPI * PS2_DEVICE_CMDPROC)(LPVOID Param, BYTE Command)
Definition: ps2.h:48
#define PS2_CONFIG_AUX_INT
Definition: ps2.h:31
#define PS2_CONFIG_KBD_INT
Definition: ps2.h:30
#define PS2_CONFIG_AUX_DISABLE
Definition: ps2.h:35
#define PS2_DATA_PORT
Definition: ps2.h:16
#define PS2_CONFIG_KBD_DISABLE
Definition: ps2.h:34
#define PS2_OUT_CPU_NO_RESET
Definition: ps2.h:39
#define PS2_OUT_IRQ01
Definition: ps2.h:43
#define PS2_STAT_AUX_OUT_BUF_FULL
Definition: ps2.h:25
#define PS2_CONTROL_PORT
Definition: ps2.h:17
#define PS2_STAT_KBD_ENABLE
Definition: ps2.h:24
#define PS2_OUT_IRQ12
Definition: ps2.h:44
#define PS2_CONFIG_SYSTEM
Definition: ps2.h:32
#define PS2_STAT_COMMAND
Definition: ps2.h:23
Definition: shell.h:41
Definition: ps2.c:34
HANDLE QueueMutex
Definition: ps2.c:41
UINT QueueStart
Definition: ps2.c:39
UINT QueueEnd
Definition: ps2.c:40
BOOLEAN QueueEmpty
Definition: ps2.c:37
BOOLEAN IsEnabled
Definition: ps2.c:35
LPVOID Param
Definition: ps2.c:43
PS2_DEVICE_CMDPROC DeviceCommand
Definition: ps2.c:44
PHARDWARE_TIMER CreateHardwareTimer(ULONG Flags, ULONGLONG Delay, PHARDWARE_TIMER_PROC Callback)
Definition: clock.c:144
VOID DestroyHardwareTimer(PHARDWARE_TIMER Timer)
Definition: clock.c:210
VOID EnableHardwareTimer(PHARDWARE_TIMER Timer)
Definition: clock.c:161
VOID PicInterruptRequest(BYTE Number)
Definition: pic.c:192
VOID RegisterIoPort(USHORT Port, EMULATOR_INB_PROC InHandler, EMULATOR_OUTB_PROC OutHandler)
Definition: io.c:320
BOOLEAN EmulatorGetA20(VOID)
Definition: memory.c:280
VOID EmulatorSetA20(BOOLEAN Enabled)
Definition: memory.c:275
DWORD WINAPI WaitForSingleObject(IN HANDLE hHandle, IN DWORD dwMilliseconds)
Definition: synch.c:82
BOOL WINAPI DECLSPEC_HOTPATCH ReleaseMutex(IN HANDLE hMutex)
Definition: synch.c:618
uint64_t ULONGLONG
Definition: typedefs.h:67
_Must_inspect_result_ _In_ WDFDEVICE _In_ PIRP _In_ WDFQUEUE Queue
Definition: wdfdevice.h:2225
_Must_inspect_result_ _In_ WDFIOTARGET _In_opt_ WDFREQUEST _In_opt_ PWDF_MEMORY_DESCRIPTOR OutputBuffer
Definition: wdfiotarget.h:863
_Must_inspect_result_ _In_opt_ PWDF_OBJECT_ATTRIBUTES _In_ _Strict_type_match_ POOL_TYPE _In_opt_ ULONG _In_ _Out_ WDFMEMORY * Memory
Definition: wdfmemory.h:169
#define CreateMutex
Definition: winbase.h:3756
#define WINAPI
Definition: msvc.h:6
_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:409
unsigned char BYTE
Definition: xxhash.c:193