/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    Kbdtest.c

Abstract:

    This module implements the Keyboard and mouse test for the self-test.

Author:

    Lluis Abello (lluis) 10-Feb-1991

Environment:

    Rom self-test.

Revision History:

--*/
#include <ntos.h>
#include "iodevice.h"
#include "kbdmouse.h"
#include "ioaccess.h"

extern volatile ULONG TimerTicks;


VOID
ClearKbdFifo(
    )
/*++

Routine Description:

    This routine empties the Keyboard controller Fifo.

Arguments:

    None.

Return Value:

    None.

--*/
{
    UCHAR Trash, Stat;
    volatile Timeout;

    //
    // wait until the previous command is processed.
    //

    while ((READ_REGISTER_UCHAR(&KEYBOARD_READ->Status) & KBD_IBF_MASK) != 0) {
    }
    while ((READ_REGISTER_UCHAR(&KEYBOARD_READ->Status) & KBD_OBF_MASK) != 0) {
        Trash= READ_REGISTER_UCHAR(&KEYBOARD_READ->Data);
        for (Timeout=0;Timeout<10000;Timeout++) {
        }
    }
}

BOOLEAN
GetKbdData(
    PUCHAR C,
    ULONG msec
    )
/*++

Routine Description:

    This routine polls the Status Register until Data is available or timeout,
    then it reads and returns the Data.

Arguments:

    C - pointer to a byte where to write the read value
    msec - time-out time in milliseconds

Return Value:

    TRUE if timeout, FALSE if OK;

--*/
{
    TimerTicks=msec;
    while (TimerTicks) {
        if (READ_REGISTER_UCHAR(&KEYBOARD_READ->Status) & KBD_OBF_MASK) {
            *C = READ_REGISTER_UCHAR(&KEYBOARD_READ->Data);
            return FALSE;
        }
    }
    return TRUE;
}

BOOLEAN
SendKbdData(
    IN UCHAR Data
    )
/*++

Routine Description:

    This routine polls the Status Register until the controller is ready to
    accept a data or timeout, then it send the Data.


Arguments:

    None.

Return Value:

    TRUE if timeout, FALSE if OK;

--*/
{
    ULONG i;

    for (i=0; i <KBD_TIMEOUT; i++) {
        if ((READ_REGISTER_UCHAR(&KEYBOARD_READ->Status) & KBD_IBF_MASK) == 0) {
            WRITE_REGISTER_UCHAR(&KEYBOARD_WRITE->Data,Data);
            return FALSE;
        }
    }
    return TRUE;
}

BOOLEAN
SendKbdCommand(
    IN UCHAR Command
    )
/*++

Routine Description:

    This routine polls the Status Register until the controller is ready to
    accept a command or timeout, then it send the Command.


Arguments:

    None.

Return Value:

    TRUE if timeout, FALSE if OK;

--*/
{
    ULONG i;

    for (i=0; i <KBD_TIMEOUT; i++) {
        if ((READ_REGISTER_UCHAR(&KEYBOARD_READ->Status) & KBD_IBF_MASK) == 0) {
            WRITE_REGISTER_UCHAR(&KEYBOARD_WRITE->Command,Command);
            return FALSE;
        }
    }
    return TRUE;
}

ULONG
InitKeyboard(
    )
/*++

Routine Description:

    This routine enables amd initializes the keyboard.

Arguments:

    None.

Return Value:

    FALSE if passed,
    TRUE if bad ACK or BAT received,
    TIME_OUT if no response is received from the keyboard.

--*/
{
    UCHAR Result;
    ULONG i;

    //
    // Send Reset to Keyboard.
    //

    ClearKbdFifo();
    for (;;) {
        if (SendKbdData(KbdReset)) {
            return TIME_OUT;
        }
        if (GetKbdData(&Result,1000)) {
            return TIME_OUT;
        }
        if (Result == KbdResend) {
            if (GetKbdData(&Result,1000)) {
                return TIME_OUT;
            }
            continue;
        }
        if (Result != KbdAck) {
            return TRUE;
        }
        if (GetKbdData(&Result,7000)) {
            return TIME_OUT;
        }
        if (Result != KbdBat) {
            return TRUE;
        }
        break;
    }

    //
    // Enable Kbd and Select keyboard Scan code.
    //

    if (SendKbdCommand(KBD_CTR_ENABLE_KBD)) {
        return TIME_OUT;
    }
    if (SendKbdData(KbdSelScanCode)) {
        return TIME_OUT;
    }
    if (GetKbdData(&Result,1000)) {
        return TIME_OUT;
    }
    if (SendKbdData(1)) {              // select Scan code 1
        return TIME_OUT;
    }
    if (GetKbdData(&Result,1000)) {
        return TIME_OUT;
    }
    return FALSE;
}

ULONG
InitKeyboardController(
    )
/*++

Routine Description:

    This routine Initializes the Keyboard controller.

Arguments:

    None.

Return Value:

    FALSE if passed,
    TRUE if bad response received from keyboard controller,
    TIME_OUT if no response is received from the keyboard controller.

--*/
{
    UCHAR Result;

    //
    // Clear both fifos.
    //

    ClearKbdFifo();

    //
    // Send Selftest Command. This has to be done before anything else.
    //

    if (SendKbdCommand(KBD_CTR_SELFTEST)) {
        return TIME_OUT;
    }
    if (GetKbdData(&Result,1000)) {
        return TIME_OUT;
    }
    if (Result != Kbd_Ctr_Selftest_Passed) {
        return TRUE;
    }

    //
    // Now the Keyboard and Mouse are disabled.
    //

    //
    // Test Keyboard lines.
    //

    if (SendKbdCommand(KBD_CTR_KBDLINES_TEST)) {
        return TIME_OUT;
    }
    if (GetKbdData(&Result,1000)) {
        return TIME_OUT;
    }
    if (Result != INTERFACE_NO_ERROR) {
        return TRUE;
    }

    //
    // Test Aux lines.
    //

    if (SendKbdCommand(KBD_CTR_AUXLINES_TEST)) {
        return TIME_OUT;
    }
    if (GetKbdData(&Result,1000)) {
        return TIME_OUT;
    }
    if (Result != INTERFACE_NO_ERROR) {
        return TRUE;
    }
    return FALSE;
}