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 *******************************************************************/
20 /* PRIVATE VARIABLES **********************************************************/
22 #define BUFFER_SIZE 32
24 typedef struct _PS2_PORT
29 BYTE Queue
[BUFFER_SIZE
];
35 PS2_DEVICE_CMDPROC DeviceCommand
;
36 } PS2_PORT
, *PPS2_PORT
;
43 static PS2_PORT Ports
[PS2_PORTS
];
45 #define PS2_DEFAULT_CONFIG 0x45
46 static BYTE ControllerConfig
= PS2_DEFAULT_CONFIG
;
47 static BYTE ControllerCommand
= 0x00;
49 static BYTE StatusRegister
= 0x00;
50 // static BYTE InputBuffer = 0x00; // PS/2 Input Buffer
51 static BYTE OutputBuffer
= 0x00; // PS/2 Output Buffer
53 static PHARDWARE_TIMER IrqTimer
= NULL
;
55 /* PRIVATE FUNCTIONS **********************************************************/
57 static VOID
PS2SendCommand(PPS2_PORT Port
, BYTE Command
)
59 if (!Port
->IsEnabled
) return;
61 /* Call the device command */
62 if (Port
->DeviceCommand
) Port
->DeviceCommand(Port
->Param
, Command
);
65 static BYTE WINAPI
PS2ReadPort(USHORT Port
)
67 if (Port
== PS2_CONTROL_PORT
)
69 /* Be sure bit 2 is always set */
70 StatusRegister
|= 1 << 2;
72 // FIXME: Should clear bits 6 and 7 because there are
73 // no timeouts and no parity errors.
75 return StatusRegister
;
77 else if (Port
== PS2_DATA_PORT
)
80 * If there is something to read (response byte from the
81 * controller or data from a PS/2 device), read it.
83 if (StatusRegister
& (1 << 0)) // || StatusRegister & (1 << 5) for second PS/2 port
84 StatusRegister
&= ~(1 << 0); // StatusRegister &= ~(1 << 5);
86 // FIXME: We may check there whether there is data latched in
87 // PS2 ports 1 or 2 (keyboard or mouse) and retrieve it there...
89 /* Always return the available byte stored in the output buffer */
96 static VOID WINAPI
PS2WritePort(USHORT Port
, BYTE Data
)
98 if (Port
== PS2_CONTROL_PORT
)
102 /* Read configuration byte */
105 OutputBuffer
= ControllerConfig
;
106 StatusRegister
|= (1 << 0); // There is something to read
110 /* Write configuration byte */
112 /* Write controller output port */
114 /* Write to the first PS/2 port output buffer */
116 /* Write to the second PS/2 port output buffer */
118 /* Write to the second PS/2 port input buffer */
121 /* These commands require a response */
122 ControllerCommand
= Data
;
123 StatusRegister
|= (1 << 3); // This is a controller command
127 /* Disable second PS/2 port */
130 Ports
[1].IsEnabled
= FALSE
;
134 /* Enable second PS/2 port */
137 Ports
[1].IsEnabled
= TRUE
;
141 /* Test second PS/2 port */
144 OutputBuffer
= 0x00; // Success code
145 StatusRegister
|= (1 << 0); // There is something to read
149 /* Test PS/2 controller */
152 OutputBuffer
= 0x55; // Success code
153 StatusRegister
|= (1 << 0); // There is something to read
157 /* Test first PS/2 port */
160 OutputBuffer
= 0x00; // Success code
161 StatusRegister
|= (1 << 0); // There is something to read
165 /* Disable first PS/2 port */
168 Ports
[0].IsEnabled
= FALSE
;
172 /* Enable first PS/2 port */
175 Ports
[0].IsEnabled
= TRUE
;
179 /* Read controller output port */
182 // TODO: Not implemented
202 else if (Port
== PS2_DATA_PORT
)
204 /* Check if the controller is waiting for a response */
205 if (StatusRegister
& (1 << 3)) // If we have data for the controller
207 StatusRegister
&= ~(1 << 3);
209 /* Check which command it was */
210 switch (ControllerCommand
)
212 /* Write configuration byte */
215 ControllerConfig
= Data
;
219 /* Write controller output */
222 /* Check if bit 0 is unset */
223 if (!(Data
& (1 << 0)))
225 /* CPU disabled - Stop the VDM */
229 /* Update the A20 line setting */
230 EmulatorSetA20(Data
& (1 << 1));
235 /* Push the data byte into the first PS/2 port queue */
238 PS2QueuePush(0, Data
);
242 /* Push the data byte into the second PS/2 port queue */
245 PS2QueuePush(1, Data
);
250 * Send a command to the second PS/2 port (by default
251 * it is a command for the first PS/2 port)
255 PS2SendCommand(&Ports
[1], Data
);
263 /* By default, send a command to the first PS/2 port */
264 PS2SendCommand(&Ports
[0], Data
);
268 static VOID FASTCALL
GeneratePS2Irq(ULONGLONG ElapsedTime
)
270 UNREFERENCED_PARAMETER(ElapsedTime
);
272 /* Generate an IRQ 1 if there is data ready in the output queue */
273 if (PS2PortQueueRead(0))
275 /* Generate an interrupt if interrupts for the first PS/2 port are enabled */
276 if (ControllerConfig
& 0x01) PicInterruptRequest(1);
280 /* Generate an IRQ 12 if there is data ready in the output queue */
281 if (PS2PortQueueRead(1))
283 /* Generate an interrupt if interrupts for the second PS/2 port are enabled */
284 if (ControllerConfig
& 0x02) PicInterruptRequest(12);
289 /* PUBLIC FUNCTIONS ***********************************************************/
291 BOOLEAN
PS2PortQueueRead(BYTE PS2Port
)
293 BOOLEAN Result
= TRUE
;
296 if (PS2Port
>= PS2_PORTS
) return FALSE
;
297 Port
= &Ports
[PS2Port
];
299 if (!Port
->IsEnabled
) return FALSE
;
301 /* Make sure the queue is not empty (fast check) */
302 if (Port
->QueueEmpty
)
304 /* Only the keyboard should have its last data latched */
305 // FIXME: Alternatively this can be done in PS2ReadPort when
306 // we read PS2_DATA_PORT. What is the best solution??
309 OutputBuffer
= Port
->Queue
[(Port
->QueueStart
- 1) % BUFFER_SIZE
];
315 WaitForSingleObject(Port
->QueueMutex
, INFINITE
);
318 * Recheck whether the queue is not empty (it may
319 * have changed after having grabbed the mutex).
321 if (Port
->QueueEmpty
)
328 OutputBuffer
= Port
->Queue
[Port
->QueueStart
];
329 StatusRegister
|= (1 << 0); // There is something to read
330 // Sometimes StatusRegister |= (1 << 5); for the second PS/2 port
332 /* Remove the value from the queue */
334 Port
->QueueStart
%= BUFFER_SIZE
;
336 /* Check if the queue is now empty */
337 if (Port
->QueueStart
== Port
->QueueEnd
)
338 Port
->QueueEmpty
= TRUE
;
341 ReleaseMutex(Port
->QueueMutex
);
345 VOID
PS2SetDeviceCmdProc(BYTE PS2Port
, LPVOID Param
, PS2_DEVICE_CMDPROC DeviceCommand
)
347 if (PS2Port
>= PS2_PORTS
) return;
349 Ports
[PS2Port
].Param
= Param
;
350 Ports
[PS2Port
].DeviceCommand
= DeviceCommand
;
354 BOOLEAN
PS2QueuePush(BYTE PS2Port
, BYTE Data
)
356 BOOLEAN Result
= TRUE
;
359 if (PS2Port
>= PS2_PORTS
) return FALSE
;
360 Port
= &Ports
[PS2Port
];
362 if (!Port
->IsEnabled
) return FALSE
;
364 WaitForSingleObject(Port
->QueueMutex
, INFINITE
);
366 /* Check if the queue is full */
367 if (!Port
->QueueEmpty
&& (Port
->QueueStart
== Port
->QueueEnd
))
373 /* Insert the value in the queue */
374 Port
->Queue
[Port
->QueueEnd
] = Data
;
376 Port
->QueueEnd
%= BUFFER_SIZE
;
378 /* The queue is not empty anymore */
379 Port
->QueueEmpty
= FALSE
;
381 /* Schedule the IRQ */
382 EnableHardwareTimer(IrqTimer
);
385 ReleaseMutex(Port
->QueueMutex
);
389 BOOLEAN
PS2Initialize(VOID
)
391 /* Initialize the PS/2 ports */
392 Ports
[0].IsEnabled
= TRUE
;
393 Ports
[0].QueueEmpty
= TRUE
;
394 Ports
[0].QueueStart
= 0;
395 Ports
[0].QueueEnd
= 0;
396 Ports
[0].QueueMutex
= CreateMutex(NULL
, FALSE
, NULL
);
398 Ports
[1].IsEnabled
= TRUE
;
399 Ports
[1].QueueEmpty
= TRUE
;
400 Ports
[1].QueueStart
= 0;
401 Ports
[1].QueueEnd
= 0;
402 Ports
[1].QueueMutex
= CreateMutex(NULL
, FALSE
, NULL
);
404 /* Register the I/O Ports */
405 RegisterIoPort(PS2_CONTROL_PORT
, PS2ReadPort
, PS2WritePort
);
406 RegisterIoPort(PS2_DATA_PORT
, PS2ReadPort
, PS2WritePort
);
408 IrqTimer
= CreateHardwareTimer(HARDWARE_TIMER_ONESHOT
, 100, GeneratePS2Irq
);
413 VOID
PS2Cleanup(VOID
)
415 DestroyHardwareTimer(IrqTimer
);
417 CloseHandle(Ports
[1].QueueMutex
);
418 CloseHandle(Ports
[0].QueueMutex
);