pl050port.cpp
来自「WinCE 3.0 BSP, 包含Inter SA1110, Intel_815」· C++ 代码 · 共 1,034 行 · 第 1/2 页
CPP
1,034 行
/* -*-C-*-
*
* $Revision: 1.3 $
* $Author: kwelton $
* $Date: 2000/06/14 03:38:22 $
*
* 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
* Copyright (c) 2000 ARM Limited
* All Rights Reserved
*/
#include <windows.h>
#include <winbase.h>
#include <ceddk.h>
#include "oalintr.h"
#include "pl050port.hpp"
static const unsigned int msPollLimit = 5000; // 5 seconds.
static const unsigned int crForceClockLow = (1 << 0);
static const unsigned int crForceDataLow = (1 << 1);
static const unsigned int crEnableIf = (1 << 2);
static const unsigned int crEnableTxInt = (1 << 3);
static const unsigned int crEnableRxInt = (1 << 4);
static const unsigned int crNoLineControl = (1 << 5);
static const unsigned int pl050KMID = (1 << 0);
static const unsigned int pl050KMIC = (1 << 1);
static const unsigned int statRxParity = (1 << 2);
static const unsigned int statRxBusy = (1 << 3);
static const unsigned int statRxFull = (1 << 4);
static const unsigned int statTxBusy = (1 << 5);
static const unsigned int statTxEmpty = (1 << 6);
static const unsigned int pl050ClockDivisor = 0x02;
static const unsigned int iirRxInt = (1 << 0);
static const unsigned int iirTxInt = (1 << 1);
static const unsigned char cmdKeybdReset = 0xff;
static const unsigned char cmdKeybdMode = 0xf0;
static const unsigned char cmdKeybdLights = 0xed;
static const unsigned char cmdMouseReadId = 0xf2;
static const unsigned char cmdMouseSetReportRate = 0xf3;
static const int responseACK = 0xfa;
static const int responseToReset = 0xaa;
static const unsigned char MouseIdIntelliMouse = 0x03;
/*
* flags for buffered commands
*/
static const unsigned int CommandHasACK = (1 << 8);
/**********************************************************************/
unsigned int pl050Port::selfTest()
{
int i;
DEBUGMSG(1, (TEXT("pl050:SelfTest: base %x, opening port\r\n"),
portbase));
/*
* bottom 4 bits of divisor register should be writeable
*/
for (i = 0; i < 16; ++i)
{
writeCLKDIV(i);
if (readCLKDIV() != i)
/* failed already */
return portbase | (1 << 8) | i;
}
/* set correct clock divisor */
writeCLKDIV(pl050ClockDivisor);
/* enable the interface */
writeCR(crEnableIf | crEnableTxInt | crEnableRxInt);
/* should get a transmit interrupt... */
if (!(readIIR() & iirTxInt))
return portbase | (2 << 8) | 1;
/* ...and we shouldn't be sending anything */
if ((readSTAT() & (statTxBusy | statTxEmpty)) != statTxEmpty)
return (portbase | (2 << 8) | 2);
/* write a byte */
writeDATA(0xee);
/* should cancel the interrupt... */
if (readIIR() & iirTxInt)
return portbase | (2 << 8) | 3;
/* ...and we should be busy in Tx */
if ((readSTAT() & (statTxBusy | statTxEmpty)) != statTxBusy)
return portbase | (2 << 8) | 4;
/*
* turn off the interface and wait a bit
* for the keyboard to reply
*/
writeCR(0x00);
Sleep(20);
/* test the output lines */
if ((readSTAT() & (pl050KMID | pl050KMIC)) != (pl050KMID | pl050KMIC))
return portbase | (3 << 8) | 1;
/* force clock line low */
writeCR(crForceClockLow);
Sleep(1);
if ((readSTAT() & (pl050KMID | pl050KMIC)) != pl050KMID)
return portbase | (3 << 8) | 2;
/* force data line low */
writeCR(crForceClockLow | crForceDataLow);
Sleep(1);
if ((readSTAT() & (pl050KMID | pl050KMIC)) != 0x00)
return portbase | (3 << 8) | 3;
/* reset and wait for the lines to settle */
writeCR(0);
Sleep(20);
return 0;
}
int pl050Port::readPort(void)
{
unsigned char status;
unsigned int msStart = GetTickCount();
do
{
if (((status = readSTAT()) & statRxFull) != 0)
return readDATA();
} while ((((unsigned int)GetTickCount()) - msStart) < 500);
#if 0
DEBUGMSG(1, (TEXT("pl050::readPort: timeout\r\n")));
#endif
return -1;
}
bool pl050Port::writePort(unsigned int value)
{
unsigned int status;
unsigned int msStart;
msStart = GetTickCount();
do
{
if (((status = readSTAT()) & statTxEmpty) != 0)
{
writeDATA(value);
return true;
}
} while ((((unsigned int)GetTickCount()) - msStart) < 500);
DEBUGMSG(1, (TEXT("pl050::writePort: bad status %x\r\n"),
status));
return false;
}
void pl050Port::OpenPort(void)
{
ASSERT(readCR() == 0);
writeCLKDIV(pl050ClockDivisor);
writeCR(crEnableIf);
#if 0
DEBUGMSG(1, (TEXT("pl050::OpenPort: base %x, junking\r\n"), portbase));
#endif
/*
* junk any crap that we may have gotten sent during testing
*/
(void)readPort();
#if 0
DEBUGMSG(1, (TEXT("pl050::OpenPort: finished\r\n")));
#endif
}
void pl050Port::ClosePort(void)
{
if (portbase != 0)
{
writeCR(0);
}
}
bool pl050Port::pollACKCommand(unsigned int cmd)
{
int response;
/*
* this command shouldn't be used when interrupts are enabled
*/
ASSERT((readCR() & (crEnableTxInt | crEnableRxInt)) == 0);
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::pollACKCommand %x\r\n"),
portbase, cmd));
#endif
if (!writePort(cmd))
{
DEBUGMSG(1, (TEXT("pl050Port%x::pollACKCommand: writePort failed\r\n"),
portbase));
return false;
}
/*
* given that interrupts are not running at the moment, we
* need to poll for the ACK response
*/
if ((response = readPort()) == -1)
{
DEBUGMSG(1, (TEXT("pl050::pollACKCommand: no response given\r\n")));
return false;
}
else if (response != responseACK)
{
DEBUGMSG(1, (TEXT("pl050::pollACKCommand: bad response %x\r\n"),
response));
return false;
}
return true;
}
bool pl050Port::pollOneShotCommand(unsigned int cmd)
{
/*
* this command shouldn't be used when interrupts are enabled
*/
ASSERT((readCR() & (crEnableTxInt | crEnableRxInt)) == 0);
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::pollOneShotCommand %x\r\n"),
portbase, cmd));
#endif
return writePort(cmd);
}
/*
* pl050Port::irqACKCommand
*
* transmit a command that expects an ACK, and completes
* under interrupt
*
* returns true if the command successfully loaded
*/
bool pl050Port::irqACKCommand(unsigned int cmd)
{
bool retc = true;
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::irqACKCommand %x\r\n"), portbase, cmd));
#endif
SetLock(&commandslocked);
/*
* send the command directly if we can, else just queue it up
*/
if (!parityfailed &&
commands.empty() &&
(readSTAT() & statTxEmpty) != 0)
{
#if 0
DEBUGMSG(1, (TEXT(" writing it out immediately\r\n")));
#endif /* 0/1 */
writeDATA((unsigned char)cmd);
awaitingACK = true;
}
/*
* even if we managed to send the command above, we still
* need to enqueue it in case we get asked to resend it
*/
cmd |= CommandHasACK;
if (!commands.add(cmd))
{
ERRORMSG(1, (TEXT("Command buffer overflow\r\n")));
retc = false;
}
/*
* that's it - we don't need to enable interrupts since
* the buffer pointer will get advanced during ACK
* processing
*/
if (!ClearLock(&commandslocked))
ERRORMSG(1, (TEXT("Cleared an unset lock!\r\n")));
return retc;
}
/*
* pl050Port::irqOneShotCommand
*
* transmit a command that doesn't expect an ACK, but which completes
* under interrupt
*
* returns true if the command successfully loaded
*/
bool pl050Port::irqOneShotCommand(unsigned int cmd)
{
bool retc = true;
SetLock(&commandslocked);
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::irqOneShotCommand %x\r\n"),
portbase, cmd));
#endif
/*
* send the command directly if we can, else just queue it up
*/
if (!parityfailed &&
commands.empty() &&
(readSTAT() & statTxEmpty) != 0)
{
#if 0
DEBUGMSG(1, (TEXT(" writing it out immediately\r\n")));
#endif /* 0/1 */
writeDATA((unsigned char)cmd);
}
else
{
/*
* try an queue the command for sending later
*/
if (!commands.add(cmd))
{
ERRORMSG(1, (TEXT("Command buffer overflow\r\n")));
retc = false;
}
}
/*
* we always enable interrupts: ACK commands don't enable
* interrupts ('cause they don't need to), but we may queue
* an ACK command while waiting for this one to complete
*/
writeCR(readCR() | crEnableTxInt);
if (!ClearLock(&commandslocked))
ERRORMSG(1, (TEXT("Cleared an unset lock!\r\n")));
return retc;
}
void pl050Port::autodetect(void)
{
int reply;
#if 0
DEBUGMSG(1, (TEXT("pl050::autodetect: base %x\r\n"), portbase));
#endif
OpenPort();
/*
* send a reset command
*/
pollACKCommand(0xff);
/*
* look for confirmation
*/
if ((reply = readPort()) != 0xaa)
{
#if 0
DEBUGMSG(1, (TEXT("pl050::autodetect: bad device scan %x on %x\r\n"),
reply, portbase));
#endif
porttype = pl050PortNone;
ClosePort();
return;
}
/*
* we've got >something< - is it keyboard or mouse?
*/
/*
* if the device is a keyboard, then we expect it to timeout (unless
* the user has pressed some random key); a mouse should return 0
*/
if ((reply = readPort()) == 0)
porttype = pl050PortMouse;
else
porttype = pl050PortKbd;
ClosePort();
}
/*
* pl050Port::InterruptEnable
*
* enables port interrupts.
*/
bool pl050Port::interruptEnable(void)
{
irqsenabled = true;
writeCR(readCR() | crEnableRxInt);
return true;
}
/*
* pl050Port::setModeIntelliMouse
*
* set mouse to IntelliMouse mode. if an IntelliMouse is present:
*
* 1. mouse id becomes response8042intellimouseid
* 2. the motion report format changes
*
* if intellimouse not present the mouse id and motion report format
* remain at the default values
*/
void pl050Port::setModeIntelliMouse(void)
{
/*
* IntelliMouse(r) is uniquely identified by issuing the specific
* series of set report rate commands:
*
* 200hz (0xc8), then
* 100hz (0x64), then
* 80hz (0x50).
*
* the set report rate commands are valid and we therefore have to
* set the report rate back to the default 100hz (this is done by
* mouseid()).
*/
ACKCommand(cmdMouseSetReportRate);
ACKCommand(0xc8);
ACKCommand(cmdMouseSetReportRate);
ACKCommand(0x64);
ACKCommand(cmdMouseSetReportRate);
ACKCommand(0x50);
}
unsigned char pl050Port::mouseId(void)
{
int id;
ACKCommand(cmdMouseReadId);
id = readPort();
DEBUGMSG(1, (TEXT("pl050Port%x::MouseId: %x\r\n"), portbase, id));
// set the report rate back to the default 100hz
ACKCommand(cmdMouseSetReportRate);
ACKCommand(0x64);
return id & 0xff;
}
/**********************************************************************/
/*
* public functions
*/
/*
* initialise the interface. we deliberately anglicise the spelling
* 'cause the function has changed from the usoft original (it now
* takes no args, rather than an unsigned int).
*/
bool pl050Port::Initialise(unsigned int baseaddr, unsigned int sysintr)
{
unsigned int errcode;
LPVOID virtbase;
PHYSICAL_ADDRESS physaddr;
/*
* the base address is given as a physical address; we need to
* create a usable virtual address that maps to this address
*/
physaddr.u.LowPart = baseaddr;
physaddr.u.HighPart = 0;
if (!(virtbase = MmMapIoSpace(physaddr, 0x1000, FALSE)))
{
ERRORMSG(1, (TEXT("MmMapIOSpace failed\r\n")));
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?