aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2022-02-03 02:43:40 +0100
committerJavier <dev.git@javispedro.com>2022-02-03 13:26:11 +0100
commitf6ee95ad975119d7c3106036fd5a581a62695c85 (patch)
tree6fe7d757d7dcd15b275db8e18778d1769071c6c8
parent2f1022df0395badf03fc7cb20c82ee770b5b2c1d (diff)
downloadvmusic-f6ee95ad975119d7c3106036fd5a581a62695c85.tar.gz
vmusic-f6ee95ad975119d7c3106036fd5a581a62695c85.zip
mpu401: rework using circbuf and add optional irq support for uart rx
-rw-r--r--Mpu401.cpp348
-rw-r--r--midialsa.cpp181
-rw-r--r--midialsa.h15
-rwxr-xr-xscripts/disable.sh9
4 files changed, 420 insertions, 133 deletions
diff --git a/Mpu401.cpp b/Mpu401.cpp
index eaced84..7754fd6 100644
--- a/Mpu401.cpp
+++ b/Mpu401.cpp
@@ -58,6 +58,8 @@
#include <VBox/version.h>
#include <iprt/assert.h>
#include <iprt/mem.h>
+#include <iprt/poll.h>
+#include <iprt/circbuf.h>
#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));
@@ -252,6 +363,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}
*/
static DECLCALLBACK(VBOXSTRICTRC) mpuIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
@@ -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 <iprt/assert.h>
+#include <iprt/poll.h>
#include <VBox/err.h>
#include <VBox/log.h>
#include <alsa/asoundlib.h>
+#include <sys/eventfd.h>
#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 <stddef.h>
-#include <stdint.h>
+#include <iprt/types.h>
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