[NTVDM]
[reactos.git] / reactos / subsystems / ntvdm / hardware / ps2.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: 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
10 /* INCLUDES *******************************************************************/
11
12 #define NDEBUG
13
14 #include "emulator.h"
15 #include "io.h"
16 #include "ps2.h"
17 #include "pic.h"
18
19 #include "keyboard.h"
20 #include "mouse.h"
21
22 /* PRIVATE VARIABLES **********************************************************/
23
24 #define BUFFER_SIZE 32
25
26 typedef struct _PS2_PORT
27 {
28 BOOLEAN IsEnabled;
29
30 BOOLEAN QueueEmpty;
31 BYTE Queue[BUFFER_SIZE];
32 UINT QueueStart;
33 UINT QueueEnd;
34 HANDLE QueueMutex;
35 } PS2_PORT, *PPS2_PORT;
36
37 /*
38 * Port 1: Keyboard
39 * Port 2: Mouse
40 */
41 #define PS2_PORTS 2
42 static PS2_PORT Ports[PS2_PORTS];
43
44 #define PS2_DEFAULT_CONFIG 0x47
45 static BYTE ControllerConfig = PS2_DEFAULT_CONFIG;
46 static BYTE ControllerCommand = 0x00;
47
48 static BYTE StatusRegister = 0x00;
49 // static BYTE InputBuffer = 0x00; // PS/2 Input Buffer
50 static BYTE OutputBuffer = 0x00; // PS/2 Output Buffer
51
52 /* PRIVATE FUNCTIONS **********************************************************/
53
54 static BYTE WINAPI PS2ReadPort(ULONG Port)
55 {
56 if (Port == PS2_CONTROL_PORT)
57 {
58 /* Be sure bit 2 is always set */
59 StatusRegister |= 1 << 2;
60
61 // FIXME: Should clear bits 6 and 7 because there are
62 // no timeouts and no parity errors.
63
64 return StatusRegister;
65 }
66 else if (Port == PS2_DATA_PORT)
67 {
68 /*
69 * If there is something to read (response byte from the
70 * controller or data from a PS/2 device), read it.
71 */
72 if (StatusRegister & (1 << 0)) // || StatusRegister & (1 << 5) for second PS/2 port
73 StatusRegister &= ~(1 << 0); // StatusRegister &= ~(1 << 5);
74
75 /* Always return the available byte stored in the output buffer */
76 return OutputBuffer;
77 }
78
79 return 0;
80 }
81
82 static VOID WINAPI PS2WritePort(ULONG Port, BYTE Data)
83 {
84 if (Port == PS2_CONTROL_PORT)
85 {
86 switch (Data)
87 {
88 /* Read configuration byte */
89 case 0x20:
90 {
91 OutputBuffer = ControllerConfig;
92 StatusRegister |= (1 << 0); // There is something to read
93 break;
94 }
95
96 /* Write configuration byte */
97 case 0x60:
98 /* Write controller output port */
99 case 0xD1:
100 /* Write to the first PS/2 port output buffer */
101 case 0xD2:
102 /* Write to the second PS/2 port output buffer */
103 case 0xD3:
104 /* Write to the second PS/2 port input buffer */
105 case 0xD4:
106 {
107 /* These commands require a response */
108 ControllerCommand = Data;
109 StatusRegister |= (1 << 3); // This is a controller command
110 break;
111 }
112
113 /* Disable second PS/2 port */
114 case 0xA7:
115 {
116 Ports[1].IsEnabled = FALSE;
117 break;
118 }
119
120 /* Enable second PS/2 port */
121 case 0xA8:
122 {
123 Ports[1].IsEnabled = TRUE;
124 break;
125 }
126
127 /* Test second PS/2 port */
128 case 0xA9:
129 {
130 OutputBuffer = 0x00; // Success code
131 StatusRegister |= (1 << 0); // There is something to read
132 break;
133 }
134
135 /* Test PS/2 controller */
136 case 0xAA:
137 {
138 OutputBuffer = 0x55; // Success code
139 StatusRegister |= (1 << 0); // There is something to read
140 break;
141 }
142
143 /* Test first PS/2 port */
144 case 0xAB:
145 {
146 OutputBuffer = 0x00; // Success code
147 StatusRegister |= (1 << 0); // There is something to read
148 break;
149 }
150
151 /* Disable first PS/2 port */
152 case 0xAD:
153 {
154 Ports[0].IsEnabled = FALSE;
155 break;
156 }
157
158 /* Enable first PS/2 port */
159 case 0xAE:
160 {
161 Ports[0].IsEnabled = TRUE;
162 break;
163 }
164
165 /* Read controller output port */
166 case 0xD0:
167 {
168 // TODO: Not implemented
169 break;
170 }
171
172 /* CPU Reset */
173 case 0xF0:
174 case 0xF2:
175 case 0xF4:
176 case 0xF6:
177 case 0xF8:
178 case 0xFA:
179 case 0xFC:
180 case 0xFE:
181 {
182 /* Stop the VDM */
183 EmulatorTerminate();
184 break;
185 }
186 }
187 }
188 else if (Port == PS2_DATA_PORT)
189 {
190 /* Check if the controller is waiting for a response */
191 if (StatusRegister & (1 << 3)) // If we have data for the controller
192 {
193 StatusRegister &= ~(1 << 3);
194
195 /* Check which command it was */
196 switch (ControllerCommand)
197 {
198 /* Write configuration byte */
199 case 0x60:
200 {
201 ControllerConfig = Data;
202 break;
203 }
204
205 /* Write controller output */
206 case 0xD1:
207 {
208 /* Check if bit 0 is unset */
209 if (!(Data & (1 << 0)))
210 {
211 /* CPU disabled - Stop the VDM */
212 EmulatorTerminate();
213 }
214
215 /* Update the A20 line setting */
216 EmulatorSetA20(Data & (1 << 1));
217
218 break;
219 }
220
221 /* Push the data byte into the first PS/2 port queue */
222 case 0xD2:
223 {
224 PS2QueuePush(0, Data);
225 break;
226 }
227
228 /* Push the data byte into the second PS/2 port queue */
229 case 0xD3:
230 {
231 PS2QueuePush(1, Data);
232 break;
233 }
234
235 /*
236 * Send a command to the second PS/2 port (by default
237 * it is a command for the first PS/2 port)
238 */
239 case 0xD4:
240 {
241 if (Ports[1].IsEnabled)
242 // Ports[1].Function
243 MouseCommand(Data);
244
245 break;
246 }
247 }
248
249 return;
250 }
251
252 // TODO: Implement PS/2 device commands
253 if (Ports[0].IsEnabled)
254 // Ports[0].Function
255 KeyboardCommand(Data);
256 }
257 }
258
259 static BOOLEAN PS2PortQueueRead(BYTE PS2Port)
260 {
261 BOOLEAN Result = TRUE;
262 PPS2_PORT Port;
263
264 if (PS2Port >= PS2_PORTS) return FALSE;
265 Port = &Ports[PS2Port];
266
267 if (!Port->IsEnabled) return FALSE;
268
269 /* Make sure the queue is not empty (fast check) */
270 if (Port->QueueEmpty) return FALSE;
271
272 WaitForSingleObject(Port->QueueMutex, INFINITE);
273
274 /*
275 * Recheck whether the queue is not empty (it may
276 * have changed after having grabbed the mutex).
277 */
278 if (Port->QueueEmpty)
279 {
280 Result = FALSE;
281 goto Done;
282 }
283
284 /* Get the data */
285 OutputBuffer = Port->Queue[Port->QueueStart];
286 StatusRegister |= (1 << 0); // There is something to read
287 // Sometimes StatusRegister |= (1 << 5); for the second PS/2 port
288
289 /* Remove the value from the queue */
290 Port->QueueStart++;
291 Port->QueueStart %= BUFFER_SIZE;
292
293 /* Check if the queue is now empty */
294 if (Port->QueueStart == Port->QueueEnd)
295 Port->QueueEmpty = TRUE;
296
297 Done:
298 ReleaseMutex(Port->QueueMutex);
299 return Result;
300 }
301
302 /* PUBLIC FUNCTIONS ***********************************************************/
303
304 // PS2SendToPort
305 BOOLEAN PS2QueuePush(BYTE PS2Port, BYTE Data)
306 {
307 BOOLEAN Result = TRUE;
308 PPS2_PORT Port;
309
310 if (PS2Port >= PS2_PORTS) return FALSE;
311 Port = &Ports[PS2Port];
312
313 if (!Port->IsEnabled) return FALSE;
314
315 WaitForSingleObject(Port->QueueMutex, INFINITE);
316
317 /* Check if the queue is full */
318 if (!Port->QueueEmpty && (Port->QueueStart == Port->QueueEnd))
319 {
320 Result = FALSE;
321 goto Done;
322 }
323
324 /* Insert the value in the queue */
325 Port->Queue[Port->QueueEnd] = Data;
326 Port->QueueEnd++;
327 Port->QueueEnd %= BUFFER_SIZE;
328
329 /* Since we inserted a value, it's not empty anymore */
330 Port->QueueEmpty = FALSE;
331
332 /*
333 // Get the data
334 OutputBuffer = Port->Queue[Port->QueueStart];
335 StatusRegister |= (1 << 0); // There is something to read
336 // FIXME: Sometimes StatusRegister |= (1 << 5); for the second PS/2 port
337
338 if (PS2Port == 0)
339 PicInterruptRequest(1);
340 else if (PS2Port == 1)
341 PicInterruptRequest(12);
342 */
343
344 Done:
345 ReleaseMutex(Port->QueueMutex);
346 return Result;
347 }
348
349 VOID GenerateIrq1(VOID)
350 {
351 /* Generate an interrupt if interrupts for the first PS/2 port are enabled */
352 if (ControllerConfig & 0x01)
353 {
354 /* Generate an IRQ 1 if there is data ready in the output queue */
355 if (PS2PortQueueRead(0)) PicInterruptRequest(1);
356 }
357 }
358
359 VOID GenerateIrq12(VOID)
360 {
361 /* Generate an interrupt if interrupts for the second PS/2 port are enabled */
362 if (ControllerConfig & 0x02)
363 {
364 /* Generate an IRQ 12 if there is data ready in the output queue */
365 if (PS2PortQueueRead(1)) PicInterruptRequest(12);
366 }
367 }
368
369 BOOLEAN PS2Initialize(VOID)
370 {
371 /* Initialize the PS/2 ports */
372 Ports[0].IsEnabled = TRUE;
373 Ports[0].QueueEmpty = TRUE;
374 Ports[0].QueueStart = 0;
375 Ports[0].QueueEnd = 0;
376 Ports[0].QueueMutex = CreateMutex(NULL, FALSE, NULL);
377
378 Ports[1].IsEnabled = TRUE;
379 Ports[1].QueueEmpty = TRUE;
380 Ports[1].QueueStart = 0;
381 Ports[1].QueueEnd = 0;
382 Ports[1].QueueMutex = CreateMutex(NULL, FALSE, NULL);
383
384 /* Register the I/O Ports */
385 RegisterIoPort(PS2_CONTROL_PORT, PS2ReadPort, PS2WritePort);
386 RegisterIoPort(PS2_DATA_PORT , PS2ReadPort, PS2WritePort);
387
388 return TRUE;
389 }
390
391 VOID PS2Cleanup(VOID)
392 {
393 CloseHandle(Ports[1].QueueMutex);
394 CloseHandle(Ports[0].QueueMutex);
395 }
396
397 /* EOF */