pl050port.cpp
来自「WinCE 3.0 BSP, 包含Inter SA1110, Intel_815」· C++ 代码 · 共 1,034 行 · 第 1/2 页
CPP
1,034 行
return false;
}
portbase = (unsigned int)virtbase;
ASSERT((LPVOID)portbase == virtbase);
DEBUGMSG(1, (TEXT("Assigned baseaddr %x to virtbase %x\r\n"),
baseaddr, virtbase));
portintr = sysintr;
nLocks = 0;
intelliMouseFound = false;
portupcall = NULL;
parityfailed = false;
awaitingACK = false;
commandslocked = false;
irqsenabled = false;
if ((errcode = selfTest()))
{
ERRORMSG(1, (TEXT("pl050::Init: selfTest failed with error %x\r\n"),
errcode));
return false;
}
/*
* look see whether there is anything connected to this port
*/
autodetect();
DEBUGMSG(1, (TEXT("pl050::Init: device type = %s\r\n"),
porttype == pl050PortKbd ? TEXT("keyboard") :
porttype == pl050PortMouse ? TEXT("mouse") :
TEXT("<none>")));
if (porttype == pl050PortMouse)
{
/*
* see whether this is an IntelliMouse
*/
OpenPort();
setModeIntelliMouse();
if (mouseId() == MouseIdIntelliMouse)
intelliMouseFound = true;
ClosePort();
}
return true;
}
/*
* Low-level Interupt Service Routine. This has been moved from
* the specific device classes (keyboard/mouse) into this generic
* class because we need to add support for Tx interrupts too, and
* this is the obvious place for the code to go. If a recieved
* character is not a special-case one (ACK, request for retransmit),
* then it is passed along to the upcall function registered via
* SetISRUpcall().
*
* This routine should never return
*/
void pl050Port::ISRLoop(unsigned int timeout)
{
HANDLE irqevent;
unsigned int iir;
/*
* setup: create the interrupt event
*/
if ((irqevent = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL)
{
ERRORMSG(1, (TEXT("Cannot create irqevent\r\n")));
return;
}
if (!InterruptInitialize(portintr, irqevent, NULL, NULL))
{
ERRORMSG(1, (TEXT("Cannot initialise SYSINTR %d\r\n"), portintr));
return;
}
/*
* turn on interrupts from the device
*/
interruptEnable();
/*
* main loop for processing interrupts
*/
for (;;)
{
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::ISRLoop: wait with timeout %d\r\n"),
portbase, timeout));
#endif /* 0/1 */
if (WaitForSingleObject(irqevent, timeout) == WAIT_TIMEOUT)
{
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::ISRLoop: timeout, calling %x\r\n"),
portbase, portupcall));
#endif /* 0/1 */
/*
* no interrupts to process, just pass the timeout along
*/
if (portupcall != NULL)
timeout = portupcall(true, 0);
/*
* wait for next event
*/
continue;
}
/*
* it wasn't a timeout, so let the fun and games begin
*/
while (((iir = readIIR()) & (iirRxInt | iirTxInt)) != 0)
{
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::ISRLoop: IRQ %x\r\n"),
portbase, iir));
#endif /* 0/1 */
/*
* priority is given to Rx interrupts because:
*
* - we should read the datum ASAP, and
*
* - it could be an ACK/Retransmit for a Tx datum
*/
if ((iir & iirRxInt) != 0)
{
unsigned int parity, temp, datum;
temp = datum = readDATA();
parity = ((readSTAT() & statRxParity) != 0) ? 1 : 0;
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::ISRLoop: Rx datum %x\r\n"),
portbase, datum));
#endif /* 0/1 */
while (temp != 0)
{
parity ^= (temp & 0x01);
temp >>= 1;
}
if (parity == 0)
{
/*
* parity failure - request retransmission
*
* note that it is possible for parityfailed
* to be already set: the assumption being that
* we've had two consecutive parity failures
*/
parityfailed = true;
DEBUGMSG(1,
(TEXT("pl050Port%x::ISRLoop: PARITY FAILURE\r\n"),
portbase));
/*
* get the retransmit command going
* as soon as we possibly can
*/
if (readSTAT() & statTxEmpty)
writeDATA(0xfe);
else
/*
* make sure we're interrupted when
* Tx buffer is available
*/
writeCR(readCR() | crEnableTxInt);
/*
* rescan pending interrupts
*/
continue;
}
/*
* check for special-case characters
*/
if (datum == 0xfa)
{
/*
* got an ACK, I hope we were expecting it
*/
if (awaitingACK)
{
/* phew! */
awaitingACK = false;
/*
* the current Tx character has been
* ACKnowledged, so we have now finished
* with it
*/
commands.advancedrain();
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::ISRLoop: ACK\r\n"),
portbase));
#endif /* 0/1 */
}
else
{
/*
* XXX
*
* the Wombat code we based aspects of the driver
* upon >always< special-cases ACK and RESEND
* REQUEST, but it would seem that these are valid
* motion amounts, so if they're not expected, we
* we should pass them along to the upcall routine
*/
if (portupcall != NULL)
timeout = portupcall(false, datum);
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::ISRLoop: unexpected ")
TEXT("ACK\r\n"), portbase));
#endif /* 0/1 */
}
/*
* we can but assume that this clears any
* previous parity failure
*/
parityfailed = false;
}
else if (datum == 0xfe)
{
/*
* got a resend request, again I hope we were
* anticipating it
*/
if (awaitingACK)
{
/*
* yes, we were - treat this as an ACK (i.e. it's OK
* to send the next character when the buffer is
* clear), but don't advance the drain pointer
*/
awaitingACK = false;
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::ISRLoop: Resend ")
TEXT("Request\r\n"), portbase));
#endif /* 0/1 */
}
else
{
/*
* XXX
*
* the Wombat code we based aspects of the driver
* upon >always< special-cases ACK and RESEND
* REQUEST, but it would seem that these are valid
* motion amounts, so if they're not expected, we
* we should pass them along to the upcall routine
*/
if (portupcall != NULL)
timeout = portupcall(false, datum);
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::ISRLoop: unexpected ")
TEXT("resend request\r\n"), portbase));
#endif /* 0/1 */
}
}
else
{
if (portupcall != NULL)
{
/*
* this is assumed to be a scancode, and we have
* a registered upcall, so just pass it along
*/
timeout = portupcall(false, datum);
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::ISRLoop: new")
TEXT(" timeout = %x\r\n"),
portbase, timeout));
#endif /* 0/1 */
}
else
{
DEBUGMSG(1, (TEXT("pl050Port%x::ISRLoop: no ")
TEXT("upcall function registered\r\n"),
portbase));
}
/*
* we can but assume that this clears any
* previous parity failure
*/
parityfailed = false;
}
}
/*
* There is no specific processing to do for the Rx
* interrupt; the important step is to check whether
* there are any more outstanding commands, and whether
* the Tx register is capable of accepting them.
*
* XXX
*
* Even if the Tx register is empty, we can only send
* the next command iff we are not waiting for an ACK; this
* is because we may be asked to re-transmit the same command.
* EVen if we have more commands to send, and >are< awaiting
* an ACK, then it is safe to turn of the Tx interrupt, 'cause
* we'll get down here again when the ACK is finally received.
*
* TODO
*
* Technically, the Rx interrupt screws up the timeout
* mechanism used by the upcall owners to implement (for
* example) autorepeat, so we should probably try to fix
* it. However, autorepeat is screwed up by ACKs and
* Resend Requests too, and we can almost certainly live
* with slightly uncertain timeout intervals.
*/
SetLock(&commandslocked);
if (awaitingACK || (!parityfailed && commands.empty()))
{
/*
* there's nothing more to do at the moment, so make
* sure the Tx interrupt is disabled, and that's it
*/
writeCR(readCR() & ~crEnableTxInt);
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::ISRLoop: no ")
TEXT("upcall function registered\r\n"),
portbase));
DEBUGMSG(1, (TEXT("pl050Port%x::ISRLoop: %s\r\n"),
portbase, ((awaitingACK) ?
TEXT("still awaiting ACK") :
TEXT("nothing more to send"))));
#endif /* 0/1 */
if (!ClearLock(&commandslocked))
ERRORMSG(1, (TEXT("Cleared an unset lock!\r\n")));
continue;
}
if (readSTAT() & statTxEmpty)
{
/*
* parity failure has priority over all other commands
*/
if (parityfailed)
{
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::ISRLoop: requesting")
TEXT("resend\r\n"), portbase));
#endif /* 0/1 */
writeDATA(0xfe);
}
else
{
unsigned int nextcmd;
if (!commands.peek(&nextcmd))
{
/*
* this shouldn't happen
*/
ERRORMSG(1, (TEXT("Ring buffer empty\r\n")));
writeCR(readCR() & ~crEnableTxInt);
if (!ClearLock(&commandslocked))
ERRORMSG(1, (TEXT("Cleared an unset lock!\r\n")));
continue;
}
if ((nextcmd & CommandHasACK) != 0)
{
nextcmd ^= CommandHasACK;
awaitingACK = true;
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::ISRLoop: sending ")
TEXT("ACK command %x\r\n"),
portbase, nextcmd));
#endif /* 0/1 */
}
else
{
/*
* Since this command is not acknowledged,
* we assume that we can't be asked to retransmit
* it. This is almost certainly false, but there
* is no way to know when it has been successfully
* received. This is therefore the only chance we
* have to remove it from the ringbuffer.
*/
commands.advancedrain();
/*
* we only need to enqble the Tx interrupt
* when semding an unacknowledged command
*/
writeCR(readCR() | crEnableTxInt);
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::ISRLoop: sending ")
TEXT("one-shot command %x\r\n"),
portbase, nextcmd));
#endif /* 0/1 */
}
writeDATA(nextcmd);
}
if (!ClearLock(&commandslocked))
ERRORMSG(1, (TEXT("Cleared an unset lock!\r\n")));
}
}
/*
* all IRQs processed, get set for the next one
*/
InterruptDone(portintr);
}
}
/*
* pl050Port::KeyboardMode
*
* sets the keyboard mode
*/
void pl050Port::KeyboardMode(unsigned int mode)
{
if (mode < 1 || mode > 3)
{
ERRORMSG(1, (TEXT("Bad mode setting: %d\r\n"), mode));
return;
}
if (!ACKCommand(cmdKeybdMode))
goto leave;
if (!ACKCommand(mode))
goto leave;
leave:
return;
}
/*
* pl050Port::KeyboardLights
*
* sets the keyboard indicator lights
*/
void pl050Port::KeyboardLights(unsigned int fLights)
{
if (!ACKCommand(cmdKeybdLights))
goto leave;
if (!ACKCommand(fLights))
goto leave;
leave:
return;
}
/*
* pl050Port::DataRead
*
* Reads data from port
*/
bool pl050Port::DataRead(UINT8 *pui8Data)
{
#if 0
DEBUGMSG(1, (TEXT("pl050Port%x::DataRead, IIR = %x\r\n"),
portbase, readIIR()));
#endif /* 0/1 */
int portval = readPort();
if (portval != -1)
{
*pui8Data = (UINT8)(portval & 0xff);
return true;
}
return false;
}
/* EOF pl050port.cpp */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?