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