Пример #1
0
/* Reset the pointing device. */
static void ps2mReset(PPS2M pThis)
{
    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_BAT_OK);
    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, 0);
    pThis->enmMode   = AUX_MODE_STD;
    pThis->u8CurrCmd = 0;

    /// @todo move to its proper home!
    ps2mSetDriverState(pThis, true);
}
Пример #2
0
/**
 * Mouse event handler.
 *
 * @returns VBox status code.
 * @param   pThis           The PS/2 auxiliary device instance data.
 * @param   dx              X direction movement delta.
 * @param   dy              Y direction movement delta.
 * @param   dz              Z (vertical scroll) movement delta.
 * @param   dw              W (horizontal scroll) movement delta.
 * @param   fButtons        Depressed button mask.
 */
static int ps2mPutEventWorker(PPS2M pThis, int32_t dx, int32_t dy,
                              int32_t dz, int32_t dw, uint32_t fButtons)
{
    RT_NOREF1(dw);
    int             rc = VINF_SUCCESS;

    /* Update internal accumulators and button state. */
    pThis->iAccumX += dx;
    pThis->iAccumY += dy;
    pThis->iAccumZ += dz;
    pThis->fAccumB |= fButtons;     /// @todo accumulate based on current protocol?
    pThis->fCurrB   = fButtons;

#if 1
    /* Report the event and start the throttle timer unless it's already running. */
    if (!pThis->fThrottleActive)
    {
        ps2mReportAccumulatedEvents(pThis, (GeneriQ *)&pThis->evtQ, true);
        KBCUpdateInterrupts(pThis->pParent);
        pThis->fThrottleActive = true;
        TMTimerSetMillies(pThis->CTX_SUFF(pThrottleTimer), pThis->uThrottleDelay);
    }
#else
    /* Clamp the delta values to the allowed range. */
    dx = RT_MIN(RT_MAX(dx, -256), 255);
    dy = RT_MIN(RT_MAX(dy, -256), 255);

    /* Start with the sync bit. */
    val  = RT_BIT(3);
    /* Add buttons 1-3. */
    val |= fButtons & PS2M_STD_BTN_MASK;
    /* Set the X/Y sign bits. */
    if (dx < 0)
        val |= RT_BIT(4);
    if (dy < 0)
        val |= RT_BIT(5);

    ps2kInsertQueue((GeneriQ *)&pThis->evtQ, val);
    ps2kInsertQueue((GeneriQ *)&pThis->evtQ, (uint8_t)dx);
    ps2kInsertQueue((GeneriQ *)&pThis->evtQ, (uint8_t)dy);
    if (pThis->enmProtocol > PS2M_PROTO_PS2STD)
    {
        ps2kInsertQueue((GeneriQ *)&pThis->evtQ, (uint8_t)dz);
    }
#endif

    return rc;
}
Пример #3
0
/* Report accumulated movement and button presses, then clear the accumulators. */
static void ps2mReportAccumulatedEvents(PPS2M pThis, GeneriQ *pQueue, bool fAccumBtns)
{
    uint32_t    fBtnState = fAccumBtns ? pThis->fAccumB : pThis->fCurrB;
    uint8_t     val;
    int         dX, dY, dZ;

    /* Clamp the accumulated delta values to the allowed range. */
    dX = RT_MIN(RT_MAX(pThis->iAccumX, -255), 255);
    dY = RT_MIN(RT_MAX(pThis->iAccumY, -255), 255);
    dZ = RT_MIN(RT_MAX(pThis->iAccumZ, -8), 7);

    /* Start with the sync bit and buttons 1-3. */
    val = RT_BIT(3) | (fBtnState & PS2M_STD_BTN_MASK);
    /* Set the X/Y sign bits. */
    if (dX < 0)
        val |= RT_BIT(4);
    if (dY < 0)
        val |= RT_BIT(5);

    /* Send the standard 3-byte packet (always the same). */
    ps2kInsertQueue(pQueue, val);
    ps2kInsertQueue(pQueue, dX);
    ps2kInsertQueue(pQueue, dY);

    /* Add fourth byte if extended protocol is in use. */
    if (pThis->enmProtocol > PS2M_PROTO_PS2STD)
    {
        if (pThis->enmProtocol == PS2M_PROTO_IMPS2)
            ps2kInsertQueue(pQueue, dZ);
        else
        {
            Assert(pThis->enmProtocol == PS2M_PROTO_IMEX);
            /* Z value uses 4 bits; buttons 4/5 in bits 4 and 5. */
            val  = dZ & 0x0f;
            val |= (fBtnState << 1) & (RT_BIT(4) | RT_BIT(5));
            ps2kInsertQueue(pQueue, val);
        }
    }

    /* Clear the movement accumulators, but not necessarily button state. */
    pThis->iAccumX = pThis->iAccumY = pThis->iAccumZ = 0;
    /* Clear accumulated button state only when it's being used. */
    if (fAccumBtns)
    {
        pThis->fReportedB = pThis->fAccumB;
        pThis->fAccumB    = 0;
    }
}
Пример #4
0
/* Report accumulated movement and button presses, then clear the accumulators. */
static void ps2mReportAccumulatedEvents(PPS2M pThis)
{
    uint8_t     val;
    int8_t      dX, dY, dZ;

    /* Clamp the accumulated delta values to the allowed range. */
    dX = RT_MIN(RT_MAX(pThis->iAccumX, -256), 255);
    dY = RT_MIN(RT_MAX(pThis->iAccumY, -256), 255);
    dZ = RT_MIN(RT_MAX(pThis->iAccumZ, -8), 7);

    /* Start with the sync bit and buttons 1-3. */
    val = RT_BIT(3) | (pThis->fAccumB & PS2M_STD_BTN_MASK);
    /* Set the X/Y sign bits. */
    if (dX < 0)
        val |= RT_BIT(4);
    if (dY < 0)
        val |= RT_BIT(5);

    /* Send the standard 3-byte packet (always the same). */
    ps2kInsertQueue((GeneriQ *)&pThis->evtQ, val);
    ps2kInsertQueue((GeneriQ *)&pThis->evtQ, dX);
    ps2kInsertQueue((GeneriQ *)&pThis->evtQ, dY);

    /* Add fourth byte if extended protocol is in use. */
    if (pThis->enmProtocol > PS2M_PROTO_PS2STD)
    {
        if (pThis->enmProtocol == PS2M_PROTO_IMPS2)
            ps2kInsertQueue((GeneriQ *)&pThis->evtQ, dZ);
        else
        {
            Assert(pThis->enmProtocol == PS2M_PROTO_IMEX);
            /* Z value uses 4 bits; buttons 4/5 in bits 4 and 5. */
            val  = dZ & 0x0f;
            val |= (pThis->fAccumB << 1) & (RT_BIT(4) | RT_BIT(5));
            ps2kInsertQueue((GeneriQ *)&pThis->evtQ, dZ);
        }
    }

    /* Clear the accumulators. */
    pThis->iAccumX = pThis->iAccumY = pThis->iAccumZ = pThis->fAccumB = 0;

    /* Poke the KBC to update its state. */
    KBCUpdateInterrupts(pThis->pParent);
}
Пример #5
0
/* The auxiliary device is specified to take up to about 500 milliseconds. We need
 * to delay sending the result to the host for at least a tiny little while.
 */
