/* 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); }
/* 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); } }
/** * 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; }
/** * 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); } } }
/** * 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; }
/** * 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; }