mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-08-31 04:13:37 +00:00
[NTVDM]
- When we have few R/W IO port handlers, just register them directly instead of using a wrap-up function. - Remove hardcoded bit flag values. svn path=/trunk/; revision=68710
This commit is contained in:
@@ -5,6 +5,10 @@
|
||||
* PURPOSE: PS/2 controller emulation
|
||||
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
||||
* Hermes Belusca-Maito ([email protected])
|
||||
*
|
||||
* DOCUMENTATION: IBM Personal System/2 Hardware Interface Technical Reference, May 1988 (Section 10)
|
||||
* http://wiki.osdev.org/%228042%22_PS/2_Controller
|
||||
* http://www.computer-engineering.org/ps2keyboard/
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
@@ -45,286 +49,362 @@ typedef struct _PS2_PORT
|
||||
#define PS2_PORTS 2
|
||||
static PS2_PORT Ports[PS2_PORTS];
|
||||
|
||||
static BYTE ControllerConfig = 0x00;
|
||||
static BYTE ControllerCommand = 0x00;
|
||||
static BYTE Memory[0x20]; // PS/2 Controller internal memory
|
||||
#define ControllerConfig Memory[0] // The configuration byte is byte 0
|
||||
|
||||
static BYTE StatusRegister = 0x00;
|
||||
static BYTE StatusRegister = 0x00; // Controller status register
|
||||
// static BYTE InputBuffer = 0x00; // PS/2 Input Buffer
|
||||
static BYTE OutputBuffer = 0x00; // PS/2 Output Buffer
|
||||
|
||||
static BYTE ControllerCommand = 0x00;
|
||||
|
||||
static PHARDWARE_TIMER IrqTimer = NULL;
|
||||
|
||||
/* PRIVATE FUNCTIONS **********************************************************/
|
||||
|
||||
static VOID PS2SendCommand(PPS2_PORT Port, BYTE Command)
|
||||
static VOID PS2SendCommand(BYTE PS2Port, BYTE Command)
|
||||
{
|
||||
if (!Port->IsEnabled) return;
|
||||
PPS2_PORT Port;
|
||||
|
||||
ASSERT(PS2Port < PS2_PORTS);
|
||||
Port = &Ports[PS2Port];
|
||||
|
||||
/*
|
||||
* According to http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html#kccad
|
||||
* any PS/2 command sent reenables the corresponding port.
|
||||
*/
|
||||
if (PS2Port == 0)
|
||||
ControllerConfig &= ~PS2_CONFIG_KBD_DISABLE;
|
||||
else // if (PS2Port == 1)
|
||||
ControllerConfig &= ~PS2_CONFIG_AUX_DISABLE;
|
||||
|
||||
Port->IsEnabled = TRUE;
|
||||
|
||||
/* Call the device command */
|
||||
if (Port->DeviceCommand) Port->DeviceCommand(Port->Param, Command);
|
||||
}
|
||||
|
||||
static BYTE WINAPI PS2ReadPort(USHORT Port)
|
||||
|
||||
static BYTE WINAPI PS2ReadControl(USHORT Port)
|
||||
{
|
||||
if (Port == PS2_CONTROL_PORT)
|
||||
{
|
||||
/*
|
||||
* Be sure bit 4 "Keyboard enable flag" is always set.
|
||||
* Keyboard enable (or keyboard lock) flag values are:
|
||||
* 0: Locked; 1: Not locked.
|
||||
* On IBM PC-ATs this is the state of the hardware keyboard
|
||||
* lock mechanism. It is not widely used, but some programs
|
||||
* still use it: see for example:
|
||||
* http://www.os2museum.com/wp/the-dos-4-0-shell-mouse-mystery/
|
||||
*/
|
||||
StatusRegister |= (1 << 4);
|
||||
UNREFERENCED_PARAMETER(Port);
|
||||
|
||||
// FIXME: Should clear bits 6 and 7 because there are
|
||||
// no timeouts and no parity errors.
|
||||
/*
|
||||
* Be sure the "Keyboard enable" flag is always set.
|
||||
* On IBM PC-ATs this is the state of the hardware keyboard
|
||||
* lock mechanism. It is not widely used, but some programs
|
||||
* still use it, see for example:
|
||||
* http://www.os2museum.com/wp/the-dos-4-0-shell-mouse-mystery/
|
||||
*/
|
||||
StatusRegister |= PS2_STAT_KBD_ENABLE;
|
||||
|
||||
return StatusRegister;
|
||||
}
|
||||
else if (Port == PS2_DATA_PORT)
|
||||
{
|
||||
/*
|
||||
* If there is something to read (response byte from the
|
||||
* controller or data from a PS/2 device), read it.
|
||||
*/
|
||||
if (StatusRegister & (1 << 0)) // || StatusRegister & (1 << 5) for second PS/2 port
|
||||
StatusRegister &= ~(1 << 0); // StatusRegister &= ~(1 << 5);
|
||||
/* We do not have any timeouts nor parity errors */
|
||||
StatusRegister &= ~(PS2_STAT_PARITY_ERROR | PS2_STAT_GEN_TIMEOUT);
|
||||
|
||||
// FIXME: We may check there whether there is data latched in
|
||||
// PS2 ports 1 or 2 (keyboard or mouse) and retrieve it there...
|
||||
|
||||
/* Always return the available byte stored in the output buffer */
|
||||
return OutputBuffer;
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
return StatusRegister;
|
||||
}
|
||||
|
||||
static VOID WINAPI PS2WritePort(USHORT Port, BYTE Data)
|
||||
static BYTE WINAPI PS2ReadData(USHORT Port)
|
||||
{
|
||||
if (Port == PS2_CONTROL_PORT)
|
||||
UNREFERENCED_PARAMETER(Port);
|
||||
|
||||
/*
|
||||
* If there is something to read (response byte from the
|
||||
* controller or data from a PS/2 device), read it.
|
||||
*/
|
||||
StatusRegister &= ~PS2_STAT_OUT_BUF_FULL;
|
||||
|
||||
// Keep the state of the "Auxiliary output buffer full" flag
|
||||
// in order to remember from where the data came from.
|
||||
// StatusRegister &= ~PS2_STAT_AUX_OUT_BUF_FULL;
|
||||
|
||||
// FIXME: We may check there whether there is data latched in
|
||||
// PS2 ports 1 or 2 (keyboard or mouse) and retrieve it there...
|
||||
|
||||
/* Always return the available byte stored in the output buffer */
|
||||
return OutputBuffer;
|
||||
}
|
||||
|
||||
static VOID WINAPI PS2WriteControl(USHORT Port, BYTE Data)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(Port);
|
||||
|
||||
switch (Data)
|
||||
{
|
||||
switch (Data)
|
||||
/* Read configuration byte (byte 0 from internal memory) */
|
||||
case 0x20:
|
||||
/* Read byte N from internal memory */
|
||||
case 0x21: case 0x22: case 0x23:
|
||||
case 0x24: case 0x25: case 0x26: case 0x27:
|
||||
case 0x28: case 0x29: case 0x2A: case 0x2B:
|
||||
case 0x2C: case 0x2D: case 0x2E: case 0x2F:
|
||||
case 0x30: case 0x31: case 0x32: case 0x33:
|
||||
case 0x34: case 0x35: case 0x36: case 0x37:
|
||||
case 0x38: case 0x39: case 0x3A: case 0x3B:
|
||||
case 0x3C: case 0x3D: case 0x3E: case 0x3F:
|
||||
{
|
||||
/* Read configuration byte */
|
||||
case 0x20:
|
||||
{
|
||||
OutputBuffer = ControllerConfig;
|
||||
StatusRegister |= (1 << 0); // There is something to read
|
||||
break;
|
||||
}
|
||||
|
||||
/* Write configuration byte */
|
||||
case 0x60:
|
||||
/* Write controller output port */
|
||||
case 0xD1:
|
||||
/* Write to the first PS/2 port output buffer */
|
||||
case 0xD2:
|
||||
/* Write to the second PS/2 port output buffer */
|
||||
case 0xD3:
|
||||
/* Write to the second PS/2 port input buffer */
|
||||
case 0xD4:
|
||||
{
|
||||
/* These commands require a response */
|
||||
ControllerCommand = Data;
|
||||
StatusRegister |= (1 << 3); // This is a controller command
|
||||
break;
|
||||
}
|
||||
|
||||
/* Disable second PS/2 port */
|
||||
case 0xA7:
|
||||
{
|
||||
Ports[1].IsEnabled = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Enable second PS/2 port */
|
||||
case 0xA8:
|
||||
{
|
||||
Ports[1].IsEnabled = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Test second PS/2 port */
|
||||
case 0xA9:
|
||||
{
|
||||
OutputBuffer = 0x00; // Success code
|
||||
StatusRegister |= (1 << 0); // There is something to read
|
||||
break;
|
||||
}
|
||||
|
||||
/* Test PS/2 controller */
|
||||
case 0xAA:
|
||||
{
|
||||
OutputBuffer = 0x55; // Success code
|
||||
StatusRegister |= (1 << 0); // There is something to read
|
||||
break;
|
||||
}
|
||||
|
||||
/* Test first PS/2 port */
|
||||
case 0xAB:
|
||||
{
|
||||
OutputBuffer = 0x00; // Success code
|
||||
StatusRegister |= (1 << 0); // There is something to read
|
||||
break;
|
||||
}
|
||||
|
||||
/* Disable first PS/2 port */
|
||||
case 0xAD:
|
||||
{
|
||||
Ports[0].IsEnabled = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Enable first PS/2 port */
|
||||
case 0xAE:
|
||||
{
|
||||
Ports[0].IsEnabled = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read controller output port */
|
||||
case 0xD0:
|
||||
{
|
||||
/* Bit 0 always set, and bit 1 is the A20 gate state */
|
||||
OutputBuffer = (!!EmulatorGetA20() << 1) | 0x01;
|
||||
// FIXME: Set the status of IRQ1 and IRQ12
|
||||
|
||||
StatusRegister |= (1 << 0); // There is something to read
|
||||
break;
|
||||
}
|
||||
|
||||
/* CPU Reset */
|
||||
case 0xF0:
|
||||
case 0xF2:
|
||||
case 0xF4:
|
||||
case 0xF6:
|
||||
case 0xF8:
|
||||
case 0xFA:
|
||||
case 0xFC:
|
||||
case 0xFE:
|
||||
{
|
||||
/* Stop the VDM */
|
||||
EmulatorTerminate();
|
||||
return;
|
||||
}
|
||||
OutputBuffer = Memory[Data & 0x1F];
|
||||
StatusRegister |= PS2_STAT_OUT_BUF_FULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (Port == PS2_DATA_PORT)
|
||||
{
|
||||
/* Check if the controller is waiting for a response */
|
||||
if (StatusRegister & (1 << 3)) // If we have data for the controller
|
||||
|
||||
/* Write configuration byte (byte 0 from internal memory) */
|
||||
case 0x60:
|
||||
/* Write to byte N of internal memory */
|
||||
case 0x61: case 0x62: case 0x63:
|
||||
case 0x64: case 0x65: case 0x66: case 0x67:
|
||||
case 0x68: case 0x69: case 0x6A: case 0x6B:
|
||||
case 0x6C: case 0x6D: case 0x6E: case 0x6F:
|
||||
case 0x70: case 0x71: case 0x72: case 0x73:
|
||||
case 0x74: case 0x75: case 0x76: case 0x77:
|
||||
case 0x78: case 0x79: case 0x7A: case 0x7B:
|
||||
case 0x7C: case 0x7D: case 0x7E: case 0x7F:
|
||||
|
||||
/* Write controller output port */
|
||||
case 0xD1:
|
||||
/* Write to the first PS/2 port output buffer */
|
||||
case 0xD2:
|
||||
/* Write to the second PS/2 port output buffer */
|
||||
case 0xD3:
|
||||
/* Write to the second PS/2 port input buffer */
|
||||
case 0xD4:
|
||||
{
|
||||
StatusRegister &= ~(1 << 3);
|
||||
/* These commands require a response */
|
||||
ControllerCommand = Data;
|
||||
StatusRegister |= PS2_STAT_COMMAND;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check which command it was */
|
||||
switch (ControllerCommand)
|
||||
/* Disable second PS/2 port */
|
||||
case 0xA7:
|
||||
{
|
||||
ControllerConfig |= PS2_CONFIG_AUX_DISABLE;
|
||||
Ports[1].IsEnabled = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Enable second PS/2 port */
|
||||
case 0xA8:
|
||||
{
|
||||
ControllerConfig &= ~PS2_CONFIG_AUX_DISABLE;
|
||||
Ports[1].IsEnabled = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Test second PS/2 port */
|
||||
case 0xA9:
|
||||
{
|
||||
OutputBuffer = 0x00; // Success code
|
||||
StatusRegister |= PS2_STAT_OUT_BUF_FULL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Test PS/2 controller */
|
||||
case 0xAA:
|
||||
{
|
||||
OutputBuffer = 0x55; // Success code
|
||||
StatusRegister |= PS2_STAT_OUT_BUF_FULL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Test first PS/2 port */
|
||||
case 0xAB:
|
||||
{
|
||||
OutputBuffer = 0x00; // Success code
|
||||
StatusRegister |= PS2_STAT_OUT_BUF_FULL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Disable first PS/2 port */
|
||||
case 0xAD:
|
||||
{
|
||||
ControllerConfig |= PS2_CONFIG_KBD_DISABLE;
|
||||
Ports[0].IsEnabled = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Enable first PS/2 port */
|
||||
case 0xAE:
|
||||
{
|
||||
ControllerConfig &= ~PS2_CONFIG_KBD_DISABLE;
|
||||
Ports[0].IsEnabled = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read controller output port */
|
||||
case 0xD0:
|
||||
{
|
||||
/* Bit 0 always set, and bit 1 is the A20 gate state */
|
||||
OutputBuffer = (!!EmulatorGetA20() << 1) | PS2_OUT_CPU_NO_RESET;
|
||||
|
||||
/* Set IRQ1 state */
|
||||
if (ControllerConfig & PS2_CONFIG_KBD_INT)
|
||||
OutputBuffer |= PS2_OUT_IRQ01;
|
||||
else
|
||||
OutputBuffer &= ~PS2_OUT_IRQ01;
|
||||
|
||||
/* Set IRQ12 state */
|
||||
if (ControllerConfig & PS2_CONFIG_AUX_INT)
|
||||
OutputBuffer |= PS2_OUT_IRQ12;
|
||||
else
|
||||
OutputBuffer &= ~PS2_OUT_IRQ12;
|
||||
|
||||
/* Check whether data is already present */
|
||||
if (StatusRegister & PS2_STAT_OUT_BUF_FULL)
|
||||
{
|
||||
/* Write configuration byte */
|
||||
case 0x60:
|
||||
{
|
||||
ControllerConfig = Data;
|
||||
|
||||
/*
|
||||
* Update bit 2 "System flag" of the status register
|
||||
* with bit 2 of the controller configuration byte.
|
||||
* See: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html#kccb2
|
||||
* for more details.
|
||||
*/
|
||||
if (ControllerConfig & (1 << 2))
|
||||
StatusRegister |= (1 << 2);
|
||||
else
|
||||
StatusRegister &= ~(1 << 2);
|
||||
|
||||
/*
|
||||
* Update bit 4 "Keyboard enable flag" of the status register
|
||||
* with bit 3 "Ignore keyboard lock" of the controller
|
||||
* configuration byte (if set), then reset the latter one.
|
||||
* See: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html#kccb3
|
||||
* for more details.
|
||||
*/
|
||||
if (ControllerConfig & (1 << 3))
|
||||
{
|
||||
StatusRegister |= (1 << 4);
|
||||
ControllerConfig &= ~(1 << 3);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Write controller output */
|
||||
case 0xD1:
|
||||
{
|
||||
/* Check if bit 0 is unset */
|
||||
if (!(Data & (1 << 0)))
|
||||
{
|
||||
/* CPU disabled - Stop the VDM */
|
||||
EmulatorTerminate();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update the A20 line setting */
|
||||
EmulatorSetA20(Data & (1 << 1));
|
||||
|
||||
// FIXME: Add the status of IRQ1 and IRQ12
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Push the data byte into the first PS/2 port queue */
|
||||
case 0xD2:
|
||||
{
|
||||
PS2QueuePush(0, Data);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Push the data byte into the second PS/2 port queue */
|
||||
case 0xD3:
|
||||
{
|
||||
PS2QueuePush(1, Data);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a command to the second PS/2 port (by default
|
||||
* it is a command for the first PS/2 port)
|
||||
*/
|
||||
case 0xD4:
|
||||
{
|
||||
PS2SendCommand(&Ports[1], Data);
|
||||
break;
|
||||
}
|
||||
if (StatusRegister & PS2_STAT_AUX_OUT_BUF_FULL)
|
||||
OutputBuffer |= PS2_OUT_AUX_DATA;
|
||||
else
|
||||
OutputBuffer |= PS2_OUT_KBD_DATA;
|
||||
}
|
||||
|
||||
StatusRegister |= PS2_STAT_OUT_BUF_FULL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* CPU Reset */
|
||||
case 0xF0: case 0xF2: case 0xF4: case 0xF6:
|
||||
case 0xF8: case 0xFA: case 0xFC: case 0xFE:
|
||||
{
|
||||
/* Stop the VDM */
|
||||
EmulatorTerminate();
|
||||
return;
|
||||
}
|
||||
|
||||
/* By default, send a command to the first PS/2 port */
|
||||
PS2SendCommand(&Ports[0], Data);
|
||||
}
|
||||
}
|
||||
|
||||
static VOID WINAPI PS2WriteData(USHORT Port, BYTE Data)
|
||||
{
|
||||
/* Check if the controller is waiting for a response */
|
||||
if (StatusRegister & PS2_STAT_COMMAND)
|
||||
{
|
||||
StatusRegister &= ~PS2_STAT_COMMAND;
|
||||
|
||||
/* Check which command it was */
|
||||
switch (ControllerCommand)
|
||||
{
|
||||
/* Write configuration byte (byte 0 from internal memory) */
|
||||
case 0x60:
|
||||
{
|
||||
ControllerConfig = Data;
|
||||
|
||||
/*
|
||||
* Synchronize the enable state of the PS/2 ports
|
||||
* with the flags in the configuration byte.
|
||||
*/
|
||||
Ports[0].IsEnabled = !(ControllerConfig & PS2_CONFIG_KBD_DISABLE);
|
||||
Ports[1].IsEnabled = !(ControllerConfig & PS2_CONFIG_AUX_DISABLE);
|
||||
|
||||
/*
|
||||
* Update the "System enabled" flag of the status register
|
||||
* with bit 2 of the controller configuration byte.
|
||||
* See: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html#kccb2
|
||||
* for more details.
|
||||
*/
|
||||
if (ControllerConfig & PS2_CONFIG_SYSTEM)
|
||||
StatusRegister |= PS2_STAT_SYSTEM;
|
||||
else
|
||||
StatusRegister &= ~PS2_STAT_SYSTEM;
|
||||
|
||||
/*
|
||||
* Update the "Keyboard enable" flag of the status register
|
||||
* with the "Ignore keyboard lock" flag of the controller
|
||||
* configuration byte (if set), then reset the latter one.
|
||||
* See: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html#kccb3
|
||||
* for more details.
|
||||
*/
|
||||
if (ControllerConfig & PS2_CONFIG_NO_KEYLOCK)
|
||||
{
|
||||
ControllerConfig &= ~PS2_CONFIG_NO_KEYLOCK;
|
||||
StatusRegister |= PS2_STAT_KBD_ENABLE;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Write to byte N of internal memory */
|
||||
case 0x61: case 0x62: case 0x63:
|
||||
case 0x64: case 0x65: case 0x66: case 0x67:
|
||||
case 0x68: case 0x69: case 0x6A: case 0x6B:
|
||||
case 0x6C: case 0x6D: case 0x6E: case 0x6F:
|
||||
case 0x70: case 0x71: case 0x72: case 0x73:
|
||||
case 0x74: case 0x75: case 0x76: case 0x77:
|
||||
case 0x78: case 0x79: case 0x7A: case 0x7B:
|
||||
case 0x7C: case 0x7D: case 0x7E: case 0x7F:
|
||||
{
|
||||
Memory[ControllerCommand & 0x1F] = Data;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Write controller output */
|
||||
case 0xD1:
|
||||
{
|
||||
/* Check if bit 0 is unset */
|
||||
if (!(Data & PS2_OUT_CPU_NO_RESET))
|
||||
{
|
||||
/* CPU disabled - Stop the VDM */
|
||||
EmulatorTerminate();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update the A20 line setting */
|
||||
EmulatorSetA20(Data & PS2_OUT_A20_SET);
|
||||
|
||||
// FIXME: Should we need to add the status of IRQ1 and IRQ12??
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Push the data byte into the first PS/2 port queue */
|
||||
case 0xD2:
|
||||
{
|
||||
PS2QueuePush(0, Data);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Push the data byte into the second PS/2 port queue */
|
||||
case 0xD3:
|
||||
{
|
||||
PS2QueuePush(1, Data);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a command to the second PS/2 port (by default
|
||||
* it is a command for the first PS/2 port)
|
||||
*/
|
||||
case 0xD4:
|
||||
{
|
||||
PS2SendCommand(1, Data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* By default, send a command to the first PS/2 port */
|
||||
PS2SendCommand(0, Data);
|
||||
}
|
||||
|
||||
static VOID FASTCALL GeneratePS2Irq(ULONGLONG ElapsedTime)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(ElapsedTime);
|
||||
|
||||
/* Generate an IRQ 1 if there is data ready in the output queue */
|
||||
/*
|
||||
* Pop out fresh new data from the PS/2 port output queues, and only
|
||||
* in case there is data ready, generate an IRQ1 or IRQ12 depending
|
||||
* on whether interrupts are enabled for the given port.
|
||||
*
|
||||
* NOTE: The first PS/2 port (keyboard) has priority over the second one (mouse).
|
||||
*/
|
||||
if (PS2PortQueueRead(0))
|
||||
{
|
||||
/* Generate an interrupt if interrupts for the first PS/2 port are enabled */
|
||||
if (ControllerConfig & 0x01) PicInterruptRequest(1);
|
||||
return;
|
||||
if (ControllerConfig & PS2_CONFIG_KBD_INT) PicInterruptRequest(1);
|
||||
}
|
||||
|
||||
/* Generate an IRQ 12 if there is data ready in the output queue */
|
||||
if (PS2PortQueueRead(1))
|
||||
else if (PS2PortQueueRead(1))
|
||||
{
|
||||
/* Generate an interrupt if interrupts for the second PS/2 port are enabled */
|
||||
if (ControllerConfig & 0x02) PicInterruptRequest(12);
|
||||
return;
|
||||
if (ControllerConfig & PS2_CONFIG_AUX_INT) PicInterruptRequest(12);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,10 +412,11 @@ static VOID FASTCALL GeneratePS2Irq(ULONGLONG ElapsedTime)
|
||||
|
||||
BOOLEAN PS2PortQueueRead(BYTE PS2Port)
|
||||
{
|
||||
BOOLEAN Result = TRUE;
|
||||
BOOLEAN Result = FALSE;
|
||||
PPS2_PORT Port;
|
||||
|
||||
if (PS2Port >= PS2_PORTS) return FALSE;
|
||||
// NOTE: The first PS/2 port (keyboard) has priority over the second one (mouse).
|
||||
|
||||
Port = &Ports[PS2Port];
|
||||
|
||||
if (!Port->IsEnabled) return FALSE;
|
||||
@@ -344,11 +425,12 @@ BOOLEAN PS2PortQueueRead(BYTE PS2Port)
|
||||
if (Port->QueueEmpty)
|
||||
{
|
||||
/* Only the keyboard should have its last data latched */
|
||||
// FIXME: Alternatively this can be done in PS2ReadPort when
|
||||
// FIXME: Alternatively this can be done in PS2ReadData when
|
||||
// we read PS2_DATA_PORT. What is the best solution??
|
||||
if (PS2Port == 0)
|
||||
{
|
||||
OutputBuffer = Port->Queue[(Port->QueueStart - 1) % BUFFER_SIZE];
|
||||
StatusRegister &= ~PS2_STAT_AUX_OUT_BUF_FULL; // Clear flag: keyboard data
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
@@ -360,16 +442,21 @@ BOOLEAN PS2PortQueueRead(BYTE PS2Port)
|
||||
* Recheck whether the queue is not empty (it may
|
||||
* have changed after having grabbed the mutex).
|
||||
*/
|
||||
if (Port->QueueEmpty)
|
||||
{
|
||||
Result = FALSE;
|
||||
goto Done;
|
||||
}
|
||||
if (Port->QueueEmpty) goto Done;
|
||||
|
||||
/* Get the data */
|
||||
OutputBuffer = Port->Queue[Port->QueueStart];
|
||||
StatusRegister |= (1 << 0); // There is something to read
|
||||
// Sometimes StatusRegister |= (1 << 5); for the second PS/2 port
|
||||
|
||||
// StatusRegister &= ~(PS2_STAT_AUX_OUT_BUF_FULL | PS2_STAT_OUT_BUF_FULL);
|
||||
|
||||
/* Always set the "Output buffer full" flag */
|
||||
StatusRegister |= PS2_STAT_OUT_BUF_FULL;
|
||||
|
||||
/* Set the "Auxiliary output buffer full" flag according to where the data came from */
|
||||
if (PS2Port == 0)
|
||||
StatusRegister &= ~PS2_STAT_AUX_OUT_BUF_FULL; // Clear flag: keyboard data
|
||||
else // if (PS2Port == 1)
|
||||
StatusRegister |= PS2_STAT_AUX_OUT_BUF_FULL; // Set flag: mouse data
|
||||
|
||||
/* Remove the value from the queue */
|
||||
Port->QueueStart++;
|
||||
@@ -379,6 +466,8 @@ BOOLEAN PS2PortQueueRead(BYTE PS2Port)
|
||||
if (Port->QueueStart == Port->QueueEnd)
|
||||
Port->QueueEmpty = TRUE;
|
||||
|
||||
Result = TRUE;
|
||||
|
||||
Done:
|
||||
ReleaseMutex(Port->QueueMutex);
|
||||
return Result;
|
||||
@@ -386,8 +475,7 @@ Done:
|
||||
|
||||
VOID PS2SetDeviceCmdProc(BYTE PS2Port, LPVOID Param, PS2_DEVICE_CMDPROC DeviceCommand)
|
||||
{
|
||||
if (PS2Port >= PS2_PORTS) return;
|
||||
|
||||
ASSERT(PS2Port < PS2_PORTS);
|
||||
Ports[PS2Port].Param = Param;
|
||||
Ports[PS2Port].DeviceCommand = DeviceCommand;
|
||||
}
|
||||
@@ -395,10 +483,10 @@ VOID PS2SetDeviceCmdProc(BYTE PS2Port, LPVOID Param, PS2_DEVICE_CMDPROC DeviceCo
|
||||
// PS2SendToPort
|
||||
BOOLEAN PS2QueuePush(BYTE PS2Port, BYTE Data)
|
||||
{
|
||||
BOOLEAN Result = TRUE;
|
||||
BOOLEAN Result = FALSE;
|
||||
PPS2_PORT Port;
|
||||
|
||||
if (PS2Port >= PS2_PORTS) return FALSE;
|
||||
ASSERT(PS2Port < PS2_PORTS);
|
||||
Port = &Ports[PS2Port];
|
||||
|
||||
if (!Port->IsEnabled) return FALSE;
|
||||
@@ -407,10 +495,7 @@ BOOLEAN PS2QueuePush(BYTE PS2Port, BYTE Data)
|
||||
|
||||
/* Check if the queue is full */
|
||||
if (!Port->QueueEmpty && (Port->QueueStart == Port->QueueEnd))
|
||||
{
|
||||
Result = FALSE;
|
||||
goto Done;
|
||||
}
|
||||
|
||||
/* Insert the value in the queue */
|
||||
Port->Queue[Port->QueueEnd] = Data;
|
||||
@@ -423,6 +508,8 @@ BOOLEAN PS2QueuePush(BYTE PS2Port, BYTE Data)
|
||||
/* Schedule the IRQ */
|
||||
EnableHardwareTimer(IrqTimer);
|
||||
|
||||
Result = TRUE;
|
||||
|
||||
Done:
|
||||
ReleaseMutex(Port->QueueMutex);
|
||||
return Result;
|
||||
@@ -444,8 +531,8 @@ BOOLEAN PS2Initialize(VOID)
|
||||
Ports[1].QueueMutex = CreateMutex(NULL, FALSE, NULL);
|
||||
|
||||
/* Register the I/O Ports */
|
||||
RegisterIoPort(PS2_CONTROL_PORT, PS2ReadPort, PS2WritePort);
|
||||
RegisterIoPort(PS2_DATA_PORT , PS2ReadPort, PS2WritePort);
|
||||
RegisterIoPort(PS2_CONTROL_PORT, PS2ReadControl, PS2WriteControl);
|
||||
RegisterIoPort(PS2_DATA_PORT , PS2ReadData , PS2WriteData );
|
||||
|
||||
IrqTimer = CreateHardwareTimer(HARDWARE_TIMER_ONESHOT,
|
||||
HZ_TO_NS(100),
|
||||
|
||||
@@ -12,9 +12,39 @@
|
||||
|
||||
/* DEFINES ********************************************************************/
|
||||
|
||||
/* I/O Ports */
|
||||
#define PS2_DATA_PORT 0x60
|
||||
#define PS2_CONTROL_PORT 0x64
|
||||
|
||||
/* Controller Status Register flags */
|
||||
#define PS2_STAT_OUT_BUF_FULL (1 << 0)
|
||||
// #define PS2_STAT_IN_BUF_FULL (1 << 1)
|
||||
#define PS2_STAT_SYSTEM (1 << 2)
|
||||
#define PS2_STAT_COMMAND (1 << 3)
|
||||
#define PS2_STAT_KBD_ENABLE (1 << 4) // 0: Locked; 1: Not locked
|
||||
#define PS2_STAT_AUX_OUT_BUF_FULL (1 << 5)
|
||||
#define PS2_STAT_GEN_TIMEOUT (1 << 6)
|
||||
#define PS2_STAT_PARITY_ERROR (1 << 7)
|
||||
|
||||
/* Controller Configuration Byte flags */
|
||||
#define PS2_CONFIG_KBD_INT (1 << 0)
|
||||
#define PS2_CONFIG_AUX_INT (1 << 1)
|
||||
#define PS2_CONFIG_SYSTEM (1 << 2)
|
||||
#define PS2_CONFIG_NO_KEYLOCK (1 << 3)
|
||||
#define PS2_CONFIG_KBD_DISABLE (1 << 4)
|
||||
#define PS2_CONFIG_AUX_DISABLE (1 << 5)
|
||||
// #define PS2_CONFIG_KBD_XLAT (1 << 6)
|
||||
|
||||
/* Output Port flags */
|
||||
#define PS2_OUT_CPU_NO_RESET (1 << 0)
|
||||
#define PS2_OUT_A20_SET (1 << 1)
|
||||
#define PS2_OUT_AUX_DATA (1 << 2)
|
||||
// #define PS2_OUT_AUX_CLOCK (1 << 3)
|
||||
#define PS2_OUT_IRQ01 (1 << 4)
|
||||
#define PS2_OUT_IRQ12 (1 << 5)
|
||||
// #define PS2_OUT_KBD_CLOCK (1 << 6)
|
||||
#define PS2_OUT_KBD_DATA (1 << 7)
|
||||
|
||||
typedef VOID (WINAPI *PS2_DEVICE_CMDPROC)(LPVOID Param, BYTE Command);
|
||||
|
||||
/* FUNCTIONS ******************************************************************/
|
||||
|
||||
Reference in New Issue
Block a user