static DECLCALLBACK(void) ps2mDelayTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
{
    PPS2M pThis = (PS2M *)pvUser; NOREF(pDevIns);

    LogFlowFunc(("Delay timer: cmd %02X\n", pThis->u8CurrCmd));

    Assert(pThis->u8CurrCmd == ACMD_RESET);
    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_BAT_OK);
    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, 0);
    pThis->enmMode   = AUX_MODE_STD;
    pThis->u8CurrCmd = 0;

    ///@todo Might want a PS2MCompleteCommand() to push last response, clear command, and kick the KBC...
    /* Give the KBC a kick. */
    KBCUpdateInterrupts(pThis->pParent);

    //@todo: move to its proper home!
    ps2mSetDriverState(pThis, true);
}
Пример #6
0
/**
 * Receive and process a byte sent by the keyboard controller.
 *
 * @param   pThis               The PS/2 auxiliary device instance data.
 * @param   cmd                 The command (or data) byte.
 */
int PS2MByteToAux(PPS2M pThis, uint8_t cmd)
{
    uint8_t u8Val;
    bool    fHandled = true;

    LogFlowFunc(("cmd=0x%02X, active cmd=0x%02X\n", cmd, pThis->u8CurrCmd));
//LogRel(("aux: cmd=0x%02X, active cmd=0x%02X\n", cmd, pThis->u8CurrCmd));

    if (pThis->enmMode == AUX_MODE_RESET)
    {
        /* In reset mode, do not respond at all. */
        return VINF_SUCCESS;
    }
    else if (pThis->enmMode == AUX_MODE_WRAP)
    {
        /* In wrap mode, bounce most data right back.*/
        if (cmd == ACMD_RESET || cmd == ACMD_RESET_WRAP)
            ;   /* Handle as regular commands. */
        else
        {
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, cmd);
            return VINF_SUCCESS;
        }
    }

    switch (cmd)
    {
        case ACMD_SET_SCALE_11:
            pThis->u8State &= ~AUX_STATE_SCALING;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_SET_SCALE_21:
            pThis->u8State |= AUX_STATE_SCALING;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_REQ_STATUS:
            /* Report current status, sample rate, and resolution. */
            //@todo: buttons
            u8Val  = pThis->u8State;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, u8Val);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->u8Resolution);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->u8SampleRate);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_SET_STREAM:
            pThis->u8State &= ~AUX_STATE_REMOTE;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_RESET_WRAP:
            pThis->enmMode = AUX_MODE_STD;
            /* NB: Stream mode reporting remains disabled! */
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_SET_WRAP:
            pThis->enmMode = AUX_MODE_WRAP;
            pThis->u8State &= ~AUX_STATE_ENABLED;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_SET_REMOTE:
            pThis->u8State |= AUX_STATE_REMOTE;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_READ_ID:
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->enmProtocol);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_ENABLE:
            pThis->u8State |= AUX_STATE_ENABLED;
            //@todo: R3 only!
