📄 ps2port.cpp
字号:
/*
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-1998 Microsoft Corporation
*/
#include <windows.h>
#include <ceddk.h>
#include "ps2port.hpp"
static const unsigned int PollLimitLoop = 500; //about 500ms
//
// FPGA PS/2 Control Register bit.
//
static const UINT8 Ps2EnableInterrupts = 0x08;
static const UINT8 Ps2DisableInterface = 0x04;
static const UINT8 Ps2EnableFIFO = 0x10;
//
// FPGA PS/2 Status register flags.
//
static const UINT8 stsPs2RxBufFull = 0x01;
static const UINT8 stsPs2TxBufFull = 0x02;
//
// Commands sent to the keyboard.
//
static const UINT8 cmdKeybdReset = 0xFF;
static const UINT8 cmdKeybdLights = 0xED;
static const UINT8 cmdKeybdChangeCode = 0xF0;
//
// Commands sent to the mouse.
//
static const UINT8 cmdMouseReadId = 0xF2;
static const UINT8 cmdMouseEnable = 0xF4;
/*++
Ps2Port::
RxBufIsFull:
Returns true if the Receiving buffer is full.
--*/
inline
bool
Ps2Port::
RxBufIsFull(
unsigned int port
)
{
return (ui8StatusRead(port) & stsPs2RxBufFull);
}
bool
Ps2Port::
RxBufPollForEmpty(
unsigned int port
)
{
unsigned int i=0;
// Poll while the Tx buffer is full
while ( RxBufIsFull(port) ) {
ui8RxBufRead(port);
RtcWait(1); // Time passes.
if( i > PollLimitLoop)
{
ERRORMSG(1,(TEXT("Ps2Port::TxBufPollForEmpty: too long waiting for Tx buffer empty.\r\n")));
break;
}
i++;
}
if (RxBufIsFull(port))
return FALSE;
else
return TRUE;
}
/*++
Ps2Port::
TxBufPollForEmpty:
SPINS waiting for the Tx buffer to go empty. Returns true if the
buffer is empty, false if the buffer did not go empty.
--*/
bool
Ps2Port::
TxBufPollForEmpty(
unsigned int port
)
{
bool bRet = false;
UINT8 ui8Status;
unsigned int i=0;
// Poll while the Tx buffer is full
ui8Status = ui8StatusRead(port);
if ( ui8Status & stsPs2TxBufFull )
{
for ( ; ; )
{
RtcWait(1); // Time passes.
// See if buffer is still full.
ui8Status = ui8StatusRead(port);
if ( ui8Status & stsPs2TxBufFull )
{
// If still full, check elapsed time.
// Unsigned arithmetic deals with timer rollover.
if( i > PollLimitLoop)
{
ERRORMSG(1,(TEXT("Ps2Port::TxBufPollForEmpty: too long waiting for Tx buffer empty.\r\n")));
break;
}
}
else
{
bRet = true;
break;
}
i++;
}
}
else
{
bRet = true;
}
return bRet;
}
/*++
Ps2Port::
RxBufPollRead:
SPINS waiting for the Receiving buffer to go full and then reads the data
from the Rx buffer. Returns true if the buffer goes full, false if
the buffer never goes full.
--*/
bool
Ps2Port::
RxBufPollRead(
unsigned int port,
UINT8 *pui8
)
{
bool bRet = false;
UINT8 ui8Status;
unsigned int i=0;
// Check for data ready first.
ui8Status = ui8StatusRead(port);
if ( ui8Status & stsPs2RxBufFull )
{
bRet = true;
}
else
{
// Start polling if Rx data not ready.
for ( ; ; )
{
RtcWait(1); // Time passes.
// Check for data again.
ui8Status = ui8StatusRead(port);
if ( ui8Status & stsPs2RxBufFull )
{
bRet = true;
break;
}
// Check elapsed time.
// Unsigned arithmetic deals with timer rollover.
if( i > PollLimitLoop)
{
//ERRORMSG(1,(TEXT("Ps2Port::RxBufPollRead: too long waiting for output buffer full.\r\n")));
break;
}
i++;
}
}
// May as well read it even if we failed.
*pui8 = ui8RxBufRead(port);
return bRet;
}
/*++
Ps2Port::
EnterWrite:
Must be called before writing data which is to be sent to the keyboard or
mouse. Takes the write critical section if successful.
Disables the keyboard and mouse interrupts.
Disables the keyboard and mouse interfaces.
May be called multiple times but only the first caller actually does the
disabling.
A count is kept to verify that the number of calls to LeaveWrite match the
number of calls to EnterWrite.
Returns true if successful, false if there is an error. If there is an
error, the write critical section is not held.
--*/
bool
Ps2Port::
EnterWrite(
unsigned int port
)
{
UINT8 ui8CmdByte;
// Only one writer at a time.
EnterCriticalSection(&m_csWrite);
// If we are already in, we're done.
if ( m_cEnterWrites++ )
{
goto leave_success;
}
if(port == KeybdPort)
{
ui8CmdByte = m_ui8KeybdControlByte;
}
else if(port == MousePort)
{
ui8CmdByte = m_ui8MouseControlByte;
}
if ( !TxBufPollForEmpty(port) )
{
ERRORMSG(1, (TEXT("Ps2Port::enterWrite Not TxBufEmpty.\r\n")));
ASSERT(0);
goto leave_fail;
}
// Disable the interface.
ui8CmdByte |= Ps2DisableInterface;
// Disable the interrupts.
ui8CmdByte &= ~Ps2EnableInterrupts;
ControlRegWrite(port, ui8CmdByte);
// Wait more than 100us.
RtcWait(1);
// Read any junk data.
/* while( RxBufIsFull(port) )
{
ui8RxBufRead(port);
}
*/
if (!RxBufPollForEmpty(port)) {
ERRORMSG(1, (TEXT("Ps2Port::enterWrite Not RxBufPooForEmpty.\r\n")));
// ASSERT(0);
// goto leave_fail;
};
leave_success:
// Keep the critical section if we succeed.
return true;
leave_fail:
// Don't hold the critical section if we fail.
m_cEnterWrites--;
LeaveCriticalSection(&m_csWrite);
return false;
}
/*++
Ps2Port::
LeaveWrite:
Counterpart to EnterWrite. The last caller to LeaveWrite re-enables the
interrupts and interfaces.
Returns true if successful.
--*/
bool
Ps2Port::
LeaveWrite(
unsigned int port
)
{
UINT8 ui8Data;
bool bRet = false;
// Haven't really seen this but it's worth checking.
if ( m_cEnterWrites == 0 )
{
//ERRORMSG(1,(TEXT("LeaveWrite: too many calls to LeaveWrite.\r\n")));
return bRet;
}
// Last one out turns everything back on.
if ( m_cEnterWrites != 1 )
{
bRet = true;
goto leave;
}
// Wait more than 100us.
RtcWait(1);
// Only enable Interface.
if(port == KeybdPort)
{
ControlRegWrite(port, m_ui8KeybdControlByte & ~Ps2EnableInterrupts);
}
else if(port == MousePort)
{
ControlRegWrite(port, m_ui8MouseControlByte & ~Ps2EnableInterrupts);
}
// Wait until Tx end.
if ( !TxBufPollForEmpty(port) )
{
//ERRORMSG(1, (TEXT("Ps2Port::enterWrite Not TxBufEmpty.\r\n")));
//ASSERT(0);
goto leave;
}
if ( !RxBufPollRead(port, &ui8Data) )
{
goto leave;
}
if ( ui8Data != 0xFA )
{
//ERRORMSG(1, (TEXT("Ps2Port::LeaveWrite Not correct ACK data.\r\n")));
//ASSERT(0);
goto leave;
}
// Put back original control bits.
if(port == KeybdPort)
{
ControlRegWrite(port, m_ui8KeybdControlByte);
}
else if(port == MousePort)
{
ControlRegWrite(port, m_ui8MouseControlByte);
}
bRet = true;
leave:
--m_cEnterWrites;
LeaveCriticalSection(&m_csWrite);
return bRet;
}
/*++
Ps2Port::
TxCommandPut:
Puts data in the Tx buffer. Caller must have called EnterWrite
before calling.
Returns true if successful.
--*/
bool
Ps2Port::
TxCommandPut(
unsigned int port,
UINT8 ui8Data
)
{
bool bRet = false;
EnterWrite(port);
if ( !m_cEnterWrites )
{
//ERRORMSG(1,(TEXT("Did not use EnterWrite before calling TxBufferPut.\r\n")));
//ASSERT(0);
goto leave;
}
// Write command to keyboard.
TxDataWrite(port, ui8Data);
LeaveWrite(port);
bRet = true;
leave:
return bRet;
}
/*++
Ps2Port::
KeyboardReset:
Sends the keyboard reset command to the keyboard and reads the response.
Returns true if the success response is read.
--*/
bool
Ps2Port::
KeyboardReset(
void
)
{
bool bRet = false;
UINT8 ui8Data;
if ( !TxCommandPut(KeybdPort, cmdKeybdReset) )
{
goto leave;
}
if ( !RxBufPollRead(KeybdPort, &ui8Data) )
{
goto leave;
}
if ( ui8Data != 0xAA )
{
//ERRORMSG(1, (TEXT("Ps2Port::KeyboardReset Not ACK data.\r\n")));
//ASSERT(0);
goto leave;
}
bRet = true;
leave:
if ( !bRet )
{
}
return bRet;
}
/*++
Ps2Port::
KeyboardLights:
Sets the keyboard indicator lights.
--*/
void
Ps2Port::
KeyboardLights(
unsigned int fLights
)
{
if ( !TxCommandPut(KeybdPort, cmdKeybdLights) )
{
goto leave;
}
TxCommandPut(KeybdPort, fLights);
leave:
return;
}
/*++
Ps2Port::
KeybdChangScanToSystem:
Change the keyboard mode from Scan code to System Scan code.
--*/
void
Ps2Port::
KeybdChangScanToSystem(
void
)
{
if ( !TxCommandPut(KeybdPort, cmdKeybdChangeCode) )
{
goto leave;
}
// Change keyboad data from scan code to system scan code.
TxCommandPut(KeybdPort, 1);
leave:
return;
}
/*++
Ps2Port::
MouseTest:
Tests for a mouse present on the port.
Returns true if a mouse is found.
--*/
bool
Ps2Port::
MouseTest(
void
)
{
bool bRet = false;
UINT8 ui8Data;
TxCommandPut(MousePort, cmdMouseReadId);
if ( !RxBufPollRead(MousePort, &ui8Data) )
{
goto leave;
}
bRet = true;
m_bMouseFound = TRUE;
TxCommandPut(MousePort, cmdMouseEnable);
leave:
if ( !bRet )
{
//RETAILMSG(1,(TEXT("MouseTest fails. Mouse probably not present.\r\n")));
}
return bRet;
}
/*++
Ps2Port::
DataRead:
Reads data from PS2 Receiving port.
--*/
bool
Ps2Port::
DataRead(
unsigned int port,
UINT8 *pui8Data
)
{
if ( !RxBufIsFull(port) )
{
// ERRORMSG(1, (TEXT("DataRead: Port data not ready\r\n")));
}
*pui8Data = ui8RxBufRead(port);
return true;
}
/*++
Ps2Port::
InterruptEnable:
Enables Keyboard/Mouse interrupts.
--*/
void
Ps2Port::
InterruptEnable(
unsigned int port
)
{
if( port == KeybdPort )
{
m_ui8KeybdControlByte |= Ps2EnableInterrupts;
ControlRegWrite(KeybdPort, m_ui8KeybdControlByte);
}
else if( port == MousePort )
{
m_ui8MouseControlByte |= Ps2EnableInterrupts;
ControlRegWrite(MousePort, m_ui8MouseControlByte);
}
return;
}
/*++
Ps2Port::
Initialize:
Initializes the Ps2Port object.
--*/
bool
Ps2Port::
Initialize()
{
bool bRet = false;
m_iopBase = pVRC4173;
InitializeCriticalSection(&m_csWrite);
m_cEnterWrites = 0;
//Set PS2 port
EnablePS2Port();
//Enable PS2 clock.
EnablePS2Clock();
//initalize control data.
m_ui8MouseControlByte = 0;
m_ui8KeybdControlByte = 0;
if ( !MouseTest() )
{
RETAILMSG(1,(TEXT("MouseTest fails! PS2 Mouse probably not present.\r\n")));
goto keyboard_init;
}
// Mouse
// Read the current command byte by sending command and reading the output buffer.
if ( !TxBufPollForEmpty(MousePort) )
{
goto keyboard_init;
}
keyboard_init:
// Set PS2 mouse regiters anyway!!
// Read the existing command byte from the controller. We
// keep a copy in the instance data.
// Turn on the keyboard interface but turn off keyboard interrupts until ready.
m_ui8MouseControlByte = Ps2EnableFIFO;
ControlRegWrite(MousePort, m_ui8MouseControlByte);
// Keyboard
// Read the current command byte by sending command and reading the output buffer.
if ( !TxBufPollForEmpty(KeybdPort) )
{
goto leave;
}
// Read the existing command byte from the controller. We
// keep a copy in the instance data.
// Turn on the keyboard interface but turn off keyboard interrupts until ready.
m_ui8KeybdControlByte = Ps2EnableFIFO;
ControlRegWrite(KeybdPort, m_ui8KeybdControlByte);
bRet = true;
leave:
return bRet;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -