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