xref: /openbmc/qemu/hw/audio/virtio-snd.c (revision a3fb4e93a3a7cf2be355c41cd550bef856f5ffe4)
12880e676SManos Pitsidianakis /*
22880e676SManos Pitsidianakis  * VIRTIO Sound Device conforming to
32880e676SManos Pitsidianakis  *
42880e676SManos Pitsidianakis  * "Virtual I/O Device (VIRTIO) Version 1.2
52880e676SManos Pitsidianakis  * Committee Specification Draft 01
62880e676SManos Pitsidianakis  * 09 May 2022"
72880e676SManos Pitsidianakis  *
82880e676SManos Pitsidianakis  * <https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-52900014>
92880e676SManos Pitsidianakis  *
102880e676SManos Pitsidianakis  * Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
112880e676SManos Pitsidianakis  * Copyright (C) 2019 OpenSynergy GmbH
122880e676SManos Pitsidianakis  *
132880e676SManos Pitsidianakis  * This work is licensed under the terms of the GNU GPL, version 2 or
142880e676SManos Pitsidianakis  * (at your option) any later version.  See the COPYING file in the
152880e676SManos Pitsidianakis  * top-level directory.
162880e676SManos Pitsidianakis  */
172880e676SManos Pitsidianakis 
182880e676SManos Pitsidianakis #include "qemu/osdep.h"
192880e676SManos Pitsidianakis #include "qemu/iov.h"
202880e676SManos Pitsidianakis #include "qemu/log.h"
212880e676SManos Pitsidianakis #include "qemu/error-report.h"
228b4d80bbSPhilippe Mathieu-Daudé #include "qemu/lockable.h"
232880e676SManos Pitsidianakis #include "sysemu/runstate.h"
242880e676SManos Pitsidianakis #include "trace.h"
252880e676SManos Pitsidianakis #include "qapi/error.h"
262880e676SManos Pitsidianakis #include "hw/audio/virtio-snd.h"
272880e676SManos Pitsidianakis 
282880e676SManos Pitsidianakis #define VIRTIO_SOUND_VM_VERSION 1
292880e676SManos Pitsidianakis #define VIRTIO_SOUND_JACK_DEFAULT 0
30d8d64acbSManos Pitsidianakis #define VIRTIO_SOUND_STREAM_DEFAULT 2
312880e676SManos Pitsidianakis #define VIRTIO_SOUND_CHMAP_DEFAULT 0
322880e676SManos Pitsidianakis #define VIRTIO_SOUND_HDA_FN_NID 0
332880e676SManos Pitsidianakis 
3418a75281SManos Pitsidianakis static void virtio_snd_pcm_out_cb(void *data, int available);
3518a75281SManos Pitsidianakis static void virtio_snd_process_cmdq(VirtIOSound *s);
3618a75281SManos Pitsidianakis static void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream);
37d8d64acbSManos Pitsidianakis static void virtio_snd_pcm_in_cb(void *data, int available);
38f7856181SManos Pitsidianakis static void virtio_snd_unrealize(DeviceState *dev);
3918a75281SManos Pitsidianakis 
40eb9ad377SManos Pitsidianakis static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8)
41eb9ad377SManos Pitsidianakis                                   | BIT(VIRTIO_SND_PCM_FMT_U8)
42eb9ad377SManos Pitsidianakis                                   | BIT(VIRTIO_SND_PCM_FMT_S16)
43eb9ad377SManos Pitsidianakis                                   | BIT(VIRTIO_SND_PCM_FMT_U16)
44eb9ad377SManos Pitsidianakis                                   | BIT(VIRTIO_SND_PCM_FMT_S32)
45eb9ad377SManos Pitsidianakis                                   | BIT(VIRTIO_SND_PCM_FMT_U32)
46eb9ad377SManos Pitsidianakis                                   | BIT(VIRTIO_SND_PCM_FMT_FLOAT);
47eb9ad377SManos Pitsidianakis 
48eb9ad377SManos Pitsidianakis static uint32_t supported_rates = BIT(VIRTIO_SND_PCM_RATE_5512)
49eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_8000)
50eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_11025)
51eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_16000)
52eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_22050)
53eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_32000)
54eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_44100)
55eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_48000)
56eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_64000)
57eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_88200)
58eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_96000)
59eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_176400)
60eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_192000)
61eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_384000);
62eb9ad377SManos Pitsidianakis 
632880e676SManos Pitsidianakis static const VMStateDescription vmstate_virtio_snd_device = {
642880e676SManos Pitsidianakis     .name = TYPE_VIRTIO_SND,
652880e676SManos Pitsidianakis     .version_id = VIRTIO_SOUND_VM_VERSION,
662880e676SManos Pitsidianakis     .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
672880e676SManos Pitsidianakis };
682880e676SManos Pitsidianakis 
692880e676SManos Pitsidianakis static const VMStateDescription vmstate_virtio_snd = {
702880e676SManos Pitsidianakis     .name = TYPE_VIRTIO_SND,
71551ef0faSVolker Rümelin     .unmigratable = 1,
722880e676SManos Pitsidianakis     .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
732880e676SManos Pitsidianakis     .version_id = VIRTIO_SOUND_VM_VERSION,
74856a6fe4SRichard Henderson     .fields = (const VMStateField[]) {
752880e676SManos Pitsidianakis         VMSTATE_VIRTIO_DEVICE,
762880e676SManos Pitsidianakis         VMSTATE_END_OF_LIST()
772880e676SManos Pitsidianakis     },
782880e676SManos Pitsidianakis };
792880e676SManos Pitsidianakis 
802880e676SManos Pitsidianakis static Property virtio_snd_properties[] = {
812880e676SManos Pitsidianakis     DEFINE_AUDIO_PROPERTIES(VirtIOSound, card),
822880e676SManos Pitsidianakis     DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks,
832880e676SManos Pitsidianakis                        VIRTIO_SOUND_JACK_DEFAULT),
842880e676SManos Pitsidianakis     DEFINE_PROP_UINT32("streams", VirtIOSound, snd_conf.streams,
852880e676SManos Pitsidianakis                        VIRTIO_SOUND_STREAM_DEFAULT),
862880e676SManos Pitsidianakis     DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps,
872880e676SManos Pitsidianakis                        VIRTIO_SOUND_CHMAP_DEFAULT),
882880e676SManos Pitsidianakis     DEFINE_PROP_END_OF_LIST(),
892880e676SManos Pitsidianakis };
902880e676SManos Pitsidianakis 
912880e676SManos Pitsidianakis static void
virtio_snd_get_config(VirtIODevice * vdev,uint8_t * config)922880e676SManos Pitsidianakis virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config)
932880e676SManos Pitsidianakis {
942880e676SManos Pitsidianakis     VirtIOSound *s = VIRTIO_SND(vdev);
952880e676SManos Pitsidianakis     virtio_snd_config *sndconfig =
962880e676SManos Pitsidianakis         (virtio_snd_config *)config;
972880e676SManos Pitsidianakis     trace_virtio_snd_get_config(vdev,
982880e676SManos Pitsidianakis                                 s->snd_conf.jacks,
992880e676SManos Pitsidianakis                                 s->snd_conf.streams,
1002880e676SManos Pitsidianakis                                 s->snd_conf.chmaps);
1012880e676SManos Pitsidianakis 
1022880e676SManos Pitsidianakis     memcpy(sndconfig, &s->snd_conf, sizeof(s->snd_conf));
1032880e676SManos Pitsidianakis     cpu_to_le32s(&sndconfig->jacks);
1042880e676SManos Pitsidianakis     cpu_to_le32s(&sndconfig->streams);
1052880e676SManos Pitsidianakis     cpu_to_le32s(&sndconfig->chmaps);
1062880e676SManos Pitsidianakis 
1072880e676SManos Pitsidianakis }
1082880e676SManos Pitsidianakis 
1092880e676SManos Pitsidianakis static void
virtio_snd_pcm_buffer_free(VirtIOSoundPCMBuffer * buffer)11018a75281SManos Pitsidianakis virtio_snd_pcm_buffer_free(VirtIOSoundPCMBuffer *buffer)
11118a75281SManos Pitsidianakis {
11218a75281SManos Pitsidianakis     g_free(buffer->elem);
11318a75281SManos Pitsidianakis     g_free(buffer);
11418a75281SManos Pitsidianakis }
11518a75281SManos Pitsidianakis 
11618a75281SManos Pitsidianakis static void
virtio_snd_ctrl_cmd_free(virtio_snd_ctrl_command * cmd)117eb9ad377SManos Pitsidianakis virtio_snd_ctrl_cmd_free(virtio_snd_ctrl_command *cmd)
118eb9ad377SManos Pitsidianakis {
119eb9ad377SManos Pitsidianakis     g_free(cmd->elem);
120eb9ad377SManos Pitsidianakis     g_free(cmd);
121eb9ad377SManos Pitsidianakis }
122eb9ad377SManos Pitsidianakis 
1232880e676SManos Pitsidianakis /*
124eb9ad377SManos Pitsidianakis  * Get a specific stream from the virtio sound card device.
125eb9ad377SManos Pitsidianakis  * Returns NULL if @stream_id is invalid or not allocated.
126eb9ad377SManos Pitsidianakis  *
127eb9ad377SManos Pitsidianakis  * @s: VirtIOSound device
128eb9ad377SManos Pitsidianakis  * @stream_id: stream id
129eb9ad377SManos Pitsidianakis  */
virtio_snd_pcm_get_stream(VirtIOSound * s,uint32_t stream_id)130eb9ad377SManos Pitsidianakis static VirtIOSoundPCMStream *virtio_snd_pcm_get_stream(VirtIOSound *s,
131eb9ad377SManos Pitsidianakis                                                        uint32_t stream_id)
132eb9ad377SManos Pitsidianakis {
133eb9ad377SManos Pitsidianakis     return stream_id >= s->snd_conf.streams ? NULL :
134eb9ad377SManos Pitsidianakis         s->pcm->streams[stream_id];
135eb9ad377SManos Pitsidianakis }
136eb9ad377SManos Pitsidianakis 
137eb9ad377SManos Pitsidianakis /*
138eb9ad377SManos Pitsidianakis  * Get params for a specific stream.
139eb9ad377SManos Pitsidianakis  *
140eb9ad377SManos Pitsidianakis  * @s: VirtIOSound device
141eb9ad377SManos Pitsidianakis  * @stream_id: stream id
142eb9ad377SManos Pitsidianakis  */
virtio_snd_pcm_get_params(VirtIOSound * s,uint32_t stream_id)143eb9ad377SManos Pitsidianakis static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s,
144eb9ad377SManos Pitsidianakis                                                             uint32_t stream_id)
145eb9ad377SManos Pitsidianakis {
146eb9ad377SManos Pitsidianakis     return stream_id >= s->snd_conf.streams ? NULL
147eb9ad377SManos Pitsidianakis         : &s->pcm->pcm_params[stream_id];
148eb9ad377SManos Pitsidianakis }
149eb9ad377SManos Pitsidianakis 
150eb9ad377SManos Pitsidianakis /*
1510ff05dd2SManos Pitsidianakis  * Handle the VIRTIO_SND_R_PCM_INFO request.
1520ff05dd2SManos Pitsidianakis  * The function writes the info structs to the request element.
1530ff05dd2SManos Pitsidianakis  *
1540ff05dd2SManos Pitsidianakis  * @s: VirtIOSound device
1550ff05dd2SManos Pitsidianakis  * @cmd: The request command queue element from VirtIOSound cmdq field
1560ff05dd2SManos Pitsidianakis  */
virtio_snd_handle_pcm_info(VirtIOSound * s,virtio_snd_ctrl_command * cmd)1570ff05dd2SManos Pitsidianakis static void virtio_snd_handle_pcm_info(VirtIOSound *s,
1580ff05dd2SManos Pitsidianakis                                        virtio_snd_ctrl_command *cmd)
1590ff05dd2SManos Pitsidianakis {
1600ff05dd2SManos Pitsidianakis     uint32_t stream_id, start_id, count, size;
1610ff05dd2SManos Pitsidianakis     virtio_snd_pcm_info val;
1620ff05dd2SManos Pitsidianakis     virtio_snd_query_info req;
1630ff05dd2SManos Pitsidianakis     VirtIOSoundPCMStream *stream = NULL;
1640ff05dd2SManos Pitsidianakis     g_autofree virtio_snd_pcm_info *pcm_info = NULL;
1650ff05dd2SManos Pitsidianakis     size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
1660ff05dd2SManos Pitsidianakis                                cmd->elem->out_num,
1670ff05dd2SManos Pitsidianakis                                0,
1680ff05dd2SManos Pitsidianakis                                &req,
1690ff05dd2SManos Pitsidianakis                                sizeof(virtio_snd_query_info));
1700ff05dd2SManos Pitsidianakis 
1710ff05dd2SManos Pitsidianakis     if (msg_sz != sizeof(virtio_snd_query_info)) {
1720ff05dd2SManos Pitsidianakis         /*
1730ff05dd2SManos Pitsidianakis          * TODO: do we need to set DEVICE_NEEDS_RESET?
1740ff05dd2SManos Pitsidianakis          */
1750ff05dd2SManos Pitsidianakis         qemu_log_mask(LOG_GUEST_ERROR,
1760ff05dd2SManos Pitsidianakis                 "%s: virtio-snd command size incorrect %zu vs \
1770ff05dd2SManos Pitsidianakis                 %zu\n", __func__, msg_sz, sizeof(virtio_snd_query_info));
1780ff05dd2SManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
1790ff05dd2SManos Pitsidianakis         return;
1800ff05dd2SManos Pitsidianakis     }
1810ff05dd2SManos Pitsidianakis 
1820ff05dd2SManos Pitsidianakis     start_id = le32_to_cpu(req.start_id);
1830ff05dd2SManos Pitsidianakis     count = le32_to_cpu(req.count);
1840ff05dd2SManos Pitsidianakis     size = le32_to_cpu(req.size);
1850ff05dd2SManos Pitsidianakis 
1860ff05dd2SManos Pitsidianakis     if (iov_size(cmd->elem->in_sg, cmd->elem->in_num) <
1870ff05dd2SManos Pitsidianakis         sizeof(virtio_snd_hdr) + size * count) {
1880ff05dd2SManos Pitsidianakis         /*
1890ff05dd2SManos Pitsidianakis          * TODO: do we need to set DEVICE_NEEDS_RESET?
1900ff05dd2SManos Pitsidianakis          */
1910ff05dd2SManos Pitsidianakis         error_report("pcm info: buffer too small, got: %zu, needed: %zu",
1920ff05dd2SManos Pitsidianakis                 iov_size(cmd->elem->in_sg, cmd->elem->in_num),
1930ff05dd2SManos Pitsidianakis                 sizeof(virtio_snd_pcm_info));
1940ff05dd2SManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
1950ff05dd2SManos Pitsidianakis         return;
1960ff05dd2SManos Pitsidianakis     }
1970ff05dd2SManos Pitsidianakis 
1980ff05dd2SManos Pitsidianakis     pcm_info = g_new0(virtio_snd_pcm_info, count);
1990ff05dd2SManos Pitsidianakis     for (uint32_t i = 0; i < count; i++) {
2000ff05dd2SManos Pitsidianakis         stream_id = i + start_id;
2010ff05dd2SManos Pitsidianakis         trace_virtio_snd_handle_pcm_info(stream_id);
2020ff05dd2SManos Pitsidianakis         stream = virtio_snd_pcm_get_stream(s, stream_id);
2030ff05dd2SManos Pitsidianakis         if (!stream) {
2040ff05dd2SManos Pitsidianakis             error_report("Invalid stream id: %"PRIu32, stream_id);
2050ff05dd2SManos Pitsidianakis             cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
2060ff05dd2SManos Pitsidianakis             return;
2070ff05dd2SManos Pitsidianakis         }
2080ff05dd2SManos Pitsidianakis         val = stream->info;
2090ff05dd2SManos Pitsidianakis         val.hdr.hda_fn_nid = cpu_to_le32(val.hdr.hda_fn_nid);
2100ff05dd2SManos Pitsidianakis         val.features = cpu_to_le32(val.features);
2110ff05dd2SManos Pitsidianakis         val.formats = cpu_to_le64(val.formats);
2120ff05dd2SManos Pitsidianakis         val.rates = cpu_to_le64(val.rates);
2130ff05dd2SManos Pitsidianakis         /*
2140ff05dd2SManos Pitsidianakis          * 5.14.6.6.2.1 Device Requirements: Stream Information The device MUST
2150ff05dd2SManos Pitsidianakis          * NOT set undefined feature, format, rate and direction values. The
2160ff05dd2SManos Pitsidianakis          * device MUST initialize the padding bytes to 0.
2170ff05dd2SManos Pitsidianakis          */
2180ff05dd2SManos Pitsidianakis         pcm_info[i] = val;
2190ff05dd2SManos Pitsidianakis         memset(&pcm_info[i].padding, 0, 5);
2200ff05dd2SManos Pitsidianakis     }
2210ff05dd2SManos Pitsidianakis 
222633487dfSVolker Rümelin     cmd->payload_size = sizeof(virtio_snd_pcm_info) * count;
2230ff05dd2SManos Pitsidianakis     cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
2240ff05dd2SManos Pitsidianakis     iov_from_buf(cmd->elem->in_sg,
2250ff05dd2SManos Pitsidianakis                  cmd->elem->in_num,
2260ff05dd2SManos Pitsidianakis                  sizeof(virtio_snd_hdr),
2270ff05dd2SManos Pitsidianakis                  pcm_info,
228633487dfSVolker Rümelin                  cmd->payload_size);
2290ff05dd2SManos Pitsidianakis }
2300ff05dd2SManos Pitsidianakis 
2310ff05dd2SManos Pitsidianakis /*
232eb9ad377SManos Pitsidianakis  * Set the given stream params.
233eb9ad377SManos Pitsidianakis  * Called by both virtio_snd_handle_pcm_set_params and during device
234eb9ad377SManos Pitsidianakis  * initialization.
235eb9ad377SManos Pitsidianakis  * Returns the response status code. (VIRTIO_SND_S_*).
236eb9ad377SManos Pitsidianakis  *
237eb9ad377SManos Pitsidianakis  * @s: VirtIOSound device
238eb9ad377SManos Pitsidianakis  * @params: The PCM params as defined in the virtio specification
239eb9ad377SManos Pitsidianakis  */
240eb9ad377SManos Pitsidianakis static
virtio_snd_set_pcm_params(VirtIOSound * s,uint32_t stream_id,virtio_snd_pcm_set_params * params)241eb9ad377SManos Pitsidianakis uint32_t virtio_snd_set_pcm_params(VirtIOSound *s,
242eb9ad377SManos Pitsidianakis                                    uint32_t stream_id,
243eb9ad377SManos Pitsidianakis                                    virtio_snd_pcm_set_params *params)
244eb9ad377SManos Pitsidianakis {
245eb9ad377SManos Pitsidianakis     virtio_snd_pcm_set_params *st_params;
246eb9ad377SManos Pitsidianakis 
247eb9ad377SManos Pitsidianakis     if (stream_id >= s->snd_conf.streams || s->pcm->pcm_params == NULL) {
248eb9ad377SManos Pitsidianakis         /*
249eb9ad377SManos Pitsidianakis          * TODO: do we need to set DEVICE_NEEDS_RESET?
250eb9ad377SManos Pitsidianakis          */
251eb9ad377SManos Pitsidianakis         virtio_error(VIRTIO_DEVICE(s), "Streams have not been initialized.\n");
252eb9ad377SManos Pitsidianakis         return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
253eb9ad377SManos Pitsidianakis     }
254eb9ad377SManos Pitsidianakis 
255eb9ad377SManos Pitsidianakis     st_params = virtio_snd_pcm_get_params(s, stream_id);
256eb9ad377SManos Pitsidianakis 
257eb9ad377SManos Pitsidianakis     if (params->channels < 1 || params->channels > AUDIO_MAX_CHANNELS) {
258eb9ad377SManos Pitsidianakis         error_report("Number of channels is not supported.");
259eb9ad377SManos Pitsidianakis         return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
260eb9ad377SManos Pitsidianakis     }
261*7d14471aSVolker Rümelin     if (params->format >= sizeof(supported_formats) * BITS_PER_BYTE ||
2629b608346SManos Pitsidianakis         !(supported_formats & BIT(params->format))) {
263eb9ad377SManos Pitsidianakis         error_report("Stream format is not supported.");
264eb9ad377SManos Pitsidianakis         return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
265eb9ad377SManos Pitsidianakis     }
266*7d14471aSVolker Rümelin     if (params->rate >= sizeof(supported_rates) * BITS_PER_BYTE ||
2679b608346SManos Pitsidianakis         !(supported_rates & BIT(params->rate))) {
268eb9ad377SManos Pitsidianakis         error_report("Stream rate is not supported.");
269eb9ad377SManos Pitsidianakis         return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
270eb9ad377SManos Pitsidianakis     }
271eb9ad377SManos Pitsidianakis 
272eb9ad377SManos Pitsidianakis     st_params->buffer_bytes = le32_to_cpu(params->buffer_bytes);
273eb9ad377SManos Pitsidianakis     st_params->period_bytes = le32_to_cpu(params->period_bytes);
274eb9ad377SManos Pitsidianakis     st_params->features = le32_to_cpu(params->features);
275eb9ad377SManos Pitsidianakis     /* the following are uint8_t, so there's no need to bswap the values. */
276eb9ad377SManos Pitsidianakis     st_params->channels = params->channels;
277eb9ad377SManos Pitsidianakis     st_params->format = params->format;
278eb9ad377SManos Pitsidianakis     st_params->rate = params->rate;
279eb9ad377SManos Pitsidianakis 
280eb9ad377SManos Pitsidianakis     return cpu_to_le32(VIRTIO_SND_S_OK);
281eb9ad377SManos Pitsidianakis }
282eb9ad377SManos Pitsidianakis 
283eb9ad377SManos Pitsidianakis /*
28464704ce0SManos Pitsidianakis  * Handles the VIRTIO_SND_R_PCM_SET_PARAMS request.
28564704ce0SManos Pitsidianakis  *
28664704ce0SManos Pitsidianakis  * @s: VirtIOSound device
28764704ce0SManos Pitsidianakis  * @cmd: The request command queue element from VirtIOSound cmdq field
28864704ce0SManos Pitsidianakis  */
virtio_snd_handle_pcm_set_params(VirtIOSound * s,virtio_snd_ctrl_command * cmd)28964704ce0SManos Pitsidianakis static void virtio_snd_handle_pcm_set_params(VirtIOSound *s,
29064704ce0SManos Pitsidianakis                                              virtio_snd_ctrl_command *cmd)
29164704ce0SManos Pitsidianakis {
29264704ce0SManos Pitsidianakis     virtio_snd_pcm_set_params req = { 0 };
29364704ce0SManos Pitsidianakis     uint32_t stream_id;
29464704ce0SManos Pitsidianakis     size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
29564704ce0SManos Pitsidianakis                                cmd->elem->out_num,
29664704ce0SManos Pitsidianakis                                0,
29764704ce0SManos Pitsidianakis                                &req,
29864704ce0SManos Pitsidianakis                                sizeof(virtio_snd_pcm_set_params));
29964704ce0SManos Pitsidianakis 
30064704ce0SManos Pitsidianakis     if (msg_sz != sizeof(virtio_snd_pcm_set_params)) {
30164704ce0SManos Pitsidianakis         /*
30264704ce0SManos Pitsidianakis          * TODO: do we need to set DEVICE_NEEDS_RESET?
30364704ce0SManos Pitsidianakis          */
30464704ce0SManos Pitsidianakis         qemu_log_mask(LOG_GUEST_ERROR,
30564704ce0SManos Pitsidianakis                 "%s: virtio-snd command size incorrect %zu vs \
30664704ce0SManos Pitsidianakis                 %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_set_params));
30764704ce0SManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
30864704ce0SManos Pitsidianakis         return;
30964704ce0SManos Pitsidianakis     }
31064704ce0SManos Pitsidianakis     stream_id = le32_to_cpu(req.hdr.stream_id);
31164704ce0SManos Pitsidianakis     trace_virtio_snd_handle_pcm_set_params(stream_id);
31264704ce0SManos Pitsidianakis     cmd->resp.code = virtio_snd_set_pcm_params(s, stream_id, &req);
31364704ce0SManos Pitsidianakis }
31464704ce0SManos Pitsidianakis 
31564704ce0SManos Pitsidianakis /*
316eb9ad377SManos Pitsidianakis  * Get a QEMU Audiosystem compatible format value from a VIRTIO_SND_PCM_FMT_*
317eb9ad377SManos Pitsidianakis  */
virtio_snd_get_qemu_format(uint32_t format)318eb9ad377SManos Pitsidianakis static AudioFormat virtio_snd_get_qemu_format(uint32_t format)
319eb9ad377SManos Pitsidianakis {
320eb9ad377SManos Pitsidianakis     #define CASE(FMT)               \
321eb9ad377SManos Pitsidianakis     case VIRTIO_SND_PCM_FMT_##FMT:  \
322eb9ad377SManos Pitsidianakis         return AUDIO_FORMAT_##FMT;
323eb9ad377SManos Pitsidianakis 
324eb9ad377SManos Pitsidianakis     switch (format) {
325eb9ad377SManos Pitsidianakis     CASE(U8)
326eb9ad377SManos Pitsidianakis     CASE(S8)
327eb9ad377SManos Pitsidianakis     CASE(U16)
328eb9ad377SManos Pitsidianakis     CASE(S16)
329eb9ad377SManos Pitsidianakis     CASE(U32)
330eb9ad377SManos Pitsidianakis     CASE(S32)
331eb9ad377SManos Pitsidianakis     case VIRTIO_SND_PCM_FMT_FLOAT:
332eb9ad377SManos Pitsidianakis         return AUDIO_FORMAT_F32;
333eb9ad377SManos Pitsidianakis     default:
334eb9ad377SManos Pitsidianakis         g_assert_not_reached();
335eb9ad377SManos Pitsidianakis     }
336eb9ad377SManos Pitsidianakis 
337eb9ad377SManos Pitsidianakis     #undef CASE
338eb9ad377SManos Pitsidianakis }
339eb9ad377SManos Pitsidianakis 
340eb9ad377SManos Pitsidianakis /*
341eb9ad377SManos Pitsidianakis  * Get a QEMU Audiosystem compatible frequency value from a
342eb9ad377SManos Pitsidianakis  * VIRTIO_SND_PCM_RATE_*
343eb9ad377SManos Pitsidianakis  */
virtio_snd_get_qemu_freq(uint32_t rate)344eb9ad377SManos Pitsidianakis static uint32_t virtio_snd_get_qemu_freq(uint32_t rate)
345eb9ad377SManos Pitsidianakis {
346eb9ad377SManos Pitsidianakis     #define CASE(RATE)               \
347eb9ad377SManos Pitsidianakis     case VIRTIO_SND_PCM_RATE_##RATE: \
348eb9ad377SManos Pitsidianakis         return RATE;
349eb9ad377SManos Pitsidianakis 
350eb9ad377SManos Pitsidianakis     switch (rate) {
351eb9ad377SManos Pitsidianakis     CASE(5512)
352eb9ad377SManos Pitsidianakis     CASE(8000)
353eb9ad377SManos Pitsidianakis     CASE(11025)
354eb9ad377SManos Pitsidianakis     CASE(16000)
355eb9ad377SManos Pitsidianakis     CASE(22050)
356eb9ad377SManos Pitsidianakis     CASE(32000)
357eb9ad377SManos Pitsidianakis     CASE(44100)
358eb9ad377SManos Pitsidianakis     CASE(48000)
359eb9ad377SManos Pitsidianakis     CASE(64000)
360eb9ad377SManos Pitsidianakis     CASE(88200)
361eb9ad377SManos Pitsidianakis     CASE(96000)
362eb9ad377SManos Pitsidianakis     CASE(176400)
363eb9ad377SManos Pitsidianakis     CASE(192000)
364eb9ad377SManos Pitsidianakis     CASE(384000)
365eb9ad377SManos Pitsidianakis     default:
366eb9ad377SManos Pitsidianakis         g_assert_not_reached();
367eb9ad377SManos Pitsidianakis     }
368eb9ad377SManos Pitsidianakis 
369eb9ad377SManos Pitsidianakis     #undef CASE
370eb9ad377SManos Pitsidianakis }
371eb9ad377SManos Pitsidianakis 
372eb9ad377SManos Pitsidianakis /*
373eb9ad377SManos Pitsidianakis  * Get QEMU Audiosystem compatible audsettings from virtio based pcm stream
374eb9ad377SManos Pitsidianakis  * params.
375eb9ad377SManos Pitsidianakis  */
virtio_snd_get_qemu_audsettings(audsettings * as,virtio_snd_pcm_set_params * params)376eb9ad377SManos Pitsidianakis static void virtio_snd_get_qemu_audsettings(audsettings *as,
377eb9ad377SManos Pitsidianakis                                             virtio_snd_pcm_set_params *params)
378eb9ad377SManos Pitsidianakis {
379eb9ad377SManos Pitsidianakis     as->nchannels = MIN(AUDIO_MAX_CHANNELS, params->channels);
380eb9ad377SManos Pitsidianakis     as->fmt = virtio_snd_get_qemu_format(params->format);
381eb9ad377SManos Pitsidianakis     as->freq = virtio_snd_get_qemu_freq(params->rate);
382a276ec8eSPhilippe Mathieu-Daudé     as->endianness = 0; /* Conforming to VIRTIO 1.0: always little endian. */
383eb9ad377SManos Pitsidianakis }
384eb9ad377SManos Pitsidianakis 
385eb9ad377SManos Pitsidianakis /*
386eb9ad377SManos Pitsidianakis  * Close a stream and free all its resources.
387eb9ad377SManos Pitsidianakis  *
388eb9ad377SManos Pitsidianakis  * @stream: VirtIOSoundPCMStream *stream
389eb9ad377SManos Pitsidianakis  */
virtio_snd_pcm_close(VirtIOSoundPCMStream * stream)390eb9ad377SManos Pitsidianakis static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream)
391eb9ad377SManos Pitsidianakis {
39218a75281SManos Pitsidianakis     if (stream) {
39318a75281SManos Pitsidianakis         virtio_snd_pcm_flush(stream);
394d8d64acbSManos Pitsidianakis         if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
39518a75281SManos Pitsidianakis             AUD_close_out(&stream->pcm->snd->card, stream->voice.out);
39618a75281SManos Pitsidianakis             stream->voice.out = NULL;
397d8d64acbSManos Pitsidianakis         } else if (stream->info.direction == VIRTIO_SND_D_INPUT) {
398d8d64acbSManos Pitsidianakis             AUD_close_in(&stream->pcm->snd->card, stream->voice.in);
399d8d64acbSManos Pitsidianakis             stream->voice.in = NULL;
40018a75281SManos Pitsidianakis         }
40118a75281SManos Pitsidianakis     }
402eb9ad377SManos Pitsidianakis }
403eb9ad377SManos Pitsidianakis 
404eb9ad377SManos Pitsidianakis /*
405eb9ad377SManos Pitsidianakis  * Prepares a VirtIOSound card stream.
406eb9ad377SManos Pitsidianakis  * Returns the response status code. (VIRTIO_SND_S_*).
407eb9ad377SManos Pitsidianakis  *
408eb9ad377SManos Pitsidianakis  * @s: VirtIOSound device
409eb9ad377SManos Pitsidianakis  * @stream_id: stream id
410eb9ad377SManos Pitsidianakis  */
virtio_snd_pcm_prepare(VirtIOSound * s,uint32_t stream_id)411eb9ad377SManos Pitsidianakis static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
412eb9ad377SManos Pitsidianakis {
413eb9ad377SManos Pitsidianakis     audsettings as;
414eb9ad377SManos Pitsidianakis     virtio_snd_pcm_set_params *params;
415eb9ad377SManos Pitsidianakis     VirtIOSoundPCMStream *stream;
416eb9ad377SManos Pitsidianakis 
417eb9ad377SManos Pitsidianakis     if (s->pcm->streams == NULL ||
418eb9ad377SManos Pitsidianakis         s->pcm->pcm_params == NULL ||
419eb9ad377SManos Pitsidianakis         stream_id >= s->snd_conf.streams) {
420eb9ad377SManos Pitsidianakis         return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
421eb9ad377SManos Pitsidianakis     }
422eb9ad377SManos Pitsidianakis 
423eb9ad377SManos Pitsidianakis     params = virtio_snd_pcm_get_params(s, stream_id);
424eb9ad377SManos Pitsidianakis     if (params == NULL) {
425eb9ad377SManos Pitsidianakis         return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
426eb9ad377SManos Pitsidianakis     }
427eb9ad377SManos Pitsidianakis 
428eb9ad377SManos Pitsidianakis     stream = virtio_snd_pcm_get_stream(s, stream_id);
429eb9ad377SManos Pitsidianakis     if (stream == NULL) {
430eb9ad377SManos Pitsidianakis         stream = g_new0(VirtIOSoundPCMStream, 1);
431eb9ad377SManos Pitsidianakis         stream->active = false;
432eb9ad377SManos Pitsidianakis         stream->id = stream_id;
433eb9ad377SManos Pitsidianakis         stream->pcm = s->pcm;
434eb9ad377SManos Pitsidianakis         stream->s = s;
43518a75281SManos Pitsidianakis         qemu_mutex_init(&stream->queue_mutex);
43618a75281SManos Pitsidianakis         QSIMPLEQ_INIT(&stream->queue);
437eb9ad377SManos Pitsidianakis 
438eb9ad377SManos Pitsidianakis         /*
439eb9ad377SManos Pitsidianakis          * stream_id >= s->snd_conf.streams was checked before so this is
440eb9ad377SManos Pitsidianakis          * in-bounds
441eb9ad377SManos Pitsidianakis          */
442eb9ad377SManos Pitsidianakis         s->pcm->streams[stream_id] = stream;
443eb9ad377SManos Pitsidianakis     }
444eb9ad377SManos Pitsidianakis 
445eb9ad377SManos Pitsidianakis     virtio_snd_get_qemu_audsettings(&as, params);
446eb9ad377SManos Pitsidianakis     stream->info.direction = stream_id < s->snd_conf.streams / 2 +
447eb9ad377SManos Pitsidianakis         (s->snd_conf.streams & 1) ? VIRTIO_SND_D_OUTPUT : VIRTIO_SND_D_INPUT;
448eb9ad377SManos Pitsidianakis     stream->info.hdr.hda_fn_nid = VIRTIO_SOUND_HDA_FN_NID;
449eb9ad377SManos Pitsidianakis     stream->info.features = 0;
450eb9ad377SManos Pitsidianakis     stream->info.channels_min = 1;
451eb9ad377SManos Pitsidianakis     stream->info.channels_max = as.nchannels;
452eb9ad377SManos Pitsidianakis     stream->info.formats = supported_formats;
453eb9ad377SManos Pitsidianakis     stream->info.rates = supported_rates;
454eb9ad377SManos Pitsidianakis     stream->params = *params;
455eb9ad377SManos Pitsidianakis 
456eb9ad377SManos Pitsidianakis     stream->positions[0] = VIRTIO_SND_CHMAP_FL;
457eb9ad377SManos Pitsidianakis     stream->positions[1] = VIRTIO_SND_CHMAP_FR;
458eb9ad377SManos Pitsidianakis     stream->as = as;
459eb9ad377SManos Pitsidianakis 
46018a75281SManos Pitsidianakis     if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
46118a75281SManos Pitsidianakis         stream->voice.out = AUD_open_out(&s->card,
46218a75281SManos Pitsidianakis                                          stream->voice.out,
46318a75281SManos Pitsidianakis                                          "virtio-sound.out",
46418a75281SManos Pitsidianakis                                          stream,
46518a75281SManos Pitsidianakis                                          virtio_snd_pcm_out_cb,
46618a75281SManos Pitsidianakis                                          &as);
46718a75281SManos Pitsidianakis         AUD_set_volume_out(stream->voice.out, 0, 255, 255);
46818a75281SManos Pitsidianakis     } else {
469d8d64acbSManos Pitsidianakis         stream->voice.in = AUD_open_in(&s->card,
470d8d64acbSManos Pitsidianakis                                         stream->voice.in,
471d8d64acbSManos Pitsidianakis                                         "virtio-sound.in",
472d8d64acbSManos Pitsidianakis                                         stream,
473d8d64acbSManos Pitsidianakis                                         virtio_snd_pcm_in_cb,
474d8d64acbSManos Pitsidianakis                                         &as);
475d8d64acbSManos Pitsidianakis         AUD_set_volume_in(stream->voice.in, 0, 255, 255);
47618a75281SManos Pitsidianakis     }
47718a75281SManos Pitsidianakis 
478eb9ad377SManos Pitsidianakis     return cpu_to_le32(VIRTIO_SND_S_OK);
479eb9ad377SManos Pitsidianakis }
480eb9ad377SManos Pitsidianakis 
print_code(uint32_t code)481eb9ad377SManos Pitsidianakis static const char *print_code(uint32_t code)
482eb9ad377SManos Pitsidianakis {
483eb9ad377SManos Pitsidianakis     #define CASE(CODE)            \
484eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_##CODE:     \
485eb9ad377SManos Pitsidianakis         return "VIRTIO_SND_R_"#CODE
486eb9ad377SManos Pitsidianakis 
487eb9ad377SManos Pitsidianakis     switch (code) {
488eb9ad377SManos Pitsidianakis     CASE(JACK_INFO);
489eb9ad377SManos Pitsidianakis     CASE(JACK_REMAP);
490eb9ad377SManos Pitsidianakis     CASE(PCM_INFO);
491eb9ad377SManos Pitsidianakis     CASE(PCM_SET_PARAMS);
492eb9ad377SManos Pitsidianakis     CASE(PCM_PREPARE);
493eb9ad377SManos Pitsidianakis     CASE(PCM_RELEASE);
494eb9ad377SManos Pitsidianakis     CASE(PCM_START);
495eb9ad377SManos Pitsidianakis     CASE(PCM_STOP);
496eb9ad377SManos Pitsidianakis     CASE(CHMAP_INFO);
497eb9ad377SManos Pitsidianakis     default:
498eb9ad377SManos Pitsidianakis         return "invalid code";
499eb9ad377SManos Pitsidianakis     }
500eb9ad377SManos Pitsidianakis 
501eb9ad377SManos Pitsidianakis     #undef CASE
502eb9ad377SManos Pitsidianakis };
503eb9ad377SManos Pitsidianakis 
504eb9ad377SManos Pitsidianakis /*
505e5788b8fSManos Pitsidianakis  * Handles VIRTIO_SND_R_PCM_PREPARE.
506e5788b8fSManos Pitsidianakis  *
507e5788b8fSManos Pitsidianakis  * @s: VirtIOSound device
508e5788b8fSManos Pitsidianakis  * @cmd: The request command queue element from VirtIOSound cmdq field
509e5788b8fSManos Pitsidianakis  */
virtio_snd_handle_pcm_prepare(VirtIOSound * s,virtio_snd_ctrl_command * cmd)510e5788b8fSManos Pitsidianakis static void virtio_snd_handle_pcm_prepare(VirtIOSound *s,
511e5788b8fSManos Pitsidianakis                                           virtio_snd_ctrl_command *cmd)
512e5788b8fSManos Pitsidianakis {
513e5788b8fSManos Pitsidianakis     uint32_t stream_id;
514e5788b8fSManos Pitsidianakis     size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
515e5788b8fSManos Pitsidianakis                                cmd->elem->out_num,
516e5788b8fSManos Pitsidianakis                                sizeof(virtio_snd_hdr),
517e5788b8fSManos Pitsidianakis                                &stream_id,
518e5788b8fSManos Pitsidianakis                                sizeof(stream_id));
519e5788b8fSManos Pitsidianakis 
520e5788b8fSManos Pitsidianakis     stream_id = le32_to_cpu(stream_id);
521e5788b8fSManos Pitsidianakis     cmd->resp.code = msg_sz == sizeof(stream_id)
522e5788b8fSManos Pitsidianakis                    ? virtio_snd_pcm_prepare(s, stream_id)
523e5788b8fSManos Pitsidianakis                    : cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
524e5788b8fSManos Pitsidianakis }
525e5788b8fSManos Pitsidianakis 
526e5788b8fSManos Pitsidianakis /*
527fa131d4aSManos Pitsidianakis  * Handles VIRTIO_SND_R_PCM_START.
528fa131d4aSManos Pitsidianakis  *
529fa131d4aSManos Pitsidianakis  * @s: VirtIOSound device
530fa131d4aSManos Pitsidianakis  * @cmd: The request command queue element from VirtIOSound cmdq field
531fa131d4aSManos Pitsidianakis  * @start: whether to start or stop the device
532fa131d4aSManos Pitsidianakis  */
virtio_snd_handle_pcm_start_stop(VirtIOSound * s,virtio_snd_ctrl_command * cmd,bool start)533fa131d4aSManos Pitsidianakis static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
534fa131d4aSManos Pitsidianakis                                              virtio_snd_ctrl_command *cmd,
535fa131d4aSManos Pitsidianakis                                              bool start)
536fa131d4aSManos Pitsidianakis {
537fa131d4aSManos Pitsidianakis     VirtIOSoundPCMStream *stream;
538fa131d4aSManos Pitsidianakis     virtio_snd_pcm_hdr req;
539fa131d4aSManos Pitsidianakis     uint32_t stream_id;
540fa131d4aSManos Pitsidianakis     size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
541fa131d4aSManos Pitsidianakis                                cmd->elem->out_num,
542fa131d4aSManos Pitsidianakis                                0,
543fa131d4aSManos Pitsidianakis                                &req,
544fa131d4aSManos Pitsidianakis                                sizeof(virtio_snd_pcm_hdr));
545fa131d4aSManos Pitsidianakis 
546fa131d4aSManos Pitsidianakis     if (msg_sz != sizeof(virtio_snd_pcm_hdr)) {
547fa131d4aSManos Pitsidianakis         qemu_log_mask(LOG_GUEST_ERROR,
548fa131d4aSManos Pitsidianakis                 "%s: virtio-snd command size incorrect %zu vs \
549fa131d4aSManos Pitsidianakis                 %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_hdr));
550fa131d4aSManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
551fa131d4aSManos Pitsidianakis         return;
552fa131d4aSManos Pitsidianakis     }
553fa131d4aSManos Pitsidianakis 
554fa131d4aSManos Pitsidianakis     stream_id = le32_to_cpu(req.stream_id);
555fa131d4aSManos Pitsidianakis     cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
556fa131d4aSManos Pitsidianakis     trace_virtio_snd_handle_pcm_start_stop(start ? "VIRTIO_SND_R_PCM_START" :
557fa131d4aSManos Pitsidianakis             "VIRTIO_SND_R_PCM_STOP", stream_id);
55818a75281SManos Pitsidianakis 
559fa131d4aSManos Pitsidianakis     stream = virtio_snd_pcm_get_stream(s, stream_id);
56018a75281SManos Pitsidianakis     if (stream) {
56118a75281SManos Pitsidianakis         WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
56218a75281SManos Pitsidianakis             stream->active = start;
56318a75281SManos Pitsidianakis         }
56418a75281SManos Pitsidianakis         if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
56518a75281SManos Pitsidianakis             AUD_set_active_out(stream->voice.out, start);
566d8d64acbSManos Pitsidianakis         } else {
567d8d64acbSManos Pitsidianakis             AUD_set_active_in(stream->voice.in, start);
56818a75281SManos Pitsidianakis         }
56918a75281SManos Pitsidianakis     } else {
57018a75281SManos Pitsidianakis         error_report("Invalid stream id: %"PRIu32, stream_id);
571fa131d4aSManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
572fa131d4aSManos Pitsidianakis         return;
573fa131d4aSManos Pitsidianakis     }
574fa131d4aSManos Pitsidianakis     stream->active = start;
575fa131d4aSManos Pitsidianakis }
576fa131d4aSManos Pitsidianakis 
577fa131d4aSManos Pitsidianakis /*
57818a75281SManos Pitsidianakis  * Returns the number of I/O messages that are being processed.
57918a75281SManos Pitsidianakis  *
58018a75281SManos Pitsidianakis  * @stream: VirtIOSoundPCMStream
58118a75281SManos Pitsidianakis  */
virtio_snd_pcm_get_io_msgs_count(VirtIOSoundPCMStream * stream)58218a75281SManos Pitsidianakis static size_t virtio_snd_pcm_get_io_msgs_count(VirtIOSoundPCMStream *stream)
58318a75281SManos Pitsidianakis {
58418a75281SManos Pitsidianakis     VirtIOSoundPCMBuffer *buffer, *next;
58518a75281SManos Pitsidianakis     size_t count = 0;
58618a75281SManos Pitsidianakis 
58718a75281SManos Pitsidianakis     WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
58818a75281SManos Pitsidianakis         QSIMPLEQ_FOREACH_SAFE(buffer, &stream->queue, entry, next) {
58918a75281SManos Pitsidianakis             count += 1;
59018a75281SManos Pitsidianakis         }
59118a75281SManos Pitsidianakis     }
59218a75281SManos Pitsidianakis     return count;
59318a75281SManos Pitsidianakis }
59418a75281SManos Pitsidianakis 
59518a75281SManos Pitsidianakis /*
59618a75281SManos Pitsidianakis  * Handles VIRTIO_SND_R_PCM_RELEASE.
597d48800d7SManos Pitsidianakis  *
598d48800d7SManos Pitsidianakis  * @s: VirtIOSound device
599d48800d7SManos Pitsidianakis  * @cmd: The request command queue element from VirtIOSound cmdq field
600d48800d7SManos Pitsidianakis  */
virtio_snd_handle_pcm_release(VirtIOSound * s,virtio_snd_ctrl_command * cmd)601d48800d7SManos Pitsidianakis static void virtio_snd_handle_pcm_release(VirtIOSound *s,
602d48800d7SManos Pitsidianakis                                           virtio_snd_ctrl_command *cmd)
603d48800d7SManos Pitsidianakis {
604d48800d7SManos Pitsidianakis     uint32_t stream_id;
605d48800d7SManos Pitsidianakis     VirtIOSoundPCMStream *stream;
606d48800d7SManos Pitsidianakis     size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
607d48800d7SManos Pitsidianakis                                cmd->elem->out_num,
608d48800d7SManos Pitsidianakis                                sizeof(virtio_snd_hdr),
609d48800d7SManos Pitsidianakis                                &stream_id,
610d48800d7SManos Pitsidianakis                                sizeof(stream_id));
611d48800d7SManos Pitsidianakis 
612d48800d7SManos Pitsidianakis     if (msg_sz != sizeof(stream_id)) {
613d48800d7SManos Pitsidianakis         /*
614d48800d7SManos Pitsidianakis          * TODO: do we need to set DEVICE_NEEDS_RESET?
615d48800d7SManos Pitsidianakis          */
616d48800d7SManos Pitsidianakis         qemu_log_mask(LOG_GUEST_ERROR,
617d48800d7SManos Pitsidianakis                 "%s: virtio-snd command size incorrect %zu vs \
618d48800d7SManos Pitsidianakis                 %zu\n", __func__, msg_sz, sizeof(stream_id));
619d48800d7SManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
620d48800d7SManos Pitsidianakis         return;
621d48800d7SManos Pitsidianakis     }
622d48800d7SManos Pitsidianakis 
623d48800d7SManos Pitsidianakis     stream_id = le32_to_cpu(stream_id);
624d48800d7SManos Pitsidianakis     trace_virtio_snd_handle_pcm_release(stream_id);
625d48800d7SManos Pitsidianakis     stream = virtio_snd_pcm_get_stream(s, stream_id);
626d48800d7SManos Pitsidianakis     if (stream == NULL) {
627d48800d7SManos Pitsidianakis         /*
628d48800d7SManos Pitsidianakis          * TODO: do we need to set DEVICE_NEEDS_RESET?
629d48800d7SManos Pitsidianakis          */
630d48800d7SManos Pitsidianakis         error_report("already released stream %"PRIu32, stream_id);
631d48800d7SManos Pitsidianakis         virtio_error(VIRTIO_DEVICE(s),
632d48800d7SManos Pitsidianakis                      "already released stream %"PRIu32,
633d48800d7SManos Pitsidianakis                      stream_id);
634d48800d7SManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
635d48800d7SManos Pitsidianakis         return;
636d48800d7SManos Pitsidianakis     }
63718a75281SManos Pitsidianakis 
63818a75281SManos Pitsidianakis     if (virtio_snd_pcm_get_io_msgs_count(stream)) {
63918a75281SManos Pitsidianakis         /*
64018a75281SManos Pitsidianakis          * virtio-v1.2-csd01, 5.14.6.6.5.1,
64118a75281SManos Pitsidianakis          * Device Requirements: Stream Release
64218a75281SManos Pitsidianakis          *
64318a75281SManos Pitsidianakis          * - The device MUST complete all pending I/O messages for the
64418a75281SManos Pitsidianakis          *   specified stream ID.
64518a75281SManos Pitsidianakis          * - The device MUST NOT complete the control request while there
64618a75281SManos Pitsidianakis          *   are pending I/O messages for the specified stream ID.
64718a75281SManos Pitsidianakis          */
64818a75281SManos Pitsidianakis         trace_virtio_snd_pcm_stream_flush(stream_id);
64918a75281SManos Pitsidianakis         virtio_snd_pcm_flush(stream);
65018a75281SManos Pitsidianakis     }
65118a75281SManos Pitsidianakis 
652d48800d7SManos Pitsidianakis     cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
653d48800d7SManos Pitsidianakis }
654d48800d7SManos Pitsidianakis 
655d48800d7SManos Pitsidianakis /*
656eb9ad377SManos Pitsidianakis  * The actual processing done in virtio_snd_process_cmdq().
657eb9ad377SManos Pitsidianakis  *
658eb9ad377SManos Pitsidianakis  * @s: VirtIOSound device
659eb9ad377SManos Pitsidianakis  * @cmd: control command request
660eb9ad377SManos Pitsidianakis  */
661eb9ad377SManos Pitsidianakis static inline void
process_cmd(VirtIOSound * s,virtio_snd_ctrl_command * cmd)662eb9ad377SManos Pitsidianakis process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd)
663eb9ad377SManos Pitsidianakis {
664eb9ad377SManos Pitsidianakis     uint32_t code;
665eb9ad377SManos Pitsidianakis     size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
666eb9ad377SManos Pitsidianakis                                cmd->elem->out_num,
667eb9ad377SManos Pitsidianakis                                0,
668eb9ad377SManos Pitsidianakis                                &cmd->ctrl,
669eb9ad377SManos Pitsidianakis                                sizeof(virtio_snd_hdr));
670eb9ad377SManos Pitsidianakis 
671eb9ad377SManos Pitsidianakis     if (msg_sz != sizeof(virtio_snd_hdr)) {
672eb9ad377SManos Pitsidianakis         /*
673eb9ad377SManos Pitsidianakis          * TODO: do we need to set DEVICE_NEEDS_RESET?
674eb9ad377SManos Pitsidianakis          */
675eb9ad377SManos Pitsidianakis         qemu_log_mask(LOG_GUEST_ERROR,
676eb9ad377SManos Pitsidianakis                 "%s: virtio-snd command size incorrect %zu vs \
677eb9ad377SManos Pitsidianakis                 %zu\n", __func__, msg_sz, sizeof(virtio_snd_hdr));
678eb9ad377SManos Pitsidianakis         return;
679eb9ad377SManos Pitsidianakis     }
680eb9ad377SManos Pitsidianakis 
681eb9ad377SManos Pitsidianakis     code = le32_to_cpu(cmd->ctrl.code);
682eb9ad377SManos Pitsidianakis 
683eb9ad377SManos Pitsidianakis     trace_virtio_snd_handle_code(code, print_code(code));
684eb9ad377SManos Pitsidianakis 
685eb9ad377SManos Pitsidianakis     switch (code) {
686eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_JACK_INFO:
687eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_JACK_REMAP:
688eb9ad377SManos Pitsidianakis         qemu_log_mask(LOG_UNIMP,
689eb9ad377SManos Pitsidianakis                      "virtio_snd: jack functionality is unimplemented.\n");
690eb9ad377SManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
691eb9ad377SManos Pitsidianakis         break;
692eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_PCM_INFO:
6930ff05dd2SManos Pitsidianakis         virtio_snd_handle_pcm_info(s, cmd);
6940ff05dd2SManos Pitsidianakis         break;
695fa131d4aSManos Pitsidianakis     case VIRTIO_SND_R_PCM_START:
696fa131d4aSManos Pitsidianakis         virtio_snd_handle_pcm_start_stop(s, cmd, true);
697fa131d4aSManos Pitsidianakis         break;
698fa131d4aSManos Pitsidianakis     case VIRTIO_SND_R_PCM_STOP:
699fa131d4aSManos Pitsidianakis         virtio_snd_handle_pcm_start_stop(s, cmd, false);
700fa131d4aSManos Pitsidianakis         break;
701eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_PCM_SET_PARAMS:
70264704ce0SManos Pitsidianakis         virtio_snd_handle_pcm_set_params(s, cmd);
70364704ce0SManos Pitsidianakis         break;
704eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_PCM_PREPARE:
705e5788b8fSManos Pitsidianakis         virtio_snd_handle_pcm_prepare(s, cmd);
706e5788b8fSManos Pitsidianakis         break;
707eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_PCM_RELEASE:
708d48800d7SManos Pitsidianakis         virtio_snd_handle_pcm_release(s, cmd);
709eb9ad377SManos Pitsidianakis         break;
710eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_CHMAP_INFO:
711eb9ad377SManos Pitsidianakis         qemu_log_mask(LOG_UNIMP,
712eb9ad377SManos Pitsidianakis                      "virtio_snd: chmap info functionality is unimplemented.\n");
713eb9ad377SManos Pitsidianakis         trace_virtio_snd_handle_chmap_info();
714eb9ad377SManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
715eb9ad377SManos Pitsidianakis         break;
716eb9ad377SManos Pitsidianakis     default:
717eb9ad377SManos Pitsidianakis         /* error */
718eb9ad377SManos Pitsidianakis         error_report("virtio snd header not recognized: %"PRIu32, code);
719eb9ad377SManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
720eb9ad377SManos Pitsidianakis     }
721eb9ad377SManos Pitsidianakis 
722eb9ad377SManos Pitsidianakis     iov_from_buf(cmd->elem->in_sg,
723eb9ad377SManos Pitsidianakis                  cmd->elem->in_num,
724eb9ad377SManos Pitsidianakis                  0,
725eb9ad377SManos Pitsidianakis                  &cmd->resp,
726eb9ad377SManos Pitsidianakis                  sizeof(virtio_snd_hdr));
727633487dfSVolker Rümelin     virtqueue_push(cmd->vq, cmd->elem,
728633487dfSVolker Rümelin                    sizeof(virtio_snd_hdr) + cmd->payload_size);
729eb9ad377SManos Pitsidianakis     virtio_notify(VIRTIO_DEVICE(s), cmd->vq);
730eb9ad377SManos Pitsidianakis }
731eb9ad377SManos Pitsidianakis 
732eb9ad377SManos Pitsidianakis /*
733eb9ad377SManos Pitsidianakis  * Consume all elements in command queue.
734eb9ad377SManos Pitsidianakis  *
735eb9ad377SManos Pitsidianakis  * @s: VirtIOSound device
736eb9ad377SManos Pitsidianakis  */
virtio_snd_process_cmdq(VirtIOSound * s)737eb9ad377SManos Pitsidianakis static void virtio_snd_process_cmdq(VirtIOSound *s)
738eb9ad377SManos Pitsidianakis {
739eb9ad377SManos Pitsidianakis     virtio_snd_ctrl_command *cmd;
740eb9ad377SManos Pitsidianakis 
741eb9ad377SManos Pitsidianakis     if (unlikely(qatomic_read(&s->processing_cmdq))) {
742eb9ad377SManos Pitsidianakis         return;
743eb9ad377SManos Pitsidianakis     }
744eb9ad377SManos Pitsidianakis 
745eb9ad377SManos Pitsidianakis     WITH_QEMU_LOCK_GUARD(&s->cmdq_mutex) {
746eb9ad377SManos Pitsidianakis         qatomic_set(&s->processing_cmdq, true);
747eb9ad377SManos Pitsidianakis         while (!QTAILQ_EMPTY(&s->cmdq)) {
748eb9ad377SManos Pitsidianakis             cmd = QTAILQ_FIRST(&s->cmdq);
749eb9ad377SManos Pitsidianakis 
750eb9ad377SManos Pitsidianakis             /* process command */
751eb9ad377SManos Pitsidianakis             process_cmd(s, cmd);
752eb9ad377SManos Pitsidianakis 
753eb9ad377SManos Pitsidianakis             QTAILQ_REMOVE(&s->cmdq, cmd, next);
754eb9ad377SManos Pitsidianakis 
755eb9ad377SManos Pitsidianakis             virtio_snd_ctrl_cmd_free(cmd);
756eb9ad377SManos Pitsidianakis         }
757eb9ad377SManos Pitsidianakis         qatomic_set(&s->processing_cmdq, false);
758eb9ad377SManos Pitsidianakis     }
759eb9ad377SManos Pitsidianakis }
760eb9ad377SManos Pitsidianakis 
761eb9ad377SManos Pitsidianakis /*
762eb9ad377SManos Pitsidianakis  * The control message handler. Pops an element from the control virtqueue,
763eb9ad377SManos Pitsidianakis  * and stores them to VirtIOSound's cmdq queue and finally calls
764eb9ad377SManos Pitsidianakis  * virtio_snd_process_cmdq() for processing.
765eb9ad377SManos Pitsidianakis  *
766eb9ad377SManos Pitsidianakis  * @vdev: VirtIOSound device
767eb9ad377SManos Pitsidianakis  * @vq: Control virtqueue
768eb9ad377SManos Pitsidianakis  */
virtio_snd_handle_ctrl(VirtIODevice * vdev,VirtQueue * vq)769eb9ad377SManos Pitsidianakis static void virtio_snd_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
770eb9ad377SManos Pitsidianakis {
771eb9ad377SManos Pitsidianakis     VirtIOSound *s = VIRTIO_SND(vdev);
772eb9ad377SManos Pitsidianakis     VirtQueueElement *elem;
773eb9ad377SManos Pitsidianakis     virtio_snd_ctrl_command *cmd;
774eb9ad377SManos Pitsidianakis 
775eb9ad377SManos Pitsidianakis     trace_virtio_snd_handle_ctrl(vdev, vq);
776eb9ad377SManos Pitsidianakis 
777eb9ad377SManos Pitsidianakis     if (!virtio_queue_ready(vq)) {
778eb9ad377SManos Pitsidianakis         return;
779eb9ad377SManos Pitsidianakis     }
780eb9ad377SManos Pitsidianakis 
781eb9ad377SManos Pitsidianakis     elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
782eb9ad377SManos Pitsidianakis     while (elem) {
783eb9ad377SManos Pitsidianakis         cmd = g_new0(virtio_snd_ctrl_command, 1);
784eb9ad377SManos Pitsidianakis         cmd->elem = elem;
785eb9ad377SManos Pitsidianakis         cmd->vq = vq;
786eb9ad377SManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
787633487dfSVolker Rümelin         /* implicit cmd->payload_size = 0; */
788eb9ad377SManos Pitsidianakis         QTAILQ_INSERT_TAIL(&s->cmdq, cmd, next);
789eb9ad377SManos Pitsidianakis         elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
790eb9ad377SManos Pitsidianakis     }
791eb9ad377SManos Pitsidianakis 
792eb9ad377SManos Pitsidianakis     virtio_snd_process_cmdq(s);
793eb9ad377SManos Pitsidianakis }
794eb9ad377SManos Pitsidianakis 
795eb9ad377SManos Pitsidianakis /*
796eb9ad377SManos Pitsidianakis  * The event virtqueue handler.
797eb9ad377SManos Pitsidianakis  * Not implemented yet.
798eb9ad377SManos Pitsidianakis  *
799eb9ad377SManos Pitsidianakis  * @vdev: VirtIOSound device
800eb9ad377SManos Pitsidianakis  * @vq: event vq
801eb9ad377SManos Pitsidianakis  */
virtio_snd_handle_event(VirtIODevice * vdev,VirtQueue * vq)802eb9ad377SManos Pitsidianakis static void virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq)
803eb9ad377SManos Pitsidianakis {
804eb9ad377SManos Pitsidianakis     qemu_log_mask(LOG_UNIMP, "virtio_snd: event queue is unimplemented.\n");
805eb9ad377SManos Pitsidianakis     trace_virtio_snd_handle_event();
806eb9ad377SManos Pitsidianakis }
807eb9ad377SManos Pitsidianakis 
808731655f8SManos Pitsidianakis /*
809731655f8SManos Pitsidianakis  * Must only be called if vsnd->invalid is not empty.
810731655f8SManos Pitsidianakis  */
empty_invalid_queue(VirtIODevice * vdev,VirtQueue * vq)811d8d64acbSManos Pitsidianakis static inline void empty_invalid_queue(VirtIODevice *vdev, VirtQueue *vq)
812d8d64acbSManos Pitsidianakis {
813d8d64acbSManos Pitsidianakis     VirtIOSoundPCMBuffer *buffer = NULL;
814d8d64acbSManos Pitsidianakis     virtio_snd_pcm_status resp = { 0 };
815d8d64acbSManos Pitsidianakis     VirtIOSound *vsnd = VIRTIO_SND(vdev);
816d8d64acbSManos Pitsidianakis 
817731655f8SManos Pitsidianakis     g_assert(!QSIMPLEQ_EMPTY(&vsnd->invalid));
818731655f8SManos Pitsidianakis 
819731655f8SManos Pitsidianakis     while (!QSIMPLEQ_EMPTY(&vsnd->invalid)) {
820731655f8SManos Pitsidianakis         buffer = QSIMPLEQ_FIRST(&vsnd->invalid);
821731655f8SManos Pitsidianakis         /* If buffer->vq != vq, our logic is fundamentally wrong, so bail out */
822731655f8SManos Pitsidianakis         g_assert(buffer->vq == vq);
823731655f8SManos Pitsidianakis 
824d8d64acbSManos Pitsidianakis         resp.status = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
825d8d64acbSManos Pitsidianakis         iov_from_buf(buffer->elem->in_sg,
826d8d64acbSManos Pitsidianakis                      buffer->elem->in_num,
827d8d64acbSManos Pitsidianakis                      0,
828d8d64acbSManos Pitsidianakis                      &resp,
829d8d64acbSManos Pitsidianakis                      sizeof(virtio_snd_pcm_status));
830d8d64acbSManos Pitsidianakis         virtqueue_push(vq,
831d8d64acbSManos Pitsidianakis                        buffer->elem,
832d8d64acbSManos Pitsidianakis                        sizeof(virtio_snd_pcm_status));
833731655f8SManos Pitsidianakis         QSIMPLEQ_REMOVE_HEAD(&vsnd->invalid, entry);
834d8d64acbSManos Pitsidianakis         virtio_snd_pcm_buffer_free(buffer);
835d8d64acbSManos Pitsidianakis     }
836731655f8SManos Pitsidianakis     /* Notify vq about virtio_snd_pcm_status responses. */
837d8d64acbSManos Pitsidianakis     virtio_notify(vdev, vq);
838d8d64acbSManos Pitsidianakis }
839d8d64acbSManos Pitsidianakis 
840eb9ad377SManos Pitsidianakis /*
84118a75281SManos Pitsidianakis  * The tx virtqueue handler. Makes the buffers available to their respective
84218a75281SManos Pitsidianakis  * streams for consumption.
84318a75281SManos Pitsidianakis  *
84418a75281SManos Pitsidianakis  * @vdev: VirtIOSound device
84518a75281SManos Pitsidianakis  * @vq: tx virtqueue
84618a75281SManos Pitsidianakis  */
virtio_snd_handle_tx_xfer(VirtIODevice * vdev,VirtQueue * vq)847d8d64acbSManos Pitsidianakis static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq)
84818a75281SManos Pitsidianakis {
849731655f8SManos Pitsidianakis     VirtIOSound *vsnd = VIRTIO_SND(vdev);
85018a75281SManos Pitsidianakis     VirtIOSoundPCMBuffer *buffer;
85118a75281SManos Pitsidianakis     VirtQueueElement *elem;
85218a75281SManos Pitsidianakis     size_t msg_sz, size;
85318a75281SManos Pitsidianakis     virtio_snd_pcm_xfer hdr;
85418a75281SManos Pitsidianakis     uint32_t stream_id;
85518a75281SManos Pitsidianakis     /*
856731655f8SManos Pitsidianakis      * If any of the I/O messages are invalid, put them in vsnd->invalid and
85718a75281SManos Pitsidianakis      * return them after the for loop.
85818a75281SManos Pitsidianakis      */
85918a75281SManos Pitsidianakis     bool must_empty_invalid_queue = false;
86018a75281SManos Pitsidianakis 
86118a75281SManos Pitsidianakis     if (!virtio_queue_ready(vq)) {
86218a75281SManos Pitsidianakis         return;
86318a75281SManos Pitsidianakis     }
864d8d64acbSManos Pitsidianakis     trace_virtio_snd_handle_tx_xfer();
86518a75281SManos Pitsidianakis 
866dcb0a1acSPhilippe Mathieu-Daudé     for (;;) {
867dcb0a1acSPhilippe Mathieu-Daudé         VirtIOSoundPCMStream *stream;
868dcb0a1acSPhilippe Mathieu-Daudé 
86918a75281SManos Pitsidianakis         elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
87018a75281SManos Pitsidianakis         if (!elem) {
87118a75281SManos Pitsidianakis             break;
87218a75281SManos Pitsidianakis         }
87318a75281SManos Pitsidianakis         /* get the message hdr object */
87418a75281SManos Pitsidianakis         msg_sz = iov_to_buf(elem->out_sg,
87518a75281SManos Pitsidianakis                             elem->out_num,
87618a75281SManos Pitsidianakis                             0,
87718a75281SManos Pitsidianakis                             &hdr,
87818a75281SManos Pitsidianakis                             sizeof(virtio_snd_pcm_xfer));
87918a75281SManos Pitsidianakis         if (msg_sz != sizeof(virtio_snd_pcm_xfer)) {
880731655f8SManos Pitsidianakis             goto tx_err;
88118a75281SManos Pitsidianakis         }
88218a75281SManos Pitsidianakis         stream_id = le32_to_cpu(hdr.stream_id);
88318a75281SManos Pitsidianakis 
884731655f8SManos Pitsidianakis         if (stream_id >= vsnd->snd_conf.streams
885731655f8SManos Pitsidianakis             || vsnd->pcm->streams[stream_id] == NULL) {
886731655f8SManos Pitsidianakis             goto tx_err;
88718a75281SManos Pitsidianakis         }
88818a75281SManos Pitsidianakis 
889731655f8SManos Pitsidianakis         stream = vsnd->pcm->streams[stream_id];
89018a75281SManos Pitsidianakis         if (stream->info.direction != VIRTIO_SND_D_OUTPUT) {
89118a75281SManos Pitsidianakis             goto tx_err;
89218a75281SManos Pitsidianakis         }
89318a75281SManos Pitsidianakis 
89418a75281SManos Pitsidianakis         WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
89518a75281SManos Pitsidianakis             size = iov_size(elem->out_sg, elem->out_num) - msg_sz;
89618a75281SManos Pitsidianakis 
89718a75281SManos Pitsidianakis             buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
89818a75281SManos Pitsidianakis             buffer->elem = elem;
89918a75281SManos Pitsidianakis             buffer->populated = false;
90018a75281SManos Pitsidianakis             buffer->vq = vq;
90118a75281SManos Pitsidianakis             buffer->size = size;
90218a75281SManos Pitsidianakis             buffer->offset = 0;
90318a75281SManos Pitsidianakis 
90418a75281SManos Pitsidianakis             QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry);
90518a75281SManos Pitsidianakis         }
90618a75281SManos Pitsidianakis         continue;
90718a75281SManos Pitsidianakis 
90818a75281SManos Pitsidianakis tx_err:
90918a75281SManos Pitsidianakis         must_empty_invalid_queue = true;
91018a75281SManos Pitsidianakis         buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer));
91118a75281SManos Pitsidianakis         buffer->elem = elem;
91218a75281SManos Pitsidianakis         buffer->vq = vq;
913731655f8SManos Pitsidianakis         QSIMPLEQ_INSERT_TAIL(&vsnd->invalid, buffer, entry);
91418a75281SManos Pitsidianakis     }
91518a75281SManos Pitsidianakis 
91618a75281SManos Pitsidianakis     if (must_empty_invalid_queue) {
917d8d64acbSManos Pitsidianakis         empty_invalid_queue(vdev, vq);
91818a75281SManos Pitsidianakis     }
91918a75281SManos Pitsidianakis }
92018a75281SManos Pitsidianakis 
92118a75281SManos Pitsidianakis /*
922d8d64acbSManos Pitsidianakis  * The rx virtqueue handler. Makes the buffers available to their respective
923d8d64acbSManos Pitsidianakis  * streams for consumption.
9242880e676SManos Pitsidianakis  *
9252880e676SManos Pitsidianakis  * @vdev: VirtIOSound device
926d8d64acbSManos Pitsidianakis  * @vq: rx virtqueue
9272880e676SManos Pitsidianakis  */
virtio_snd_handle_rx_xfer(VirtIODevice * vdev,VirtQueue * vq)928d8d64acbSManos Pitsidianakis static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq)
929d8d64acbSManos Pitsidianakis {
930731655f8SManos Pitsidianakis     VirtIOSound *vsnd = VIRTIO_SND(vdev);
931d8d64acbSManos Pitsidianakis     VirtIOSoundPCMBuffer *buffer;
932d8d64acbSManos Pitsidianakis     VirtQueueElement *elem;
933d8d64acbSManos Pitsidianakis     size_t msg_sz, size;
934d8d64acbSManos Pitsidianakis     virtio_snd_pcm_xfer hdr;
935d8d64acbSManos Pitsidianakis     uint32_t stream_id;
936d8d64acbSManos Pitsidianakis     /*
937731655f8SManos Pitsidianakis      * if any of the I/O messages are invalid, put them in vsnd->invalid and
938d8d64acbSManos Pitsidianakis      * return them after the for loop.
939d8d64acbSManos Pitsidianakis      */
940d8d64acbSManos Pitsidianakis     bool must_empty_invalid_queue = false;
941d8d64acbSManos Pitsidianakis 
942d8d64acbSManos Pitsidianakis     if (!virtio_queue_ready(vq)) {
943d8d64acbSManos Pitsidianakis         return;
944d8d64acbSManos Pitsidianakis     }
945d8d64acbSManos Pitsidianakis     trace_virtio_snd_handle_rx_xfer();
946d8d64acbSManos Pitsidianakis 
947dcb0a1acSPhilippe Mathieu-Daudé     for (;;) {
948dcb0a1acSPhilippe Mathieu-Daudé         VirtIOSoundPCMStream *stream;
949dcb0a1acSPhilippe Mathieu-Daudé 
950d8d64acbSManos Pitsidianakis         elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
951d8d64acbSManos Pitsidianakis         if (!elem) {
952d8d64acbSManos Pitsidianakis             break;
953d8d64acbSManos Pitsidianakis         }
954d8d64acbSManos Pitsidianakis         /* get the message hdr object */
955d8d64acbSManos Pitsidianakis         msg_sz = iov_to_buf(elem->out_sg,
956d8d64acbSManos Pitsidianakis                             elem->out_num,
957d8d64acbSManos Pitsidianakis                             0,
958d8d64acbSManos Pitsidianakis                             &hdr,
959d8d64acbSManos Pitsidianakis                             sizeof(virtio_snd_pcm_xfer));
960d8d64acbSManos Pitsidianakis         if (msg_sz != sizeof(virtio_snd_pcm_xfer)) {
961731655f8SManos Pitsidianakis             goto rx_err;
962d8d64acbSManos Pitsidianakis         }
963d8d64acbSManos Pitsidianakis         stream_id = le32_to_cpu(hdr.stream_id);
964d8d64acbSManos Pitsidianakis 
965731655f8SManos Pitsidianakis         if (stream_id >= vsnd->snd_conf.streams
966731655f8SManos Pitsidianakis             || !vsnd->pcm->streams[stream_id]) {
967731655f8SManos Pitsidianakis             goto rx_err;
968d8d64acbSManos Pitsidianakis         }
969d8d64acbSManos Pitsidianakis 
970731655f8SManos Pitsidianakis         stream = vsnd->pcm->streams[stream_id];
971d8d64acbSManos Pitsidianakis         if (stream == NULL || stream->info.direction != VIRTIO_SND_D_INPUT) {
972d8d64acbSManos Pitsidianakis             goto rx_err;
973d8d64acbSManos Pitsidianakis         }
974d8d64acbSManos Pitsidianakis         WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
975d8d64acbSManos Pitsidianakis             size = iov_size(elem->in_sg, elem->in_num) -
976d8d64acbSManos Pitsidianakis                 sizeof(virtio_snd_pcm_status);
977d8d64acbSManos Pitsidianakis             buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
978d8d64acbSManos Pitsidianakis             buffer->elem = elem;
979d8d64acbSManos Pitsidianakis             buffer->vq = vq;
980d8d64acbSManos Pitsidianakis             buffer->size = 0;
981d8d64acbSManos Pitsidianakis             buffer->offset = 0;
982d8d64acbSManos Pitsidianakis             QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry);
983d8d64acbSManos Pitsidianakis         }
984d8d64acbSManos Pitsidianakis         continue;
985d8d64acbSManos Pitsidianakis 
986d8d64acbSManos Pitsidianakis rx_err:
987d8d64acbSManos Pitsidianakis         must_empty_invalid_queue = true;
988d8d64acbSManos Pitsidianakis         buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer));
989d8d64acbSManos Pitsidianakis         buffer->elem = elem;
990d8d64acbSManos Pitsidianakis         buffer->vq = vq;
991731655f8SManos Pitsidianakis         QSIMPLEQ_INSERT_TAIL(&vsnd->invalid, buffer, entry);
992d8d64acbSManos Pitsidianakis     }
993d8d64acbSManos Pitsidianakis 
994d8d64acbSManos Pitsidianakis     if (must_empty_invalid_queue) {
995d8d64acbSManos Pitsidianakis         empty_invalid_queue(vdev, vq);
996d8d64acbSManos Pitsidianakis     }
997d8d64acbSManos Pitsidianakis }
9982880e676SManos Pitsidianakis 
get_features(VirtIODevice * vdev,uint64_t features,Error ** errp)9992880e676SManos Pitsidianakis static uint64_t get_features(VirtIODevice *vdev, uint64_t features,
10002880e676SManos Pitsidianakis                              Error **errp)
10012880e676SManos Pitsidianakis {
10022880e676SManos Pitsidianakis     /*
10032880e676SManos Pitsidianakis      * virtio-v1.2-csd01, 5.14.3,
10042880e676SManos Pitsidianakis      * Feature Bits
10052880e676SManos Pitsidianakis      * None currently defined.
10062880e676SManos Pitsidianakis      */
10072880e676SManos Pitsidianakis     VirtIOSound *s = VIRTIO_SND(vdev);
10082880e676SManos Pitsidianakis     features |= s->features;
10092880e676SManos Pitsidianakis 
10102880e676SManos Pitsidianakis     trace_virtio_snd_get_features(vdev, features);
10112880e676SManos Pitsidianakis 
10122880e676SManos Pitsidianakis     return features;
10132880e676SManos Pitsidianakis }
10142880e676SManos Pitsidianakis 
10152880e676SManos Pitsidianakis static void
virtio_snd_vm_state_change(void * opaque,bool running,RunState state)10162880e676SManos Pitsidianakis virtio_snd_vm_state_change(void *opaque, bool running,
10172880e676SManos Pitsidianakis                                        RunState state)
10182880e676SManos Pitsidianakis {
10192880e676SManos Pitsidianakis     if (running) {
10202880e676SManos Pitsidianakis         trace_virtio_snd_vm_state_running();
10212880e676SManos Pitsidianakis     } else {
10222880e676SManos Pitsidianakis         trace_virtio_snd_vm_state_stopped();
10232880e676SManos Pitsidianakis     }
10242880e676SManos Pitsidianakis }
10252880e676SManos Pitsidianakis 
virtio_snd_realize(DeviceState * dev,Error ** errp)10262880e676SManos Pitsidianakis static void virtio_snd_realize(DeviceState *dev, Error **errp)
10272880e676SManos Pitsidianakis {
10282880e676SManos Pitsidianakis     ERRP_GUARD();
10292880e676SManos Pitsidianakis     VirtIOSound *vsnd = VIRTIO_SND(dev);
10302880e676SManos Pitsidianakis     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
1031eb9ad377SManos Pitsidianakis     virtio_snd_pcm_set_params default_params = { 0 };
1032eb9ad377SManos Pitsidianakis     uint32_t status;
10332880e676SManos Pitsidianakis 
10342880e676SManos Pitsidianakis     trace_virtio_snd_realize(vsnd);
10352880e676SManos Pitsidianakis 
1036f7856181SManos Pitsidianakis     /* check number of jacks and streams */
10372880e676SManos Pitsidianakis     if (vsnd->snd_conf.jacks > 8) {
10382880e676SManos Pitsidianakis         error_setg(errp,
10392880e676SManos Pitsidianakis                    "Invalid number of jacks: %"PRIu32,
10402880e676SManos Pitsidianakis                    vsnd->snd_conf.jacks);
10412880e676SManos Pitsidianakis         return;
10422880e676SManos Pitsidianakis     }
10432880e676SManos Pitsidianakis     if (vsnd->snd_conf.streams < 1 || vsnd->snd_conf.streams > 10) {
10442880e676SManos Pitsidianakis         error_setg(errp,
10452880e676SManos Pitsidianakis                    "Invalid number of streams: %"PRIu32,
10462880e676SManos Pitsidianakis                     vsnd->snd_conf.streams);
10472880e676SManos Pitsidianakis         return;
10482880e676SManos Pitsidianakis     }
10492880e676SManos Pitsidianakis 
10502880e676SManos Pitsidianakis     if (vsnd->snd_conf.chmaps > VIRTIO_SND_CHMAP_MAX_SIZE) {
10512880e676SManos Pitsidianakis         error_setg(errp,
10522880e676SManos Pitsidianakis                    "Invalid number of channel maps: %"PRIu32,
10532880e676SManos Pitsidianakis                    vsnd->snd_conf.chmaps);
10542880e676SManos Pitsidianakis         return;
10552880e676SManos Pitsidianakis     }
10562880e676SManos Pitsidianakis 
1057691d3d8bSManos Pitsidianakis     if (!AUD_register_card("virtio-sound", &vsnd->card, errp)) {
1058691d3d8bSManos Pitsidianakis         return;
1059691d3d8bSManos Pitsidianakis     }
10602880e676SManos Pitsidianakis 
1061f7856181SManos Pitsidianakis     vsnd->vmstate =
1062f7856181SManos Pitsidianakis         qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd);
1063f7856181SManos Pitsidianakis 
1064f7856181SManos Pitsidianakis     vsnd->pcm = g_new0(VirtIOSoundPCM, 1);
1065f7856181SManos Pitsidianakis     vsnd->pcm->snd = vsnd;
1066f7856181SManos Pitsidianakis     vsnd->pcm->streams =
1067f7856181SManos Pitsidianakis         g_new0(VirtIOSoundPCMStream *, vsnd->snd_conf.streams);
1068f7856181SManos Pitsidianakis     vsnd->pcm->pcm_params =
1069f7856181SManos Pitsidianakis         g_new0(virtio_snd_pcm_set_params, vsnd->snd_conf.streams);
1070f7856181SManos Pitsidianakis 
1071f7856181SManos Pitsidianakis     virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config));
1072f7856181SManos Pitsidianakis     virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1);
1073f7856181SManos Pitsidianakis 
1074eb9ad377SManos Pitsidianakis     /* set default params for all streams */
1075eb9ad377SManos Pitsidianakis     default_params.features = 0;
1076eb9ad377SManos Pitsidianakis     default_params.buffer_bytes = cpu_to_le32(8192);
1077eb9ad377SManos Pitsidianakis     default_params.period_bytes = cpu_to_le32(2048);
1078eb9ad377SManos Pitsidianakis     default_params.channels = 2;
1079eb9ad377SManos Pitsidianakis     default_params.format = VIRTIO_SND_PCM_FMT_S16;
1080eb9ad377SManos Pitsidianakis     default_params.rate = VIRTIO_SND_PCM_RATE_48000;
10812880e676SManos Pitsidianakis     vsnd->queues[VIRTIO_SND_VQ_CONTROL] =
1082eb9ad377SManos Pitsidianakis         virtio_add_queue(vdev, 64, virtio_snd_handle_ctrl);
10832880e676SManos Pitsidianakis     vsnd->queues[VIRTIO_SND_VQ_EVENT] =
1084eb9ad377SManos Pitsidianakis         virtio_add_queue(vdev, 64, virtio_snd_handle_event);
10852880e676SManos Pitsidianakis     vsnd->queues[VIRTIO_SND_VQ_TX] =
1086d8d64acbSManos Pitsidianakis         virtio_add_queue(vdev, 64, virtio_snd_handle_tx_xfer);
10872880e676SManos Pitsidianakis     vsnd->queues[VIRTIO_SND_VQ_RX] =
1088d8d64acbSManos Pitsidianakis         virtio_add_queue(vdev, 64, virtio_snd_handle_rx_xfer);
1089eb9ad377SManos Pitsidianakis     qemu_mutex_init(&vsnd->cmdq_mutex);
1090eb9ad377SManos Pitsidianakis     QTAILQ_INIT(&vsnd->cmdq);
1091731655f8SManos Pitsidianakis     QSIMPLEQ_INIT(&vsnd->invalid);
1092eb9ad377SManos Pitsidianakis 
1093eb9ad377SManos Pitsidianakis     for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
1094eb9ad377SManos Pitsidianakis         status = virtio_snd_set_pcm_params(vsnd, i, &default_params);
1095eb9ad377SManos Pitsidianakis         if (status != cpu_to_le32(VIRTIO_SND_S_OK)) {
1096eb9ad377SManos Pitsidianakis             error_setg(errp,
1097aaf851a2SMichael Tokarev                        "Can't initialize stream params, device responded with %s.",
1098eb9ad377SManos Pitsidianakis                        print_code(status));
1099f7856181SManos Pitsidianakis             goto error_cleanup;
1100eb9ad377SManos Pitsidianakis         }
1101eb9ad377SManos Pitsidianakis         status = virtio_snd_pcm_prepare(vsnd, i);
1102eb9ad377SManos Pitsidianakis         if (status != cpu_to_le32(VIRTIO_SND_S_OK)) {
1103eb9ad377SManos Pitsidianakis             error_setg(errp,
1104eb9ad377SManos Pitsidianakis                        "Can't prepare streams, device responded with %s.",
1105eb9ad377SManos Pitsidianakis                        print_code(status));
1106f7856181SManos Pitsidianakis             goto error_cleanup;
1107f7856181SManos Pitsidianakis         }
1108f7856181SManos Pitsidianakis     }
1109f7856181SManos Pitsidianakis 
1110eb9ad377SManos Pitsidianakis     return;
1111f7856181SManos Pitsidianakis 
1112f7856181SManos Pitsidianakis error_cleanup:
1113f7856181SManos Pitsidianakis     virtio_snd_unrealize(dev);
11142880e676SManos Pitsidianakis }
11152880e676SManos Pitsidianakis 
return_tx_buffer(VirtIOSoundPCMStream * stream,VirtIOSoundPCMBuffer * buffer)111618a75281SManos Pitsidianakis static inline void return_tx_buffer(VirtIOSoundPCMStream *stream,
111718a75281SManos Pitsidianakis                                     VirtIOSoundPCMBuffer *buffer)
111818a75281SManos Pitsidianakis {
111918a75281SManos Pitsidianakis     virtio_snd_pcm_status resp = { 0 };
112018a75281SManos Pitsidianakis     resp.status = cpu_to_le32(VIRTIO_SND_S_OK);
112118a75281SManos Pitsidianakis     resp.latency_bytes = cpu_to_le32((uint32_t)buffer->size);
112218a75281SManos Pitsidianakis     iov_from_buf(buffer->elem->in_sg,
112318a75281SManos Pitsidianakis                  buffer->elem->in_num,
112418a75281SManos Pitsidianakis                  0,
112518a75281SManos Pitsidianakis                  &resp,
112618a75281SManos Pitsidianakis                  sizeof(virtio_snd_pcm_status));
112718a75281SManos Pitsidianakis     virtqueue_push(buffer->vq,
112818a75281SManos Pitsidianakis                    buffer->elem,
112918a75281SManos Pitsidianakis                    sizeof(virtio_snd_pcm_status));
113018a75281SManos Pitsidianakis     virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq);
113118a75281SManos Pitsidianakis     QSIMPLEQ_REMOVE(&stream->queue,
113218a75281SManos Pitsidianakis                     buffer,
113318a75281SManos Pitsidianakis                     VirtIOSoundPCMBuffer,
113418a75281SManos Pitsidianakis                     entry);
113518a75281SManos Pitsidianakis     virtio_snd_pcm_buffer_free(buffer);
113618a75281SManos Pitsidianakis }
113718a75281SManos Pitsidianakis 
113818a75281SManos Pitsidianakis /*
113918a75281SManos Pitsidianakis  * AUD_* output callback.
114018a75281SManos Pitsidianakis  *
114118a75281SManos Pitsidianakis  * @data: VirtIOSoundPCMStream stream
114218a75281SManos Pitsidianakis  * @available: number of bytes that can be written with AUD_write()
114318a75281SManos Pitsidianakis  */
virtio_snd_pcm_out_cb(void * data,int available)114418a75281SManos Pitsidianakis static void virtio_snd_pcm_out_cb(void *data, int available)
114518a75281SManos Pitsidianakis {
114618a75281SManos Pitsidianakis     VirtIOSoundPCMStream *stream = data;
114718a75281SManos Pitsidianakis     VirtIOSoundPCMBuffer *buffer;
114818a75281SManos Pitsidianakis     size_t size;
114918a75281SManos Pitsidianakis 
115018a75281SManos Pitsidianakis     WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
115118a75281SManos Pitsidianakis         while (!QSIMPLEQ_EMPTY(&stream->queue)) {
115218a75281SManos Pitsidianakis             buffer = QSIMPLEQ_FIRST(&stream->queue);
115318a75281SManos Pitsidianakis             if (!virtio_queue_ready(buffer->vq)) {
115418a75281SManos Pitsidianakis                 return;
115518a75281SManos Pitsidianakis             }
115618a75281SManos Pitsidianakis             if (!stream->active) {
115718a75281SManos Pitsidianakis                 /* Stream has stopped, so do not perform AUD_write. */
115818a75281SManos Pitsidianakis                 return_tx_buffer(stream, buffer);
115918a75281SManos Pitsidianakis                 continue;
116018a75281SManos Pitsidianakis             }
116118a75281SManos Pitsidianakis             if (!buffer->populated) {
116218a75281SManos Pitsidianakis                 iov_to_buf(buffer->elem->out_sg,
116318a75281SManos Pitsidianakis                            buffer->elem->out_num,
116418a75281SManos Pitsidianakis                            sizeof(virtio_snd_pcm_xfer),
116518a75281SManos Pitsidianakis                            buffer->data,
116618a75281SManos Pitsidianakis                            buffer->size);
116718a75281SManos Pitsidianakis                 buffer->populated = true;
116818a75281SManos Pitsidianakis             }
116918a75281SManos Pitsidianakis             for (;;) {
117018a75281SManos Pitsidianakis                 size = AUD_write(stream->voice.out,
117118a75281SManos Pitsidianakis                                  buffer->data + buffer->offset,
117218a75281SManos Pitsidianakis                                  MIN(buffer->size, available));
117318a75281SManos Pitsidianakis                 assert(size <= MIN(buffer->size, available));
117418a75281SManos Pitsidianakis                 if (size == 0) {
117518a75281SManos Pitsidianakis                     /* break out of both loops */
117618a75281SManos Pitsidianakis                     available = 0;
117718a75281SManos Pitsidianakis                     break;
117818a75281SManos Pitsidianakis                 }
117918a75281SManos Pitsidianakis                 buffer->size -= size;
118018a75281SManos Pitsidianakis                 buffer->offset += size;
118118a75281SManos Pitsidianakis                 available -= size;
118218a75281SManos Pitsidianakis                 if (buffer->size < 1) {
118318a75281SManos Pitsidianakis                     return_tx_buffer(stream, buffer);
118418a75281SManos Pitsidianakis                     break;
118518a75281SManos Pitsidianakis                 }
118618a75281SManos Pitsidianakis                 if (!available) {
118718a75281SManos Pitsidianakis                     break;
118818a75281SManos Pitsidianakis                 }
118918a75281SManos Pitsidianakis             }
119018a75281SManos Pitsidianakis             if (!available) {
119118a75281SManos Pitsidianakis                 break;
119218a75281SManos Pitsidianakis             }
119318a75281SManos Pitsidianakis         }
119418a75281SManos Pitsidianakis     }
119518a75281SManos Pitsidianakis }
119618a75281SManos Pitsidianakis 
119718a75281SManos Pitsidianakis /*
1198d8d64acbSManos Pitsidianakis  * Flush all buffer data from this input stream's queue into the driver's
1199d8d64acbSManos Pitsidianakis  * virtual queue.
1200d8d64acbSManos Pitsidianakis  *
1201d8d64acbSManos Pitsidianakis  * @stream: VirtIOSoundPCMStream *stream
1202d8d64acbSManos Pitsidianakis  */
return_rx_buffer(VirtIOSoundPCMStream * stream,VirtIOSoundPCMBuffer * buffer)1203d8d64acbSManos Pitsidianakis static inline void return_rx_buffer(VirtIOSoundPCMStream *stream,
1204d8d64acbSManos Pitsidianakis                                     VirtIOSoundPCMBuffer *buffer)
1205d8d64acbSManos Pitsidianakis {
1206d8d64acbSManos Pitsidianakis     virtio_snd_pcm_status resp = { 0 };
1207d8d64acbSManos Pitsidianakis     resp.status = cpu_to_le32(VIRTIO_SND_S_OK);
1208d8d64acbSManos Pitsidianakis     resp.latency_bytes = 0;
1209d8d64acbSManos Pitsidianakis     /* Copy data -if any- to guest */
1210d8d64acbSManos Pitsidianakis     iov_from_buf(buffer->elem->in_sg,
1211d8d64acbSManos Pitsidianakis                  buffer->elem->in_num,
1212d8d64acbSManos Pitsidianakis                  0,
1213d8d64acbSManos Pitsidianakis                  buffer->data,
1214d8d64acbSManos Pitsidianakis                  buffer->size);
1215d8d64acbSManos Pitsidianakis     iov_from_buf(buffer->elem->in_sg,
1216d8d64acbSManos Pitsidianakis                  buffer->elem->in_num,
1217d8d64acbSManos Pitsidianakis                  buffer->size,
1218d8d64acbSManos Pitsidianakis                  &resp,
1219d8d64acbSManos Pitsidianakis                  sizeof(virtio_snd_pcm_status));
1220d8d64acbSManos Pitsidianakis     virtqueue_push(buffer->vq,
1221d8d64acbSManos Pitsidianakis                    buffer->elem,
1222d8d64acbSManos Pitsidianakis                    sizeof(virtio_snd_pcm_status) + buffer->size);
1223d8d64acbSManos Pitsidianakis     virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq);
1224d8d64acbSManos Pitsidianakis     QSIMPLEQ_REMOVE(&stream->queue,
1225d8d64acbSManos Pitsidianakis                     buffer,
1226d8d64acbSManos Pitsidianakis                     VirtIOSoundPCMBuffer,
1227d8d64acbSManos Pitsidianakis                     entry);
1228d8d64acbSManos Pitsidianakis     virtio_snd_pcm_buffer_free(buffer);
1229d8d64acbSManos Pitsidianakis }
1230d8d64acbSManos Pitsidianakis 
1231d8d64acbSManos Pitsidianakis 
1232d8d64acbSManos Pitsidianakis /*
1233d8d64acbSManos Pitsidianakis  * AUD_* input callback.
1234d8d64acbSManos Pitsidianakis  *
1235d8d64acbSManos Pitsidianakis  * @data: VirtIOSoundPCMStream stream
1236d8d64acbSManos Pitsidianakis  * @available: number of bytes that can be read with AUD_read()
1237d8d64acbSManos Pitsidianakis  */
virtio_snd_pcm_in_cb(void * data,int available)1238d8d64acbSManos Pitsidianakis static void virtio_snd_pcm_in_cb(void *data, int available)
1239d8d64acbSManos Pitsidianakis {
1240d8d64acbSManos Pitsidianakis     VirtIOSoundPCMStream *stream = data;
1241d8d64acbSManos Pitsidianakis     VirtIOSoundPCMBuffer *buffer;
124298e77e3dSManos Pitsidianakis     size_t size, max_size;
1243d8d64acbSManos Pitsidianakis 
1244d8d64acbSManos Pitsidianakis     WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
1245d8d64acbSManos Pitsidianakis         while (!QSIMPLEQ_EMPTY(&stream->queue)) {
1246d8d64acbSManos Pitsidianakis             buffer = QSIMPLEQ_FIRST(&stream->queue);
1247d8d64acbSManos Pitsidianakis             if (!virtio_queue_ready(buffer->vq)) {
1248d8d64acbSManos Pitsidianakis                 return;
1249d8d64acbSManos Pitsidianakis             }
1250d8d64acbSManos Pitsidianakis             if (!stream->active) {
1251d8d64acbSManos Pitsidianakis                 /* Stream has stopped, so do not perform AUD_read. */
1252d8d64acbSManos Pitsidianakis                 return_rx_buffer(stream, buffer);
1253d8d64acbSManos Pitsidianakis                 continue;
1254d8d64acbSManos Pitsidianakis             }
1255d8d64acbSManos Pitsidianakis 
125698e77e3dSManos Pitsidianakis             max_size = iov_size(buffer->elem->in_sg, buffer->elem->in_num);
1257d8d64acbSManos Pitsidianakis             for (;;) {
125898e77e3dSManos Pitsidianakis                 if (buffer->size >= max_size) {
125998e77e3dSManos Pitsidianakis                     return_rx_buffer(stream, buffer);
126098e77e3dSManos Pitsidianakis                     break;
126198e77e3dSManos Pitsidianakis                 }
1262d8d64acbSManos Pitsidianakis                 size = AUD_read(stream->voice.in,
1263d8d64acbSManos Pitsidianakis                         buffer->data + buffer->size,
1264d8d64acbSManos Pitsidianakis                         MIN(available, (stream->params.period_bytes -
1265d8d64acbSManos Pitsidianakis                                         buffer->size)));
1266d8d64acbSManos Pitsidianakis                 if (!size) {
1267d8d64acbSManos Pitsidianakis                     available = 0;
1268d8d64acbSManos Pitsidianakis                     break;
1269d8d64acbSManos Pitsidianakis                 }
1270d8d64acbSManos Pitsidianakis                 buffer->size += size;
1271d8d64acbSManos Pitsidianakis                 available -= size;
1272d8d64acbSManos Pitsidianakis                 if (buffer->size >= stream->params.period_bytes) {
1273d8d64acbSManos Pitsidianakis                     return_rx_buffer(stream, buffer);
1274d8d64acbSManos Pitsidianakis                     break;
1275d8d64acbSManos Pitsidianakis                 }
1276d8d64acbSManos Pitsidianakis                 if (!available) {
1277d8d64acbSManos Pitsidianakis                     break;
1278d8d64acbSManos Pitsidianakis                 }
1279d8d64acbSManos Pitsidianakis             }
1280d8d64acbSManos Pitsidianakis             if (!available) {
1281d8d64acbSManos Pitsidianakis                 break;
1282d8d64acbSManos Pitsidianakis             }
1283d8d64acbSManos Pitsidianakis         }
1284d8d64acbSManos Pitsidianakis     }
1285d8d64acbSManos Pitsidianakis }
1286d8d64acbSManos Pitsidianakis 
1287d8d64acbSManos Pitsidianakis /*
1288d8d64acbSManos Pitsidianakis  * Flush all buffer data from this output stream's queue into the driver's
1289d8d64acbSManos Pitsidianakis  * virtual queue.
129018a75281SManos Pitsidianakis  *
129118a75281SManos Pitsidianakis  * @stream: VirtIOSoundPCMStream *stream
129218a75281SManos Pitsidianakis  */
virtio_snd_pcm_flush(VirtIOSoundPCMStream * stream)129318a75281SManos Pitsidianakis static inline void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream)
129418a75281SManos Pitsidianakis {
129518a75281SManos Pitsidianakis     VirtIOSoundPCMBuffer *buffer;
1296d8d64acbSManos Pitsidianakis     void (*cb)(VirtIOSoundPCMStream *, VirtIOSoundPCMBuffer *) =
1297d8d64acbSManos Pitsidianakis         (stream->info.direction == VIRTIO_SND_D_OUTPUT) ? return_tx_buffer :
1298d8d64acbSManos Pitsidianakis         return_rx_buffer;
129918a75281SManos Pitsidianakis 
130018a75281SManos Pitsidianakis     WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
130118a75281SManos Pitsidianakis         while (!QSIMPLEQ_EMPTY(&stream->queue)) {
130218a75281SManos Pitsidianakis             buffer = QSIMPLEQ_FIRST(&stream->queue);
1303d8d64acbSManos Pitsidianakis             cb(stream, buffer);
130418a75281SManos Pitsidianakis         }
130518a75281SManos Pitsidianakis     }
130618a75281SManos Pitsidianakis }
130718a75281SManos Pitsidianakis 
virtio_snd_unrealize(DeviceState * dev)13082880e676SManos Pitsidianakis static void virtio_snd_unrealize(DeviceState *dev)
13092880e676SManos Pitsidianakis {
13102880e676SManos Pitsidianakis     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
13112880e676SManos Pitsidianakis     VirtIOSound *vsnd = VIRTIO_SND(dev);
1312eb9ad377SManos Pitsidianakis     VirtIOSoundPCMStream *stream;
13132880e676SManos Pitsidianakis 
13142880e676SManos Pitsidianakis     qemu_del_vm_change_state_handler(vsnd->vmstate);
13152880e676SManos Pitsidianakis     trace_virtio_snd_unrealize(vsnd);
13162880e676SManos Pitsidianakis 
1317eb9ad377SManos Pitsidianakis     if (vsnd->pcm) {
1318eb9ad377SManos Pitsidianakis         if (vsnd->pcm->streams) {
1319eb9ad377SManos Pitsidianakis             for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
1320eb9ad377SManos Pitsidianakis                 stream = vsnd->pcm->streams[i];
1321eb9ad377SManos Pitsidianakis                 if (stream) {
1322eb9ad377SManos Pitsidianakis                     virtio_snd_process_cmdq(stream->s);
1323eb9ad377SManos Pitsidianakis                     virtio_snd_pcm_close(stream);
132418a75281SManos Pitsidianakis                     qemu_mutex_destroy(&stream->queue_mutex);
1325eb9ad377SManos Pitsidianakis                     g_free(stream);
1326eb9ad377SManos Pitsidianakis                 }
1327eb9ad377SManos Pitsidianakis             }
1328eb9ad377SManos Pitsidianakis             g_free(vsnd->pcm->streams);
1329eb9ad377SManos Pitsidianakis         }
1330eb9ad377SManos Pitsidianakis         g_free(vsnd->pcm->pcm_params);
1331eb9ad377SManos Pitsidianakis         g_free(vsnd->pcm);
1332eb9ad377SManos Pitsidianakis         vsnd->pcm = NULL;
1333eb9ad377SManos Pitsidianakis     }
13342880e676SManos Pitsidianakis     AUD_remove_card(&vsnd->card);
1335eb9ad377SManos Pitsidianakis     qemu_mutex_destroy(&vsnd->cmdq_mutex);
13362880e676SManos Pitsidianakis     virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]);
13372880e676SManos Pitsidianakis     virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]);
13382880e676SManos Pitsidianakis     virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]);
13392880e676SManos Pitsidianakis     virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_RX]);
13402880e676SManos Pitsidianakis     virtio_cleanup(vdev);
13412880e676SManos Pitsidianakis }
13422880e676SManos Pitsidianakis 
13432880e676SManos Pitsidianakis 
virtio_snd_reset(VirtIODevice * vdev)1344eb9ad377SManos Pitsidianakis static void virtio_snd_reset(VirtIODevice *vdev)
1345eb9ad377SManos Pitsidianakis {
1346731655f8SManos Pitsidianakis     VirtIOSound *vsnd = VIRTIO_SND(vdev);
1347eb9ad377SManos Pitsidianakis     virtio_snd_ctrl_command *cmd;
1348eb9ad377SManos Pitsidianakis 
1349731655f8SManos Pitsidianakis     /*
1350731655f8SManos Pitsidianakis      * Sanity check that the invalid buffer message queue is emptied at the end
1351731655f8SManos Pitsidianakis      * of every virtio_snd_handle_tx_xfer/virtio_snd_handle_rx_xfer call, and
1352731655f8SManos Pitsidianakis      * must be empty otherwise.
1353731655f8SManos Pitsidianakis      */
1354731655f8SManos Pitsidianakis     g_assert(QSIMPLEQ_EMPTY(&vsnd->invalid));
1355731655f8SManos Pitsidianakis 
1356731655f8SManos Pitsidianakis     WITH_QEMU_LOCK_GUARD(&vsnd->cmdq_mutex) {
1357731655f8SManos Pitsidianakis         while (!QTAILQ_EMPTY(&vsnd->cmdq)) {
1358731655f8SManos Pitsidianakis             cmd = QTAILQ_FIRST(&vsnd->cmdq);
1359731655f8SManos Pitsidianakis             QTAILQ_REMOVE(&vsnd->cmdq, cmd, next);
1360eb9ad377SManos Pitsidianakis             virtio_snd_ctrl_cmd_free(cmd);
1361eb9ad377SManos Pitsidianakis         }
1362eb9ad377SManos Pitsidianakis     }
1363eb9ad377SManos Pitsidianakis }
13642880e676SManos Pitsidianakis 
virtio_snd_class_init(ObjectClass * klass,void * data)13652880e676SManos Pitsidianakis static void virtio_snd_class_init(ObjectClass *klass, void *data)
13662880e676SManos Pitsidianakis {
13672880e676SManos Pitsidianakis     DeviceClass *dc = DEVICE_CLASS(klass);
13682880e676SManos Pitsidianakis     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
13692880e676SManos Pitsidianakis 
13702880e676SManos Pitsidianakis 
13712880e676SManos Pitsidianakis     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
13722880e676SManos Pitsidianakis     device_class_set_props(dc, virtio_snd_properties);
13732880e676SManos Pitsidianakis 
13742880e676SManos Pitsidianakis     dc->vmsd = &vmstate_virtio_snd;
13752880e676SManos Pitsidianakis     vdc->vmsd = &vmstate_virtio_snd_device;
13762880e676SManos Pitsidianakis     vdc->realize = virtio_snd_realize;
13772880e676SManos Pitsidianakis     vdc->unrealize = virtio_snd_unrealize;
13782880e676SManos Pitsidianakis     vdc->get_config = virtio_snd_get_config;
13792880e676SManos Pitsidianakis     vdc->get_features = get_features;
13802880e676SManos Pitsidianakis     vdc->reset = virtio_snd_reset;
13812880e676SManos Pitsidianakis     vdc->legacy_features = 0;
13822880e676SManos Pitsidianakis }
13832880e676SManos Pitsidianakis 
13842880e676SManos Pitsidianakis static const TypeInfo virtio_snd_types[] = {
13852880e676SManos Pitsidianakis     {
13862880e676SManos Pitsidianakis       .name          = TYPE_VIRTIO_SND,
13872880e676SManos Pitsidianakis       .parent        = TYPE_VIRTIO_DEVICE,
13882880e676SManos Pitsidianakis       .instance_size = sizeof(VirtIOSound),
13892880e676SManos Pitsidianakis       .class_init    = virtio_snd_class_init,
13902880e676SManos Pitsidianakis     }
13912880e676SManos Pitsidianakis };
13922880e676SManos Pitsidianakis 
13932880e676SManos Pitsidianakis DEFINE_TYPES(virtio_snd_types)
1394