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
];
37 PS2_DEVICE_CMDPROC DeviceCommand
;
38 } PS2_PORT
, *PPS2_PORT
;
45 static PS2_PORT Ports
[PS2_PORTS
];
47 #define PS2_DEFAULT_CONFIG 0x45
48 static BYTE ControllerConfig
= PS2_DEFAULT_CONFIG
;
49 static BYTE ControllerCommand
= 0x00;
51 static BYTE StatusRegister
= 0x00;
52 // static BYTE InputBuffer = 0x00; // PS/2 Input Buffer
53 static BYTE OutputBuffer
= 0x00; // PS/2 Output Buffer
55 static PHARDWARE_TIMER IrqTimer
= NULL
;
57 /* PRIVATE FUNCTIONS **********************************************************/
59 static VOID
PS2SendCommand(PPS2_PORT Port
, BYTE Command
)
61 if (!Port
->IsEnabled
) return;
63 /* Call the device command */
64 if (Port
->DeviceCommand
) Port
->DeviceCommand(Port
->Param
, Command
);
67 static BYTE WINAPI
PS2ReadPort(USHORT Port
)
69 if (Port
== PS2_CONTROL_PORT
)
71 /* Be sure bit 2 is always set */
72 StatusRegister
|= 1 << 2;
74 // FIXME: Should clear bits 6 and 7 because there are
75 // no timeouts and no parity errors.
77 return StatusRegister
;
79 else if (Port
== PS2_DATA_PORT
)
82 * If there is something to read (response byte from the
83 * controller or data from a PS/2 device), read it.
85 if (StatusRegister
& (1 << 0)) // || StatusRegister & (1 << 5) for second PS/2 port
86 StatusRegister
&= ~(1 << 0); // StatusRegister &= ~(1 << 5);
88 // FIXME: We may check there whether there is data latched in
89 // PS2 ports 1 or 2 (keyboard or mouse) and retrieve it there...
91 /* Always return the available byte stored in the output buffer */
98 static VOID WINAPI
PS2WritePort(USHORT Port
, BYTE Data
)
100 if (Port
== PS2_CONTROL_PORT
)
104 /* Read configuration byte */
107 OutputBuffer
= ControllerConfig
;
108 StatusRegister
|= (1 << 0); // There is something to read
112 /* Write configuration byte */
114 /* Write controller output port */
116 /* Write to the first PS/2 port output buffer */
118 /* Write to the second PS/2 port output buffer */
120 /* Write to the second PS/2 port input buffer */
123 /* These commands require a response */
124 ControllerCommand
= Data
;
125 StatusRegister
|= (1 << 3); // This is a controller command
129 /* Disable second PS/2 port */
132 Ports
[1].IsEnabled
= FALSE
;
136 /* Enable second PS/2 port */
139 Ports
[1].IsEnabled
= TRUE
;
143 /* Test second PS/2 port */
146 OutputBuffer
= 0x00; // Success code
147 StatusRegister
|= (1 << 0); // There is something to read
151 /* Test PS/2 controller */
154 OutputBuffer
= 0x55; // Success code
155 StatusRegister
|= (1 << 0); // There is something to read
159 /* Test first PS/2 port */
162 OutputBuffer
= 0x00; // Success code
163 StatusRegister
|= (1 << 0); // There is something to read
167 /* Disable first PS/2 port */
170 Ports
[0].IsEnabled
= FALSE
;
174 /* Enable first PS/2 port */
177 Ports
[0].IsEnabled
= TRUE
;
181 /* Read controller output port */
184 /* Bit 0 always set, and bit 1 is the A20 gate state */
185 OutputBuffer
= (!!EmulatorGetA20() << 1) | 0x01;
186 // FIXME: Set the status of IRQ1 and IRQ12
188 StatusRegister
|= (1 << 0); // There is something to read
208 else if (Port
== PS2_DATA_PORT
)
210 /* Check if the controller is waiting for a response */
211 if (StatusRegister
& (1 << 3)) // If we have data for the controller
213 StatusRegister
&= ~(1 << 3);
215 /* Check which command it was */
216 switch (ControllerCommand
)
218 /* Write configuration byte */
221 ControllerConfig
= Data
;
225 /* Write controller output */
228 /* Check if bit 0 is unset */
229 if (!(Data
& (1 << 0)))
231 /* CPU disabled - Stop the VDM */
235 /* Update the A20 line setting */
236 EmulatorSetA20(Data
& (1 << 1));
238 // FIXME: Add the status of IRQ1 and IRQ12
243 /* Push the data byte into the first PS/2 port queue */
246 PS2QueuePush(0, Data
);
250 /* Push the data byte into the second PS/2 port queue */
253 PS2QueuePush(1, Data
);
258 * Send a command to the second PS/2 port (by default
259 * it is a command for the first PS/2 port)
263 PS2SendCommand(&Ports
[1], Data
);
271 /* By default, send a command to the first PS/2 port */
272 PS2SendCommand(&Ports
[0], Data
);
276 static VOID FASTCALL
GeneratePS2Irq(ULONGLONG ElapsedTime
)
278 UNREFERENCED_PARAMETER(ElapsedTime
);
280 /* Generate an IRQ 1 if there is data ready in the output queue */
281 if (PS2PortQueueRead(0))
283 /* Generate an interrupt if interrupts for the first PS/2 port are enabled */
284 if (ControllerConfig
& 0x01) PicInterruptRequest(1);
288 /* Generate an IRQ 12 if there is data ready in the output queue */
289 if (PS2PortQueueRead(1))
291 /* Generate an interrupt if interrupts for the second PS/2 port are enabled */
292 if (ControllerConfig
& 0x02) PicInterruptRequest(12);
297 /* PUBLIC FUNCTIONS ***********************************************************/
299 BOOLEAN
PS2PortQueueRead(BYTE PS2Port
)
301 BOOLEAN Result
= TRUE
;
304 if (PS2Port
>= PS2_PORTS
) return FALSE
;
305 Port
= &Ports
[PS2Port
];
307 if (!Port
->IsEnabled
) return FALSE
;
309 /* Make sure the queue is not empty (fast check) */
310 if (Port
->QueueEmpty
)
312 /* Only the keyboard should have its last data latched */
313 // FIXME: Alternatively this can be done in PS2ReadPort when
314 // we read PS2_DATA_PORT. What is the best solution??
317 OutputBuffer
= Port
->Queue
[(Port
->QueueStart
- 1) % BUFFER_SIZE
];
323 WaitForSingleObject(Port
->QueueMutex
, INFINITE
);
326 * Recheck whether the queue is not empty (it may
327 * have changed after having grabbed the mutex).
329 if (Port
->QueueEmpty
)
336 OutputBuffer
= Port
->Queue
[Port
->QueueStart
];
337 StatusRegister
|= (1 << 0); // There is something to read
338 // Sometimes StatusRegister |= (1 << 5); for the second PS/2 port
340 /* Remove the value from the queue */
342 Port
->QueueStart
%= BUFFER_SIZE
;
344 /* Check if the queue is now empty */
345 if (Port
->QueueStart
== Port
->QueueEnd
)
346 Port
->QueueEmpty
= TRUE
;
349 ReleaseMutex(Port
->QueueMutex
);
353 VOID
PS2SetDeviceCmdProc(BYTE PS2Port
, LPVOID Param
, PS2_DEVICE_CMDPROC DeviceCommand
)
355 if (PS2Port
>= PS2_PORTS
) return;
357 Ports
[PS2Port
].Param
= Param
;
358 Ports
[PS2Port
].DeviceCommand
= DeviceCommand
;
362 BOOLEAN
PS2QueuePush(BYTE PS2Port
, BYTE Data
)
364 BOOLEAN Result
= TRUE
;
367 if (PS2Port
>= PS2_PORTS
) return FALSE
;
368 Port
= &Ports
[PS2Port
];
370 if (!Port
->IsEnabled
) return FALSE
;
372 WaitForSingleObject(Port
->QueueMutex
, INFINITE
);
374 /* Check if the queue is full */
375 if (!Port
->QueueEmpty
&& (Port
->QueueStart
== Port
->QueueEnd
))
381 /* Insert the value in the queue */
382 Port
->Queue
[Port
->QueueEnd
] = Data
;
384 Port
->QueueEnd
%= BUFFER_SIZE
;
386 /* The queue is not empty anymore */
387 Port
->QueueEmpty
= FALSE
;
389 /* Schedule the IRQ */
390 EnableHardwareTimer(IrqTimer
);
393 ReleaseMutex(Port
->QueueMutex
);
397 BOOLEAN
PS2Initialize(VOID
)
399 /* Initialize the PS/2 ports */
400 Ports
[0].IsEnabled
= TRUE
;
401 Ports
[0].QueueEmpty
= TRUE
;
402 Ports
[0].QueueStart
= 0;
403 Ports
[0].QueueEnd
= 0;
404 Ports
[0].QueueMutex
= CreateMutex(NULL
, FALSE
, NULL
);
406 Ports
[1].IsEnabled
= TRUE
;
407 Ports
[1].QueueEmpty
= TRUE
;
408 Ports
[1].QueueStart
= 0;
409 Ports
[1].QueueEnd
= 0;
410 Ports
[1].QueueMutex
= CreateMutex(NULL
, FALSE
, NULL
);
412 /* Register the I/O Ports */
413 RegisterIoPort(PS2_CONTROL_PORT
, PS2ReadPort
, PS2WritePort
);
414 RegisterIoPort(PS2_DATA_PORT
, PS2ReadPort
, PS2WritePort
);
416 IrqTimer
= CreateHardwareTimer(HARDWARE_TIMER_ONESHOT
,
423 VOID
PS2Cleanup(VOID
)
425 DestroyHardwareTimer(IrqTimer
);
427 CloseHandle(Ports
[1].QueueMutex
);
428 CloseHandle(Ports
[0].QueueMutex
);