[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v11 00/11] Add VIRTIO sound card
From: |
Manos Pitsidianakis |
Subject: |
[PATCH v11 00/11] Add VIRTIO sound card |
Date: |
Wed, 11 Oct 2023 17:34:45 +0300 |
From: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
This patch series adds an audio device implementing the recent virtio
sound spec (1.2) and a corresponding PCI wrapper device.
v11 can be found online at:
https://gitlab.com/epilys/qemu/-/tree/virtio-snd-v11
Ref 885b01fe272541fdab5583780d4c3a59bfd8e734
Main differences with v10 patch series [^v10]
<cover.1695996196.git.manos.pitsidianakis@linaro.org>:
- Rebased against upstream, which has minor changes to the AUD_* API.
- Fixed noise in playback because of invalid bounds when accessing the
audio data in the VirtQueueElement.
- Refactor invalid I/O message queue flushing into separate function.
- Removed attempt to write unwritten bytes to QEMU sound backend when
flushing: it should only happen when the stream STARTs.
- Set latency_bytes to buffer size when returning TX I/O message because
it happens immediately after writing the last bytes to the QEMU
backend, therefore there might be up to <buffer size> bytes to be
played before all the buffer data has finished playing.
- Addressed [^v10] review comments:
- Refactored VirtIOSoundPCMBuffer return code into a function instead
of using goto labels in output/input audio callbacks. (Suggested by
<philmd@linaro.org>)
Previously:
[^v10]:
https://lore.kernel.org/qemu-devel/cover.1695996196.git.manos.pitsidianakis@linaro.org/
[^v9]:
https://lore.kernel.org/qemu-devel/cover.1694588927.git.manos.pitsidianakis@linaro.org/
[^v8]:
https://lore.kernel.org/qemu-devel/cover.1693252037.git.manos.pitsidianakis@linaro.org/
[^v7]:
https://lore.kernel.org/qemu-devel/cover.1692731646.git.manos.pitsidianakis@linaro.org/
[^v6]:
https://lore.kernel.org/qemu-devel/cover.1692089917.git.manos.pitsidianakis@linaro.org/
[^v5]:
https://lore.kernel.org/qemu-devel/cover.1690626150.git.manos.pitsidianakis@linaro.org/
[^v4]:
https://lore.kernel.org/qemu-devel/cover.1689857559.git.manos.pitsidianakis@linaro.org/
[^v3]:
https://lore.kernel.org/qemu-devel/cover.1689692765.git.manos.pitsidianakis@linaro.org/
Emmanouil Pitsidianakis (11):
Add virtio-sound device stub
Add virtio-sound-pci device
virtio-sound: handle control messages and streams
virtio-sound: handle VIRTIO_SND_R_PCM_INFO request
virtio-sound: handle VIRTIO_SND_R_PCM_{START,STOP}
virtio-sound: handle VIRTIO_SND_R_PCM_SET_PARAMS
virtio-sound: handle VIRTIO_SND_R_PCM_PREPARE
virtio-sound: handle VIRTIO_SND_R_PCM_RELEASE
virtio-sound: implement audio output (TX)
virtio-sound: implement audio capture (RX)
docs/system: add basic virtio-snd documentation
MAINTAINERS | 7 +
docs/system/device-emulation.rst | 1 +
docs/system/devices/virtio-snd.rst (new) | 49 +
hw/virtio/Kconfig | 5 +
hw/virtio/meson.build | 2 +
hw/virtio/trace-events | 20 +
hw/virtio/virtio-snd-pci.c (new) | 93 ++
hw/virtio/virtio-snd.c (new) | 1409 ++++++++++++++++++++++
include/hw/virtio/virtio-snd.h (new) | 235 ++++
system/qdev-monitor.c | 1 +
10 files changed, 1822 insertions(+)
create mode 100644 docs/system/devices/virtio-snd.rst
create mode 100644 hw/virtio/virtio-snd-pci.c
create mode 100644 hw/virtio/virtio-snd.c
create mode 100644 include/hw/virtio/virtio-snd.h
Range-diff against v10:
1: 6e7bdf6dda ! 1: 03ecf1f615 Add virtio-sound device stub
@@ hw/virtio/virtio-snd.c (new)
+ return;
+ }
+
-+ AUD_register_card("virtio-sound", &vsnd->card);
++ AUD_register_card("virtio-sound", &vsnd->card, errp);
+
+ vsnd->queues[VIRTIO_SND_VQ_CONTROL] =
+ virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
2: 82138b9c7d ! 2: ba49f45eb3 Add virtio-sound-pci device
@@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const
uint8_t
/*
- ## softmmu/qdev-monitor.c ##
-@@ softmmu/qdev-monitor.c: static const QDevAlias qdev_alias_table[] = {
+ ## system/qdev-monitor.c ##
+@@ system/qdev-monitor.c: static const QDevAlias qdev_alias_table[] = {
{ "virtio-serial-device", "virtio-serial", QEMU_ARCH_VIRTIO_MMIO },
{ "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_VIRTIO_CCW },
{ "virtio-serial-pci", "virtio-serial", QEMU_ARCH_VIRTIO_PCI},
3: c1a2cb0304 ! 3: 5831b5cfa5 virtio-sound: handle control messages and
streams
@@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev,
Error *
@@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState
*dev, Error **errp)
- AUD_register_card("virtio-sound", &vsnd->card);
+ AUD_register_card("virtio-sound", &vsnd->card, errp);
+ /* set default params for all streams */
+ default_params.features = 0;
4: 28b2ecfa1f = 4: 425cbc2986 virtio-sound: handle VIRTIO_SND_R_PCM_INFO
request
5: a52d20b2c3 = 5: d1403721fa virtio-sound: handle
VIRTIO_SND_R_PCM_{START,STOP}
6: 25fbb2eb25 = 6: 68ac43df35 virtio-sound: handle
VIRTIO_SND_R_PCM_SET_PARAMS
7: 9e8d9923ba = 7: 175e6fbe75 virtio-sound: handle VIRTIO_SND_R_PCM_PREPARE
8: b50c94decc = 8: 93a5b96b58 virtio-sound: handle VIRTIO_SND_R_PCM_RELEASE
9: 4cbb908742 ! 9: 34be52e56e virtio-sound: implement audio output (TX)
@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_event(VirtIODevice
*vdev,
+ if (!virtio_queue_ready(vq)) {
+ return;
+ }
-+ trace_virtio_snd_handle_tx_xfer();
++ trace_virtio_snd_handle_xfer();
+
+ for (;;) {
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_event(VirtIODevice
*vdev,
+ }
+
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
-+ size = iov_size(elem->out_sg, elem->out_num);
++ size = iov_size(elem->out_sg, elem->out_num) - msg_sz;
+
+ buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
+ buffer->elem = elem;
-+ buffer->stale = true;
++ buffer->populated = false;
+ buffer->vq = vq;
+ buffer->size = size;
-+ buffer->offset = sizeof(virtio_snd_pcm_xfer);
++ buffer->offset = 0;
+
+ QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry);
+ }
@@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev,
Error *
}
}
++static inline void return_tx_buffer(VirtIOSoundPCMStream *stream,
++ VirtIOSoundPCMBuffer *buffer)
++{
++ virtio_snd_pcm_status resp = { 0 };
++ resp.status = cpu_to_le32(VIRTIO_SND_S_OK);
++ resp.latency_bytes = cpu_to_le32((uint32_t)buffer->size);
++ iov_from_buf(buffer->elem->in_sg,
++ buffer->elem->in_num,
++ 0,
++ &resp,
++ sizeof(virtio_snd_pcm_status));
++ virtqueue_push(buffer->vq,
++ buffer->elem,
++ sizeof(virtio_snd_pcm_status));
++ virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq);
++ QSIMPLEQ_REMOVE(&stream->queue,
++ buffer,
++ VirtIOSoundPCMBuffer,
++ entry);
++ virtio_snd_pcm_buffer_free(buffer);
++}
++
+/*
+ * AUD_* output callback.
+ *
@@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev,
Error *
+ }
+ if (!stream->active) {
+ /* Stream has stopped, so do not perform AUD_write. */
-+ goto return_tx_buffer;
++ return_tx_buffer(stream, buffer);
++ continue;
+ }
-+ if (buffer->stale) {
++ if (!buffer->populated) {
+ iov_to_buf(buffer->elem->out_sg,
+ buffer->elem->out_num,
-+ buffer->offset,
++ sizeof(virtio_snd_pcm_xfer),
+ buffer->data,
+ buffer->size);
-+ buffer->stale = false;
++ buffer->populated = true;
+ }
+ for (;;) {
+ size = AUD_write(stream->voice.out,
@@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev,
Error *
+ buffer->offset += size;
+ available -= size;
+ if (buffer->size < 1) {
-+return_tx_buffer:
-+ virtio_snd_pcm_status resp = { 0 };
-+ resp.status = cpu_to_le32(VIRTIO_SND_S_OK);
-+ resp.latency_bytes = 0;
-+ iov_from_buf(buffer->elem->in_sg,
-+ buffer->elem->in_num,
-+ 0,
-+ &resp,
-+ sizeof(virtio_snd_pcm_status));
-+ virtqueue_push(buffer->vq,
-+ buffer->elem,
-+ sizeof(virtio_snd_pcm_status));
-+ virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq);
-+ QSIMPLEQ_REMOVE_HEAD(&stream->queue, entry);
-+ virtio_snd_pcm_buffer_free(buffer);
++ return_tx_buffer(stream, buffer);
+ break;
+ }
+ if (!available) {
@@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev,
Error *
+ *
+ * @stream: VirtIOSoundPCMStream *stream
+ */
-+static void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream)
++static inline void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream)
+{
-+ VirtIOSoundPCMBuffer *buffer, *next;
++ VirtIOSoundPCMBuffer *buffer;
+
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
-+ QSIMPLEQ_FOREACH_SAFE(buffer, &stream->queue, entry, next) {
-+ AUD_write(stream->voice.out,
-+ buffer->data + buffer->offset,
-+ buffer->size);
-+ virtqueue_push(buffer->vq,
-+ buffer->elem,
-+ sizeof(VirtQueueElement));
-+ virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq);
-+ QSIMPLEQ_REMOVE(&stream->queue,
-+ buffer,
-+ VirtIOSoundPCMBuffer,
-+ entry);
-+ virtio_snd_pcm_buffer_free(buffer);
++ while (!QSIMPLEQ_EMPTY(&stream->queue)) {
++ buffer = QSIMPLEQ_FIRST(&stream->queue);
++ return_tx_buffer(stream, buffer);
+ }
+ }
+}
@@ include/hw/virtio/virtio-snd.h: typedef struct virtio_snd_ctrl_command
virtio_sn
+ *
+ * In the case of TX (i.e. playback) buffers, we defer reading the raw
PCM data
+ * from the virtqueue until QEMU's sound backsystem calls the output
callback.
-+ * This is tracked by the `bool stale;` field, which is set to false when
data
-+ * has been read into our own buffer for consumption.
++ * This is tracked by the `bool populated;` field, which is set to true
when
++ * data has been read into our own buffer for consumption.
+ *
+ * VirtIOSoundPCMBuffer has a dynamic size since it includes the raw PCM
data
+ * in its allocation. It must be initialized and destroyed as follows:
@@ include/hw/virtio/virtio-snd.h: typedef struct virtio_snd_ctrl_command
virtio_sn
+ */
+ uint64_t offset;
+ /* Used for the TX queue for lazy I/O copy from `elem` */
-+ bool stale;
++ bool populated;
+ /*
+ * VirtIOSoundPCMBuffer is an unsized type because it ends with an
array of
+ * bytes. The size of `data` is determined from the I/O message's
read-only
10: 992b0d5ff4 ! 10: 68f9e0f298 virtio-sound: implement audio capture (RX)
@@ hw/virtio/virtio-snd.c
static void virtio_snd_pcm_out_cb(void *data, int available);
static void virtio_snd_process_cmdq(VirtIOSound *s);
--static void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream);
-+static void virtio_snd_pcm_out_flush(VirtIOSoundPCMStream *stream);
-+static void virtio_snd_pcm_in_flush(VirtIOSoundPCMStream *stream);
+ static void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream);
+static void virtio_snd_pcm_in_cb(void *data, int available);
static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8)
| BIT(VIRTIO_SND_PCM_FMT_U8)
-@@ hw/virtio/virtio-snd.c: static void
virtio_snd_pcm_close(VirtIOSoundPCMStream *stream)
+@@ hw/virtio/virtio-snd.c: static void
virtio_snd_get_qemu_audsettings(audsettings *as,
+ static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream)
{
if (stream) {
++ virtio_snd_pcm_flush(stream);
if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
- virtio_snd_pcm_flush(stream);
-+ virtio_snd_pcm_out_flush(stream);
AUD_close_out(&stream->pcm->snd->card, stream->voice.out);
stream->voice.out = NULL;
+ } else if (stream->info.direction == VIRTIO_SND_D_INPUT) {
-+ virtio_snd_pcm_in_flush(stream);
+ AUD_close_in(&stream->pcm->snd->card, stream->voice.in);
+ stream->voice.in = NULL;
}
@@ hw/virtio/virtio-snd.c: static void
virtio_snd_handle_pcm_start_stop(VirtIOSound
}
} else {
error_report("Invalid stream id: %"PRIu32, stream_id);
-@@ hw/virtio/virtio-snd.c: static void
virtio_snd_handle_pcm_release(VirtIOSound *s,
- * are pending I/O messages for the specified stream ID.
- */
- trace_virtio_snd_pcm_stream_flush(stream_id);
-- virtio_snd_pcm_flush(stream);
-+ if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
-+ virtio_snd_pcm_out_flush(stream);
-+ } else {
-+ virtio_snd_pcm_in_flush(stream);
-+ }
- }
+@@ hw/virtio/virtio-snd.c: static void
virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq)
+ trace_virtio_snd_handle_event();
+ }
- cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
++static inline void empty_invalid_queue(VirtIODevice *vdev, VirtQueue *vq)
++{
++ VirtIOSoundPCMBuffer *buffer = NULL;
++ VirtIOSoundPCMStream *stream = NULL;
++ virtio_snd_pcm_status resp = { 0 };
++ VirtIOSound *vsnd = VIRTIO_SND(vdev);
++ bool any = false;
++
++ for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
++ stream = vsnd->pcm->streams[i];
++ if (stream) {
++ any = false;
++ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
++ while (!QSIMPLEQ_EMPTY(&stream->invalid)) {
++ buffer = QSIMPLEQ_FIRST(&stream->invalid);
++ if (buffer->vq != vq) {
++ break;
++ }
++ any = true;
++ resp.status = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
++ iov_from_buf(buffer->elem->in_sg,
++ buffer->elem->in_num,
++ 0,
++ &resp,
++ sizeof(virtio_snd_pcm_status));
++ virtqueue_push(vq,
++ buffer->elem,
++ sizeof(virtio_snd_pcm_status));
++ QSIMPLEQ_REMOVE_HEAD(&stream->invalid, entry);
++ virtio_snd_pcm_buffer_free(buffer);
++ }
++ if (any) {
++ /*
++ * Notify vq about virtio_snd_pcm_status responses.
++ * Buffer responses must be notified separately later.
++ */
++ virtio_notify(vdev, vq);
++ }
++ }
++ }
++ }
++}
++
+ /*
+ * The tx virtqueue handler. Makes the buffers available to their
respective
+ * streams for consumption.
@@ hw/virtio/virtio-snd.c: static void
virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq)
* @vdev: VirtIOSound device
* @vq: tx virtqueue
@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_event(VirtIODevice
*vdev,
{
VirtIOSound *s = VIRTIO_SND(vdev);
VirtIOSoundPCMStream *stream = NULL;
+@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_tx(VirtIODevice
*vdev, VirtQueue *vq)
+ VirtQueueElement *elem;
+ size_t msg_sz, size;
+ virtio_snd_pcm_xfer hdr;
+- virtio_snd_pcm_status resp = { 0 };
+ uint32_t stream_id;
+ /*
+ * If any of the I/O messages are invalid, put them in
stream->invalid and
+@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_tx(VirtIODevice
*vdev, VirtQueue *vq)
+ if (!virtio_queue_ready(vq)) {
+ return;
+ }
+- trace_virtio_snd_handle_xfer();
++ trace_virtio_snd_handle_tx_xfer();
+
+ for (;;) {
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
@@ hw/virtio/virtio-snd.c: tx_err:
+ }
+
+ if (must_empty_invalid_queue) {
+- WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
+- while (!QSIMPLEQ_EMPTY(&stream->invalid)) {
+- buffer = QSIMPLEQ_FIRST(&stream->invalid);
+-
+- resp.status = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+- iov_from_buf(buffer->elem->in_sg,
+- buffer->elem->in_num,
+- 0,
+- &resp,
+- sizeof(virtio_snd_pcm_status));
+- virtqueue_push(vq, buffer->elem,
sizeof(virtio_snd_pcm_status));
+- QSIMPLEQ_REMOVE_HEAD(&stream->invalid, entry);
+- virtio_snd_pcm_buffer_free(buffer);
+- }
+- /*
+- * Notify vq about virtio_snd_pcm_status responses.
+- * Buffer responses must be notified separately later.
+- */
+- virtio_notify(vdev, vq);
+- }
++ empty_invalid_queue(vdev, vq);
+ }
}
/*
@@ hw/virtio/virtio-snd.c: tx_err:
*
* @vdev: VirtIOSound device
- * @vq: virtqueue
-+ * @vq: tx virtqueue
++ * @vq: rx virtqueue
*/
-static void virtio_snd_handle_xfer(VirtIODevice *vdev, VirtQueue *vq) {}
+static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq)
@@ hw/virtio/virtio-snd.c: tx_err:
+ VirtQueueElement *elem;
+ size_t msg_sz, size;
+ virtio_snd_pcm_xfer hdr;
-+ virtio_snd_pcm_status resp = { 0 };
+ uint32_t stream_id;
+ /*
+ * if any of the I/O messages are invalid, put them in
stream->invalid and
@@ hw/virtio/virtio-snd.c: tx_err:
+ }
+
+ if (must_empty_invalid_queue) {
-+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
-+ while (!QSIMPLEQ_EMPTY(&stream->invalid)) {
-+ buffer = QSIMPLEQ_FIRST(&stream->invalid);
-+
-+ resp.status = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
-+ iov_from_buf(buffer->elem->in_sg,
-+ buffer->elem->in_num,
-+ 0,
-+ &resp,
-+ sizeof(virtio_snd_pcm_status));
-+ virtqueue_push(vq, buffer->elem,
sizeof(virtio_snd_pcm_status));
-+ QSIMPLEQ_REMOVE_HEAD(&stream->invalid, entry);
-+ virtio_snd_pcm_buffer_free(buffer);
-+ }
-+ /*
-+ * Notify vq about virtio_snd_pcm_status responses.
-+ * Buffer responses must be notified separately later.
-+ */
-+ virtio_notify(vdev, vq);
-+ }
++ empty_invalid_queue(vdev, vq);
+ }
+}
@@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev,
Error *
qemu_mutex_init(&vsnd->cmdq_mutex);
QTAILQ_INIT(&vsnd->cmdq);
-@@ hw/virtio/virtio-snd.c: return_tx_buffer:
+@@ hw/virtio/virtio-snd.c: static void virtio_snd_pcm_out_cb(void *data,
int available)
}
/*
- * Flush all buffer data from this stream's queue into the driver's
virtual
- * queue.
++ * Flush all buffer data from this input stream's queue into the driver's
++ * virtual queue.
++ *
++ * @stream: VirtIOSoundPCMStream *stream
++ */
++static inline void return_rx_buffer(VirtIOSoundPCMStream *stream,
++ VirtIOSoundPCMBuffer *buffer)
++{
++ virtio_snd_pcm_status resp = { 0 };
++ resp.status = cpu_to_le32(VIRTIO_SND_S_OK);
++ resp.latency_bytes = 0;
++ /* Copy data -if any- to guest */
++ iov_from_buf(buffer->elem->in_sg,
++ buffer->elem->in_num,
++ 0,
++ buffer->data,
++ buffer->size);
++ iov_from_buf(buffer->elem->in_sg,
++ buffer->elem->in_num,
++ buffer->size,
++ &resp,
++ sizeof(virtio_snd_pcm_status));
++ virtqueue_push(buffer->vq,
++ buffer->elem,
++ sizeof(virtio_snd_pcm_status) + buffer->size);
++ virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq);
++ QSIMPLEQ_REMOVE(&stream->queue,
++ buffer,
++ VirtIOSoundPCMBuffer,
++ entry);
++ virtio_snd_pcm_buffer_free(buffer);
++}
++
++
++/*
+ * AUD_* input callback.
- *
-- * @stream: VirtIOSoundPCMStream *stream
++ *
+ * @data: VirtIOSoundPCMStream stream
+ * @available: number of bytes that can be read with AUD_read()
- */
--static void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream)
++ */
+static void virtio_snd_pcm_in_cb(void *data, int available)
- {
-- VirtIOSoundPCMBuffer *buffer, *next;
++{
+ VirtIOSoundPCMStream *stream = data;
+ VirtIOSoundPCMBuffer *buffer;
-+ virtio_snd_pcm_status resp = { 0 };
+ size_t size;
-
- WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
-- QSIMPLEQ_FOREACH_SAFE(buffer, &stream->queue, entry, next) {
-- AUD_write(stream->voice.out,
-- buffer->data + buffer->offset,
-- buffer->size);
-- virtqueue_push(buffer->vq,
-- buffer->elem,
-- sizeof(VirtQueueElement));
-- virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq);
-- QSIMPLEQ_REMOVE(&stream->queue,
-- buffer,
-- VirtIOSoundPCMBuffer,
-- entry);
-- virtio_snd_pcm_buffer_free(buffer);
++
++ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
+ while (!QSIMPLEQ_EMPTY(&stream->queue)) {
+ buffer = QSIMPLEQ_FIRST(&stream->queue);
+ if (!virtio_queue_ready(buffer->vq)) {
@@ hw/virtio/virtio-snd.c: return_tx_buffer:
+ }
+ if (!stream->active) {
+ /* Stream has stopped, so do not perform AUD_read. */
-+ goto return_rx_buffer;
++ return_rx_buffer(stream, buffer);
++ continue;
+ }
+
+ for (;;) {
@@ hw/virtio/virtio-snd.c: return_tx_buffer:
+ buffer->size += size;
+ available -= size;
+ if (buffer->size >= stream->params.period_bytes) {
-+return_rx_buffer:
-+ resp.status = cpu_to_le32(VIRTIO_SND_S_OK);
-+ resp.latency_bytes = 0;
-+ /* Copy data -if any- to guest */
-+ iov_from_buf(buffer->elem->in_sg,
-+ buffer->elem->in_num,
-+ 0,
-+ buffer->data,
-+ buffer->size);
-+ iov_from_buf(buffer->elem->in_sg,
-+ buffer->elem->in_num,
-+ buffer->size,
-+ &resp,
-+ sizeof(resp));
-+ virtqueue_push(buffer->vq,
-+ buffer->elem,
-+ sizeof(virtio_snd_pcm_status) +
-+ buffer->size);
-+ virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq);
-+ QSIMPLEQ_REMOVE_HEAD(&stream->queue, entry);
-+ virtio_snd_pcm_buffer_free(buffer);
++ return_rx_buffer(stream, buffer);
+ break;
+ }
+ if (!available) {
@@ hw/virtio/virtio-snd.c: return_tx_buffer:
+ if (!available) {
+ break;
+ }
- }
- }
- }
-
-+#define virtio_snd_pcm_flush(AUD_CB)
\
-+ VirtIOSoundPCMBuffer *buffer;
\
-+ virtio_snd_pcm_status resp = { 0 };
\
-+ unsigned int len = 0;
\
-+ resp.status = cpu_to_le32(VIRTIO_SND_S_OK);
\
-+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
\
-+ while (!QSIMPLEQ_EMPTY(&stream->queue)) {
\
-+ buffer = QSIMPLEQ_FIRST(&stream->queue);
\
-+ do {
\
-+ AUD_CB;
\
-+ } while (0)
\
-+ ;
\
-+ virtqueue_push(buffer->vq, buffer->elem, len);
\
-+ virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq);
\
-+ QSIMPLEQ_REMOVE_HEAD(&stream->queue, entry);
\
-+ virtio_snd_pcm_buffer_free(buffer);
\
-+ }
\
++ }
+ }
-+
-+
-+/*
-+ * Flush all buffer data from this output stream's queue into the driver's
-+ * virtual queue.
-+ *
-+ * @stream: VirtIOSoundPCMStream *stream
-+ */
-+static void virtio_snd_pcm_out_flush(VirtIOSoundPCMStream *stream)
-+{
-+ /*
-+ * We should flush the buffers as soon as possible, because it is a
-+ * time-sensitive operation.
-+ *
-+ * TODO: find out if copying leftover flushed data to an intermediate
-+ * buffer is a good approach.
-+ */
-+ size_t written;
-+ virtio_snd_pcm_flush(
-+ if (stream->active && buffer->stale) {
-+ iov_to_buf(buffer->elem->out_sg,
-+ buffer->elem->out_num,
-+ buffer->offset,
-+ buffer->data,
-+ buffer->size);
-+ buffer->stale = false;
-+ }
-+ if (stream->active)
-+ while (buffer->size > 0) {
-+ written = AUD_write(stream->voice.out,
-+ buffer->data + buffer->offset,
-+ buffer->size);
-+ if (written < 1) {
-+ break;
-+ }
-+ buffer->size -= written;
-+ buffer->offset += written;
-+ }
-+ len = sizeof(virtio_snd_pcm_status);
-+ iov_from_buf(buffer->elem->in_sg,
-+ buffer->elem->in_num,
-+ 0,
-+ &resp,
-+ sizeof(virtio_snd_pcm_status));
-+ );
+}
+
+/*
-+ * Flush all buffer data from this input stream's queue into the driver's
++ * Flush all buffer data from this output stream's queue into the driver's
+ * virtual queue.
-+ *
-+ * @stream: VirtIOSoundPCMStream *stream
-+ */
-+static void virtio_snd_pcm_in_flush(VirtIOSoundPCMStream *stream)
-+{
-+ virtio_snd_pcm_flush(
-+ len = sizeof(virtio_snd_pcm_status) + buffer->size;
-+ iov_from_buf(buffer->elem->in_sg,
-+ buffer->elem->in_num,
-+ 0,
-+ buffer->data,
-+ buffer->size);
-+ iov_from_buf(buffer->elem->in_sg,
-+ buffer->elem->in_num,
-+ buffer->size,
-+ &resp,
-+ sizeof(virtio_snd_pcm_status));
-+ );
-+}
-+
- static void virtio_snd_unrealize(DeviceState *dev)
+ *
+ * @stream: VirtIOSoundPCMStream *stream
+ */
+ static inline void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream)
{
- VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOSoundPCMBuffer *buffer;
++ void (*cb)(VirtIOSoundPCMStream *, VirtIOSoundPCMBuffer *) =
++ (stream->info.direction == VIRTIO_SND_D_OUTPUT) ?
return_tx_buffer :
++ return_rx_buffer;
+
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
+ while (!QSIMPLEQ_EMPTY(&stream->queue)) {
+ buffer = QSIMPLEQ_FIRST(&stream->queue);
+- return_tx_buffer(stream, buffer);
++ cb(stream, buffer);
+ }
+ }
+ }
11: b720e00121 = 11: 885b01fe27 docs/system: add basic virtio-snd
documentation
base-commit: cea3ea670fe265421131aad90c36fbb87bc4d206
--
2.39.2
- [PATCH v11 00/11] Add VIRTIO sound card,
Manos Pitsidianakis <=
- [PATCH v11 01/11] Add virtio-sound device stub, Manos Pitsidianakis, 2023/10/11
- [PATCH v11 03/11] virtio-sound: handle control messages and streams, Manos Pitsidianakis, 2023/10/11
- [PATCH v11 05/11] virtio-sound: handle VIRTIO_SND_R_PCM_{START,STOP}, Manos Pitsidianakis, 2023/10/11
- [PATCH v11 06/11] virtio-sound: handle VIRTIO_SND_R_PCM_SET_PARAMS, Manos Pitsidianakis, 2023/10/11
- [PATCH v11 02/11] Add virtio-sound-pci device, Manos Pitsidianakis, 2023/10/11
- [PATCH v11 08/11] virtio-sound: handle VIRTIO_SND_R_PCM_RELEASE, Manos Pitsidianakis, 2023/10/11
- [PATCH v11 04/11] virtio-sound: handle VIRTIO_SND_R_PCM_INFO request, Manos Pitsidianakis, 2023/10/11
- [PATCH v11 07/11] virtio-sound: handle VIRTIO_SND_R_PCM_PREPARE, Manos Pitsidianakis, 2023/10/11
- [PATCH v11 09/11] virtio-sound: implement audio output (TX), Manos Pitsidianakis, 2023/10/11
- [PATCH v11 10/11] virtio-sound: implement audio capture (RX), Manos Pitsidianakis, 2023/10/11