qemu-devel
[Top][All Lists]
Advanced

[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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]