#ifdef IN_RING3
            ps2mSetDriverState(pThis, true);
#endif
            ps2kClearQueue((GeneriQ *)&pThis->evtQ);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_DFLT_DISABLE:
            ps2mSetDefaults(pThis);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_SET_DEFAULT:
            ps2mSetDefaults(pThis);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_RESEND:
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_RESET:
            ps2mSetDefaults(pThis);
            ///@todo reset more?
            pThis->u8CurrCmd = cmd;
            pThis->enmMode   = AUX_MODE_RESET;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            /* Slightly delay reset completion; it might take hundreds of ms. */
            TMTimerSetMillies(pThis->CTX_SUFF(pDelayTimer), 1);
            break;
        /* The following commands need a parameter. */
        case ACMD_SET_RES:
        case ACMD_SET_SAMP_RATE:
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = cmd;
            break;
        default:
            /* Sending a command instead of a parameter starts the new command. */
            switch (pThis->u8CurrCmd)
            {
                case ACMD_SET_RES:
                    //@todo reject unsupported resolutions
                    pThis->u8Resolution = cmd;
                    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
                    pThis->u8CurrCmd = 0;
                    break;
                case ACMD_SET_SAMP_RATE:
                    //@todo reject unsupported rates                    
                    ps2mSetRate(pThis, cmd);
                    ps2mRateProtocolKnock(pThis, cmd);
                    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
                    pThis->u8CurrCmd = 0;
                    break;
                default:
                    fHandled = false;
            }
            /* Fall through only to handle unrecognized commands. */
            if (fHandled)
                break;

        case ACMD_INVALID_1:
        case ACMD_INVALID_2:
        case ACMD_INVALID_3:
        case ACMD_INVALID_4:
        case ACMD_INVALID_5:
        case ACMD_INVALID_6:
        case ACMD_INVALID_7:
        case ACMD_INVALID_8:
        case ACMD_INVALID_9:
        case ACMD_INVALID_10:
            Log(("Unsupported command 0x%02X!\n", cmd));
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_RESEND);
            pThis->u8CurrCmd = 0;
            break;
    }
    LogFlowFunc(("Active cmd now 0x%02X; updating interrupts\n", pThis->u8CurrCmd));
