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