ps2port.cpp

来自「WinCE 3.0 BSP, 包含Inter SA1110, Intel_815」· C++ 代码 · 共 832 行 · 第 1/2 页

CPP
832
字号
/* -*-C-*-
 *
 * $Revision: 1.1 $
 *   $Author: kwelton $
 *     $Date: 2000/04/12 00:50:42 $
 *
 * 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 <ceddk.h>

#include "ps2port.hpp"

static const unsigned int msPollLimit = 5000;   // 5 seconds.


//      Alternate and Output Buffer Full Table
//
//      ABF     OBF
//      0       0       output buf empty (data not valid)
//      0       1       output buffer has keyboard or command data
//      1       0       output buf empty (data not valid)
//      1       1       output buffer has auxilliary (mouse) data


//
// Commands sent to the 8042 command register.
//
static const UINT8 cmd8042ReadModeByte = 0x20;
static const UINT8 cmd8042WriteModeByte = 0x60;
static const UINT8 cmd8042SelfTest = 0xAA;
static const UINT8 cmd8042KeyboardInterfaceTest = 0xAB;
static const UINT8 cmd8042AuxDeviceWrite = 0xD4;


//
// Flags for the 8042 mode byte.  Used by the read mode and write mode
// commands.
//
static const UINT8 cmdByteEnableAuxInterrupts = 0x02;
static const UINT8 cmdByteEnableKeybdInterrupts = 0x01;
static const UINT8 cmdByteDisableKeybdInterface = 0x10;
static const UINT8 cmdByteDisableAuxInterface = 0x20;

//
// Commands sent to the keyboard.
//
static const UINT8 cmdKeybdReset = 0xFF;
static const UINT8 cmdKeybdLights = 0xED;

//
// Commands sent to the mouse.
//
static const UINT8 cmdMouseReadId = 0xF2;
static const UINT8 cmdMouseSetReportRate = 0xF3;
static const UINT8 cmdMouseEnable = 0xF4;

//
// Status register flags.
//
static const UINT8 sts8042OutputBufFull = 0x01;
static const UINT8 sts8042InputBufFull = 0x02;
static const UINT8 sts8042OutputBufIsAux = 0x20;

//
// Status flags combinations for checking the output buffer full status.
//
static const UINT8 sts8042ObfFlags = 0x21;
static const UINT8 sts8042ObfMain = 0x01;
static const UINT8 sts8042ObfAux = 0x21;


//
// Mouse and Keyboard Response
//
static const UINT8 response8042Ack = 0xFA;
static const UINT8 response8042Resend = 0xFE;
static const UINT8 response8042IntelliMouseId = 0x03;

/**********************************************************************/

/*
 * Ps2Port::MainOutputBufIsFull
 *
 * Returns true if the main output buffer is full.
 */
inline bool Ps2Port::MainOutputBufIsFull(void)
{
    return (ui8StatusRead() & sts8042ObfFlags) == sts8042ObfMain;
}


/*
 * Ps2Port::AuxOutputBufIsFull
 *
 * Returns true if the auxiliary output buffer is full.
 */
inline bool Ps2Port::AuxOutputBufIsFull(void)
{
    return (ui8StatusRead() & sts8042ObfFlags) == sts8042ObfAux;
}

/*
 * Ps2Port::InputBufPollForEmpty
 *
 * SPINS waiting for the input buffer to go empty.  Returns true if the
 * buffer is empty, false if the buffer did not go empty. Remember that input
 * means input of the 8042, not the PC.
 */
bool Ps2Port::InputBufPollForEmpty(void)
{
    bool bRet = false;
    UINT8 ui8Status;
    unsigned int msStart;
    unsigned int msEnd;

    // Poll while the input buffer is full
    ui8Status = ui8StatusRead();
    if (ui8Status & sts8042InputBufFull)
    {
        msStart = GetTickCount();

        for (;;)
        {
            Sleep(0);       // Time passes.

            // See if buffer is still full.
            ui8Status = ui8StatusRead();
            if (ui8Status & sts8042InputBufFull)
            {
                //
                //      If still full, check elapsed time.
                //      Unsigned arithmetic deals with timer rollover.
                //
                msEnd = GetTickCount();

                if ((msEnd - msStart) > msPollLimit)
                {
                    ERRORMSG(1,(TEXT("Ps2Port::InputBufPollForEmpty: too long")
                                TEXT(" waiting for input buffer empty.\r\n")));
                    break;
                }
            }
            else
            {
                bRet = true;
                break;
            }
        }
    }
    else
        bRet = true;

    return bRet;
}

/*
 * Ps2Port::OutputBufPollRead
 *
 * SPINS waiting for the output buffer to go full and then reads the data
 * from the output buffer.  Returns true if the buffer goes full, false if
 * the buffer never goes full.  Remember that output means output of the
 * 8042, not the PC.
 */
