2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
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)
10 /* INCLUDES *******************************************************************/
22 /* PRIVATE VARIABLES **********************************************************/
24 #define BUFFER_SIZE 32
26 typedef struct _PS2_PORT
31 BYTE Queue
[BUFFER_SIZE
];
35 } PS2_PORT
, *PPS2_PORT
;
42 static PS2_PORT Ports
[PS2_PORTS
];
44 #define PS2_DEFAULT_CONFIG 0x47
45 static BYTE ControllerConfig
= PS2_DEFAULT_CONFIG
;
46 static BYTE ControllerCommand
= 0x00;
48 static BYTE StatusRegister
= 0x00;
49 // static BYTE InputBuffer = 0x00; // PS/2 Input Buffer
50 static BYTE OutputBuffer
= 0x00; // PS/2 Output Buffer
52 /* PRIVATE FUNCTIONS **********************************************************/
54 static BYTE WINAPI
PS2ReadPort(ULONG Port
)
56 if (Port
== PS2_CONTROL_PORT
)
58 /* Be sure bit 2 is always set */
59 StatusRegister
|= 1 << 2;
61 // FIXME: Should clear bits 6 and 7 because there are
62 // no timeouts and no parity errors.
64 return StatusRegister
;
66 else if (Port
== PS2_DATA_PORT
)
69 * If there is something to read (response byte from the
70 * controller or data from a PS/2 device), read it.
72 if (StatusRegister
& (1 << 0)) // || StatusRegister & (1 << 5) for second PS/2 port
73 StatusRegister
&= ~(1 << 0); // StatusRegister &= ~(1 << 5);
75 /* Always return the available byte stored in the output buffer */
82 static VOID WINAPI
PS2WritePort(ULONG Port
, BYTE Data
)
84 if (Port
== PS2_CONTROL_PORT
)
88 /* Read configuration byte */
91 OutputBuffer
= ControllerConfig
;
92 StatusRegister
|= (1 << 0); // There is something to read
96 /* Write configuration byte */
98 /* Write controller output port */
100 /* Write to the first PS/2 port output buffer */
102 /* Write to the second PS/2 port output buffer */
104 /* Write to the second PS/2 port input buffer */
107 /* These commands require a response */
108 ControllerCommand
= Data
;
109 StatusRegister
|= (1 << 3); // This is a controller command
113 /* Disable second PS/2 port */
116 Ports
[1].IsEnabled
= FALSE
;
120 /* Enable second PS/2 port */
123 Ports
[1].IsEnabled
= TRUE
;
127 /* Test second PS/2 port */
130 OutputBuffer
= 0x00; // Success code
131 StatusRegister
|= (1 << 0); // There is something to read
135 /* Test PS/2 controller */
138 OutputBuffer
= 0x55; // Success code
139 StatusRegister
|= (1 << 0); // There is something to read
143 /* Test first PS/2 port */
146 OutputBuffer
= 0x00; // Success code
147 StatusRegister
|= (1 << 0); // There is something to read
151 /* Disable first PS/2 port */
154 Ports
[0].IsEnabled
= FALSE
;
158 /* Enable first PS/2 port */
161 Ports
[0].IsEnabled
= TRUE
;
165 /* Read controller output port */
168 // TODO: Not implemented
188 else if (Port
== PS2_DATA_PORT
)
190 /* Check if the controller is waiting for a response */
191 if (StatusRegister
& (1 << 3)) // If we have data for the controller
193 StatusRegister
&= ~(1 << 3);
195 /* Check which command it was */
196 switch (ControllerCommand
)
198 /* Write configuration byte */
201 ControllerConfig
= Data
;
205 /* Write controller output */
208 /* Check if bit 0 is unset */
209 if (!(Data
& (1 << 0)))
211 /* CPU disabled - Stop the VDM */
215 /* Update the A20 line setting */
216 EmulatorSetA20(Data
& (1 << 1));
221 /* Push the data byte into the first PS/2 port queue */
224 PS2QueuePush(0, Data
);
228 /* Push the data byte into the second PS/2 port queue */
231 PS2QueuePush(1, Data
);
236 * Send a command to the second PS/2 port (by default
237 * it is a command for the first PS/2 port)
241 if (Ports
[1].IsEnabled
)
252 // TODO: Implement PS/2 device commands
253 if (Ports
[0].IsEnabled
)
255 KeyboardCommand(Data
);
259 static BOOLEAN
PS2PortQueueRead(BYTE PS2Port
)
261 BOOLEAN Result
= TRUE
;
264 if (PS2Port
>= PS2_PORTS
) return FALSE
;
265 Port
= &Ports
[PS2Port
];
267 if (!Port
->IsEnabled
) return FALSE
;
269 /* Make sure the queue is not empty (fast check) */
270 if (Port
->QueueEmpty
) return FALSE
;
272 WaitForSingleObject(Port
->QueueMutex
, INFINITE
);
275 * Recheck whether the queue is not empty (it may
276 * have changed after having grabbed the mutex).
278 if (Port
->QueueEmpty
)
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
289 /* Remove the value from the queue */
291 Port
->QueueStart
%= BUFFER_SIZE
;
293 /* Check if the queue is now empty */
294 if (Port
->QueueStart
== Port
->QueueEnd
)
295 Port
->QueueEmpty
= TRUE
;
298 ReleaseMutex(Port
->QueueMutex
);
302 /* PUBLIC FUNCTIONS ***********************************************************/
305 BOOLEAN
PS2QueuePush(BYTE PS2Port
, BYTE Data
)
307 BOOLEAN Result
= TRUE
;
310 if (PS2Port
>= PS2_PORTS
) return FALSE
;
311 Port
= &Ports
[PS2Port
];
313 if (!Port
->IsEnabled
) return FALSE
;
315 WaitForSingleObject(Port
->QueueMutex
, INFINITE
);
317 /* Check if the queue is full */
318 if (!Port
->QueueEmpty
&& (Port
->QueueStart
== Port
->QueueEnd
))
324 /* Insert the value in the queue */
325 Port
->Queue
[Port
->QueueEnd
] = Data
;
327 Port
->QueueEnd
%= BUFFER_SIZE
;
329 /* Since we inserted a value, it's not empty anymore */
330 Port
->QueueEmpty
= FALSE
;
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
339 PicInterruptRequest(1);
340 else if (PS2Port == 1)
341 PicInterruptRequest(12);
345 ReleaseMutex(Port
->QueueMutex
);
349 VOID
GenerateIrq1(VOID
)
351 /* Generate an interrupt if interrupts for the first PS/2 port are enabled */
352 if (ControllerConfig
& 0x01)
354 /* Generate an IRQ 1 if there is data ready in the output queue */
355 if (PS2PortQueueRead(0)) PicInterruptRequest(1);
359 VOID
GenerateIrq12(VOID
)
361 /* Generate an interrupt if interrupts for the second PS/2 port are enabled */
362 if (ControllerConfig
& 0x02)
364 /* Generate an IRQ 12 if there is data ready in the output queue */
365 if (PS2PortQueueRead(1)) PicInterruptRequest(12);
369 BOOLEAN
PS2Initialize(VOID
)
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
);
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
);
384 /* Register the I/O Ports */
385 RegisterIoPort(PS2_CONTROL_PORT
, PS2ReadPort
, PS2WritePort
);
386 RegisterIoPort(PS2_DATA_PORT
, PS2ReadPort
, PS2WritePort
);
391 VOID
PS2Cleanup(VOID
)
393 CloseHandle(Ports
[1].QueueMutex
);
394 CloseHandle(Ports
[0].QueueMutex
);