From f6ee95ad975119d7c3106036fd5a581a62695c85 Mon Sep 17 00:00:00 2001 From: Javier Date: Thu, 3 Feb 2022 02:43:40 +0100 Subject: mpu401: rework using circbuf and add optional irq support for uart rx --- Mpu401.cpp | 348 ++++++++++++++++++++++++++++++++++++++++++----------- midialsa.cpp | 181 +++++++++++++++++++--------- midialsa.h | 15 ++- scripts/disable.sh | 9 ++ 4 files changed, 420 insertions(+), 133 deletions(-) create mode 100755 scripts/disable.sh diff --git a/Mpu401.cpp b/Mpu401.cpp index eaced84..7754fd6 100644 --- a/Mpu401.cpp +++ b/Mpu401.cpp @@ -58,6 +58,8 @@ #include #include #include +#include +#include #ifndef IN_RING3 #error "R3-only driver" @@ -76,7 +78,9 @@ typedef MIDIWin MIDIBackend; *********************************************************************************************************************************/ #define MPU_DEFAULT_IO_BASE 0x330 +#define MPU_DEFAULT_IRQ -1 /* disabled */ #define MPU_IO_SIZE 2 +#define MPU_CIRC_BUFFER_SIZE 16 enum { MPU_PORT_DATA = 0, @@ -101,70 +105,163 @@ typedef struct { /* Device configuration. */ /** Base port. */ RTIOPORT uPort; + /** IRQ */ + int8_t uIrq; /* Current state. */ - /** Whether we have an input/result byte waiting to be read. */ - bool fHaveInput; - /** Current input byte waiting to be read. */ - uint8_t uInput; - /** True if UART mode, false if regular/intelligent mode. */ - bool fModeUart; /** MIDI backend. */ MIDIBackend midi; + /** True if UART mode, false if regular/intelligent mode. */ + bool fModeUart; + /** Buffer used for sending UART data. */ + R3PTRTYPE(PRTCIRCBUF) pTxBuf; + /** Buffer used for receiving UART data / command responses. */ + R3PTRTYPE(PRTCIRCBUF) pRxBuf; + + /** Thread which does actual RX/TX. */ + PPDMTHREAD pIoThread; IOMIOPORTHANDLE hIoPorts; + } MPUSTATE; /** Pointer to the shared device state. */ typedef MPUSTATE *PMPUSTATE; #ifndef VBOX_DEVICE_STRUCT_TESTCASE +static void mpuLowerIrq(PPDMDEVINS pDevIns) +{ + PMPUSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PMPUSTATE); + + if (pThis->uIrq >= 0) { + Log7Func(("irq=%RTbool\n", false)); + PDMDevHlpISASetIrqNoWait(pDevIns, pThis->uIrq, PDM_IRQ_LEVEL_LOW); + } +} + +static void mpuUpdateIrq(PPDMDEVINS pDevIns) +{ + PMPUSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PMPUSTATE); + + // This function may be called from the IO thread, too. + + if (pThis->uIrq >= 0) { + bool raise = RTCircBufUsed(pThis->pRxBuf) > 0; + Log7Func(("irq=%RTbool\n", raise)); + PDMDevHlpISASetIrqNoWait(pDevIns, pThis->uIrq, raise ? PDM_IRQ_LEVEL_HIGH : PDM_IRQ_LEVEL_LOW); + } +} + +static void mpuWakeIoThread(PPDMDEVINS pDevIns) +{ + PMPUSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PMPUSTATE); + Log7(("wake io thread\n")); + int rc = pThis->midi.pollInterrupt(); + AssertLogRelRC(rc); +} + static void mpuReset(PPDMDEVINS pDevIns) { PMPUSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PMPUSTATE); + if (pThis->fModeUart) { + Log(("Leaving UART mode\n")); + } + + pThis->fModeUart = false; + + if (pThis->pIoThread) { + int rc = PDMDevHlpThreadSuspend(pDevIns, pThis->pIoThread); + AssertLogRelRC(rc); + } + + mpuLowerIrq(pDevIns); + + RTCircBufReset(pThis->pTxBuf); + RTCircBufReset(pThis->pRxBuf); + pThis->midi.reset(); + mpuUpdateIrq(pDevIns); + + if (pThis->pIoThread) { + int rc = PDMDevHlpThreadResume(pDevIns, pThis->pIoThread); + AssertLogRelRC(rc); + } +} + +static void mpuEnterUart(PPDMDEVINS pDevIns) +{ + PMPUSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PMPUSTATE); + if (pThis->fModeUart) { - Log(("Leaving UART mode")); + Log2(("Already in UART mode\n")); + return; } - pThis->fModeUart = false; - pThis->fHaveInput = false; - pThis->uInput = 0; + Log(("Entering UART mode\n")); + + pThis->fModeUart = true; + + // IO thread needs to wakeup and start polling + mpuWakeIoThread(pDevIns); } static void mpuRespondData(PPDMDEVINS pDevIns, uint8_t data) { PMPUSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PMPUSTATE); + // In UART mode we should not be generating system responses + // which are going to corrupt the MIDI stream. + AssertLogRelReturnVoid(!pThis->fModeUart); + // Also the RxBuf CIRCBUF does not support multiple concurrent writers anyway, + // and in UART mode the IoThread could be writing to it + Log3Func(("enqueing response=0x%x\n", data)); - pThis->fHaveInput = true; - pThis->uInput = data; + uint8_t *buf; + size_t bufSize; + RTCircBufAcquireWriteBlock(pThis->pRxBuf, 1, (void**)&buf, &bufSize); + if (bufSize < 1) { + LogWarnFunc(("overflow in MIDI RX buffer\n")); + return; + } + + *buf = data; + RTCircBufReleaseWriteBlock(pThis->pRxBuf, 1); + + mpuUpdateIrq(pDevIns); } static uint8_t mpuReadData(PPDMDEVINS pDevIns) { PMPUSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PMPUSTATE); + uint8_t ret = 0xff; - if (pThis->fHaveInput) { - pThis->fHaveInput = false; - return pThis->uInput; - } + // Always lower IRQ after a port read, even if there is still more data left + mpuLowerIrq(pDevIns); + + uint8_t *buf; + size_t bufSize; + RTCircBufAcquireReadBlock(pThis->pRxBuf, 1, (void**)&buf, &bufSize); + if (bufSize > 0) { + ret = *buf; + RTCircBufReleaseReadBlock(pThis->pRxBuf, 1); + + Log5Func(("midi_in data=0x%x\n", ret)); - if (pThis->fModeUart && pThis->midi.readAvail() >= 1) { - uint8_t data; - ssize_t read = pThis->midi.read(&data, 1); - if (read == 1) { - Log5Func(("midi_in data=0x%x\n", data)); - return data; + // Raise the IRQ again if we have more data to read + mpuUpdateIrq(pDevIns); + + if (pThis->fModeUart) { + // Also ensure we wake the IO thread to poll for more data + mpuWakeIoThread(pDevIns); } + } else { + Log3Func(("Trying to read, but no data to read, returning 0x%x\n", ret)); } - Log3Func(("Trying to read, but no data to read\n")); - - return MPU_RESPONSE_ACK; + return ret; } static void mpuWriteData(PPDMDEVINS pDevIns, uint8_t data) @@ -172,9 +269,18 @@ static void mpuWriteData(PPDMDEVINS pDevIns, uint8_t data) PMPUSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PMPUSTATE); if (pThis->fModeUart) { - ssize_t written = pThis->midi.write(&data, 1); - if (written == 1) { + uint8_t *buf; + size_t bufSize; + RTCircBufAcquireWriteBlock(pThis->pTxBuf, 1, (void**)&buf, &bufSize); + if (bufSize > 0) { + *buf = data; + RTCircBufReleaseWriteBlock(pThis->pTxBuf, 1); + Log5Func(("midi_out data=0x%x\n", data)); + + mpuWakeIoThread(pDevIns); // So that it has a chance to send the data + } else { + LogWarnFunc(("Overflow in MIDI TX buffer\n")); } } else { Log3Func(("Ignoring data, not in UART mode\n")); @@ -199,18 +305,20 @@ static uint8_t mpuReadStatus(PPDMDEVINS pDevIns) uint8_t status = 0; - bool outputReady = !pThis->fModeUart || pThis->midi.writeAvail() >= 1; - if (!outputReady) { + Log7Func(("tx buf=%zu/%zu rx buf=%zu/%zu\n", + RTCircBufUsed(pThis->pTxBuf), RTCircBufFree(pThis->pTxBuf), + RTCircBufUsed(pThis->pRxBuf), RTCircBufFree(pThis->pRxBuf))); + + if (RTCircBufFree(pThis->pTxBuf) == 0) { status |= RT_BIT(6); } - bool inputReady = pThis->fHaveInput - || (pThis->fModeUart && pThis->midi.readAvail() >= 1); - if (!inputReady) { + if (RTCircBufUsed(pThis->pRxBuf) == 0) { status |= RT_BIT(7); } - Log5(("mpu status: outputReady=%RTbool inputReady=%RTbool\n", outputReady, inputReady)); + Log7(("mpu status: output=%RTbool input=%RTbool\n", + !(status & RT_BIT(6)), !(status & RT_BIT(7)))); return status; } @@ -225,7 +333,11 @@ static void mpuDoCommand(PPDMDEVINS pDevIns, uint8_t cmd) switch (cmd) { case MPU_COMMAND_RESET: mpuReset(pDevIns); - mpuRespondData(pDevIns, MPU_RESPONSE_ACK); + /* "An ACK will not be sent back upon sending a SYSTEM RESET to + * leave the UART MODE ($3F)". */ + break; + case MPU_COMMAND_ENTER_UART: + // Nothing to do? break; default: LogWarnFunc(("Unknown command in UART mode: 0x%hx\n", cmd)); @@ -239,9 +351,8 @@ static void mpuDoCommand(PPDMDEVINS pDevIns, uint8_t cmd) mpuRespondData(pDevIns, MPU_RESPONSE_ACK); break; case MPU_COMMAND_ENTER_UART: - Log(("Entering UART mode\n")); - pThis->fModeUart = true; mpuRespondData(pDevIns, MPU_RESPONSE_ACK); + mpuEnterUart(pDevIns); break; default: LogWarnFunc(("Unknown command in normal mode: 0x%hx\n", cmd)); @@ -251,6 +362,97 @@ static void mpuDoCommand(PPDMDEVINS pDevIns, uint8_t cmd) } } +/** + * @callback_method_impl{PFNPDMTHREADDEV} + */ +static DECLCALLBACK(int) mpuIoThreadLoop(PPDMDEVINS pDevIns, PPDMTHREAD pThread) +{ + RT_NOREF(pDevIns); + PMPUSTATE pThis = (PMPUSTATE)pThread->pvUser; + + if (pThread->enmState == PDMTHREADSTATE_INITIALIZING) + return VINF_SUCCESS; + + LogFlowFuncEnter(); + + while (pThread->enmState == PDMTHREADSTATE_RUNNING) + { + uint32_t events = 0, revents = 0; + + if (pThis->fModeUart) { + if (RTCircBufUsed(pThis->pTxBuf) > 0) { + events |= RTPOLL_EVT_WRITE; + } + if (RTCircBufFree(pThis->pRxBuf) > 0) { + events |= RTPOLL_EVT_READ; + } + } + + Log7Func(("polling for write=%RTbool read=%RTbool\n", + bool(events & RTPOLL_EVT_WRITE), bool(events & RTPOLL_EVT_READ))); + + int rc = pThis->midi.poll(events, &revents, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) { + if (revents & RTPOLL_EVT_WRITE) { + // Write data from the Tx Buf + uint8_t *buf; + size_t bufSize = RTCircBufUsed(pThis->pTxBuf); + if (bufSize > 0) { + ssize_t written = 0; + RTCircBufAcquireReadBlock(pThis->pTxBuf, bufSize, (void**)&buf, &bufSize); + if (bufSize > 0) { + Log7Func(("writing %zu bytes\n", bufSize)); + written = pThis->midi.write(buf, bufSize); + if (written < 0) { + LogWarn(("write failed with %Rrc\n", written)); + written = 0; + } + } + RTCircBufReleaseReadBlock(pThis->pTxBuf, written); + } + } + if (revents & RTPOLL_EVT_READ) { + // Read data into the Rx Buf + uint8_t *buf; + size_t bufSize = RTCircBufFree(pThis->pRxBuf); + if (bufSize > 0) { + ssize_t read = 0; + RTCircBufAcquireWriteBlock(pThis->pRxBuf, bufSize, (void**)&buf, &bufSize); + if (bufSize > 0) { + Log7Func(("reading %zu bytes\n", bufSize)); + read = pThis->midi.read(buf, bufSize); + if (read < 0) { + LogWarnFunc(("read failed with %Rrc\n", read)); + read = 0; + } + } + RTCircBufReleaseWriteBlock(pThis->pRxBuf, read); + if (read > 0) { + mpuUpdateIrq(pDevIns); + } + } + } + } else { + LogWarnFunc(("poll failed with %Rrc", rc)); + } + } + + LogFlowFuncLeave(); + + return VINF_SUCCESS; +} + +/** + * @callback_method_impl{PFNPDMTHREADWAKEUPDEV} + */ +static DECLCALLBACK(int) mpuIoThreadWakeup(PPDMDEVINS pDevIns, PPDMTHREAD pThread) +{ + PMPUSTATE pThis = (PMPUSTATE)pThread->pvUser; + + RT_NOREF(pDevIns); + return pThis->midi.pollInterrupt(); +} + /** * @callback_method_impl{FNIOMIOPORTNEWIN} */ @@ -326,8 +528,8 @@ static DECLCALLBACK(int) mpuR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) PMPUSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PMPUSTATE); PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3; - pHlp->pfnSSMPutBool(pSSM, pThis->fHaveInput); - pHlp->pfnSSMPutU8 (pSSM, pThis->uInput); + AssertLogRel(pThis->pIoThread->enmState != PDMTHREADSTATE_RUNNING); + pHlp->pfnSSMPutBool(pSSM, pThis->fModeUart); return 0; @@ -344,8 +546,8 @@ static DECLCALLBACK(int) mpuR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint Assert(uPass == SSM_PASS_FINAL); NOREF(uPass); - pHlp->pfnSSMGetBool(pSSM, &pThis->fHaveInput); - pHlp->pfnSSMGetU8 (pSSM, &pThis->uInput); + AssertLogRel(pThis->pIoThread->enmState != PDMTHREADSTATE_RUNNING); + pHlp->pfnSSMGetBool(pSSM, &pThis->fModeUart); if (uVersion > MPU_SAVED_STATE_VERSION) @@ -366,25 +568,29 @@ static DECLCALLBACK(int) mpuR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGM Assert(iInstance == 0); - /* - * Validate and read the configuration. - */ - PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "Port", ""); + // Validate and read the configuration. + PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "Port|IRQ", ""); rc = pHlp->pfnCFGMQueryPortDef(pCfg, "Port", &pThis->uPort, MPU_DEFAULT_IO_BASE); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"Port\" from the config")); - LogFlowFunc(("mpu401#%i: port 0x%x\n", iInstance, pThis->uPort)); + rc = pHlp->pfnCFGMQueryS8Def(pCfg, "IRQ", &pThis->uIrq, MPU_DEFAULT_IRQ); + if (RT_FAILURE(rc)) + return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"IRQ\" from the config")); + + LogFlowFunc(("mpu401#%i: port 0x%x irq %d\n", iInstance, pThis->uPort, pThis->uIrq)); - /* - * Initialize the device state. - */ + // Create buffers + rc = RTCircBufCreate(&pThis->pTxBuf, MPU_CIRC_BUFFER_SIZE); + AssertRCReturn(rc, rc); + rc = RTCircBufCreate(&pThis->pRxBuf, MPU_CIRC_BUFFER_SIZE); + AssertRCReturn(rc, rc); + + // Initialize the device state. mpuReset(pDevIns); - /* - * Register I/O ports. - */ + // Register I/O ports static const IOMIOPORTDESC s_aDescs[] = { { "Data", "Data", NULL, NULL }, // base + 00h @@ -395,17 +601,23 @@ static DECLCALLBACK(int) mpuR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGM "MPU-401", s_aDescs, &pThis->hIoPorts); AssertRCReturn(rc, rc); - /* - * Register saved state. - */ + // Register saved state rc = PDMDevHlpSSMRegister(pDevIns, MPU_SAVED_STATE_VERSION, sizeof(*pThis), mpuR3SaveExec, mpuR3LoadExec); AssertRCReturn(rc, rc); - /* Open the MIDI device now. */ + // Open the MIDI device now, before we create the IO thread which may poll it. rc = pThis->midi.open("default"); AssertRCReturn(rc, rc); + // Create the IO thread; note that this starts it... + rc = PDMDevHlpThreadCreate(pDevIns, &pThis->pIoThread, pThis, mpuIoThreadLoop, + mpuIoThreadWakeup, 0, RTTHREADTYPE_IO, "MpuIo"); + AssertRCReturn(rc, rc); + LogRel(("mpu401#%i: Configured on port 0x%x-0x%x\n", iInstance, pThis->uPort, pThis->uPort + MPU_IO_SIZE - 1)); + if (pThis->uIrq >= 0) { + LogRel(("mpu401#%i: Using IRQ %d\n", iInstance, pThis->uIrq)); + } return VINF_SUCCESS; } @@ -417,8 +629,19 @@ static DECLCALLBACK(int) mpuR3Destruct(PPDMDEVINS pDevIns) { PMPUSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PMPUSTATE); + if (pThis->pIoThread) { + int rc, rcThread; + rc = PDMDevHlpThreadDestroy(pDevIns, pThis->pIoThread, &rcThread); + AssertLogRelRC(rc); + pThis->pIoThread = NULL; + } + int rc = pThis->midi.close(); - AssertRCReturn(rc, rc); + AssertLogRelRC(rc); + + RTCircBufDestroy(pThis->pTxBuf); + RTCircBufDestroy(pThis->pRxBuf); + return VINF_SUCCESS; } @@ -434,17 +657,6 @@ static DECLCALLBACK(void) mpuR3Reset(PPDMDEVINS pDevIns) mpuReset(pDevIns); } -/** - * @interface_method_impl{PDMDEVREG,pfnPowerOff} - */ -static DECLCALLBACK(void) mpuR3PowerOff(PPDMDEVINS pDevIns) -{ - PMPUSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PMPUSTATE); - - int rc = pThis->midi.close(); - AssertRC(rc); -} - # endif /* !IN_RING3 */ @@ -481,7 +693,7 @@ static const PDMDEVREG g_DeviceMpu = /* .pfnDetach = */ NULL, /* .pfnQueryInterface = */ NULL, /* .pfnInitComplete = */ NULL, - /* .pfnPowerOff = */ mpuR3PowerOff, + /* .pfnPowerOff = */ NULL, /* .pfnSoftReset = */ NULL, /* .pfnReserved0 = */ NULL, /* .pfnReserved1 = */ NULL, diff --git a/midialsa.cpp b/midialsa.cpp index d9c36ed..ff2c41f 100644 --- a/midialsa.cpp +++ b/midialsa.cpp @@ -16,56 +16,23 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#define LOG_ENABLED 1 -#define LOG_ENABLE_FLOW 1 +//#define LOG_ENABLED 1 +//#define LOG_ENABLE_FLOW 1 #define LOG_GROUP LOG_GROUP_DEV_SB16 +#include +#include #include #include #include +#include #include "midialsa.h" #define MAX_POLL_FDS 4 -static ssize_t rawmidi_avail(snd_rawmidi_t *rmidi) -{ - struct pollfd pfds[MAX_POLL_FDS]; - - int nfds = snd_rawmidi_poll_descriptors(rmidi, pfds, MAX_POLL_FDS); - if (nfds <= 0) { - LogWarn(("ALSA rawmidi avail: no descriptors to poll!\n")); - return VERR_AUDIO_ENUMERATION_FAILED; - } - - int ready = poll(pfds, nfds, 0); - if (ready < 0) { - if (errno != EAGAIN && errno != EINTR) { - LogWarnFunc(("Cannot poll, errno=%d\n", errno)); - return VERR_AUDIO_STREAM_NOT_READY; - } - return 0; - } else if (ready == 0) { - return 0; - } else /* ready > 0 */ { - unsigned short revents; - int err = snd_rawmidi_poll_descriptors_revents(rmidi, pfds, nfds, &revents); - if (err != 0) { - LogWarnFunc(("Cannot call revents, err=%d\n", err)); - return VERR_AUDIO_STREAM_NOT_READY; - } - if (revents & POLLNVAL) { - LogWarnFunc(("POLLNVAL\n")); - } - if (revents & POLLERR) { - LogWarnFunc(("POLLERR\n")); - } - return revents & (POLLIN | POLLOUT); - } -} - -MIDIAlsa::MIDIAlsa() : _out(NULL) +MIDIAlsa::MIDIAlsa() : _out(NULL), _eventfd(-1) { } @@ -78,6 +45,12 @@ int MIDIAlsa::open(const char *dev) { int err; + _eventfd = ::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (_eventfd == -1) { + LogWarn(("eventfd error: %s\n", strerror(errno))); + return RTErrConvertFromErrno(errno); + } + if ((err = snd_rawmidi_open(&_in, &_out, "virtual", SND_RAWMIDI_NONBLOCK))) { LogWarn(("ALSA rawmidi open error: %s\n", snd_strerror(err))); return VERR_AUDIO_STREAM_COULD_NOT_CREATE; @@ -99,47 +72,137 @@ int MIDIAlsa::close() snd_rawmidi_close(_out); _out = NULL; } + if (_eventfd) { + ::close(_eventfd); + } return VINF_SUCCESS; } -ssize_t MIDIAlsa::writeAvail() +int MIDIAlsa::reset() { - return _out ? rawmidi_avail(_out) : 0; + if (_in) { + snd_rawmidi_drop(_in); + } + if (_out) { + snd_rawmidi_drop(_out); + } + return VINF_SUCCESS; } +int MIDIAlsa::poll(uint32_t events, uint32_t *revents, RTMSINTERVAL millies) +{ + struct pollfd pfds[MAX_POLL_FDS]; + int i_pipe = -1, i_in = -1, i_out = -1; + int n_in = 0, n_out = 0; + int nfds = 0; + + LogFlowFuncEnter(); + + *revents = 0; + + if (millies > 0 && _eventfd != -1) { + LogFlowFunc(("including eventfd\n")); + i_pipe = nfds; + pfds[nfds].fd = _eventfd; + pfds[nfds].events = POLLIN; + nfds++; + } + + if (_in && events & RTPOLL_EVT_READ) { + LogFlowFunc(("including in\n")); + i_in = nfds; + n_in = snd_rawmidi_poll_descriptors(_in, &pfds[i_in], MAX_POLL_FDS - nfds); + AssertLogRelReturn(n_in > 0, VERR_IO_NOT_READY); + nfds+=n_in; + } + if (_out && events & RTPOLL_EVT_WRITE) { + LogFlowFunc(("including out\n")); + i_out = nfds; + n_out = snd_rawmidi_poll_descriptors(_out, &pfds[i_out], MAX_POLL_FDS - nfds); + AssertLogRelReturn(n_out > 0, VERR_IO_NOT_READY); + nfds+=n_out; + } + + AssertReturn(millies != RT_INDEFINITE_WAIT || nfds > 0, VERR_DEADLOCK); + + int rc; + int ready = ::poll(pfds, nfds, millies = RT_INDEFINITE_WAIT ? -1 : millies); + LogFlowFunc(("poll ready=%d\n", ready)); + if (ready < 0) { + if (errno != EAGAIN && errno != EINTR) { + LogWarnFunc(("Cannot poll, errno=%d\n", errno)); + rc = VERR_IO_NOT_READY; + } else { + rc = VINF_TRY_AGAIN; + } + } else if (ready == 0) { + // Timeout + rc = VINF_TIMEOUT; + } else /* ready > 0 */ { + rc = VINF_SUCCESS; + if (i_pipe != -1) { + if (pfds[i_pipe].revents) { + uint64_t val; + ssize_t r = ::read(_eventfd, &val, sizeof(val)); + Assert(r == sizeof(val)); + NOREF(r); + rc = VINF_INTERRUPTED; + } + } + if (i_in != -1) { + Assert(n_in > 0); + unsigned short rev; + + int err = snd_rawmidi_poll_descriptors_revents(_in, &pfds[i_in], n_in, &rev); + AssertLogRelReturn(err >= 0, VERR_IO_GEN_FAILURE); + + if (rev & POLLIN) *revents |= RTPOLL_EVT_READ; + if (rev & (POLLNVAL|POLLERR)) *revents |= RTPOLL_EVT_ERROR; + } + if (i_out != -1) { + Assert(n_out > 0); + unsigned short rev; + int err = snd_rawmidi_poll_descriptors_revents(_out, &pfds[i_out], n_out, &rev); + AssertLogRelReturn(err >= 0, VERR_IO_GEN_FAILURE); + + if (rev & POLLOUT) *revents |= RTPOLL_EVT_WRITE; + if (rev & (POLLNVAL|POLLERR)) *revents |= RTPOLL_EVT_ERROR; + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +int MIDIAlsa::pollInterrupt() +{ + if (_eventfd) { + uint64_t val = 1; + ssize_t r = ::write(_eventfd, &val, sizeof(val)); + Assert(r == sizeof(uint64_t)); + NOREF(r); + return VINF_SUCCESS; + } else { + return VERR_INVALID_STATE; + } +} ssize_t MIDIAlsa::write(uint8_t *data, size_t len) { ssize_t result = snd_rawmidi_write(_out, data, len); if (result < 0) { LogWarn(("ALSA midi write error: %s\n", snd_strerror(result))); - return VERR_AUDIO_STREAM_NOT_READY; + return VERR_BROKEN_PIPE; } return result; } -ssize_t MIDIAlsa::readAvail() -{ - return _in ? rawmidi_avail(_in) : 0; -} - ssize_t MIDIAlsa::read(uint8_t *buf, size_t len) { ssize_t result = snd_rawmidi_read(_in, buf, len); if (result < 0) { LogWarn(("ALSA midi read error: %s\n", snd_strerror(result))); - return VERR_AUDIO_STREAM_NOT_READY; + return VERR_BROKEN_PIPE; } return result; } - -int MIDIAlsa::reset() -{ - if (_in) { - snd_rawmidi_drop(_in); - } - if (_out) { - snd_rawmidi_drop(_out); - } - return VINF_SUCCESS; -} diff --git a/midialsa.h b/midialsa.h index f41200b..eb2fe2c 100644 --- a/midialsa.h +++ b/midialsa.h @@ -19,8 +19,7 @@ #ifndef VMUSIC_MIDIALSA_H #define VMUSIC_MIDIALSA_H -#include -#include +#include typedef struct _snd_rawmidi snd_rawmidi_t; @@ -30,20 +29,24 @@ public: MIDIAlsa(); ~MIDIAlsa(); + /** dev has no effect right now. */ int open(const char *dev); int close(); - ssize_t writeAvail(); + /** reset device, dropping all buffers, but keep open */ + int reset(); + + int poll(uint32_t events, uint32_t *revents, RTMSINTERVAL millies); + int pollInterrupt(); + ssize_t write(uint8_t *data, size_t len); - ssize_t readAvail(); ssize_t read(uint8_t *buf, size_t len); - int reset(); - private: snd_rawmidi_t *_in; snd_rawmidi_t *_out; + int _eventfd; }; #endif diff --git a/scripts/disable.sh b/scripts/disable.sh new file mode 100755 index 0000000..8031d25 --- /dev/null +++ b/scripts/disable.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -ex + +vm="$1" + +VBoxManage setextradata "$vm" VBoxInternal/Devices/adlib/0/Trusted +VBoxManage setextradata "$vm" VBoxInternal/Devices/adlib/0/Config/MirrorPort +VBoxManage setextradata "$vm" VBoxInternal/Devices/mpu401/0/Trusted -- cgit v1.2.3