bool Ps2Port::OutputBufPollRead(UINT8 *pui8)
{
    bool bRet = false;
    UINT8 ui8Status;
    unsigned int msStart;
    unsigned int msEnd;

    // Check for data ready first.
    ui8Status = ui8StatusRead();

#if 0
    DEBUGMSG(1, (TEXT("status =  0x%X\r\n"), ui8Status));
#endif /* 0/1 */

    if (ui8Status & sts8042OutputBufFull)
        bRet = true;
    else
    {
        // Start polling if output data not ready.
        msStart = GetTickCount();

        for (;;)
        {
            Sleep(0);       // Time passes.

            // Check for data again.
            ui8Status = ui8StatusRead();

#if 0
            DEBUGMSG(1, (TEXT("status2 =  0x%X\r\n"), ui8Status));
#endif /* 0/1 */

            if (ui8Status & sts8042OutputBufFull)
            {
                bRet = true;
                break;
            }

            // Check elapsed time.
            // Unsigned arithmetic deals with timer rollover.
            msEnd = GetTickCount();

            if ((msEnd - msStart) > msPollLimit)
            {
                ERRORMSG(1,(TEXT("Ps2Port::OutputBufPollRead: too long ")
                            TEXT("waiting for output buffer full.\r\n")));
#if 0
                ASSERT(0);
#endif /* 0/1 */

                break;
            }
        }
    }

    // May as well read it even if we failed.
    *pui8 = ui8OutputBufRead();
    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 auxiliary interrupts.
 *
 * Disables the keyboard and auxiliary 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(void)
{
    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;

    /*
     * There does not seem to be any command to control the interrupt
     * enable bits in the command byte.  We cannot read the command
     * byte since if the ints are enabled, the isr will pick up the
     * data before we can get to it by polling.  We can either coordinate
     * with the keyboard isr or keep the command byte independently.  I chose
     * to keep the command byte independently so that we can still work
     * even if something is wrong with the keyboard isr.  This means however
     * that we can't use the other commands for enabling the interfaces.  We
     * will need to modify these command byte bits directly also.
     */
    ui8CmdByte = m_ui8CmdByte;

    /*
     * Disable the interface first.  Note that we are changing the local copy
     * of the command byte, not the member variable.
     */
    ui8CmdByte |= cmdByteDisableKeybdInterface | cmdByteDisableAuxInterface;

    // Write command byte directly.
    if (!InputBufPollForEmpty())
    {
        ASSERT(0);
        goto leave_fail;
    }

    CommandWrite(cmd8042WriteModeByte);
    InputBufPut(ui8CmdByte);

    // Wait for any data in the pipeline to finish. Most of the
    // timings are on the order of 10's of microseconds, so 10
    // milliseconds should be enough.
    Sleep(10);

    // Read any junk data.
    ui8OutputBufRead();

    // Now disable the interrupts.
    ui8CmdByte &= ~(cmdByteEnableAuxInterrupts | cmdByteEnableKeybdInterrupts);

    // Write the command byte directly.
    if (!InputBufPollForEmpty())
    {
        ASSERT(0);
        goto leave_fail;
    }

    CommandWrite(cmd8042WriteModeByte);
    InputBufPut(ui8CmdByte);

  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(void)
{
    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;
    }

    // Write command byte by sending command and writing.
    if (!InputBufPollForEmpty())
    {
        ERRORMSG(1,(TEXT("LeaveWrite: too long waiting for input buffer ")
                    TEXT("to empty (2).\r\n")));
        goto leave;
    }

    CommandWrite(cmd8042WriteModeByte);
    InputBufPut(m_ui8CmdByte);

    bRet = true;

  leave:
    --m_cEnterWrites;
    LeaveCriticalSection(&m_csWrite);

    return bRet;
}

/*
 * Ps2Port::CommandPut
 *
 * Puts a command in the 8042 command register.  Calls EnterWrite/ExitWrite.
 *
 * Returns true if successful.
 */
bool Ps2Port::CommandPut(UINT8 cmd8042)
{
    bool bRet = false;

    EnterWrite();

    if (!InputBufPollForEmpty())
        goto leave;

    CommandWrite(cmd8042);
    bRet = true;

  leave:
    LeaveWrite();
    return bRet;
}

/*
 * Ps2Port::InputBufPut
 *
 * Puts data in the 8042 input buffer.  Caller must have called EnterWrite
 * before calling.
 *
 * Returns true if successful.
 */
bool Ps2Port::InputBufPut(UINT8 ui8Data)
{
    bool bRet = false;

    if (!m_cEnterWrites)
    {
        ERRORMSG(1,(TEXT("Did not use EnterWrite before calling ")
                    TEXT("InputBufferPut.\r\n")));
        ASSERT(0);
    }

    if (!InputBufPollForEmpty())
        goto leave;

    InputBufWrite(ui8Data);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?