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 + -
显示快捷键?