Beispiel #1
0
/* Event rate throttling timer to emulate the auxiliary device sampling rate.
 */
static DECLCALLBACK(void) ps2mThrottleTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
{
    PPS2M       pThis = (PS2M *)pvUser; NOREF(pDevIns);
    uint32_t    uHaveEvents;

    /* Grab the lock to avoid races with PutEvent(). */
    int rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
    AssertReleaseRC(rc);

#if 0
    /* If the input queue is not empty, restart the timer. */
#else
    /* If more movement is accumulated, report it and restart the timer. */
    uHaveEvents = pThis->iAccumX | pThis->iAccumY | pThis->iAccumZ | pThis->fAccumB;
    LogFlowFunc(("Have%s events\n", uHaveEvents ? "" : " no"));

    if (uHaveEvents)
#endif
    {
        ps2mReportAccumulatedEvents(pThis);
        TMTimerSetMillies(pThis->CTX_SUFF(pThrottleTimer), pThis->uThrottleDelay);
    }
    else
        pThis->fThrottleActive = false;

    PDMCritSectLeave(pThis->pCritSectR3);
}
Beispiel #2
0
/* Event rate throttling timer to emulate the auxiliary device sampling rate.
 */
static DECLCALLBACK(void) ps2mThrottleTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
{
    RT_NOREF2(pDevIns, pTimer);
    PPS2M       pThis = (PS2M *)pvUser;
    uint32_t    uHaveEvents;

    /* Grab the lock to avoid races with PutEvent(). */
    int rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
    AssertReleaseRC(rc);

#if 0
    /* If the input queue is not empty, restart the timer. */
#else
    /* If more movement is accumulated, report it and restart the timer. */
    uHaveEvents = pThis->iAccumX | pThis->iAccumY | pThis->iAccumZ | (pThis->fCurrB != pThis->fReportedB);
    LogFlowFunc(("Have%s events\n", uHaveEvents ? "" : " no"));

    if (uHaveEvents)
#endif
    {
        /* Report accumulated data, poke the KBC, and start the timer. */
        ps2mReportAccumulatedEvents(pThis, (GeneriQ *)&pThis->evtQ, true);
        KBCUpdateInterrupts(pThis->pParent);
        TMTimerSetMillies(pThis->CTX_SUFF(pThrottleTimer), pThis->uThrottleDelay);
    }
    else
        pThis->fThrottleActive = false;

    PDMCritSectLeave(pThis->pCritSectR3);
}
static DECLCALLBACK(void) pdmacR3TimerCallback(PVM pVM, PTMTIMER pTimer, void *pvUser)
{
    uint64_t tsCur = RTTimeProgramMilliTS();
    uint64_t cMilliesNext = UINT64_MAX;
    PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pvUser;

    ASMAtomicWriteU64(&pEpClassFile->cMilliesNext, UINT64_MAX);

    /* Go through all endpoints and check for expired requests. */
    PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpClassFile->Core.pEndpointsHead;

    while (pEpFile)
    {
        /* Check for an expired delay. */
        if (pEpFile->pDelayedHead != NULL)
        {
            PPDMASYNCCOMPLETIONTASKFILE pTaskFile = ASMAtomicXchgPtrT(&pEpFile->pDelayedHead, NULL, PPDMASYNCCOMPLETIONTASKFILE);

            while (pTaskFile)
            {
                PPDMASYNCCOMPLETIONTASKFILE pTmp = pTaskFile;
                pTaskFile = pTaskFile->pDelayedNext;

                if (tsCur >= pTmp->tsDelayEnd)
                {
                    LogRel(("AIOMgr: Delayed request %#p completed\n", pTmp));
                    pdmR3AsyncCompletionCompleteTask(&pTmp->Core, pTmp->rc, true);
                }
                else
                {
                    /* Prepend to the delayed list again. */
                    PPDMASYNCCOMPLETIONTASKFILE pHead = NULL;

                    if (pTmp->tsDelayEnd - tsCur < cMilliesNext)
                        cMilliesNext = pTmp->tsDelayEnd - tsCur;

                    do
                    {
                        pHead = ASMAtomicReadPtrT(&pEpFile->pDelayedHead, PPDMASYNCCOMPLETIONTASKFILE);
                        pTmp->pDelayedNext = pHead;
                    } while (!ASMAtomicCmpXchgPtr(&pEpFile->pDelayedHead, pTmp, pHead));
                }
            }
        }

        pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpFile->Core.pNext;
    }

    if (cMilliesNext < pEpClassFile->cMilliesNext)
    {
        ASMAtomicWriteU64(&pEpClassFile->cMilliesNext, cMilliesNext);
        TMTimerSetMillies(pEpClassFile->pTimer, cMilliesNext);
    }
}
Beispiel #4
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;
}
Beispiel #5
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;
}
void pdmacFileEpTaskCompleted(PPDMACTASKFILE pTask, void *pvUser, int rc)
{
    PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pvUser;

    LogFlowFunc(("pTask=%#p pvUser=%#p rc=%Rrc\n", pTask, pvUser, rc));

    if (pTask->enmTransferType == PDMACTASKFILETRANSFER_FLUSH)
        pdmR3AsyncCompletionCompleteTask(&pTaskFile->Core, rc, true);
    else
    {
        Assert((uint32_t)pTask->DataSeg.cbSeg == pTask->DataSeg.cbSeg && (int32_t)pTask->DataSeg.cbSeg >= 0);
        uint32_t uOld = ASMAtomicSubS32(&pTaskFile->cbTransferLeft, (int32_t)pTask->DataSeg.cbSeg);

        /* The first error will be returned. */
        if (RT_FAILURE(rc))
            ASMAtomicCmpXchgS32(&pTaskFile->rc, rc, VINF_SUCCESS);
#ifdef VBOX_WITH_DEBUGGER
        else
        {
            PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pTaskFile->Core.pEndpoint;

            /* Overwrite with injected error code. */
            if (pTask->enmTransferType == PDMACTASKFILETRANSFER_READ)
                rc = ASMAtomicXchgS32(&pEpFile->rcReqRead, VINF_SUCCESS);
            else
                rc = ASMAtomicXchgS32(&pEpFile->rcReqWrite, VINF_SUCCESS);

            if (RT_FAILURE(rc))
                ASMAtomicCmpXchgS32(&pTaskFile->rc, rc, VINF_SUCCESS);
        }
#endif

        if (!(uOld - pTask->DataSeg.cbSeg)
            && !ASMAtomicXchgBool(&pTaskFile->fCompleted, true))
        {
#ifdef PDM_ASYNC_COMPLETION_FILE_WITH_DELAY
            PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pTaskFile->Core.pEndpoint;
            PPDMASYNCCOMPLETIONEPCLASSFILE  pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEpFile->Core.pEpClass;

            /* Check if we should delay completion of the request. */
            if (   ASMAtomicReadU32(&pEpFile->msDelay) > 0
                && ASMAtomicReadU32(&pEpFile->cReqsDelay) > 0)
            {
                uint64_t tsDelay = pEpFile->msDelay;

                if (pEpFile->msJitter)
                    tsDelay = (RTRandU32() % 100) > 50 ? pEpFile->msDelay + (RTRandU32() % pEpFile->msJitter)
                                                       : pEpFile->msDelay - (RTRandU32() % pEpFile->msJitter);
                ASMAtomicDecU32(&pEpFile->cReqsDelay);

                /* Arm the delay. */
                pTaskFile->tsDelayEnd = RTTimeProgramMilliTS() + tsDelay;

                /* Append to the list. */
                PPDMASYNCCOMPLETIONTASKFILE pHead = NULL;
                do
                {
                    pHead = ASMAtomicReadPtrT(&pEpFile->pDelayedHead, PPDMASYNCCOMPLETIONTASKFILE);
                    pTaskFile->pDelayedNext = pHead;
                } while (!ASMAtomicCmpXchgPtr(&pEpFile->pDelayedHead, pTaskFile, pHead));

                if (tsDelay < pEpClassFile->cMilliesNext)
                {
                    ASMAtomicWriteU64(&pEpClassFile->cMilliesNext, tsDelay);
                    TMTimerSetMillies(pEpClassFile->pTimer, tsDelay);
                }

                LogRel(("AIOMgr: Delaying request %#p for %u ms\n", pTaskFile, tsDelay));
            }
            else
#endif
                pdmR3AsyncCompletionCompleteTask(&pTaskFile->Core, pTaskFile->rc, true);
        }
    }
}
Beispiel #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;
}
Beispiel #8
0
/**
 * Internal worker for the queue creation apis.
 *
 * @returns VBox status.
 * @param   pVM                 Pointer to the VM.
 * @param   cbItem              Item size.
 * @param   cItems              Number of items.
 * @param   cMilliesInterval    Number of milliseconds between polling the queue.
 *                              If 0 then the emulation thread will be notified whenever an item arrives.
 * @param   fRZEnabled          Set if the queue will be used from RC/R0 and need to be allocated from the hyper heap.
 * @param   pszName             The queue name. Unique. Not copied.
 * @param   ppQueue             Where to store the queue handle.
 */
