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 *******************************************************************/
19 /* PRIVATE VARIABLES **********************************************************/
21 #define BUFFER_SIZE 32
23 typedef struct _PS2_PORT
28 BYTE Queue
[BUFFER_SIZE
];
34 PS2_DEVICE_CMDPROC DeviceCommand
;
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 VOID
PS2SendCommand(PPS2_PORT Port
, BYTE Command
)
56 if (!Port
->IsEnabled
) return;
58 /* Call the device command */
59 if (Port
->DeviceCommand
) Port
->DeviceCommand(Port
->Param
, Command
);
62 static BYTE WINAPI
PS2ReadPort(USHORT Port
)
64 if (Port
== PS2_CONTROL_PORT
)
66 /* Be sure bit 2 is always set */
67 StatusRegister
|= 1 << 2;
69 // FIXME: Should clear bits 6 and 7 because there are
70 // no timeouts and no parity errors.
72 return StatusRegister
;
74 else if (Port
== PS2_DATA_PORT
)
77 * If there is something to read (response byte from the
78 * controller or data from a PS/2 device), read it.
80 if (StatusRegister
& (1 << 0)) // || StatusRegister & (1 << 5) for second PS/2 port
81 StatusRegister
&= ~(1 << 0); // StatusRegister &= ~(1 << 5);
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...
86 /* Always return the available byte stored in the output buffer */
93 static VOID WINAPI
PS2WritePort(USHORT Port
, BYTE Data
)
95 if (Port
== PS2_CONTROL_PORT
)
99 /* Read configuration byte */
102 OutputBuffer
= ControllerConfig
;
103 StatusRegister
|= (1 << 0); // There is something to read
107 /* Write configuration byte */
109 /* Write controller output port */
111 /* Write to the first PS/2 port output buffer */
113 /* Write to the second PS/2 port output buffer */
115 /* Write to the second PS/2 port input buffer */
118 /* These commands require a response */
119 ControllerCommand
= Data
;
120 StatusRegister
|= (1 << 3); // This is a controller command
124 /* Disable second PS/2 port */
127 Ports
[1].IsEnabled
= FALSE
;
131 /* Enable second PS/2 port */
134 Ports
[1].IsEnabled
= TRUE
;
138 /* Test second PS/2 port */
141 OutputBuffer
= 0x00; // Success code
142 StatusRegister
|= (1 << 0); // There is something to read
146 /* Test PS/2 controller */
149 OutputBuffer
= 0x55; // Success code
150 StatusRegister
|= (1 << 0); // There is something to read
154 /* Test first PS/2 port */
157 OutputBuffer
= 0x00; // Success code
158 StatusRegister
|= (1 << 0); // There is something to read
162 /* Disable first PS/2 port */
165 Ports
[0].IsEnabled
= FALSE
;
169 /* Enable first PS/2 port */
172 Ports
[0].IsEnabled
= TRUE
;
176 /* Read controller output port */
179 // TODO: Not implemented
199 else if (Port
== PS2_DATA_PORT
)
201 /* Check if the controller is waiting for a response */
202 if (StatusRegister
& (1 << 3)) // If we have data for the controller
204 StatusRegister
&= ~(1 << 3);
206 /* Check which command it was */
207 switch (ControllerCommand
)
209 /* Write configuration byte */
212 ControllerConfig
= Data
;
216 /* Write controller output */
219 /* Check if bit 0 is unset */
220 if (!(Data
& (1 << 0)))
222 /* CPU disabled - Stop the VDM */
226 /* Update the A20 line setting */
227 EmulatorSetA20(Data
& (1 << 1));
232 /* Push the data byte into the first PS/2 port queue */
235 PS2QueuePush(0, Data
);
239 /* Push the data byte into the second PS/2 port queue */
242 PS2QueuePush(1, Data
);
247 * Send a command to the second PS/2 port (by default
248 * it is a command for the first PS/2 port)
252 PS2SendCommand(&Ports
[1], Data
);
260 /* By default, send a command to the first PS/2 port */
261 PS2SendCommand(&Ports
[0], Data
);
265 static BOOLEAN
PS2PortQueueRead(BYTE PS2Port
)
267 BOOLEAN Result
= TRUE
;
270 if (PS2Port
>= PS2_PORTS
) return FALSE
;
271 Port
= &Ports
[PS2Port
];
273 if (!Port
->IsEnabled
) return FALSE
;
275 /* Make sure the queue is not empty (fast check) */
276 if (Port
->QueueEmpty
)
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??
283 OutputBuffer
= Port
->Queue
[(Port
->QueueStart
- 1) % BUFFER_SIZE
];
289 WaitForSingleObject(Port
->QueueMutex
, INFINITE
);
292 * Recheck whether the queue is not empty (it may
293 * have changed after having grabbed the mutex).
295 if (Port
->QueueEmpty
)
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
306 /* Remove the value from the queue */
308 Port
->QueueStart
%= BUFFER_SIZE
;
310 /* Check if the queue is now empty */
311 if (Port
->QueueStart
== Port
->QueueEnd
)
312 Port
->QueueEmpty
= TRUE
;
315 ReleaseMutex(Port
->QueueMutex
);
319 /* PUBLIC FUNCTIONS ***********************************************************/
321 VOID
PS2SetDeviceCmdProc(BYTE PS2Port
, LPVOID Param
, PS2_DEVICE_CMDPROC DeviceCommand
)
323 if (PS2Port
>= PS2_PORTS
) return;
325 Ports
[PS2Port
].Param
= Param
;
326 Ports
[PS2Port
].DeviceCommand
= DeviceCommand
;
330 BOOLEAN
PS2QueuePush(BYTE PS2Port
, BYTE Data
)
332 BOOLEAN Result
= TRUE
;
335 if (PS2Port
>= PS2_PORTS
) return FALSE
;
336 Port
= &Ports
[PS2Port
];
338 if (!Port
->IsEnabled
) return FALSE
;
340 WaitForSingleObject(Port
->QueueMutex
, INFINITE
);
342 /* Check if the queue is full */
343 if (!Port
->QueueEmpty
&& (Port
->QueueStart
== Port
->QueueEnd
))
349 /* Insert the value in the queue */
350 Port
->Queue
[Port
->QueueEnd
] = Data
;
352 Port
->QueueEnd
%= BUFFER_SIZE
;
354 /* The queue is not empty anymore */
355 Port
->QueueEmpty
= FALSE
;
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
364 PicInterruptRequest(1);
365 else if (PS2Port == 1)
366 PicInterruptRequest(12);
370 ReleaseMutex(Port
->QueueMutex
);
374 VOID
GenerateIrq1(VOID
)
376 /* Generate an interrupt if interrupts for the first PS/2 port are enabled */
377 if (ControllerConfig
& 0x01)
379 /* Generate an IRQ 1 if there is data ready in the output queue */
380 if (PS2PortQueueRead(0)) PicInterruptRequest(1);
384 VOID
GenerateIrq12(VOID
)
386 /* Generate an interrupt if interrupts for the second PS/2 port are enabled */
387 if (ControllerConfig
& 0x02)
389 /* Generate an IRQ 12 if there is data ready in the output queue */
390 if (PS2PortQueueRead(1)) PicInterruptRequest(12);
394 BOOLEAN
PS2Initialize(VOID
)
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
);
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
);
409 /* Register the I/O Ports */
410 RegisterIoPort(PS2_CONTROL_PORT
, PS2ReadPort
, PS2WritePort
);
411 RegisterIoPort(PS2_DATA_PORT
, PS2ReadPort
, PS2WritePort
);
416 VOID
PS2Cleanup(VOID
)
418 CloseHandle(Ports
[1].QueueMutex
);
419 CloseHandle(Ports
[0].QueueMutex
);