Message ID | cover.1686238728.git.manos.pitsidianakis@linaro.org (mailing list archive) |
---|---|
Headers | show |
Series | Add VIRTIO sound card | expand |
Ping Patch series on patchew: https://patchew.org/QEMU/cover.1686238728.git.manos.pitsidianakis@linaro.org/ Patch series on lore: https://lore.kernel.org/qemu-devel/cover.1686238728.git.manos.pitsidianakis@linaro.org/ On Thu, 08 Jun 2023 18:56, Manos Pitsidianakis <manos.pitsidianakis@linaro.org> wrote: >This patch series adds an audio device implementing the recent virtio >sound spec (1.2) and a corresponding PCI wrapper device. > >Main differences with v1 patch ><20230526204845.673031-1-manos.pitsidianakis@linaro.org>: > >- Split virtio-snd and virtio-snd-pci devices to two commits >- Added audio capture support > >Known problems (On pipewire at least): > >- Stereo recording results in one channel with the recording + a ticking > kind of artifact and one channel that has no artifacts, just the > recording. > >Manos Pitsidianakis (3): > Add virtio-sound device > Add virtio-sound-pci device > Implement audio capture in virtio-snd device > > MAINTAINERS | 6 + > hw/virtio/Kconfig | 5 + > hw/virtio/meson.build | 2 + > hw/virtio/trace-events | 22 + > hw/virtio/virtio-snd-pci.c | 102 +++ > hw/virtio/virtio-snd.c | 1290 ++++++++++++++++++++++++++++++++ > include/hw/pci/pci.h | 1 + > include/hw/virtio/virtio-snd.h | 194 +++++ > softmmu/qdev-monitor.c | 1 + > 9 files changed, 1623 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 v1: >1: 652c7d2d01 ! 1: 95d564fc1f Add virtio-sound and virtio-sound-pci devices > @@ Metadata > Author: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> > > ## Commit message ## > - Add virtio-sound and virtio-sound-pci devices > + Add virtio-sound device > > This patch adds an audio device implementing the recent virtio sound > - spec (1.2) and a corresponding PCI wrapper device. > + spec (1.2). > > PCM functionality is implemented, and jack[0], chmaps[1] messages are > - at the moment ignored. > - > - To test this, you'll need a >6.0 kernel compiled with the virtio-snd > - flag enabled, which distros have off by default. > - > - Use with following flags in the invocation: > - > - -device virtio-sound-pci,disable-legacy=on > - > - And an audio backend listed with `-audio driver=help` that works on > - your host machine, e.g.: > - > - Pulseaudio: > - -audio driver=pa,model=virtio-sound,server=/run/user/1000/pulse/native > - sdl: > - -audio driver=sdl,model=virtio-sound > - coreaudio: > - -audio driver=coreaudio,model=virtio-sound > - etc. > - > - You can use speaker-test from alsa-tools to play noise, sines, or > - WAV files. > + at the moment left unimplemented. > > PS2: This patch was based on a draft patch posted by OpenSynergy in 2019. [2] > > @@ Commit message > Signed-off-by: Igor Skalkin <Igor.Skalkin@opensynergy.com> > Signed-off-by: Anton Yakovlev <Anton.Yakovlev@opensynergy.com> > > + ## MAINTAINERS ## > +@@ MAINTAINERS: F: hw/virtio/virtio-mem-pci.h > + F: hw/virtio/virtio-mem-pci.c > + F: include/hw/virtio/virtio-mem.h > + > ++virtio-snd > ++M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> > ++S: Supported > ++F: hw/virtio/virtio-snd*.c > ++F: include/hw/virtio/virtio-snd.h > ++ > + nvme > + M: Keith Busch <kbusch@kernel.org> > + M: Klaus Jensen <its@irrelevant.dk> > + > ## hw/virtio/Kconfig ## > @@ hw/virtio/Kconfig: config VIRTIO_PCI > depends on PCI > @@ hw/virtio/Kconfig: config VIRTIO_PCI > select VIRTIO > > ## hw/virtio/meson.build ## > -@@ hw/virtio/meson.build: virtio_pci_ss.add(when: 'CONFIG_VIRTIO_SERIAL', if_true: files('virtio-serial-pc > - virtio_pci_ss.add(when: 'CONFIG_VIRTIO_PMEM', if_true: files('virtio-pmem-pci.c')) > - virtio_pci_ss.add(when: 'CONFIG_VIRTIO_IOMMU', if_true: files('virtio-iommu-pci.c')) > - virtio_pci_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem-pci.c')) > -+virtio_pci_ss.add(when: 'CONFIG_VIRTIO_SND', if_true: files('virtio-snd-pci.c')) > - virtio_pci_ss.add(when: 'CONFIG_VHOST_VDPA_DEV', if_true: files('vdpa-dev-pci.c')) > - > - specific_virtio_ss.add_all(when: 'CONFIG_VIRTIO_PCI', if_true: virtio_pci_ss) > @@ hw/virtio/meson.build: softmmu_ss.add(when: 'CONFIG_VIRTIO', if_false: files('virtio-stub.c')) > softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-stub.c')) > softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('virtio-stub.c')) > @@ hw/virtio/trace-events: virtio_pmem_flush_done(int type) "fsync return=%d" > +virtio_snd_handle_pcm_info(int stream) "VIRTIO_SND_R_PCM_INFO called for stream %d" > +virtio_snd_handle_pcm_set_params(int stream) "VIRTIO_SND_PCM_SET_PARAMS called for stream %d" > +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 %id" > ++virtio_snd_handle_pcm_release(int stream) "VIRTIO_SND_PCM_RELEASE called for stream %d" > +virtio_snd_handle_chmap_info(void) "VIRTIO_SND_CHMAP_INFO called" > +virtio_snd_handle_xfer(void) "tx/rx queue callback called" > -+virtio_snd_handle_xfer_elem(const char * k) "xfer handled in virtio_snd_pcm_%s" > +virtio_snd_handle_event(void) "event queue callback called" > -+virtio_snd_cpu_is_stopped(void *snd, int size) "snd %p: cpu is stopped, dropping %d bytes" > +virtio_snd_realize(void *snd) "snd %p: realize" > -+virtio_snd_unrealize(void *snd) "snd %p: realize" > ++virtio_snd_unrealize(void *snd) "snd %p: unrealize" > +virtio_snd_get_features(void *vdev, uint64_t features) "snd %p: get_features 0x%"PRIx64 > +virtio_snd_get_config(void *vdev, uint32_t jacks, uint32_t streams, uint32_t chmaps) "snd %p: get_config jacks=%d streams=%d chmaps=%d" > +virtio_snd_set_config(void *vdev, uint32_t jacks, uint32_t new_jacks, uint32_t streams, uint32_t new_streams, uint32_t chmaps, uint32_t new_chmaps) "snd %p: set_config jacks from %d->%d, streams from %d->%d, chmaps from %d->%d" > @@ hw/virtio/trace-events: virtio_pmem_flush_done(int type) "fsync return=%d" > +virtio_snd_vm_state_stopped(void) "vm state stopped" > +virtio_snd_handle_code(int val, const char *code) "ctrl code msg val = %d == %s" > > - ## hw/virtio/virtio-snd-pci.c (new) ## > -@@ > -+/* > -+ * VIRTIO Sound Device PCI Bindings > -+ * > -+ * Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org> > -+ * > -+ * This work is licensed under the terms of the GNU GPL, version 2 or > -+ * (at your option) any later version. See the COPYING file in the > -+ * top-level directory. > -+ */ > -+ > -+#include "qemu/osdep.h" > -+#include "hw/audio/soundhw.h" > -+#include "hw/virtio/virtio-pci.h" > -+#include "hw/virtio/virtio-snd.h" > -+ > -+typedef struct VirtIOSoundPCI VirtIOSoundPCI; > -+ > -+/* > -+ * virtio-snd-pci: This extends VirtioPCIProxy. > -+ */ > -+#define TYPE_VIRTIO_SND_PCI "virtio-sound-pci-base" > -+DECLARE_INSTANCE_CHECKER(VirtIOSoundPCI, VIRTIO_SOUND_PCI, > -+ TYPE_VIRTIO_SND_PCI) > -+ > -+struct VirtIOSoundPCI { > -+ VirtIOPCIProxy parent; > -+ VirtIOSound vdev; > -+}; > -+ > -+static Property virtio_snd_pci_properties[] = { > -+ DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), > -+ DEFINE_PROP_END_OF_LIST(), > -+}; > -+ > -+static const char *audiodev_id; > -+ > -+static int virtio_snd_init_pci(PCIBus *init_bus, const char *audiodev) > -+{ > -+ audiodev_id = audiodev; > -+ return 0; > -+} > -+ > -+static void virtio_snd_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) > -+{ > -+ 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); > -+ AUD_register_card ("virtio-sound", &dev->vdev.card); > -+ > -+ object_property_set_bool(OBJECT(vdev), "realized", true, errp); > -+} > -+ > -+static void virtio_snd_pci_class_init(ObjectClass *klass, void *data) > -+{ > -+ DeviceClass *dc = DEVICE_CLASS(klass); > -+ VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass); > -+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); > -+ > -+ vpciklass->realize = virtio_snd_pci_realize; > -+ set_bit(DEVICE_CATEGORY_SOUND, dc->categories); > -+ > -+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; > -+ pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SND; > -+ pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; > -+ pcidev_k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO; > -+ device_class_set_props(dc, virtio_snd_pci_properties); > -+} > -+ > -+static void virtio_snd_pci_instance_init(Object *obj) > -+{ > -+ VirtIOSoundPCI *dev = VIRTIO_SOUND_PCI(obj); > -+ > -+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), > -+ TYPE_VIRTIO_SND); > -+} > -+ > -+static const VirtioPCIDeviceTypeInfo virtio_snd_pci_info = { > -+ .base_name = TYPE_VIRTIO_SND_PCI, > -+ .generic_name = "virtio-sound-pci", > -+ .instance_size = sizeof(VirtIOSoundPCI), > -+ .instance_init = virtio_snd_pci_instance_init, > -+ .class_init = virtio_snd_pci_class_init, > -+}; > -+ > -+static void virtio_snd_pci_register(void) > -+{ > -+ virtio_pci_types_register(&virtio_snd_pci_info); > -+ pci_register_soundhw("virtio-sound", "Virtio Sound Device", > -+ virtio_snd_init_pci); > -+} > -+ > -+type_init(virtio_snd_pci_register); > - > ## hw/virtio/virtio-snd.c (new) ## > @@ > +/* > @@ hw/virtio/virtio-snd.c (new) > +#define VIRTIO_SOUND_STREAM_DEFAULT 1 > +#define VIRTIO_SOUND_CHMAP_DEFAULT 0 > + > -+#define VIRTIO_SOUND_HDA_FN_NID_OUT 0 > -+#define VIRTIO_SOUND_HDA_FN_NID_IN 1 > ++#define VIRTIO_SOUND_HDA_FN_NID 0 > + > +static const VMStateDescription vmstate_virtio_snd_device = { > + .name = TYPE_VIRTIO_SND, > @@ hw/virtio/virtio-snd.c (new) > +uint32_t virtio_snd_pcm_set_params_impl(VirtIOSound *s, > + virtio_snd_pcm_set_params *params) > +{ > ++ uint32_t supported_formats, supported_rates; > ++ VirtIOSoundPCMParams *st_params; > + uint32_t stream_id = params->hdr.stream_id; > ++ > + if (stream_id > s->snd_conf.streams || !(s->pcm->pcm_params)) { > + virtio_error(VIRTIO_DEVICE(s), "Streams have not been initialized.\n"); > + return VIRTIO_SND_S_BAD_MSG; > @@ hw/virtio/virtio-snd.c (new) > + if (!s->pcm->pcm_params[stream_id]) { > + s->pcm->pcm_params[stream_id] = g_new0(VirtIOSoundPCMParams, 1); > + } > -+ VirtIOSoundPCMParams *st_params = virtio_snd_pcm_get_params(s, stream_id); > ++ st_params = virtio_snd_pcm_get_params(s, stream_id); > + > + st_params->features = params->features; > + st_params->buffer_bytes = params->buffer_bytes; > @@ hw/virtio/virtio-snd.c (new) > + } > + st_params->channels = params->channels; > + > -+ uint32_t supported_formats = 1 << VIRTIO_SND_PCM_FMT_S8 > -+ | 1 << VIRTIO_SND_PCM_FMT_U8 > -+ | 1 << VIRTIO_SND_PCM_FMT_S16 > -+ | 1 << VIRTIO_SND_PCM_FMT_U16 > -+ | 1 << VIRTIO_SND_PCM_FMT_S32 > -+ | 1 << VIRTIO_SND_PCM_FMT_U32 > -+ | 1 << VIRTIO_SND_PCM_FMT_FLOAT; > -+ > -+ uint32_t supported_rates = 1 << VIRTIO_SND_PCM_RATE_5512 > -+ | 1 << VIRTIO_SND_PCM_RATE_8000 > -+ | 1 << VIRTIO_SND_PCM_RATE_11025 > -+ | 1 << VIRTIO_SND_PCM_RATE_16000 > -+ | 1 << VIRTIO_SND_PCM_RATE_22050 > -+ | 1 << VIRTIO_SND_PCM_RATE_32000 > -+ | 1 << VIRTIO_SND_PCM_RATE_44100 > -+ | 1 << VIRTIO_SND_PCM_RATE_48000 > -+ | 1 << VIRTIO_SND_PCM_RATE_64000 > -+ | 1 << VIRTIO_SND_PCM_RATE_88200 > -+ | 1 << VIRTIO_SND_PCM_RATE_96000 > -+ | 1 << VIRTIO_SND_PCM_RATE_176400 > -+ | 1 << VIRTIO_SND_PCM_RATE_192000 > -+ | 1 << VIRTIO_SND_PCM_RATE_384000; > ++ supported_formats = 1 << VIRTIO_SND_PCM_FMT_S8 > ++ | 1 << VIRTIO_SND_PCM_FMT_U8 > ++ | 1 << VIRTIO_SND_PCM_FMT_S16 > ++ | 1 << VIRTIO_SND_PCM_FMT_U16 > ++ | 1 << VIRTIO_SND_PCM_FMT_S32 > ++ | 1 << VIRTIO_SND_PCM_FMT_U32 > ++ | 1 << VIRTIO_SND_PCM_FMT_FLOAT; > ++ > ++ supported_rates = 1 << VIRTIO_SND_PCM_RATE_5512 > ++ | 1 << VIRTIO_SND_PCM_RATE_8000 > ++ | 1 << VIRTIO_SND_PCM_RATE_11025 > ++ | 1 << VIRTIO_SND_PCM_RATE_16000 > ++ | 1 << VIRTIO_SND_PCM_RATE_22050 > ++ | 1 << VIRTIO_SND_PCM_RATE_32000 > ++ | 1 << VIRTIO_SND_PCM_RATE_44100 > ++ | 1 << VIRTIO_SND_PCM_RATE_48000 > ++ | 1 << VIRTIO_SND_PCM_RATE_64000 > ++ | 1 << VIRTIO_SND_PCM_RATE_88200 > ++ | 1 << VIRTIO_SND_PCM_RATE_96000 > ++ | 1 << VIRTIO_SND_PCM_RATE_176400 > ++ | 1 << VIRTIO_SND_PCM_RATE_192000 > ++ | 1 << VIRTIO_SND_PCM_RATE_384000; > + > + if (!(supported_formats & (1 << params->format))) { > + error_report("Stream format is not supported."); > @@ hw/virtio/virtio-snd.c (new) > + st_params->rate = params->rate; > + st_params->period_bytes = params->period_bytes; > + st_params->buffer_bytes = params->buffer_bytes; > ++ > + return VIRTIO_SND_S_OK; > +} > + > @@ hw/virtio/virtio-snd.c (new) > +{ > + virtio_snd_pcm_set_params req; > + uint32_t sz; > ++ > + sz = iov_to_buf(cmd->elem->out_sg, > + cmd->elem->out_num, > + 0, > @@ hw/virtio/virtio-snd.c (new) > + > + trace_virtio_snd_handle_pcm_set_params(req.hdr.stream_id); > + cmd->resp.code = virtio_snd_pcm_set_params_impl(s, &req); > ++ > + return; > +} > + > @@ hw/virtio/virtio-snd.c (new) > +static void virtio_snd_get_qemu_audsettings(audsettings *as, > + VirtIOSoundPCMParams *params) > +{ > -+ as->nchannels = params->channels; > ++ as->nchannels = MIN(AUDIO_MAX_CHANNELS, params->channels); > + as->fmt = virtio_snd_get_qemu_format(params->format); > + as->freq = virtio_snd_get_qemu_freq(params->rate); > + as->endianness = AUDIO_HOST_ENDIANNESS; > @@ hw/virtio/virtio-snd.c (new) > + */ > +static uint32_t virtio_snd_pcm_prepare_impl(VirtIOSound *s, uint32_t stream_id) > +{ > ++ audsettings as; > ++ uint32_t supported_formats, supported_rates; > ++ VirtIOSoundPCMParams *params; > ++ VirtIOSoundPCMStream *stream; > ++ > + if (!s->pcm->streams > + || !s->pcm->pcm_params > + || !s->pcm->pcm_params[stream_id]) { > + return VIRTIO_SND_S_BAD_MSG; > + } > + > -+ uint32_t supported_formats = 1 << VIRTIO_SND_PCM_FMT_S8 > -+ | 1 << VIRTIO_SND_PCM_FMT_U8 > -+ | 1 << VIRTIO_SND_PCM_FMT_S16 > -+ | 1 << VIRTIO_SND_PCM_FMT_U16 > -+ | 1 << VIRTIO_SND_PCM_FMT_S32 > -+ | 1 << VIRTIO_SND_PCM_FMT_U32 > -+ | 1 << VIRTIO_SND_PCM_FMT_FLOAT; > -+ > -+ uint32_t supported_rates = 1 << VIRTIO_SND_PCM_RATE_5512 > -+ | 1 << VIRTIO_SND_PCM_RATE_8000 > -+ | 1 << VIRTIO_SND_PCM_RATE_11025 > -+ | 1 << VIRTIO_SND_PCM_RATE_16000 > -+ | 1 << VIRTIO_SND_PCM_RATE_22050 > -+ | 1 << VIRTIO_SND_PCM_RATE_32000 > -+ | 1 << VIRTIO_SND_PCM_RATE_44100 > -+ | 1 << VIRTIO_SND_PCM_RATE_48000 > -+ | 1 << VIRTIO_SND_PCM_RATE_64000 > -+ | 1 << VIRTIO_SND_PCM_RATE_88200 > -+ | 1 << VIRTIO_SND_PCM_RATE_96000 > -+ | 1 << VIRTIO_SND_PCM_RATE_176400 > -+ | 1 << VIRTIO_SND_PCM_RATE_192000 > -+ | 1 << VIRTIO_SND_PCM_RATE_384000; > -+ > -+ VirtIOSoundPCMParams *params = virtio_snd_pcm_get_params(s, stream_id); > ++ supported_formats = 1 << VIRTIO_SND_PCM_FMT_S8 > ++ | 1 << VIRTIO_SND_PCM_FMT_U8 > ++ | 1 << VIRTIO_SND_PCM_FMT_S16 > ++ | 1 << VIRTIO_SND_PCM_FMT_U16 > ++ | 1 << VIRTIO_SND_PCM_FMT_S32 > ++ | 1 << VIRTIO_SND_PCM_FMT_U32 > ++ | 1 << VIRTIO_SND_PCM_FMT_FLOAT; > ++ > ++ supported_rates = 1 << VIRTIO_SND_PCM_RATE_5512 > ++ | 1 << VIRTIO_SND_PCM_RATE_8000 > ++ | 1 << VIRTIO_SND_PCM_RATE_11025 > ++ | 1 << VIRTIO_SND_PCM_RATE_16000 > ++ | 1 << VIRTIO_SND_PCM_RATE_22050 > ++ | 1 << VIRTIO_SND_PCM_RATE_32000 > ++ | 1 << VIRTIO_SND_PCM_RATE_44100 > ++ | 1 << VIRTIO_SND_PCM_RATE_48000 > ++ | 1 << VIRTIO_SND_PCM_RATE_64000 > ++ | 1 << VIRTIO_SND_PCM_RATE_88200 > ++ | 1 << VIRTIO_SND_PCM_RATE_96000 > ++ | 1 << VIRTIO_SND_PCM_RATE_176400 > ++ | 1 << VIRTIO_SND_PCM_RATE_192000 > ++ | 1 << VIRTIO_SND_PCM_RATE_384000; > ++ > ++ params = virtio_snd_pcm_get_params(s, stream_id); > + assert(params); > + > -+ VirtIOSoundPCMStream *stream = g_new0(VirtIOSoundPCMStream, 1); > ++ virtio_snd_get_qemu_audsettings(&as, params); > ++ > ++ stream = g_new0(VirtIOSoundPCMStream, 1); > + > ++ stream->id = stream_id; > + stream->pcm = s->pcm; > + stream->direction = stream_id < s->snd_conf.streams / 2 + > + (s->snd_conf.streams & 1) ? VIRTIO_SND_D_OUTPUT : VIRTIO_SND_D_INPUT; > -+ stream->info.hdr.hda_fn_nid = stream->direction == VIRTIO_SND_D_OUTPUT ? > -+ VIRTIO_SOUND_HDA_FN_NID_OUT : VIRTIO_SOUND_HDA_FN_NID_IN; > ++ stream->info.hdr.hda_fn_nid = VIRTIO_SOUND_HDA_FN_NID; > + stream->features = 0; > + stream->channels_min = 1; > -+ stream->channels_max = AUDIO_MAX_CHANNELS; > ++ stream->channels_max = as.nchannels; > + stream->formats = supported_formats; > + stream->rates = supported_rates; > + stream->s = s; > @@ hw/virtio/virtio-snd.c (new) > + stream->positions[0] = VIRTIO_SND_CHMAP_FL; > + stream->positions[1] = VIRTIO_SND_CHMAP_FR; > + > -+ audsettings as; > -+ virtio_snd_get_qemu_audsettings(&as, params); > + > + if (stream->direction == VIRTIO_SND_D_OUTPUT) { > + stream->voice.out = AUD_open_out(&s->card, > @@ hw/virtio/virtio-snd.c (new) > + stream->desired_as = stream->as; > + qemu_mutex_init(&stream->queue_mutex); > + QSIMPLEQ_INIT(&stream->queue); > ++ > + s->pcm->streams[stream_id] = stream; > + > + return VIRTIO_SND_S_OK; > @@ hw/virtio/virtio-snd.c (new) > + virtio_snd_ctrl_command *cmd, > + bool start) > +{ > ++ VirtIOSoundPCMStream *stream; > + virtio_snd_pcm_hdr req; > + size_t sz; > + > @@ hw/virtio/virtio-snd.c (new) > + trace_virtio_snd_handle_pcm_start_stop(start ? "VIRTIO_SND_R_PCM_START" : > + "VIRTIO_SND_R_PCM_STOP", req.stream_id); > + > -+ VirtIOSoundPCMStream *stream = virtio_snd_pcm_get_stream(s, req.stream_id); > ++ stream = virtio_snd_pcm_get_stream(s, req.stream_id); > + > + if (stream) { > + if (stream->direction == VIRTIO_SND_D_OUTPUT) { > @@ hw/virtio/virtio-snd.c (new) > +} > + > +/* > -+ * Releases the resources allocated to a stream. Seperated from the handler > -+ * so that the code can be reused in the unrealize function. > -+ * Returns the response status code. (VIRTIO_SND_S_*). > ++ * Releases the buffer resources allocated to a stream. Seperated from the > ++ * handler so that the code can be reused in the unrealize function. Returns > ++ * the response status code. (VIRTIO_SND_S_*). > + * > + * @stream: VirtIOSoundPCMStream stream > + * @stream_id: stream id > @@ hw/virtio/virtio-snd.c (new) > +static uint32_t virtio_snd_pcm_release_impl(VirtIOSoundPCMStream *stream, > + uint32_t stream_id) > +{ > ++ assert(stream->s->pcm->streams[stream_id] == stream); > ++ > + if (virtio_snd_pcm_get_pending_bytes(stream)) { > + /* > + * virtio-v1.2-csd01, 5.14.6.6.5.1, > @@ hw/virtio/virtio-snd.c (new) > + virtio_snd_pcm_flush(stream); > + } > + > -+ if (stream->direction == VIRTIO_SND_D_OUTPUT) { > -+ AUD_close_out(&stream->s->card, stream->voice.out); > -+ } else { > -+ AUD_close_in(&stream->s->card, stream->voice.in); > -+ } > -+ > -+ assert(stream->s->pcm->streams[stream_id] == stream); > -+ > + return VIRTIO_SND_S_OK; > +} > + > @@ hw/virtio/virtio-snd.c (new) > +{ > + uint32_t stream_id; > + size_t sz; > ++ VirtIOSoundPCMStream *stream; > + > + sz = iov_to_buf(cmd->elem->out_sg, cmd->elem->out_num, > + sizeof(virtio_snd_hdr), &stream_id, sizeof(stream_id)); > @@ hw/virtio/virtio-snd.c (new) > + > + trace_virtio_snd_handle_pcm_release(stream_id); > + > -+ VirtIOSoundPCMStream *stream = virtio_snd_pcm_get_stream(s, stream_id); > ++ stream = virtio_snd_pcm_get_stream(s, stream_id); > + if (!stream) { > + error_report("already released stream %d", stream_id); > + virtio_error(VIRTIO_DEVICE(s), "already released stream %d", stream_id); > @@ hw/virtio/virtio-snd.c (new) > + > + switch (cmd->ctrl.code) { > + case VIRTIO_SND_R_JACK_INFO: > -+ /* > -+ * Unimplemented > -+ * virtio_snd_handle_jack_info(s, cmd); > -+ */ > -+ break; > + case VIRTIO_SND_R_JACK_REMAP: > -+ /* > -+ * Unimplemented > -+ * virtio_snd_handle_jack_remap(s, cmd); > -+ */ > ++ qemu_log_mask(LOG_UNIMP, > ++ "virtio_snd: jack functionality is unimplemented."); > + break; > + case VIRTIO_SND_R_PCM_INFO: > + virtio_snd_handle_pcm_info(s, cmd); > @@ hw/virtio/virtio-snd.c (new) > + virtio_snd_handle_pcm_release(s, cmd); > + break; > + case VIRTIO_SND_R_CHMAP_INFO: > ++ qemu_log_mask(LOG_UNIMP, > ++ "virtio_snd: chmap info functionality is unimplemented."); > + trace_virtio_snd_handle_chmap_info(); > + break; > + default: > @@ hw/virtio/virtio-snd.c (new) > + */ > +static void virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq) > +{ > ++ qemu_log_mask(LOG_UNIMP, "virtio_snd: event queue is unimplemented."); > + trace_virtio_snd_handle_event(); > +} > + > @@ hw/virtio/virtio-snd.c (new) > + VirtQueueElement *elem; > + size_t sz; > + virtio_snd_pcm_xfer hdr; > ++ virtio_snd_pcm_status resp = { 0 }; > + > + trace_virtio_snd_handle_xfer(); > + > @@ hw/virtio/virtio-snd.c (new) > + elem, > + hdr.stream_id == VIRTIO_SND_D_INPUT); > + > -+ virtio_snd_pcm_status resp = { 0 }; > + resp.status = VIRTIO_SND_S_OK; > + sz = iov_from_buf(elem->in_sg, > + elem->in_num, > @@ hw/virtio/virtio-snd.c (new) > + VirtIOSoundPCMStream *stream = data; > + VirtIOSoundPCMBlock *block; > + VirtIOSoundPCMBlock *next; > ++ int size; > + > + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { > + QSIMPLEQ_FOREACH_SAFE(block, &stream->queue, entry, next) { > -+ int size = MIN(block->size, available); > -+ > + for (;;) { > ++ size = MIN(block->size, available); > + size = AUD_write(stream->voice.out, > + block->data + block->offset, > + size); > @@ hw/virtio/virtio-snd.c (new) > + sizeof(block->elem)); > + virtio_notify(VIRTIO_DEVICE(stream->s), > + block->vq); > -+ QSIMPLEQ_REMOVE(&stream->queue, > -+ block, > -+ VirtIOSoundPCMBlock, > -+ entry); > ++ QSIMPLEQ_REMOVE_HEAD(&stream->queue, entry); > ++ g_free(block); > + available -= size; > + break; > + } > @@ hw/virtio/virtio-snd.c (new) > +{ > + VirtIODevice *vdev = VIRTIO_DEVICE(dev); > + VirtIOSound *vsnd = VIRTIO_SND(dev); > ++ VirtIOSoundPCMStream *stream; > + > + qemu_del_vm_change_state_handler(vsnd->vmstate); > + virtio_del_queue(vdev, 0); > @@ hw/virtio/virtio-snd.c (new) > + trace_virtio_snd_unrealize(vsnd); > + > + for (int i = VIRTIO_SND_D_OUTPUT; i <= VIRTIO_SND_D_INPUT; i++) { > -+ VirtIOSoundPCMStream *stream = vsnd->pcm->streams[i]; > -+ > ++ stream = vsnd->pcm->streams[i]; > + virtio_snd_pcm_close(stream); > -+ > + g_free(stream); > + } > + > @@ hw/virtio/virtio-snd.c (new) > + sizeof(virtio_snd_pcm_xfer); > + assert(size <= stream->period_bytes); > + > -+ trace_virtio_snd_handle_xfer_elem(read ? "read" : "write"); > -+ > -+ fragment = g_malloc(sizeof(VirtIOSoundPCMBlock) + size); > ++ fragment = g_malloc0(sizeof(VirtIOSoundPCMBlock) + size); > + fragment->elem = element; > + fragment->vq = vq; > + fragment->size = size; > @@ hw/virtio/virtio-snd.c (new) > + > +type_init(virtio_snd_register); > > - ## include/hw/pci/pci.h ## > -@@ include/hw/pci/pci.h: extern bool pci_available; > - #define PCI_DEVICE_ID_VIRTIO_RNG 0x1005 > - #define PCI_DEVICE_ID_VIRTIO_9P 0x1009 > - #define PCI_DEVICE_ID_VIRTIO_VSOCK 0x1012 > -+#define PCI_DEVICE_ID_VIRTIO_SND 0x1019 > - > - /* > - * modern virtio-pci devices get their id assigned automatically, > - > ## include/hw/virtio/virtio-snd.h (new) ## > @@ > +/* > @@ include/hw/virtio/virtio-snd.h (new) > +typedef struct VirtIOSoundPCMStream { > + VirtIOSoundPCM *pcm; > + struct virtio_snd_pcm_info info; > ++ uint32_t id; > + uint32_t buffer_bytes; > + uint32_t period_bytes; > + /* channel position values (VIRTIO_SND_CHMAP_XXX) */ > @@ include/hw/virtio/virtio-snd.h (new) > + } > +}; > +#endif > - > - ## softmmu/qdev-monitor.c ## > -@@ softmmu/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}, > -+ { "virtio-sound-pci", "virtio-sound", QEMU_ARCH_VIRTIO_PCI}, > - { "virtio-tablet-device", "virtio-tablet", QEMU_ARCH_VIRTIO_MMIO }, > - { "virtio-tablet-ccw", "virtio-tablet", QEMU_ARCH_VIRTIO_CCW }, > - { "virtio-tablet-pci", "virtio-tablet", QEMU_ARCH_VIRTIO_PCI }, >-: ---------- > 2: db527bc9a1 Add virtio-sound-pci device >-: ---------- > 3: 4c130f066d Implement audio capture in virtio-snd device >-- >2.39.2 >
Cc'ing more contributors involved with audio in general. On 13/7/23 13:00, Manos Pitsidianakis wrote: > Ping > > Patch series on patchew: > https://patchew.org/QEMU/cover.1686238728.git.manos.pitsidianakis@linaro.org/ > > Patch series on lore: > https://lore.kernel.org/qemu-devel/cover.1686238728.git.manos.pitsidianakis@linaro.org/ > > > On Thu, 08 Jun 2023 18:56, Manos Pitsidianakis > <manos.pitsidianakis@linaro.org> wrote: >> This patch series adds an audio device implementing the recent virtio >> sound spec (1.2) and a corresponding PCI wrapper device. >> >> Main differences with v1 patch >> <20230526204845.673031-1-manos.pitsidianakis@linaro.org>: >> >> - Split virtio-snd and virtio-snd-pci devices to two commits >> - Added audio capture support >> >> Known problems (On pipewire at least): >> >> - Stereo recording results in one channel with the recording + a >> ticking kind of artifact and one channel that has no artifacts, just >> the recording. >> >> Manos Pitsidianakis (3): >> Add virtio-sound device >> Add virtio-sound-pci device >> Implement audio capture in virtio-snd device >> >> MAINTAINERS | 6 + >> hw/virtio/Kconfig | 5 + >> hw/virtio/meson.build | 2 + >> hw/virtio/trace-events | 22 + >> hw/virtio/virtio-snd-pci.c | 102 +++ >> hw/virtio/virtio-snd.c | 1290 ++++++++++++++++++++++++++++++++ >> include/hw/pci/pci.h | 1 + >> include/hw/virtio/virtio-snd.h | 194 +++++ >> softmmu/qdev-monitor.c | 1 + >> 9 files changed, 1623 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 v1: >> 1: 652c7d2d01 ! 1: 95d564fc1f Add virtio-sound and virtio-sound-pci >> devices >> @@ Metadata >> Author: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> >> ## Commit message ## >> - Add virtio-sound and virtio-sound-pci devices >> + Add virtio-sound device >> This patch adds an audio device implementing the recent virtio >> sound >> - spec (1.2) and a corresponding PCI wrapper device. >> + spec (1.2). >> PCM functionality is implemented, and jack[0], chmaps[1] >> messages are >> - at the moment ignored. >> - >> - To test this, you'll need a >6.0 kernel compiled with the >> virtio-snd >> - flag enabled, which distros have off by default. >> - >> - Use with following flags in the invocation: >> - >> - -device virtio-sound-pci,disable-legacy=on >> - >> - And an audio backend listed with `-audio driver=help` that >> works on >> - your host machine, e.g.: >> - >> - Pulseaudio: >> - -audio >> driver=pa,model=virtio-sound,server=/run/user/1000/pulse/native >> - sdl: >> - -audio driver=sdl,model=virtio-sound >> - coreaudio: >> - -audio driver=coreaudio,model=virtio-sound >> - etc. >> - >> - You can use speaker-test from alsa-tools to play noise, sines, or >> - WAV files. >> + at the moment left unimplemented. >> PS2: This patch was based on a draft patch posted by >> OpenSynergy in 2019. [2] >> @@ Commit message >> Signed-off-by: Igor Skalkin <Igor.Skalkin@opensynergy.com> >> Signed-off-by: Anton Yakovlev <Anton.Yakovlev@opensynergy.com> >> + ## MAINTAINERS ## >> +@@ MAINTAINERS: F: hw/virtio/virtio-mem-pci.h >> + F: hw/virtio/virtio-mem-pci.c >> + F: include/hw/virtio/virtio-mem.h >> + ++virtio-snd >> ++M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> >> ++S: Supported >> ++F: hw/virtio/virtio-snd*.c >> ++F: include/hw/virtio/virtio-snd.h >> ++ >> + nvme >> + M: Keith Busch <kbusch@kernel.org> >> + M: Klaus Jensen <its@irrelevant.dk> >> + >> ## hw/virtio/Kconfig ## >> @@ hw/virtio/Kconfig: config VIRTIO_PCI >> depends on PCI >> @@ hw/virtio/Kconfig: config VIRTIO_PCI >> select VIRTIO >> ## hw/virtio/meson.build ## >> -@@ hw/virtio/meson.build: virtio_pci_ss.add(when: >> 'CONFIG_VIRTIO_SERIAL', if_true: files('virtio-serial-pc >> - virtio_pci_ss.add(when: 'CONFIG_VIRTIO_PMEM', if_true: >> files('virtio-pmem-pci.c')) >> - virtio_pci_ss.add(when: 'CONFIG_VIRTIO_IOMMU', if_true: >> files('virtio-iommu-pci.c')) >> - virtio_pci_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: >> files('virtio-mem-pci.c')) >> -+virtio_pci_ss.add(when: 'CONFIG_VIRTIO_SND', if_true: >> files('virtio-snd-pci.c')) >> - virtio_pci_ss.add(when: 'CONFIG_VHOST_VDPA_DEV', if_true: >> files('vdpa-dev-pci.c')) >> - - specific_virtio_ss.add_all(when: 'CONFIG_VIRTIO_PCI', >> if_true: virtio_pci_ss) >> @@ hw/virtio/meson.build: softmmu_ss.add(when: 'CONFIG_VIRTIO', >> if_false: files('virtio-stub.c')) >> softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-stub.c')) >> softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('virtio-stub.c')) >> @@ hw/virtio/trace-events: virtio_pmem_flush_done(int type) "fsync >> return=%d" >> +virtio_snd_handle_pcm_info(int stream) "VIRTIO_SND_R_PCM_INFO >> called for stream %d" >> +virtio_snd_handle_pcm_set_params(int stream) >> "VIRTIO_SND_PCM_SET_PARAMS called for stream %d" >> +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 %id" >> ++virtio_snd_handle_pcm_release(int stream) "VIRTIO_SND_PCM_RELEASE >> called for stream %d" >> +virtio_snd_handle_chmap_info(void) "VIRTIO_SND_CHMAP_INFO called" >> +virtio_snd_handle_xfer(void) "tx/rx queue callback called" >> -+virtio_snd_handle_xfer_elem(const char * k) "xfer handled in >> virtio_snd_pcm_%s" >> +virtio_snd_handle_event(void) "event queue callback called" >> -+virtio_snd_cpu_is_stopped(void *snd, int size) "snd %p: cpu is >> stopped, dropping %d bytes" >> +virtio_snd_realize(void *snd) "snd %p: realize" >> -+virtio_snd_unrealize(void *snd) "snd %p: realize" >> ++virtio_snd_unrealize(void *snd) "snd %p: unrealize" >> +virtio_snd_get_features(void *vdev, uint64_t features) "snd %p: >> get_features 0x%"PRIx64 >> +virtio_snd_get_config(void *vdev, uint32_t jacks, uint32_t >> streams, uint32_t chmaps) "snd %p: get_config jacks=%d streams=%d >> chmaps=%d" >> +virtio_snd_set_config(void *vdev, uint32_t jacks, uint32_t >> new_jacks, uint32_t streams, uint32_t new_streams, uint32_t chmaps, >> uint32_t new_chmaps) "snd %p: set_config jacks from %d->%d, streams >> from %d->%d, chmaps from %d->%d" >> @@ hw/virtio/trace-events: virtio_pmem_flush_done(int type) "fsync >> return=%d" >> +virtio_snd_vm_state_stopped(void) "vm state stopped" >> +virtio_snd_handle_code(int val, const char *code) "ctrl code msg >> val = %d == %s" >> - ## hw/virtio/virtio-snd-pci.c (new) ## >> -@@ >> -+/* >> -+ * VIRTIO Sound Device PCI Bindings >> -+ * >> -+ * Copyright (c) 2023 Emmanouil Pitsidianakis >> <manos.pitsidianakis@linaro.org> >> -+ * >> -+ * This work is licensed under the terms of the GNU GPL, version >> 2 or >> -+ * (at your option) any later version. See the COPYING file in the >> -+ * top-level directory. >> -+ */ >> -+ >> -+#include "qemu/osdep.h" >> -+#include "hw/audio/soundhw.h" >> -+#include "hw/virtio/virtio-pci.h" >> -+#include "hw/virtio/virtio-snd.h" >> -+ >> -+typedef struct VirtIOSoundPCI VirtIOSoundPCI; >> -+ >> -+/* >> -+ * virtio-snd-pci: This extends VirtioPCIProxy. >> -+ */ >> -+#define TYPE_VIRTIO_SND_PCI "virtio-sound-pci-base" >> -+DECLARE_INSTANCE_CHECKER(VirtIOSoundPCI, VIRTIO_SOUND_PCI, >> -+ TYPE_VIRTIO_SND_PCI) >> -+ >> -+struct VirtIOSoundPCI { >> -+ VirtIOPCIProxy parent; >> -+ VirtIOSound vdev; >> -+}; >> -+ >> -+static Property virtio_snd_pci_properties[] = { >> -+ DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), >> -+ DEFINE_PROP_END_OF_LIST(), >> -+}; >> -+ >> -+static const char *audiodev_id; >> -+ >> -+static int virtio_snd_init_pci(PCIBus *init_bus, const char >> *audiodev) >> -+{ >> -+ audiodev_id = audiodev; >> -+ return 0; >> -+} >> -+ >> -+static void virtio_snd_pci_realize(VirtIOPCIProxy *vpci_dev, >> Error **errp) >> -+{ >> -+ 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); >> -+ AUD_register_card ("virtio-sound", &dev->vdev.card); >> -+ >> -+ object_property_set_bool(OBJECT(vdev), "realized", true, errp); >> -+} >> -+ >> -+static void virtio_snd_pci_class_init(ObjectClass *klass, void >> *data) >> -+{ >> -+ DeviceClass *dc = DEVICE_CLASS(klass); >> -+ VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass); >> -+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); >> -+ >> -+ vpciklass->realize = virtio_snd_pci_realize; >> -+ set_bit(DEVICE_CATEGORY_SOUND, dc->categories); >> -+ >> -+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; >> -+ pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SND; >> -+ pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; >> -+ pcidev_k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO; >> -+ device_class_set_props(dc, virtio_snd_pci_properties); >> -+} >> -+ >> -+static void virtio_snd_pci_instance_init(Object *obj) >> -+{ >> -+ VirtIOSoundPCI *dev = VIRTIO_SOUND_PCI(obj); >> -+ >> -+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), >> -+ TYPE_VIRTIO_SND); >> -+} >> -+ >> -+static const VirtioPCIDeviceTypeInfo virtio_snd_pci_info = { >> -+ .base_name = TYPE_VIRTIO_SND_PCI, >> -+ .generic_name = "virtio-sound-pci", >> -+ .instance_size = sizeof(VirtIOSoundPCI), >> -+ .instance_init = virtio_snd_pci_instance_init, >> -+ .class_init = virtio_snd_pci_class_init, >> -+}; >> -+ >> -+static void virtio_snd_pci_register(void) >> -+{ >> -+ virtio_pci_types_register(&virtio_snd_pci_info); >> -+ pci_register_soundhw("virtio-sound", "Virtio Sound Device", >> -+ virtio_snd_init_pci); >> -+} >> -+ >> -+type_init(virtio_snd_pci_register); >> - >> ## hw/virtio/virtio-snd.c (new) ## >> @@ >> +/* >> @@ hw/virtio/virtio-snd.c (new) >> +#define VIRTIO_SOUND_STREAM_DEFAULT 1 >> +#define VIRTIO_SOUND_CHMAP_DEFAULT 0 >> + >> -+#define VIRTIO_SOUND_HDA_FN_NID_OUT 0 >> -+#define VIRTIO_SOUND_HDA_FN_NID_IN 1 >> ++#define VIRTIO_SOUND_HDA_FN_NID 0 >> + >> +static const VMStateDescription vmstate_virtio_snd_device = { >> + .name = TYPE_VIRTIO_SND, >> @@ hw/virtio/virtio-snd.c (new) >> +uint32_t virtio_snd_pcm_set_params_impl(VirtIOSound *s, >> + virtio_snd_pcm_set_params >> *params) >> +{ >> ++ uint32_t supported_formats, supported_rates; >> ++ VirtIOSoundPCMParams *st_params; >> + uint32_t stream_id = params->hdr.stream_id; >> ++ >> + if (stream_id > s->snd_conf.streams || !(s->pcm->pcm_params)) { >> + virtio_error(VIRTIO_DEVICE(s), "Streams have not been >> initialized.\n"); >> + return VIRTIO_SND_S_BAD_MSG; >> @@ hw/virtio/virtio-snd.c (new) >> + if (!s->pcm->pcm_params[stream_id]) { >> + s->pcm->pcm_params[stream_id] = >> g_new0(VirtIOSoundPCMParams, 1); >> + } >> -+ VirtIOSoundPCMParams *st_params = >> virtio_snd_pcm_get_params(s, stream_id); >> ++ st_params = virtio_snd_pcm_get_params(s, stream_id); >> + >> + st_params->features = params->features; >> + st_params->buffer_bytes = params->buffer_bytes; >> @@ hw/virtio/virtio-snd.c (new) >> + } >> + st_params->channels = params->channels; >> + >> -+ uint32_t supported_formats = 1 << VIRTIO_SND_PCM_FMT_S8 >> -+ | 1 << VIRTIO_SND_PCM_FMT_U8 >> -+ | 1 << VIRTIO_SND_PCM_FMT_S16 >> -+ | 1 << VIRTIO_SND_PCM_FMT_U16 >> -+ | 1 << VIRTIO_SND_PCM_FMT_S32 >> -+ | 1 << VIRTIO_SND_PCM_FMT_U32 >> -+ | 1 << VIRTIO_SND_PCM_FMT_FLOAT; >> -+ >> -+ uint32_t supported_rates = 1 << VIRTIO_SND_PCM_RATE_5512 >> -+ | 1 << VIRTIO_SND_PCM_RATE_8000 >> -+ | 1 << VIRTIO_SND_PCM_RATE_11025 >> -+ | 1 << VIRTIO_SND_PCM_RATE_16000 >> -+ | 1 << VIRTIO_SND_PCM_RATE_22050 >> -+ | 1 << VIRTIO_SND_PCM_RATE_32000 >> -+ | 1 << VIRTIO_SND_PCM_RATE_44100 >> -+ | 1 << VIRTIO_SND_PCM_RATE_48000 >> -+ | 1 << VIRTIO_SND_PCM_RATE_64000 >> -+ | 1 << VIRTIO_SND_PCM_RATE_88200 >> -+ | 1 << VIRTIO_SND_PCM_RATE_96000 >> -+ | 1 << VIRTIO_SND_PCM_RATE_176400 >> -+ | 1 << VIRTIO_SND_PCM_RATE_192000 >> -+ | 1 << VIRTIO_SND_PCM_RATE_384000; >> ++ supported_formats = 1 << VIRTIO_SND_PCM_FMT_S8 >> ++ | 1 << VIRTIO_SND_PCM_FMT_U8 >> ++ | 1 << VIRTIO_SND_PCM_FMT_S16 >> ++ | 1 << VIRTIO_SND_PCM_FMT_U16 >> ++ | 1 << VIRTIO_SND_PCM_FMT_S32 >> ++ | 1 << VIRTIO_SND_PCM_FMT_U32 >> ++ | 1 << VIRTIO_SND_PCM_FMT_FLOAT; >> ++ >> ++ supported_rates = 1 << VIRTIO_SND_PCM_RATE_5512 >> ++ | 1 << VIRTIO_SND_PCM_RATE_8000 >> ++ | 1 << VIRTIO_SND_PCM_RATE_11025 >> ++ | 1 << VIRTIO_SND_PCM_RATE_16000 >> ++ | 1 << VIRTIO_SND_PCM_RATE_22050 >> ++ | 1 << VIRTIO_SND_PCM_RATE_32000 >> ++ | 1 << VIRTIO_SND_PCM_RATE_44100 >> ++ | 1 << VIRTIO_SND_PCM_RATE_48000 >> ++ | 1 << VIRTIO_SND_PCM_RATE_64000 >> ++ | 1 << VIRTIO_SND_PCM_RATE_88200 >> ++ | 1 << VIRTIO_SND_PCM_RATE_96000 >> ++ | 1 << VIRTIO_SND_PCM_RATE_176400 >> ++ | 1 << VIRTIO_SND_PCM_RATE_192000 >> ++ | 1 << VIRTIO_SND_PCM_RATE_384000; >> + >> + if (!(supported_formats & (1 << params->format))) { >> + error_report("Stream format is not supported."); >> @@ hw/virtio/virtio-snd.c (new) >> + st_params->rate = params->rate; >> + st_params->period_bytes = params->period_bytes; >> + st_params->buffer_bytes = params->buffer_bytes; >> ++ >> + return VIRTIO_SND_S_OK; >> +} >> + >> @@ hw/virtio/virtio-snd.c (new) >> +{ >> + virtio_snd_pcm_set_params req; >> + uint32_t sz; >> ++ >> + sz = iov_to_buf(cmd->elem->out_sg, >> + cmd->elem->out_num, >> + 0, >> @@ hw/virtio/virtio-snd.c (new) >> + >> + trace_virtio_snd_handle_pcm_set_params(req.hdr.stream_id); >> + cmd->resp.code = virtio_snd_pcm_set_params_impl(s, &req); >> ++ >> + return; >> +} >> + >> @@ hw/virtio/virtio-snd.c (new) >> +static void virtio_snd_get_qemu_audsettings(audsettings *as, >> + VirtIOSoundPCMParams >> *params) >> +{ >> -+ as->nchannels = params->channels; >> ++ as->nchannels = MIN(AUDIO_MAX_CHANNELS, params->channels); >> + as->fmt = virtio_snd_get_qemu_format(params->format); >> + as->freq = virtio_snd_get_qemu_freq(params->rate); >> + as->endianness = AUDIO_HOST_ENDIANNESS; >> @@ hw/virtio/virtio-snd.c (new) >> + */ >> +static uint32_t virtio_snd_pcm_prepare_impl(VirtIOSound *s, >> uint32_t stream_id) >> +{ >> ++ audsettings as; >> ++ uint32_t supported_formats, supported_rates; >> ++ VirtIOSoundPCMParams *params; >> ++ VirtIOSoundPCMStream *stream; >> ++ >> + if (!s->pcm->streams >> + || !s->pcm->pcm_params >> + || !s->pcm->pcm_params[stream_id]) { >> + return VIRTIO_SND_S_BAD_MSG; >> + } >> + >> -+ uint32_t supported_formats = 1 << VIRTIO_SND_PCM_FMT_S8 >> -+ | 1 << VIRTIO_SND_PCM_FMT_U8 >> -+ | 1 << VIRTIO_SND_PCM_FMT_S16 >> -+ | 1 << VIRTIO_SND_PCM_FMT_U16 >> -+ | 1 << VIRTIO_SND_PCM_FMT_S32 >> -+ | 1 << VIRTIO_SND_PCM_FMT_U32 >> -+ | 1 << VIRTIO_SND_PCM_FMT_FLOAT; >> -+ >> -+ uint32_t supported_rates = 1 << VIRTIO_SND_PCM_RATE_5512 >> -+ | 1 << VIRTIO_SND_PCM_RATE_8000 >> -+ | 1 << VIRTIO_SND_PCM_RATE_11025 >> -+ | 1 << VIRTIO_SND_PCM_RATE_16000 >> -+ | 1 << VIRTIO_SND_PCM_RATE_22050 >> -+ | 1 << VIRTIO_SND_PCM_RATE_32000 >> -+ | 1 << VIRTIO_SND_PCM_RATE_44100 >> -+ | 1 << VIRTIO_SND_PCM_RATE_48000 >> -+ | 1 << VIRTIO_SND_PCM_RATE_64000 >> -+ | 1 << VIRTIO_SND_PCM_RATE_88200 >> -+ | 1 << VIRTIO_SND_PCM_RATE_96000 >> -+ | 1 << VIRTIO_SND_PCM_RATE_176400 >> -+ | 1 << VIRTIO_SND_PCM_RATE_192000 >> -+ | 1 << VIRTIO_SND_PCM_RATE_384000; >> -+ >> -+ VirtIOSoundPCMParams *params = virtio_snd_pcm_get_params(s, >> stream_id); >> ++ supported_formats = 1 << VIRTIO_SND_PCM_FMT_S8 >> ++ | 1 << VIRTIO_SND_PCM_FMT_U8 >> ++ | 1 << VIRTIO_SND_PCM_FMT_S16 >> ++ | 1 << VIRTIO_SND_PCM_FMT_U16 >> ++ | 1 << VIRTIO_SND_PCM_FMT_S32 >> ++ | 1 << VIRTIO_SND_PCM_FMT_U32 >> ++ | 1 << VIRTIO_SND_PCM_FMT_FLOAT; >> ++ >> ++ supported_rates = 1 << VIRTIO_SND_PCM_RATE_5512 >> ++ | 1 << VIRTIO_SND_PCM_RATE_8000 >> ++ | 1 << VIRTIO_SND_PCM_RATE_11025 >> ++ | 1 << VIRTIO_SND_PCM_RATE_16000 >> ++ | 1 << VIRTIO_SND_PCM_RATE_22050 >> ++ | 1 << VIRTIO_SND_PCM_RATE_32000 >> ++ | 1 << VIRTIO_SND_PCM_RATE_44100 >> ++ | 1 << VIRTIO_SND_PCM_RATE_48000 >> ++ | 1 << VIRTIO_SND_PCM_RATE_64000 >> ++ | 1 << VIRTIO_SND_PCM_RATE_88200 >> ++ | 1 << VIRTIO_SND_PCM_RATE_96000 >> ++ | 1 << VIRTIO_SND_PCM_RATE_176400 >> ++ | 1 << VIRTIO_SND_PCM_RATE_192000 >> ++ | 1 << VIRTIO_SND_PCM_RATE_384000; >> ++ >> ++ params = virtio_snd_pcm_get_params(s, stream_id); >> + assert(params); >> + >> -+ VirtIOSoundPCMStream *stream = g_new0(VirtIOSoundPCMStream, 1); >> ++ virtio_snd_get_qemu_audsettings(&as, params); >> ++ >> ++ stream = g_new0(VirtIOSoundPCMStream, 1); >> + >> ++ stream->id = stream_id; >> + stream->pcm = s->pcm; >> + stream->direction = stream_id < s->snd_conf.streams / 2 + >> + (s->snd_conf.streams & 1) ? VIRTIO_SND_D_OUTPUT : >> VIRTIO_SND_D_INPUT; >> -+ stream->info.hdr.hda_fn_nid = stream->direction == >> VIRTIO_SND_D_OUTPUT ? >> -+ VIRTIO_SOUND_HDA_FN_NID_OUT : VIRTIO_SOUND_HDA_FN_NID_IN; >> ++ stream->info.hdr.hda_fn_nid = VIRTIO_SOUND_HDA_FN_NID; >> + stream->features = 0; >> + stream->channels_min = 1; >> -+ stream->channels_max = AUDIO_MAX_CHANNELS; >> ++ stream->channels_max = as.nchannels; >> + stream->formats = supported_formats; >> + stream->rates = supported_rates; >> + stream->s = s; >> @@ hw/virtio/virtio-snd.c (new) >> + stream->positions[0] = VIRTIO_SND_CHMAP_FL; >> + stream->positions[1] = VIRTIO_SND_CHMAP_FR; >> + >> -+ audsettings as; >> -+ virtio_snd_get_qemu_audsettings(&as, params); >> + >> + if (stream->direction == VIRTIO_SND_D_OUTPUT) { >> + stream->voice.out = AUD_open_out(&s->card, >> @@ hw/virtio/virtio-snd.c (new) >> + stream->desired_as = stream->as; >> + qemu_mutex_init(&stream->queue_mutex); >> + QSIMPLEQ_INIT(&stream->queue); >> ++ >> + s->pcm->streams[stream_id] = stream; >> + >> + return VIRTIO_SND_S_OK; >> @@ hw/virtio/virtio-snd.c (new) >> + >> virtio_snd_ctrl_command *cmd, >> + bool start) >> +{ >> ++ VirtIOSoundPCMStream *stream; >> + virtio_snd_pcm_hdr req; >> + size_t sz; >> + >> @@ hw/virtio/virtio-snd.c (new) >> + trace_virtio_snd_handle_pcm_start_stop(start ? >> "VIRTIO_SND_R_PCM_START" : >> + "VIRTIO_SND_R_PCM_STOP", req.stream_id); >> + >> -+ VirtIOSoundPCMStream *stream = virtio_snd_pcm_get_stream(s, >> req.stream_id); >> ++ stream = virtio_snd_pcm_get_stream(s, req.stream_id); >> + >> + if (stream) { >> + if (stream->direction == VIRTIO_SND_D_OUTPUT) { >> @@ hw/virtio/virtio-snd.c (new) >> +} >> + >> +/* >> -+ * Releases the resources allocated to a stream. Seperated from >> the handler >> -+ * so that the code can be reused in the unrealize function. >> -+ * Returns the response status code. (VIRTIO_SND_S_*). >> ++ * Releases the buffer resources allocated to a stream. Seperated >> from the >> ++ * handler so that the code can be reused in the unrealize >> function. Returns >> ++ * the response status code. (VIRTIO_SND_S_*). >> + * >> + * @stream: VirtIOSoundPCMStream stream >> + * @stream_id: stream id >> @@ hw/virtio/virtio-snd.c (new) >> +static uint32_t virtio_snd_pcm_release_impl(VirtIOSoundPCMStream >> *stream, >> + uint32_t stream_id) >> +{ >> ++ assert(stream->s->pcm->streams[stream_id] == stream); >> ++ >> + if (virtio_snd_pcm_get_pending_bytes(stream)) { >> + /* >> + * virtio-v1.2-csd01, 5.14.6.6.5.1, >> @@ hw/virtio/virtio-snd.c (new) >> + virtio_snd_pcm_flush(stream); >> + } >> + >> -+ if (stream->direction == VIRTIO_SND_D_OUTPUT) { >> -+ AUD_close_out(&stream->s->card, stream->voice.out); >> -+ } else { >> -+ AUD_close_in(&stream->s->card, stream->voice.in); >> -+ } >> -+ >> -+ assert(stream->s->pcm->streams[stream_id] == stream); >> -+ >> + return VIRTIO_SND_S_OK; >> +} >> + >> @@ hw/virtio/virtio-snd.c (new) >> +{ >> + uint32_t stream_id; >> + size_t sz; >> ++ VirtIOSoundPCMStream *stream; >> + >> + sz = iov_to_buf(cmd->elem->out_sg, cmd->elem->out_num, >> + sizeof(virtio_snd_hdr), &stream_id, sizeof(stream_id)); >> @@ hw/virtio/virtio-snd.c (new) >> + >> + trace_virtio_snd_handle_pcm_release(stream_id); >> + >> -+ VirtIOSoundPCMStream *stream = virtio_snd_pcm_get_stream(s, >> stream_id); >> ++ stream = virtio_snd_pcm_get_stream(s, stream_id); >> + if (!stream) { >> + error_report("already released stream %d", stream_id); >> + virtio_error(VIRTIO_DEVICE(s), "already released stream >> %d", stream_id); >> @@ hw/virtio/virtio-snd.c (new) >> + >> + switch (cmd->ctrl.code) { >> + case VIRTIO_SND_R_JACK_INFO: >> -+ /* >> -+ * Unimplemented >> -+ * virtio_snd_handle_jack_info(s, cmd); >> -+ */ >> -+ break; >> + case VIRTIO_SND_R_JACK_REMAP: >> -+ /* >> -+ * Unimplemented >> -+ * virtio_snd_handle_jack_remap(s, cmd); >> -+ */ >> ++ qemu_log_mask(LOG_UNIMP, >> ++ "virtio_snd: jack functionality is >> unimplemented."); >> + break; >> + case VIRTIO_SND_R_PCM_INFO: >> + virtio_snd_handle_pcm_info(s, cmd); >> @@ hw/virtio/virtio-snd.c (new) >> + virtio_snd_handle_pcm_release(s, cmd); >> + break; >> + case VIRTIO_SND_R_CHMAP_INFO: >> ++ qemu_log_mask(LOG_UNIMP, >> ++ "virtio_snd: chmap info functionality is >> unimplemented."); >> + trace_virtio_snd_handle_chmap_info(); >> + break; >> + default: >> @@ hw/virtio/virtio-snd.c (new) >> + */ >> +static void virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue >> *vq) >> +{ >> ++ qemu_log_mask(LOG_UNIMP, "virtio_snd: event queue is >> unimplemented."); >> + trace_virtio_snd_handle_event(); >> +} >> + >> @@ hw/virtio/virtio-snd.c (new) >> + VirtQueueElement *elem; >> + size_t sz; >> + virtio_snd_pcm_xfer hdr; >> ++ virtio_snd_pcm_status resp = { 0 }; >> + >> + trace_virtio_snd_handle_xfer(); >> + >> @@ hw/virtio/virtio-snd.c (new) >> + elem, >> + hdr.stream_id == VIRTIO_SND_D_INPUT); >> + >> -+ virtio_snd_pcm_status resp = { 0 }; >> + resp.status = VIRTIO_SND_S_OK; >> + sz = iov_from_buf(elem->in_sg, >> + elem->in_num, >> @@ hw/virtio/virtio-snd.c (new) >> + VirtIOSoundPCMStream *stream = data; >> + VirtIOSoundPCMBlock *block; >> + VirtIOSoundPCMBlock *next; >> ++ int size; >> + >> + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { >> + QSIMPLEQ_FOREACH_SAFE(block, &stream->queue, entry, next) { >> -+ int size = MIN(block->size, available); >> -+ >> + for (;;) { >> ++ size = MIN(block->size, available); >> + size = AUD_write(stream->voice.out, >> + block->data + block->offset, >> + size); >> @@ hw/virtio/virtio-snd.c (new) >> + sizeof(block->elem)); >> + virtio_notify(VIRTIO_DEVICE(stream->s), >> + block->vq); >> -+ QSIMPLEQ_REMOVE(&stream->queue, >> -+ block, >> -+ VirtIOSoundPCMBlock, >> -+ entry); >> ++ QSIMPLEQ_REMOVE_HEAD(&stream->queue, entry); >> ++ g_free(block); >> + available -= size; >> + break; >> + } >> @@ hw/virtio/virtio-snd.c (new) >> +{ >> + VirtIODevice *vdev = VIRTIO_DEVICE(dev); >> + VirtIOSound *vsnd = VIRTIO_SND(dev); >> ++ VirtIOSoundPCMStream *stream; >> + >> + qemu_del_vm_change_state_handler(vsnd->vmstate); >> + virtio_del_queue(vdev, 0); >> @@ hw/virtio/virtio-snd.c (new) >> + trace_virtio_snd_unrealize(vsnd); >> + >> + for (int i = VIRTIO_SND_D_OUTPUT; i <= VIRTIO_SND_D_INPUT; >> i++) { >> -+ VirtIOSoundPCMStream *stream = vsnd->pcm->streams[i]; >> -+ >> ++ stream = vsnd->pcm->streams[i]; >> + virtio_snd_pcm_close(stream); >> -+ >> + g_free(stream); >> + } >> + >> @@ hw/virtio/virtio-snd.c (new) >> + sizeof(virtio_snd_pcm_xfer); >> + assert(size <= stream->period_bytes); >> + >> -+ trace_virtio_snd_handle_xfer_elem(read ? "read" : "write"); >> -+ >> -+ fragment = g_malloc(sizeof(VirtIOSoundPCMBlock) + size); >> ++ fragment = g_malloc0(sizeof(VirtIOSoundPCMBlock) + size); >> + fragment->elem = element; >> + fragment->vq = vq; >> + fragment->size = size; >> @@ hw/virtio/virtio-snd.c (new) >> + >> +type_init(virtio_snd_register); >> - ## include/hw/pci/pci.h ## >> -@@ include/hw/pci/pci.h: extern bool pci_available; >> - #define PCI_DEVICE_ID_VIRTIO_RNG 0x1005 >> - #define PCI_DEVICE_ID_VIRTIO_9P 0x1009 >> - #define PCI_DEVICE_ID_VIRTIO_VSOCK 0x1012 >> -+#define PCI_DEVICE_ID_VIRTIO_SND 0x1019 >> - - /* >> - * modern virtio-pci devices get their id assigned automatically, >> - >> ## include/hw/virtio/virtio-snd.h (new) ## >> @@ >> +/* >> @@ include/hw/virtio/virtio-snd.h (new) >> +typedef struct VirtIOSoundPCMStream { >> + VirtIOSoundPCM *pcm; >> + struct virtio_snd_pcm_info info; >> ++ uint32_t id; >> + uint32_t buffer_bytes; >> + uint32_t period_bytes; >> + /* channel position values (VIRTIO_SND_CHMAP_XXX) */ >> @@ include/hw/virtio/virtio-snd.h (new) >> + } >> +}; >> +#endif >> - >> - ## softmmu/qdev-monitor.c ## >> -@@ softmmu/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}, >> -+ { "virtio-sound-pci", "virtio-sound", QEMU_ARCH_VIRTIO_PCI}, >> - { "virtio-tablet-device", "virtio-tablet", >> QEMU_ARCH_VIRTIO_MMIO }, >> - { "virtio-tablet-ccw", "virtio-tablet", QEMU_ARCH_VIRTIO_CCW }, >> - { "virtio-tablet-pci", "virtio-tablet", QEMU_ARCH_VIRTIO_PCI }, >> -: ---------- > 2: db527bc9a1 Add virtio-sound-pci device >> -: ---------- > 3: 4c130f066d Implement audio capture in virtio-snd >> device >> -- >> 2.39.2 >> >