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