* [PATCH v10 00/11] Add VIRTIO sound card
@ 2023-09-29 14:08 Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 01/11] Add virtio-sound device stub Emmanouil Pitsidianakis
` (11 more replies)
0 siblings, 12 replies; 21+ messages in thread
From: Emmanouil Pitsidianakis @ 2023-09-29 14:08 UTC (permalink / raw)
To: qemu-devel
Cc: Emmanouil Pitsidianakis, Igor Skalkin, Anton Yakovlev,
Paolo Bonzini, Gerd Hoffmann, Michael S. Tsirkin,
Marcel Apfelbaum, Daniel P. Berrangé, Eduardo Habkost,
Marc-André Lureau, Volker Rümelin,
Kővágó, Zoltán, Alex Bennée,
Philippe Mathieu-Daudé, Mark Cave-Ayland
This patch series adds an audio device implementing the recent virtio
sound spec (1.2) and a corresponding PCI wrapper device.
v10 can be found online at:
https://gitlab.com/epilys/qemu/-/tree/virtio-snd-v10
Ref b720e00121878cb91c9690fd1f9ca609b9484346
Main differences with v9 patch series [^v9]
<cover.1694588927.git.manos.pitsidianakis@linaro.org>:
- Addressed [^v9] review comments.
- Copy buffer data just before playing it on the host instead of when
the IO message arrives. This in most cases takes care of the buffer
not being populated with data from the guest application when it
firsts arrives.
Previously:
[^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 | 49 +
hw/virtio/Kconfig | 5 +
hw/virtio/meson.build | 2 +
hw/virtio/trace-events | 20 +
hw/virtio/virtio-snd-pci.c | 93 ++
hw/virtio/virtio-snd.c | 1455 ++++++++++++++++++++++++++++
include/hw/virtio/virtio-snd.h | 235 +++++
softmmu/qdev-monitor.c | 1 +
10 files changed, 1868 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 v9:
1: 5173e2c243 = 1: 6e7bdf6dda Add virtio-sound device stub
2: dd3acea293 ! 2: 82138b9c7d Add virtio-sound-pci device
@@ hw/virtio/virtio-snd-pci.c (new)
+
+type_init(virtio_snd_pci_register);
+ ## hw/virtio/virtio-snd.c ##
+@@ hw/virtio/virtio-snd.c: static void
+ virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config)
+ {
+ VirtIOSound *s = VIRTIO_SND(vdev);
++ virtio_snd_config *sndconfig =
++ (virtio_snd_config *)config;
+ trace_virtio_snd_get_config(vdev,
+ s->snd_conf.jacks,
+ s->snd_conf.streams,
+ s->snd_conf.chmaps);
+
+- memcpy(config, &s->snd_conf, sizeof(s->snd_conf));
++ memcpy(sndconfig, &s->snd_conf, sizeof(s->snd_conf));
++ cpu_to_le32s(&sndconfig->jacks);
++ cpu_to_le32s(&sndconfig->streams);
++ cpu_to_le32s(&sndconfig->chmaps);
++
+ }
+
+ static void
+@@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
+ s->snd_conf.chmaps,
+ sndconfig->chmaps);
+
+- memcpy(&s->snd_conf, sndconfig, sizeof(s->snd_conf));
++ memcpy(&s->snd_conf, sndconfig, sizeof(virtio_snd_config));
++ le32_to_cpus(&s->snd_conf.jacks);
++ le32_to_cpus(&s->snd_conf.streams);
++ le32_to_cpus(&s->snd_conf.chmaps);
++
+ }
+
+ /*
+
## softmmu/qdev-monitor.c ##
@@ softmmu/qdev-monitor.c: static const QDevAlias qdev_alias_table[] = {
{ "virtio-serial-device", "virtio-serial", QEMU_ARCH_VIRTIO_MMIO },
3: a75dc75f24 ! 3: c1a2cb0304 virtio-sound: handle control messages and streams
@@ hw/virtio/trace-events: virtio_snd_vm_state_running(void) "vm state running"
+virtio_snd_handle_event(void) "event queue callback called"
## hw/virtio/virtio-snd.c ##
+@@
+ #define VIRTIO_SOUND_CHMAP_DEFAULT 0
+ #define VIRTIO_SOUND_HDA_FN_NID 0
+
++static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8)
++ | BIT(VIRTIO_SND_PCM_FMT_U8)
++ | BIT(VIRTIO_SND_PCM_FMT_S16)
++ | BIT(VIRTIO_SND_PCM_FMT_U16)
++ | BIT(VIRTIO_SND_PCM_FMT_S32)
++ | BIT(VIRTIO_SND_PCM_FMT_U32)
++ | BIT(VIRTIO_SND_PCM_FMT_FLOAT);
++
++static uint32_t supported_rates = BIT(VIRTIO_SND_PCM_RATE_5512)
++ | BIT(VIRTIO_SND_PCM_RATE_8000)
++ | BIT(VIRTIO_SND_PCM_RATE_11025)
++ | BIT(VIRTIO_SND_PCM_RATE_16000)
++ | BIT(VIRTIO_SND_PCM_RATE_22050)
++ | BIT(VIRTIO_SND_PCM_RATE_32000)
++ | BIT(VIRTIO_SND_PCM_RATE_44100)
++ | BIT(VIRTIO_SND_PCM_RATE_48000)
++ | BIT(VIRTIO_SND_PCM_RATE_64000)
++ | BIT(VIRTIO_SND_PCM_RATE_88200)
++ | BIT(VIRTIO_SND_PCM_RATE_96000)
++ | BIT(VIRTIO_SND_PCM_RATE_176400)
++ | BIT(VIRTIO_SND_PCM_RATE_192000)
++ | BIT(VIRTIO_SND_PCM_RATE_384000);
++
+ static const VMStateDescription vmstate_virtio_snd_device = {
+ .name = TYPE_VIRTIO_SND,
+ .version_id = VIRTIO_SOUND_VM_VERSION,
@@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
- memcpy(&s->snd_conf, sndconfig, sizeof(s->snd_conf));
+
}
+static void
@@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const uint8_t
+ g_free(cmd);
+}
+
++/*
++ * Get a specific stream from the virtio sound card device.
++ * Returns NULL if @stream_id is invalid or not allocated.
++ *
++ * @s: VirtIOSound device
++ * @stream_id: stream id
++ */
++static VirtIOSoundPCMStream *virtio_snd_pcm_get_stream(VirtIOSound *s,
++ uint32_t stream_id)
++{
++ return stream_id >= s->snd_conf.streams ? NULL :
++ s->pcm->streams[stream_id];
++}
++
++/*
++ * Get params for a specific stream.
++ *
++ * @s: VirtIOSound device
++ * @stream_id: stream id
++ */
++static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s,
++ uint32_t stream_id)
++{
++ return stream_id >= s->snd_conf.streams ? NULL
++ : &s->pcm->pcm_params[stream_id];
++}
++
++/*
++ * Set the given stream params.
++ * Called by both virtio_snd_handle_pcm_set_params and during device
++ * initialization.
++ * Returns the response status code. (VIRTIO_SND_S_*).
++ *
++ * @s: VirtIOSound device
++ * @params: The PCM params as defined in the virtio specification
++ */
++static
++uint32_t virtio_snd_set_pcm_params(VirtIOSound *s,
++ uint32_t stream_id,
++ virtio_snd_pcm_set_params *params)
++{
++ virtio_snd_pcm_set_params *st_params;
++
++ if (stream_id >= s->snd_conf.streams || s->pcm->pcm_params == NULL) {
++ /*
++ * TODO: do we need to set DEVICE_NEEDS_RESET?
++ */
++ virtio_error(VIRTIO_DEVICE(s), "Streams have not been initialized.\n");
++ return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
++ }
++
++ st_params = virtio_snd_pcm_get_params(s, stream_id);
++
++ if (params->channels < 1 || params->channels > AUDIO_MAX_CHANNELS) {
++ error_report("Number of channels is not supported.");
++ return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
++ }
++ if (!(supported_formats & BIT(params->format))) {
++ error_report("Stream format is not supported.");
++ return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
++ }
++ if (!(supported_rates & BIT(params->rate))) {
++ error_report("Stream rate is not supported.");
++ return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
++ }
++
++ st_params->buffer_bytes = le32_to_cpu(params->buffer_bytes);
++ st_params->period_bytes = le32_to_cpu(params->period_bytes);
++ st_params->features = le32_to_cpu(params->features);
++ /* the following are uint8_t, so there's no need to bswap the values. */
++ st_params->channels = params->channels;
++ st_params->format = params->format;
++ st_params->rate = params->rate;
++
++ return cpu_to_le32(VIRTIO_SND_S_OK);
++}
++
++/*
++ * Get a QEMU Audiosystem compatible format value from a VIRTIO_SND_PCM_FMT_*
++ */
++static AudioFormat virtio_snd_get_qemu_format(uint32_t format)
++{
++ #define CASE(FMT) \
++ case VIRTIO_SND_PCM_FMT_##FMT: \
++ return AUDIO_FORMAT_##FMT;
++
++ switch (format) {
++ CASE(U8)
++ CASE(S8)
++ CASE(U16)
++ CASE(S16)
++ CASE(U32)
++ CASE(S32)
++ case VIRTIO_SND_PCM_FMT_FLOAT:
++ return AUDIO_FORMAT_F32;
++ default:
++ g_assert_not_reached();
++ }
++
++ #undef CASE
++}
++
++/*
++ * Get a QEMU Audiosystem compatible frequency value from a
++ * VIRTIO_SND_PCM_RATE_*
++ */
++static uint32_t virtio_snd_get_qemu_freq(uint32_t rate)
++{
++ #define CASE(RATE) \
++ case VIRTIO_SND_PCM_RATE_##RATE: \
++ return RATE;
++
++ switch (rate) {
++ CASE(5512)
++ CASE(8000)
++ CASE(11025)
++ CASE(16000)
++ CASE(22050)
++ CASE(32000)
++ CASE(44100)
++ CASE(48000)
++ CASE(64000)
++ CASE(88200)
++ CASE(96000)
++ CASE(176400)
++ CASE(192000)
++ CASE(384000)
++ default:
++ g_assert_not_reached();
++ }
++
++ #undef CASE
++}
++
++/*
++ * Get QEMU Audiosystem compatible audsettings from virtio based pcm stream
++ * params.
++ */
++static void virtio_snd_get_qemu_audsettings(audsettings *as,
++ virtio_snd_pcm_set_params *params)
++{
++ 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 = target_words_bigendian() ? 1 : 0;
++}
++
++/*
++ * Close a stream and free all its resources.
++ *
++ * @stream: VirtIOSoundPCMStream *stream
++ */
++static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream)
++{
++}
++
+ /*
+- * Queue handler stub.
++ * Prepares a VirtIOSound card stream.
++ * Returns the response status code. (VIRTIO_SND_S_*).
++ *
++ * @s: VirtIOSound device
++ * @stream_id: stream id
++ */
++static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
++{
++ audsettings as;
++ virtio_snd_pcm_set_params *params;
++ VirtIOSoundPCMStream *stream;
++
++ if (s->pcm->streams == NULL ||
++ s->pcm->pcm_params == NULL ||
++ stream_id >= s->snd_conf.streams) {
++ return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
++ }
++
++ params = virtio_snd_pcm_get_params(s, stream_id);
++ if (params == NULL) {
++ return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
++ }
++
++ stream = virtio_snd_pcm_get_stream(s, stream_id);
++ if (stream == NULL) {
++ stream = g_new0(VirtIOSoundPCMStream, 1);
++ stream->active = false;
++ stream->id = stream_id;
++ stream->pcm = s->pcm;
++ stream->s = s;
++
++ /*
++ * stream_id >= s->snd_conf.streams was checked before so this is
++ * in-bounds
++ */
++ s->pcm->streams[stream_id] = stream;
++ }
++
++ virtio_snd_get_qemu_audsettings(&as, params);
++ stream->info.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 = VIRTIO_SOUND_HDA_FN_NID;
++ stream->info.features = 0;
++ stream->info.channels_min = 1;
++ stream->info.channels_max = as.nchannels;
++ stream->info.formats = supported_formats;
++ stream->info.rates = supported_rates;
++ stream->params = *params;
++
++ stream->positions[0] = VIRTIO_SND_CHMAP_FL;
++ stream->positions[1] = VIRTIO_SND_CHMAP_FR;
++ stream->as = as;
++
++ return cpu_to_le32(VIRTIO_SND_S_OK);
++}
++
+static const char *print_code(uint32_t code)
+{
+ #define CASE(CODE) \
@@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const uint8_t
+ cmd->elem->out_num,
+ 0,
+ &cmd->ctrl,
-+ sizeof(cmd->ctrl));
++ sizeof(virtio_snd_hdr));
+
-+ if (msg_sz != sizeof(cmd->ctrl)) {
++ if (msg_sz != sizeof(virtio_snd_hdr)) {
++ /*
++ * TODO: do we need to set DEVICE_NEEDS_RESET?
++ */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: virtio-snd command size incorrect %zu vs \
-+ %zu\n", __func__, msg_sz, sizeof(cmd->ctrl));
++ %zu\n", __func__, msg_sz, sizeof(virtio_snd_hdr));
+ return;
+ }
+
@@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const uint8_t
+ cmd->elem->in_num,
+ 0,
+ &cmd->resp,
-+ sizeof(cmd->resp));
-+ virtqueue_push(cmd->vq, cmd->elem, sizeof(cmd->elem));
++ sizeof(virtio_snd_hdr));
++ virtqueue_push(cmd->vq, cmd->elem, sizeof(virtio_snd_hdr));
+ virtio_notify(VIRTIO_DEVICE(s), cmd->vq);
+}
+
@@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const uint8_t
+ }
+}
+
- /*
-- * Queue handler stub.
++/*
+ * The control message handler. Pops an element from the control virtqueue,
+ * and stores them to VirtIOSound's cmdq queue and finally calls
+ * virtio_snd_process_cmdq() for processing.
@@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const uint8_t
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)
+ ERRP_GUARD();
VirtIOSound *vsnd = VIRTIO_SND(dev);
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
++ virtio_snd_pcm_set_params default_params = { 0 };
++ uint32_t status;
+ vsnd->pcm = NULL;
vsnd->vmstate =
@@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev, Error *
+ vsnd->pcm = g_new0(VirtIOSoundPCM, 1);
+ vsnd->pcm->snd = vsnd;
-+ vsnd->pcm->streams = g_new0(VirtIOSoundPCMStream *, vsnd->snd_conf.streams);
-+ vsnd->pcm->pcm_params = g_new0(virtio_snd_pcm_set_params, vsnd->snd_conf.streams);
++ vsnd->pcm->streams =
++ g_new0(VirtIOSoundPCMStream *, vsnd->snd_conf.streams);
++ vsnd->pcm->pcm_params =
++ g_new0(virtio_snd_pcm_set_params, vsnd->snd_conf.streams);
+
virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config));
virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1);
@@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev, Error **errp)
+
AUD_register_card("virtio-sound", &vsnd->card);
++ /* set default params for all streams */
++ default_params.features = 0;
++ default_params.buffer_bytes = cpu_to_le32(8192);
++ default_params.period_bytes = cpu_to_le32(2048);
++ default_params.channels = 2;
++ default_params.format = VIRTIO_SND_PCM_FMT_S16;
++ default_params.rate = VIRTIO_SND_PCM_RATE_48000;
vsnd->queues[VIRTIO_SND_VQ_CONTROL] =
- virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
+ virtio_add_queue(vdev, 64, virtio_snd_handle_ctrl);
@@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev, Error *
+ virtio_add_queue(vdev, 64, virtio_snd_handle_xfer);
+ qemu_mutex_init(&vsnd->cmdq_mutex);
+ QTAILQ_INIT(&vsnd->cmdq);
-+}
+
-+/*
-+ * Close the stream and free its resources.
-+ *
-+ * @stream: VirtIOSoundPCMStream *stream
-+ */
-+static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream)
-+{
++ for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
++ status = virtio_snd_set_pcm_params(vsnd, i, &default_params);
++ if (status != cpu_to_le32(VIRTIO_SND_S_OK)) {
++ error_setg(errp,
++ "Can't initalize stream params, device responded with %s.",
++ print_code(status));
++ return;
++ }
++ status = virtio_snd_pcm_prepare(vsnd, i);
++ if (status != cpu_to_le32(VIRTIO_SND_S_OK)) {
++ error_setg(errp,
++ "Can't prepare streams, device responded with %s.",
++ print_code(status));
++ return;
++ }
++ }
}
static void virtio_snd_unrealize(DeviceState *dev)
@@ include/hw/virtio/virtio-snd.h: typedef struct virtio_snd_pcm_xfer virtio_snd_pc
+
+struct VirtIOSoundPCM {
+ VirtIOSound *snd;
++ /*
++ * PCM parameters are a separate field instead of a VirtIOSoundPCMStream
++ * field, because the operation of PCM control requests is first
++ * VIRTIO_SND_R_PCM_SET_PARAMS and then VIRTIO_SND_R_PCM_PREPARE; this
++ * means that some times we get parameters without having an allocated
++ * stream yet.
++ */
+ virtio_snd_pcm_set_params *pcm_params;
+ VirtIOSoundPCMStream **streams;
+};
+
++struct VirtIOSoundPCMStream {
++ VirtIOSoundPCM *pcm;
++ virtio_snd_pcm_info info;
++ virtio_snd_pcm_set_params params;
++ uint32_t id;
++ /* channel position values (VIRTIO_SND_CHMAP_XXX) */
++ uint8_t positions[VIRTIO_SND_CHMAP_MAX_SIZE];
++ VirtIOSound *s;
++ bool flushing;
++ audsettings as;
++ union {
++ SWVoiceIn *in;
++ SWVoiceOut *out;
++ } voice;
++ bool active;
++};
++
+/*
+ * PCM stream state machine.
+ * -------------------------
@@ include/hw/virtio/virtio-snd.h: typedef struct virtio_snd_pcm_xfer virtio_snd_pc
+ *
+ * The CTRL message is finally handled in `process_cmd()`.
+ */
-+struct VirtIOSoundPCMStream {
-+ VirtIOSoundPCM *pcm;
-+ virtio_snd_pcm_info info;
-+ virtio_snd_pcm_set_params params;
-+ uint32_t id;
-+ /* channel position values (VIRTIO_SND_CHMAP_XXX) */
-+ uint8_t positions[VIRTIO_SND_CHMAP_MAX_SIZE];
-+ VirtIOSound *s;
-+ bool flushing;
-+ audsettings as;
-+ union {
-+ SWVoiceIn *in;
-+ SWVoiceOut *out;
-+ } voice;
-+ QemuMutex queue_mutex;
-+ QSIMPLEQ_HEAD(, VirtIOSoundPCMBlock) queue;
-+};
-+
+struct VirtIOSound {
VirtIODevice parent_obj;
4: 49eb46f184 < -: ---------- virtio-sound: set PCM stream parameters
5: 1f1dc7a200 ! 4: 28b2ecfa1f virtio-sound: handle VIRTIO_SND_R_PCM_INFO request
@@ hw/virtio/trace-events: virtio_snd_vm_state_stopped(void) "vm state stopped"
virtio_snd_handle_event(void) "event queue callback called"
## hw/virtio/virtio-snd.c ##
-@@
- #define VIRTIO_SOUND_CHMAP_DEFAULT 0
- #define VIRTIO_SOUND_HDA_FN_NID 0
-
-+static struct virtio_snd_info info_to_le32(struct virtio_snd_info val) {
-+ val.hda_fn_nid = cpu_to_le32(val.hda_fn_nid);
-+
-+ return val;
-+}
-+
-+static virtio_snd_pcm_info pcm_info_to_le32(virtio_snd_pcm_info val) {
-+ val.hdr = info_to_le32(val.hdr);
-+ val.features = cpu_to_le32(val.features);
-+ val.formats = cpu_to_le64(val.formats);
-+ val.rates = cpu_to_le64(val.rates);
-+
-+ return val;
-+}
-+
- static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8)
- | BIT(VIRTIO_SND_PCM_FMT_U8)
- | BIT(VIRTIO_SND_PCM_FMT_S16)
@@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s,
: &s->pcm->pcm_params[stream_id];
}
@@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_set_params *virtio_snd_pcm_get_par
+ virtio_snd_ctrl_command *cmd)
+{
+ uint32_t stream_id, start_id, count, size;
++ virtio_snd_pcm_info val;
+ virtio_snd_query_info req;
+ VirtIOSoundPCMStream *stream = NULL;
+ g_autofree virtio_snd_pcm_info *pcm_info = NULL;
@@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_set_params *virtio_snd_pcm_get_par
+ cmd->elem->out_num,
+ 0,
+ &req,
-+ sizeof(req));
++ sizeof(virtio_snd_query_info));
+
+ if (msg_sz != sizeof(virtio_snd_query_info)) {
++ /*
++ * TODO: do we need to set DEVICE_NEEDS_RESET?
++ */
++ qemu_log_mask(LOG_GUEST_ERROR,
++ "%s: virtio-snd command size incorrect %zu vs \
++ %zu\n", __func__, msg_sz, sizeof(virtio_snd_query_info));
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ return;
+ }
@@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_set_params *virtio_snd_pcm_get_par
+
+ if (iov_size(cmd->elem->in_sg, cmd->elem->in_num) <
+ sizeof(virtio_snd_hdr) + size * count) {
++ /*
++ * TODO: do we need to set DEVICE_NEEDS_RESET?
++ */
+ error_report("pcm info: buffer too small, got: %zu, needed: %zu",
+ iov_size(cmd->elem->in_sg, cmd->elem->in_num),
+ sizeof(virtio_snd_pcm_info));
@@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_set_params *virtio_snd_pcm_get_par
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ return;
+ }
-+ pcm_info[i] = pcm_info_to_le32(stream->info);
++ val = stream->info;
++ val.hdr.hda_fn_nid = cpu_to_le32(val.hdr.hda_fn_nid);
++ val.features = cpu_to_le32(val.features);
++ val.formats = cpu_to_le64(val.formats);
++ val.rates = cpu_to_le64(val.rates);
++ /*
++ * 5.14.6.6.2.1 Device Requirements: Stream Information The device MUST
++ * NOT set undefined feature, format, rate and direction values. The
++ * device MUST initialize the padding bytes to 0.
++ */
++ pcm_info[i] = val;
++ memset(&pcm_info[i].padding, 0, 5);
+ }
+
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
6: 7fbbe07fda ! 5: a52d20b2c3 virtio-sound: handle VIRTIO_SND_R_PCM_{START,STOP}
@@ hw/virtio/virtio-snd.c: static const char *print_code(uint32_t code)
+ cmd->elem->out_num,
+ 0,
+ &req,
-+ sizeof(req));
++ sizeof(virtio_snd_pcm_hdr));
+
+ if (msg_sz != sizeof(virtio_snd_pcm_hdr)) {
++ qemu_log_mask(LOG_GUEST_ERROR,
++ "%s: virtio-snd command size incorrect %zu vs \
++ %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_hdr));
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ return;
+ }
@@ hw/virtio/virtio-snd.c: static const char *print_code(uint32_t code)
+ trace_virtio_snd_handle_pcm_start_stop(start ? "VIRTIO_SND_R_PCM_START" :
+ "VIRTIO_SND_R_PCM_STOP", stream_id);
+ stream = virtio_snd_pcm_get_stream(s, stream_id);
-+ if (!stream) {
++ if (stream == NULL) {
+ error_report("Invalid stream id: %"PRIu32, req.stream_id);
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
++ return;
+ }
++ stream->active = start;
+}
+
/*
7: 92e65a6115 ! 6: 25fbb2eb25 virtio-sound: handle VIRTIO_SND_R_PCM_SET_PARAMS
@@ hw/virtio/virtio-snd.c: uint32_t virtio_snd_set_pcm_params(VirtIOSound *s,
+ cmd->elem->out_num,
+ 0,
+ &req,
-+ sizeof(req));
++ sizeof(virtio_snd_pcm_set_params));
+
+ if (msg_sz != sizeof(virtio_snd_pcm_set_params)) {
++ /*
++ * TODO: do we need to set DEVICE_NEEDS_RESET?
++ */
++ qemu_log_mask(LOG_GUEST_ERROR,
++ "%s: virtio-snd command size incorrect %zu vs \
++ %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_set_params));
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ return;
+ }
8: 173bd9dafe ! 7: 9e8d9923ba virtio-sound: handle VIRTIO_SND_R_PCM_PREPARE
@@ hw/virtio/virtio-snd.c: static const char *print_code(uint32_t code)
+ sizeof(stream_id));
+
+ stream_id = le32_to_cpu(stream_id);
-+ cmd->resp.code = msg_sz == sizeof(uint32_t)
++ cmd->resp.code = msg_sz == sizeof(stream_id)
+ ? virtio_snd_pcm_prepare(s, stream_id)
+ : cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+}
9: 6e05194e02 ! 8: b50c94decc virtio-sound: handle VIRTIO_SND_R_PCM_RELEASE
@@ hw/virtio/trace-events: virtio_snd_handle_pcm_set_params(uint32_t stream) "VIRTI
## hw/virtio/virtio-snd.c ##
@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
- }
+ stream->active = start;
}
+/*
@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_start_stop(VirtIOSound
+ &stream_id,
+ sizeof(stream_id));
+
-+ if (msg_sz != sizeof(uint32_t)) {
++ if (msg_sz != sizeof(stream_id)) {
++ /*
++ * TODO: do we need to set DEVICE_NEEDS_RESET?
++ */
++ qemu_log_mask(LOG_GUEST_ERROR,
++ "%s: virtio-snd command size incorrect %zu vs \
++ %zu\n", __func__, msg_sz, sizeof(stream_id));
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ return;
+ }
@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_start_stop(VirtIOSound
+ stream_id = le32_to_cpu(stream_id);
+ trace_virtio_snd_handle_pcm_release(stream_id);
+ stream = virtio_snd_pcm_get_stream(s, stream_id);
-+ if (!stream) {
++ if (stream == NULL) {
++ /*
++ * TODO: do we need to set DEVICE_NEEDS_RESET?
++ */
+ error_report("already released stream %"PRIu32, stream_id);
+ virtio_error(VIRTIO_DEVICE(s),
+ "already released stream %"PRIu32,
10: a241f8cf92 ! 9: 4cbb908742 virtio-sound: implement audio output (TX)
@@ Commit message
Handle output IO messages in the transmit (TX) virtqueue.
- It allocates a VirtIOSoundPCMBlock for each IO message and copies the
+ It allocates a VirtIOSoundPCMBuffer for each IO message and copies the
data buffer to it. When the IO buffer is written to the host's sound
card, the guest will be notified that it has been consumed.
@@ hw/virtio/trace-events: virtio_snd_handle_pcm_release(uint32_t stream) "VIRTIO_S
+virtio_snd_handle_xfer(void) "tx/rx queue callback called"
## hw/virtio/virtio-snd.c ##
-@@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_info pcm_info_to_le32(virtio_snd_pcm_info val) {
- return val;
- }
+@@
+ #define VIRTIO_SOUND_CHMAP_DEFAULT 0
+ #define VIRTIO_SOUND_HDA_FN_NID 0
+static void virtio_snd_pcm_out_cb(void *data, int available);
+static void virtio_snd_process_cmdq(VirtIOSound *s);
@@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_info pcm_info_to_le32(virtio_snd_p
| BIT(VIRTIO_SND_PCM_FMT_U8)
| BIT(VIRTIO_SND_PCM_FMT_S16)
@@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
- memcpy(&s->snd_conf, sndconfig, sizeof(s->snd_conf));
+
}
+static void
-+virtio_snd_pcm_block_free(VirtIOSoundPCMBlock *block)
++virtio_snd_pcm_buffer_free(VirtIOSoundPCMBuffer *buffer)
+{
-+ g_free(block->elem);
-+ g_free(block);
++ g_free(buffer->elem);
++ g_free(buffer);
+}
+
static void
@@ hw/virtio/virtio-snd.c: static void virtio_snd_get_qemu_audsettings(audsettings
*/
static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream)
{
-+ VirtIOSoundPCMBlock *block, *next;
-+
+ if (stream) {
-+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
-+ QSIMPLEQ_FOREACH_SAFE(block, &stream->queue, entry, next) {
-+ virtqueue_push(block->vq,
-+ block->elem,
-+ sizeof(block->elem));
-+ virtio_notify(VIRTIO_DEVICE(stream->s),
-+ block->vq);
-+ QSIMPLEQ_REMOVE_HEAD(&stream->queue, entry);
-+ virtio_snd_pcm_block_free(block);
-+ }
-+ }
+ if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
++ virtio_snd_pcm_flush(stream);
+ AUD_close_out(&stream->pcm->snd->card, stream->voice.out);
+ stream->voice.out = NULL;
+ }
@@ hw/virtio/virtio-snd.c: static void virtio_snd_get_qemu_audsettings(audsettings
}
/*
+@@ hw/virtio/virtio-snd.c: static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
+ stream->id = stream_id;
+ stream->pcm = s->pcm;
+ stream->s = s;
++ qemu_mutex_init(&stream->queue_mutex);
++ QSIMPLEQ_INIT(&stream->queue);
++ QSIMPLEQ_INIT(&stream->invalid);
+
+ /*
+ * stream_id >= s->snd_conf.streams was checked before so this is
@@ hw/virtio/virtio-snd.c: static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
stream->positions[1] = VIRTIO_SND_CHMAP_FR;
stream->as = as;
@@ hw/virtio/virtio-snd.c: static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, u
return cpu_to_le32(VIRTIO_SND_S_OK);
}
-@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
- bool start)
- {
- VirtIOSoundPCMStream *stream;
-+ VirtIOSoundPCMBlock *block, *next;
- virtio_snd_pcm_hdr req;
- uint32_t stream_id;
- size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
trace_virtio_snd_handle_pcm_start_stop(start ? "VIRTIO_SND_R_PCM_START" :
"VIRTIO_SND_R_PCM_STOP", stream_id);
+
stream = virtio_snd_pcm_get_stream(s, stream_id);
-- if (!stream) {
+- if (stream == NULL) {
- error_report("Invalid stream id: %"PRIu32, req.stream_id);
+ if (stream) {
++ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
++ stream->active = start;
++ }
+ if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
+ AUD_set_active_out(stream->voice.out, start);
+ }
-+ /* remove previous buffers. */
-+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
-+ QSIMPLEQ_FOREACH_SAFE(block, &stream->queue, entry, next) {
-+ virtqueue_push(block->vq,
-+ block->elem,
-+ sizeof(block->elem));
-+ virtio_notify(VIRTIO_DEVICE(stream->s), block->vq);
-+ QSIMPLEQ_REMOVE_HEAD(&stream->queue, entry);
-+ virtio_snd_pcm_block_free(block);
-+ }
-+ }
+ } else {
+ error_report("Invalid stream id: %"PRIu32, stream_id);
cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ return;
}
+@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
}
/*
@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_start_stop(VirtIOSound
+ */
+static size_t virtio_snd_pcm_get_io_msgs_count(VirtIOSoundPCMStream *stream)
+{
-+ VirtIOSoundPCMBlock *block;
-+ VirtIOSoundPCMBlock *next;
++ VirtIOSoundPCMBuffer *buffer, *next;
+ size_t count = 0;
+
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
-+ QSIMPLEQ_FOREACH_SAFE(block, &stream->queue, entry, next) {
++ QSIMPLEQ_FOREACH_SAFE(buffer, &stream->queue, entry, next) {
++ count += 1;
++ }
++ QSIMPLEQ_FOREACH_SAFE(buffer, &stream->invalid, entry, next) {
+ count += 1;
+ }
+ }
@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_release(VirtIOSound *s
+ * - The device MUST NOT complete the control request while there
+ * are pending I/O messages for the specified stream ID.
+ */
-+ virtio_snd_process_cmdq(stream->s);
+ trace_virtio_snd_pcm_stream_flush(stream_id);
+ virtio_snd_pcm_flush(stream);
+ }
@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_event(VirtIODevice *vdev,
+{
+ VirtIOSound *s = VIRTIO_SND(vdev);
+ VirtIOSoundPCMStream *stream = NULL;
-+ VirtIOSoundPCMBlock *block;
++ VirtIOSoundPCMBuffer *buffer;
+ 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
++ * return them after the for loop.
++ */
++ bool must_empty_invalid_queue = false;
+
-+ trace_virtio_snd_handle_xfer();
++ if (!virtio_queue_ready(vq)) {
++ return;
++ }
++ trace_virtio_snd_handle_tx_xfer();
+
+ for (;;) {
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_event(VirtIODevice *vdev,
+ }
+ /* get the message hdr object */
+ msg_sz = iov_to_buf(elem->out_sg,
-+ elem->out_num,
-+ 0,
-+ &hdr,
-+ sizeof(hdr));
-+ if (msg_sz != sizeof(hdr)) {
++ elem->out_num,
++ 0,
++ &hdr,
++ sizeof(virtio_snd_pcm_xfer));
++ if (msg_sz != sizeof(virtio_snd_pcm_xfer)) {
+ goto tx_err;
+ }
+ stream_id = le32_to_cpu(hdr.stream_id);
+
+ if (stream_id >= s->snd_conf.streams
-+ || !s->pcm->streams[stream_id]) {
++ || s->pcm->streams[stream_id] == NULL) {
+ goto tx_err;
+ }
+
+ stream = s->pcm->streams[stream_id];
-+ if (!stream || stream->info.direction != VIRTIO_SND_D_OUTPUT) {
++ if (stream->info.direction != VIRTIO_SND_D_OUTPUT) {
+ goto tx_err;
+ }
+
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
-+ size = iov_size(elem->out_sg, elem->out_num) -
-+ sizeof(virtio_snd_pcm_xfer);
-+ block = g_malloc0(sizeof(VirtIOSoundPCMBlock) + size);
-+ block->elem = elem;
-+ block->vq = vq;
-+ block->size = size;
-+ block->offset = 0;
++ size = iov_size(elem->out_sg, elem->out_num);
+
-+ iov_to_buf(elem->out_sg,
-+ elem->out_num,
-+ sizeof(virtio_snd_pcm_xfer),
-+ block->data,
-+ size);
++ buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
++ buffer->elem = elem;
++ buffer->stale = true;
++ buffer->vq = vq;
++ buffer->size = size;
++ buffer->offset = sizeof(virtio_snd_pcm_xfer);
+
-+ QSIMPLEQ_INSERT_TAIL(&stream->queue, block, entry);
++ QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry);
+ }
+ continue;
+
+tx_err:
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
-+ resp.status = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
-+ iov_from_buf(elem->in_sg,
-+ elem->in_num,
-+ 0,
-+ &resp,
-+ sizeof(resp));
-+ virtqueue_push(vq, elem, sizeof(elem));
-+ break;
++ must_empty_invalid_queue = true;
++ buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer));
++ buffer->elem = elem;
++ buffer->vq = vq;
++ QSIMPLEQ_INSERT_TAIL(&stream->invalid, buffer, entry);
+ }
+ }
+
-+ /*
-+ * Notify vq about virtio_snd_pcm_status responses.
-+ * Buffer responses must be notified separately later.
-+ */
-+ virtio_notify(VIRTIO_DEVICE(s), vq);
++ 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);
++ }
++ }
+}
+
/*
@@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev, Error *
+static void virtio_snd_pcm_out_cb(void *data, int available)
+{
+ VirtIOSoundPCMStream *stream = data;
-+ VirtIOSoundPCMBlock *block;
++ VirtIOSoundPCMBuffer *buffer;
+ size_t size;
+
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
+ while (!QSIMPLEQ_EMPTY(&stream->queue)) {
-+ block = QSIMPLEQ_FIRST(&stream->queue);
-+
++ buffer = QSIMPLEQ_FIRST(&stream->queue);
++ if (!virtio_queue_ready(buffer->vq)) {
++ return;
++ }
++ if (!stream->active) {
++ /* Stream has stopped, so do not perform AUD_write. */
++ goto return_tx_buffer;
++ }
++ if (buffer->stale) {
++ iov_to_buf(buffer->elem->out_sg,
++ buffer->elem->out_num,
++ buffer->offset,
++ buffer->data,
++ buffer->size);
++ buffer->stale = false;
++ }
+ for (;;) {
+ size = AUD_write(stream->voice.out,
-+ block->data + block->offset,
-+ MIN(block->size, available));
-+ assert(size <= MIN(block->size, available));
++ buffer->data + buffer->offset,
++ MIN(buffer->size, available));
++ assert(size <= MIN(buffer->size, available));
+ if (size == 0) {
+ /* break out of both loops */
+ available = 0;
+ break;
+ }
-+ block->size -= size;
-+ block->offset += size;
++ buffer->size -= size;
++ buffer->offset += size;
+ available -= size;
-+ if (!block->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(block->elem->in_sg,
-+ block->elem->in_num,
++ iov_from_buf(buffer->elem->in_sg,
++ buffer->elem->in_num,
+ 0,
+ &resp,
-+ sizeof(resp));
-+ virtqueue_push(block->vq,
-+ block->elem,
-+ sizeof(block->elem));
-+ virtio_notify(VIRTIO_DEVICE(stream->s), block->vq);
++ 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_block_free(block);
++ virtio_snd_pcm_buffer_free(buffer);
+ break;
+ }
-+
+ if (!available) {
+ break;
+ }
@@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev, Error *
+ */
+static void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream)
+{
-+ VirtIOSoundPCMBlock *block;
-+ VirtIOSoundPCMBlock *next;
++ VirtIOSoundPCMBuffer *buffer, *next;
+
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
-+ QSIMPLEQ_FOREACH_SAFE(block, &stream->queue, entry, next) {
-+ AUD_write(stream->voice.out, block->data + block->offset, block->size);
-+ virtqueue_push(block->vq, block->elem, sizeof(block->elem));
-+ virtio_notify(VIRTIO_DEVICE(stream->s), block->vq);
-+ QSIMPLEQ_REMOVE(&stream->queue, block, VirtIOSoundPCMBlock, entry);
++ 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);
+ }
+ }
+}
@@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev, Error *
static void virtio_snd_unrealize(DeviceState *dev)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+@@ hw/virtio/virtio-snd.c: static void virtio_snd_unrealize(DeviceState *dev)
+ if (stream) {
+ virtio_snd_process_cmdq(stream->s);
+ virtio_snd_pcm_close(stream);
++ qemu_mutex_destroy(&stream->queue_mutex);
+ g_free(stream);
+ }
+ }
## include/hw/virtio/virtio-snd.h ##
@@ include/hw/virtio/virtio-snd.h: typedef struct virtio_snd_ctrl_command virtio_snd_ctrl_command;
typedef struct VirtIOSoundPCM VirtIOSoundPCM;
-+typedef struct VirtIOSoundPCMBlock VirtIOSoundPCMBlock;
++typedef struct VirtIOSoundPCMBuffer VirtIOSoundPCMBuffer;
+
-+struct VirtIOSoundPCMBlock {
-+ QSIMPLEQ_ENTRY(VirtIOSoundPCMBlock) entry;
++/*
++ * The VirtIO sound spec reuses layouts and values from the High Definition
++ * Audio spec (virtio/v1.2: 5.14 Sound Device). This struct handles each I/O
++ * message's buffer (virtio/v1.2: 5.14.6.8 PCM I/O Messages).
++ *
++ * 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.
++ *
++ * VirtIOSoundPCMBuffer has a dynamic size since it includes the raw PCM data
++ * in its allocation. It must be initialized and destroyed as follows:
++ *
++ * size_t size = [[derived from owned VQ element descriptor sizes]];
++ * buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
++ * buffer->elem = [[owned VQ element]];
++ *
++ * [..]
++ *
++ * g_free(buffer->elem);
++ * g_free(buffer);
++ */
++struct VirtIOSoundPCMBuffer {
++ QSIMPLEQ_ENTRY(VirtIOSoundPCMBuffer) entry;
+ VirtQueueElement *elem;
+ VirtQueue *vq;
+ size_t size;
++ /*
++ * In TX / Plaback, `offset` represents the first unused position inside
++ * `data`. If `offset == size` then there are no unused data left.
++ */
+ uint64_t offset;
++ /* Used for the TX queue for lazy I/O copy from `elem` */
++ bool stale;
++ /*
++ * 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
++ * or write-only size when allocating VirtIOSoundPCMBuffer.
++ */
+ uint8_t data[];
+};
+
struct VirtIOSoundPCM {
VirtIOSound *snd;
- virtio_snd_pcm_set_params *pcm_params;
+ /*
+@@ include/hw/virtio/virtio-snd.h: struct VirtIOSoundPCMStream {
+ SWVoiceIn *in;
+ SWVoiceOut *out;
+ } voice;
++ QemuMutex queue_mutex;
+ bool active;
++ QSIMPLEQ_HEAD(, VirtIOSoundPCMBuffer) queue;
++ QSIMPLEQ_HEAD(, VirtIOSoundPCMBuffer) invalid;
+ };
+
+ /*
11: 6f3526e067 ! 10: 992b0d5ff4 virtio-sound: implement audio capture (RX)
@@ hw/virtio/virtio-snd.c
#define VIRTIO_SOUND_CHMAP_DEFAULT 0
#define VIRTIO_SOUND_HDA_FN_NID 0
-@@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_info pcm_info_to_le32(virtio_snd_pcm_info val) {
-
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);
@@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_info pcm_info_to_le32(virtio_snd_p
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)
+ {
+ if (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 uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, u
+ stream,
+ virtio_snd_pcm_in_cb,
+ &as);
++ AUD_set_volume_in(stream->voice.in, 0, 255, 255);
}
return cpu_to_le32(VIRTIO_SND_S_OK);
@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
- if (stream) {
+ }
if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
AUD_set_active_out(stream->voice.out, start);
+ } else {
+ AUD_set_active_in(stream->voice.in, start);
}
- /* remove previous buffers. */
- WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
+ } 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.
*/
- virtio_snd_process_cmdq(stream->s);
trace_virtio_snd_pcm_stream_flush(stream_id);
- virtio_snd_pcm_flush(stream);
+ if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
@@ 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)
- virtio_snd_pcm_status resp = { 0 };
- uint32_t stream_id;
-
-- 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:
}
@@ hw/virtio/virtio-snd.c: tx_err:
+{
+ VirtIOSound *s = VIRTIO_SND(vdev);
+ VirtIOSoundPCMStream *stream = NULL;
-+ VirtIOSoundPCMBlock *block;
++ VirtIOSoundPCMBuffer *buffer;
+ 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
++ * return them after the for loop.
++ */
++ bool must_empty_invalid_queue = false;
+
++ if (!virtio_queue_ready(vq)) {
++ return;
++ }
+ trace_virtio_snd_handle_rx_xfer();
+
+ for (;;) {
@@ hw/virtio/virtio-snd.c: tx_err:
+ elem->out_num,
+ 0,
+ &hdr,
-+ sizeof(hdr));
-+ if (msg_sz != sizeof(hdr)) {
++ sizeof(virtio_snd_pcm_xfer));
++ if (msg_sz != sizeof(virtio_snd_pcm_xfer)) {
+ goto rx_err;
+ }
+ stream_id = le32_to_cpu(hdr.stream_id);
@@ hw/virtio/virtio-snd.c: tx_err:
+ }
+
+ stream = s->pcm->streams[stream_id];
-+ if (!stream || stream->info.direction != VIRTIO_SND_D_INPUT) {
++ if (stream == NULL || stream->info.direction != VIRTIO_SND_D_INPUT) {
+ goto rx_err;
+ }
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
+ size = iov_size(elem->in_sg, elem->in_num) -
+ sizeof(virtio_snd_pcm_status);
-+ block = g_malloc0(sizeof(VirtIOSoundPCMBlock) + size);
-+ block->elem = elem;
-+ block->vq = vq;
-+ block->size = 0;
-+ block->offset = 0;
-+ QSIMPLEQ_INSERT_TAIL(&stream->queue, block, entry);
++ buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
++ buffer->elem = elem;
++ buffer->vq = vq;
++ buffer->size = 0;
++ buffer->offset = 0;
++ QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry);
+ }
+ continue;
+
+rx_err:
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
-+ resp.status = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
-+ iov_from_buf(elem->in_sg,
-+ elem->in_num,
-+ 0,
-+ &resp,
-+ sizeof(resp));
++ must_empty_invalid_queue = true;
++ buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer));
++ buffer->elem = elem;
++ buffer->vq = vq;
++ QSIMPLEQ_INSERT_TAIL(&stream->invalid, buffer, entry);
+ }
+ }
+
-+ /*
-+ * Notify vq about virtio_snd_pcm_status responses.
-+ * Buffer responses must be notified separately later.
-+ */
-+ virtio_notify(VIRTIO_DEVICE(s), vq);
++ 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);
++ }
++ }
+}
static uint64_t get_features(VirtIODevice *vdev, uint64_t features,
@@ 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: static void virtio_snd_pcm_out_cb(void *data, int available)
+@@ hw/virtio/virtio-snd.c: return_tx_buffer:
}
/*
@@ hw/virtio/virtio-snd.c: static void virtio_snd_pcm_out_cb(void *data, int availa
-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;
- VirtIOSoundPCMBlock *block;
-- VirtIOSoundPCMBlock *next;
++ VirtIOSoundPCMBuffer *buffer;
+ virtio_snd_pcm_status resp = { 0 };
+ size_t size;
WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
-- QSIMPLEQ_FOREACH_SAFE(block, &stream->queue, entry, next) {
-- AUD_write(stream->voice.out, block->data + block->offset, block->size);
-- virtqueue_push(block->vq, block->elem, sizeof(block->elem));
-- virtio_notify(VIRTIO_DEVICE(stream->s), block->vq);
-- QSIMPLEQ_REMOVE(&stream->queue, block, VirtIOSoundPCMBlock, entry);
+- 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)) {
-+ block = QSIMPLEQ_FIRST(&stream->queue);
++ buffer = QSIMPLEQ_FIRST(&stream->queue);
++ if (!virtio_queue_ready(buffer->vq)) {
++ return;
++ }
++ if (!stream->active) {
++ /* Stream has stopped, so do not perform AUD_read. */
++ goto return_rx_buffer;
++ }
+
+ for (;;) {
+ size = AUD_read(stream->voice.in,
-+ block->data + block->size,
-+ MIN(available, (stream->params.period_bytes - block->size)));
++ buffer->data + buffer->size,
++ MIN(available, (stream->params.period_bytes -
++ buffer->size)));
+ if (!size) {
+ available = 0;
+ break;
+ }
-+ block->size += size;
++ buffer->size += size;
+ available -= size;
-+ if (block->size >= stream->params.period_bytes) {
++ 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(block->elem->in_sg,
-+ block->elem->in_num,
++ iov_from_buf(buffer->elem->in_sg,
++ buffer->elem->in_num,
+ 0,
-+ block->data,
-+ stream->params.period_bytes);
-+ iov_from_buf(block->elem->in_sg,
-+ block->elem->in_num,
-+ block->size,
++ buffer->data,
++ buffer->size);
++ iov_from_buf(buffer->elem->in_sg,
++ buffer->elem->in_num,
++ buffer->size,
+ &resp,
+ sizeof(resp));
-+ virtqueue_push(block->vq,
-+ block->elem,
-+ sizeof(block->elem));
-+ virtio_notify(VIRTIO_DEVICE(stream->s),
-+ block->vq);
++ 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_block_free(block);
++ virtio_snd_pcm_buffer_free(buffer);
+ break;
+ }
+ if (!available) {
@@ hw/virtio/virtio-snd.c: static void virtio_snd_pcm_out_cb(void *data, int availa
}
}
-+#define virtio_snd_pcm_flush(AUD_CB) \
-+ VirtIOSoundPCMBlock *block; \
-+ VirtIOSoundPCMBlock *next; \
-+ virtio_snd_pcm_status resp = { 0 }; \
-+ resp.status = cpu_to_le32(VIRTIO_SND_S_OK); \
-+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { \
-+ QSIMPLEQ_FOREACH_SAFE(block, &stream->queue, entry, next) { \
-+ do { \
-+ AUD_CB; \
-+ } while (0) \
-+ ; \
-+ virtqueue_push(block->vq, block->elem, sizeof(block->elem));\
-+ virtio_notify(VIRTIO_DEVICE(stream->s), block->vq); \
-+ QSIMPLEQ_REMOVE(&stream->queue, \
-+ block, \
-+ VirtIOSoundPCMBlock, \
-+ entry); \
-+ virtio_snd_pcm_block_free(block); \
-+ } \
-+ } \
++#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); \
++ } \
++ }
+
+
+/*
@@ hw/virtio/virtio-snd.c: static void virtio_snd_pcm_out_cb(void *data, int availa
+ */
+static void virtio_snd_pcm_out_flush(VirtIOSoundPCMStream *stream)
+{
-+ /* We should flush the buffers as soon as possible, because it is a
++ /*
++ * 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(
-+ iov_from_buf(block->elem->in_sg,
-+ block->elem->in_num,
++ 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(resp));
++ sizeof(virtio_snd_pcm_status));
+ );
+}
+
@@ hw/virtio/virtio-snd.c: static void virtio_snd_pcm_out_cb(void *data, int availa
+static void virtio_snd_pcm_in_flush(VirtIOSoundPCMStream *stream)
+{
+ virtio_snd_pcm_flush(
-+ iov_from_buf(block->elem->in_sg,
-+ block->elem->in_num,
++ len = sizeof(virtio_snd_pcm_status) + buffer->size;
++ iov_from_buf(buffer->elem->in_sg,
++ buffer->elem->in_num,
+ 0,
-+ block->data,
-+ block->size);
-+ iov_from_buf(block->elem->in_sg,
-+ block->elem->in_num,
-+ block->size,
++ buffer->data,
++ buffer->size);
++ iov_from_buf(buffer->elem->in_sg,
++ buffer->elem->in_num,
++ buffer->size,
+ &resp,
-+ sizeof(resp));
++ sizeof(virtio_snd_pcm_status));
+ );
+}
+
static void virtio_snd_unrealize(DeviceState *dev)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
-@@ hw/virtio/virtio-snd.c: static void virtio_snd_unrealize(DeviceState *dev)
- virtio_cleanup(vdev);
- }
-
--
- static void virtio_snd_reset(VirtIODevice *vdev)
- {
- VirtIOSound *s = VIRTIO_SND(vdev);
12: 06e6b17186 ! 11: b720e00121 docs/system: add basic virtio-snd documentation
@@ Commit message
Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
+ ## MAINTAINERS ##
+@@ MAINTAINERS: M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+ S: Supported
+ F: hw/virtio/virtio-snd*.c
+ F: include/hw/virtio/virtio-snd.h
++F: docs/system/devices/virtio-snd.rst
+
+ nvme
+ M: Keith Busch <kbusch@kernel.org>
+
## docs/system/device-emulation.rst ##
@@ docs/system/device-emulation.rst: Emulated Devices
devices/usb.rst
base-commit: 36e9aab3c569d4c9ad780473596e18479838d1aa
--
2.39.2
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v10 01/11] Add virtio-sound device stub
2023-09-29 14:08 [PATCH v10 00/11] Add VIRTIO sound card Emmanouil Pitsidianakis
@ 2023-09-29 14:08 ` Emmanouil Pitsidianakis
2023-10-03 14:13 ` Michael S. Tsirkin
2023-09-29 14:08 ` [PATCH v10 02/11] Add virtio-sound-pci device Emmanouil Pitsidianakis
` (10 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Emmanouil Pitsidianakis @ 2023-09-29 14:08 UTC (permalink / raw)
To: qemu-devel
Cc: Emmanouil Pitsidianakis, Igor Skalkin, Anton Yakovlev,
Paolo Bonzini, Gerd Hoffmann, Michael S. Tsirkin,
Marcel Apfelbaum, Daniel P. Berrangé, Eduardo Habkost,
Marc-André Lureau, Volker Rümelin,
Kővágó, Zoltán, Alex Bennée,
Philippe Mathieu-Daudé, Mark Cave-Ayland
Add a new VIRTIO device for the virtio sound device id. Functionality
will be added in the following commits.
Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Igor Skalkin <Igor.Skalkin@opensynergy.com>
Signed-off-by: Anton Yakovlev <Anton.Yakovlev@opensynergy.com>
Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
---
MAINTAINERS | 6 +
hw/virtio/Kconfig | 5 +
hw/virtio/meson.build | 1 +
hw/virtio/trace-events | 9 ++
hw/virtio/virtio-snd.c | 223 +++++++++++++++++++++++++++++++++
include/hw/virtio/virtio-snd.h | 79 ++++++++++++
6 files changed, 323 insertions(+)
create mode 100644 hw/virtio/virtio-snd.c
create mode 100644 include/hw/virtio/virtio-snd.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 355b1960ce..81ca61e90b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2241,6 +2241,12 @@ 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>
diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig
index 92c9cf6c96..d6f20657b3 100644
--- a/hw/virtio/Kconfig
+++ b/hw/virtio/Kconfig
@@ -17,6 +17,11 @@ config VIRTIO_PCI
depends on PCI
select VIRTIO
+config VIRTIO_SND
+ bool
+ default y
+ depends on VIRTIO
+
config VIRTIO_MMIO
bool
select VIRTIO
diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build
index 13e7c6c272..120d4bfa0a 100644
--- a/hw/virtio/meson.build
+++ b/hw/virtio/meson.build
@@ -31,6 +31,7 @@ specific_virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock.c
specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_VSOCK', if_true: files('vhost-user-vsock.c'))
specific_virtio_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng.c'))
specific_virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c'))
+specific_virtio_ss.add(when: 'CONFIG_VIRTIO_SND', if_true: files('virtio-snd.c'))
specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_I2C', if_true: files('vhost-user-i2c.c'))
specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_RNG', if_true: files('vhost-user-rng.c'))
specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_GPIO', if_true: files('vhost-user-gpio.c'))
diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
index 7109cf1a3b..3ed7da35f2 100644
--- a/hw/virtio/trace-events
+++ b/hw/virtio/trace-events
@@ -154,3 +154,12 @@ virtio_pmem_flush_done(int type) "fsync return=%d"
virtio_gpio_start(void) "start"
virtio_gpio_stop(void) "stop"
virtio_gpio_set_status(uint8_t status) "0x%x"
+
+#virtio-snd.c
+virtio_snd_get_config(void *vdev, uint32_t jacks, uint32_t streams, uint32_t chmaps) "snd %p: get_config jacks=%"PRIu32" streams=%"PRIu32" chmaps=%"PRIu32""
+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 %"PRIu32"->%"PRIu32", streams from %"PRIu32"->%"PRIu32", chmaps from %"PRIu32"->%"PRIu32
+virtio_snd_get_features(void *vdev, uint64_t features) "snd %p: get_features 0x%"PRIx64
+virtio_snd_vm_state_running(void) "vm state running"
+virtio_snd_vm_state_stopped(void) "vm state stopped"
+virtio_snd_realize(void *snd) "snd %p: realize"
+virtio_snd_unrealize(void *snd) "snd %p: unrealize"
diff --git a/hw/virtio/virtio-snd.c b/hw/virtio/virtio-snd.c
new file mode 100644
index 0000000000..a8204e8a02
--- /dev/null
+++ b/hw/virtio/virtio-snd.c
@@ -0,0 +1,223 @@
+/*
+ * VIRTIO Sound Device conforming to
+ *
+ * "Virtual I/O Device (VIRTIO) Version 1.2
+ * Committee Specification Draft 01
+ * 09 May 2022"
+ *
+ * <https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-52900014>
+ *
+ * Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
+ * Copyright (C) 2019 OpenSynergy GmbH
+ *
+ * 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 "qemu/iov.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "include/qemu/lockable.h"
+#include "sysemu/runstate.h"
+#include "trace.h"
+#include "qapi/error.h"
+#include "hw/virtio/virtio-snd.h"
+#include "hw/core/cpu.h"
+
+#define VIRTIO_SOUND_VM_VERSION 1
+#define VIRTIO_SOUND_JACK_DEFAULT 0
+#define VIRTIO_SOUND_STREAM_DEFAULT 1
+#define VIRTIO_SOUND_CHMAP_DEFAULT 0
+#define VIRTIO_SOUND_HDA_FN_NID 0
+
+static const VMStateDescription vmstate_virtio_snd_device = {
+ .name = TYPE_VIRTIO_SND,
+ .version_id = VIRTIO_SOUND_VM_VERSION,
+ .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
+};
+
+static const VMStateDescription vmstate_virtio_snd = {
+ .name = "virtio-sound",
+ .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
+ .version_id = VIRTIO_SOUND_VM_VERSION,
+ .fields = (VMStateField[]) {
+ VMSTATE_VIRTIO_DEVICE,
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static Property virtio_snd_properties[] = {
+ DEFINE_AUDIO_PROPERTIES(VirtIOSound, card),
+ DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks,
+ VIRTIO_SOUND_JACK_DEFAULT),
+ DEFINE_PROP_UINT32("streams", VirtIOSound, snd_conf.streams,
+ VIRTIO_SOUND_STREAM_DEFAULT),
+ DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps,
+ VIRTIO_SOUND_CHMAP_DEFAULT),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void
+virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config)
+{
+ VirtIOSound *s = VIRTIO_SND(vdev);
+ trace_virtio_snd_get_config(vdev,
+ s->snd_conf.jacks,
+ s->snd_conf.streams,
+ s->snd_conf.chmaps);
+
+ memcpy(config, &s->snd_conf, sizeof(s->snd_conf));
+}
+
+static void
+virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
+{
+ VirtIOSound *s = VIRTIO_SND(vdev);
+ const virtio_snd_config *sndconfig =
+ (const virtio_snd_config *)config;
+
+
+ trace_virtio_snd_set_config(vdev,
+ s->snd_conf.jacks,
+ sndconfig->jacks,
+ s->snd_conf.streams,
+ sndconfig->streams,
+ s->snd_conf.chmaps,
+ sndconfig->chmaps);
+
+ memcpy(&s->snd_conf, sndconfig, sizeof(s->snd_conf));
+}
+
+/*
+ * Queue handler stub.
+ *
+ * @vdev: VirtIOSound device
+ * @vq: virtqueue
+ */
+static void virtio_snd_handle_queue(VirtIODevice *vdev, VirtQueue *vq) {}
+
+static uint64_t get_features(VirtIODevice *vdev, uint64_t features,
+ Error **errp)
+{
+ /*
+ * virtio-v1.2-csd01, 5.14.3,
+ * Feature Bits
+ * None currently defined.
+ */
+ VirtIOSound *s = VIRTIO_SND(vdev);
+ features |= s->features;
+
+ trace_virtio_snd_get_features(vdev, features);
+
+ return features;
+}
+
+static void
+virtio_snd_vm_state_change(void *opaque, bool running,
+ RunState state)
+{
+ if (running) {
+ trace_virtio_snd_vm_state_running();
+ } else {
+ trace_virtio_snd_vm_state_stopped();
+ }
+}
+
+static void virtio_snd_realize(DeviceState *dev, Error **errp)
+{
+ ERRP_GUARD();
+ VirtIOSound *vsnd = VIRTIO_SND(dev);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+
+ vsnd->vmstate =
+ qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd);
+
+ trace_virtio_snd_realize(vsnd);
+
+ virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config));
+ virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1);
+
+ /* set number of jacks and streams */
+ if (vsnd->snd_conf.jacks > 8) {
+ error_setg(errp,
+ "Invalid number of jacks: %"PRIu32,
+ vsnd->snd_conf.jacks);
+ return;
+ }
+ if (vsnd->snd_conf.streams < 1 || vsnd->snd_conf.streams > 10) {
+ error_setg(errp,
+ "Invalid number of streams: %"PRIu32,
+ vsnd->snd_conf.streams);
+ return;
+ }
+
+ if (vsnd->snd_conf.chmaps > VIRTIO_SND_CHMAP_MAX_SIZE) {
+ error_setg(errp,
+ "Invalid number of channel maps: %"PRIu32,
+ vsnd->snd_conf.chmaps);
+ return;
+ }
+
+ AUD_register_card("virtio-sound", &vsnd->card);
+
+ vsnd->queues[VIRTIO_SND_VQ_CONTROL] =
+ virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
+ vsnd->queues[VIRTIO_SND_VQ_EVENT] =
+ virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
+ vsnd->queues[VIRTIO_SND_VQ_TX] =
+ virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
+ vsnd->queues[VIRTIO_SND_VQ_RX] =
+ virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
+}
+
+static void virtio_snd_unrealize(DeviceState *dev)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOSound *vsnd = VIRTIO_SND(dev);
+
+ qemu_del_vm_change_state_handler(vsnd->vmstate);
+ trace_virtio_snd_unrealize(vsnd);
+
+ AUD_remove_card(&vsnd->card);
+ virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]);
+ virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]);
+ virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]);
+ virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_RX]);
+ virtio_cleanup(vdev);
+}
+
+
+static void virtio_snd_reset(VirtIODevice *vdev) {}
+
+static void virtio_snd_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+
+
+ set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
+ device_class_set_props(dc, virtio_snd_properties);
+
+ dc->vmsd = &vmstate_virtio_snd;
+ vdc->vmsd = &vmstate_virtio_snd_device;
+ vdc->realize = virtio_snd_realize;
+ vdc->unrealize = virtio_snd_unrealize;
+ vdc->get_config = virtio_snd_get_config;
+ vdc->set_config = virtio_snd_set_config;
+ vdc->get_features = get_features;
+ vdc->reset = virtio_snd_reset;
+ vdc->legacy_features = 0;
+}
+
+static const TypeInfo virtio_snd_types[] = {
+ {
+ .name = TYPE_VIRTIO_SND,
+ .parent = TYPE_VIRTIO_DEVICE,
+ .instance_size = sizeof(VirtIOSound),
+ .class_init = virtio_snd_class_init,
+ }
+};
+
+DEFINE_TYPES(virtio_snd_types)
diff --git a/include/hw/virtio/virtio-snd.h b/include/hw/virtio/virtio-snd.h
new file mode 100644
index 0000000000..934e854a80
--- /dev/null
+++ b/include/hw/virtio/virtio-snd.h
@@ -0,0 +1,79 @@
+/*
+ * VIRTIO Sound Device conforming to
+ *
+ * "Virtual I/O Device (VIRTIO) Version 1.2
+ * Committee Specification Draft 01
+ * 09 May 2022"
+ *
+ * Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
+ * Copyright (C) 2019 OpenSynergy GmbH
+ *
+ * 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.
+ */
+
+#ifndef QEMU_VIRTIO_SOUND_H
+#define QEMU_VIRTIO_SOUND_H
+
+#include "hw/virtio/virtio.h"
+#include "audio/audio.h"
+#include "standard-headers/linux/virtio_ids.h"
+#include "standard-headers/linux/virtio_snd.h"
+
+#define TYPE_VIRTIO_SND "virtio-sound"
+#define VIRTIO_SND(obj) \
+ OBJECT_CHECK(VirtIOSound, (obj), TYPE_VIRTIO_SND)
+
+/* CONFIGURATION SPACE */
+
+typedef struct virtio_snd_config virtio_snd_config;
+
+/* COMMON DEFINITIONS */
+
+/* common header for request/response*/
+typedef struct virtio_snd_hdr virtio_snd_hdr;
+
+/* event notification */
+typedef struct virtio_snd_event virtio_snd_event;
+
+/* common control request to query an item information */
+typedef struct virtio_snd_query_info virtio_snd_query_info;
+
+/* JACK CONTROL MESSAGES */
+
+typedef struct virtio_snd_jack_hdr virtio_snd_jack_hdr;
+
+/* jack information structure */
+typedef struct virtio_snd_jack_info virtio_snd_jack_info;
+
+/* jack remapping control request */
+typedef struct virtio_snd_jack_remap virtio_snd_jack_remap;
+
+/*
+ * PCM CONTROL MESSAGES
+ */
+typedef struct virtio_snd_pcm_hdr virtio_snd_pcm_hdr;
+
+/* PCM stream info structure */
+typedef struct virtio_snd_pcm_info virtio_snd_pcm_info;
+
+/* set PCM stream params */
+typedef struct virtio_snd_pcm_set_params virtio_snd_pcm_set_params;
+
+/* I/O request header */
+typedef struct virtio_snd_pcm_xfer virtio_snd_pcm_xfer;
+
+/* I/O request status */
+typedef struct virtio_snd_pcm_status virtio_snd_pcm_status;
+
+typedef struct VirtIOSound {
+ VirtIODevice parent_obj;
+
+ VirtQueue *queues[VIRTIO_SND_VQ_MAX];
+ uint64_t features;
+ QEMUSoundCard card;
+ VMChangeStateEntry *vmstate;
+ virtio_snd_config snd_conf;
+} VirtIOSound;
+#endif
--
2.39.2
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v10 02/11] Add virtio-sound-pci device
2023-09-29 14:08 [PATCH v10 00/11] Add VIRTIO sound card Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 01/11] Add virtio-sound device stub Emmanouil Pitsidianakis
@ 2023-09-29 14:08 ` Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 03/11] virtio-sound: handle control messages and streams Emmanouil Pitsidianakis
` (9 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Emmanouil Pitsidianakis @ 2023-09-29 14:08 UTC (permalink / raw)
To: qemu-devel
Cc: Emmanouil Pitsidianakis, Igor Skalkin, Anton Yakovlev,
Paolo Bonzini, Gerd Hoffmann, Michael S. Tsirkin,
Marcel Apfelbaum, Daniel P. Berrangé, Eduardo Habkost,
Marc-André Lureau, Volker Rümelin,
Kővágó, Zoltán, Alex Bennée,
Philippe Mathieu-Daudé, Mark Cave-Ayland
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 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:
Pulseaudio:
-audio driver=pa,model=virtio
or
-audio driver=pa,model=virtio,server=/run/user/1000/pulse/native
sdl:
-audio driver=sdl,model=virtio
coreaudio (macos/darwin):
-audio driver=coreaudio,model=virtio
etc.
Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Igor Skalkin <Igor.Skalkin@opensynergy.com>
Signed-off-by: Anton Yakovlev <Anton.Yakovlev@opensynergy.com>
Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
---
hw/virtio/meson.build | 1 +
hw/virtio/virtio-snd-pci.c | 93 ++++++++++++++++++++++++++++++++++++++
hw/virtio/virtio-snd.c | 14 +++++-
softmmu/qdev-monitor.c | 1 +
4 files changed, 107 insertions(+), 2 deletions(-)
create mode 100644 hw/virtio/virtio-snd-pci.c
diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build
index 120d4bfa0a..5e5a83a4ee 100644
--- a/hw/virtio/meson.build
+++ b/hw/virtio/meson.build
@@ -63,6 +63,7 @@ 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'))
virtio_pci_ss.add(when: 'CONFIG_VIRTIO_MD', if_true: files('virtio-md-pci.c'))
diff --git a/hw/virtio/virtio-snd-pci.c b/hw/virtio/virtio-snd-pci.c
new file mode 100644
index 0000000000..afe50a5354
--- /dev/null
+++ b/hw/virtio/virtio-snd-pci.c
@@ -0,0 +1,93 @@
+/*
+ * 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 "qom/object.h"
+#include "qapi/error.h"
+#include "hw/audio/soundhw.h"
+#include "hw/virtio/virtio-pci.h"
+#include "hw/virtio/virtio-snd.h"
+
+/*
+ * virtio-snd-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_SND_PCI "virtio-sound-pci"
+OBJECT_DECLARE_SIMPLE_TYPE(VirtIOSoundPCI, VIRTIO_SND_PCI)
+
+struct VirtIOSoundPCI {
+ VirtIOPCIProxy parent_obj;
+
+ VirtIOSound vdev;
+};
+
+static Property virtio_snd_pci_properties[] = {
+ DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_snd_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VirtIOSoundPCI *dev = VIRTIO_SND_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+
+ virtio_pci_force_virtio_1(vpci_dev);
+ qdev_realize(vdev, BUS(&vpci_dev->bus), errp);
+}
+
+static void virtio_snd_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass);
+
+ device_class_set_props(dc, virtio_snd_pci_properties);
+ dc->desc = "Virtio Sound";
+ set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
+
+ vpciklass->realize = virtio_snd_pci_realize;
+}
+
+static void virtio_snd_pci_instance_init(Object *obj)
+{
+ VirtIOSoundPCI *dev = VIRTIO_SND_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_SND);
+}
+
+static const VirtioPCIDeviceTypeInfo virtio_snd_pci_info = {
+ .generic_name = TYPE_VIRTIO_SND_PCI,
+ .instance_size = sizeof(VirtIOSoundPCI),
+ .instance_init = virtio_snd_pci_instance_init,
+ .class_init = virtio_snd_pci_class_init,
+};
+
+/* Create a Virtio Sound PCI device, so '-audio driver,model=virtio' works. */
+static int virtio_snd_pci_init(PCIBus *bus, const char *audiodev)
+{
+ DeviceState *vdev = NULL;
+ VirtIOSoundPCI *dev = NULL;
+
+ vdev = qdev_new(TYPE_VIRTIO_SND_PCI);
+ assert(vdev);
+ dev = VIRTIO_SND_PCI(vdev);
+ qdev_prop_set_string(DEVICE(&dev->vdev), "audiodev", audiodev);
+ qdev_realize_and_unref(vdev, BUS(bus), &error_fatal);
+ return 0;
+}
+
+static void virtio_snd_pci_register(void)
+{
+ virtio_pci_types_register(&virtio_snd_pci_info);
+ pci_register_soundhw("virtio", "Virtio Sound", virtio_snd_pci_init);
+}
+
+type_init(virtio_snd_pci_register);
diff --git a/hw/virtio/virtio-snd.c b/hw/virtio/virtio-snd.c
index a8204e8a02..46e10ad7e0 100644
--- a/hw/virtio/virtio-snd.c
+++ b/hw/virtio/virtio-snd.c
@@ -63,12 +63,18 @@ static void
virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config)
{
VirtIOSound *s = VIRTIO_SND(vdev);
+ virtio_snd_config *sndconfig =
+ (virtio_snd_config *)config;
trace_virtio_snd_get_config(vdev,
s->snd_conf.jacks,
s->snd_conf.streams,
s->snd_conf.chmaps);
- memcpy(config, &s->snd_conf, sizeof(s->snd_conf));
+ memcpy(sndconfig, &s->snd_conf, sizeof(s->snd_conf));
+ cpu_to_le32s(&sndconfig->jacks);
+ cpu_to_le32s(&sndconfig->streams);
+ cpu_to_le32s(&sndconfig->chmaps);
+
}
static void
@@ -87,7 +93,11 @@ virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
s->snd_conf.chmaps,
sndconfig->chmaps);
- memcpy(&s->snd_conf, sndconfig, sizeof(s->snd_conf));
+ memcpy(&s->snd_conf, sndconfig, sizeof(virtio_snd_config));
+ le32_to_cpus(&s->snd_conf.jacks);
+ le32_to_cpus(&s->snd_conf.streams);
+ le32_to_cpus(&s->snd_conf.chmaps);
+
}
/*
diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c
index 74f4e41338..2e9835ad88 100644
--- a/softmmu/qdev-monitor.c
+++ b/softmmu/qdev-monitor.c
@@ -108,6 +108,7 @@ 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.39.2
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v10 03/11] virtio-sound: handle control messages and streams
2023-09-29 14:08 [PATCH v10 00/11] Add VIRTIO sound card Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 01/11] Add virtio-sound device stub Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 02/11] Add virtio-sound-pci device Emmanouil Pitsidianakis
@ 2023-09-29 14:08 ` Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 04/11] virtio-sound: handle VIRTIO_SND_R_PCM_INFO request Emmanouil Pitsidianakis
` (8 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Emmanouil Pitsidianakis @ 2023-09-29 14:08 UTC (permalink / raw)
To: qemu-devel
Cc: Emmanouil Pitsidianakis, Igor Skalkin, Anton Yakovlev,
Paolo Bonzini, Gerd Hoffmann, Michael S. Tsirkin,
Marcel Apfelbaum, Daniel P. Berrangé, Eduardo Habkost,
Marc-André Lureau, Volker Rümelin,
Kővágó, Zoltán, Alex Bennée,
Philippe Mathieu-Daudé, Mark Cave-Ayland
Receive guest requests in the control (CTRL) queue of the virtio sound
device and reply with a NOT SUPPORTED error to all control commands.
The receiving handler is virtio_snd_handle_ctrl(). It stores all control
messages in the queue in the device's command queue. Then it calls
virtio_snd_process_cmdq() to handle each message.
The handler is process_cmd() which replies with VIRTIO_SND_S_NOT_SUPP.
Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Igor Skalkin <Igor.Skalkin@opensynergy.com>
Signed-off-by: Anton Yakovlev <Anton.Yakovlev@opensynergy.com>
Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
---
hw/virtio/trace-events | 4 +
hw/virtio/virtio-snd.c | 487 ++++++++++++++++++++++++++++++++-
include/hw/virtio/virtio-snd.h | 113 +++++++-
3 files changed, 595 insertions(+), 9 deletions(-)
diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
index 3ed7da35f2..8a223e36e9 100644
--- a/hw/virtio/trace-events
+++ b/hw/virtio/trace-events
@@ -163,3 +163,7 @@ virtio_snd_vm_state_running(void) "vm state running"
virtio_snd_vm_state_stopped(void) "vm state stopped"
virtio_snd_realize(void *snd) "snd %p: realize"
virtio_snd_unrealize(void *snd) "snd %p: unrealize"
+virtio_snd_handle_ctrl(void *vdev, void *vq) "snd %p: handle ctrl event for queue %p"
+virtio_snd_handle_code(uint32_t val, const char *code) "ctrl code msg val = %"PRIu32" == %s"
+virtio_snd_handle_chmap_info(void) "VIRTIO_SND_CHMAP_INFO called"
+virtio_snd_handle_event(void) "event queue callback called"
diff --git a/hw/virtio/virtio-snd.c b/hw/virtio/virtio-snd.c
index 46e10ad7e0..96dbe2be65 100644
--- a/hw/virtio/virtio-snd.c
+++ b/hw/virtio/virtio-snd.c
@@ -32,6 +32,29 @@
#define VIRTIO_SOUND_CHMAP_DEFAULT 0
#define VIRTIO_SOUND_HDA_FN_NID 0
+static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8)
+ | BIT(VIRTIO_SND_PCM_FMT_U8)
+ | BIT(VIRTIO_SND_PCM_FMT_S16)
+ | BIT(VIRTIO_SND_PCM_FMT_U16)
+ | BIT(VIRTIO_SND_PCM_FMT_S32)
+ | BIT(VIRTIO_SND_PCM_FMT_U32)
+ | BIT(VIRTIO_SND_PCM_FMT_FLOAT);
+
+static uint32_t supported_rates = BIT(VIRTIO_SND_PCM_RATE_5512)
+ | BIT(VIRTIO_SND_PCM_RATE_8000)
+ | BIT(VIRTIO_SND_PCM_RATE_11025)
+ | BIT(VIRTIO_SND_PCM_RATE_16000)
+ | BIT(VIRTIO_SND_PCM_RATE_22050)
+ | BIT(VIRTIO_SND_PCM_RATE_32000)
+ | BIT(VIRTIO_SND_PCM_RATE_44100)
+ | BIT(VIRTIO_SND_PCM_RATE_48000)
+ | BIT(VIRTIO_SND_PCM_RATE_64000)
+ | BIT(VIRTIO_SND_PCM_RATE_88200)
+ | BIT(VIRTIO_SND_PCM_RATE_96000)
+ | BIT(VIRTIO_SND_PCM_RATE_176400)
+ | BIT(VIRTIO_SND_PCM_RATE_192000)
+ | BIT(VIRTIO_SND_PCM_RATE_384000);
+
static const VMStateDescription vmstate_virtio_snd_device = {
.name = TYPE_VIRTIO_SND,
.version_id = VIRTIO_SOUND_VM_VERSION,
@@ -100,13 +123,397 @@ virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
}
+static void
+virtio_snd_ctrl_cmd_free(virtio_snd_ctrl_command *cmd)
+{
+ g_free(cmd->elem);
+ g_free(cmd);
+}
+
+/*
+ * Get a specific stream from the virtio sound card device.
+ * Returns NULL if @stream_id is invalid or not allocated.
+ *
+ * @s: VirtIOSound device
+ * @stream_id: stream id
+ */
+static VirtIOSoundPCMStream *virtio_snd_pcm_get_stream(VirtIOSound *s,
+ uint32_t stream_id)
+{
+ return stream_id >= s->snd_conf.streams ? NULL :
+ s->pcm->streams[stream_id];
+}
+
+/*
+ * Get params for a specific stream.
+ *
+ * @s: VirtIOSound device
+ * @stream_id: stream id
+ */
+static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s,
+ uint32_t stream_id)
+{
+ return stream_id >= s->snd_conf.streams ? NULL
+ : &s->pcm->pcm_params[stream_id];
+}
+
+/*
+ * Set the given stream params.
+ * Called by both virtio_snd_handle_pcm_set_params and during device
+ * initialization.
+ * Returns the response status code. (VIRTIO_SND_S_*).
+ *
+ * @s: VirtIOSound device
+ * @params: The PCM params as defined in the virtio specification
+ */
+static
+uint32_t virtio_snd_set_pcm_params(VirtIOSound *s,
+ uint32_t stream_id,
+ virtio_snd_pcm_set_params *params)
+{
+ virtio_snd_pcm_set_params *st_params;
+
+ if (stream_id >= s->snd_conf.streams || s->pcm->pcm_params == NULL) {
+ /*
+ * TODO: do we need to set DEVICE_NEEDS_RESET?
+ */
+ virtio_error(VIRTIO_DEVICE(s), "Streams have not been initialized.\n");
+ return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ }
+
+ st_params = virtio_snd_pcm_get_params(s, stream_id);
+
+ if (params->channels < 1 || params->channels > AUDIO_MAX_CHANNELS) {
+ error_report("Number of channels is not supported.");
+ return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
+ }
+ if (!(supported_formats & BIT(params->format))) {
+ error_report("Stream format is not supported.");
+ return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
+ }
+ if (!(supported_rates & BIT(params->rate))) {
+ error_report("Stream rate is not supported.");
+ return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
+ }
+
+ st_params->buffer_bytes = le32_to_cpu(params->buffer_bytes);
+ st_params->period_bytes = le32_to_cpu(params->period_bytes);
+ st_params->features = le32_to_cpu(params->features);
+ /* the following are uint8_t, so there's no need to bswap the values. */
+ st_params->channels = params->channels;
+ st_params->format = params->format;
+ st_params->rate = params->rate;
+
+ return cpu_to_le32(VIRTIO_SND_S_OK);
+}
+
+/*
+ * Get a QEMU Audiosystem compatible format value from a VIRTIO_SND_PCM_FMT_*
+ */
+static AudioFormat virtio_snd_get_qemu_format(uint32_t format)
+{
+ #define CASE(FMT) \
+ case VIRTIO_SND_PCM_FMT_##FMT: \
+ return AUDIO_FORMAT_##FMT;
+
+ switch (format) {
+ CASE(U8)
+ CASE(S8)
+ CASE(U16)
+ CASE(S16)
+ CASE(U32)
+ CASE(S32)
+ case VIRTIO_SND_PCM_FMT_FLOAT:
+ return AUDIO_FORMAT_F32;
+ default:
+ g_assert_not_reached();
+ }
+
+ #undef CASE
+}
+
+/*
+ * Get a QEMU Audiosystem compatible frequency value from a
+ * VIRTIO_SND_PCM_RATE_*
+ */
+static uint32_t virtio_snd_get_qemu_freq(uint32_t rate)
+{
+ #define CASE(RATE) \
+ case VIRTIO_SND_PCM_RATE_##RATE: \
+ return RATE;
+
+ switch (rate) {
+ CASE(5512)
+ CASE(8000)
+ CASE(11025)
+ CASE(16000)
+ CASE(22050)
+ CASE(32000)
+ CASE(44100)
+ CASE(48000)
+ CASE(64000)
+ CASE(88200)
+ CASE(96000)
+ CASE(176400)
+ CASE(192000)
+ CASE(384000)
+ default:
+ g_assert_not_reached();
+ }
+
+ #undef CASE
+}
+
+/*
+ * Get QEMU Audiosystem compatible audsettings from virtio based pcm stream
+ * params.
+ */
+static void virtio_snd_get_qemu_audsettings(audsettings *as,
+ virtio_snd_pcm_set_params *params)
+{
+ 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 = target_words_bigendian() ? 1 : 0;
+}
+
+/*
+ * Close a stream and free all its resources.
+ *
+ * @stream: VirtIOSoundPCMStream *stream
+ */
+static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream)
+{
+}
+
/*
- * Queue handler stub.
+ * Prepares a VirtIOSound card stream.
+ * Returns the response status code. (VIRTIO_SND_S_*).
+ *
+ * @s: VirtIOSound device
+ * @stream_id: stream id
+ */
+static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
+{
+ audsettings as;
+ virtio_snd_pcm_set_params *params;
+ VirtIOSoundPCMStream *stream;
+
+ if (s->pcm->streams == NULL ||
+ s->pcm->pcm_params == NULL ||
+ stream_id >= s->snd_conf.streams) {
+ return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ }
+
+ params = virtio_snd_pcm_get_params(s, stream_id);
+ if (params == NULL) {
+ return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ }
+
+ stream = virtio_snd_pcm_get_stream(s, stream_id);
+ if (stream == NULL) {
+ stream = g_new0(VirtIOSoundPCMStream, 1);
+ stream->active = false;
+ stream->id = stream_id;
+ stream->pcm = s->pcm;
+ stream->s = s;
+
+ /*
+ * stream_id >= s->snd_conf.streams was checked before so this is
+ * in-bounds
+ */
+ s->pcm->streams[stream_id] = stream;
+ }
+
+ virtio_snd_get_qemu_audsettings(&as, params);
+ stream->info.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 = VIRTIO_SOUND_HDA_FN_NID;
+ stream->info.features = 0;
+ stream->info.channels_min = 1;
+ stream->info.channels_max = as.nchannels;
+ stream->info.formats = supported_formats;
+ stream->info.rates = supported_rates;
+ stream->params = *params;
+
+ stream->positions[0] = VIRTIO_SND_CHMAP_FL;
+ stream->positions[1] = VIRTIO_SND_CHMAP_FR;
+ stream->as = as;
+
+ return cpu_to_le32(VIRTIO_SND_S_OK);
+}
+
+static const char *print_code(uint32_t code)
+{
+ #define CASE(CODE) \
+ case VIRTIO_SND_R_##CODE: \
+ return "VIRTIO_SND_R_"#CODE
+
+ switch (code) {
+ CASE(JACK_INFO);
+ CASE(JACK_REMAP);
+ CASE(PCM_INFO);
+ CASE(PCM_SET_PARAMS);
+ CASE(PCM_PREPARE);
+ CASE(PCM_RELEASE);
+ CASE(PCM_START);
+ CASE(PCM_STOP);
+ CASE(CHMAP_INFO);
+ default:
+ return "invalid code";
+ }
+
+ #undef CASE
+};
+
+/*
+ * The actual processing done in virtio_snd_process_cmdq().
+ *
+ * @s: VirtIOSound device
+ * @cmd: control command request
+ */
+static inline void
+process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd)
+{
+ uint32_t code;
+ size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
+ cmd->elem->out_num,
+ 0,
+ &cmd->ctrl,
+ sizeof(virtio_snd_hdr));
+
+ if (msg_sz != sizeof(virtio_snd_hdr)) {
+ /*
+ * TODO: do we need to set DEVICE_NEEDS_RESET?
+ */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: virtio-snd command size incorrect %zu vs \
+ %zu\n", __func__, msg_sz, sizeof(virtio_snd_hdr));
+ return;
+ }
+
+ code = le32_to_cpu(cmd->ctrl.code);
+
+ trace_virtio_snd_handle_code(code, print_code(code));
+
+ switch (code) {
+ case VIRTIO_SND_R_JACK_INFO:
+ case VIRTIO_SND_R_JACK_REMAP:
+ qemu_log_mask(LOG_UNIMP,
+ "virtio_snd: jack functionality is unimplemented.\n");
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
+ break;
+ case VIRTIO_SND_R_PCM_INFO:
+ case VIRTIO_SND_R_PCM_SET_PARAMS:
+ case VIRTIO_SND_R_PCM_PREPARE:
+ case VIRTIO_SND_R_PCM_START:
+ case VIRTIO_SND_R_PCM_STOP:
+ case VIRTIO_SND_R_PCM_RELEASE:
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
+ break;
+ case VIRTIO_SND_R_CHMAP_INFO:
+ qemu_log_mask(LOG_UNIMP,
+ "virtio_snd: chmap info functionality is unimplemented.\n");
+ trace_virtio_snd_handle_chmap_info();
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
+ break;
+ default:
+ /* error */
+ error_report("virtio snd header not recognized: %"PRIu32, code);
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ }
+
+ iov_from_buf(cmd->elem->in_sg,
+ cmd->elem->in_num,
+ 0,
+ &cmd->resp,
+ sizeof(virtio_snd_hdr));
+ virtqueue_push(cmd->vq, cmd->elem, sizeof(virtio_snd_hdr));
+ virtio_notify(VIRTIO_DEVICE(s), cmd->vq);
+}
+
+/*
+ * Consume all elements in command queue.
+ *
+ * @s: VirtIOSound device
+ */
+static void virtio_snd_process_cmdq(VirtIOSound *s)
+{
+ virtio_snd_ctrl_command *cmd;
+
+ if (unlikely(qatomic_read(&s->processing_cmdq))) {
+ return;
+ }
+
+ WITH_QEMU_LOCK_GUARD(&s->cmdq_mutex) {
+ qatomic_set(&s->processing_cmdq, true);
+ while (!QTAILQ_EMPTY(&s->cmdq)) {
+ cmd = QTAILQ_FIRST(&s->cmdq);
+
+ /* process command */
+ process_cmd(s, cmd);
+
+ QTAILQ_REMOVE(&s->cmdq, cmd, next);
+
+ virtio_snd_ctrl_cmd_free(cmd);
+ }
+ qatomic_set(&s->processing_cmdq, false);
+ }
+}
+
+/*
+ * The control message handler. Pops an element from the control virtqueue,
+ * and stores them to VirtIOSound's cmdq queue and finally calls
+ * virtio_snd_process_cmdq() for processing.
+ *
+ * @vdev: VirtIOSound device
+ * @vq: Control virtqueue
+ */
+static void virtio_snd_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOSound *s = VIRTIO_SND(vdev);
+ VirtQueueElement *elem;
+ virtio_snd_ctrl_command *cmd;
+
+ trace_virtio_snd_handle_ctrl(vdev, vq);
+
+ if (!virtio_queue_ready(vq)) {
+ return;
+ }
+
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+ while (elem) {
+ cmd = g_new0(virtio_snd_ctrl_command, 1);
+ cmd->elem = elem;
+ cmd->vq = vq;
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
+ QTAILQ_INSERT_TAIL(&s->cmdq, cmd, next);
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+ }
+
+ virtio_snd_process_cmdq(s);
+}
+
+/*
+ * The event virtqueue handler.
+ * Not implemented yet.
+ *
+ * @vdev: VirtIOSound device
+ * @vq: event vq
+ */
+static void virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq)
+{
+ qemu_log_mask(LOG_UNIMP, "virtio_snd: event queue is unimplemented.\n");
+ trace_virtio_snd_handle_event();
+}
+
+/*
+ * Stub buffer virtqueue handler.
*
* @vdev: VirtIOSound device
* @vq: virtqueue
*/
-static void virtio_snd_handle_queue(VirtIODevice *vdev, VirtQueue *vq) {}
+static void virtio_snd_handle_xfer(VirtIODevice *vdev, VirtQueue *vq) {}
static uint64_t get_features(VirtIODevice *vdev, uint64_t features,
Error **errp)
@@ -140,12 +547,22 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp)
ERRP_GUARD();
VirtIOSound *vsnd = VIRTIO_SND(dev);
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ virtio_snd_pcm_set_params default_params = { 0 };
+ uint32_t status;
+ vsnd->pcm = NULL;
vsnd->vmstate =
qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd);
trace_virtio_snd_realize(vsnd);
+ vsnd->pcm = g_new0(VirtIOSoundPCM, 1);
+ vsnd->pcm->snd = vsnd;
+ vsnd->pcm->streams =
+ g_new0(VirtIOSoundPCMStream *, vsnd->snd_conf.streams);
+ vsnd->pcm->pcm_params =
+ g_new0(virtio_snd_pcm_set_params, vsnd->snd_conf.streams);
+
virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config));
virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1);
@@ -172,25 +589,69 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp)
AUD_register_card("virtio-sound", &vsnd->card);
+ /* set default params for all streams */
+ default_params.features = 0;
+ default_params.buffer_bytes = cpu_to_le32(8192);
+ default_params.period_bytes = cpu_to_le32(2048);
+ default_params.channels = 2;
+ default_params.format = VIRTIO_SND_PCM_FMT_S16;
+ default_params.rate = VIRTIO_SND_PCM_RATE_48000;
vsnd->queues[VIRTIO_SND_VQ_CONTROL] =
- virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
+ virtio_add_queue(vdev, 64, virtio_snd_handle_ctrl);
vsnd->queues[VIRTIO_SND_VQ_EVENT] =
- virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
+ virtio_add_queue(vdev, 64, virtio_snd_handle_event);
vsnd->queues[VIRTIO_SND_VQ_TX] =
- virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
+ virtio_add_queue(vdev, 64, virtio_snd_handle_xfer);
vsnd->queues[VIRTIO_SND_VQ_RX] =
- virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
+ virtio_add_queue(vdev, 64, virtio_snd_handle_xfer);
+ qemu_mutex_init(&vsnd->cmdq_mutex);
+ QTAILQ_INIT(&vsnd->cmdq);
+
+ for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
+ status = virtio_snd_set_pcm_params(vsnd, i, &default_params);
+ if (status != cpu_to_le32(VIRTIO_SND_S_OK)) {
+ error_setg(errp,
+ "Can't initalize stream params, device responded with %s.",
+ print_code(status));
+ return;
+ }
+ status = virtio_snd_pcm_prepare(vsnd, i);
+ if (status != cpu_to_le32(VIRTIO_SND_S_OK)) {
+ error_setg(errp,
+ "Can't prepare streams, device responded with %s.",
+ print_code(status));
+ return;
+ }
+ }
}
static void virtio_snd_unrealize(DeviceState *dev)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VirtIOSound *vsnd = VIRTIO_SND(dev);
+ VirtIOSoundPCMStream *stream;
qemu_del_vm_change_state_handler(vsnd->vmstate);
trace_virtio_snd_unrealize(vsnd);
+ if (vsnd->pcm) {
+ if (vsnd->pcm->streams) {
+ for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
+ stream = vsnd->pcm->streams[i];
+ if (stream) {
+ virtio_snd_process_cmdq(stream->s);
+ virtio_snd_pcm_close(stream);
+ g_free(stream);
+ }
+ }
+ g_free(vsnd->pcm->streams);
+ }
+ g_free(vsnd->pcm->pcm_params);
+ g_free(vsnd->pcm);
+ vsnd->pcm = NULL;
+ }
AUD_remove_card(&vsnd->card);
+ qemu_mutex_destroy(&vsnd->cmdq_mutex);
virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]);
virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]);
virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]);
@@ -199,7 +660,19 @@ static void virtio_snd_unrealize(DeviceState *dev)
}
-static void virtio_snd_reset(VirtIODevice *vdev) {}
+static void virtio_snd_reset(VirtIODevice *vdev)
+{
+ VirtIOSound *s = VIRTIO_SND(vdev);
+ virtio_snd_ctrl_command *cmd;
+
+ WITH_QEMU_LOCK_GUARD(&s->cmdq_mutex) {
+ while (!QTAILQ_EMPTY(&s->cmdq)) {
+ cmd = QTAILQ_FIRST(&s->cmdq);
+ QTAILQ_REMOVE(&s->cmdq, cmd, next);
+ virtio_snd_ctrl_cmd_free(cmd);
+ }
+ }
+}
static void virtio_snd_class_init(ObjectClass *klass, void *data)
{
diff --git a/include/hw/virtio/virtio-snd.h b/include/hw/virtio/virtio-snd.h
index 934e854a80..4b4aaffff1 100644
--- a/include/hw/virtio/virtio-snd.h
+++ b/include/hw/virtio/virtio-snd.h
@@ -67,13 +67,122 @@ typedef struct virtio_snd_pcm_xfer virtio_snd_pcm_xfer;
/* I/O request status */
typedef struct virtio_snd_pcm_status virtio_snd_pcm_status;
-typedef struct VirtIOSound {
+/* device structs */
+
+typedef struct VirtIOSound VirtIOSound;
+
+typedef struct VirtIOSoundPCMStream VirtIOSoundPCMStream;
+
+typedef struct virtio_snd_ctrl_command virtio_snd_ctrl_command;
+
+typedef struct VirtIOSoundPCM VirtIOSoundPCM;
+
+struct VirtIOSoundPCM {
+ VirtIOSound *snd;
+ /*
+ * PCM parameters are a separate field instead of a VirtIOSoundPCMStream
+ * field, because the operation of PCM control requests is first
+ * VIRTIO_SND_R_PCM_SET_PARAMS and then VIRTIO_SND_R_PCM_PREPARE; this
+ * means that some times we get parameters without having an allocated
+ * stream yet.
+ */
+ virtio_snd_pcm_set_params *pcm_params;
+ VirtIOSoundPCMStream **streams;
+};
+
+struct VirtIOSoundPCMStream {
+ VirtIOSoundPCM *pcm;
+ virtio_snd_pcm_info info;
+ virtio_snd_pcm_set_params params;
+ uint32_t id;
+ /* channel position values (VIRTIO_SND_CHMAP_XXX) */
+ uint8_t positions[VIRTIO_SND_CHMAP_MAX_SIZE];
+ VirtIOSound *s;
+ bool flushing;
+ audsettings as;
+ union {
+ SWVoiceIn *in;
+ SWVoiceOut *out;
+ } voice;
+ bool active;
+};
+
+/*
+ * PCM stream state machine.
+ * -------------------------
+ *
+ * 5.14.6.6.1 PCM Command Lifecycle
+ * ================================
+ *
+ * A PCM stream has the following command lifecycle:
+ * - `SET PARAMETERS`
+ * The driver negotiates the stream parameters (format, transport, etc) with
+ * the device.
+ * Possible valid transitions: `SET PARAMETERS`, `PREPARE`.
+ * - `PREPARE`
+ * The device prepares the stream (allocates resources, etc).
+ * Possible valid transitions: `SET PARAMETERS`, `PREPARE`, `START`,
+ * `RELEASE`. Output only: the driver transfers data for pre-buffing.
+ * - `START`
+ * The device starts the stream (unmute, putting into running state, etc).
+ * Possible valid transitions: `STOP`.
+ * The driver transfers data to/from the stream.
+ * - `STOP`
+ * The device stops the stream (mute, putting into non-running state, etc).
+ * Possible valid transitions: `START`, `RELEASE`.
+ * - `RELEASE`
+ * The device releases the stream (frees resources, etc).
+ * Possible valid transitions: `SET PARAMETERS`, `PREPARE`.
+ *
+ * +---------------+ +---------+ +---------+ +-------+ +-------+
+ * | SetParameters | | Prepare | | Release | | Start | | Stop |
+ * +---------------+ +---------+ +---------+ +-------+ +-------+
+ * |- | | | |
+ * || | | | |
+ * |< | | | |
+ * |------------->| | | |
+ * |<-------------| | | |
+ * | |- | | |
+ * | || | | |
+ * | |< | | |
+ * | |--------------------->| |
+ * | |---------->| | |
+ * | | | |-------->|
+ * | | | |<--------|
+ * | | |<-------------------|
+ * |<-------------------------| | |
+ * | |<----------| | |
+ *
+ * CTRL in the VirtIOSound device
+ * ==============================
+ *
+ * The control messages that affect the state of a stream arrive in the
+ * `virtio_snd_handle_ctrl()` queue callback and are of type `struct
+ * virtio_snd_ctrl_command`. They are stored in a queue field in the device
+ * type, `VirtIOSound`. This allows deferring the CTRL request completion if
+ * it's not immediately possible due to locking/state reasons.
+ *
+ * The CTRL message is finally handled in `process_cmd()`.
+ */
+struct VirtIOSound {
VirtIODevice parent_obj;
VirtQueue *queues[VIRTIO_SND_VQ_MAX];
uint64_t features;
+ VirtIOSoundPCM *pcm;
QEMUSoundCard card;
VMChangeStateEntry *vmstate;
virtio_snd_config snd_conf;
-} VirtIOSound;
+ QemuMutex cmdq_mutex;
+ QTAILQ_HEAD(, virtio_snd_ctrl_command) cmdq;
+ bool processing_cmdq;
+};
+
+struct virtio_snd_ctrl_command {
+ VirtQueueElement *elem;
+ VirtQueue *vq;
+ virtio_snd_hdr ctrl;
+ virtio_snd_hdr resp;
+ QTAILQ_ENTRY(virtio_snd_ctrl_command) next;
+};
#endif
--
2.39.2
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v10 04/11] virtio-sound: handle VIRTIO_SND_R_PCM_INFO request
2023-09-29 14:08 [PATCH v10 00/11] Add VIRTIO sound card Emmanouil Pitsidianakis
` (2 preceding siblings ...)
2023-09-29 14:08 ` [PATCH v10 03/11] virtio-sound: handle control messages and streams Emmanouil Pitsidianakis
@ 2023-09-29 14:08 ` Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 05/11] virtio-sound: handle VIRTIO_SND_R_PCM_{START,STOP} Emmanouil Pitsidianakis
` (7 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Emmanouil Pitsidianakis @ 2023-09-29 14:08 UTC (permalink / raw)
To: qemu-devel
Cc: Emmanouil Pitsidianakis, Igor Skalkin, Anton Yakovlev,
Paolo Bonzini, Gerd Hoffmann, Michael S. Tsirkin,
Marcel Apfelbaum, Daniel P. Berrangé, Eduardo Habkost,
Marc-André Lureau, Volker Rümelin,
Kővágó, Zoltán, Alex Bennée,
Philippe Mathieu-Daudé, Mark Cave-Ayland
Respond to the VIRTIO_SND_R_PCM_INFO control request with the parameters
of each requested PCM stream.
Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471
Signed-off-by: Igor Skalkin <Igor.Skalkin@opensynergy.com>
Signed-off-by: Anton Yakovlev <Anton.Yakovlev@opensynergy.com>
Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
---
hw/virtio/trace-events | 1 +
hw/virtio/virtio-snd.c | 82 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 83 insertions(+)
diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
index 8a223e36e9..3e619f778b 100644
--- a/hw/virtio/trace-events
+++ b/hw/virtio/trace-events
@@ -164,6 +164,7 @@ virtio_snd_vm_state_stopped(void) "vm state stopped"
virtio_snd_realize(void *snd) "snd %p: realize"
virtio_snd_unrealize(void *snd) "snd %p: unrealize"
virtio_snd_handle_ctrl(void *vdev, void *vq) "snd %p: handle ctrl event for queue %p"
+virtio_snd_handle_pcm_info(uint32_t stream) "VIRTIO_SND_R_PCM_INFO called for stream %"PRIu32
virtio_snd_handle_code(uint32_t val, const char *code) "ctrl code msg val = %"PRIu32" == %s"
virtio_snd_handle_chmap_info(void) "VIRTIO_SND_CHMAP_INFO called"
virtio_snd_handle_event(void) "event queue callback called"
diff --git a/hw/virtio/virtio-snd.c b/hw/virtio/virtio-snd.c
index 96dbe2be65..823aa3b5dc 100644
--- a/hw/virtio/virtio-snd.c
+++ b/hw/virtio/virtio-snd.c
@@ -157,6 +157,86 @@ static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s,
: &s->pcm->pcm_params[stream_id];
}
+/*
+ * Handle the VIRTIO_SND_R_PCM_INFO request.
+ * The function writes the info structs to the request element.
+ *
+ * @s: VirtIOSound device
+ * @cmd: The request command queue element from VirtIOSound cmdq field
+ */
+static void virtio_snd_handle_pcm_info(VirtIOSound *s,
+ virtio_snd_ctrl_command *cmd)
+{
+ uint32_t stream_id, start_id, count, size;
+ virtio_snd_pcm_info val;
+ virtio_snd_query_info req;
+ VirtIOSoundPCMStream *stream = NULL;
+ g_autofree virtio_snd_pcm_info *pcm_info = NULL;
+ size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
+ cmd->elem->out_num,
+ 0,
+ &req,
+ sizeof(virtio_snd_query_info));
+
+ if (msg_sz != sizeof(virtio_snd_query_info)) {
+ /*
+ * TODO: do we need to set DEVICE_NEEDS_RESET?
+ */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: virtio-snd command size incorrect %zu vs \
+ %zu\n", __func__, msg_sz, sizeof(virtio_snd_query_info));
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ return;
+ }
+
+ start_id = le32_to_cpu(req.start_id);
+ count = le32_to_cpu(req.count);
+ size = le32_to_cpu(req.size);
+
+ if (iov_size(cmd->elem->in_sg, cmd->elem->in_num) <
+ sizeof(virtio_snd_hdr) + size * count) {
+ /*
+ * TODO: do we need to set DEVICE_NEEDS_RESET?
+ */
+ error_report("pcm info: buffer too small, got: %zu, needed: %zu",
+ iov_size(cmd->elem->in_sg, cmd->elem->in_num),
+ sizeof(virtio_snd_pcm_info));
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ return;
+ }
+
+ pcm_info = g_new0(virtio_snd_pcm_info, count);
+ for (uint32_t i = 0; i < count; i++) {
+ stream_id = i + start_id;
+ trace_virtio_snd_handle_pcm_info(stream_id);
+ stream = virtio_snd_pcm_get_stream(s, stream_id);
+ if (!stream) {
+ error_report("Invalid stream id: %"PRIu32, stream_id);
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ return;
+ }
+ val = stream->info;
+ val.hdr.hda_fn_nid = cpu_to_le32(val.hdr.hda_fn_nid);
+ val.features = cpu_to_le32(val.features);
+ val.formats = cpu_to_le64(val.formats);
+ val.rates = cpu_to_le64(val.rates);
+ /*
+ * 5.14.6.6.2.1 Device Requirements: Stream Information The device MUST
+ * NOT set undefined feature, format, rate and direction values. The
+ * device MUST initialize the padding bytes to 0.
+ */
+ pcm_info[i] = val;
+ memset(&pcm_info[i].padding, 0, 5);
+ }
+
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
+ iov_from_buf(cmd->elem->in_sg,
+ cmd->elem->in_num,
+ sizeof(virtio_snd_hdr),
+ pcm_info,
+ sizeof(virtio_snd_pcm_info) * count);
+}
+
/*
* Set the given stream params.
* Called by both virtio_snd_handle_pcm_set_params and during device
@@ -404,6 +484,8 @@ process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd)
cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
break;
case VIRTIO_SND_R_PCM_INFO:
+ virtio_snd_handle_pcm_info(s, cmd);
+ break;
case VIRTIO_SND_R_PCM_SET_PARAMS:
case VIRTIO_SND_R_PCM_PREPARE:
case VIRTIO_SND_R_PCM_START:
--
2.39.2
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v10 05/11] virtio-sound: handle VIRTIO_SND_R_PCM_{START,STOP}
2023-09-29 14:08 [PATCH v10 00/11] Add VIRTIO sound card Emmanouil Pitsidianakis
` (3 preceding siblings ...)
2023-09-29 14:08 ` [PATCH v10 04/11] virtio-sound: handle VIRTIO_SND_R_PCM_INFO request Emmanouil Pitsidianakis
@ 2023-09-29 14:08 ` Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 06/11] virtio-sound: handle VIRTIO_SND_R_PCM_SET_PARAMS Emmanouil Pitsidianakis
` (6 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Emmanouil Pitsidianakis @ 2023-09-29 14:08 UTC (permalink / raw)
To: qemu-devel
Cc: Emmanouil Pitsidianakis, Igor Skalkin, Anton Yakovlev,
Paolo Bonzini, Gerd Hoffmann, Michael S. Tsirkin,
Marcel Apfelbaum, Daniel P. Berrangé, Eduardo Habkost,
Marc-André Lureau, Volker Rümelin,
Kővágó, Zoltán, Alex Bennée,
Philippe Mathieu-Daudé, Mark Cave-Ayland
Handle the start and stop control messages for a stream_id. This request
does nothing at the moment except for replying to it. Audio playback
or capture will be started/stopped here in follow-up commits.
Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471
Signed-off-by: Igor Skalkin <Igor.Skalkin@opensynergy.com>
Signed-off-by: Anton Yakovlev <Anton.Yakovlev@opensynergy.com>
Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
---
hw/virtio/trace-events | 1 +
hw/virtio/virtio-snd.c | 49 ++++++++++++++++++++++++++++++++++++++++--
2 files changed, 48 insertions(+), 2 deletions(-)
diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
index 3e619f778b..8eae1bf881 100644
--- a/hw/virtio/trace-events
+++ b/hw/virtio/trace-events
@@ -165,6 +165,7 @@ virtio_snd_realize(void *snd) "snd %p: realize"
virtio_snd_unrealize(void *snd) "snd %p: unrealize"
virtio_snd_handle_ctrl(void *vdev, void *vq) "snd %p: handle ctrl event for queue %p"
virtio_snd_handle_pcm_info(uint32_t stream) "VIRTIO_SND_R_PCM_INFO called for stream %"PRIu32
+virtio_snd_handle_pcm_start_stop(const char *code, uint32_t stream) "%s called for stream %"PRIu32
virtio_snd_handle_code(uint32_t val, const char *code) "ctrl code msg val = %"PRIu32" == %s"
virtio_snd_handle_chmap_info(void) "VIRTIO_SND_CHMAP_INFO called"
virtio_snd_handle_event(void) "event queue callback called"
diff --git a/hw/virtio/virtio-snd.c b/hw/virtio/virtio-snd.c
index 823aa3b5dc..de1fad32c6 100644
--- a/hw/virtio/virtio-snd.c
+++ b/hw/virtio/virtio-snd.c
@@ -446,6 +446,47 @@ static const char *print_code(uint32_t code)
#undef CASE
};
+/*
+ * Handles VIRTIO_SND_R_PCM_START.
+ *
+ * @s: VirtIOSound device
+ * @cmd: The request command queue element from VirtIOSound cmdq field
+ * @start: whether to start or stop the device
+ */
+static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
+ virtio_snd_ctrl_command *cmd,
+ bool start)
+{
+ VirtIOSoundPCMStream *stream;
+ virtio_snd_pcm_hdr req;
+ uint32_t stream_id;
+ size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
+ cmd->elem->out_num,
+ 0,
+ &req,
+ sizeof(virtio_snd_pcm_hdr));
+
+ if (msg_sz != sizeof(virtio_snd_pcm_hdr)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: virtio-snd command size incorrect %zu vs \
+ %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_hdr));
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ return;
+ }
+
+ stream_id = le32_to_cpu(req.stream_id);
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
+ trace_virtio_snd_handle_pcm_start_stop(start ? "VIRTIO_SND_R_PCM_START" :
+ "VIRTIO_SND_R_PCM_STOP", stream_id);
+ stream = virtio_snd_pcm_get_stream(s, stream_id);
+ if (stream == NULL) {
+ error_report("Invalid stream id: %"PRIu32, req.stream_id);
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ return;
+ }
+ stream->active = start;
+}
+
/*
* The actual processing done in virtio_snd_process_cmdq().
*
@@ -486,10 +527,14 @@ process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd)
case VIRTIO_SND_R_PCM_INFO:
virtio_snd_handle_pcm_info(s, cmd);
break;
- case VIRTIO_SND_R_PCM_SET_PARAMS:
- case VIRTIO_SND_R_PCM_PREPARE:
case VIRTIO_SND_R_PCM_START:
+ virtio_snd_handle_pcm_start_stop(s, cmd, true);
+ break;
case VIRTIO_SND_R_PCM_STOP:
+ virtio_snd_handle_pcm_start_stop(s, cmd, false);
+ break;
+ case VIRTIO_SND_R_PCM_SET_PARAMS:
+ case VIRTIO_SND_R_PCM_PREPARE:
case VIRTIO_SND_R_PCM_RELEASE:
cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
break;
--
2.39.2
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v10 06/11] virtio-sound: handle VIRTIO_SND_R_PCM_SET_PARAMS
2023-09-29 14:08 [PATCH v10 00/11] Add VIRTIO sound card Emmanouil Pitsidianakis
` (4 preceding siblings ...)
2023-09-29 14:08 ` [PATCH v10 05/11] virtio-sound: handle VIRTIO_SND_R_PCM_{START,STOP} Emmanouil Pitsidianakis
@ 2023-09-29 14:08 ` Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 07/11] virtio-sound: handle VIRTIO_SND_R_PCM_PREPARE Emmanouil Pitsidianakis
` (5 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Emmanouil Pitsidianakis @ 2023-09-29 14:08 UTC (permalink / raw)
To: qemu-devel
Cc: Emmanouil Pitsidianakis, Igor Skalkin, Anton Yakovlev,
Paolo Bonzini, Gerd Hoffmann, Michael S. Tsirkin,
Marcel Apfelbaum, Daniel P. Berrangé, Eduardo Habkost,
Marc-André Lureau, Volker Rümelin,
Kővágó, Zoltán, Alex Bennée,
Philippe Mathieu-Daudé, Mark Cave-Ayland
Handle the set parameters control request. It reconfigures a stream
based on a guest's preference if the values are valid and supported.
Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471
Signed-off-by: Igor Skalkin <Igor.Skalkin@opensynergy.com>
Signed-off-by: Anton Yakovlev <Anton.Yakovlev@opensynergy.com>
Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
---
hw/virtio/trace-events | 1 +
hw/virtio/virtio-snd.c | 34 ++++++++++++++++++++++++++++++++++
2 files changed, 35 insertions(+)
diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
index 8eae1bf881..f70cde4f01 100644
--- a/hw/virtio/trace-events
+++ b/hw/virtio/trace-events
@@ -163,6 +163,7 @@ virtio_snd_vm_state_running(void) "vm state running"
virtio_snd_vm_state_stopped(void) "vm state stopped"
virtio_snd_realize(void *snd) "snd %p: realize"
virtio_snd_unrealize(void *snd) "snd %p: unrealize"
+virtio_snd_handle_pcm_set_params(uint32_t stream) "VIRTIO_SND_PCM_SET_PARAMS called for stream %"PRIu32
virtio_snd_handle_ctrl(void *vdev, void *vq) "snd %p: handle ctrl event for queue %p"
virtio_snd_handle_pcm_info(uint32_t stream) "VIRTIO_SND_R_PCM_INFO called for stream %"PRIu32
virtio_snd_handle_pcm_start_stop(const char *code, uint32_t stream) "%s called for stream %"PRIu32
diff --git a/hw/virtio/virtio-snd.c b/hw/virtio/virtio-snd.c
index de1fad32c6..4b5cb6b01f 100644
--- a/hw/virtio/virtio-snd.c
+++ b/hw/virtio/virtio-snd.c
@@ -287,6 +287,38 @@ uint32_t virtio_snd_set_pcm_params(VirtIOSound *s,
return cpu_to_le32(VIRTIO_SND_S_OK);
}
+/*
+ * Handles the VIRTIO_SND_R_PCM_SET_PARAMS request.
+ *
+ * @s: VirtIOSound device
+ * @cmd: The request command queue element from VirtIOSound cmdq field
+ */
+static void virtio_snd_handle_pcm_set_params(VirtIOSound *s,
+ virtio_snd_ctrl_command *cmd)
+{
+ virtio_snd_pcm_set_params req = { 0 };
+ uint32_t stream_id;
+ size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
+ cmd->elem->out_num,
+ 0,
+ &req,
+ sizeof(virtio_snd_pcm_set_params));
+
+ if (msg_sz != sizeof(virtio_snd_pcm_set_params)) {
+ /*
+ * TODO: do we need to set DEVICE_NEEDS_RESET?
+ */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: virtio-snd command size incorrect %zu vs \
+ %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_set_params));
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ return;
+ }
+ stream_id = le32_to_cpu(req.hdr.stream_id);
+ trace_virtio_snd_handle_pcm_set_params(stream_id);
+ cmd->resp.code = virtio_snd_set_pcm_params(s, stream_id, &req);
+}
+
/*
* Get a QEMU Audiosystem compatible format value from a VIRTIO_SND_PCM_FMT_*
*/
@@ -534,6 +566,8 @@ process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd)
virtio_snd_handle_pcm_start_stop(s, cmd, false);
break;
case VIRTIO_SND_R_PCM_SET_PARAMS:
+ virtio_snd_handle_pcm_set_params(s, cmd);
+ break;
case VIRTIO_SND_R_PCM_PREPARE:
case VIRTIO_SND_R_PCM_RELEASE:
cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
--
2.39.2
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v10 07/11] virtio-sound: handle VIRTIO_SND_R_PCM_PREPARE
2023-09-29 14:08 [PATCH v10 00/11] Add VIRTIO sound card Emmanouil Pitsidianakis
` (5 preceding siblings ...)
2023-09-29 14:08 ` [PATCH v10 06/11] virtio-sound: handle VIRTIO_SND_R_PCM_SET_PARAMS Emmanouil Pitsidianakis
@ 2023-09-29 14:08 ` Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 08/11] virtio-sound: handle VIRTIO_SND_R_PCM_RELEASE Emmanouil Pitsidianakis
` (4 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Emmanouil Pitsidianakis @ 2023-09-29 14:08 UTC (permalink / raw)
To: qemu-devel
Cc: Emmanouil Pitsidianakis, Igor Skalkin, Anton Yakovlev,
Paolo Bonzini, Gerd Hoffmann, Michael S. Tsirkin,
Marcel Apfelbaum, Daniel P. Berrangé, Eduardo Habkost,
Marc-André Lureau, Volker Rümelin,
Kővágó, Zoltán, Alex Bennée,
Philippe Mathieu-Daudé, Mark Cave-Ayland
Handles the PCM prepare control request. It initializes a PCM stream
when the guests asks for it.
Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471
Signed-off-by: Igor Skalkin <Igor.Skalkin@opensynergy.com>
Signed-off-by: Anton Yakovlev <Anton.Yakovlev@opensynergy.com>
Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
---
hw/virtio/virtio-snd.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/hw/virtio/virtio-snd.c b/hw/virtio/virtio-snd.c
index 4b5cb6b01f..8ed0e9b130 100644
--- a/hw/virtio/virtio-snd.c
+++ b/hw/virtio/virtio-snd.c
@@ -478,6 +478,28 @@ static const char *print_code(uint32_t code)
#undef CASE
};
+/*
+ * Handles VIRTIO_SND_R_PCM_PREPARE.
+ *
+ * @s: VirtIOSound device
+ * @cmd: The request command queue element from VirtIOSound cmdq field
+ */
+static void virtio_snd_handle_pcm_prepare(VirtIOSound *s,
+ virtio_snd_ctrl_command *cmd)
+{
+ uint32_t stream_id;
+ size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
+ cmd->elem->out_num,
+ sizeof(virtio_snd_hdr),
+ &stream_id,
+ sizeof(stream_id));
+
+ stream_id = le32_to_cpu(stream_id);
+ cmd->resp.code = msg_sz == sizeof(stream_id)
+ ? virtio_snd_pcm_prepare(s, stream_id)
+ : cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+}
+
/*
* Handles VIRTIO_SND_R_PCM_START.
*
@@ -569,6 +591,8 @@ process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd)
virtio_snd_handle_pcm_set_params(s, cmd);
break;
case VIRTIO_SND_R_PCM_PREPARE:
+ virtio_snd_handle_pcm_prepare(s, cmd);
+ break;
case VIRTIO_SND_R_PCM_RELEASE:
cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
break;
--
2.39.2
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v10 08/11] virtio-sound: handle VIRTIO_SND_R_PCM_RELEASE
2023-09-29 14:08 [PATCH v10 00/11] Add VIRTIO sound card Emmanouil Pitsidianakis
` (6 preceding siblings ...)
2023-09-29 14:08 ` [PATCH v10 07/11] virtio-sound: handle VIRTIO_SND_R_PCM_PREPARE Emmanouil Pitsidianakis
@ 2023-09-29 14:08 ` Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 09/11] virtio-sound: implement audio output (TX) Emmanouil Pitsidianakis
` (3 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Emmanouil Pitsidianakis @ 2023-09-29 14:08 UTC (permalink / raw)
To: qemu-devel
Cc: Emmanouil Pitsidianakis, Igor Skalkin, Anton Yakovlev,
Paolo Bonzini, Gerd Hoffmann, Michael S. Tsirkin,
Marcel Apfelbaum, Daniel P. Berrangé, Eduardo Habkost,
Marc-André Lureau, Volker Rümelin,
Kővágó, Zoltán, Alex Bennée,
Philippe Mathieu-Daudé, Mark Cave-Ayland
Handle the PCM release control request, which is necessary for flushing
pending sound IO. No IO is handled yet so currently it only replies to
the request.
Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471
Signed-off-by: Igor Skalkin <Igor.Skalkin@opensynergy.com>
Signed-off-by: Anton Yakovlev <Anton.Yakovlev@opensynergy.com>
Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
---
hw/virtio/trace-events | 1 +
hw/virtio/virtio-snd.c | 48 +++++++++++++++++++++++++++++++++++++++++-
2 files changed, 48 insertions(+), 1 deletion(-)
diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
index f70cde4f01..60ab62a80d 100644
--- a/hw/virtio/trace-events
+++ b/hw/virtio/trace-events
@@ -167,6 +167,7 @@ virtio_snd_handle_pcm_set_params(uint32_t stream) "VIRTIO_SND_PCM_SET_PARAMS cal
virtio_snd_handle_ctrl(void *vdev, void *vq) "snd %p: handle ctrl event for queue %p"
virtio_snd_handle_pcm_info(uint32_t stream) "VIRTIO_SND_R_PCM_INFO called for stream %"PRIu32
virtio_snd_handle_pcm_start_stop(const char *code, uint32_t stream) "%s called for stream %"PRIu32
+virtio_snd_handle_pcm_release(uint32_t stream) "VIRTIO_SND_PCM_RELEASE called for stream %"PRIu32
virtio_snd_handle_code(uint32_t val, const char *code) "ctrl code msg val = %"PRIu32" == %s"
virtio_snd_handle_chmap_info(void) "VIRTIO_SND_CHMAP_INFO called"
virtio_snd_handle_event(void) "event queue callback called"
diff --git a/hw/virtio/virtio-snd.c b/hw/virtio/virtio-snd.c
index 8ed0e9b130..8cc910e0b9 100644
--- a/hw/virtio/virtio-snd.c
+++ b/hw/virtio/virtio-snd.c
@@ -541,6 +541,52 @@ static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
stream->active = start;
}
+/*
+ * Handles VIRTIO_SND_R_PCM_RELEASE. Releases the buffer resources allocated to
+ * a stream.
+ *
+ * @s: VirtIOSound device
+ * @cmd: The request command queue element from VirtIOSound cmdq field
+ */
+static void virtio_snd_handle_pcm_release(VirtIOSound *s,
+ virtio_snd_ctrl_command *cmd)
+{
+ uint32_t stream_id;
+ VirtIOSoundPCMStream *stream;
+ size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
+ cmd->elem->out_num,
+ sizeof(virtio_snd_hdr),
+ &stream_id,
+ sizeof(stream_id));
+
+ if (msg_sz != sizeof(stream_id)) {
+ /*
+ * TODO: do we need to set DEVICE_NEEDS_RESET?
+ */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: virtio-snd command size incorrect %zu vs \
+ %zu\n", __func__, msg_sz, sizeof(stream_id));
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ return;
+ }
+
+ stream_id = le32_to_cpu(stream_id);
+ trace_virtio_snd_handle_pcm_release(stream_id);
+ stream = virtio_snd_pcm_get_stream(s, stream_id);
+ if (stream == NULL) {
+ /*
+ * TODO: do we need to set DEVICE_NEEDS_RESET?
+ */
+ error_report("already released stream %"PRIu32, stream_id);
+ virtio_error(VIRTIO_DEVICE(s),
+ "already released stream %"PRIu32,
+ stream_id);
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ return;
+ }
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
+}
+
/*
* The actual processing done in virtio_snd_process_cmdq().
*
@@ -594,7 +640,7 @@ process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd)
virtio_snd_handle_pcm_prepare(s, cmd);
break;
case VIRTIO_SND_R_PCM_RELEASE:
- cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
+ virtio_snd_handle_pcm_release(s, cmd);
break;
case VIRTIO_SND_R_CHMAP_INFO:
qemu_log_mask(LOG_UNIMP,
--
2.39.2
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v10 09/11] virtio-sound: implement audio output (TX)
2023-09-29 14:08 [PATCH v10 00/11] Add VIRTIO sound card Emmanouil Pitsidianakis
` (7 preceding siblings ...)
2023-09-29 14:08 ` [PATCH v10 08/11] virtio-sound: handle VIRTIO_SND_R_PCM_RELEASE Emmanouil Pitsidianakis
@ 2023-09-29 14:08 ` Emmanouil Pitsidianakis
2023-09-29 18:59 ` Manos Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 10/11] virtio-sound: implement audio capture (RX) Emmanouil Pitsidianakis
` (2 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Emmanouil Pitsidianakis @ 2023-09-29 14:08 UTC (permalink / raw)
To: qemu-devel
Cc: Emmanouil Pitsidianakis, Igor Skalkin, Anton Yakovlev,
Paolo Bonzini, Gerd Hoffmann, Michael S. Tsirkin,
Marcel Apfelbaum, Daniel P. Berrangé, Eduardo Habkost,
Marc-André Lureau, Volker Rümelin,
Kővágó, Zoltán, Alex Bennée,
Philippe Mathieu-Daudé, Mark Cave-Ayland
Handle output IO messages in the transmit (TX) virtqueue.
It allocates a VirtIOSoundPCMBuffer for each IO message and copies the
data buffer to it. When the IO buffer is written to the host's sound
card, the guest will be notified that it has been consumed.
The lifetime of an IO message is:
1. Guest sends IO message to TX virtqueue.
2. QEMU adds it to the appropriate stream's IO buffer queue.
3. Sometime later, the host audio backend calls the output callback,
virtio_snd_pcm_out_cb(), which is defined with an AUD_open_out()
call. The callback gets an available number of bytes the backend can
receive. Then it writes data from the IO buffer queue to the backend.
If at any time a buffer is exhausted, it is returned to the guest as
completed.
4. If the guest releases the stream, its buffer queue is flushed by
attempting to write any leftover data to the audio backend and
releasing all IO messages back to the guest. This is how according to
the spec the guest knows the release was successful.
Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471
Signed-off-by: Igor Skalkin <Igor.Skalkin@opensynergy.com>
Signed-off-by: Anton Yakovlev <Anton.Yakovlev@opensynergy.com>
Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
---
hw/virtio/trace-events | 2 +
hw/virtio/virtio-snd.c | 289 ++++++++++++++++++++++++++++++++-
include/hw/virtio/virtio-snd.h | 47 ++++++
3 files changed, 333 insertions(+), 5 deletions(-)
diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
index 60ab62a80d..3b95e745c2 100644
--- a/hw/virtio/trace-events
+++ b/hw/virtio/trace-events
@@ -171,3 +171,5 @@ virtio_snd_handle_pcm_release(uint32_t stream) "VIRTIO_SND_PCM_RELEASE called fo
virtio_snd_handle_code(uint32_t val, const char *code) "ctrl code msg val = %"PRIu32" == %s"
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"
diff --git a/hw/virtio/virtio-snd.c b/hw/virtio/virtio-snd.c
index 8cc910e0b9..d9114930d6 100644
--- a/hw/virtio/virtio-snd.c
+++ b/hw/virtio/virtio-snd.c
@@ -32,6 +32,10 @@
#define VIRTIO_SOUND_CHMAP_DEFAULT 0
#define VIRTIO_SOUND_HDA_FN_NID 0
+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 supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8)
| BIT(VIRTIO_SND_PCM_FMT_U8)
| BIT(VIRTIO_SND_PCM_FMT_S16)
@@ -123,6 +127,13 @@ virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
}
+static void
+virtio_snd_pcm_buffer_free(VirtIOSoundPCMBuffer *buffer)
+{
+ g_free(buffer->elem);
+ g_free(buffer);
+}
+
static void
virtio_snd_ctrl_cmd_free(virtio_snd_ctrl_command *cmd)
{
@@ -396,6 +407,13 @@ static void virtio_snd_get_qemu_audsettings(audsettings *as,
*/
static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream)
{
+ if (stream) {
+ if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
+ virtio_snd_pcm_flush(stream);
+ AUD_close_out(&stream->pcm->snd->card, stream->voice.out);
+ stream->voice.out = NULL;
+ }
+ }
}
/*
@@ -429,6 +447,9 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
stream->id = stream_id;
stream->pcm = s->pcm;
stream->s = s;
+ qemu_mutex_init(&stream->queue_mutex);
+ QSIMPLEQ_INIT(&stream->queue);
+ QSIMPLEQ_INIT(&stream->invalid);
/*
* stream_id >= s->snd_conf.streams was checked before so this is
@@ -452,6 +473,18 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
stream->positions[1] = VIRTIO_SND_CHMAP_FR;
stream->as = as;
+ if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
+ stream->voice.out = AUD_open_out(&s->card,
+ stream->voice.out,
+ "virtio-sound.out",
+ stream,
+ virtio_snd_pcm_out_cb,
+ &as);
+ AUD_set_volume_out(stream->voice.out, 0, 255, 255);
+ } else {
+ qemu_log_mask(LOG_UNIMP, "virtio_snd: input/capture is unimplemented.");
+ }
+
return cpu_to_le32(VIRTIO_SND_S_OK);
}
@@ -532,9 +565,17 @@ static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
trace_virtio_snd_handle_pcm_start_stop(start ? "VIRTIO_SND_R_PCM_START" :
"VIRTIO_SND_R_PCM_STOP", stream_id);
+
stream = virtio_snd_pcm_get_stream(s, stream_id);
- if (stream == NULL) {
- error_report("Invalid stream id: %"PRIu32, req.stream_id);
+ if (stream) {
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
+ stream->active = start;
+ }
+ if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
+ AUD_set_active_out(stream->voice.out, start);
+ }
+ } else {
+ error_report("Invalid stream id: %"PRIu32, stream_id);
cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
return;
}
@@ -542,8 +583,28 @@ static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
}
/*
- * Handles VIRTIO_SND_R_PCM_RELEASE. Releases the buffer resources allocated to
- * a stream.
+ * Returns the number of I/O messages that are being processed.
+ *
+ * @stream: VirtIOSoundPCMStream
+ */
+static size_t virtio_snd_pcm_get_io_msgs_count(VirtIOSoundPCMStream *stream)
+{
+ VirtIOSoundPCMBuffer *buffer, *next;
+ size_t count = 0;
+
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
+ QSIMPLEQ_FOREACH_SAFE(buffer, &stream->queue, entry, next) {
+ count += 1;
+ }
+ QSIMPLEQ_FOREACH_SAFE(buffer, &stream->invalid, entry, next) {
+ count += 1;
+ }
+ }
+ return count;
+}
+
+/*
+ * Handles VIRTIO_SND_R_PCM_RELEASE.
*
* @s: VirtIOSound device
* @cmd: The request command queue element from VirtIOSound cmdq field
@@ -584,6 +645,21 @@ static void virtio_snd_handle_pcm_release(VirtIOSound *s,
cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
return;
}
+
+ if (virtio_snd_pcm_get_io_msgs_count(stream)) {
+ /*
+ * virtio-v1.2-csd01, 5.14.6.6.5.1,
+ * Device Requirements: Stream Release
+ *
+ * - The device MUST complete all pending I/O messages for the
+ * specified stream ID.
+ * - The device MUST NOT complete the control request while there
+ * are pending I/O messages for the specified stream ID.
+ */
+ trace_virtio_snd_pcm_stream_flush(stream_id);
+ virtio_snd_pcm_flush(stream);
+ }
+
cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
}
@@ -738,6 +814,108 @@ static void virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq)
trace_virtio_snd_handle_event();
}
+/*
+ * The tx virtqueue handler. Makes the buffers available to their respective
+ * streams for consumption.
+ *
+ * @vdev: VirtIOSound device
+ * @vq: tx virtqueue
+ */
+static void virtio_snd_handle_tx(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOSound *s = VIRTIO_SND(vdev);
+ VirtIOSoundPCMStream *stream = NULL;
+ VirtIOSoundPCMBuffer *buffer;
+ 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
+ * return them after the for loop.
+ */
+ bool must_empty_invalid_queue = false;
+
+ if (!virtio_queue_ready(vq)) {
+ return;
+ }
+ trace_virtio_snd_handle_tx_xfer();
+
+ for (;;) {
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+ if (!elem) {
+ break;
+ }
+ /* get the message hdr object */
+ msg_sz = iov_to_buf(elem->out_sg,
+ elem->out_num,
+ 0,
+ &hdr,
+ sizeof(virtio_snd_pcm_xfer));
+ if (msg_sz != sizeof(virtio_snd_pcm_xfer)) {
+ goto tx_err;
+ }
+ stream_id = le32_to_cpu(hdr.stream_id);
+
+ if (stream_id >= s->snd_conf.streams
+ || s->pcm->streams[stream_id] == NULL) {
+ goto tx_err;
+ }
+
+ stream = s->pcm->streams[stream_id];
+ if (stream->info.direction != VIRTIO_SND_D_OUTPUT) {
+ goto tx_err;
+ }
+
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
+ size = iov_size(elem->out_sg, elem->out_num);
+
+ buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
+ buffer->elem = elem;
+ buffer->stale = true;
+ buffer->vq = vq;
+ buffer->size = size;
+ buffer->offset = sizeof(virtio_snd_pcm_xfer);
+
+ QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry);
+ }
+ continue;
+
+tx_err:
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
+ must_empty_invalid_queue = true;
+ buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer));
+ buffer->elem = elem;
+ buffer->vq = vq;
+ QSIMPLEQ_INSERT_TAIL(&stream->invalid, buffer, entry);
+ }
+ }
+
+ 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);
+ }
+ }
+}
+
/*
* Stub buffer virtqueue handler.
*
@@ -832,7 +1010,7 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp)
vsnd->queues[VIRTIO_SND_VQ_EVENT] =
virtio_add_queue(vdev, 64, virtio_snd_handle_event);
vsnd->queues[VIRTIO_SND_VQ_TX] =
- virtio_add_queue(vdev, 64, virtio_snd_handle_xfer);
+ virtio_add_queue(vdev, 64, virtio_snd_handle_tx);
vsnd->queues[VIRTIO_SND_VQ_RX] =
virtio_add_queue(vdev, 64, virtio_snd_handle_xfer);
qemu_mutex_init(&vsnd->cmdq_mutex);
@@ -856,6 +1034,106 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp)
}
}
+/*
+ * AUD_* output callback.
+ *
+ * @data: VirtIOSoundPCMStream stream
+ * @available: number of bytes that can be written with AUD_write()
+ */
+static void virtio_snd_pcm_out_cb(void *data, int available)
+{
+ VirtIOSoundPCMStream *stream = data;
+ VirtIOSoundPCMBuffer *buffer;
+ size_t size;
+
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
+ while (!QSIMPLEQ_EMPTY(&stream->queue)) {
+ buffer = QSIMPLEQ_FIRST(&stream->queue);
+ if (!virtio_queue_ready(buffer->vq)) {
+ return;
+ }
+ if (!stream->active) {
+ /* Stream has stopped, so do not perform AUD_write. */
+ goto return_tx_buffer;
+ }
+ if (buffer->stale) {
+ iov_to_buf(buffer->elem->out_sg,
+ buffer->elem->out_num,
+ buffer->offset,
+ buffer->data,
+ buffer->size);
+ buffer->stale = false;
+ }
+ for (;;) {
+ size = AUD_write(stream->voice.out,
+ buffer->data + buffer->offset,
+ MIN(buffer->size, available));
+ assert(size <= MIN(buffer->size, available));
+ if (size == 0) {
+ /* break out of both loops */
+ available = 0;
+ break;
+ }
+ buffer->size -= size;
+ 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);
+ break;
+ }
+ if (!available) {
+ break;
+ }
+ }
+ if (!available) {
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Flush all buffer data from this stream's queue into the driver's virtual
+ * queue.
+ *
+ * @stream: VirtIOSoundPCMStream *stream
+ */
+static void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream)
+{
+ VirtIOSoundPCMBuffer *buffer, *next;
+
+ 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);
+ }
+ }
+}
+
static void virtio_snd_unrealize(DeviceState *dev)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
@@ -872,6 +1150,7 @@ static void virtio_snd_unrealize(DeviceState *dev)
if (stream) {
virtio_snd_process_cmdq(stream->s);
virtio_snd_pcm_close(stream);
+ qemu_mutex_destroy(&stream->queue_mutex);
g_free(stream);
}
}
diff --git a/include/hw/virtio/virtio-snd.h b/include/hw/virtio/virtio-snd.h
index 4b4aaffff1..327d622066 100644
--- a/include/hw/virtio/virtio-snd.h
+++ b/include/hw/virtio/virtio-snd.h
@@ -77,6 +77,50 @@ typedef struct virtio_snd_ctrl_command virtio_snd_ctrl_command;
typedef struct VirtIOSoundPCM VirtIOSoundPCM;
+typedef struct VirtIOSoundPCMBuffer VirtIOSoundPCMBuffer;
+
+/*
+ * The VirtIO sound spec reuses layouts and values from the High Definition
+ * Audio spec (virtio/v1.2: 5.14 Sound Device). This struct handles each I/O
+ * message's buffer (virtio/v1.2: 5.14.6.8 PCM I/O Messages).
+ *
+ * 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.
+ *
+ * VirtIOSoundPCMBuffer has a dynamic size since it includes the raw PCM data
+ * in its allocation. It must be initialized and destroyed as follows:
+ *
+ * size_t size = [[derived from owned VQ element descriptor sizes]];
+ * buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
+ * buffer->elem = [[owned VQ element]];
+ *
+ * [..]
+ *
+ * g_free(buffer->elem);
+ * g_free(buffer);
+ */
+struct VirtIOSoundPCMBuffer {
+ QSIMPLEQ_ENTRY(VirtIOSoundPCMBuffer) entry;
+ VirtQueueElement *elem;
+ VirtQueue *vq;
+ size_t size;
+ /*
+ * In TX / Plaback, `offset` represents the first unused position inside
+ * `data`. If `offset == size` then there are no unused data left.
+ */
+ uint64_t offset;
+ /* Used for the TX queue for lazy I/O copy from `elem` */
+ bool stale;
+ /*
+ * 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
+ * or write-only size when allocating VirtIOSoundPCMBuffer.
+ */
+ uint8_t data[];
+};
+
struct VirtIOSoundPCM {
VirtIOSound *snd;
/*
@@ -104,7 +148,10 @@ struct VirtIOSoundPCMStream {
SWVoiceIn *in;
SWVoiceOut *out;
} voice;
+ QemuMutex queue_mutex;
bool active;
+ QSIMPLEQ_HEAD(, VirtIOSoundPCMBuffer) queue;
+ QSIMPLEQ_HEAD(, VirtIOSoundPCMBuffer) invalid;
};
/*
--
2.39.2
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v10 10/11] virtio-sound: implement audio capture (RX)
2023-09-29 14:08 [PATCH v10 00/11] Add VIRTIO sound card Emmanouil Pitsidianakis
` (8 preceding siblings ...)
2023-09-29 14:08 ` [PATCH v10 09/11] virtio-sound: implement audio output (TX) Emmanouil Pitsidianakis
@ 2023-09-29 14:08 ` Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 11/11] docs/system: add basic virtio-snd documentation Emmanouil Pitsidianakis
2023-10-03 14:10 ` [PATCH v10 00/11] Add VIRTIO sound card Michael S. Tsirkin
11 siblings, 0 replies; 21+ messages in thread
From: Emmanouil Pitsidianakis @ 2023-09-29 14:08 UTC (permalink / raw)
To: qemu-devel
Cc: Emmanouil Pitsidianakis, Igor Skalkin, Anton Yakovlev,
Paolo Bonzini, Gerd Hoffmann, Michael S. Tsirkin,
Marcel Apfelbaum, Daniel P. Berrangé, Eduardo Habkost,
Marc-André Lureau, Volker Rümelin,
Kővágó, Zoltán, Alex Bennée,
Philippe Mathieu-Daudé, Mark Cave-Ayland
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>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
---
hw/virtio/trace-events | 3 +-
hw/virtio/virtio-snd.c | 297 +++++++++++++++++++++++++++++++++++++----
2 files changed, 270 insertions(+), 30 deletions(-)
diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
index 3b95e745c2..9b7fbffedc 100644
--- a/hw/virtio/trace-events
+++ b/hw/virtio/trace-events
@@ -172,4 +172,5 @@ 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"
diff --git a/hw/virtio/virtio-snd.c b/hw/virtio/virtio-snd.c
index d9114930d6..b857b69f00 100644
--- a/hw/virtio/virtio-snd.c
+++ b/hw/virtio/virtio-snd.c
@@ -28,13 +28,15 @@
#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
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_in_cb(void *data, int available);
static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8)
| BIT(VIRTIO_SND_PCM_FMT_U8)
@@ -409,9 +411,13 @@ static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream)
{
if (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;
}
}
}
@@ -482,7 +488,13 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
&as);
AUD_set_volume_out(stream->voice.out, 0, 255, 255);
} else {
- qemu_log_mask(LOG_UNIMP, "virtio_snd: input/capture is unimplemented.");
+ stream->voice.in = AUD_open_in(&s->card,
+ stream->voice.in,
+ "virtio-sound.in",
+ stream,
+ virtio_snd_pcm_in_cb,
+ &as);
+ AUD_set_volume_in(stream->voice.in, 0, 255, 255);
}
return cpu_to_le32(VIRTIO_SND_S_OK);
@@ -573,6 +585,8 @@ static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
}
if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
AUD_set_active_out(stream->voice.out, start);
+ } else {
+ AUD_set_active_in(stream->voice.in, start);
}
} else {
error_report("Invalid stream id: %"PRIu32, stream_id);
@@ -657,7 +671,11 @@ 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);
+ }
}
cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
@@ -821,7 +839,7 @@ static void virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq)
* @vdev: VirtIOSound device
* @vq: tx virtqueue
*/
-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;
@@ -917,12 +935,103 @@ tx_err:
}
/*
- * Stub buffer virtqueue handler.
+ * The rx virtqueue handler. Makes the buffers available to their respective
+ * streams for consumption.
*
* @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);
+ VirtIOSoundPCMStream *stream = NULL;
+ VirtIOSoundPCMBuffer *buffer;
+ 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
+ * return them after the for loop.
+ */
+ bool must_empty_invalid_queue = false;
+
+ if (!virtio_queue_ready(vq)) {
+ return;
+ }
+ trace_virtio_snd_handle_rx_xfer();
+
+ for (;;) {
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+ if (!elem) {
+ break;
+ }
+ /* get the message hdr object */
+ msg_sz = iov_to_buf(elem->out_sg,
+ elem->out_num,
+ 0,
+ &hdr,
+ sizeof(virtio_snd_pcm_xfer));
+ if (msg_sz != sizeof(virtio_snd_pcm_xfer)) {
+ goto rx_err;
+ }
+ stream_id = le32_to_cpu(hdr.stream_id);
+
+ if (stream_id >= s->snd_conf.streams
+ || !s->pcm->streams[stream_id]) {
+ goto rx_err;
+ }
+
+ stream = s->pcm->streams[stream_id];
+ if (stream == NULL || stream->info.direction != VIRTIO_SND_D_INPUT) {
+ goto rx_err;
+ }
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
+ size = iov_size(elem->in_sg, elem->in_num) -
+ sizeof(virtio_snd_pcm_status);
+ buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
+ buffer->elem = elem;
+ buffer->vq = vq;
+ buffer->size = 0;
+ buffer->offset = 0;
+ QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry);
+ }
+ continue;
+
+rx_err:
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
+ must_empty_invalid_queue = true;
+ buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer));
+ buffer->elem = elem;
+ buffer->vq = vq;
+ QSIMPLEQ_INSERT_TAIL(&stream->invalid, buffer, entry);
+ }
+ }
+
+ 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);
+ }
+ }
+}
static uint64_t get_features(VirtIODevice *vdev, uint64_t features,
Error **errp)
@@ -1010,9 +1119,9 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp)
vsnd->queues[VIRTIO_SND_VQ_EVENT] =
virtio_add_queue(vdev, 64, virtio_snd_handle_event);
vsnd->queues[VIRTIO_SND_VQ_TX] =
- virtio_add_queue(vdev, 64, virtio_snd_handle_tx);
+ virtio_add_queue(vdev, 64, virtio_snd_handle_tx_xfer);
vsnd->queues[VIRTIO_SND_VQ_RX] =
- virtio_add_queue(vdev, 64, virtio_snd_handle_xfer);
+ virtio_add_queue(vdev, 64, virtio_snd_handle_rx_xfer);
qemu_mutex_init(&vsnd->cmdq_mutex);
QTAILQ_INIT(&vsnd->cmdq);
@@ -1107,33 +1216,163 @@ return_tx_buffer:
}
/*
- * Flush all buffer data from this stream's queue into the driver's virtual
- * queue.
+ * 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);
+ while (!QSIMPLEQ_EMPTY(&stream->queue)) {
+ buffer = QSIMPLEQ_FIRST(&stream->queue);
+ if (!virtio_queue_ready(buffer->vq)) {
+ return;
+ }
+ if (!stream->active) {
+ /* Stream has stopped, so do not perform AUD_read. */
+ goto return_rx_buffer;
+ }
+
+ for (;;) {
+ size = AUD_read(stream->voice.in,
+ buffer->data + buffer->size,
+ MIN(available, (stream->params.period_bytes -
+ buffer->size)));
+ if (!size) {
+ available = 0;
+ break;
+ }
+ 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);
+ break;
+ }
+ if (!available) {
+ break;
+ }
+ }
+ 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
+ * 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)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
--
2.39.2
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v10 11/11] docs/system: add basic virtio-snd documentation
2023-09-29 14:08 [PATCH v10 00/11] Add VIRTIO sound card Emmanouil Pitsidianakis
` (9 preceding siblings ...)
2023-09-29 14:08 ` [PATCH v10 10/11] virtio-sound: implement audio capture (RX) Emmanouil Pitsidianakis
@ 2023-09-29 14:08 ` Emmanouil Pitsidianakis
2023-10-03 14:10 ` [PATCH v10 00/11] Add VIRTIO sound card Michael S. Tsirkin
11 siblings, 0 replies; 21+ messages in thread
From: Emmanouil Pitsidianakis @ 2023-09-29 14:08 UTC (permalink / raw)
To: qemu-devel
Cc: Emmanouil Pitsidianakis, Igor Skalkin, Anton Yakovlev,
Paolo Bonzini, Gerd Hoffmann, Michael S. Tsirkin,
Marcel Apfelbaum, Daniel P. Berrangé, Eduardo Habkost,
Marc-André Lureau, Volker Rümelin,
Kővágó, Zoltán, Alex Bennée,
Philippe Mathieu-Daudé, Mark Cave-Ayland
This commit adds basic documentation for using virtio-snd.
Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
---
MAINTAINERS | 1 +
docs/system/device-emulation.rst | 1 +
docs/system/devices/virtio-snd.rst | 49 ++++++++++++++++++++++++++++++
3 files changed, 51 insertions(+)
create mode 100644 docs/system/devices/virtio-snd.rst
diff --git a/MAINTAINERS b/MAINTAINERS
index 81ca61e90b..7c5a47bed5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2246,6 +2246,7 @@ M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
S: Supported
F: hw/virtio/virtio-snd*.c
F: include/hw/virtio/virtio-snd.h
+F: docs/system/devices/virtio-snd.rst
nvme
M: Keith Busch <kbusch@kernel.org>
diff --git a/docs/system/device-emulation.rst b/docs/system/device-emulation.rst
index 4491c4cbf7..dae19446e5 100644
--- a/docs/system/device-emulation.rst
+++ b/docs/system/device-emulation.rst
@@ -92,6 +92,7 @@ Emulated Devices
devices/usb.rst
devices/vhost-user.rst
devices/virtio-pmem.rst
+ devices/virtio-snd.rst
devices/vhost-user-rng.rst
devices/canokey.rst
devices/usb-u2f.rst
diff --git a/docs/system/devices/virtio-snd.rst b/docs/system/devices/virtio-snd.rst
new file mode 100644
index 0000000000..2a9187fd70
--- /dev/null
+++ b/docs/system/devices/virtio-snd.rst
@@ -0,0 +1,49 @@
+virtio sound
+============
+
+This document explains the setup and usage of the Virtio sound device.
+The Virtio sound device is a paravirtualized sound card device.
+
+Linux kernel support
+--------------------
+
+Virtio sound requires a guest Linux kernel built with the
+``CONFIG_SND_VIRTIO`` option.
+
+Description
+-----------
+
+Virtio sound implements capture and playback from inside a guest using the
+configured audio backend of the host machine.
+
+Device properties
+-----------------
+
+The Virtio sound device can be configured with the following properties:
+
+ * ``jacks`` number of physical jacks (Unimplemented).
+ * ``streams`` number of PCM streams. At the moment, no stream configuration is supported: the first one will always be a playback stream, an optional second will always be a capture stream. Adding more will cycle stream directions from playback to capture.
+ * ``chmaps`` number of channel maps (Unimplemented).
+
+All streams are stereo and have the default channel positions ``Front left, right``.
+
+Examples
+--------
+
+Add an audio device and an audio backend at once with ``-audio`` and ``model=virtio``:
+
+ * pulseaudio: ``-audio driver=pa,model=virtio``
+ or ``-audio driver=pa,model=virtio,server=/run/user/1000/pulse/native``
+ * sdl: ``-audio driver=sdl,model=virtio``
+ * coreaudio: ``-audio driver=coreaudio,model=virtio``
+
+etc.
+
+To specifically add virtualized sound devices, you have to specify a PCI device
+and an audio backend listed with ``-audio driver=help`` that works on your host
+machine, e.g.:
+
+::
+
+ -device virtio-sound-pci,audiodev=my_audiodev \
+ -audiodev alsa,id=my_audiodev
--
2.39.2
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v10 09/11] virtio-sound: implement audio output (TX)
2023-09-29 14:08 ` [PATCH v10 09/11] virtio-sound: implement audio output (TX) Emmanouil Pitsidianakis
@ 2023-09-29 18:59 ` Manos Pitsidianakis
2023-10-03 14:27 ` Philippe Mathieu-Daudé
0 siblings, 1 reply; 21+ messages in thread
From: Manos Pitsidianakis @ 2023-09-29 18:59 UTC (permalink / raw)
To: qemu-devel
Cc: Emmanouil Pitsidianakis, Igor Skalkin, Anton Yakovlev,
Paolo Bonzini, Gerd Hoffmann, Michael S. Tsirkin,
Marcel Apfelbaum, Daniel P. Berrangé , Eduardo Habkost,
Marc-André Lureau, Volker Rü melin,
Kő vá gó , Zoltá n, Alex Benné e,
Philippe Mathieu-Daudé , Mark Cave-Ayland
On Fri, 29 Sep 2023 17:08, Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org> wrote:
>Handle output IO messages in the transmit (TX) virtqueue.
[..]
>+ if (!stream->active) {
>+ /* Stream has stopped, so do not perform AUD_write. */
>+ goto return_tx_buffer;
>+ }
[..]
>+return_tx_buffer:
>+ virtio_snd_pcm_status resp = { 0 };
>+ resp.status = cpu_to_le32(VIRTIO_SND_S_OK);
It seems I was too hasty to submit this patch. It does not build with
clang on macos because it does not allow labels before declarations.
It needs the following changes to compile:
--- a/hw/virtio/virtio-snd.c
+++ b/hw/virtio/virtio-snd.c
@@ -1187,7 +1187,7 @@ static void virtio_snd_pcm_out_cb(void *data, int available)
buffer->offset += size;
available -= size;
if (buffer->size < 1) {
-return_tx_buffer:
+return_tx_buffer:;
virtio_snd_pcm_status resp = { 0 };
resp.status = cpu_to_le32(VIRTIO_SND_S_OK);
resp.latency_bytes = 0;
--- a/hw/virtio/virtio-snd.c
+++ b/hw/virtio/virtio-snd.c
@@ -1251,7 +1251,7 @@ static void virtio_snd_pcm_in_cb(void *data, int available)
buffer->size += size;
available -= size;
if (buffer->size >= stream->params.period_bytes) {
-return_rx_buffer:
+return_rx_buffer:;
resp.status = cpu_to_le32(VIRTIO_SND_S_OK);
resp.latency_bytes = 0;
/* Copy data -if any- to guest */
- Manos
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v10 00/11] Add VIRTIO sound card
2023-09-29 14:08 [PATCH v10 00/11] Add VIRTIO sound card Emmanouil Pitsidianakis
` (10 preceding siblings ...)
2023-09-29 14:08 ` [PATCH v10 11/11] docs/system: add basic virtio-snd documentation Emmanouil Pitsidianakis
@ 2023-10-03 14:10 ` Michael S. Tsirkin
2023-10-03 16:09 ` Manos Pitsidianakis
11 siblings, 1 reply; 21+ messages in thread
From: Michael S. Tsirkin @ 2023-10-03 14:10 UTC (permalink / raw)
To: Emmanouil Pitsidianakis
Cc: qemu-devel, Igor Skalkin, Anton Yakovlev, Paolo Bonzini,
Gerd Hoffmann, Marcel Apfelbaum, Daniel P. Berrangé,
Eduardo Habkost, Marc-André Lureau, Volker Rümelin,
Kővágó, Zoltán, Alex Bennée,
Philippe Mathieu-Daudé, Mark Cave-Ayland
On Fri, Sep 29, 2023 at 05:08:20PM +0300, Emmanouil Pitsidianakis wrote:
> This patch series adds an audio device implementing the recent virtio
> sound spec (1.2) and a corresponding PCI wrapper device.
>
> v10 can be found online at:
>
> https://gitlab.com/epilys/qemu/-/tree/virtio-snd-v10
>
> Ref b720e00121878cb91c9690fd1f9ca609b9484346
>
> Main differences with v9 patch series [^v9]
> <cover.1694588927.git.manos.pitsidianakis@linaro.org>:
>
> - Addressed [^v9] review comments.
> - Copy buffer data just before playing it on the host instead of when
> the IO message arrives. This in most cases takes care of the buffer
> not being populated with data from the guest application when it
> firsts arrives.
I hope you are also fixing the linux driver to fix this in a robust
way, yes?
> Previously:
>
> [^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 | 49 +
> hw/virtio/Kconfig | 5 +
> hw/virtio/meson.build | 2 +
> hw/virtio/trace-events | 20 +
> hw/virtio/virtio-snd-pci.c | 93 ++
> hw/virtio/virtio-snd.c | 1455 ++++++++++++++++++++++++++++
> include/hw/virtio/virtio-snd.h | 235 +++++
> softmmu/qdev-monitor.c | 1 +
> 10 files changed, 1868 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 v9:
> 1: 5173e2c243 = 1: 6e7bdf6dda Add virtio-sound device stub
> 2: dd3acea293 ! 2: 82138b9c7d Add virtio-sound-pci device
> @@ hw/virtio/virtio-snd-pci.c (new)
> +
> +type_init(virtio_snd_pci_register);
>
> + ## hw/virtio/virtio-snd.c ##
> +@@ hw/virtio/virtio-snd.c: static void
> + virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config)
> + {
> + VirtIOSound *s = VIRTIO_SND(vdev);
> ++ virtio_snd_config *sndconfig =
> ++ (virtio_snd_config *)config;
> + trace_virtio_snd_get_config(vdev,
> + s->snd_conf.jacks,
> + s->snd_conf.streams,
> + s->snd_conf.chmaps);
> +
> +- memcpy(config, &s->snd_conf, sizeof(s->snd_conf));
> ++ memcpy(sndconfig, &s->snd_conf, sizeof(s->snd_conf));
> ++ cpu_to_le32s(&sndconfig->jacks);
> ++ cpu_to_le32s(&sndconfig->streams);
> ++ cpu_to_le32s(&sndconfig->chmaps);
> ++
> + }
> +
> + static void
> +@@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
> + s->snd_conf.chmaps,
> + sndconfig->chmaps);
> +
> +- memcpy(&s->snd_conf, sndconfig, sizeof(s->snd_conf));
> ++ memcpy(&s->snd_conf, sndconfig, sizeof(virtio_snd_config));
> ++ le32_to_cpus(&s->snd_conf.jacks);
> ++ le32_to_cpus(&s->snd_conf.streams);
> ++ le32_to_cpus(&s->snd_conf.chmaps);
> ++
> + }
> +
> + /*
> +
> ## softmmu/qdev-monitor.c ##
> @@ softmmu/qdev-monitor.c: static const QDevAlias qdev_alias_table[] = {
> { "virtio-serial-device", "virtio-serial", QEMU_ARCH_VIRTIO_MMIO },
> 3: a75dc75f24 ! 3: c1a2cb0304 virtio-sound: handle control messages and streams
> @@ hw/virtio/trace-events: virtio_snd_vm_state_running(void) "vm state running"
> +virtio_snd_handle_event(void) "event queue callback called"
>
> ## hw/virtio/virtio-snd.c ##
> +@@
> + #define VIRTIO_SOUND_CHMAP_DEFAULT 0
> + #define VIRTIO_SOUND_HDA_FN_NID 0
> +
> ++static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8)
> ++ | BIT(VIRTIO_SND_PCM_FMT_U8)
> ++ | BIT(VIRTIO_SND_PCM_FMT_S16)
> ++ | BIT(VIRTIO_SND_PCM_FMT_U16)
> ++ | BIT(VIRTIO_SND_PCM_FMT_S32)
> ++ | BIT(VIRTIO_SND_PCM_FMT_U32)
> ++ | BIT(VIRTIO_SND_PCM_FMT_FLOAT);
> ++
> ++static uint32_t supported_rates = BIT(VIRTIO_SND_PCM_RATE_5512)
> ++ | BIT(VIRTIO_SND_PCM_RATE_8000)
> ++ | BIT(VIRTIO_SND_PCM_RATE_11025)
> ++ | BIT(VIRTIO_SND_PCM_RATE_16000)
> ++ | BIT(VIRTIO_SND_PCM_RATE_22050)
> ++ | BIT(VIRTIO_SND_PCM_RATE_32000)
> ++ | BIT(VIRTIO_SND_PCM_RATE_44100)
> ++ | BIT(VIRTIO_SND_PCM_RATE_48000)
> ++ | BIT(VIRTIO_SND_PCM_RATE_64000)
> ++ | BIT(VIRTIO_SND_PCM_RATE_88200)
> ++ | BIT(VIRTIO_SND_PCM_RATE_96000)
> ++ | BIT(VIRTIO_SND_PCM_RATE_176400)
> ++ | BIT(VIRTIO_SND_PCM_RATE_192000)
> ++ | BIT(VIRTIO_SND_PCM_RATE_384000);
> ++
> + static const VMStateDescription vmstate_virtio_snd_device = {
> + .name = TYPE_VIRTIO_SND,
> + .version_id = VIRTIO_SOUND_VM_VERSION,
> @@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
> - memcpy(&s->snd_conf, sndconfig, sizeof(s->snd_conf));
> +
> }
>
> +static void
> @@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const uint8_t
> + g_free(cmd);
> +}
> +
> ++/*
> ++ * Get a specific stream from the virtio sound card device.
> ++ * Returns NULL if @stream_id is invalid or not allocated.
> ++ *
> ++ * @s: VirtIOSound device
> ++ * @stream_id: stream id
> ++ */
> ++static VirtIOSoundPCMStream *virtio_snd_pcm_get_stream(VirtIOSound *s,
> ++ uint32_t stream_id)
> ++{
> ++ return stream_id >= s->snd_conf.streams ? NULL :
> ++ s->pcm->streams[stream_id];
> ++}
> ++
> ++/*
> ++ * Get params for a specific stream.
> ++ *
> ++ * @s: VirtIOSound device
> ++ * @stream_id: stream id
> ++ */
> ++static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s,
> ++ uint32_t stream_id)
> ++{
> ++ return stream_id >= s->snd_conf.streams ? NULL
> ++ : &s->pcm->pcm_params[stream_id];
> ++}
> ++
> ++/*
> ++ * Set the given stream params.
> ++ * Called by both virtio_snd_handle_pcm_set_params and during device
> ++ * initialization.
> ++ * Returns the response status code. (VIRTIO_SND_S_*).
> ++ *
> ++ * @s: VirtIOSound device
> ++ * @params: The PCM params as defined in the virtio specification
> ++ */
> ++static
> ++uint32_t virtio_snd_set_pcm_params(VirtIOSound *s,
> ++ uint32_t stream_id,
> ++ virtio_snd_pcm_set_params *params)
> ++{
> ++ virtio_snd_pcm_set_params *st_params;
> ++
> ++ if (stream_id >= s->snd_conf.streams || s->pcm->pcm_params == NULL) {
> ++ /*
> ++ * TODO: do we need to set DEVICE_NEEDS_RESET?
> ++ */
> ++ virtio_error(VIRTIO_DEVICE(s), "Streams have not been initialized.\n");
> ++ return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> ++ }
> ++
> ++ st_params = virtio_snd_pcm_get_params(s, stream_id);
> ++
> ++ if (params->channels < 1 || params->channels > AUDIO_MAX_CHANNELS) {
> ++ error_report("Number of channels is not supported.");
> ++ return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
> ++ }
> ++ if (!(supported_formats & BIT(params->format))) {
> ++ error_report("Stream format is not supported.");
> ++ return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
> ++ }
> ++ if (!(supported_rates & BIT(params->rate))) {
> ++ error_report("Stream rate is not supported.");
> ++ return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
> ++ }
> ++
> ++ st_params->buffer_bytes = le32_to_cpu(params->buffer_bytes);
> ++ st_params->period_bytes = le32_to_cpu(params->period_bytes);
> ++ st_params->features = le32_to_cpu(params->features);
> ++ /* the following are uint8_t, so there's no need to bswap the values. */
> ++ st_params->channels = params->channels;
> ++ st_params->format = params->format;
> ++ st_params->rate = params->rate;
> ++
> ++ return cpu_to_le32(VIRTIO_SND_S_OK);
> ++}
> ++
> ++/*
> ++ * Get a QEMU Audiosystem compatible format value from a VIRTIO_SND_PCM_FMT_*
> ++ */
> ++static AudioFormat virtio_snd_get_qemu_format(uint32_t format)
> ++{
> ++ #define CASE(FMT) \
> ++ case VIRTIO_SND_PCM_FMT_##FMT: \
> ++ return AUDIO_FORMAT_##FMT;
> ++
> ++ switch (format) {
> ++ CASE(U8)
> ++ CASE(S8)
> ++ CASE(U16)
> ++ CASE(S16)
> ++ CASE(U32)
> ++ CASE(S32)
> ++ case VIRTIO_SND_PCM_FMT_FLOAT:
> ++ return AUDIO_FORMAT_F32;
> ++ default:
> ++ g_assert_not_reached();
> ++ }
> ++
> ++ #undef CASE
> ++}
> ++
> ++/*
> ++ * Get a QEMU Audiosystem compatible frequency value from a
> ++ * VIRTIO_SND_PCM_RATE_*
> ++ */
> ++static uint32_t virtio_snd_get_qemu_freq(uint32_t rate)
> ++{
> ++ #define CASE(RATE) \
> ++ case VIRTIO_SND_PCM_RATE_##RATE: \
> ++ return RATE;
> ++
> ++ switch (rate) {
> ++ CASE(5512)
> ++ CASE(8000)
> ++ CASE(11025)
> ++ CASE(16000)
> ++ CASE(22050)
> ++ CASE(32000)
> ++ CASE(44100)
> ++ CASE(48000)
> ++ CASE(64000)
> ++ CASE(88200)
> ++ CASE(96000)
> ++ CASE(176400)
> ++ CASE(192000)
> ++ CASE(384000)
> ++ default:
> ++ g_assert_not_reached();
> ++ }
> ++
> ++ #undef CASE
> ++}
> ++
> ++/*
> ++ * Get QEMU Audiosystem compatible audsettings from virtio based pcm stream
> ++ * params.
> ++ */
> ++static void virtio_snd_get_qemu_audsettings(audsettings *as,
> ++ virtio_snd_pcm_set_params *params)
> ++{
> ++ 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 = target_words_bigendian() ? 1 : 0;
> ++}
> ++
> ++/*
> ++ * Close a stream and free all its resources.
> ++ *
> ++ * @stream: VirtIOSoundPCMStream *stream
> ++ */
> ++static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream)
> ++{
> ++}
> ++
> + /*
> +- * Queue handler stub.
> ++ * Prepares a VirtIOSound card stream.
> ++ * Returns the response status code. (VIRTIO_SND_S_*).
> ++ *
> ++ * @s: VirtIOSound device
> ++ * @stream_id: stream id
> ++ */
> ++static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
> ++{
> ++ audsettings as;
> ++ virtio_snd_pcm_set_params *params;
> ++ VirtIOSoundPCMStream *stream;
> ++
> ++ if (s->pcm->streams == NULL ||
> ++ s->pcm->pcm_params == NULL ||
> ++ stream_id >= s->snd_conf.streams) {
> ++ return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> ++ }
> ++
> ++ params = virtio_snd_pcm_get_params(s, stream_id);
> ++ if (params == NULL) {
> ++ return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> ++ }
> ++
> ++ stream = virtio_snd_pcm_get_stream(s, stream_id);
> ++ if (stream == NULL) {
> ++ stream = g_new0(VirtIOSoundPCMStream, 1);
> ++ stream->active = false;
> ++ stream->id = stream_id;
> ++ stream->pcm = s->pcm;
> ++ stream->s = s;
> ++
> ++ /*
> ++ * stream_id >= s->snd_conf.streams was checked before so this is
> ++ * in-bounds
> ++ */
> ++ s->pcm->streams[stream_id] = stream;
> ++ }
> ++
> ++ virtio_snd_get_qemu_audsettings(&as, params);
> ++ stream->info.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 = VIRTIO_SOUND_HDA_FN_NID;
> ++ stream->info.features = 0;
> ++ stream->info.channels_min = 1;
> ++ stream->info.channels_max = as.nchannels;
> ++ stream->info.formats = supported_formats;
> ++ stream->info.rates = supported_rates;
> ++ stream->params = *params;
> ++
> ++ stream->positions[0] = VIRTIO_SND_CHMAP_FL;
> ++ stream->positions[1] = VIRTIO_SND_CHMAP_FR;
> ++ stream->as = as;
> ++
> ++ return cpu_to_le32(VIRTIO_SND_S_OK);
> ++}
> ++
> +static const char *print_code(uint32_t code)
> +{
> + #define CASE(CODE) \
> @@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const uint8_t
> + cmd->elem->out_num,
> + 0,
> + &cmd->ctrl,
> -+ sizeof(cmd->ctrl));
> ++ sizeof(virtio_snd_hdr));
> +
> -+ if (msg_sz != sizeof(cmd->ctrl)) {
> ++ if (msg_sz != sizeof(virtio_snd_hdr)) {
> ++ /*
> ++ * TODO: do we need to set DEVICE_NEEDS_RESET?
> ++ */
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: virtio-snd command size incorrect %zu vs \
> -+ %zu\n", __func__, msg_sz, sizeof(cmd->ctrl));
> ++ %zu\n", __func__, msg_sz, sizeof(virtio_snd_hdr));
> + return;
> + }
> +
> @@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const uint8_t
> + cmd->elem->in_num,
> + 0,
> + &cmd->resp,
> -+ sizeof(cmd->resp));
> -+ virtqueue_push(cmd->vq, cmd->elem, sizeof(cmd->elem));
> ++ sizeof(virtio_snd_hdr));
> ++ virtqueue_push(cmd->vq, cmd->elem, sizeof(virtio_snd_hdr));
> + virtio_notify(VIRTIO_DEVICE(s), cmd->vq);
> +}
> +
> @@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const uint8_t
> + }
> +}
> +
> - /*
> -- * Queue handler stub.
> ++/*
> + * The control message handler. Pops an element from the control virtqueue,
> + * and stores them to VirtIOSound's cmdq queue and finally calls
> + * virtio_snd_process_cmdq() for processing.
> @@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const uint8_t
> 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)
> + ERRP_GUARD();
> VirtIOSound *vsnd = VIRTIO_SND(dev);
> VirtIODevice *vdev = VIRTIO_DEVICE(dev);
> ++ virtio_snd_pcm_set_params default_params = { 0 };
> ++ uint32_t status;
>
> + vsnd->pcm = NULL;
> vsnd->vmstate =
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev, Error *
>
> + vsnd->pcm = g_new0(VirtIOSoundPCM, 1);
> + vsnd->pcm->snd = vsnd;
> -+ vsnd->pcm->streams = g_new0(VirtIOSoundPCMStream *, vsnd->snd_conf.streams);
> -+ vsnd->pcm->pcm_params = g_new0(virtio_snd_pcm_set_params, vsnd->snd_conf.streams);
> ++ vsnd->pcm->streams =
> ++ g_new0(VirtIOSoundPCMStream *, vsnd->snd_conf.streams);
> ++ vsnd->pcm->pcm_params =
> ++ g_new0(virtio_snd_pcm_set_params, vsnd->snd_conf.streams);
> +
> virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config));
> virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1);
>
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev, Error **errp)
> +
> AUD_register_card("virtio-sound", &vsnd->card);
>
> ++ /* set default params for all streams */
> ++ default_params.features = 0;
> ++ default_params.buffer_bytes = cpu_to_le32(8192);
> ++ default_params.period_bytes = cpu_to_le32(2048);
> ++ default_params.channels = 2;
> ++ default_params.format = VIRTIO_SND_PCM_FMT_S16;
> ++ default_params.rate = VIRTIO_SND_PCM_RATE_48000;
> vsnd->queues[VIRTIO_SND_VQ_CONTROL] =
> - virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
> + virtio_add_queue(vdev, 64, virtio_snd_handle_ctrl);
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev, Error *
> + virtio_add_queue(vdev, 64, virtio_snd_handle_xfer);
> + qemu_mutex_init(&vsnd->cmdq_mutex);
> + QTAILQ_INIT(&vsnd->cmdq);
> -+}
> +
> -+/*
> -+ * Close the stream and free its resources.
> -+ *
> -+ * @stream: VirtIOSoundPCMStream *stream
> -+ */
> -+static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream)
> -+{
> ++ for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
> ++ status = virtio_snd_set_pcm_params(vsnd, i, &default_params);
> ++ if (status != cpu_to_le32(VIRTIO_SND_S_OK)) {
> ++ error_setg(errp,
> ++ "Can't initalize stream params, device responded with %s.",
> ++ print_code(status));
> ++ return;
> ++ }
> ++ status = virtio_snd_pcm_prepare(vsnd, i);
> ++ if (status != cpu_to_le32(VIRTIO_SND_S_OK)) {
> ++ error_setg(errp,
> ++ "Can't prepare streams, device responded with %s.",
> ++ print_code(status));
> ++ return;
> ++ }
> ++ }
> }
>
> static void virtio_snd_unrealize(DeviceState *dev)
> @@ include/hw/virtio/virtio-snd.h: typedef struct virtio_snd_pcm_xfer virtio_snd_pc
> +
> +struct VirtIOSoundPCM {
> + VirtIOSound *snd;
> ++ /*
> ++ * PCM parameters are a separate field instead of a VirtIOSoundPCMStream
> ++ * field, because the operation of PCM control requests is first
> ++ * VIRTIO_SND_R_PCM_SET_PARAMS and then VIRTIO_SND_R_PCM_PREPARE; this
> ++ * means that some times we get parameters without having an allocated
> ++ * stream yet.
> ++ */
> + virtio_snd_pcm_set_params *pcm_params;
> + VirtIOSoundPCMStream **streams;
> +};
> +
> ++struct VirtIOSoundPCMStream {
> ++ VirtIOSoundPCM *pcm;
> ++ virtio_snd_pcm_info info;
> ++ virtio_snd_pcm_set_params params;
> ++ uint32_t id;
> ++ /* channel position values (VIRTIO_SND_CHMAP_XXX) */
> ++ uint8_t positions[VIRTIO_SND_CHMAP_MAX_SIZE];
> ++ VirtIOSound *s;
> ++ bool flushing;
> ++ audsettings as;
> ++ union {
> ++ SWVoiceIn *in;
> ++ SWVoiceOut *out;
> ++ } voice;
> ++ bool active;
> ++};
> ++
> +/*
> + * PCM stream state machine.
> + * -------------------------
> @@ include/hw/virtio/virtio-snd.h: typedef struct virtio_snd_pcm_xfer virtio_snd_pc
> + *
> + * The CTRL message is finally handled in `process_cmd()`.
> + */
> -+struct VirtIOSoundPCMStream {
> -+ VirtIOSoundPCM *pcm;
> -+ virtio_snd_pcm_info info;
> -+ virtio_snd_pcm_set_params params;
> -+ uint32_t id;
> -+ /* channel position values (VIRTIO_SND_CHMAP_XXX) */
> -+ uint8_t positions[VIRTIO_SND_CHMAP_MAX_SIZE];
> -+ VirtIOSound *s;
> -+ bool flushing;
> -+ audsettings as;
> -+ union {
> -+ SWVoiceIn *in;
> -+ SWVoiceOut *out;
> -+ } voice;
> -+ QemuMutex queue_mutex;
> -+ QSIMPLEQ_HEAD(, VirtIOSoundPCMBlock) queue;
> -+};
> -+
> +struct VirtIOSound {
> VirtIODevice parent_obj;
>
> 4: 49eb46f184 < -: ---------- virtio-sound: set PCM stream parameters
> 5: 1f1dc7a200 ! 4: 28b2ecfa1f virtio-sound: handle VIRTIO_SND_R_PCM_INFO request
> @@ hw/virtio/trace-events: virtio_snd_vm_state_stopped(void) "vm state stopped"
> virtio_snd_handle_event(void) "event queue callback called"
>
> ## hw/virtio/virtio-snd.c ##
> -@@
> - #define VIRTIO_SOUND_CHMAP_DEFAULT 0
> - #define VIRTIO_SOUND_HDA_FN_NID 0
> -
> -+static struct virtio_snd_info info_to_le32(struct virtio_snd_info val) {
> -+ val.hda_fn_nid = cpu_to_le32(val.hda_fn_nid);
> -+
> -+ return val;
> -+}
> -+
> -+static virtio_snd_pcm_info pcm_info_to_le32(virtio_snd_pcm_info val) {
> -+ val.hdr = info_to_le32(val.hdr);
> -+ val.features = cpu_to_le32(val.features);
> -+ val.formats = cpu_to_le64(val.formats);
> -+ val.rates = cpu_to_le64(val.rates);
> -+
> -+ return val;
> -+}
> -+
> - static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8)
> - | BIT(VIRTIO_SND_PCM_FMT_U8)
> - | BIT(VIRTIO_SND_PCM_FMT_S16)
> @@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s,
> : &s->pcm->pcm_params[stream_id];
> }
> @@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_set_params *virtio_snd_pcm_get_par
> + virtio_snd_ctrl_command *cmd)
> +{
> + uint32_t stream_id, start_id, count, size;
> ++ virtio_snd_pcm_info val;
> + virtio_snd_query_info req;
> + VirtIOSoundPCMStream *stream = NULL;
> + g_autofree virtio_snd_pcm_info *pcm_info = NULL;
> @@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_set_params *virtio_snd_pcm_get_par
> + cmd->elem->out_num,
> + 0,
> + &req,
> -+ sizeof(req));
> ++ sizeof(virtio_snd_query_info));
> +
> + if (msg_sz != sizeof(virtio_snd_query_info)) {
> ++ /*
> ++ * TODO: do we need to set DEVICE_NEEDS_RESET?
> ++ */
> ++ qemu_log_mask(LOG_GUEST_ERROR,
> ++ "%s: virtio-snd command size incorrect %zu vs \
> ++ %zu\n", __func__, msg_sz, sizeof(virtio_snd_query_info));
> + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> + return;
> + }
> @@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_set_params *virtio_snd_pcm_get_par
> +
> + if (iov_size(cmd->elem->in_sg, cmd->elem->in_num) <
> + sizeof(virtio_snd_hdr) + size * count) {
> ++ /*
> ++ * TODO: do we need to set DEVICE_NEEDS_RESET?
> ++ */
> + error_report("pcm info: buffer too small, got: %zu, needed: %zu",
> + iov_size(cmd->elem->in_sg, cmd->elem->in_num),
> + sizeof(virtio_snd_pcm_info));
> @@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_set_params *virtio_snd_pcm_get_par
> + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> + return;
> + }
> -+ pcm_info[i] = pcm_info_to_le32(stream->info);
> ++ val = stream->info;
> ++ val.hdr.hda_fn_nid = cpu_to_le32(val.hdr.hda_fn_nid);
> ++ val.features = cpu_to_le32(val.features);
> ++ val.formats = cpu_to_le64(val.formats);
> ++ val.rates = cpu_to_le64(val.rates);
> ++ /*
> ++ * 5.14.6.6.2.1 Device Requirements: Stream Information The device MUST
> ++ * NOT set undefined feature, format, rate and direction values. The
> ++ * device MUST initialize the padding bytes to 0.
> ++ */
> ++ pcm_info[i] = val;
> ++ memset(&pcm_info[i].padding, 0, 5);
> + }
> +
> + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
> 6: 7fbbe07fda ! 5: a52d20b2c3 virtio-sound: handle VIRTIO_SND_R_PCM_{START,STOP}
> @@ hw/virtio/virtio-snd.c: static const char *print_code(uint32_t code)
> + cmd->elem->out_num,
> + 0,
> + &req,
> -+ sizeof(req));
> ++ sizeof(virtio_snd_pcm_hdr));
> +
> + if (msg_sz != sizeof(virtio_snd_pcm_hdr)) {
> ++ qemu_log_mask(LOG_GUEST_ERROR,
> ++ "%s: virtio-snd command size incorrect %zu vs \
> ++ %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_hdr));
> + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> + return;
> + }
> @@ hw/virtio/virtio-snd.c: static const char *print_code(uint32_t code)
> + trace_virtio_snd_handle_pcm_start_stop(start ? "VIRTIO_SND_R_PCM_START" :
> + "VIRTIO_SND_R_PCM_STOP", stream_id);
> + stream = virtio_snd_pcm_get_stream(s, stream_id);
> -+ if (!stream) {
> ++ if (stream == NULL) {
> + error_report("Invalid stream id: %"PRIu32, req.stream_id);
> + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> ++ return;
> + }
> ++ stream->active = start;
> +}
> +
> /*
> 7: 92e65a6115 ! 6: 25fbb2eb25 virtio-sound: handle VIRTIO_SND_R_PCM_SET_PARAMS
> @@ hw/virtio/virtio-snd.c: uint32_t virtio_snd_set_pcm_params(VirtIOSound *s,
> + cmd->elem->out_num,
> + 0,
> + &req,
> -+ sizeof(req));
> ++ sizeof(virtio_snd_pcm_set_params));
> +
> + if (msg_sz != sizeof(virtio_snd_pcm_set_params)) {
> ++ /*
> ++ * TODO: do we need to set DEVICE_NEEDS_RESET?
> ++ */
> ++ qemu_log_mask(LOG_GUEST_ERROR,
> ++ "%s: virtio-snd command size incorrect %zu vs \
> ++ %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_set_params));
> + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> + return;
> + }
> 8: 173bd9dafe ! 7: 9e8d9923ba virtio-sound: handle VIRTIO_SND_R_PCM_PREPARE
> @@ hw/virtio/virtio-snd.c: static const char *print_code(uint32_t code)
> + sizeof(stream_id));
> +
> + stream_id = le32_to_cpu(stream_id);
> -+ cmd->resp.code = msg_sz == sizeof(uint32_t)
> ++ cmd->resp.code = msg_sz == sizeof(stream_id)
> + ? virtio_snd_pcm_prepare(s, stream_id)
> + : cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> +}
> 9: 6e05194e02 ! 8: b50c94decc virtio-sound: handle VIRTIO_SND_R_PCM_RELEASE
> @@ hw/virtio/trace-events: virtio_snd_handle_pcm_set_params(uint32_t stream) "VIRTI
>
> ## hw/virtio/virtio-snd.c ##
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
> - }
> + stream->active = start;
> }
>
> +/*
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_start_stop(VirtIOSound
> + &stream_id,
> + sizeof(stream_id));
> +
> -+ if (msg_sz != sizeof(uint32_t)) {
> ++ if (msg_sz != sizeof(stream_id)) {
> ++ /*
> ++ * TODO: do we need to set DEVICE_NEEDS_RESET?
> ++ */
> ++ qemu_log_mask(LOG_GUEST_ERROR,
> ++ "%s: virtio-snd command size incorrect %zu vs \
> ++ %zu\n", __func__, msg_sz, sizeof(stream_id));
> + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> + return;
> + }
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_start_stop(VirtIOSound
> + stream_id = le32_to_cpu(stream_id);
> + trace_virtio_snd_handle_pcm_release(stream_id);
> + stream = virtio_snd_pcm_get_stream(s, stream_id);
> -+ if (!stream) {
> ++ if (stream == NULL) {
> ++ /*
> ++ * TODO: do we need to set DEVICE_NEEDS_RESET?
> ++ */
> + error_report("already released stream %"PRIu32, stream_id);
> + virtio_error(VIRTIO_DEVICE(s),
> + "already released stream %"PRIu32,
> 10: a241f8cf92 ! 9: 4cbb908742 virtio-sound: implement audio output (TX)
> @@ Commit message
>
> Handle output IO messages in the transmit (TX) virtqueue.
>
> - It allocates a VirtIOSoundPCMBlock for each IO message and copies the
> + It allocates a VirtIOSoundPCMBuffer for each IO message and copies the
> data buffer to it. When the IO buffer is written to the host's sound
> card, the guest will be notified that it has been consumed.
>
> @@ hw/virtio/trace-events: virtio_snd_handle_pcm_release(uint32_t stream) "VIRTIO_S
> +virtio_snd_handle_xfer(void) "tx/rx queue callback called"
>
> ## hw/virtio/virtio-snd.c ##
> -@@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_info pcm_info_to_le32(virtio_snd_pcm_info val) {
> - return val;
> - }
> +@@
> + #define VIRTIO_SOUND_CHMAP_DEFAULT 0
> + #define VIRTIO_SOUND_HDA_FN_NID 0
>
> +static void virtio_snd_pcm_out_cb(void *data, int available);
> +static void virtio_snd_process_cmdq(VirtIOSound *s);
> @@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_info pcm_info_to_le32(virtio_snd_p
> | BIT(VIRTIO_SND_PCM_FMT_U8)
> | BIT(VIRTIO_SND_PCM_FMT_S16)
> @@ hw/virtio/virtio-snd.c: virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
> - memcpy(&s->snd_conf, sndconfig, sizeof(s->snd_conf));
> +
> }
>
> +static void
> -+virtio_snd_pcm_block_free(VirtIOSoundPCMBlock *block)
> ++virtio_snd_pcm_buffer_free(VirtIOSoundPCMBuffer *buffer)
> +{
> -+ g_free(block->elem);
> -+ g_free(block);
> ++ g_free(buffer->elem);
> ++ g_free(buffer);
> +}
> +
> static void
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_get_qemu_audsettings(audsettings
> */
> static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream)
> {
> -+ VirtIOSoundPCMBlock *block, *next;
> -+
> + if (stream) {
> -+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
> -+ QSIMPLEQ_FOREACH_SAFE(block, &stream->queue, entry, next) {
> -+ virtqueue_push(block->vq,
> -+ block->elem,
> -+ sizeof(block->elem));
> -+ virtio_notify(VIRTIO_DEVICE(stream->s),
> -+ block->vq);
> -+ QSIMPLEQ_REMOVE_HEAD(&stream->queue, entry);
> -+ virtio_snd_pcm_block_free(block);
> -+ }
> -+ }
> + if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
> ++ virtio_snd_pcm_flush(stream);
> + AUD_close_out(&stream->pcm->snd->card, stream->voice.out);
> + stream->voice.out = NULL;
> + }
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_get_qemu_audsettings(audsettings
> }
>
> /*
> +@@ hw/virtio/virtio-snd.c: static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
> + stream->id = stream_id;
> + stream->pcm = s->pcm;
> + stream->s = s;
> ++ qemu_mutex_init(&stream->queue_mutex);
> ++ QSIMPLEQ_INIT(&stream->queue);
> ++ QSIMPLEQ_INIT(&stream->invalid);
> +
> + /*
> + * stream_id >= s->snd_conf.streams was checked before so this is
> @@ hw/virtio/virtio-snd.c: static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
> stream->positions[1] = VIRTIO_SND_CHMAP_FR;
> stream->as = as;
> @@ hw/virtio/virtio-snd.c: static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, u
> return cpu_to_le32(VIRTIO_SND_S_OK);
> }
>
> -@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
> - bool start)
> - {
> - VirtIOSoundPCMStream *stream;
> -+ VirtIOSoundPCMBlock *block, *next;
> - virtio_snd_pcm_hdr req;
> - uint32_t stream_id;
> - size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
> cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
> trace_virtio_snd_handle_pcm_start_stop(start ? "VIRTIO_SND_R_PCM_START" :
> "VIRTIO_SND_R_PCM_STOP", stream_id);
> +
> stream = virtio_snd_pcm_get_stream(s, stream_id);
> -- if (!stream) {
> +- if (stream == NULL) {
> - error_report("Invalid stream id: %"PRIu32, req.stream_id);
> + if (stream) {
> ++ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
> ++ stream->active = start;
> ++ }
> + if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
> + AUD_set_active_out(stream->voice.out, start);
> + }
> -+ /* remove previous buffers. */
> -+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
> -+ QSIMPLEQ_FOREACH_SAFE(block, &stream->queue, entry, next) {
> -+ virtqueue_push(block->vq,
> -+ block->elem,
> -+ sizeof(block->elem));
> -+ virtio_notify(VIRTIO_DEVICE(stream->s), block->vq);
> -+ QSIMPLEQ_REMOVE_HEAD(&stream->queue, entry);
> -+ virtio_snd_pcm_block_free(block);
> -+ }
> -+ }
> + } else {
> + error_report("Invalid stream id: %"PRIu32, stream_id);
> cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> + return;
> }
> +@@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
> }
>
> /*
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_start_stop(VirtIOSound
> + */
> +static size_t virtio_snd_pcm_get_io_msgs_count(VirtIOSoundPCMStream *stream)
> +{
> -+ VirtIOSoundPCMBlock *block;
> -+ VirtIOSoundPCMBlock *next;
> ++ VirtIOSoundPCMBuffer *buffer, *next;
> + size_t count = 0;
> +
> + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
> -+ QSIMPLEQ_FOREACH_SAFE(block, &stream->queue, entry, next) {
> ++ QSIMPLEQ_FOREACH_SAFE(buffer, &stream->queue, entry, next) {
> ++ count += 1;
> ++ }
> ++ QSIMPLEQ_FOREACH_SAFE(buffer, &stream->invalid, entry, next) {
> + count += 1;
> + }
> + }
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_release(VirtIOSound *s
> + * - The device MUST NOT complete the control request while there
> + * are pending I/O messages for the specified stream ID.
> + */
> -+ virtio_snd_process_cmdq(stream->s);
> + trace_virtio_snd_pcm_stream_flush(stream_id);
> + virtio_snd_pcm_flush(stream);
> + }
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_event(VirtIODevice *vdev,
> +{
> + VirtIOSound *s = VIRTIO_SND(vdev);
> + VirtIOSoundPCMStream *stream = NULL;
> -+ VirtIOSoundPCMBlock *block;
> ++ VirtIOSoundPCMBuffer *buffer;
> + 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
> ++ * return them after the for loop.
> ++ */
> ++ bool must_empty_invalid_queue = false;
> +
> -+ trace_virtio_snd_handle_xfer();
> ++ if (!virtio_queue_ready(vq)) {
> ++ return;
> ++ }
> ++ trace_virtio_snd_handle_tx_xfer();
> +
> + for (;;) {
> + elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_event(VirtIODevice *vdev,
> + }
> + /* get the message hdr object */
> + msg_sz = iov_to_buf(elem->out_sg,
> -+ elem->out_num,
> -+ 0,
> -+ &hdr,
> -+ sizeof(hdr));
> -+ if (msg_sz != sizeof(hdr)) {
> ++ elem->out_num,
> ++ 0,
> ++ &hdr,
> ++ sizeof(virtio_snd_pcm_xfer));
> ++ if (msg_sz != sizeof(virtio_snd_pcm_xfer)) {
> + goto tx_err;
> + }
> + stream_id = le32_to_cpu(hdr.stream_id);
> +
> + if (stream_id >= s->snd_conf.streams
> -+ || !s->pcm->streams[stream_id]) {
> ++ || s->pcm->streams[stream_id] == NULL) {
> + goto tx_err;
> + }
> +
> + stream = s->pcm->streams[stream_id];
> -+ if (!stream || stream->info.direction != VIRTIO_SND_D_OUTPUT) {
> ++ if (stream->info.direction != VIRTIO_SND_D_OUTPUT) {
> + goto tx_err;
> + }
> +
> + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
> -+ size = iov_size(elem->out_sg, elem->out_num) -
> -+ sizeof(virtio_snd_pcm_xfer);
> -+ block = g_malloc0(sizeof(VirtIOSoundPCMBlock) + size);
> -+ block->elem = elem;
> -+ block->vq = vq;
> -+ block->size = size;
> -+ block->offset = 0;
> ++ size = iov_size(elem->out_sg, elem->out_num);
> +
> -+ iov_to_buf(elem->out_sg,
> -+ elem->out_num,
> -+ sizeof(virtio_snd_pcm_xfer),
> -+ block->data,
> -+ size);
> ++ buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
> ++ buffer->elem = elem;
> ++ buffer->stale = true;
> ++ buffer->vq = vq;
> ++ buffer->size = size;
> ++ buffer->offset = sizeof(virtio_snd_pcm_xfer);
> +
> -+ QSIMPLEQ_INSERT_TAIL(&stream->queue, block, entry);
> ++ QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry);
> + }
> + continue;
> +
> +tx_err:
> + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
> -+ resp.status = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> -+ iov_from_buf(elem->in_sg,
> -+ elem->in_num,
> -+ 0,
> -+ &resp,
> -+ sizeof(resp));
> -+ virtqueue_push(vq, elem, sizeof(elem));
> -+ break;
> ++ must_empty_invalid_queue = true;
> ++ buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer));
> ++ buffer->elem = elem;
> ++ buffer->vq = vq;
> ++ QSIMPLEQ_INSERT_TAIL(&stream->invalid, buffer, entry);
> + }
> + }
> +
> -+ /*
> -+ * Notify vq about virtio_snd_pcm_status responses.
> -+ * Buffer responses must be notified separately later.
> -+ */
> -+ virtio_notify(VIRTIO_DEVICE(s), vq);
> ++ 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);
> ++ }
> ++ }
> +}
> +
> /*
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev, Error *
> +static void virtio_snd_pcm_out_cb(void *data, int available)
> +{
> + VirtIOSoundPCMStream *stream = data;
> -+ VirtIOSoundPCMBlock *block;
> ++ VirtIOSoundPCMBuffer *buffer;
> + size_t size;
> +
> + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
> + while (!QSIMPLEQ_EMPTY(&stream->queue)) {
> -+ block = QSIMPLEQ_FIRST(&stream->queue);
> -+
> ++ buffer = QSIMPLEQ_FIRST(&stream->queue);
> ++ if (!virtio_queue_ready(buffer->vq)) {
> ++ return;
> ++ }
> ++ if (!stream->active) {
> ++ /* Stream has stopped, so do not perform AUD_write. */
> ++ goto return_tx_buffer;
> ++ }
> ++ if (buffer->stale) {
> ++ iov_to_buf(buffer->elem->out_sg,
> ++ buffer->elem->out_num,
> ++ buffer->offset,
> ++ buffer->data,
> ++ buffer->size);
> ++ buffer->stale = false;
> ++ }
> + for (;;) {
> + size = AUD_write(stream->voice.out,
> -+ block->data + block->offset,
> -+ MIN(block->size, available));
> -+ assert(size <= MIN(block->size, available));
> ++ buffer->data + buffer->offset,
> ++ MIN(buffer->size, available));
> ++ assert(size <= MIN(buffer->size, available));
> + if (size == 0) {
> + /* break out of both loops */
> + available = 0;
> + break;
> + }
> -+ block->size -= size;
> -+ block->offset += size;
> ++ buffer->size -= size;
> ++ buffer->offset += size;
> + available -= size;
> -+ if (!block->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(block->elem->in_sg,
> -+ block->elem->in_num,
> ++ iov_from_buf(buffer->elem->in_sg,
> ++ buffer->elem->in_num,
> + 0,
> + &resp,
> -+ sizeof(resp));
> -+ virtqueue_push(block->vq,
> -+ block->elem,
> -+ sizeof(block->elem));
> -+ virtio_notify(VIRTIO_DEVICE(stream->s), block->vq);
> ++ 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_block_free(block);
> ++ virtio_snd_pcm_buffer_free(buffer);
> + break;
> + }
> -+
> + if (!available) {
> + break;
> + }
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev, Error *
> + */
> +static void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream)
> +{
> -+ VirtIOSoundPCMBlock *block;
> -+ VirtIOSoundPCMBlock *next;
> ++ VirtIOSoundPCMBuffer *buffer, *next;
> +
> + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
> -+ QSIMPLEQ_FOREACH_SAFE(block, &stream->queue, entry, next) {
> -+ AUD_write(stream->voice.out, block->data + block->offset, block->size);
> -+ virtqueue_push(block->vq, block->elem, sizeof(block->elem));
> -+ virtio_notify(VIRTIO_DEVICE(stream->s), block->vq);
> -+ QSIMPLEQ_REMOVE(&stream->queue, block, VirtIOSoundPCMBlock, entry);
> ++ 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);
> + }
> + }
> +}
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_realize(DeviceState *dev, Error *
> static void virtio_snd_unrealize(DeviceState *dev)
> {
> VirtIODevice *vdev = VIRTIO_DEVICE(dev);
> +@@ hw/virtio/virtio-snd.c: static void virtio_snd_unrealize(DeviceState *dev)
> + if (stream) {
> + virtio_snd_process_cmdq(stream->s);
> + virtio_snd_pcm_close(stream);
> ++ qemu_mutex_destroy(&stream->queue_mutex);
> + g_free(stream);
> + }
> + }
>
> ## include/hw/virtio/virtio-snd.h ##
> @@ include/hw/virtio/virtio-snd.h: typedef struct virtio_snd_ctrl_command virtio_snd_ctrl_command;
>
> typedef struct VirtIOSoundPCM VirtIOSoundPCM;
>
> -+typedef struct VirtIOSoundPCMBlock VirtIOSoundPCMBlock;
> ++typedef struct VirtIOSoundPCMBuffer VirtIOSoundPCMBuffer;
> +
> -+struct VirtIOSoundPCMBlock {
> -+ QSIMPLEQ_ENTRY(VirtIOSoundPCMBlock) entry;
> ++/*
> ++ * The VirtIO sound spec reuses layouts and values from the High Definition
> ++ * Audio spec (virtio/v1.2: 5.14 Sound Device). This struct handles each I/O
> ++ * message's buffer (virtio/v1.2: 5.14.6.8 PCM I/O Messages).
> ++ *
> ++ * 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.
> ++ *
> ++ * VirtIOSoundPCMBuffer has a dynamic size since it includes the raw PCM data
> ++ * in its allocation. It must be initialized and destroyed as follows:
> ++ *
> ++ * size_t size = [[derived from owned VQ element descriptor sizes]];
> ++ * buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
> ++ * buffer->elem = [[owned VQ element]];
> ++ *
> ++ * [..]
> ++ *
> ++ * g_free(buffer->elem);
> ++ * g_free(buffer);
> ++ */
> ++struct VirtIOSoundPCMBuffer {
> ++ QSIMPLEQ_ENTRY(VirtIOSoundPCMBuffer) entry;
> + VirtQueueElement *elem;
> + VirtQueue *vq;
> + size_t size;
> ++ /*
> ++ * In TX / Plaback, `offset` represents the first unused position inside
> ++ * `data`. If `offset == size` then there are no unused data left.
> ++ */
> + uint64_t offset;
> ++ /* Used for the TX queue for lazy I/O copy from `elem` */
> ++ bool stale;
> ++ /*
> ++ * 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
> ++ * or write-only size when allocating VirtIOSoundPCMBuffer.
> ++ */
> + uint8_t data[];
> +};
> +
> struct VirtIOSoundPCM {
> VirtIOSound *snd;
> - virtio_snd_pcm_set_params *pcm_params;
> + /*
> +@@ include/hw/virtio/virtio-snd.h: struct VirtIOSoundPCMStream {
> + SWVoiceIn *in;
> + SWVoiceOut *out;
> + } voice;
> ++ QemuMutex queue_mutex;
> + bool active;
> ++ QSIMPLEQ_HEAD(, VirtIOSoundPCMBuffer) queue;
> ++ QSIMPLEQ_HEAD(, VirtIOSoundPCMBuffer) invalid;
> + };
> +
> + /*
> 11: 6f3526e067 ! 10: 992b0d5ff4 virtio-sound: implement audio capture (RX)
> @@ hw/virtio/virtio-snd.c
> #define VIRTIO_SOUND_CHMAP_DEFAULT 0
> #define VIRTIO_SOUND_HDA_FN_NID 0
>
> -@@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_info pcm_info_to_le32(virtio_snd_pcm_info val) {
> -
> 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);
> @@ hw/virtio/virtio-snd.c: static virtio_snd_pcm_info pcm_info_to_le32(virtio_snd_p
> 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)
> + {
> + if (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 uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, u
> + stream,
> + virtio_snd_pcm_in_cb,
> + &as);
> ++ AUD_set_volume_in(stream->voice.in, 0, 255, 255);
> }
>
> return cpu_to_le32(VIRTIO_SND_S_OK);
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
> - if (stream) {
> + }
> if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
> AUD_set_active_out(stream->voice.out, start);
> + } else {
> + AUD_set_active_in(stream->voice.in, start);
> }
> - /* remove previous buffers. */
> - WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
> + } 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.
> */
> - virtio_snd_process_cmdq(stream->s);
> trace_virtio_snd_pcm_stream_flush(stream_id);
> - virtio_snd_pcm_flush(stream);
> + if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
> @@ 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)
> - virtio_snd_pcm_status resp = { 0 };
> - uint32_t stream_id;
> -
> -- 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:
> }
>
> @@ hw/virtio/virtio-snd.c: tx_err:
> +{
> + VirtIOSound *s = VIRTIO_SND(vdev);
> + VirtIOSoundPCMStream *stream = NULL;
> -+ VirtIOSoundPCMBlock *block;
> ++ VirtIOSoundPCMBuffer *buffer;
> + 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
> ++ * return them after the for loop.
> ++ */
> ++ bool must_empty_invalid_queue = false;
> +
> ++ if (!virtio_queue_ready(vq)) {
> ++ return;
> ++ }
> + trace_virtio_snd_handle_rx_xfer();
> +
> + for (;;) {
> @@ hw/virtio/virtio-snd.c: tx_err:
> + elem->out_num,
> + 0,
> + &hdr,
> -+ sizeof(hdr));
> -+ if (msg_sz != sizeof(hdr)) {
> ++ sizeof(virtio_snd_pcm_xfer));
> ++ if (msg_sz != sizeof(virtio_snd_pcm_xfer)) {
> + goto rx_err;
> + }
> + stream_id = le32_to_cpu(hdr.stream_id);
> @@ hw/virtio/virtio-snd.c: tx_err:
> + }
> +
> + stream = s->pcm->streams[stream_id];
> -+ if (!stream || stream->info.direction != VIRTIO_SND_D_INPUT) {
> ++ if (stream == NULL || stream->info.direction != VIRTIO_SND_D_INPUT) {
> + goto rx_err;
> + }
> + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
> + size = iov_size(elem->in_sg, elem->in_num) -
> + sizeof(virtio_snd_pcm_status);
> -+ block = g_malloc0(sizeof(VirtIOSoundPCMBlock) + size);
> -+ block->elem = elem;
> -+ block->vq = vq;
> -+ block->size = 0;
> -+ block->offset = 0;
> -+ QSIMPLEQ_INSERT_TAIL(&stream->queue, block, entry);
> ++ buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
> ++ buffer->elem = elem;
> ++ buffer->vq = vq;
> ++ buffer->size = 0;
> ++ buffer->offset = 0;
> ++ QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry);
> + }
> + continue;
> +
> +rx_err:
> + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
> -+ resp.status = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> -+ iov_from_buf(elem->in_sg,
> -+ elem->in_num,
> -+ 0,
> -+ &resp,
> -+ sizeof(resp));
> ++ must_empty_invalid_queue = true;
> ++ buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer));
> ++ buffer->elem = elem;
> ++ buffer->vq = vq;
> ++ QSIMPLEQ_INSERT_TAIL(&stream->invalid, buffer, entry);
> + }
> + }
> +
> -+ /*
> -+ * Notify vq about virtio_snd_pcm_status responses.
> -+ * Buffer responses must be notified separately later.
> -+ */
> -+ virtio_notify(VIRTIO_DEVICE(s), vq);
> ++ 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);
> ++ }
> ++ }
> +}
>
> static uint64_t get_features(VirtIODevice *vdev, uint64_t features,
> @@ 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: static void virtio_snd_pcm_out_cb(void *data, int available)
> +@@ hw/virtio/virtio-snd.c: return_tx_buffer:
> }
>
> /*
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_pcm_out_cb(void *data, int availa
> -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;
> - VirtIOSoundPCMBlock *block;
> -- VirtIOSoundPCMBlock *next;
> ++ VirtIOSoundPCMBuffer *buffer;
> + virtio_snd_pcm_status resp = { 0 };
> + size_t size;
>
> WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
> -- QSIMPLEQ_FOREACH_SAFE(block, &stream->queue, entry, next) {
> -- AUD_write(stream->voice.out, block->data + block->offset, block->size);
> -- virtqueue_push(block->vq, block->elem, sizeof(block->elem));
> -- virtio_notify(VIRTIO_DEVICE(stream->s), block->vq);
> -- QSIMPLEQ_REMOVE(&stream->queue, block, VirtIOSoundPCMBlock, entry);
> +- 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)) {
> -+ block = QSIMPLEQ_FIRST(&stream->queue);
> ++ buffer = QSIMPLEQ_FIRST(&stream->queue);
> ++ if (!virtio_queue_ready(buffer->vq)) {
> ++ return;
> ++ }
> ++ if (!stream->active) {
> ++ /* Stream has stopped, so do not perform AUD_read. */
> ++ goto return_rx_buffer;
> ++ }
> +
> + for (;;) {
> + size = AUD_read(stream->voice.in,
> -+ block->data + block->size,
> -+ MIN(available, (stream->params.period_bytes - block->size)));
> ++ buffer->data + buffer->size,
> ++ MIN(available, (stream->params.period_bytes -
> ++ buffer->size)));
> + if (!size) {
> + available = 0;
> + break;
> + }
> -+ block->size += size;
> ++ buffer->size += size;
> + available -= size;
> -+ if (block->size >= stream->params.period_bytes) {
> ++ 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(block->elem->in_sg,
> -+ block->elem->in_num,
> ++ iov_from_buf(buffer->elem->in_sg,
> ++ buffer->elem->in_num,
> + 0,
> -+ block->data,
> -+ stream->params.period_bytes);
> -+ iov_from_buf(block->elem->in_sg,
> -+ block->elem->in_num,
> -+ block->size,
> ++ buffer->data,
> ++ buffer->size);
> ++ iov_from_buf(buffer->elem->in_sg,
> ++ buffer->elem->in_num,
> ++ buffer->size,
> + &resp,
> + sizeof(resp));
> -+ virtqueue_push(block->vq,
> -+ block->elem,
> -+ sizeof(block->elem));
> -+ virtio_notify(VIRTIO_DEVICE(stream->s),
> -+ block->vq);
> ++ 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_block_free(block);
> ++ virtio_snd_pcm_buffer_free(buffer);
> + break;
> + }
> + if (!available) {
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_pcm_out_cb(void *data, int availa
> }
> }
>
> -+#define virtio_snd_pcm_flush(AUD_CB) \
> -+ VirtIOSoundPCMBlock *block; \
> -+ VirtIOSoundPCMBlock *next; \
> -+ virtio_snd_pcm_status resp = { 0 }; \
> -+ resp.status = cpu_to_le32(VIRTIO_SND_S_OK); \
> -+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { \
> -+ QSIMPLEQ_FOREACH_SAFE(block, &stream->queue, entry, next) { \
> -+ do { \
> -+ AUD_CB; \
> -+ } while (0) \
> -+ ; \
> -+ virtqueue_push(block->vq, block->elem, sizeof(block->elem));\
> -+ virtio_notify(VIRTIO_DEVICE(stream->s), block->vq); \
> -+ QSIMPLEQ_REMOVE(&stream->queue, \
> -+ block, \
> -+ VirtIOSoundPCMBlock, \
> -+ entry); \
> -+ virtio_snd_pcm_block_free(block); \
> -+ } \
> -+ } \
> ++#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); \
> ++ } \
> ++ }
> +
> +
> +/*
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_pcm_out_cb(void *data, int availa
> + */
> +static void virtio_snd_pcm_out_flush(VirtIOSoundPCMStream *stream)
> +{
> -+ /* We should flush the buffers as soon as possible, because it is a
> ++ /*
> ++ * 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(
> -+ iov_from_buf(block->elem->in_sg,
> -+ block->elem->in_num,
> ++ 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(resp));
> ++ sizeof(virtio_snd_pcm_status));
> + );
> +}
> +
> @@ hw/virtio/virtio-snd.c: static void virtio_snd_pcm_out_cb(void *data, int availa
> +static void virtio_snd_pcm_in_flush(VirtIOSoundPCMStream *stream)
> +{
> + virtio_snd_pcm_flush(
> -+ iov_from_buf(block->elem->in_sg,
> -+ block->elem->in_num,
> ++ len = sizeof(virtio_snd_pcm_status) + buffer->size;
> ++ iov_from_buf(buffer->elem->in_sg,
> ++ buffer->elem->in_num,
> + 0,
> -+ block->data,
> -+ block->size);
> -+ iov_from_buf(block->elem->in_sg,
> -+ block->elem->in_num,
> -+ block->size,
> ++ buffer->data,
> ++ buffer->size);
> ++ iov_from_buf(buffer->elem->in_sg,
> ++ buffer->elem->in_num,
> ++ buffer->size,
> + &resp,
> -+ sizeof(resp));
> ++ sizeof(virtio_snd_pcm_status));
> + );
> +}
> +
> static void virtio_snd_unrealize(DeviceState *dev)
> {
> VirtIODevice *vdev = VIRTIO_DEVICE(dev);
> -@@ hw/virtio/virtio-snd.c: static void virtio_snd_unrealize(DeviceState *dev)
> - virtio_cleanup(vdev);
> - }
> -
> --
> - static void virtio_snd_reset(VirtIODevice *vdev)
> - {
> - VirtIOSound *s = VIRTIO_SND(vdev);
> 12: 06e6b17186 ! 11: b720e00121 docs/system: add basic virtio-snd documentation
> @@ Commit message
> Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
>
> + ## MAINTAINERS ##
> +@@ MAINTAINERS: M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> + S: Supported
> + F: hw/virtio/virtio-snd*.c
> + F: include/hw/virtio/virtio-snd.h
> ++F: docs/system/devices/virtio-snd.rst
> +
> + nvme
> + M: Keith Busch <kbusch@kernel.org>
> +
> ## docs/system/device-emulation.rst ##
> @@ docs/system/device-emulation.rst: Emulated Devices
> devices/usb.rst
>
> base-commit: 36e9aab3c569d4c9ad780473596e18479838d1aa
> --
> 2.39.2
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v10 01/11] Add virtio-sound device stub
2023-09-29 14:08 ` [PATCH v10 01/11] Add virtio-sound device stub Emmanouil Pitsidianakis
@ 2023-10-03 14:13 ` Michael S. Tsirkin
2023-10-03 14:34 ` Alex Bennée
0 siblings, 1 reply; 21+ messages in thread
From: Michael S. Tsirkin @ 2023-10-03 14:13 UTC (permalink / raw)
To: Emmanouil Pitsidianakis
Cc: qemu-devel, Igor Skalkin, Anton Yakovlev, Paolo Bonzini,
Gerd Hoffmann, Marcel Apfelbaum, Daniel P. Berrangé,
Eduardo Habkost, Marc-André Lureau, Volker Rümelin,
Kővágó, Zoltán, Alex Bennée,
Philippe Mathieu-Daudé, Mark Cave-Ayland
On Fri, Sep 29, 2023 at 05:08:21PM +0300, Emmanouil Pitsidianakis wrote:
> Add a new VIRTIO device for the virtio sound device id. Functionality
> will be added in the following commits.
>
> Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471
> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
> Signed-off-by: Igor Skalkin <Igor.Skalkin@opensynergy.com>
> Signed-off-by: Anton Yakovlev <Anton.Yakovlev@opensynergy.com>
> Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
> ---
> MAINTAINERS | 6 +
> hw/virtio/Kconfig | 5 +
> hw/virtio/meson.build | 1 +
> hw/virtio/trace-events | 9 ++
> hw/virtio/virtio-snd.c | 223 +++++++++++++++++++++++++++++++++
Why isn't this under hw/virtio and not under hw/audio?
> include/hw/virtio/virtio-snd.h | 79 ++++++++++++
> 6 files changed, 323 insertions(+)
> create mode 100644 hw/virtio/virtio-snd.c
> create mode 100644 include/hw/virtio/virtio-snd.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 355b1960ce..81ca61e90b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2241,6 +2241,12 @@ 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>
> diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig
> index 92c9cf6c96..d6f20657b3 100644
> --- a/hw/virtio/Kconfig
> +++ b/hw/virtio/Kconfig
> @@ -17,6 +17,11 @@ config VIRTIO_PCI
> depends on PCI
> select VIRTIO
>
> +config VIRTIO_SND
> + bool
> + default y
> + depends on VIRTIO
> +
> config VIRTIO_MMIO
> bool
> select VIRTIO
> diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build
> index 13e7c6c272..120d4bfa0a 100644
> --- a/hw/virtio/meson.build
> +++ b/hw/virtio/meson.build
> @@ -31,6 +31,7 @@ specific_virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock.c
> specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_VSOCK', if_true: files('vhost-user-vsock.c'))
> specific_virtio_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng.c'))
> specific_virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c'))
> +specific_virtio_ss.add(when: 'CONFIG_VIRTIO_SND', if_true: files('virtio-snd.c'))
> specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_I2C', if_true: files('vhost-user-i2c.c'))
> specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_RNG', if_true: files('vhost-user-rng.c'))
> specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_GPIO', if_true: files('vhost-user-gpio.c'))
> diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
> index 7109cf1a3b..3ed7da35f2 100644
> --- a/hw/virtio/trace-events
> +++ b/hw/virtio/trace-events
> @@ -154,3 +154,12 @@ virtio_pmem_flush_done(int type) "fsync return=%d"
> virtio_gpio_start(void) "start"
> virtio_gpio_stop(void) "stop"
> virtio_gpio_set_status(uint8_t status) "0x%x"
> +
> +#virtio-snd.c
> +virtio_snd_get_config(void *vdev, uint32_t jacks, uint32_t streams, uint32_t chmaps) "snd %p: get_config jacks=%"PRIu32" streams=%"PRIu32" chmaps=%"PRIu32""
> +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 %"PRIu32"->%"PRIu32", streams from %"PRIu32"->%"PRIu32", chmaps from %"PRIu32"->%"PRIu32
> +virtio_snd_get_features(void *vdev, uint64_t features) "snd %p: get_features 0x%"PRIx64
> +virtio_snd_vm_state_running(void) "vm state running"
> +virtio_snd_vm_state_stopped(void) "vm state stopped"
> +virtio_snd_realize(void *snd) "snd %p: realize"
> +virtio_snd_unrealize(void *snd) "snd %p: unrealize"
> diff --git a/hw/virtio/virtio-snd.c b/hw/virtio/virtio-snd.c
> new file mode 100644
> index 0000000000..a8204e8a02
> --- /dev/null
> +++ b/hw/virtio/virtio-snd.c
> @@ -0,0 +1,223 @@
> +/*
> + * VIRTIO Sound Device conforming to
> + *
> + * "Virtual I/O Device (VIRTIO) Version 1.2
> + * Committee Specification Draft 01
> + * 09 May 2022"
> + *
> + * <https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-52900014>
> + *
> + * Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
> + * Copyright (C) 2019 OpenSynergy GmbH
> + *
> + * 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 "qemu/iov.h"
> +#include "qemu/log.h"
> +#include "qemu/error-report.h"
> +#include "include/qemu/lockable.h"
> +#include "sysemu/runstate.h"
> +#include "trace.h"
> +#include "qapi/error.h"
> +#include "hw/virtio/virtio-snd.h"
> +#include "hw/core/cpu.h"
> +
> +#define VIRTIO_SOUND_VM_VERSION 1
> +#define VIRTIO_SOUND_JACK_DEFAULT 0
> +#define VIRTIO_SOUND_STREAM_DEFAULT 1
> +#define VIRTIO_SOUND_CHMAP_DEFAULT 0
> +#define VIRTIO_SOUND_HDA_FN_NID 0
> +
> +static const VMStateDescription vmstate_virtio_snd_device = {
> + .name = TYPE_VIRTIO_SND,
> + .version_id = VIRTIO_SOUND_VM_VERSION,
> + .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
> +};
> +
> +static const VMStateDescription vmstate_virtio_snd = {
> + .name = "virtio-sound",
> + .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
> + .version_id = VIRTIO_SOUND_VM_VERSION,
> + .fields = (VMStateField[]) {
> + VMSTATE_VIRTIO_DEVICE,
> + VMSTATE_END_OF_LIST()
> + },
> +};
> +
> +static Property virtio_snd_properties[] = {
> + DEFINE_AUDIO_PROPERTIES(VirtIOSound, card),
> + DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks,
> + VIRTIO_SOUND_JACK_DEFAULT),
> + DEFINE_PROP_UINT32("streams", VirtIOSound, snd_conf.streams,
> + VIRTIO_SOUND_STREAM_DEFAULT),
> + DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps,
> + VIRTIO_SOUND_CHMAP_DEFAULT),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void
> +virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config)
> +{
> + VirtIOSound *s = VIRTIO_SND(vdev);
> + trace_virtio_snd_get_config(vdev,
> + s->snd_conf.jacks,
> + s->snd_conf.streams,
> + s->snd_conf.chmaps);
> +
> + memcpy(config, &s->snd_conf, sizeof(s->snd_conf));
> +}
> +
> +static void
> +virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
> +{
> + VirtIOSound *s = VIRTIO_SND(vdev);
> + const virtio_snd_config *sndconfig =
> + (const virtio_snd_config *)config;
> +
> +
> + trace_virtio_snd_set_config(vdev,
> + s->snd_conf.jacks,
> + sndconfig->jacks,
> + s->snd_conf.streams,
> + sndconfig->streams,
> + s->snd_conf.chmaps,
> + sndconfig->chmaps);
> +
> + memcpy(&s->snd_conf, sndconfig, sizeof(s->snd_conf));
> +}
> +
> +/*
> + * Queue handler stub.
> + *
> + * @vdev: VirtIOSound device
> + * @vq: virtqueue
> + */
> +static void virtio_snd_handle_queue(VirtIODevice *vdev, VirtQueue *vq) {}
> +
> +static uint64_t get_features(VirtIODevice *vdev, uint64_t features,
> + Error **errp)
> +{
> + /*
> + * virtio-v1.2-csd01, 5.14.3,
> + * Feature Bits
> + * None currently defined.
> + */
> + VirtIOSound *s = VIRTIO_SND(vdev);
> + features |= s->features;
> +
> + trace_virtio_snd_get_features(vdev, features);
> +
> + return features;
> +}
> +
> +static void
> +virtio_snd_vm_state_change(void *opaque, bool running,
> + RunState state)
> +{
> + if (running) {
> + trace_virtio_snd_vm_state_running();
> + } else {
> + trace_virtio_snd_vm_state_stopped();
> + }
> +}
> +
> +static void virtio_snd_realize(DeviceState *dev, Error **errp)
> +{
> + ERRP_GUARD();
> + VirtIOSound *vsnd = VIRTIO_SND(dev);
> + VirtIODevice *vdev = VIRTIO_DEVICE(dev);
> +
> + vsnd->vmstate =
> + qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd);
> +
> + trace_virtio_snd_realize(vsnd);
> +
> + virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config));
> + virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1);
> +
> + /* set number of jacks and streams */
> + if (vsnd->snd_conf.jacks > 8) {
> + error_setg(errp,
> + "Invalid number of jacks: %"PRIu32,
> + vsnd->snd_conf.jacks);
> + return;
> + }
> + if (vsnd->snd_conf.streams < 1 || vsnd->snd_conf.streams > 10) {
> + error_setg(errp,
> + "Invalid number of streams: %"PRIu32,
> + vsnd->snd_conf.streams);
> + return;
> + }
> +
> + if (vsnd->snd_conf.chmaps > VIRTIO_SND_CHMAP_MAX_SIZE) {
> + error_setg(errp,
> + "Invalid number of channel maps: %"PRIu32,
> + vsnd->snd_conf.chmaps);
> + return;
> + }
> +
> + AUD_register_card("virtio-sound", &vsnd->card);
> +
> + vsnd->queues[VIRTIO_SND_VQ_CONTROL] =
> + virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
> + vsnd->queues[VIRTIO_SND_VQ_EVENT] =
> + virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
> + vsnd->queues[VIRTIO_SND_VQ_TX] =
> + virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
> + vsnd->queues[VIRTIO_SND_VQ_RX] =
> + virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
> +}
> +
> +static void virtio_snd_unrealize(DeviceState *dev)
> +{
> + VirtIODevice *vdev = VIRTIO_DEVICE(dev);
> + VirtIOSound *vsnd = VIRTIO_SND(dev);
> +
> + qemu_del_vm_change_state_handler(vsnd->vmstate);
> + trace_virtio_snd_unrealize(vsnd);
> +
> + AUD_remove_card(&vsnd->card);
> + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]);
> + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]);
> + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]);
> + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_RX]);
> + virtio_cleanup(vdev);
> +}
> +
> +
> +static void virtio_snd_reset(VirtIODevice *vdev) {}
> +
> +static void virtio_snd_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
> +
> +
> + set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
> + device_class_set_props(dc, virtio_snd_properties);
> +
> + dc->vmsd = &vmstate_virtio_snd;
> + vdc->vmsd = &vmstate_virtio_snd_device;
> + vdc->realize = virtio_snd_realize;
> + vdc->unrealize = virtio_snd_unrealize;
> + vdc->get_config = virtio_snd_get_config;
> + vdc->set_config = virtio_snd_set_config;
> + vdc->get_features = get_features;
> + vdc->reset = virtio_snd_reset;
> + vdc->legacy_features = 0;
> +}
> +
> +static const TypeInfo virtio_snd_types[] = {
> + {
> + .name = TYPE_VIRTIO_SND,
> + .parent = TYPE_VIRTIO_DEVICE,
> + .instance_size = sizeof(VirtIOSound),
> + .class_init = virtio_snd_class_init,
> + }
> +};
> +
> +DEFINE_TYPES(virtio_snd_types)
> diff --git a/include/hw/virtio/virtio-snd.h b/include/hw/virtio/virtio-snd.h
> new file mode 100644
> index 0000000000..934e854a80
> --- /dev/null
> +++ b/include/hw/virtio/virtio-snd.h
> @@ -0,0 +1,79 @@
> +/*
> + * VIRTIO Sound Device conforming to
> + *
> + * "Virtual I/O Device (VIRTIO) Version 1.2
> + * Committee Specification Draft 01
> + * 09 May 2022"
> + *
> + * Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
> + * Copyright (C) 2019 OpenSynergy GmbH
> + *
> + * 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.
> + */
> +
> +#ifndef QEMU_VIRTIO_SOUND_H
> +#define QEMU_VIRTIO_SOUND_H
> +
> +#include "hw/virtio/virtio.h"
> +#include "audio/audio.h"
> +#include "standard-headers/linux/virtio_ids.h"
> +#include "standard-headers/linux/virtio_snd.h"
> +
> +#define TYPE_VIRTIO_SND "virtio-sound"
> +#define VIRTIO_SND(obj) \
> + OBJECT_CHECK(VirtIOSound, (obj), TYPE_VIRTIO_SND)
> +
> +/* CONFIGURATION SPACE */
> +
> +typedef struct virtio_snd_config virtio_snd_config;
> +
> +/* COMMON DEFINITIONS */
> +
> +/* common header for request/response*/
> +typedef struct virtio_snd_hdr virtio_snd_hdr;
> +
> +/* event notification */
> +typedef struct virtio_snd_event virtio_snd_event;
> +
> +/* common control request to query an item information */
> +typedef struct virtio_snd_query_info virtio_snd_query_info;
> +
> +/* JACK CONTROL MESSAGES */
> +
> +typedef struct virtio_snd_jack_hdr virtio_snd_jack_hdr;
> +
> +/* jack information structure */
> +typedef struct virtio_snd_jack_info virtio_snd_jack_info;
> +
> +/* jack remapping control request */
> +typedef struct virtio_snd_jack_remap virtio_snd_jack_remap;
> +
> +/*
> + * PCM CONTROL MESSAGES
> + */
> +typedef struct virtio_snd_pcm_hdr virtio_snd_pcm_hdr;
> +
> +/* PCM stream info structure */
> +typedef struct virtio_snd_pcm_info virtio_snd_pcm_info;
> +
> +/* set PCM stream params */
> +typedef struct virtio_snd_pcm_set_params virtio_snd_pcm_set_params;
> +
> +/* I/O request header */
> +typedef struct virtio_snd_pcm_xfer virtio_snd_pcm_xfer;
> +
> +/* I/O request status */
> +typedef struct virtio_snd_pcm_status virtio_snd_pcm_status;
> +
> +typedef struct VirtIOSound {
> + VirtIODevice parent_obj;
> +
> + VirtQueue *queues[VIRTIO_SND_VQ_MAX];
> + uint64_t features;
> + QEMUSoundCard card;
> + VMChangeStateEntry *vmstate;
> + virtio_snd_config snd_conf;
> +} VirtIOSound;
> +#endif
> --
> 2.39.2
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v10 09/11] virtio-sound: implement audio output (TX)
2023-09-29 18:59 ` Manos Pitsidianakis
@ 2023-10-03 14:27 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 21+ messages in thread
From: Philippe Mathieu-Daudé @ 2023-10-03 14:27 UTC (permalink / raw)
To: Manos Pitsidianakis, qemu-devel
Cc: Igor Skalkin, Anton Yakovlev, Paolo Bonzini, Gerd Hoffmann,
Michael S. Tsirkin, Marcel Apfelbaum, Daniel P. Berrangé,
Eduardo Habkost, Marc-Andr é Lureau, Volker R ü melin,
Kő vá gó , Zoltá n, Alex Benn é e,
Mark Cave-Ayland
Hi Manos,
On 29/9/23 20:59, Manos Pitsidianakis wrote:
> On Fri, 29 Sep 2023 17:08, Emmanouil Pitsidianakis
> <manos.pitsidianakis@linaro.org> wrote:
>> Handle output IO messages in the transmit (TX) virtqueue.
> [..]
>> + if (!stream->active) {
>> + /* Stream has stopped, so do not perform AUD_write. */
>> + goto return_tx_buffer;
>> + }
> [..]
>> +return_tx_buffer:
>> + virtio_snd_pcm_status resp = { 0 };
>> + resp.status = cpu_to_le32(VIRTIO_SND_S_OK);
>
>
> It seems I was too hasty to submit this patch. It does not build with
> clang on macos because it does not allow labels before declarations.
>
> It needs the following changes to compile:
>
> --- a/hw/virtio/virtio-snd.c
> +++ b/hw/virtio/virtio-snd.c
> @@ -1187,7 +1187,7 @@ static void virtio_snd_pcm_out_cb(void *data, int
> available)
> buffer->offset += size;
> available -= size;
> if (buffer->size < 1) {
> -return_tx_buffer:
> +return_tx_buffer:;
> virtio_snd_pcm_status resp = { 0 };
> resp.status = cpu_to_le32(VIRTIO_SND_S_OK);
> resp.latency_bytes = 0;
Extract a tiny return_tx_buffer() function, which you can then
call in the 2 different places.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v10 01/11] Add virtio-sound device stub
2023-10-03 14:13 ` Michael S. Tsirkin
@ 2023-10-03 14:34 ` Alex Bennée
2023-10-03 14:45 ` Michael S. Tsirkin
0 siblings, 1 reply; 21+ messages in thread
From: Alex Bennée @ 2023-10-03 14:34 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Emmanouil Pitsidianakis, Igor Skalkin, Anton Yakovlev,
Paolo Bonzini, Gerd Hoffmann, Marcel Apfelbaum,
Daniel P. Berrangé, Eduardo Habkost, Marc-André Lureau,
Volker Rümelin, Kővágó, Zoltán,
Philippe Mathieu-Daudé, Mark Cave-Ayland, qemu-devel
"Michael S. Tsirkin" <mst@redhat.com> writes:
> On Fri, Sep 29, 2023 at 05:08:21PM +0300, Emmanouil Pitsidianakis wrote:
>> Add a new VIRTIO device for the virtio sound device id. Functionality
>> will be added in the following commits.
>>
>> Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471
>> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
>> Signed-off-by: Igor Skalkin <Igor.Skalkin@opensynergy.com>
>> Signed-off-by: Anton Yakovlev <Anton.Yakovlev@opensynergy.com>
>> Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
>> ---
>> MAINTAINERS | 6 +
>> hw/virtio/Kconfig | 5 +
>> hw/virtio/meson.build | 1 +
>> hw/virtio/trace-events | 9 ++
>> hw/virtio/virtio-snd.c | 223 +++++++++++++++++++++++++++++++++
>
>
> Why isn't this under hw/virtio and not under hw/audio?
Most of our virtio devices are under hw/virtio although we have a random
selection of other virtio devices elsewhere in the tree.
>
>
>> include/hw/virtio/virtio-snd.h | 79 ++++++++++++
>> 6 files changed, 323 insertions(+)
>> create mode 100644 hw/virtio/virtio-snd.c
>> create mode 100644 include/hw/virtio/virtio-snd.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 355b1960ce..81ca61e90b 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -2241,6 +2241,12 @@ 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>
>> diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig
>> index 92c9cf6c96..d6f20657b3 100644
>> --- a/hw/virtio/Kconfig
>> +++ b/hw/virtio/Kconfig
>> @@ -17,6 +17,11 @@ config VIRTIO_PCI
>> depends on PCI
>> select VIRTIO
>>
>> +config VIRTIO_SND
>> + bool
>> + default y
>> + depends on VIRTIO
>> +
>> config VIRTIO_MMIO
>> bool
>> select VIRTIO
>> diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build
>> index 13e7c6c272..120d4bfa0a 100644
>> --- a/hw/virtio/meson.build
>> +++ b/hw/virtio/meson.build
>> @@ -31,6 +31,7 @@ specific_virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock.c
>> specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_VSOCK', if_true: files('vhost-user-vsock.c'))
>> specific_virtio_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng.c'))
>> specific_virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c'))
>> +specific_virtio_ss.add(when: 'CONFIG_VIRTIO_SND', if_true: files('virtio-snd.c'))
>> specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_I2C', if_true: files('vhost-user-i2c.c'))
>> specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_RNG', if_true: files('vhost-user-rng.c'))
>> specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_GPIO', if_true: files('vhost-user-gpio.c'))
>> diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
>> index 7109cf1a3b..3ed7da35f2 100644
>> --- a/hw/virtio/trace-events
>> +++ b/hw/virtio/trace-events
>> @@ -154,3 +154,12 @@ virtio_pmem_flush_done(int type) "fsync return=%d"
>> virtio_gpio_start(void) "start"
>> virtio_gpio_stop(void) "stop"
>> virtio_gpio_set_status(uint8_t status) "0x%x"
>> +
>> +#virtio-snd.c
>> +virtio_snd_get_config(void *vdev, uint32_t jacks, uint32_t streams, uint32_t chmaps) "snd %p: get_config jacks=%"PRIu32" streams=%"PRIu32" chmaps=%"PRIu32""
>> +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 %"PRIu32"->%"PRIu32", streams from %"PRIu32"->%"PRIu32", chmaps from %"PRIu32"->%"PRIu32
>> +virtio_snd_get_features(void *vdev, uint64_t features) "snd %p: get_features 0x%"PRIx64
>> +virtio_snd_vm_state_running(void) "vm state running"
>> +virtio_snd_vm_state_stopped(void) "vm state stopped"
>> +virtio_snd_realize(void *snd) "snd %p: realize"
>> +virtio_snd_unrealize(void *snd) "snd %p: unrealize"
>> diff --git a/hw/virtio/virtio-snd.c b/hw/virtio/virtio-snd.c
>> new file mode 100644
>> index 0000000000..a8204e8a02
>> --- /dev/null
>> +++ b/hw/virtio/virtio-snd.c
>> @@ -0,0 +1,223 @@
>> +/*
>> + * VIRTIO Sound Device conforming to
>> + *
>> + * "Virtual I/O Device (VIRTIO) Version 1.2
>> + * Committee Specification Draft 01
>> + * 09 May 2022"
>> + *
>> + * <https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-52900014>
>> + *
>> + * Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
>> + * Copyright (C) 2019 OpenSynergy GmbH
>> + *
>> + * 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 "qemu/iov.h"
>> +#include "qemu/log.h"
>> +#include "qemu/error-report.h"
>> +#include "include/qemu/lockable.h"
>> +#include "sysemu/runstate.h"
>> +#include "trace.h"
>> +#include "qapi/error.h"
>> +#include "hw/virtio/virtio-snd.h"
>> +#include "hw/core/cpu.h"
>> +
>> +#define VIRTIO_SOUND_VM_VERSION 1
>> +#define VIRTIO_SOUND_JACK_DEFAULT 0
>> +#define VIRTIO_SOUND_STREAM_DEFAULT 1
>> +#define VIRTIO_SOUND_CHMAP_DEFAULT 0
>> +#define VIRTIO_SOUND_HDA_FN_NID 0
>> +
>> +static const VMStateDescription vmstate_virtio_snd_device = {
>> + .name = TYPE_VIRTIO_SND,
>> + .version_id = VIRTIO_SOUND_VM_VERSION,
>> + .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
>> +};
>> +
>> +static const VMStateDescription vmstate_virtio_snd = {
>> + .name = "virtio-sound",
>> + .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
>> + .version_id = VIRTIO_SOUND_VM_VERSION,
>> + .fields = (VMStateField[]) {
>> + VMSTATE_VIRTIO_DEVICE,
>> + VMSTATE_END_OF_LIST()
>> + },
>> +};
>> +
>> +static Property virtio_snd_properties[] = {
>> + DEFINE_AUDIO_PROPERTIES(VirtIOSound, card),
>> + DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks,
>> + VIRTIO_SOUND_JACK_DEFAULT),
>> + DEFINE_PROP_UINT32("streams", VirtIOSound, snd_conf.streams,
>> + VIRTIO_SOUND_STREAM_DEFAULT),
>> + DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps,
>> + VIRTIO_SOUND_CHMAP_DEFAULT),
>> + DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static void
>> +virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config)
>> +{
>> + VirtIOSound *s = VIRTIO_SND(vdev);
>> + trace_virtio_snd_get_config(vdev,
>> + s->snd_conf.jacks,
>> + s->snd_conf.streams,
>> + s->snd_conf.chmaps);
>> +
>> + memcpy(config, &s->snd_conf, sizeof(s->snd_conf));
>> +}
>> +
>> +static void
>> +virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
>> +{
>> + VirtIOSound *s = VIRTIO_SND(vdev);
>> + const virtio_snd_config *sndconfig =
>> + (const virtio_snd_config *)config;
>> +
>> +
>> + trace_virtio_snd_set_config(vdev,
>> + s->snd_conf.jacks,
>> + sndconfig->jacks,
>> + s->snd_conf.streams,
>> + sndconfig->streams,
>> + s->snd_conf.chmaps,
>> + sndconfig->chmaps);
>> +
>> + memcpy(&s->snd_conf, sndconfig, sizeof(s->snd_conf));
>> +}
>> +
>> +/*
>> + * Queue handler stub.
>> + *
>> + * @vdev: VirtIOSound device
>> + * @vq: virtqueue
>> + */
>> +static void virtio_snd_handle_queue(VirtIODevice *vdev, VirtQueue *vq) {}
>> +
>> +static uint64_t get_features(VirtIODevice *vdev, uint64_t features,
>> + Error **errp)
>> +{
>> + /*
>> + * virtio-v1.2-csd01, 5.14.3,
>> + * Feature Bits
>> + * None currently defined.
>> + */
>> + VirtIOSound *s = VIRTIO_SND(vdev);
>> + features |= s->features;
>> +
>> + trace_virtio_snd_get_features(vdev, features);
>> +
>> + return features;
>> +}
>> +
>> +static void
>> +virtio_snd_vm_state_change(void *opaque, bool running,
>> + RunState state)
>> +{
>> + if (running) {
>> + trace_virtio_snd_vm_state_running();
>> + } else {
>> + trace_virtio_snd_vm_state_stopped();
>> + }
>> +}
>> +
>> +static void virtio_snd_realize(DeviceState *dev, Error **errp)
>> +{
>> + ERRP_GUARD();
>> + VirtIOSound *vsnd = VIRTIO_SND(dev);
>> + VirtIODevice *vdev = VIRTIO_DEVICE(dev);
>> +
>> + vsnd->vmstate =
>> + qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd);
>> +
>> + trace_virtio_snd_realize(vsnd);
>> +
>> + virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config));
>> + virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1);
>> +
>> + /* set number of jacks and streams */
>> + if (vsnd->snd_conf.jacks > 8) {
>> + error_setg(errp,
>> + "Invalid number of jacks: %"PRIu32,
>> + vsnd->snd_conf.jacks);
>> + return;
>> + }
>> + if (vsnd->snd_conf.streams < 1 || vsnd->snd_conf.streams > 10) {
>> + error_setg(errp,
>> + "Invalid number of streams: %"PRIu32,
>> + vsnd->snd_conf.streams);
>> + return;
>> + }
>> +
>> + if (vsnd->snd_conf.chmaps > VIRTIO_SND_CHMAP_MAX_SIZE) {
>> + error_setg(errp,
>> + "Invalid number of channel maps: %"PRIu32,
>> + vsnd->snd_conf.chmaps);
>> + return;
>> + }
>> +
>> + AUD_register_card("virtio-sound", &vsnd->card);
>> +
>> + vsnd->queues[VIRTIO_SND_VQ_CONTROL] =
>> + virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
>> + vsnd->queues[VIRTIO_SND_VQ_EVENT] =
>> + virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
>> + vsnd->queues[VIRTIO_SND_VQ_TX] =
>> + virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
>> + vsnd->queues[VIRTIO_SND_VQ_RX] =
>> + virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
>> +}
>> +
>> +static void virtio_snd_unrealize(DeviceState *dev)
>> +{
>> + VirtIODevice *vdev = VIRTIO_DEVICE(dev);
>> + VirtIOSound *vsnd = VIRTIO_SND(dev);
>> +
>> + qemu_del_vm_change_state_handler(vsnd->vmstate);
>> + trace_virtio_snd_unrealize(vsnd);
>> +
>> + AUD_remove_card(&vsnd->card);
>> + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]);
>> + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]);
>> + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]);
>> + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_RX]);
>> + virtio_cleanup(vdev);
>> +}
>> +
>> +
>> +static void virtio_snd_reset(VirtIODevice *vdev) {}
>> +
>> +static void virtio_snd_class_init(ObjectClass *klass, void *data)
>> +{
>> + DeviceClass *dc = DEVICE_CLASS(klass);
>> + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
>> +
>> +
>> + set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
>> + device_class_set_props(dc, virtio_snd_properties);
>> +
>> + dc->vmsd = &vmstate_virtio_snd;
>> + vdc->vmsd = &vmstate_virtio_snd_device;
>> + vdc->realize = virtio_snd_realize;
>> + vdc->unrealize = virtio_snd_unrealize;
>> + vdc->get_config = virtio_snd_get_config;
>> + vdc->set_config = virtio_snd_set_config;
>> + vdc->get_features = get_features;
>> + vdc->reset = virtio_snd_reset;
>> + vdc->legacy_features = 0;
>> +}
>> +
>> +static const TypeInfo virtio_snd_types[] = {
>> + {
>> + .name = TYPE_VIRTIO_SND,
>> + .parent = TYPE_VIRTIO_DEVICE,
>> + .instance_size = sizeof(VirtIOSound),
>> + .class_init = virtio_snd_class_init,
>> + }
>> +};
>> +
>> +DEFINE_TYPES(virtio_snd_types)
>> diff --git a/include/hw/virtio/virtio-snd.h b/include/hw/virtio/virtio-snd.h
>> new file mode 100644
>> index 0000000000..934e854a80
>> --- /dev/null
>> +++ b/include/hw/virtio/virtio-snd.h
>> @@ -0,0 +1,79 @@
>> +/*
>> + * VIRTIO Sound Device conforming to
>> + *
>> + * "Virtual I/O Device (VIRTIO) Version 1.2
>> + * Committee Specification Draft 01
>> + * 09 May 2022"
>> + *
>> + * Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
>> + * Copyright (C) 2019 OpenSynergy GmbH
>> + *
>> + * 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.
>> + */
>> +
>> +#ifndef QEMU_VIRTIO_SOUND_H
>> +#define QEMU_VIRTIO_SOUND_H
>> +
>> +#include "hw/virtio/virtio.h"
>> +#include "audio/audio.h"
>> +#include "standard-headers/linux/virtio_ids.h"
>> +#include "standard-headers/linux/virtio_snd.h"
>> +
>> +#define TYPE_VIRTIO_SND "virtio-sound"
>> +#define VIRTIO_SND(obj) \
>> + OBJECT_CHECK(VirtIOSound, (obj), TYPE_VIRTIO_SND)
>> +
>> +/* CONFIGURATION SPACE */
>> +
>> +typedef struct virtio_snd_config virtio_snd_config;
>> +
>> +/* COMMON DEFINITIONS */
>> +
>> +/* common header for request/response*/
>> +typedef struct virtio_snd_hdr virtio_snd_hdr;
>> +
>> +/* event notification */
>> +typedef struct virtio_snd_event virtio_snd_event;
>> +
>> +/* common control request to query an item information */
>> +typedef struct virtio_snd_query_info virtio_snd_query_info;
>> +
>> +/* JACK CONTROL MESSAGES */
>> +
>> +typedef struct virtio_snd_jack_hdr virtio_snd_jack_hdr;
>> +
>> +/* jack information structure */
>> +typedef struct virtio_snd_jack_info virtio_snd_jack_info;
>> +
>> +/* jack remapping control request */
>> +typedef struct virtio_snd_jack_remap virtio_snd_jack_remap;
>> +
>> +/*
>> + * PCM CONTROL MESSAGES
>> + */
>> +typedef struct virtio_snd_pcm_hdr virtio_snd_pcm_hdr;
>> +
>> +/* PCM stream info structure */
>> +typedef struct virtio_snd_pcm_info virtio_snd_pcm_info;
>> +
>> +/* set PCM stream params */
>> +typedef struct virtio_snd_pcm_set_params virtio_snd_pcm_set_params;
>> +
>> +/* I/O request header */
>> +typedef struct virtio_snd_pcm_xfer virtio_snd_pcm_xfer;
>> +
>> +/* I/O request status */
>> +typedef struct virtio_snd_pcm_status virtio_snd_pcm_status;
>> +
>> +typedef struct VirtIOSound {
>> + VirtIODevice parent_obj;
>> +
>> + VirtQueue *queues[VIRTIO_SND_VQ_MAX];
>> + uint64_t features;
>> + QEMUSoundCard card;
>> + VMChangeStateEntry *vmstate;
>> + virtio_snd_config snd_conf;
>> +} VirtIOSound;
>> +#endif
>> --
>> 2.39.2
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v10 01/11] Add virtio-sound device stub
2023-10-03 14:34 ` Alex Bennée
@ 2023-10-03 14:45 ` Michael S. Tsirkin
2023-10-03 15:26 ` Alex Bennée
0 siblings, 1 reply; 21+ messages in thread
From: Michael S. Tsirkin @ 2023-10-03 14:45 UTC (permalink / raw)
To: Alex Bennée
Cc: Emmanouil Pitsidianakis, Igor Skalkin, Anton Yakovlev,
Paolo Bonzini, Gerd Hoffmann, Marcel Apfelbaum,
Daniel P. Berrangé, Eduardo Habkost, Marc-André Lureau,
Volker Rümelin, Kővágó, Zoltán,
Philippe Mathieu-Daudé, Mark Cave-Ayland, qemu-devel
On Tue, Oct 03, 2023 at 03:34:44PM +0100, Alex Bennée wrote:
>
> "Michael S. Tsirkin" <mst@redhat.com> writes:
>
> > On Fri, Sep 29, 2023 at 05:08:21PM +0300, Emmanouil Pitsidianakis wrote:
> >> Add a new VIRTIO device for the virtio sound device id. Functionality
> >> will be added in the following commits.
> >>
> >> Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471
> >> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
> >> Signed-off-by: Igor Skalkin <Igor.Skalkin@opensynergy.com>
> >> Signed-off-by: Anton Yakovlev <Anton.Yakovlev@opensynergy.com>
> >> Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
> >> ---
> >> MAINTAINERS | 6 +
> >> hw/virtio/Kconfig | 5 +
> >> hw/virtio/meson.build | 1 +
> >> hw/virtio/trace-events | 9 ++
> >> hw/virtio/virtio-snd.c | 223 +++++++++++++++++++++++++++++++++
> >
> >
> > Why isn't this under hw/virtio and not under hw/audio?
>
> Most of our virtio devices are under hw/virtio although we have a random
> selection of other virtio devices elsewhere in the tree.
That's because there wasn't anywhere to put them.
Not the case for audio.
Although, I just missed the cases of gpio and i2c -
I think we should move them.
virtio maintainers can't be a experts on all areas.
We need to locate virtio devices appropriately so they
get attention from appropriate maintainers.
> >
> >
> >> include/hw/virtio/virtio-snd.h | 79 ++++++++++++
> >> 6 files changed, 323 insertions(+)
> >> create mode 100644 hw/virtio/virtio-snd.c
> >> create mode 100644 include/hw/virtio/virtio-snd.h
> >>
> >> diff --git a/MAINTAINERS b/MAINTAINERS
> >> index 355b1960ce..81ca61e90b 100644
> >> --- a/MAINTAINERS
> >> +++ b/MAINTAINERS
> >> @@ -2241,6 +2241,12 @@ 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>
> >> diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig
> >> index 92c9cf6c96..d6f20657b3 100644
> >> --- a/hw/virtio/Kconfig
> >> +++ b/hw/virtio/Kconfig
> >> @@ -17,6 +17,11 @@ config VIRTIO_PCI
> >> depends on PCI
> >> select VIRTIO
> >>
> >> +config VIRTIO_SND
> >> + bool
> >> + default y
> >> + depends on VIRTIO
> >> +
> >> config VIRTIO_MMIO
> >> bool
> >> select VIRTIO
> >> diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build
> >> index 13e7c6c272..120d4bfa0a 100644
> >> --- a/hw/virtio/meson.build
> >> +++ b/hw/virtio/meson.build
> >> @@ -31,6 +31,7 @@ specific_virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock.c
> >> specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_VSOCK', if_true: files('vhost-user-vsock.c'))
> >> specific_virtio_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng.c'))
> >> specific_virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c'))
> >> +specific_virtio_ss.add(when: 'CONFIG_VIRTIO_SND', if_true: files('virtio-snd.c'))
> >> specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_I2C', if_true: files('vhost-user-i2c.c'))
> >> specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_RNG', if_true: files('vhost-user-rng.c'))
> >> specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_GPIO', if_true: files('vhost-user-gpio.c'))
> >> diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
> >> index 7109cf1a3b..3ed7da35f2 100644
> >> --- a/hw/virtio/trace-events
> >> +++ b/hw/virtio/trace-events
> >> @@ -154,3 +154,12 @@ virtio_pmem_flush_done(int type) "fsync return=%d"
> >> virtio_gpio_start(void) "start"
> >> virtio_gpio_stop(void) "stop"
> >> virtio_gpio_set_status(uint8_t status) "0x%x"
> >> +
> >> +#virtio-snd.c
> >> +virtio_snd_get_config(void *vdev, uint32_t jacks, uint32_t streams, uint32_t chmaps) "snd %p: get_config jacks=%"PRIu32" streams=%"PRIu32" chmaps=%"PRIu32""
> >> +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 %"PRIu32"->%"PRIu32", streams from %"PRIu32"->%"PRIu32", chmaps from %"PRIu32"->%"PRIu32
> >> +virtio_snd_get_features(void *vdev, uint64_t features) "snd %p: get_features 0x%"PRIx64
> >> +virtio_snd_vm_state_running(void) "vm state running"
> >> +virtio_snd_vm_state_stopped(void) "vm state stopped"
> >> +virtio_snd_realize(void *snd) "snd %p: realize"
> >> +virtio_snd_unrealize(void *snd) "snd %p: unrealize"
> >> diff --git a/hw/virtio/virtio-snd.c b/hw/virtio/virtio-snd.c
> >> new file mode 100644
> >> index 0000000000..a8204e8a02
> >> --- /dev/null
> >> +++ b/hw/virtio/virtio-snd.c
> >> @@ -0,0 +1,223 @@
> >> +/*
> >> + * VIRTIO Sound Device conforming to
> >> + *
> >> + * "Virtual I/O Device (VIRTIO) Version 1.2
> >> + * Committee Specification Draft 01
> >> + * 09 May 2022"
> >> + *
> >> + * <https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-52900014>
> >> + *
> >> + * Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
> >> + * Copyright (C) 2019 OpenSynergy GmbH
> >> + *
> >> + * 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 "qemu/iov.h"
> >> +#include "qemu/log.h"
> >> +#include "qemu/error-report.h"
> >> +#include "include/qemu/lockable.h"
> >> +#include "sysemu/runstate.h"
> >> +#include "trace.h"
> >> +#include "qapi/error.h"
> >> +#include "hw/virtio/virtio-snd.h"
> >> +#include "hw/core/cpu.h"
> >> +
> >> +#define VIRTIO_SOUND_VM_VERSION 1
> >> +#define VIRTIO_SOUND_JACK_DEFAULT 0
> >> +#define VIRTIO_SOUND_STREAM_DEFAULT 1
> >> +#define VIRTIO_SOUND_CHMAP_DEFAULT 0
> >> +#define VIRTIO_SOUND_HDA_FN_NID 0
> >> +
> >> +static const VMStateDescription vmstate_virtio_snd_device = {
> >> + .name = TYPE_VIRTIO_SND,
> >> + .version_id = VIRTIO_SOUND_VM_VERSION,
> >> + .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
> >> +};
> >> +
> >> +static const VMStateDescription vmstate_virtio_snd = {
> >> + .name = "virtio-sound",
> >> + .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
> >> + .version_id = VIRTIO_SOUND_VM_VERSION,
> >> + .fields = (VMStateField[]) {
> >> + VMSTATE_VIRTIO_DEVICE,
> >> + VMSTATE_END_OF_LIST()
> >> + },
> >> +};
> >> +
> >> +static Property virtio_snd_properties[] = {
> >> + DEFINE_AUDIO_PROPERTIES(VirtIOSound, card),
> >> + DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks,
> >> + VIRTIO_SOUND_JACK_DEFAULT),
> >> + DEFINE_PROP_UINT32("streams", VirtIOSound, snd_conf.streams,
> >> + VIRTIO_SOUND_STREAM_DEFAULT),
> >> + DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps,
> >> + VIRTIO_SOUND_CHMAP_DEFAULT),
> >> + DEFINE_PROP_END_OF_LIST(),
> >> +};
> >> +
> >> +static void
> >> +virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config)
> >> +{
> >> + VirtIOSound *s = VIRTIO_SND(vdev);
> >> + trace_virtio_snd_get_config(vdev,
> >> + s->snd_conf.jacks,
> >> + s->snd_conf.streams,
> >> + s->snd_conf.chmaps);
> >> +
> >> + memcpy(config, &s->snd_conf, sizeof(s->snd_conf));
> >> +}
> >> +
> >> +static void
> >> +virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
> >> +{
> >> + VirtIOSound *s = VIRTIO_SND(vdev);
> >> + const virtio_snd_config *sndconfig =
> >> + (const virtio_snd_config *)config;
> >> +
> >> +
> >> + trace_virtio_snd_set_config(vdev,
> >> + s->snd_conf.jacks,
> >> + sndconfig->jacks,
> >> + s->snd_conf.streams,
> >> + sndconfig->streams,
> >> + s->snd_conf.chmaps,
> >> + sndconfig->chmaps);
> >> +
> >> + memcpy(&s->snd_conf, sndconfig, sizeof(s->snd_conf));
> >> +}
> >> +
> >> +/*
> >> + * Queue handler stub.
> >> + *
> >> + * @vdev: VirtIOSound device
> >> + * @vq: virtqueue
> >> + */
> >> +static void virtio_snd_handle_queue(VirtIODevice *vdev, VirtQueue *vq) {}
> >> +
> >> +static uint64_t get_features(VirtIODevice *vdev, uint64_t features,
> >> + Error **errp)
> >> +{
> >> + /*
> >> + * virtio-v1.2-csd01, 5.14.3,
> >> + * Feature Bits
> >> + * None currently defined.
> >> + */
> >> + VirtIOSound *s = VIRTIO_SND(vdev);
> >> + features |= s->features;
> >> +
> >> + trace_virtio_snd_get_features(vdev, features);
> >> +
> >> + return features;
> >> +}
> >> +
> >> +static void
> >> +virtio_snd_vm_state_change(void *opaque, bool running,
> >> + RunState state)
> >> +{
> >> + if (running) {
> >> + trace_virtio_snd_vm_state_running();
> >> + } else {
> >> + trace_virtio_snd_vm_state_stopped();
> >> + }
> >> +}
> >> +
> >> +static void virtio_snd_realize(DeviceState *dev, Error **errp)
> >> +{
> >> + ERRP_GUARD();
> >> + VirtIOSound *vsnd = VIRTIO_SND(dev);
> >> + VirtIODevice *vdev = VIRTIO_DEVICE(dev);
> >> +
> >> + vsnd->vmstate =
> >> + qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd);
> >> +
> >> + trace_virtio_snd_realize(vsnd);
> >> +
> >> + virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config));
> >> + virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1);
> >> +
> >> + /* set number of jacks and streams */
> >> + if (vsnd->snd_conf.jacks > 8) {
> >> + error_setg(errp,
> >> + "Invalid number of jacks: %"PRIu32,
> >> + vsnd->snd_conf.jacks);
> >> + return;
> >> + }
> >> + if (vsnd->snd_conf.streams < 1 || vsnd->snd_conf.streams > 10) {
> >> + error_setg(errp,
> >> + "Invalid number of streams: %"PRIu32,
> >> + vsnd->snd_conf.streams);
> >> + return;
> >> + }
> >> +
> >> + if (vsnd->snd_conf.chmaps > VIRTIO_SND_CHMAP_MAX_SIZE) {
> >> + error_setg(errp,
> >> + "Invalid number of channel maps: %"PRIu32,
> >> + vsnd->snd_conf.chmaps);
> >> + return;
> >> + }
> >> +
> >> + AUD_register_card("virtio-sound", &vsnd->card);
> >> +
> >> + vsnd->queues[VIRTIO_SND_VQ_CONTROL] =
> >> + virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
> >> + vsnd->queues[VIRTIO_SND_VQ_EVENT] =
> >> + virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
> >> + vsnd->queues[VIRTIO_SND_VQ_TX] =
> >> + virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
> >> + vsnd->queues[VIRTIO_SND_VQ_RX] =
> >> + virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
> >> +}
> >> +
> >> +static void virtio_snd_unrealize(DeviceState *dev)
> >> +{
> >> + VirtIODevice *vdev = VIRTIO_DEVICE(dev);
> >> + VirtIOSound *vsnd = VIRTIO_SND(dev);
> >> +
> >> + qemu_del_vm_change_state_handler(vsnd->vmstate);
> >> + trace_virtio_snd_unrealize(vsnd);
> >> +
> >> + AUD_remove_card(&vsnd->card);
> >> + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]);
> >> + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]);
> >> + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]);
> >> + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_RX]);
> >> + virtio_cleanup(vdev);
> >> +}
> >> +
> >> +
> >> +static void virtio_snd_reset(VirtIODevice *vdev) {}
> >> +
> >> +static void virtio_snd_class_init(ObjectClass *klass, void *data)
> >> +{
> >> + DeviceClass *dc = DEVICE_CLASS(klass);
> >> + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
> >> +
> >> +
> >> + set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
> >> + device_class_set_props(dc, virtio_snd_properties);
> >> +
> >> + dc->vmsd = &vmstate_virtio_snd;
> >> + vdc->vmsd = &vmstate_virtio_snd_device;
> >> + vdc->realize = virtio_snd_realize;
> >> + vdc->unrealize = virtio_snd_unrealize;
> >> + vdc->get_config = virtio_snd_get_config;
> >> + vdc->set_config = virtio_snd_set_config;
> >> + vdc->get_features = get_features;
> >> + vdc->reset = virtio_snd_reset;
> >> + vdc->legacy_features = 0;
> >> +}
> >> +
> >> +static const TypeInfo virtio_snd_types[] = {
> >> + {
> >> + .name = TYPE_VIRTIO_SND,
> >> + .parent = TYPE_VIRTIO_DEVICE,
> >> + .instance_size = sizeof(VirtIOSound),
> >> + .class_init = virtio_snd_class_init,
> >> + }
> >> +};
> >> +
> >> +DEFINE_TYPES(virtio_snd_types)
> >> diff --git a/include/hw/virtio/virtio-snd.h b/include/hw/virtio/virtio-snd.h
> >> new file mode 100644
> >> index 0000000000..934e854a80
> >> --- /dev/null
> >> +++ b/include/hw/virtio/virtio-snd.h
> >> @@ -0,0 +1,79 @@
> >> +/*
> >> + * VIRTIO Sound Device conforming to
> >> + *
> >> + * "Virtual I/O Device (VIRTIO) Version 1.2
> >> + * Committee Specification Draft 01
> >> + * 09 May 2022"
> >> + *
> >> + * Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
> >> + * Copyright (C) 2019 OpenSynergy GmbH
> >> + *
> >> + * 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.
> >> + */
> >> +
> >> +#ifndef QEMU_VIRTIO_SOUND_H
> >> +#define QEMU_VIRTIO_SOUND_H
> >> +
> >> +#include "hw/virtio/virtio.h"
> >> +#include "audio/audio.h"
> >> +#include "standard-headers/linux/virtio_ids.h"
> >> +#include "standard-headers/linux/virtio_snd.h"
> >> +
> >> +#define TYPE_VIRTIO_SND "virtio-sound"
> >> +#define VIRTIO_SND(obj) \
> >> + OBJECT_CHECK(VirtIOSound, (obj), TYPE_VIRTIO_SND)
> >> +
> >> +/* CONFIGURATION SPACE */
> >> +
> >> +typedef struct virtio_snd_config virtio_snd_config;
> >> +
> >> +/* COMMON DEFINITIONS */
> >> +
> >> +/* common header for request/response*/
> >> +typedef struct virtio_snd_hdr virtio_snd_hdr;
> >> +
> >> +/* event notification */
> >> +typedef struct virtio_snd_event virtio_snd_event;
> >> +
> >> +/* common control request to query an item information */
> >> +typedef struct virtio_snd_query_info virtio_snd_query_info;
> >> +
> >> +/* JACK CONTROL MESSAGES */
> >> +
> >> +typedef struct virtio_snd_jack_hdr virtio_snd_jack_hdr;
> >> +
> >> +/* jack information structure */
> >> +typedef struct virtio_snd_jack_info virtio_snd_jack_info;
> >> +
> >> +/* jack remapping control request */
> >> +typedef struct virtio_snd_jack_remap virtio_snd_jack_remap;
> >> +
> >> +/*
> >> + * PCM CONTROL MESSAGES
> >> + */
> >> +typedef struct virtio_snd_pcm_hdr virtio_snd_pcm_hdr;
> >> +
> >> +/* PCM stream info structure */
> >> +typedef struct virtio_snd_pcm_info virtio_snd_pcm_info;
> >> +
> >> +/* set PCM stream params */
> >> +typedef struct virtio_snd_pcm_set_params virtio_snd_pcm_set_params;
> >> +
> >> +/* I/O request header */
> >> +typedef struct virtio_snd_pcm_xfer virtio_snd_pcm_xfer;
> >> +
> >> +/* I/O request status */
> >> +typedef struct virtio_snd_pcm_status virtio_snd_pcm_status;
> >> +
> >> +typedef struct VirtIOSound {
> >> + VirtIODevice parent_obj;
> >> +
> >> + VirtQueue *queues[VIRTIO_SND_VQ_MAX];
> >> + uint64_t features;
> >> + QEMUSoundCard card;
> >> + VMChangeStateEntry *vmstate;
> >> + virtio_snd_config snd_conf;
> >> +} VirtIOSound;
> >> +#endif
> >> --
> >> 2.39.2
>
>
> --
> Alex Bennée
> Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v10 01/11] Add virtio-sound device stub
2023-10-03 14:45 ` Michael S. Tsirkin
@ 2023-10-03 15:26 ` Alex Bennée
0 siblings, 0 replies; 21+ messages in thread
From: Alex Bennée @ 2023-10-03 15:26 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Emmanouil Pitsidianakis, Igor Skalkin, Anton Yakovlev,
Paolo Bonzini, Gerd Hoffmann, Marcel Apfelbaum,
Daniel P. Berrangé, Eduardo Habkost, Marc-André Lureau,
Volker Rümelin, Kővágó, Zoltán,
Philippe Mathieu-Daudé, Mark Cave-Ayland, qemu-devel
"Michael S. Tsirkin" <mst@redhat.com> writes:
> On Tue, Oct 03, 2023 at 03:34:44PM +0100, Alex Bennée wrote:
>>
>> "Michael S. Tsirkin" <mst@redhat.com> writes:
>>
>> > On Fri, Sep 29, 2023 at 05:08:21PM +0300, Emmanouil Pitsidianakis wrote:
>> >> Add a new VIRTIO device for the virtio sound device id. Functionality
>> >> will be added in the following commits.
>> >>
>> >> Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471
>> >> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
>> >> Signed-off-by: Igor Skalkin <Igor.Skalkin@opensynergy.com>
>> >> Signed-off-by: Anton Yakovlev <Anton.Yakovlev@opensynergy.com>
>> >> Signed-off-by: Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
>> >> ---
>> >> MAINTAINERS | 6 +
>> >> hw/virtio/Kconfig | 5 +
>> >> hw/virtio/meson.build | 1 +
>> >> hw/virtio/trace-events | 9 ++
>> >> hw/virtio/virtio-snd.c | 223 +++++++++++++++++++++++++++++++++
>> >
>> >
>> > Why isn't this under hw/virtio and not under hw/audio?
>>
>> Most of our virtio devices are under hw/virtio although we have a random
>> selection of other virtio devices elsewhere in the tree.
>
>
> That's because there wasn't anywhere to put them.
> Not the case for audio.
> Although, I just missed the cases of gpio and i2c -
> I think we should move them.
>
> virtio maintainers can't be a experts on all areas.
> We need to locate virtio devices appropriately so they
> get attention from appropriate maintainers.
Arguably for the vhost-user devices most of the code is VirtIO specific
because the internals are handled outside of QEMU. Of course this case
is different although there is a vhost-user-sound coming as well in my
next series.
>
>
>> >
>> >
>> >> include/hw/virtio/virtio-snd.h | 79 ++++++++++++
>> >> 6 files changed, 323 insertions(+)
>> >> create mode 100644 hw/virtio/virtio-snd.c
>> >> create mode 100644 include/hw/virtio/virtio-snd.h
>> >>
>> >> diff --git a/MAINTAINERS b/MAINTAINERS
>> >> index 355b1960ce..81ca61e90b 100644
>> >> --- a/MAINTAINERS
>> >> +++ b/MAINTAINERS
>> >> @@ -2241,6 +2241,12 @@ 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>
>> >> diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig
>> >> index 92c9cf6c96..d6f20657b3 100644
>> >> --- a/hw/virtio/Kconfig
>> >> +++ b/hw/virtio/Kconfig
>> >> @@ -17,6 +17,11 @@ config VIRTIO_PCI
>> >> depends on PCI
>> >> select VIRTIO
>> >>
>> >> +config VIRTIO_SND
>> >> + bool
>> >> + default y
>> >> + depends on VIRTIO
>> >> +
>> >> config VIRTIO_MMIO
>> >> bool
>> >> select VIRTIO
>> >> diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build
>> >> index 13e7c6c272..120d4bfa0a 100644
>> >> --- a/hw/virtio/meson.build
>> >> +++ b/hw/virtio/meson.build
>> >> @@ -31,6 +31,7 @@ specific_virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock.c
>> >> specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_VSOCK', if_true: files('vhost-user-vsock.c'))
>> >> specific_virtio_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng.c'))
>> >> specific_virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c'))
>> >> +specific_virtio_ss.add(when: 'CONFIG_VIRTIO_SND', if_true: files('virtio-snd.c'))
>> >> specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_I2C', if_true: files('vhost-user-i2c.c'))
>> >> specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_RNG', if_true: files('vhost-user-rng.c'))
>> >> specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_GPIO', if_true: files('vhost-user-gpio.c'))
>> >> diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
>> >> index 7109cf1a3b..3ed7da35f2 100644
>> >> --- a/hw/virtio/trace-events
>> >> +++ b/hw/virtio/trace-events
>> >> @@ -154,3 +154,12 @@ virtio_pmem_flush_done(int type) "fsync return=%d"
>> >> virtio_gpio_start(void) "start"
>> >> virtio_gpio_stop(void) "stop"
>> >> virtio_gpio_set_status(uint8_t status) "0x%x"
>> >> +
>> >> +#virtio-snd.c
>> >> +virtio_snd_get_config(void *vdev, uint32_t jacks, uint32_t streams, uint32_t chmaps) "snd %p: get_config jacks=%"PRIu32" streams=%"PRIu32" chmaps=%"PRIu32""
>> >> +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 %"PRIu32"->%"PRIu32", streams from %"PRIu32"->%"PRIu32", chmaps from %"PRIu32"->%"PRIu32
>> >> +virtio_snd_get_features(void *vdev, uint64_t features) "snd %p: get_features 0x%"PRIx64
>> >> +virtio_snd_vm_state_running(void) "vm state running"
>> >> +virtio_snd_vm_state_stopped(void) "vm state stopped"
>> >> +virtio_snd_realize(void *snd) "snd %p: realize"
>> >> +virtio_snd_unrealize(void *snd) "snd %p: unrealize"
>> >> diff --git a/hw/virtio/virtio-snd.c b/hw/virtio/virtio-snd.c
>> >> new file mode 100644
>> >> index 0000000000..a8204e8a02
>> >> --- /dev/null
>> >> +++ b/hw/virtio/virtio-snd.c
>> >> @@ -0,0 +1,223 @@
>> >> +/*
>> >> + * VIRTIO Sound Device conforming to
>> >> + *
>> >> + * "Virtual I/O Device (VIRTIO) Version 1.2
>> >> + * Committee Specification Draft 01
>> >> + * 09 May 2022"
>> >> + *
>> >> + * <https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-52900014>
>> >> + *
>> >> + * Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
>> >> + * Copyright (C) 2019 OpenSynergy GmbH
>> >> + *
>> >> + * 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 "qemu/iov.h"
>> >> +#include "qemu/log.h"
>> >> +#include "qemu/error-report.h"
>> >> +#include "include/qemu/lockable.h"
>> >> +#include "sysemu/runstate.h"
>> >> +#include "trace.h"
>> >> +#include "qapi/error.h"
>> >> +#include "hw/virtio/virtio-snd.h"
>> >> +#include "hw/core/cpu.h"
>> >> +
>> >> +#define VIRTIO_SOUND_VM_VERSION 1
>> >> +#define VIRTIO_SOUND_JACK_DEFAULT 0
>> >> +#define VIRTIO_SOUND_STREAM_DEFAULT 1
>> >> +#define VIRTIO_SOUND_CHMAP_DEFAULT 0
>> >> +#define VIRTIO_SOUND_HDA_FN_NID 0
>> >> +
>> >> +static const VMStateDescription vmstate_virtio_snd_device = {
>> >> + .name = TYPE_VIRTIO_SND,
>> >> + .version_id = VIRTIO_SOUND_VM_VERSION,
>> >> + .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
>> >> +};
>> >> +
>> >> +static const VMStateDescription vmstate_virtio_snd = {
>> >> + .name = "virtio-sound",
>> >> + .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
>> >> + .version_id = VIRTIO_SOUND_VM_VERSION,
>> >> + .fields = (VMStateField[]) {
>> >> + VMSTATE_VIRTIO_DEVICE,
>> >> + VMSTATE_END_OF_LIST()
>> >> + },
>> >> +};
>> >> +
>> >> +static Property virtio_snd_properties[] = {
>> >> + DEFINE_AUDIO_PROPERTIES(VirtIOSound, card),
>> >> + DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks,
>> >> + VIRTIO_SOUND_JACK_DEFAULT),
>> >> + DEFINE_PROP_UINT32("streams", VirtIOSound, snd_conf.streams,
>> >> + VIRTIO_SOUND_STREAM_DEFAULT),
>> >> + DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps,
>> >> + VIRTIO_SOUND_CHMAP_DEFAULT),
>> >> + DEFINE_PROP_END_OF_LIST(),
>> >> +};
>> >> +
>> >> +static void
>> >> +virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config)
>> >> +{
>> >> + VirtIOSound *s = VIRTIO_SND(vdev);
>> >> + trace_virtio_snd_get_config(vdev,
>> >> + s->snd_conf.jacks,
>> >> + s->snd_conf.streams,
>> >> + s->snd_conf.chmaps);
>> >> +
>> >> + memcpy(config, &s->snd_conf, sizeof(s->snd_conf));
>> >> +}
>> >> +
>> >> +static void
>> >> +virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
>> >> +{
>> >> + VirtIOSound *s = VIRTIO_SND(vdev);
>> >> + const virtio_snd_config *sndconfig =
>> >> + (const virtio_snd_config *)config;
>> >> +
>> >> +
>> >> + trace_virtio_snd_set_config(vdev,
>> >> + s->snd_conf.jacks,
>> >> + sndconfig->jacks,
>> >> + s->snd_conf.streams,
>> >> + sndconfig->streams,
>> >> + s->snd_conf.chmaps,
>> >> + sndconfig->chmaps);
>> >> +
>> >> + memcpy(&s->snd_conf, sndconfig, sizeof(s->snd_conf));
>> >> +}
>> >> +
>> >> +/*
>> >> + * Queue handler stub.
>> >> + *
>> >> + * @vdev: VirtIOSound device
>> >> + * @vq: virtqueue
>> >> + */
>> >> +static void virtio_snd_handle_queue(VirtIODevice *vdev, VirtQueue *vq) {}
>> >> +
>> >> +static uint64_t get_features(VirtIODevice *vdev, uint64_t features,
>> >> + Error **errp)
>> >> +{
>> >> + /*
>> >> + * virtio-v1.2-csd01, 5.14.3,
>> >> + * Feature Bits
>> >> + * None currently defined.
>> >> + */
>> >> + VirtIOSound *s = VIRTIO_SND(vdev);
>> >> + features |= s->features;
>> >> +
>> >> + trace_virtio_snd_get_features(vdev, features);
>> >> +
>> >> + return features;
>> >> +}
>> >> +
>> >> +static void
>> >> +virtio_snd_vm_state_change(void *opaque, bool running,
>> >> + RunState state)
>> >> +{
>> >> + if (running) {
>> >> + trace_virtio_snd_vm_state_running();
>> >> + } else {
>> >> + trace_virtio_snd_vm_state_stopped();
>> >> + }
>> >> +}
>> >> +
>> >> +static void virtio_snd_realize(DeviceState *dev, Error **errp)
>> >> +{
>> >> + ERRP_GUARD();
>> >> + VirtIOSound *vsnd = VIRTIO_SND(dev);
>> >> + VirtIODevice *vdev = VIRTIO_DEVICE(dev);
>> >> +
>> >> + vsnd->vmstate =
>> >> + qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd);
>> >> +
>> >> + trace_virtio_snd_realize(vsnd);
>> >> +
>> >> + virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config));
>> >> + virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1);
>> >> +
>> >> + /* set number of jacks and streams */
>> >> + if (vsnd->snd_conf.jacks > 8) {
>> >> + error_setg(errp,
>> >> + "Invalid number of jacks: %"PRIu32,
>> >> + vsnd->snd_conf.jacks);
>> >> + return;
>> >> + }
>> >> + if (vsnd->snd_conf.streams < 1 || vsnd->snd_conf.streams > 10) {
>> >> + error_setg(errp,
>> >> + "Invalid number of streams: %"PRIu32,
>> >> + vsnd->snd_conf.streams);
>> >> + return;
>> >> + }
>> >> +
>> >> + if (vsnd->snd_conf.chmaps > VIRTIO_SND_CHMAP_MAX_SIZE) {
>> >> + error_setg(errp,
>> >> + "Invalid number of channel maps: %"PRIu32,
>> >> + vsnd->snd_conf.chmaps);
>> >> + return;
>> >> + }
>> >> +
>> >> + AUD_register_card("virtio-sound", &vsnd->card);
>> >> +
>> >> + vsnd->queues[VIRTIO_SND_VQ_CONTROL] =
>> >> + virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
>> >> + vsnd->queues[VIRTIO_SND_VQ_EVENT] =
>> >> + virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
>> >> + vsnd->queues[VIRTIO_SND_VQ_TX] =
>> >> + virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
>> >> + vsnd->queues[VIRTIO_SND_VQ_RX] =
>> >> + virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
>> >> +}
>> >> +
>> >> +static void virtio_snd_unrealize(DeviceState *dev)
>> >> +{
>> >> + VirtIODevice *vdev = VIRTIO_DEVICE(dev);
>> >> + VirtIOSound *vsnd = VIRTIO_SND(dev);
>> >> +
>> >> + qemu_del_vm_change_state_handler(vsnd->vmstate);
>> >> + trace_virtio_snd_unrealize(vsnd);
>> >> +
>> >> + AUD_remove_card(&vsnd->card);
>> >> + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]);
>> >> + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]);
>> >> + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]);
>> >> + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_RX]);
>> >> + virtio_cleanup(vdev);
>> >> +}
>> >> +
>> >> +
>> >> +static void virtio_snd_reset(VirtIODevice *vdev) {}
>> >> +
>> >> +static void virtio_snd_class_init(ObjectClass *klass, void *data)
>> >> +{
>> >> + DeviceClass *dc = DEVICE_CLASS(klass);
>> >> + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
>> >> +
>> >> +
>> >> + set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
>> >> + device_class_set_props(dc, virtio_snd_properties);
>> >> +
>> >> + dc->vmsd = &vmstate_virtio_snd;
>> >> + vdc->vmsd = &vmstate_virtio_snd_device;
>> >> + vdc->realize = virtio_snd_realize;
>> >> + vdc->unrealize = virtio_snd_unrealize;
>> >> + vdc->get_config = virtio_snd_get_config;
>> >> + vdc->set_config = virtio_snd_set_config;
>> >> + vdc->get_features = get_features;
>> >> + vdc->reset = virtio_snd_reset;
>> >> + vdc->legacy_features = 0;
>> >> +}
>> >> +
>> >> +static const TypeInfo virtio_snd_types[] = {
>> >> + {
>> >> + .name = TYPE_VIRTIO_SND,
>> >> + .parent = TYPE_VIRTIO_DEVICE,
>> >> + .instance_size = sizeof(VirtIOSound),
>> >> + .class_init = virtio_snd_class_init,
>> >> + }
>> >> +};
>> >> +
>> >> +DEFINE_TYPES(virtio_snd_types)
>> >> diff --git a/include/hw/virtio/virtio-snd.h b/include/hw/virtio/virtio-snd.h
>> >> new file mode 100644
>> >> index 0000000000..934e854a80
>> >> --- /dev/null
>> >> +++ b/include/hw/virtio/virtio-snd.h
>> >> @@ -0,0 +1,79 @@
>> >> +/*
>> >> + * VIRTIO Sound Device conforming to
>> >> + *
>> >> + * "Virtual I/O Device (VIRTIO) Version 1.2
>> >> + * Committee Specification Draft 01
>> >> + * 09 May 2022"
>> >> + *
>> >> + * Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
>> >> + * Copyright (C) 2019 OpenSynergy GmbH
>> >> + *
>> >> + * 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.
>> >> + */
>> >> +
>> >> +#ifndef QEMU_VIRTIO_SOUND_H
>> >> +#define QEMU_VIRTIO_SOUND_H
>> >> +
>> >> +#include "hw/virtio/virtio.h"
>> >> +#include "audio/audio.h"
>> >> +#include "standard-headers/linux/virtio_ids.h"
>> >> +#include "standard-headers/linux/virtio_snd.h"
>> >> +
>> >> +#define TYPE_VIRTIO_SND "virtio-sound"
>> >> +#define VIRTIO_SND(obj) \
>> >> + OBJECT_CHECK(VirtIOSound, (obj), TYPE_VIRTIO_SND)
>> >> +
>> >> +/* CONFIGURATION SPACE */
>> >> +
>> >> +typedef struct virtio_snd_config virtio_snd_config;
>> >> +
>> >> +/* COMMON DEFINITIONS */
>> >> +
>> >> +/* common header for request/response*/
>> >> +typedef struct virtio_snd_hdr virtio_snd_hdr;
>> >> +
>> >> +/* event notification */
>> >> +typedef struct virtio_snd_event virtio_snd_event;
>> >> +
>> >> +/* common control request to query an item information */
>> >> +typedef struct virtio_snd_query_info virtio_snd_query_info;
>> >> +
>> >> +/* JACK CONTROL MESSAGES */
>> >> +
>> >> +typedef struct virtio_snd_jack_hdr virtio_snd_jack_hdr;
>> >> +
>> >> +/* jack information structure */
>> >> +typedef struct virtio_snd_jack_info virtio_snd_jack_info;
>> >> +
>> >> +/* jack remapping control request */
>> >> +typedef struct virtio_snd_jack_remap virtio_snd_jack_remap;
>> >> +
>> >> +/*
>> >> + * PCM CONTROL MESSAGES
>> >> + */
>> >> +typedef struct virtio_snd_pcm_hdr virtio_snd_pcm_hdr;
>> >> +
>> >> +/* PCM stream info structure */
>> >> +typedef struct virtio_snd_pcm_info virtio_snd_pcm_info;
>> >> +
>> >> +/* set PCM stream params */
>> >> +typedef struct virtio_snd_pcm_set_params virtio_snd_pcm_set_params;
>> >> +
>> >> +/* I/O request header */
>> >> +typedef struct virtio_snd_pcm_xfer virtio_snd_pcm_xfer;
>> >> +
>> >> +/* I/O request status */
>> >> +typedef struct virtio_snd_pcm_status virtio_snd_pcm_status;
>> >> +
>> >> +typedef struct VirtIOSound {
>> >> + VirtIODevice parent_obj;
>> >> +
>> >> + VirtQueue *queues[VIRTIO_SND_VQ_MAX];
>> >> + uint64_t features;
>> >> + QEMUSoundCard card;
>> >> + VMChangeStateEntry *vmstate;
>> >> + virtio_snd_config snd_conf;
>> >> +} VirtIOSound;
>> >> +#endif
>> >> --
>> >> 2.39.2
>>
>>
>> --
>> Alex Bennée
>> Virtualisation Tech Lead @ Linaro
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v10 00/11] Add VIRTIO sound card
2023-10-03 14:10 ` [PATCH v10 00/11] Add VIRTIO sound card Michael S. Tsirkin
@ 2023-10-03 16:09 ` Manos Pitsidianakis
2023-10-03 17:10 ` Michael S. Tsirkin
0 siblings, 1 reply; 21+ messages in thread
From: Manos Pitsidianakis @ 2023-10-03 16:09 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: qemu-devel, Igor Skalkin, Anton Yakovlev, Paolo Bonzini,
Gerd Hoffmann, Marcel Apfelbaum, Daniel P. Berrangé ,
Eduardo Habkost, Marc-André Lureau, Volker Rü melin,
Kő vá gó , Zoltá n, Alex Benné e,
Philippe Mathieu-Daudé , Mark Cave-Ayland
On Tue, 03 Oct 2023 17:10, "Michael S. Tsirkin" <mst@redhat.com> wrote:
>I hope you are also fixing the linux driver to fix this in a robust
>way, yes?
Hello Michael,
You can find pertinent discussion on the virtio-comment list here:
https://lists.oasis-open.org/archives/virtio-comment/202309/threads.html#00175
Fixing it is on our radar but it may prove non-trivial if it requires
changing how the virtio-snd driver interacts with the kernel alsa API.
Meanwhile AFAIK the qemu device conforms to the virtio spec in this case
both with the previous version behavior and the current one. The change
was made to compensate for the linux driver's behavior, not because it
was previously incorrect.
Manos
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v10 00/11] Add VIRTIO sound card
2023-10-03 16:09 ` Manos Pitsidianakis
@ 2023-10-03 17:10 ` Michael S. Tsirkin
0 siblings, 0 replies; 21+ messages in thread
From: Michael S. Tsirkin @ 2023-10-03 17:10 UTC (permalink / raw)
To: Manos Pitsidianakis
Cc: qemu-devel, Igor Skalkin, Anton Yakovlev, Paolo Bonzini,
Gerd Hoffmann, Marcel Apfelbaum, Daniel P. Berrangé,
Eduardo Habkost, Marc-André Lureau, Volker Rü melin,
Kő vá gó , Zoltá n, Alex Benné e,
Philippe Mathieu-Daudé, Mark Cave-Ayland
On Tue, Oct 03, 2023 at 07:09:14PM +0300, Manos Pitsidianakis wrote:
> On Tue, 03 Oct 2023 17:10, "Michael S. Tsirkin" <mst@redhat.com> wrote:
> > I hope you are also fixing the linux driver to fix this in a robust
> > way, yes?
>
> Hello Michael,
>
> You can find pertinent discussion on the virtio-comment list here:
>
> https://lists.oasis-open.org/archives/virtio-comment/202309/threads.html#00175
>
> Fixing it is on our radar but it may prove non-trivial if it requires
> changing how the virtio-snd driver interacts with the kernel alsa API.
That is worrying. If spec can not be implemented spec has to change.
> Meanwhile AFAIK the qemu device conforms to the virtio spec in this case
> both with the previous version behavior and the current one. The change was
> made to compensate for the linux driver's behavior, not because it was
> previously incorrect.
>
> Manos
It's hardly robust though, right? I.e. I think it's broken if there's
bounce buffering.
--
MST
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2023-10-03 17:11 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-09-29 14:08 [PATCH v10 00/11] Add VIRTIO sound card Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 01/11] Add virtio-sound device stub Emmanouil Pitsidianakis
2023-10-03 14:13 ` Michael S. Tsirkin
2023-10-03 14:34 ` Alex Bennée
2023-10-03 14:45 ` Michael S. Tsirkin
2023-10-03 15:26 ` Alex Bennée
2023-09-29 14:08 ` [PATCH v10 02/11] Add virtio-sound-pci device Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 03/11] virtio-sound: handle control messages and streams Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 04/11] virtio-sound: handle VIRTIO_SND_R_PCM_INFO request Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 05/11] virtio-sound: handle VIRTIO_SND_R_PCM_{START,STOP} Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 06/11] virtio-sound: handle VIRTIO_SND_R_PCM_SET_PARAMS Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 07/11] virtio-sound: handle VIRTIO_SND_R_PCM_PREPARE Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 08/11] virtio-sound: handle VIRTIO_SND_R_PCM_RELEASE Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 09/11] virtio-sound: implement audio output (TX) Emmanouil Pitsidianakis
2023-09-29 18:59 ` Manos Pitsidianakis
2023-10-03 14:27 ` Philippe Mathieu-Daudé
2023-09-29 14:08 ` [PATCH v10 10/11] virtio-sound: implement audio capture (RX) Emmanouil Pitsidianakis
2023-09-29 14:08 ` [PATCH v10 11/11] docs/system: add basic virtio-snd documentation Emmanouil Pitsidianakis
2023-10-03 14:10 ` [PATCH v10 00/11] Add VIRTIO sound card Michael S. Tsirkin
2023-10-03 16:09 ` Manos Pitsidianakis
2023-10-03 17:10 ` Michael S. Tsirkin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).