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