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(ULONG 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 /* Always return the available byte stored in the output buffer */
90 static VOID WINAPI
PS2WritePort(ULONG Port
, BYTE Data
)
92 if (Port
== PS2_CONTROL_PORT
)
96 /* Read configuration byte */
99 OutputBuffer
= ControllerConfig
;
100 StatusRegister
|= (1 << 0); // There is something to read
104 /* Write configuration byte */
106 /* Write controller output port */
108 /* Write to the first PS/2 port output buffer */
110 /* Write to the second PS/2 port output buffer */
112 /* Write to the second PS/2 port input buffer */
115 /* These commands require a response */
116 ControllerCommand
= Data
;
117 StatusRegister
|= (1 << 3); // This is a controller command
121 /* Disable second PS/2 port */
124 Ports
[1].IsEnabled
= FALSE
;
128 /* Enable second PS/2 port */
131 Ports
[1].IsEnabled
= TRUE
;
135 /* Test second PS/2 port */
138 OutputBuffer
= 0x00; // Success code
139 StatusRegister
|= (1 << 0); // There is something to read
143 /* Test PS/2 controller */
146 OutputBuffer
= 0x55; // Success code
147 StatusRegister
|= (1 << 0); // There is something to read
151 /* Test first PS/2 port */
154 OutputBuffer
= 0x00; // Success code
155 StatusRegister
|= (1 << 0); // There is something to read
159 /* Disable first PS/2 port */
162 Ports
[0].IsEnabled
= FALSE
;
166 /* Enable first PS/2 port */
169 Ports
[0].IsEnabled
= TRUE
;
173 /* Read controller output port */
176 // TODO: Not implemented
196 else if (Port
== PS2_DATA_PORT
)
198 /* Check if the controller is waiting for a response */
199 if (StatusRegister
& (1 << 3)) // If we have data for the controller
201 StatusRegister
&= ~(1 << 3);
203 /* Check which command it was */
204 switch (ControllerCommand
)
206 /* Write configuration byte */
209 ControllerConfig
= Data
;
213 /* Write controller output */
216 /* Check if bit 0 is unset */
217 if (!(Data
& (1 << 0)))
219 /* CPU disabled - Stop the VDM */
223 /* Update the A20 line setting */
224 EmulatorSetA20(Data
& (1 << 1));
229 /* Push the data byte into the first PS/2 port queue */
232 PS2QueuePush(0, Data
);
236 /* Push the data byte into the second PS/2 port queue */
239 PS2QueuePush(1, Data
);
244 * Send a command to the second PS/2 port (by default
245 * it is a command for the first PS/2 port)
249 PS2SendCommand(&Ports
[1], Data
);
257 /* By default, send a command to the first PS/2 port */
258 PS2SendCommand(&Ports
[0], Data
);
262 static BOOLEAN
PS2PortQueueRead(BYTE PS2Port
)
264 BOOLEAN Result
= TRUE
;
267 if (PS2Port
>= PS2_PORTS
) return FALSE
;
268 Port
= &Ports
[PS2Port
];
270 if (!Port
->IsEnabled
) return FALSE
;
272 /* Make sure the queue is not empty (fast check) */
273 if (Port
->QueueEmpty
) return FALSE
;
275 WaitForSingleObject(Port
->QueueMutex
, INFINITE
);
278 * Recheck whether the queue is not empty (it may
279 * have changed after having grabbed the mutex).
281 if (Port
->QueueEmpty
)
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
292 /* Remove the value from the queue */
294 Port
->QueueStart
%= BUFFER_SIZE
;
296 /* Check if the queue is now empty */
297 if (Port
->QueueStart
== Port
->QueueEnd
)
298 Port
->QueueEmpty
= TRUE
;
301 ReleaseMutex(Port
->QueueMutex
);
305 /* PUBLIC FUNCTIONS ***********************************************************/
307 VOID
PS2SetDeviceCmdProc(BYTE PS2Port
, LPVOID Param
, PS2_DEVICE_CMDPROC DeviceCommand
)
309 if (PS2Port
>= PS2_PORTS
) return;
311 Ports
[PS2Port
].Param
= Param
;
312 Ports
[PS2Port
].DeviceCommand
= DeviceCommand
;
316 BOOLEAN
PS2QueuePush(BYTE PS2Port
, BYTE Data
)
318 BOOLEAN Result
= TRUE
;
321 if (PS2Port
>= PS2_PORTS
) return FALSE
;
322 Port
= &Ports
[PS2Port
];
324 if (!Port
->IsEnabled
) return FALSE
;
326 WaitForSingleObject(Port
->QueueMutex
, INFINITE
);
328 /* Check if the queue is full */
329 if (!Port
->QueueEmpty
&& (Port
->QueueStart
== Port
->QueueEnd
))
335 /* Insert the value in the queue */
336 Port
->Queue
[Port
->QueueEnd
] = Data
;
338 Port
->QueueEnd
%= BUFFER_SIZE
;
340 /* Since we inserted a value, it's not empty anymore */
341 Port
->QueueEmpty
= FALSE
;
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
350 PicInterruptRequest(1);
351 else if (PS2Port == 1)
352 PicInterruptRequest(12);
356 ReleaseMutex(Port
->QueueMutex
);
360 VOID
GenerateIrq1(VOID
)
362 /* Generate an interrupt if interrupts for the first PS/2 port are enabled */
363 if (ControllerConfig
& 0x01)
365 /* Generate an IRQ 1 if there is data ready in the output queue */
366 if (PS2PortQueueRead(0)) PicInterruptRequest(1);
370 VOID
GenerateIrq12(VOID
)
372 /* Generate an interrupt if interrupts for the second PS/2 port are enabled */
373 if (ControllerConfig
& 0x02)
375 /* Generate an IRQ 12 if there is data ready in the output queue */
376 if (PS2PortQueueRead(1)) PicInterruptRequest(12);
380 BOOLEAN
PS2Initialize(VOID
)
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
);
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
);
395 /* Register the I/O Ports */
396 RegisterIoPort(PS2_CONTROL_PORT
, PS2ReadPort
, PS2WritePort
);
397 RegisterIoPort(PS2_DATA_PORT
, PS2ReadPort
, PS2WritePort
);
402 VOID
PS2Cleanup(VOID
)
404 CloseHandle(Ports
[1].QueueMutex
);
405 CloseHandle(Ports
[0].QueueMutex
);