//    KBCUpdateInterrupts(pThis->pParent);
    return VINF_SUCCESS;
}
Пример #7
0
/**
 * Receive and process a byte sent by the keyboard controller.
 *
 * @param   pThis               The PS/2 auxiliary device instance data.
 * @param   cmd                 The command (or data) byte.
 */
int PS2MByteToAux(PPS2M pThis, uint8_t cmd)
{
    uint8_t u8Val;
    bool    fHandled = true;

    LogFlowFunc(("cmd=0x%02X, active cmd=0x%02X\n", cmd, pThis->u8CurrCmd));
//LogRel(("aux: cmd=0x%02X, active cmd=0x%02X\n", cmd, pThis->u8CurrCmd));

    if (pThis->enmMode == AUX_MODE_RESET)
        /* In reset mode, do not respond at all. */
        return VINF_SUCCESS;

    /* If there's anything left in the command response queue, trash it. */
    ps2kClearQueue((GeneriQ *)&pThis->cmdQ);

    if (pThis->enmMode == AUX_MODE_WRAP)
    {
        /* In wrap mode, bounce most data right back.*/
        if (cmd == ACMD_RESET || cmd == ACMD_RESET_WRAP)
            ;   /* Handle as regular commands. */
        else
        {
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, cmd);
            return VINF_SUCCESS;
        }
    }

#ifndef IN_RING3
    /* Reset, Enable, and Set Default commands must be run in R3. */
    if (cmd == ACMD_RESET || cmd == ACMD_ENABLE || cmd == ACMD_SET_DEFAULT)
        return VINF_IOM_R3_IOPORT_WRITE;
#endif

    switch (cmd)
    {
        case ACMD_SET_SCALE_11:
            pThis->u8State &= ~AUX_STATE_SCALING;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_SET_SCALE_21:
            pThis->u8State |= AUX_STATE_SCALING;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_REQ_STATUS:
            /* Report current status, sample rate, and resolution. */
            u8Val  = (pThis->u8State & AUX_STATE_EXTERNAL) | (pThis->fCurrB & PS2M_STD_BTN_MASK);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, u8Val);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->u8Resolution);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->u8SampleRate);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_SET_STREAM:
            pThis->u8State &= ~AUX_STATE_REMOTE;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_READ_REMOTE:
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            ps2mReportAccumulatedEvents(pThis, (GeneriQ *)&pThis->cmdQ, false);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_RESET_WRAP:
            pThis->enmMode = AUX_MODE_STD;
            /* NB: Stream mode reporting remains disabled! */
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_SET_WRAP:
            pThis->enmMode = AUX_MODE_WRAP;
            pThis->u8State &= ~AUX_STATE_ENABLED;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_SET_REMOTE:
            pThis->u8State |= AUX_STATE_REMOTE;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_READ_ID:
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->enmProtocol);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_ENABLE:
            pThis->u8State |= AUX_STATE_ENABLED;
