1*c2c66affSColin Finck /*
2*c2c66affSColin Finck * COPYRIGHT: GPL - See COPYING in the top level directory
3*c2c66affSColin Finck * PROJECT: ReactOS Virtual DOS Machine
4*c2c66affSColin Finck * FILE: subsystems/mvdm/ntvdm/hardware/ps2.c
5*c2c66affSColin Finck * PURPOSE: PS/2 controller emulation
6*c2c66affSColin Finck * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7*c2c66affSColin Finck * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8*c2c66affSColin Finck *
9*c2c66affSColin Finck * DOCUMENTATION: IBM Personal System/2 Hardware Interface Technical Reference, May 1988 (Section 10)
10*c2c66affSColin Finck * http://wiki.osdev.org/%228042%22_PS/2_Controller
11*c2c66affSColin Finck * http://www.computer-engineering.org/ps2keyboard/
12*c2c66affSColin Finck */
13*c2c66affSColin Finck
14*c2c66affSColin Finck /* INCLUDES *******************************************************************/
15*c2c66affSColin Finck
16*c2c66affSColin Finck #include "ntvdm.h"
17*c2c66affSColin Finck
18*c2c66affSColin Finck #define NDEBUG
19*c2c66affSColin Finck #include <debug.h>
20*c2c66affSColin Finck
21*c2c66affSColin Finck #include "emulator.h"
22*c2c66affSColin Finck #include "ps2.h"
23*c2c66affSColin Finck
24*c2c66affSColin Finck #include "memory.h"
25*c2c66affSColin Finck #include "io.h"
26*c2c66affSColin Finck #include "pic.h"
27*c2c66affSColin Finck #include "clock.h"
28*c2c66affSColin Finck
29*c2c66affSColin Finck /* PRIVATE VARIABLES **********************************************************/
30*c2c66affSColin Finck
31*c2c66affSColin Finck #define BUFFER_SIZE 32
32*c2c66affSColin Finck
33*c2c66affSColin Finck typedef struct _PS2_PORT
34*c2c66affSColin Finck {
35*c2c66affSColin Finck BOOLEAN IsEnabled;
36*c2c66affSColin Finck
37*c2c66affSColin Finck BOOLEAN QueueEmpty;
38*c2c66affSColin Finck BYTE Queue[BUFFER_SIZE];
39*c2c66affSColin Finck UINT QueueStart;
40*c2c66affSColin Finck UINT QueueEnd;
41*c2c66affSColin Finck HANDLE QueueMutex;
42*c2c66affSColin Finck
43*c2c66affSColin Finck LPVOID Param;
44*c2c66affSColin Finck PS2_DEVICE_CMDPROC DeviceCommand;
45*c2c66affSColin Finck } PS2_PORT, *PPS2_PORT;
46*c2c66affSColin Finck
47*c2c66affSColin Finck /*
48*c2c66affSColin Finck * Port 1: Keyboard
49*c2c66affSColin Finck * Port 2: Mouse
50*c2c66affSColin Finck */
51*c2c66affSColin Finck #define PS2_PORTS 2
52*c2c66affSColin Finck static PS2_PORT Ports[PS2_PORTS];
53*c2c66affSColin Finck
54*c2c66affSColin Finck static BYTE Memory[0x20]; // PS/2 Controller internal memory
55*c2c66affSColin Finck #define ControllerConfig Memory[0] // The configuration byte is byte 0
56*c2c66affSColin Finck
57*c2c66affSColin Finck static BYTE StatusRegister = 0x00; // Controller status register
58*c2c66affSColin Finck // static BYTE InputBuffer = 0x00; // PS/2 Input Buffer
59*c2c66affSColin Finck static BYTE OutputBuffer = 0x00; // PS/2 Output Buffer
60*c2c66affSColin Finck
61*c2c66affSColin Finck static BYTE ControllerCommand = 0x00;
62*c2c66affSColin Finck
63*c2c66affSColin Finck static PHARDWARE_TIMER IrqTimer = NULL;
64*c2c66affSColin Finck
65*c2c66affSColin Finck /* PRIVATE FUNCTIONS **********************************************************/
66*c2c66affSColin Finck
PS2SendCommand(BYTE PS2Port,BYTE Command)67*c2c66affSColin Finck static VOID PS2SendCommand(BYTE PS2Port, BYTE Command)
68*c2c66affSColin Finck {
69*c2c66affSColin Finck PPS2_PORT Port;
70*c2c66affSColin Finck
71*c2c66affSColin Finck ASSERT(PS2Port < PS2_PORTS);
72*c2c66affSColin Finck Port = &Ports[PS2Port];
73*c2c66affSColin Finck
74*c2c66affSColin Finck /*
75*c2c66affSColin Finck * According to http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html#kccad
76*c2c66affSColin Finck * any PS/2 command sent reenables the corresponding port.
77*c2c66affSColin Finck */
78*c2c66affSColin Finck if (PS2Port == 0)
79*c2c66affSColin Finck ControllerConfig &= ~PS2_CONFIG_KBD_DISABLE;
80*c2c66affSColin Finck else // if (PS2Port == 1)
81*c2c66affSColin Finck ControllerConfig &= ~PS2_CONFIG_AUX_DISABLE;
82*c2c66affSColin Finck
83*c2c66affSColin Finck Port->IsEnabled = TRUE;
84*c2c66affSColin Finck
85*c2c66affSColin Finck /* Call the device command */
86*c2c66affSColin Finck if (Port->DeviceCommand) Port->DeviceCommand(Port->Param, Command);
87*c2c66affSColin Finck }
88*c2c66affSColin Finck
89*c2c66affSColin Finck
PS2ReadControl(USHORT Port)90*c2c66affSColin Finck static BYTE WINAPI PS2ReadControl(USHORT Port)
91*c2c66affSColin Finck {
92*c2c66affSColin Finck UNREFERENCED_PARAMETER(Port);
93*c2c66affSColin Finck
94*c2c66affSColin Finck /*
95*c2c66affSColin Finck * Be sure the "Keyboard enable" flag is always set.
96*c2c66affSColin Finck * On IBM PC-ATs this is the state of the hardware keyboard
97*c2c66affSColin Finck * lock mechanism. It is not widely used, but some programs
98*c2c66affSColin Finck * still use it, see for example:
99*c2c66affSColin Finck * http://www.os2museum.com/wp/the-dos-4-0-shell-mouse-mystery/
100*c2c66affSColin Finck */
101*c2c66affSColin Finck StatusRegister |= PS2_STAT_KBD_ENABLE;
102*c2c66affSColin Finck
103*c2c66affSColin Finck /* We do not have any timeouts nor parity errors */
104*c2c66affSColin Finck StatusRegister &= ~(PS2_STAT_PARITY_ERROR | PS2_STAT_GEN_TIMEOUT);
105*c2c66affSColin Finck
106*c2c66affSColin Finck return StatusRegister;
107*c2c66affSColin Finck }
108*c2c66affSColin Finck
PS2ReadData(USHORT Port)109*c2c66affSColin Finck static BYTE WINAPI PS2ReadData(USHORT Port)
110*c2c66affSColin Finck {
111*c2c66affSColin Finck UNREFERENCED_PARAMETER(Port);
112*c2c66affSColin Finck
113*c2c66affSColin Finck /*
114*c2c66affSColin Finck * If there is something to read (response byte from the
115*c2c66affSColin Finck * controller or data from a PS/2 device), read it.
116*c2c66affSColin Finck */
117*c2c66affSColin Finck StatusRegister &= ~PS2_STAT_OUT_BUF_FULL;
118*c2c66affSColin Finck
119*c2c66affSColin Finck // Keep the state of the "Auxiliary output buffer full" flag
120*c2c66affSColin Finck // in order to remember from where the data came from.
121*c2c66affSColin Finck // StatusRegister &= ~PS2_STAT_AUX_OUT_BUF_FULL;
122*c2c66affSColin Finck
123*c2c66affSColin Finck // FIXME: We may check there whether there is data latched in
124*c2c66affSColin Finck // PS2 ports 1 or 2 (keyboard or mouse) and retrieve it there...
125*c2c66affSColin Finck
126*c2c66affSColin Finck /* Always return the available byte stored in the output buffer */
127*c2c66affSColin Finck return OutputBuffer;
128*c2c66affSColin Finck }
129*c2c66affSColin Finck
PS2WriteControl(USHORT Port,BYTE Data)130*c2c66affSColin Finck static VOID WINAPI PS2WriteControl(USHORT Port, BYTE Data)
131*c2c66affSColin Finck {
132*c2c66affSColin Finck UNREFERENCED_PARAMETER(Port);
133*c2c66affSColin Finck
134*c2c66affSColin Finck switch (Data)
135*c2c66affSColin Finck {
136*c2c66affSColin Finck /* Read configuration byte (byte 0 from internal memory) */
137*c2c66affSColin Finck case 0x20:
138*c2c66affSColin Finck /* Read byte N from internal memory */
139*c2c66affSColin Finck case 0x21: case 0x22: case 0x23:
140*c2c66affSColin Finck case 0x24: case 0x25: case 0x26: case 0x27:
141*c2c66affSColin Finck case 0x28: case 0x29: case 0x2A: case 0x2B:
142*c2c66affSColin Finck case 0x2C: case 0x2D: case 0x2E: case 0x2F:
143*c2c66affSColin Finck case 0x30: case 0x31: case 0x32: case 0x33:
144*c2c66affSColin Finck case 0x34: case 0x35: case 0x36: case 0x37:
145*c2c66affSColin Finck case 0x38: case 0x39: case 0x3A: case 0x3B:
146*c2c66affSColin Finck case 0x3C: case 0x3D: case 0x3E: case 0x3F:
147*c2c66affSColin Finck {
148*c2c66affSColin Finck OutputBuffer = Memory[Data & 0x1F];
149*c2c66affSColin Finck StatusRegister |= PS2_STAT_OUT_BUF_FULL;
150*c2c66affSColin Finck break;
151*c2c66affSColin Finck }
152*c2c66affSColin Finck
153*c2c66affSColin Finck /* Write configuration byte (byte 0 from internal memory) */
154*c2c66affSColin Finck case 0x60:
155*c2c66affSColin Finck /* Write to byte N of internal memory */
156*c2c66affSColin Finck case 0x61: case 0x62: case 0x63:
157*c2c66affSColin Finck case 0x64: case 0x65: case 0x66: case 0x67:
158*c2c66affSColin Finck case 0x68: case 0x69: case 0x6A: case 0x6B:
159*c2c66affSColin Finck case 0x6C: case 0x6D: case 0x6E: case 0x6F:
160*c2c66affSColin Finck case 0x70: case 0x71: case 0x72: case 0x73:
161*c2c66affSColin Finck case 0x74: case 0x75: case 0x76: case 0x77:
162*c2c66affSColin Finck case 0x78: case 0x79: case 0x7A: case 0x7B:
163*c2c66affSColin Finck case 0x7C: case 0x7D: case 0x7E: case 0x7F:
164*c2c66affSColin Finck
165*c2c66affSColin Finck /* Write controller output port */
166*c2c66affSColin Finck case 0xD1:
167*c2c66affSColin Finck /* Write to the first PS/2 port output buffer */
168*c2c66affSColin Finck case 0xD2:
169*c2c66affSColin Finck /* Write to the second PS/2 port output buffer */
170*c2c66affSColin Finck case 0xD3:
171*c2c66affSColin Finck /* Write to the second PS/2 port input buffer */
172*c2c66affSColin Finck case 0xD4:
173*c2c66affSColin Finck {
174*c2c66affSColin Finck /* These commands require a response */
175*c2c66affSColin Finck ControllerCommand = Data;
176*c2c66affSColin Finck StatusRegister |= PS2_STAT_COMMAND;
177*c2c66affSColin Finck break;
178*c2c66affSColin Finck }
179*c2c66affSColin Finck
180*c2c66affSColin Finck /* Disable second PS/2 port */
181*c2c66affSColin Finck case 0xA7:
182*c2c66affSColin Finck {
183*c2c66affSColin Finck ControllerConfig |= PS2_CONFIG_AUX_DISABLE;
184*c2c66affSColin Finck Ports[1].IsEnabled = FALSE;
185*c2c66affSColin Finck break;
186*c2c66affSColin Finck }
187*c2c66affSColin Finck
188*c2c66affSColin Finck /* Enable second PS/2 port */
189*c2c66affSColin Finck case 0xA8:
190*c2c66affSColin Finck {
191*c2c66affSColin Finck ControllerConfig &= ~PS2_CONFIG_AUX_DISABLE;
192*c2c66affSColin Finck Ports[1].IsEnabled = TRUE;
193*c2c66affSColin Finck break;
194*c2c66affSColin Finck }
195*c2c66affSColin Finck
196*c2c66affSColin Finck /* Test second PS/2 port */
197*c2c66affSColin Finck case 0xA9:
198*c2c66affSColin Finck {
199*c2c66affSColin Finck OutputBuffer = 0x00; // Success code
200*c2c66affSColin Finck StatusRegister |= PS2_STAT_OUT_BUF_FULL;
201*c2c66affSColin Finck break;
202*c2c66affSColin Finck }
203*c2c66affSColin Finck
204*c2c66affSColin Finck /* Test PS/2 controller */
205*c2c66affSColin Finck case 0xAA:
206*c2c66affSColin Finck {
207*c2c66affSColin Finck OutputBuffer = 0x55; // Success code
208*c2c66affSColin Finck StatusRegister |= PS2_STAT_OUT_BUF_FULL;
209*c2c66affSColin Finck break;
210*c2c66affSColin Finck }
211*c2c66affSColin Finck
212*c2c66affSColin Finck /* Test first PS/2 port */
213*c2c66affSColin Finck case 0xAB:
214*c2c66affSColin Finck {
215*c2c66affSColin Finck OutputBuffer = 0x00; // Success code
216*c2c66affSColin Finck StatusRegister |= PS2_STAT_OUT_BUF_FULL;
217*c2c66affSColin Finck break;
218*c2c66affSColin Finck }
219*c2c66affSColin Finck
220*c2c66affSColin Finck /* Disable first PS/2 port */
221*c2c66affSColin Finck case 0xAD:
222*c2c66affSColin Finck {
223*c2c66affSColin Finck ControllerConfig |= PS2_CONFIG_KBD_DISABLE;
224*c2c66affSColin Finck Ports[0].IsEnabled = FALSE;
225*c2c66affSColin Finck break;
226*c2c66affSColin Finck }
227*c2c66affSColin Finck
228*c2c66affSColin Finck /* Enable first PS/2 port */
229*c2c66affSColin Finck case 0xAE:
230*c2c66affSColin Finck {
231*c2c66affSColin Finck ControllerConfig &= ~PS2_CONFIG_KBD_DISABLE;
232*c2c66affSColin Finck Ports[0].IsEnabled = TRUE;
233*c2c66affSColin Finck break;
234*c2c66affSColin Finck }
235*c2c66affSColin Finck
236*c2c66affSColin Finck /* Read controller output port */
237*c2c66affSColin Finck case 0xD0:
238*c2c66affSColin Finck {
239*c2c66affSColin Finck /* Bit 0 always set, and bit 1 is the A20 gate state */
240*c2c66affSColin Finck OutputBuffer = (!!EmulatorGetA20() << 1) | PS2_OUT_CPU_NO_RESET;
241*c2c66affSColin Finck
242*c2c66affSColin Finck /* Set IRQ1 state */
243*c2c66affSColin Finck if (ControllerConfig & PS2_CONFIG_KBD_INT)
244*c2c66affSColin Finck OutputBuffer |= PS2_OUT_IRQ01;
245*c2c66affSColin Finck else
246*c2c66affSColin Finck OutputBuffer &= ~PS2_OUT_IRQ01;
247*c2c66affSColin Finck
248*c2c66affSColin Finck /* Set IRQ12 state */
249*c2c66affSColin Finck if (ControllerConfig & PS2_CONFIG_AUX_INT)
250*c2c66affSColin Finck OutputBuffer |= PS2_OUT_IRQ12;
251*c2c66affSColin Finck else
252*c2c66affSColin Finck OutputBuffer &= ~PS2_OUT_IRQ12;
253*c2c66affSColin Finck
254*c2c66affSColin Finck /* Check whether data is already present */
255*c2c66affSColin Finck if (StatusRegister & PS2_STAT_OUT_BUF_FULL)
256*c2c66affSColin Finck {
257*c2c66affSColin Finck if (StatusRegister & PS2_STAT_AUX_OUT_BUF_FULL)
258*c2c66affSColin Finck OutputBuffer |= PS2_OUT_AUX_DATA;
259*c2c66affSColin Finck else
260*c2c66affSColin Finck OutputBuffer |= PS2_OUT_KBD_DATA;
261*c2c66affSColin Finck }
262*c2c66affSColin Finck
263*c2c66affSColin Finck StatusRegister |= PS2_STAT_OUT_BUF_FULL;
264*c2c66affSColin Finck break;
265*c2c66affSColin Finck }
266*c2c66affSColin Finck
267*c2c66affSColin Finck /* CPU Reset */
268*c2c66affSColin Finck case 0xF0: case 0xF2: case 0xF4: case 0xF6:
269*c2c66affSColin Finck case 0xF8: case 0xFA: case 0xFC: case 0xFE:
270*c2c66affSColin Finck {
271*c2c66affSColin Finck /* Stop the VDM */
272*c2c66affSColin Finck EmulatorTerminate();
273*c2c66affSColin Finck return;
274*c2c66affSColin Finck }
275*c2c66affSColin Finck }
276*c2c66affSColin Finck }
277*c2c66affSColin Finck
PS2WriteData(USHORT Port,BYTE Data)278*c2c66affSColin Finck static VOID WINAPI PS2WriteData(USHORT Port, BYTE Data)
279*c2c66affSColin Finck {
280*c2c66affSColin Finck /* Check if the controller is waiting for a response */
281*c2c66affSColin Finck if (StatusRegister & PS2_STAT_COMMAND)
282*c2c66affSColin Finck {
283*c2c66affSColin Finck StatusRegister &= ~PS2_STAT_COMMAND;
284*c2c66affSColin Finck
285*c2c66affSColin Finck /* Check which command it was */
286*c2c66affSColin Finck switch (ControllerCommand)
287*c2c66affSColin Finck {
288*c2c66affSColin Finck /* Write configuration byte (byte 0 from internal memory) */
289*c2c66affSColin Finck case 0x60:
290*c2c66affSColin Finck {
291*c2c66affSColin Finck ControllerConfig = Data;
292*c2c66affSColin Finck
293*c2c66affSColin Finck /*
294*c2c66affSColin Finck * Synchronize the enable state of the PS/2 ports
295*c2c66affSColin Finck * with the flags in the configuration byte.
296*c2c66affSColin Finck */
297*c2c66affSColin Finck Ports[0].IsEnabled = !(ControllerConfig & PS2_CONFIG_KBD_DISABLE);
298*c2c66affSColin Finck Ports[1].IsEnabled = !(ControllerConfig & PS2_CONFIG_AUX_DISABLE);
299*c2c66affSColin Finck
300*c2c66affSColin Finck /*
301*c2c66affSColin Finck * Update the "System enabled" flag of the status register
302*c2c66affSColin Finck * with bit 2 of the controller configuration byte.
303*c2c66affSColin Finck * See: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html#kccb2
304*c2c66affSColin Finck * for more details.
305*c2c66affSColin Finck */
306*c2c66affSColin Finck if (ControllerConfig & PS2_CONFIG_SYSTEM)
307*c2c66affSColin Finck StatusRegister |= PS2_STAT_SYSTEM;
308*c2c66affSColin Finck else
309*c2c66affSColin Finck StatusRegister &= ~PS2_STAT_SYSTEM;
310*c2c66affSColin Finck
311*c2c66affSColin Finck /*
312*c2c66affSColin Finck * Update the "Keyboard enable" flag of the status register
313*c2c66affSColin Finck * with the "Ignore keyboard lock" flag of the controller
314*c2c66affSColin Finck * configuration byte (if set), then reset the latter one.
315*c2c66affSColin Finck * See: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html#kccb3
316*c2c66affSColin Finck * for more details.
317*c2c66affSColin Finck */
318*c2c66affSColin Finck if (ControllerConfig & PS2_CONFIG_NO_KEYLOCK)
319*c2c66affSColin Finck {
320*c2c66affSColin Finck ControllerConfig &= ~PS2_CONFIG_NO_KEYLOCK;
321*c2c66affSColin Finck StatusRegister |= PS2_STAT_KBD_ENABLE;
322*c2c66affSColin Finck }
323*c2c66affSColin Finck
324*c2c66affSColin Finck break;
325*c2c66affSColin Finck }
326*c2c66affSColin Finck
327*c2c66affSColin Finck /* Write to byte N of internal memory */
328*c2c66affSColin Finck case 0x61: case 0x62: case 0x63:
329*c2c66affSColin Finck case 0x64: case 0x65: case 0x66: case 0x67:
330*c2c66affSColin Finck case 0x68: case 0x69: case 0x6A: case 0x6B:
331*c2c66affSColin Finck case 0x6C: case 0x6D: case 0x6E: case 0x6F:
332*c2c66affSColin Finck case 0x70: case 0x71: case 0x72: case 0x73:
333*c2c66affSColin Finck case 0x74: case 0x75: case 0x76: case 0x77:
334*c2c66affSColin Finck case 0x78: case 0x79: case 0x7A: case 0x7B:
335*c2c66affSColin Finck case 0x7C: case 0x7D: case 0x7E: case 0x7F:
336*c2c66affSColin Finck {
337*c2c66affSColin Finck Memory[ControllerCommand & 0x1F] = Data;
338*c2c66affSColin Finck break;
339*c2c66affSColin Finck }
340*c2c66affSColin Finck
341*c2c66affSColin Finck /* Write controller output */
342*c2c66affSColin Finck case 0xD1:
343*c2c66affSColin Finck {
344*c2c66affSColin Finck /* Check if bit 0 is unset */
345*c2c66affSColin Finck if (!(Data & PS2_OUT_CPU_NO_RESET))
346*c2c66affSColin Finck {
347*c2c66affSColin Finck /* CPU disabled - Stop the VDM */
348*c2c66affSColin Finck EmulatorTerminate();
349*c2c66affSColin Finck return;
350*c2c66affSColin Finck }
351*c2c66affSColin Finck
352*c2c66affSColin Finck /* Update the A20 line setting */
353*c2c66affSColin Finck EmulatorSetA20(Data & PS2_OUT_A20_SET);
354*c2c66affSColin Finck
355*c2c66affSColin Finck // FIXME: Should we need to add the status of IRQ1 and IRQ12??
356*c2c66affSColin Finck
357*c2c66affSColin Finck break;
358*c2c66affSColin Finck }
359*c2c66affSColin Finck
360*c2c66affSColin Finck /* Push the data byte into the first PS/2 port queue */
361*c2c66affSColin Finck case 0xD2:
362*c2c66affSColin Finck {
363*c2c66affSColin Finck PS2QueuePush(0, Data);
364*c2c66affSColin Finck break;
365*c2c66affSColin Finck }
366*c2c66affSColin Finck
367*c2c66affSColin Finck /* Push the data byte into the second PS/2 port queue */
368*c2c66affSColin Finck case 0xD3:
369*c2c66affSColin Finck {
370*c2c66affSColin Finck PS2QueuePush(1, Data);
371*c2c66affSColin Finck break;
372*c2c66affSColin Finck }
373*c2c66affSColin Finck
374*c2c66affSColin Finck /*
375*c2c66affSColin Finck * Send a command to the second PS/2 port (by default
376*c2c66affSColin Finck * it is a command for the first PS/2 port)
377*c2c66affSColin Finck */
378*c2c66affSColin Finck case 0xD4:
379*c2c66affSColin Finck {
380*c2c66affSColin Finck PS2SendCommand(1, Data);
381*c2c66affSColin Finck break;
382*c2c66affSColin Finck }
383*c2c66affSColin Finck }
384*c2c66affSColin Finck
385*c2c66affSColin Finck return;
386*c2c66affSColin Finck }
387*c2c66affSColin Finck
388*c2c66affSColin Finck /* By default, send a command to the first PS/2 port */
389*c2c66affSColin Finck PS2SendCommand(0, Data);
390*c2c66affSColin Finck }
391*c2c66affSColin Finck
GeneratePS2Irq(ULONGLONG ElapsedTime)392*c2c66affSColin Finck static VOID FASTCALL GeneratePS2Irq(ULONGLONG ElapsedTime)
393*c2c66affSColin Finck {
394*c2c66affSColin Finck UNREFERENCED_PARAMETER(ElapsedTime);
395*c2c66affSColin Finck
396*c2c66affSColin Finck /*
397*c2c66affSColin Finck * Pop out fresh new data from the PS/2 port output queues, and only
398*c2c66affSColin Finck * in case there is data ready, generate an IRQ1 or IRQ12 depending
399*c2c66affSColin Finck * on whether interrupts are enabled for the given port.
400*c2c66affSColin Finck *
401*c2c66affSColin Finck * NOTE: The first PS/2 port (keyboard) has priority over the second one (mouse).
402*c2c66affSColin Finck */
403*c2c66affSColin Finck if (PS2PortQueueRead(0))
404*c2c66affSColin Finck {
405*c2c66affSColin Finck if (ControllerConfig & PS2_CONFIG_KBD_INT) PicInterruptRequest(1);
406*c2c66affSColin Finck }
407*c2c66affSColin Finck else if (PS2PortQueueRead(1))
408*c2c66affSColin Finck {
409*c2c66affSColin Finck if (ControllerConfig & PS2_CONFIG_AUX_INT) PicInterruptRequest(12);
410*c2c66affSColin Finck }
411*c2c66affSColin Finck }
412*c2c66affSColin Finck
413*c2c66affSColin Finck /* PUBLIC FUNCTIONS ***********************************************************/
414*c2c66affSColin Finck
PS2PortQueueRead(BYTE PS2Port)415*c2c66affSColin Finck BOOLEAN PS2PortQueueRead(BYTE PS2Port)
416*c2c66affSColin Finck {
417*c2c66affSColin Finck BOOLEAN Result = FALSE;
418*c2c66affSColin Finck PPS2_PORT Port;
419*c2c66affSColin Finck
420*c2c66affSColin Finck // NOTE: The first PS/2 port (keyboard) has priority over the second one (mouse).
421*c2c66affSColin Finck
422*c2c66affSColin Finck Port = &Ports[PS2Port];
423*c2c66affSColin Finck
424*c2c66affSColin Finck if (!Port->IsEnabled) return FALSE;
425*c2c66affSColin Finck
426*c2c66affSColin Finck /* Make sure the queue is not empty (fast check) */
427*c2c66affSColin Finck if (Port->QueueEmpty)
428*c2c66affSColin Finck {
429*c2c66affSColin Finck /* Only the keyboard should have its last data latched */
430*c2c66affSColin Finck // FIXME: Alternatively this can be done in PS2ReadData when
431*c2c66affSColin Finck // we read PS2_DATA_PORT. What is the best solution??
432*c2c66affSColin Finck if (PS2Port == 0)
433*c2c66affSColin Finck {
434*c2c66affSColin Finck OutputBuffer = Port->Queue[(Port->QueueStart - 1) % BUFFER_SIZE];
435*c2c66affSColin Finck StatusRegister &= ~PS2_STAT_AUX_OUT_BUF_FULL; // Clear flag: keyboard data
436*c2c66affSColin Finck }
437*c2c66affSColin Finck
438*c2c66affSColin Finck return FALSE;
439*c2c66affSColin Finck }
440*c2c66affSColin Finck
441*c2c66affSColin Finck WaitForSingleObject(Port->QueueMutex, INFINITE);
442*c2c66affSColin Finck
443*c2c66affSColin Finck /*
444*c2c66affSColin Finck * Recheck whether the queue is not empty (it may
445*c2c66affSColin Finck * have changed after having grabbed the mutex).
446*c2c66affSColin Finck */
447*c2c66affSColin Finck if (Port->QueueEmpty) goto Done;
448*c2c66affSColin Finck
449*c2c66affSColin Finck /* Get the data */
450*c2c66affSColin Finck OutputBuffer = Port->Queue[Port->QueueStart];
451*c2c66affSColin Finck
452*c2c66affSColin Finck // StatusRegister &= ~(PS2_STAT_AUX_OUT_BUF_FULL | PS2_STAT_OUT_BUF_FULL);
453*c2c66affSColin Finck
454*c2c66affSColin Finck /* Always set the "Output buffer full" flag */
455*c2c66affSColin Finck StatusRegister |= PS2_STAT_OUT_BUF_FULL;
456*c2c66affSColin Finck
457*c2c66affSColin Finck /* Set the "Auxiliary output buffer full" flag according to where the data came from */
458*c2c66affSColin Finck if (PS2Port == 0)
459*c2c66affSColin Finck StatusRegister &= ~PS2_STAT_AUX_OUT_BUF_FULL; // Clear flag: keyboard data
460*c2c66affSColin Finck else // if (PS2Port == 1)
461*c2c66affSColin Finck StatusRegister |= PS2_STAT_AUX_OUT_BUF_FULL; // Set flag: mouse data
462*c2c66affSColin Finck
463*c2c66affSColin Finck /* Remove the value from the queue */
464*c2c66affSColin Finck Port->QueueStart++;
465*c2c66affSColin Finck Port->QueueStart %= BUFFER_SIZE;
466*c2c66affSColin Finck
467*c2c66affSColin Finck /* Check if the queue is now empty */
468*c2c66affSColin Finck if (Port->QueueStart == Port->QueueEnd)
469*c2c66affSColin Finck Port->QueueEmpty = TRUE;
470*c2c66affSColin Finck
471*c2c66affSColin Finck Result = TRUE;
472*c2c66affSColin Finck
473*c2c66affSColin Finck Done:
474*c2c66affSColin Finck ReleaseMutex(Port->QueueMutex);
475*c2c66affSColin Finck return Result;
476*c2c66affSColin Finck }
477*c2c66affSColin Finck
PS2SetDeviceCmdProc(BYTE PS2Port,LPVOID Param,PS2_DEVICE_CMDPROC DeviceCommand)478*c2c66affSColin Finck VOID PS2SetDeviceCmdProc(BYTE PS2Port, LPVOID Param, PS2_DEVICE_CMDPROC DeviceCommand)
479*c2c66affSColin Finck {
480*c2c66affSColin Finck ASSERT(PS2Port < PS2_PORTS);
481*c2c66affSColin Finck Ports[PS2Port].Param = Param;
482*c2c66affSColin Finck Ports[PS2Port].DeviceCommand = DeviceCommand;
483*c2c66affSColin Finck }
484*c2c66affSColin Finck
485*c2c66affSColin Finck // PS2SendToPort
PS2QueuePush(BYTE PS2Port,BYTE Data)486*c2c66affSColin Finck BOOLEAN PS2QueuePush(BYTE PS2Port, BYTE Data)
487*c2c66affSColin Finck {
488*c2c66affSColin Finck BOOLEAN Result = FALSE;
489*c2c66affSColin Finck PPS2_PORT Port;
490*c2c66affSColin Finck
491*c2c66affSColin Finck ASSERT(PS2Port < PS2_PORTS);
492*c2c66affSColin Finck Port = &Ports[PS2Port];
493*c2c66affSColin Finck
494*c2c66affSColin Finck if (!Port->IsEnabled) return FALSE;
495*c2c66affSColin Finck
496*c2c66affSColin Finck WaitForSingleObject(Port->QueueMutex, INFINITE);
497*c2c66affSColin Finck
498*c2c66affSColin Finck /* Check if the queue is full */
499*c2c66affSColin Finck if (!Port->QueueEmpty && (Port->QueueStart == Port->QueueEnd))
500*c2c66affSColin Finck goto Done;
501*c2c66affSColin Finck
502*c2c66affSColin Finck /* Insert the value in the queue */
503*c2c66affSColin Finck Port->Queue[Port->QueueEnd] = Data;
504*c2c66affSColin Finck Port->QueueEnd++;
505*c2c66affSColin Finck Port->QueueEnd %= BUFFER_SIZE;
506*c2c66affSColin Finck
507*c2c66affSColin Finck /* The queue is not empty anymore */
508*c2c66affSColin Finck Port->QueueEmpty = FALSE;
509*c2c66affSColin Finck
510*c2c66affSColin Finck /* Schedule the IRQ */
511*c2c66affSColin Finck EnableHardwareTimer(IrqTimer);
512*c2c66affSColin Finck
513*c2c66affSColin Finck Result = TRUE;
514*c2c66affSColin Finck
515*c2c66affSColin Finck Done:
516*c2c66affSColin Finck ReleaseMutex(Port->QueueMutex);
517*c2c66affSColin Finck return Result;
518*c2c66affSColin Finck }
519*c2c66affSColin Finck
PS2Initialize(VOID)520*c2c66affSColin Finck BOOLEAN PS2Initialize(VOID)
521*c2c66affSColin Finck {
522*c2c66affSColin Finck /* Initialize the PS/2 ports */
523*c2c66affSColin Finck Ports[0].IsEnabled = FALSE;
524*c2c66affSColin Finck Ports[0].QueueEmpty = TRUE;
525*c2c66affSColin Finck Ports[0].QueueStart = 0;
526*c2c66affSColin Finck Ports[0].QueueEnd = 0;
527*c2c66affSColin Finck Ports[0].QueueMutex = CreateMutex(NULL, FALSE, NULL);
528*c2c66affSColin Finck
529*c2c66affSColin Finck Ports[1].IsEnabled = FALSE;
530*c2c66affSColin Finck Ports[1].QueueEmpty = TRUE;
531*c2c66affSColin Finck Ports[1].QueueStart = 0;
532*c2c66affSColin Finck Ports[1].QueueEnd = 0;
533*c2c66affSColin Finck Ports[1].QueueMutex = CreateMutex(NULL, FALSE, NULL);
534*c2c66affSColin Finck
535*c2c66affSColin Finck /* Register the I/O Ports */
536*c2c66affSColin Finck RegisterIoPort(PS2_CONTROL_PORT, PS2ReadControl, PS2WriteControl);
537*c2c66affSColin Finck RegisterIoPort(PS2_DATA_PORT , PS2ReadData , PS2WriteData );
538*c2c66affSColin Finck
539*c2c66affSColin Finck IrqTimer = CreateHardwareTimer(HARDWARE_TIMER_ONESHOT,
540*c2c66affSColin Finck HZ_TO_NS(100),
541*c2c66affSColin Finck GeneratePS2Irq);
542*c2c66affSColin Finck
543*c2c66affSColin Finck return TRUE;
544*c2c66affSColin Finck }
545*c2c66affSColin Finck
PS2Cleanup(VOID)546*c2c66affSColin Finck VOID PS2Cleanup(VOID)
547*c2c66affSColin Finck {
548*c2c66affSColin Finck DestroyHardwareTimer(IrqTimer);
549*c2c66affSColin Finck
550*c2c66affSColin Finck CloseHandle(Ports[1].QueueMutex);
551*c2c66affSColin Finck CloseHandle(Ports[0].QueueMutex);
552*c2c66affSColin Finck }
553*c2c66affSColin Finck
554*c2c66affSColin Finck /* EOF */
555