6d18b9da8974367616dda5854e55fba74be6deb3
[reactos.git] / subsystems / mvdm / ntvdm / hardware / keyboard.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/hardware/keyboard.c
5 * PURPOSE: Keyboard emulation
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "ntvdm.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 #include "keyboard.h"
17 #include "ps2.h"
18
19 /* PRIVATE VARIABLES **********************************************************/
20
21 static BOOLEAN Reporting = FALSE;
22 static BYTE KeyboardId = 0; // We only support basic old-type keyboard
23 static BYTE DataByteWait = 0;
24
25 static BYTE PS2Port = 0;
26
27 /* PRIVATE FUNCTIONS **********************************************************/
28
29 static VOID WINAPI KeyboardCommand(LPVOID Param, BYTE Command)
30 {
31 /* Check if we were waiting for a data byte */
32 if (DataByteWait)
33 {
34 PS2QueuePush(PS2Port, KEYBOARD_ACK);
35
36 switch (DataByteWait)
37 {
38 /* Set/Reset Mode Indicators */
39 case 0xED:
40 {
41 // Ignore setting the keyboard LEDs
42 break;
43 }
44
45 /* PS/2 Select/Read Alternate Scan Code Sets */
46 case 0xF0:
47 /* Set Typematic Rate/Delay */
48 case 0xF3:
49 {
50 // FIXME: UNIMPLEMENTED; just return ACKnowledge.
51 // This unblocks some programs that want to initialize
52 // the keyboard by sending keyboard commands and then
53 // performing polling on the port until "valid" data
54 // comes out.
55 DPRINT1("KeyboardCommand(0x%02X) NOT IMPLEMENTED\n", DataByteWait);
56 break;
57 }
58
59 default:
60 {
61 /* Shouldn't happen */
62 ASSERT(FALSE);
63 }
64 }
65
66 DataByteWait = 0;
67 return;
68 }
69
70 switch (Command)
71 {
72 /* Set/Reset Mode Indicators */
73 case 0xED:
74 /* PS/2 Select/Read Alternate Scan Code Sets */
75 case 0xF0:
76 /* Set Typematic Rate/Delay */
77 case 0xF3:
78 {
79 DataByteWait = Command;
80 PS2QueuePush(PS2Port, KEYBOARD_ACK);
81 break;
82 }
83
84 /* Echo test command */
85 case 0xEE:
86 {
87 PS2QueuePush(PS2Port, 0xEE);
88 break;
89 }
90
91 /* Get Keyboard ID */
92 case 0xF2:
93 {
94 PS2QueuePush(PS2Port, KEYBOARD_ACK);
95 PS2QueuePush(PS2Port, KeyboardId);
96 break;
97 }
98
99 /* Enable Reporting */
100 case 0xF4:
101 {
102 Reporting = TRUE;
103 PS2QueuePush(PS2Port, KEYBOARD_ACK);
104 break;
105 }
106
107 /* Disable Reporting */
108 case 0xF5:
109 {
110 Reporting = FALSE;
111 PS2QueuePush(PS2Port, KEYBOARD_ACK);
112 break;
113 }
114
115 /* Set Defaults */
116 case 0xF6:
117 {
118 // So far, nothing to reset
119 PS2QueuePush(PS2Port, KEYBOARD_ACK);
120 break;
121 }
122
123 /* PS/2 Typematic & Make/Break key modes */
124 case 0xF7: case 0xF8: case 0xF9:
125 case 0xFA: case 0xFB: case 0xFC: case 0xFD:
126 {
127 /*
128 * Unsupported on PC-AT, they are just ignored
129 * and acknowledged as discussed in:
130 * http://stanislavs.org/helppc/keyboard_commands.html
131 */
132 PS2QueuePush(PS2Port, KEYBOARD_ACK);
133 }
134
135 /* Resend */
136 case 0xFE:
137 {
138 PS2QueuePush(PS2Port, KEYBOARD_ACK);
139 UNIMPLEMENTED;
140 break;
141 }
142
143 /* Reset */
144 case 0xFF:
145 {
146 /* Send ACKnowledge */
147 PS2QueuePush(PS2Port, KEYBOARD_ACK);
148
149 // So far, nothing to reset
150
151 /* Send the Basic Assurance Test success code and the device ID */
152 PS2QueuePush(PS2Port, KEYBOARD_BAT_SUCCESS);
153 PS2QueuePush(PS2Port, KeyboardId);
154 break;
155 }
156
157 /* Unknown command */
158 default:
159 {
160 PS2QueuePush(PS2Port, KEYBOARD_ERROR);
161 }
162 }
163 }
164
165 /* PUBLIC FUNCTIONS ***********************************************************/
166
167 VOID KeyboardEventHandler(PKEY_EVENT_RECORD KeyEvent)
168 {
169 WORD i;
170 BYTE ScanCode = (BYTE)KeyEvent->wVirtualScanCode;
171
172 /* Check if we're not reporting */
173 if (!Reporting) return;
174
175 /* If this is a key release, set the highest bit in the scan code */
176 if (!KeyEvent->bKeyDown) ScanCode |= 0x80;
177
178 /* Push the scan code into the PS/2 queue */
179 for (i = 0; i < KeyEvent->wRepeatCount; i++)
180 {
181 if (KeyEvent->dwControlKeyState & ENHANCED_KEY) PS2QueuePush(PS2Port, 0xE0);
182 PS2QueuePush(PS2Port, ScanCode);
183 }
184
185 DPRINT("Press 0x%X\n", ScanCode);
186 }
187
188 BOOLEAN KeyboardInit(BYTE PS2Connector)
189 {
190 /* Finish to plug the keyboard to the specified PS/2 port */
191 PS2Port = PS2Connector;
192 PS2SetDeviceCmdProc(PS2Port, NULL, KeyboardCommand);
193 return TRUE;
194 }