#ifdef IN_RING3
            ps2mSetDriverState(pThis, true);
#else
            AssertLogRelMsgFailed(("Invalid ACMD_ENABLE outside R3!\n"));
#endif
            ps2kClearQueue((GeneriQ *)&pThis->evtQ);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_DISABLE:
            pThis->u8State &= ~AUX_STATE_ENABLED;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_SET_DEFAULT:
            ps2mSetDefaults(pThis);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_RESEND:
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_RESET:
            ps2mSetDefaults(pThis);
            /// @todo reset more?
            pThis->u8CurrCmd = cmd;
            pThis->enmMode   = AUX_MODE_RESET;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            if (pThis->fDelayReset)
                /* Slightly delay reset completion; it might take hundreds of ms. */
                TMTimerSetMillies(pThis->CTX_SUFF(pDelayTimer), 1);
            else
#ifdef IN_RING3
                ps2mReset(pThis);
#else
                AssertLogRelMsgFailed(("Invalid ACMD_RESET outside R3!\n"));
#endif
            break;
        /* The following commands need a parameter. */
        case ACMD_SET_RES:
        case ACMD_SET_SAMP_RATE:
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = cmd;
            break;
        default:
            /* Sending a command instead of a parameter starts the new command. */
            switch (pThis->u8CurrCmd)
            {
                case ACMD_SET_RES:
                    if (cmd < 4)    /* Valid resolutions are 0-3. */
                    {
                        pThis->u8Resolution = cmd;
                        pThis->u8State &= ~AUX_STATE_RES_ERR;
                        ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
                        pThis->u8CurrCmd = 0;
                    }
                    else
                    {
                        /* Bad resolution. Reply with Resend or Error. */
                        if (pThis->u8State & AUX_STATE_RES_ERR)
                        {
                            pThis->u8State &= ~AUX_STATE_RES_ERR;
                            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ERROR);
                            pThis->u8CurrCmd = 0;
                        }
                        else
                        {
                            pThis->u8State |= AUX_STATE_RES_ERR;
                            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_RESEND);
                            /* NB: Current command remains unchanged. */
                        }
                    }
                    break;
                case ACMD_SET_SAMP_RATE:
                    if (ps2mIsRateSupported(cmd))
                    {
                        pThis->u8State &= ~AUX_STATE_RATE_ERR;
                        ps2mSetRate(pThis, cmd);
                        ps2mRateProtocolKnock(pThis, cmd);
                        ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
                        pThis->u8CurrCmd = 0;
                    }
                    else
                    {
                        /* Bad rate. Reply with Resend or Error. */
                        if (pThis->u8State & AUX_STATE_RATE_ERR)
                        {
                            pThis->u8State &= ~AUX_STATE_RATE_ERR;
                            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ERROR);
                            pThis->u8CurrCmd = 0;
                        }
                        else
                        {
                            pThis->u8State |= AUX_STATE_RATE_ERR;
                            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_RESEND);
                            /* NB: Current command remains unchanged. */
                        }
                    }
                    break;
                default:
                    fHandled = false;
            }
            /* Fall through only to handle unrecognized commands. */
            if (fHandled)
                break;

        case ACMD_INVALID_1:
        case ACMD_INVALID_2:
        case ACMD_INVALID_3:
        case ACMD_INVALID_4:
        case ACMD_INVALID_5:
        case ACMD_INVALID_6:
        case ACMD_INVALID_7:
        case ACMD_INVALID_8:
        case ACMD_INVALID_9:
        case ACMD_INVALID_10:
            Log(("Unsupported command 0x%02X!\n", cmd));
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_RESEND);
            pThis->u8CurrCmd = 0;
            break;
    }
    LogFlowFunc(("Active cmd now 0x%02X; updating interrupts\n", pThis->u8CurrCmd));
//    KBCUpdateInterrupts(pThis->pParent);
    return VINF_SUCCESS;
}