static int pdmR3QueueCreate(PVM pVM, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval, bool fRZEnabled,
                            const char *pszName, PPDMQUEUE *ppQueue)
{
    PUVM pUVM = pVM->pUVM;

    /*
     * Validate input.
     */
    AssertMsgReturn(cbItem >= sizeof(PDMQUEUEITEMCORE) && cbItem < _1M, ("cbItem=%zu\n", cbItem), VERR_OUT_OF_RANGE);
    AssertMsgReturn(cItems >= 1 && cItems <= _64K, ("cItems=%u\n", cItems), VERR_OUT_OF_RANGE);

    /*
     * Align the item size and calculate the structure size.
     */
    cbItem = RT_ALIGN(cbItem, sizeof(RTUINTPTR));
    size_t cb = cbItem * cItems + RT_ALIGN_Z(RT_OFFSETOF(PDMQUEUE, aFreeItems[cItems + PDMQUEUE_FREE_SLACK]), 16);
    PPDMQUEUE pQueue;
    int rc;
    if (fRZEnabled)
        rc = MMHyperAlloc(pVM, cb, 0, MM_TAG_PDM_QUEUE, (void **)&pQueue );
    else
        rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_QUEUE, cb, (void **)&pQueue);
    if (RT_FAILURE(rc))
        return rc;

    /*
     * Initialize the data fields.
     */
    pQueue->pVMR3 = pVM;
    pQueue->pVMR0 = fRZEnabled ? pVM->pVMR0 : NIL_RTR0PTR;
    pQueue->pVMRC = fRZEnabled ? pVM->pVMRC : NIL_RTRCPTR;
    pQueue->pszName = pszName;
    pQueue->cMilliesInterval = cMilliesInterval;
    //pQueue->pTimer = NULL;
    pQueue->cbItem = (uint32_t)cbItem;
    pQueue->cItems = cItems;
    //pQueue->pPendingR3 = NULL;
    //pQueue->pPendingR0 = NULL;
    //pQueue->pPendingRC = NULL;
    pQueue->iFreeHead = cItems;
    //pQueue->iFreeTail = 0;
    PPDMQUEUEITEMCORE pItem = (PPDMQUEUEITEMCORE)((char *)pQueue + RT_ALIGN_Z(RT_OFFSETOF(PDMQUEUE, aFreeItems[cItems + PDMQUEUE_FREE_SLACK]), 16));
    for (unsigned i = 0; i < cItems; i++, pItem = (PPDMQUEUEITEMCORE)((char *)pItem + cbItem))
    {
        pQueue->aFreeItems[i].pItemR3 = pItem;
        if (fRZEnabled)
        {
            pQueue->aFreeItems[i].pItemR0 = MMHyperR3ToR0(pVM, pItem);
            pQueue->aFreeItems[i].pItemRC = MMHyperR3ToRC(pVM, pItem);
        }
    }

    /*
     * Create timer?
     */
    if (cMilliesInterval)
    {
        rc = TMR3TimerCreateInternal(pVM, TMCLOCK_REAL, pdmR3QueueTimer, pQueue, "Queue timer", &pQueue->pTimer);
        if (RT_SUCCESS(rc))
        {
            rc = TMTimerSetMillies(pQueue->pTimer, cMilliesInterval);
            if (RT_FAILURE(rc))
            {
                AssertMsgFailed(("TMTimerSetMillies failed rc=%Rrc\n", rc));
                int rc2 = TMR3TimerDestroy(pQueue->pTimer);
                AssertRC(rc2);
            }
        }
        else
            AssertMsgFailed(("TMR3TimerCreateInternal failed rc=%Rrc\n", rc));
        if (RT_FAILURE(rc))
        {
            if (fRZEnabled)
                MMHyperFree(pVM, pQueue);
            else
                MMR3HeapFree(pQueue);
            return rc;
        }

        /*
         * Insert into the queue list for timer driven queues.
         */
        pdmLock(pVM);
        pQueue->pNext = pUVM->pdm.s.pQueuesTimer;
        pUVM->pdm.s.pQueuesTimer = pQueue;
        pdmUnlock(pVM);
    }
    else
    {
        /*
         * Insert into the queue list for forced action driven queues.
         * This is a FIFO, so insert at the end.
         */
        /** @todo we should add a priority to the queues so we don't have to rely on
         * the initialization order to deal with problems like @bugref{1605} (pgm/pcnet
         * deadlock caused by the critsect queue to be last in the chain).
         * - Update, the critical sections are no longer using queues, so this isn't a real
         *   problem any longer. The priority might be a nice feature for later though.
         */
        pdmLock(pVM);
        if (!pUVM->pdm.s.pQueuesForced)
            pUVM->pdm.s.pQueuesForced = pQueue;
        else
        {
            PPDMQUEUE pPrev = pUVM->pdm.s.pQueuesForced;
            while (pPrev->pNext)
                pPrev = pPrev->pNext;
            pPrev->pNext = pQueue;
        }
        pdmUnlock(pVM);
    }

    /*
     * Register the statistics.
     */
    STAMR3RegisterF(pVM, &pQueue->cbItem,               STAMTYPE_U32,     STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,        "Item size.",                       "/PDM/Queue/%s/cbItem",         pQueue->pszName);
    STAMR3RegisterF(pVM, &pQueue->cItems,               STAMTYPE_U32,     STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,        "Queue size.",                      "/PDM/Queue/%s/cItems",         pQueue->pszName);
    STAMR3RegisterF(pVM, &pQueue->StatAllocFailures,    STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,   "PDMQueueAlloc failures.",          "/PDM/Queue/%s/AllocFailures",  pQueue->pszName);
    STAMR3RegisterF(pVM, &pQueue->StatInsert,           STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS,        "Calls to PDMQueueInsert.",         "/PDM/Queue/%s/Insert",         pQueue->pszName);
    STAMR3RegisterF(pVM, &pQueue->StatFlush,            STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS,        "Calls to pdmR3QueueFlush.",        "/PDM/Queue/%s/Flush",          pQueue->pszName);
    STAMR3RegisterF(pVM, &pQueue->StatFlushLeftovers,   STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,   "Left over items after flush.",     "/PDM/Queue/%s/FlushLeftovers", pQueue->pszName);
#ifdef VBOX_WITH_STATISTICS
    STAMR3RegisterF(pVM, &pQueue->StatFlushPrf,         STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS,        "Profiling pdmR3QueueFlush.",       "/PDM/Queue/%s/FlushPrf",       pQueue->pszName);
    STAMR3RegisterF(pVM, (void *)&pQueue->cStatPending, STAMTYPE_U32,     STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,        "Pending items.",                   "/PDM/Queue/%s/Pending",        pQueue->pszName);
#endif

    *ppQueue = pQueue;
    return VINF_SUCCESS;
}