mbox series

[v4,00/12] Add VIRTIO sound card

Message ID cover.1689857559.git.manos.pitsidianakis@linaro.org (mailing list archive)
Headers show
Series Add VIRTIO sound card | expand

Message

Manos Pitsidianakis July 20, 2023, 12:57 p.m. UTC
This patch series adds an audio device implementing the recent virtio 
sound spec (1.2) and a corresponding PCI wrapper device.

Main differences with v3 patch [^v3]
<cover.1689692765.git.manos.pitsidianakis@linaro.org>:
- Addressed review style comments.
- Split patches for easier review.

[^v3]: 
https://lore.kernel.org/qemu-devel/cover.1689692765.git.manos.pitsidianakis@linaro.org/

Emmanouil Pitsidianakis (12):
  Add virtio-sound device stub
  Add virtio-sound-pci device
  virtio-sound: handle control messages and streams
  virtio-sound: set PCM stream parameters
  virtio-sound: prepare PCM streams
  virtio-sound: handle VIRTIO_SND_R_PCM_INFO request
  virtio-sound: handle VIRTIO_SND_R_PCM_{START,STOP}
  virtio-sound: handle VIRTIO_SND_PCM_SET_PARAMS
  virtio-sound: handle VIRTIO_SND_R_PCM_PREPARE
  virtio-sound: handle VIRTIO_SND_PCM_RELEASE
  virtio-sound: implement audio output (TX)
  virtio-sound: implement audio capture (RX)

 MAINTAINERS                    |    6 +
 hw/virtio/Kconfig              |    5 +
 hw/virtio/meson.build          |    2 +
 hw/virtio/trace-events         |   20 +
 hw/virtio/virtio-snd-pci.c     |   91 +++
 hw/virtio/virtio-snd.c         | 1298 ++++++++++++++++++++++++++++++++
 include/hw/pci/pci.h           |    1 +
 include/hw/virtio/virtio-snd.h |  157 ++++
 softmmu/qdev-monitor.c         |    1 +
 9 files changed, 1581 insertions(+)
 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 v3:
 1:  93c763e275 <  -:  ---------- Add virtio-sound device
 -:  ---------- >  1:  ae372de565 Add virtio-sound device stub
 2:  36373d92fa !  2:  5cde5472ea Add virtio-sound-pci device
    @@
      ## Metadata ##
    -Author: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
    +Author: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
     
      ## Commit message ##
         Add virtio-sound-pci device
     
         This patch adds a PCI wrapper device for the virtio-sound device.
    +    It is necessary to instantiate a virtio-snd device in a guest.
    +    All sound logic will be added to the virtio-snd device in the following
    +    commits.
     
    -    To test this, you'll need a >=5.13 kernel compiled with
    -    CONFIG_SND_VIRTIO=y, which at the time of writing most distros have off
    -    by default.
    +    To add this device with a guest, you'll need a >=5.13 kernel compiled
    +    with CONFIG_SND_VIRTIO=y, which at the time of writing most distros have
    +    off by default.
     
         Use with following flags in the invocation:
     
    @@ Commit message
     
         Pulseaudio:
           -audio driver=pa,model=virtio-sound
    +      or
    +      -audio driver=pa,model=virtio-sound,server=/run/user/1000/pulse/native
         sdl:
           -audio driver=sdl,model=virtio-sound
         coreaudio (macos/darwin):
           -audio driver=coreaudio,model=virtio-sound
         etc.
     
    -    You can use speaker-test from alsa-tools to play noise, sines, or
    -    WAV files.
    -
         Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
     
      ## hw/virtio/meson.build ##
    @@ hw/virtio/virtio-snd-pci.c (new)
     +{
     +    VirtIOSoundPCI *dev = VIRTIO_SOUND_PCI(vpci_dev);
     +    DeviceState *vdev = DEVICE(&dev->vdev);
    -+    VirtIOSound *vsnd = VIRTIO_SND(&dev->vdev);
    -+
    -+    /*
    -+     * According to spec, non-legacy virtio PCI devices are always little
    -+     * endian
    -+     */
    -+    vsnd->virtio_access_is_big_endian = false;
    -+
     +
     +    qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus), errp);
    -+
     +    qdev_prop_set_string(vdev, "audiodev", audiodev_id);
    -+
     +    object_property_set_bool(OBJECT(vdev), "realized", true, errp);
     +}
     +
 -:  ---------- >  3:  dc65cac2f4 virtio-sound: handle control messages and streams
 -:  ---------- >  4:  bbd1799fc9 virtio-sound: set PCM stream parameters
 -:  ---------- >  5:  68a13c4385 virtio-sound: prepare PCM streams
 -:  ---------- >  6:  f267d41957 virtio-sound: handle VIRTIO_SND_R_PCM_INFO request
 -:  ---------- >  7:  5939a6161e virtio-sound: handle VIRTIO_SND_R_PCM_{START,STOP}
 -:  ---------- >  8:  8f78d3a132 virtio-sound: handle VIRTIO_SND_PCM_SET_PARAMS
 -:  ---------- >  9:  d2b3854084 virtio-sound: handle VIRTIO_SND_R_PCM_PREPARE
 -:  ---------- > 10:  4e14b2d129 virtio-sound: handle VIRTIO_SND_PCM_RELEASE
 -:  ---------- > 11:  a6a0f07c7a virtio-sound: implement audio output (TX)
 3:  8d432c85a2 ! 12:  1abb69dd05 Implement audio capture in virtio-snd device
    @@
      ## Metadata ##
    -Author: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
    +Author: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
     
      ## Commit message ##
    -    Implement audio capture in virtio-snd device
    +    virtio-sound: implement audio capture (RX)
    +
    +    To perform audio capture we duplicate the TX logic of the previous
    +    commit with the following difference: we receive data from the QEMU
    +    audio backend and write it in the virt queue IO buffers the guest sends
    +    to QEMU. When they are full (i.e. they have `period_bytes` amount of
    +    data) or when recording stops in QEMU's audio backend, the buffer is
    +    returned to the guest by notifying it.
     
         Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
     
      ## hw/virtio/trace-events ##
    -@@ hw/virtio/trace-events: virtio_snd_handle_pcm_set_params(int stream) "VIRTIO_SND_PCM_SET_PARAMS called f
    - virtio_snd_handle_pcm_start_stop(const char *code, int stream) "%s called for stream %d"
    - virtio_snd_handle_pcm_release(int stream) "VIRTIO_SND_PCM_RELEASE called for stream %d"
    +@@ hw/virtio/trace-events: virtio_snd_handle_code(uint32_t val, const char *code) "ctrl code msg val = %"PR
      virtio_snd_handle_chmap_info(void) "VIRTIO_SND_CHMAP_INFO called"
    + virtio_snd_handle_event(void) "event queue callback called"
    + virtio_snd_pcm_stream_flush(uint32_t stream) "flushing stream %"PRIu32
     -virtio_snd_handle_xfer(void) "tx/rx queue callback called"
     +virtio_snd_handle_tx_xfer(void) "tx queue callback called"
    -+virtio_snd_handle_rx_xfer(void) "rx queue callback called"
    - virtio_snd_handle_event(void) "event queue callback called"
    - virtio_snd_realize(void *snd) "snd %p: realize"
    - virtio_snd_unrealize(void *snd) "snd %p: unrealize"
    ++virtio_snd_handle_rx_xfer(void) "tx queue callback called"
     
      ## hw/virtio/virtio-snd.c ##
     @@
    - #define VIRTIO_SOUND_VM_VERSION 1
      
    + #define VIRTIO_SOUND_VM_VERSION 1
      #define VIRTIO_SOUND_JACK_DEFAULT 0
     -#define VIRTIO_SOUND_STREAM_DEFAULT 1
     +#define VIRTIO_SOUND_STREAM_DEFAULT 2
      #define VIRTIO_SOUND_CHMAP_DEFAULT 0
    - 
      #define VIRTIO_SOUND_HDA_FN_NID 0
    -@@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
    - }
      
    + 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 uint32_t
    +-virtio_snd_pcm_read_write(VirtIOSoundPCMStream *stream,
    +-                          VirtQueue *vq,
    +-                          VirtQueueElement *element,
    +-                          bool read);
     +static void virtio_snd_pcm_out_flush(VirtIOSoundPCMStream *stream);
     +static void virtio_snd_pcm_in_flush(VirtIOSoundPCMStream *stream);
    - static void virtio_snd_pcm_out_cb(void *data, int available);
    --static uint32_t virtio_snd_pcm_read_write(VirtIOSoundPCMStream *stream,
     +static void virtio_snd_pcm_in_cb(void *data, int available);
     +static uint32_t virtio_snd_pcm_write(VirtIOSoundPCMStream *stream,
    -                                           VirtQueue *vq,
    --                                          VirtQueueElement *element,
    --                                          bool read);
    -+                                          VirtQueueElement *element);
    ++                                     VirtQueue *vq,
    ++                                     VirtQueueElement *element);
     +static uint32_t virtio_snd_pcm_read(VirtIOSoundPCMStream *stream,
    -+                                          VirtQueue *vq,
    -+                                          VirtQueueElement *element);
    ++                                    VirtQueue *vq,
    ++                                    VirtQueueElement *element);
      
    - /*
    -  * Get a specific stream from the virtio sound card device.
    + static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8)
    +                                   | BIT(VIRTIO_SND_PCM_FMT_U8)
     @@ hw/virtio/virtio-snd.c: static uint32_t virtio_snd_pcm_prepare_impl(VirtIOSound *s, uint32_t stream_id)
    +                                          virtio_snd_pcm_out_cb,
                                               &as);
    - 
          } else {
     -        qemu_log_mask(LOG_UNIMP, "virtio_snd: input/capture is unimplemented.");
    --        /*
    --         * stream->voice.in = AUD_open_in(&s->card,
    --         *                                stream->voice.in,
    --         *                                "virtio_snd_card",
    --         *                                stream,
    --         *                                virtio_snd_input_cb,
    --         *                                &as);
    --         */
     +        stream->voice.in = AUD_open_in(&s->card,
     +                                        stream->voice.in,
     +                                        "virtio_snd_card",
    @@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_start_stop(VirtIOSound
     +            AUD_set_active_in(stream->voice.in, start);
              }
          } else {
    -         error_report("Invalid stream id: %d", req.stream_id);
    +         error_report("Invalid stream id: %"PRIu32, req.stream_id);
     @@ hw/virtio/virtio-snd.c: static uint32_t virtio_snd_pcm_release_impl(VirtIOSoundPCMStream *stream,
               */
              virtio_snd_process_cmdq(stream->s);
    @@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_event(VirtIODevice *vdev,
       * @vdev: VirtIOSound device
       * @vq: tx virtqueue
       */
    --static void virtio_snd_handle_xfer(VirtIODevice *vdev, VirtQueue *vq)
    +-static void virtio_snd_handle_tx(VirtIODevice *vdev, VirtQueue *vq)
     +static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq)
      {
          VirtIOSound *s = VIRTIO_SND(vdev);
          VirtIOSoundPCMStream *stream = NULL;
    -@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_xfer(VirtIODevice *vdev, VirtQueue *vq)
    +@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_tx(VirtIODevice *vdev, VirtQueue *vq)
          virtio_snd_pcm_xfer hdr;
          virtio_snd_pcm_status resp = { 0 };
      
    @@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_xfer(VirtIODevice *vdev, V
      
          for (;;) {
              elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
    -@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_xfer(VirtIODevice *vdev, VirtQueue *vq)
    +@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_tx(VirtIODevice *vdev, VirtQueue *vq)
                  goto tx_err;
              }
      
    @@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_xfer(VirtIODevice *vdev, V
                  resp.status = VIRTIO_SND_S_OK;
                  iov_from_buf(elem->in_sg,
     @@ hw/virtio/virtio-snd.c: tx_err:
    -     virtio_notify(VIRTIO_DEVICE(s), vq);
      }
      
    -+/*
    + /*
    +- * Stub buffer virtqueue handler.
     + * The rx virtqueue handler. Makes the buffers available to their respective
     + * streams for consumption.
    -+ *
    -+ * @vdev: VirtIOSound device
    +  *
    +  * @vdev: VirtIOSound device
    +- * @vq: virtqueue
     + * @vq: tx virtqueue
    -+ */
    +  */
    +-static void virtio_snd_handle_xfer(VirtIODevice *vdev, VirtQueue *vq) {}
     +static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq)
     +{
     +    VirtIOSound *s = VIRTIO_SND(vdev);
    @@ hw/virtio/virtio-snd.c: tx_err:
     +     */
     +    virtio_notify(VIRTIO_DEVICE(s), vq);
     +}
    -+
    -+
    + 
      static uint64_t get_features(VirtIODevice *vdev, uint64_t features,
                                   Error **errp)
    - {
     @@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev, Error **errp)
          virtio_snd_common_realize(dev,
                                    virtio_snd_handle_ctrl,
                                    virtio_snd_handle_event,
    --                              virtio_snd_handle_xfer,
    +-                              virtio_snd_handle_tx,
     -                              virtio_snd_handle_xfer,
     +                              virtio_snd_handle_tx_xfer,
     +                              virtio_snd_handle_rx_xfer,
    @@ hw/virtio/virtio-snd.c: static void virtio_snd_pcm_out_cb(void *data, int availa
     -    VirtIOSoundPCMBlock *next;
     +    uint32_t sz;
     +    virtio_snd_pcm_status resp = { 0 };
    -+    int size;
    ++    size_t size;
      
          WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
     -        QSIMPLEQ_FOREACH_SAFE(block, &stream->queue, entry, next) {
    @@ hw/virtio/virtio-snd.c: static void virtio_snd_pcm_out_cb(void *data, int availa
     +                        MIN(stream->period_bytes - block->offset, available));
     +                block->offset += size;
     +                block->size += size;
    -+                if (size == 0 || block->size == stream->period_bytes) {
    ++                if (size == 0 || block->size >= stream->period_bytes) {
     +                    resp.status = VIRTIO_SND_S_OK;
     +                     sz = iov_from_buf(block->elem->in_sg,
     +                                  block->elem->in_num,
    @@ hw/virtio/virtio-snd.c: static void virtio_snd_pcm_out_cb(void *data, int availa
     +                                     block->elem->in_num,
     +                                     sz,
     +                                     &block->data,
    -+                                     block->size);
    ++                                     MIN(stream->period_bytes, block->size));
     +                    }
     +                    virtqueue_push(block->vq,
     +                            block->elem,

base-commit: 2c27fdc7a626408ee2cf30d791aa0b63027c7404