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"
2342508261SPhilippe Mathieu-Daudé #include "exec/tswap.h"
242880e676SManos Pitsidianakis #include "sysemu/runstate.h"
252880e676SManos Pitsidianakis #include "trace.h"
262880e676SManos Pitsidianakis #include "qapi/error.h"
272880e676SManos Pitsidianakis #include "hw/audio/virtio-snd.h"
282880e676SManos Pitsidianakis
292880e676SManos Pitsidianakis #define VIRTIO_SOUND_VM_VERSION 1
302880e676SManos Pitsidianakis #define VIRTIO_SOUND_JACK_DEFAULT 0
31d8d64acbSManos Pitsidianakis #define VIRTIO_SOUND_STREAM_DEFAULT 2
322880e676SManos Pitsidianakis #define VIRTIO_SOUND_CHMAP_DEFAULT 0
332880e676SManos Pitsidianakis #define VIRTIO_SOUND_HDA_FN_NID 0
342880e676SManos Pitsidianakis
3518a75281SManos Pitsidianakis static void virtio_snd_pcm_out_cb(void *data, int available);
3618a75281SManos Pitsidianakis static void virtio_snd_process_cmdq(VirtIOSound *s);
3718a75281SManos Pitsidianakis static void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream);
38d8d64acbSManos Pitsidianakis static void virtio_snd_pcm_in_cb(void *data, int available);
39f7856181SManos Pitsidianakis static void virtio_snd_unrealize(DeviceState *dev);
4018a75281SManos Pitsidianakis
41eb9ad377SManos Pitsidianakis static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8)
42eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_U8)
43eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_S16)
44eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_U16)
45eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_S32)
46eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_U32)
47eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_FLOAT);
48eb9ad377SManos Pitsidianakis
49eb9ad377SManos Pitsidianakis static uint32_t supported_rates = BIT(VIRTIO_SND_PCM_RATE_5512)
50eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_8000)
51eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_11025)
52eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_16000)
53eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_22050)
54eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_32000)
55eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_44100)
56eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_48000)
57eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_64000)
58eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_88200)
59eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_96000)
60eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_176400)
61eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_192000)
62eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_384000);
63eb9ad377SManos Pitsidianakis
642880e676SManos Pitsidianakis static const VMStateDescription vmstate_virtio_snd_device = {
652880e676SManos Pitsidianakis .name = TYPE_VIRTIO_SND,
662880e676SManos Pitsidianakis .version_id = VIRTIO_SOUND_VM_VERSION,
672880e676SManos Pitsidianakis .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
682880e676SManos Pitsidianakis };
692880e676SManos Pitsidianakis
702880e676SManos Pitsidianakis static const VMStateDescription vmstate_virtio_snd = {
712880e676SManos Pitsidianakis .name = TYPE_VIRTIO_SND,
72551ef0faSVolker Rümelin .unmigratable = 1,
732880e676SManos Pitsidianakis .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
742880e676SManos Pitsidianakis .version_id = VIRTIO_SOUND_VM_VERSION,
75856a6fe4SRichard Henderson .fields = (const VMStateField[]) {
762880e676SManos Pitsidianakis VMSTATE_VIRTIO_DEVICE,
772880e676SManos Pitsidianakis VMSTATE_END_OF_LIST()
782880e676SManos Pitsidianakis },
792880e676SManos Pitsidianakis };
802880e676SManos Pitsidianakis
812880e676SManos Pitsidianakis static Property virtio_snd_properties[] = {
822880e676SManos Pitsidianakis DEFINE_AUDIO_PROPERTIES(VirtIOSound, card),
832880e676SManos Pitsidianakis DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks,
842880e676SManos Pitsidianakis VIRTIO_SOUND_JACK_DEFAULT),
852880e676SManos Pitsidianakis DEFINE_PROP_UINT32("streams", VirtIOSound, snd_conf.streams,
862880e676SManos Pitsidianakis VIRTIO_SOUND_STREAM_DEFAULT),
872880e676SManos Pitsidianakis DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps,
882880e676SManos Pitsidianakis VIRTIO_SOUND_CHMAP_DEFAULT),
892880e676SManos Pitsidianakis DEFINE_PROP_END_OF_LIST(),
902880e676SManos Pitsidianakis };
912880e676SManos Pitsidianakis
922880e676SManos Pitsidianakis static void
virtio_snd_get_config(VirtIODevice * vdev,uint8_t * config)932880e676SManos Pitsidianakis virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config)
942880e676SManos Pitsidianakis {
952880e676SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev);
962880e676SManos Pitsidianakis virtio_snd_config *sndconfig =
972880e676SManos Pitsidianakis (virtio_snd_config *)config;
982880e676SManos Pitsidianakis trace_virtio_snd_get_config(vdev,
992880e676SManos Pitsidianakis s->snd_conf.jacks,
1002880e676SManos Pitsidianakis s->snd_conf.streams,
1012880e676SManos Pitsidianakis s->snd_conf.chmaps);
1022880e676SManos Pitsidianakis
1032880e676SManos Pitsidianakis memcpy(sndconfig, &s->snd_conf, sizeof(s->snd_conf));
1042880e676SManos Pitsidianakis cpu_to_le32s(&sndconfig->jacks);
1052880e676SManos Pitsidianakis cpu_to_le32s(&sndconfig->streams);
1062880e676SManos Pitsidianakis cpu_to_le32s(&sndconfig->chmaps);
1072880e676SManos Pitsidianakis
1082880e676SManos Pitsidianakis }
1092880e676SManos Pitsidianakis
1102880e676SManos Pitsidianakis static void
virtio_snd_set_config(VirtIODevice * vdev,const uint8_t * config)1112880e676SManos Pitsidianakis virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
1122880e676SManos Pitsidianakis {
1132880e676SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev);
1142880e676SManos Pitsidianakis const virtio_snd_config *sndconfig =
1152880e676SManos Pitsidianakis (const virtio_snd_config *)config;
1162880e676SManos Pitsidianakis
1172880e676SManos Pitsidianakis
1182880e676SManos Pitsidianakis trace_virtio_snd_set_config(vdev,
1192880e676SManos Pitsidianakis s->snd_conf.jacks,
1202880e676SManos Pitsidianakis sndconfig->jacks,
1212880e676SManos Pitsidianakis s->snd_conf.streams,
1222880e676SManos Pitsidianakis sndconfig->streams,
1232880e676SManos Pitsidianakis s->snd_conf.chmaps,
1242880e676SManos Pitsidianakis sndconfig->chmaps);
1252880e676SManos Pitsidianakis
1262880e676SManos Pitsidianakis memcpy(&s->snd_conf, sndconfig, sizeof(virtio_snd_config));
1272880e676SManos Pitsidianakis le32_to_cpus(&s->snd_conf.jacks);
1282880e676SManos Pitsidianakis le32_to_cpus(&s->snd_conf.streams);
1292880e676SManos Pitsidianakis le32_to_cpus(&s->snd_conf.chmaps);
1302880e676SManos Pitsidianakis
1312880e676SManos Pitsidianakis }
1322880e676SManos Pitsidianakis
133eb9ad377SManos Pitsidianakis static void
virtio_snd_pcm_buffer_free(VirtIOSoundPCMBuffer * buffer)13418a75281SManos Pitsidianakis virtio_snd_pcm_buffer_free(VirtIOSoundPCMBuffer *buffer)
13518a75281SManos Pitsidianakis {
13618a75281SManos Pitsidianakis g_free(buffer->elem);
13718a75281SManos Pitsidianakis g_free(buffer);
13818a75281SManos Pitsidianakis }
13918a75281SManos Pitsidianakis
14018a75281SManos Pitsidianakis static void
virtio_snd_ctrl_cmd_free(virtio_snd_ctrl_command * cmd)141eb9ad377SManos Pitsidianakis virtio_snd_ctrl_cmd_free(virtio_snd_ctrl_command *cmd)
142eb9ad377SManos Pitsidianakis {
143eb9ad377SManos Pitsidianakis g_free(cmd->elem);
144eb9ad377SManos Pitsidianakis g_free(cmd);
145eb9ad377SManos Pitsidianakis }
146eb9ad377SManos Pitsidianakis
1472880e676SManos Pitsidianakis /*
148eb9ad377SManos Pitsidianakis * Get a specific stream from the virtio sound card device.
149eb9ad377SManos Pitsidianakis * Returns NULL if @stream_id is invalid or not allocated.
150eb9ad377SManos Pitsidianakis *
151eb9ad377SManos Pitsidianakis * @s: VirtIOSound device
152eb9ad377SManos Pitsidianakis * @stream_id: stream id
153eb9ad377SManos Pitsidianakis */
virtio_snd_pcm_get_stream(VirtIOSound * s,uint32_t stream_id)154eb9ad377SManos Pitsidianakis static VirtIOSoundPCMStream *virtio_snd_pcm_get_stream(VirtIOSound *s,
155eb9ad377SManos Pitsidianakis uint32_t stream_id)
156eb9ad377SManos Pitsidianakis {
157eb9ad377SManos Pitsidianakis return stream_id >= s->snd_conf.streams ? NULL :
158eb9ad377SManos Pitsidianakis s->pcm->streams[stream_id];
159eb9ad377SManos Pitsidianakis }
160eb9ad377SManos Pitsidianakis
161eb9ad377SManos Pitsidianakis /*
162eb9ad377SManos Pitsidianakis * Get params for a specific stream.
163eb9ad377SManos Pitsidianakis *
164eb9ad377SManos Pitsidianakis * @s: VirtIOSound device
165eb9ad377SManos Pitsidianakis * @stream_id: stream id
166eb9ad377SManos Pitsidianakis */
virtio_snd_pcm_get_params(VirtIOSound * s,uint32_t stream_id)167eb9ad377SManos Pitsidianakis static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s,
168eb9ad377SManos Pitsidianakis uint32_t stream_id)
169eb9ad377SManos Pitsidianakis {
170eb9ad377SManos Pitsidianakis return stream_id >= s->snd_conf.streams ? NULL
171eb9ad377SManos Pitsidianakis : &s->pcm->pcm_params[stream_id];
172eb9ad377SManos Pitsidianakis }
173eb9ad377SManos Pitsidianakis
174eb9ad377SManos Pitsidianakis /*
1750ff05dd2SManos Pitsidianakis * Handle the VIRTIO_SND_R_PCM_INFO request.
1760ff05dd2SManos Pitsidianakis * The function writes the info structs to the request element.
1770ff05dd2SManos Pitsidianakis *
1780ff05dd2SManos Pitsidianakis * @s: VirtIOSound device
1790ff05dd2SManos Pitsidianakis * @cmd: The request command queue element from VirtIOSound cmdq field
1800ff05dd2SManos Pitsidianakis */
virtio_snd_handle_pcm_info(VirtIOSound * s,virtio_snd_ctrl_command * cmd)1810ff05dd2SManos Pitsidianakis static void virtio_snd_handle_pcm_info(VirtIOSound *s,
1820ff05dd2SManos Pitsidianakis virtio_snd_ctrl_command *cmd)
1830ff05dd2SManos Pitsidianakis {
1840ff05dd2SManos Pitsidianakis uint32_t stream_id, start_id, count, size;
1850ff05dd2SManos Pitsidianakis virtio_snd_pcm_info val;
1860ff05dd2SManos Pitsidianakis virtio_snd_query_info req;
1870ff05dd2SManos Pitsidianakis VirtIOSoundPCMStream *stream = NULL;
1880ff05dd2SManos Pitsidianakis g_autofree virtio_snd_pcm_info *pcm_info = NULL;
1890ff05dd2SManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
1900ff05dd2SManos Pitsidianakis cmd->elem->out_num,
1910ff05dd2SManos Pitsidianakis 0,
1920ff05dd2SManos Pitsidianakis &req,
1930ff05dd2SManos Pitsidianakis sizeof(virtio_snd_query_info));
1940ff05dd2SManos Pitsidianakis
1950ff05dd2SManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_query_info)) {
1960ff05dd2SManos Pitsidianakis /*
1970ff05dd2SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET?
1980ff05dd2SManos Pitsidianakis */
1990ff05dd2SManos Pitsidianakis qemu_log_mask(LOG_GUEST_ERROR,
2000ff05dd2SManos Pitsidianakis "%s: virtio-snd command size incorrect %zu vs \
2010ff05dd2SManos Pitsidianakis %zu\n", __func__, msg_sz, sizeof(virtio_snd_query_info));
2020ff05dd2SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
2030ff05dd2SManos Pitsidianakis return;
2040ff05dd2SManos Pitsidianakis }
2050ff05dd2SManos Pitsidianakis
2060ff05dd2SManos Pitsidianakis start_id = le32_to_cpu(req.start_id);
2070ff05dd2SManos Pitsidianakis count = le32_to_cpu(req.count);
2080ff05dd2SManos Pitsidianakis size = le32_to_cpu(req.size);
2090ff05dd2SManos Pitsidianakis
2100ff05dd2SManos Pitsidianakis if (iov_size(cmd->elem->in_sg, cmd->elem->in_num) <
2110ff05dd2SManos Pitsidianakis sizeof(virtio_snd_hdr) + size * count) {
2120ff05dd2SManos Pitsidianakis /*
2130ff05dd2SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET?
2140ff05dd2SManos Pitsidianakis */
2150ff05dd2SManos Pitsidianakis error_report("pcm info: buffer too small, got: %zu, needed: %zu",
2160ff05dd2SManos Pitsidianakis iov_size(cmd->elem->in_sg, cmd->elem->in_num),
2170ff05dd2SManos Pitsidianakis sizeof(virtio_snd_pcm_info));
2180ff05dd2SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
2190ff05dd2SManos Pitsidianakis return;
2200ff05dd2SManos Pitsidianakis }
2210ff05dd2SManos Pitsidianakis
2220ff05dd2SManos Pitsidianakis pcm_info = g_new0(virtio_snd_pcm_info, count);
2230ff05dd2SManos Pitsidianakis for (uint32_t i = 0; i < count; i++) {
2240ff05dd2SManos Pitsidianakis stream_id = i + start_id;
2250ff05dd2SManos Pitsidianakis trace_virtio_snd_handle_pcm_info(stream_id);
2260ff05dd2SManos Pitsidianakis stream = virtio_snd_pcm_get_stream(s, stream_id);
2270ff05dd2SManos Pitsidianakis if (!stream) {
2280ff05dd2SManos Pitsidianakis error_report("Invalid stream id: %"PRIu32, stream_id);
2290ff05dd2SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
2300ff05dd2SManos Pitsidianakis return;
2310ff05dd2SManos Pitsidianakis }
2320ff05dd2SManos Pitsidianakis val = stream->info;
2330ff05dd2SManos Pitsidianakis val.hdr.hda_fn_nid = cpu_to_le32(val.hdr.hda_fn_nid);
2340ff05dd2SManos Pitsidianakis val.features = cpu_to_le32(val.features);
2350ff05dd2SManos Pitsidianakis val.formats = cpu_to_le64(val.formats);
2360ff05dd2SManos Pitsidianakis val.rates = cpu_to_le64(val.rates);
2370ff05dd2SManos Pitsidianakis /*
2380ff05dd2SManos Pitsidianakis * 5.14.6.6.2.1 Device Requirements: Stream Information The device MUST
2390ff05dd2SManos Pitsidianakis * NOT set undefined feature, format, rate and direction values. The
2400ff05dd2SManos Pitsidianakis * device MUST initialize the padding bytes to 0.
2410ff05dd2SManos Pitsidianakis */
2420ff05dd2SManos Pitsidianakis pcm_info[i] = val;
2430ff05dd2SManos Pitsidianakis memset(&pcm_info[i].padding, 0, 5);
2440ff05dd2SManos Pitsidianakis }
2450ff05dd2SManos Pitsidianakis
246633487dfSVolker Rümelin cmd->payload_size = sizeof(virtio_snd_pcm_info) * count;
2470ff05dd2SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
2480ff05dd2SManos Pitsidianakis iov_from_buf(cmd->elem->in_sg,
2490ff05dd2SManos Pitsidianakis cmd->elem->in_num,
2500ff05dd2SManos Pitsidianakis sizeof(virtio_snd_hdr),
2510ff05dd2SManos Pitsidianakis pcm_info,
252633487dfSVolker Rümelin cmd->payload_size);
2530ff05dd2SManos Pitsidianakis }
2540ff05dd2SManos Pitsidianakis
2550ff05dd2SManos Pitsidianakis /*
256eb9ad377SManos Pitsidianakis * Set the given stream params.
257eb9ad377SManos Pitsidianakis * Called by both virtio_snd_handle_pcm_set_params and during device
258eb9ad377SManos Pitsidianakis * initialization.
259eb9ad377SManos Pitsidianakis * Returns the response status code. (VIRTIO_SND_S_*).
260eb9ad377SManos Pitsidianakis *
261eb9ad377SManos Pitsidianakis * @s: VirtIOSound device
262eb9ad377SManos Pitsidianakis * @params: The PCM params as defined in the virtio specification
263eb9ad377SManos Pitsidianakis */
264eb9ad377SManos Pitsidianakis static
virtio_snd_set_pcm_params(VirtIOSound * s,uint32_t stream_id,virtio_snd_pcm_set_params * params)265eb9ad377SManos Pitsidianakis uint32_t virtio_snd_set_pcm_params(VirtIOSound *s,
266eb9ad377SManos Pitsidianakis uint32_t stream_id,
267eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params *params)
268eb9ad377SManos Pitsidianakis {
269eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params *st_params;
270eb9ad377SManos Pitsidianakis
271eb9ad377SManos Pitsidianakis if (stream_id >= s->snd_conf.streams || s->pcm->pcm_params == NULL) {
272eb9ad377SManos Pitsidianakis /*
273eb9ad377SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET?
274eb9ad377SManos Pitsidianakis */
275eb9ad377SManos Pitsidianakis virtio_error(VIRTIO_DEVICE(s), "Streams have not been initialized.\n");
276eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
277eb9ad377SManos Pitsidianakis }
278eb9ad377SManos Pitsidianakis
279eb9ad377SManos Pitsidianakis st_params = virtio_snd_pcm_get_params(s, stream_id);
280eb9ad377SManos Pitsidianakis
281eb9ad377SManos Pitsidianakis if (params->channels < 1 || params->channels > AUDIO_MAX_CHANNELS) {
282eb9ad377SManos Pitsidianakis error_report("Number of channels is not supported.");
283eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
284eb9ad377SManos Pitsidianakis }
285eb9ad377SManos Pitsidianakis if (!(supported_formats & BIT(params->format))) {
286eb9ad377SManos Pitsidianakis error_report("Stream format is not supported.");
287eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
288eb9ad377SManos Pitsidianakis }
289eb9ad377SManos Pitsidianakis if (!(supported_rates & BIT(params->rate))) {
290eb9ad377SManos Pitsidianakis error_report("Stream rate is not supported.");
291eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
292eb9ad377SManos Pitsidianakis }
293eb9ad377SManos Pitsidianakis
294eb9ad377SManos Pitsidianakis st_params->buffer_bytes = le32_to_cpu(params->buffer_bytes);
295eb9ad377SManos Pitsidianakis st_params->period_bytes = le32_to_cpu(params->period_bytes);
296eb9ad377SManos Pitsidianakis st_params->features = le32_to_cpu(params->features);
297eb9ad377SManos Pitsidianakis /* the following are uint8_t, so there's no need to bswap the values. */
298eb9ad377SManos Pitsidianakis st_params->channels = params->channels;
299eb9ad377SManos Pitsidianakis st_params->format = params->format;
300eb9ad377SManos Pitsidianakis st_params->rate = params->rate;
301eb9ad377SManos Pitsidianakis
302eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_OK);
303eb9ad377SManos Pitsidianakis }
304eb9ad377SManos Pitsidianakis
305eb9ad377SManos Pitsidianakis /*
30664704ce0SManos Pitsidianakis * Handles the VIRTIO_SND_R_PCM_SET_PARAMS request.
30764704ce0SManos Pitsidianakis *
30864704ce0SManos Pitsidianakis * @s: VirtIOSound device
30964704ce0SManos Pitsidianakis * @cmd: The request command queue element from VirtIOSound cmdq field
31064704ce0SManos Pitsidianakis */
virtio_snd_handle_pcm_set_params(VirtIOSound * s,virtio_snd_ctrl_command * cmd)31164704ce0SManos Pitsidianakis static void virtio_snd_handle_pcm_set_params(VirtIOSound *s,
31264704ce0SManos Pitsidianakis virtio_snd_ctrl_command *cmd)
31364704ce0SManos Pitsidianakis {
31464704ce0SManos Pitsidianakis virtio_snd_pcm_set_params req = { 0 };
31564704ce0SManos Pitsidianakis uint32_t stream_id;
31664704ce0SManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
31764704ce0SManos Pitsidianakis cmd->elem->out_num,
31864704ce0SManos Pitsidianakis 0,
31964704ce0SManos Pitsidianakis &req,
32064704ce0SManos Pitsidianakis sizeof(virtio_snd_pcm_set_params));
32164704ce0SManos Pitsidianakis
32264704ce0SManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_pcm_set_params)) {
32364704ce0SManos Pitsidianakis /*
32464704ce0SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET?
32564704ce0SManos Pitsidianakis */
32664704ce0SManos Pitsidianakis qemu_log_mask(LOG_GUEST_ERROR,
32764704ce0SManos Pitsidianakis "%s: virtio-snd command size incorrect %zu vs \
32864704ce0SManos Pitsidianakis %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_set_params));
32964704ce0SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
33064704ce0SManos Pitsidianakis return;
33164704ce0SManos Pitsidianakis }
33264704ce0SManos Pitsidianakis stream_id = le32_to_cpu(req.hdr.stream_id);
33364704ce0SManos Pitsidianakis trace_virtio_snd_handle_pcm_set_params(stream_id);
33464704ce0SManos Pitsidianakis cmd->resp.code = virtio_snd_set_pcm_params(s, stream_id, &req);
33564704ce0SManos Pitsidianakis }
33664704ce0SManos Pitsidianakis
33764704ce0SManos Pitsidianakis /*
338eb9ad377SManos Pitsidianakis * Get a QEMU Audiosystem compatible format value from a VIRTIO_SND_PCM_FMT_*
339eb9ad377SManos Pitsidianakis */
virtio_snd_get_qemu_format(uint32_t format)340eb9ad377SManos Pitsidianakis static AudioFormat virtio_snd_get_qemu_format(uint32_t format)
341eb9ad377SManos Pitsidianakis {
342eb9ad377SManos Pitsidianakis #define CASE(FMT) \
343eb9ad377SManos Pitsidianakis case VIRTIO_SND_PCM_FMT_##FMT: \
344eb9ad377SManos Pitsidianakis return AUDIO_FORMAT_##FMT;
345eb9ad377SManos Pitsidianakis
346eb9ad377SManos Pitsidianakis switch (format) {
347eb9ad377SManos Pitsidianakis CASE(U8)
348eb9ad377SManos Pitsidianakis CASE(S8)
349eb9ad377SManos Pitsidianakis CASE(U16)
350eb9ad377SManos Pitsidianakis CASE(S16)
351eb9ad377SManos Pitsidianakis CASE(U32)
352eb9ad377SManos Pitsidianakis CASE(S32)
353eb9ad377SManos Pitsidianakis case VIRTIO_SND_PCM_FMT_FLOAT:
354eb9ad377SManos Pitsidianakis return AUDIO_FORMAT_F32;
355eb9ad377SManos Pitsidianakis default:
356eb9ad377SManos Pitsidianakis g_assert_not_reached();
357eb9ad377SManos Pitsidianakis }
358eb9ad377SManos Pitsidianakis
359eb9ad377SManos Pitsidianakis #undef CASE
360eb9ad377SManos Pitsidianakis }
361eb9ad377SManos Pitsidianakis
362eb9ad377SManos Pitsidianakis /*
363eb9ad377SManos Pitsidianakis * Get a QEMU Audiosystem compatible frequency value from a
364eb9ad377SManos Pitsidianakis * VIRTIO_SND_PCM_RATE_*
365eb9ad377SManos Pitsidianakis */
virtio_snd_get_qemu_freq(uint32_t rate)366eb9ad377SManos Pitsidianakis static uint32_t virtio_snd_get_qemu_freq(uint32_t rate)
367eb9ad377SManos Pitsidianakis {
368eb9ad377SManos Pitsidianakis #define CASE(RATE) \
369eb9ad377SManos Pitsidianakis case VIRTIO_SND_PCM_RATE_##RATE: \
370eb9ad377SManos Pitsidianakis return RATE;
371eb9ad377SManos Pitsidianakis
372eb9ad377SManos Pitsidianakis switch (rate) {
373eb9ad377SManos Pitsidianakis CASE(5512)
374eb9ad377SManos Pitsidianakis CASE(8000)
375eb9ad377SManos Pitsidianakis CASE(11025)
376eb9ad377SManos Pitsidianakis CASE(16000)
377eb9ad377SManos Pitsidianakis CASE(22050)
378eb9ad377SManos Pitsidianakis CASE(32000)
379eb9ad377SManos Pitsidianakis CASE(44100)
380eb9ad377SManos Pitsidianakis CASE(48000)
381eb9ad377SManos Pitsidianakis CASE(64000)
382eb9ad377SManos Pitsidianakis CASE(88200)
383eb9ad377SManos Pitsidianakis CASE(96000)
384eb9ad377SManos Pitsidianakis CASE(176400)
385eb9ad377SManos Pitsidianakis CASE(192000)
386eb9ad377SManos Pitsidianakis CASE(384000)
387eb9ad377SManos Pitsidianakis default:
388eb9ad377SManos Pitsidianakis g_assert_not_reached();
389eb9ad377SManos Pitsidianakis }
390eb9ad377SManos Pitsidianakis
391eb9ad377SManos Pitsidianakis #undef CASE
392eb9ad377SManos Pitsidianakis }
393eb9ad377SManos Pitsidianakis
394eb9ad377SManos Pitsidianakis /*
395eb9ad377SManos Pitsidianakis * Get QEMU Audiosystem compatible audsettings from virtio based pcm stream
396eb9ad377SManos Pitsidianakis * params.
397eb9ad377SManos Pitsidianakis */
virtio_snd_get_qemu_audsettings(audsettings * as,virtio_snd_pcm_set_params * params)398eb9ad377SManos Pitsidianakis static void virtio_snd_get_qemu_audsettings(audsettings *as,
399eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params *params)
400eb9ad377SManos Pitsidianakis {
401eb9ad377SManos Pitsidianakis as->nchannels = MIN(AUDIO_MAX_CHANNELS, params->channels);
402eb9ad377SManos Pitsidianakis as->fmt = virtio_snd_get_qemu_format(params->format);
403eb9ad377SManos Pitsidianakis as->freq = virtio_snd_get_qemu_freq(params->rate);
404eb9ad377SManos Pitsidianakis as->endianness = target_words_bigendian() ? 1 : 0;
405eb9ad377SManos Pitsidianakis }
406eb9ad377SManos Pitsidianakis
407eb9ad377SManos Pitsidianakis /*
408eb9ad377SManos Pitsidianakis * Close a stream and free all its resources.
409eb9ad377SManos Pitsidianakis *
410eb9ad377SManos Pitsidianakis * @stream: VirtIOSoundPCMStream *stream
411eb9ad377SManos Pitsidianakis */
virtio_snd_pcm_close(VirtIOSoundPCMStream * stream)412eb9ad377SManos Pitsidianakis static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream)
413eb9ad377SManos Pitsidianakis {
41418a75281SManos Pitsidianakis if (stream) {
41518a75281SManos Pitsidianakis virtio_snd_pcm_flush(stream);
416d8d64acbSManos Pitsidianakis if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
41718a75281SManos Pitsidianakis AUD_close_out(&stream->pcm->snd->card, stream->voice.out);
41818a75281SManos Pitsidianakis stream->voice.out = NULL;
419d8d64acbSManos Pitsidianakis } else if (stream->info.direction == VIRTIO_SND_D_INPUT) {
420d8d64acbSManos Pitsidianakis AUD_close_in(&stream->pcm->snd->card, stream->voice.in);
421d8d64acbSManos Pitsidianakis stream->voice.in = NULL;
42218a75281SManos Pitsidianakis }
42318a75281SManos Pitsidianakis }
424eb9ad377SManos Pitsidianakis }
425eb9ad377SManos Pitsidianakis
426eb9ad377SManos Pitsidianakis /*
427eb9ad377SManos Pitsidianakis * Prepares a VirtIOSound card stream.
428eb9ad377SManos Pitsidianakis * Returns the response status code. (VIRTIO_SND_S_*).
429eb9ad377SManos Pitsidianakis *
430eb9ad377SManos Pitsidianakis * @s: VirtIOSound device
431eb9ad377SManos Pitsidianakis * @stream_id: stream id
432eb9ad377SManos Pitsidianakis */
virtio_snd_pcm_prepare(VirtIOSound * s,uint32_t stream_id)433eb9ad377SManos Pitsidianakis static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
434eb9ad377SManos Pitsidianakis {
435eb9ad377SManos Pitsidianakis audsettings as;
436eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params *params;
437eb9ad377SManos Pitsidianakis VirtIOSoundPCMStream *stream;
438eb9ad377SManos Pitsidianakis
439eb9ad377SManos Pitsidianakis if (s->pcm->streams == NULL ||
440eb9ad377SManos Pitsidianakis s->pcm->pcm_params == NULL ||
441eb9ad377SManos Pitsidianakis stream_id >= s->snd_conf.streams) {
442eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
443eb9ad377SManos Pitsidianakis }
444eb9ad377SManos Pitsidianakis
445eb9ad377SManos Pitsidianakis params = virtio_snd_pcm_get_params(s, stream_id);
446eb9ad377SManos Pitsidianakis if (params == NULL) {
447eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
448eb9ad377SManos Pitsidianakis }
449eb9ad377SManos Pitsidianakis
450eb9ad377SManos Pitsidianakis stream = virtio_snd_pcm_get_stream(s, stream_id);
451eb9ad377SManos Pitsidianakis if (stream == NULL) {
452eb9ad377SManos Pitsidianakis stream = g_new0(VirtIOSoundPCMStream, 1);
453eb9ad377SManos Pitsidianakis stream->active = false;
454eb9ad377SManos Pitsidianakis stream->id = stream_id;
455eb9ad377SManos Pitsidianakis stream->pcm = s->pcm;
456eb9ad377SManos Pitsidianakis stream->s = s;
45718a75281SManos Pitsidianakis qemu_mutex_init(&stream->queue_mutex);
45818a75281SManos Pitsidianakis QSIMPLEQ_INIT(&stream->queue);
459eb9ad377SManos Pitsidianakis
460eb9ad377SManos Pitsidianakis /*
461eb9ad377SManos Pitsidianakis * stream_id >= s->snd_conf.streams was checked before so this is
462eb9ad377SManos Pitsidianakis * in-bounds
463eb9ad377SManos Pitsidianakis */
464eb9ad377SManos Pitsidianakis s->pcm->streams[stream_id] = stream;
465eb9ad377SManos Pitsidianakis }
466eb9ad377SManos Pitsidianakis
467eb9ad377SManos Pitsidianakis virtio_snd_get_qemu_audsettings(&as, params);
468eb9ad377SManos Pitsidianakis stream->info.direction = stream_id < s->snd_conf.streams / 2 +
469eb9ad377SManos Pitsidianakis (s->snd_conf.streams & 1) ? VIRTIO_SND_D_OUTPUT : VIRTIO_SND_D_INPUT;
470eb9ad377SManos Pitsidianakis stream->info.hdr.hda_fn_nid = VIRTIO_SOUND_HDA_FN_NID;
471eb9ad377SManos Pitsidianakis stream->info.features = 0;
472eb9ad377SManos Pitsidianakis stream->info.channels_min = 1;
473eb9ad377SManos Pitsidianakis stream->info.channels_max = as.nchannels;
474eb9ad377SManos Pitsidianakis stream->info.formats = supported_formats;
475eb9ad377SManos Pitsidianakis stream->info.rates = supported_rates;
476eb9ad377SManos Pitsidianakis stream->params = *params;
477eb9ad377SManos Pitsidianakis
478eb9ad377SManos Pitsidianakis stream->positions[0] = VIRTIO_SND_CHMAP_FL;
479eb9ad377SManos Pitsidianakis stream->positions[1] = VIRTIO_SND_CHMAP_FR;
480eb9ad377SManos Pitsidianakis stream->as = as;
481eb9ad377SManos Pitsidianakis
48218a75281SManos Pitsidianakis if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
48318a75281SManos Pitsidianakis stream->voice.out = AUD_open_out(&s->card,
48418a75281SManos Pitsidianakis stream->voice.out,
48518a75281SManos Pitsidianakis "virtio-sound.out",
48618a75281SManos Pitsidianakis stream,
48718a75281SManos Pitsidianakis virtio_snd_pcm_out_cb,
48818a75281SManos Pitsidianakis &as);
48918a75281SManos Pitsidianakis AUD_set_volume_out(stream->voice.out, 0, 255, 255);
49018a75281SManos Pitsidianakis } else {
491d8d64acbSManos Pitsidianakis stream->voice.in = AUD_open_in(&s->card,
492d8d64acbSManos Pitsidianakis stream->voice.in,
493d8d64acbSManos Pitsidianakis "virtio-sound.in",
494d8d64acbSManos Pitsidianakis stream,
495d8d64acbSManos Pitsidianakis virtio_snd_pcm_in_cb,
496d8d64acbSManos Pitsidianakis &as);
497d8d64acbSManos Pitsidianakis AUD_set_volume_in(stream->voice.in, 0, 255, 255);
49818a75281SManos Pitsidianakis }
49918a75281SManos Pitsidianakis
500eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_OK);
501eb9ad377SManos Pitsidianakis }
502eb9ad377SManos Pitsidianakis
print_code(uint32_t code)503eb9ad377SManos Pitsidianakis static const char *print_code(uint32_t code)
504eb9ad377SManos Pitsidianakis {
505eb9ad377SManos Pitsidianakis #define CASE(CODE) \
506eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_##CODE: \
507eb9ad377SManos Pitsidianakis return "VIRTIO_SND_R_"#CODE
508eb9ad377SManos Pitsidianakis
509eb9ad377SManos Pitsidianakis switch (code) {
510eb9ad377SManos Pitsidianakis CASE(JACK_INFO);
511eb9ad377SManos Pitsidianakis CASE(JACK_REMAP);
512eb9ad377SManos Pitsidianakis CASE(PCM_INFO);
513eb9ad377SManos Pitsidianakis CASE(PCM_SET_PARAMS);
514eb9ad377SManos Pitsidianakis CASE(PCM_PREPARE);
515eb9ad377SManos Pitsidianakis CASE(PCM_RELEASE);
516eb9ad377SManos Pitsidianakis CASE(PCM_START);
517eb9ad377SManos Pitsidianakis CASE(PCM_STOP);
518eb9ad377SManos Pitsidianakis CASE(CHMAP_INFO);
519eb9ad377SManos Pitsidianakis default:
520eb9ad377SManos Pitsidianakis return "invalid code";
521eb9ad377SManos Pitsidianakis }
522eb9ad377SManos Pitsidianakis
523eb9ad377SManos Pitsidianakis #undef CASE
524eb9ad377SManos Pitsidianakis };
525eb9ad377SManos Pitsidianakis
526eb9ad377SManos Pitsidianakis /*
527e5788b8fSManos Pitsidianakis * Handles VIRTIO_SND_R_PCM_PREPARE.
528e5788b8fSManos Pitsidianakis *
529e5788b8fSManos Pitsidianakis * @s: VirtIOSound device
530e5788b8fSManos Pitsidianakis * @cmd: The request command queue element from VirtIOSound cmdq field
531e5788b8fSManos Pitsidianakis */
virtio_snd_handle_pcm_prepare(VirtIOSound * s,virtio_snd_ctrl_command * cmd)532e5788b8fSManos Pitsidianakis static void virtio_snd_handle_pcm_prepare(VirtIOSound *s,
533e5788b8fSManos Pitsidianakis virtio_snd_ctrl_command *cmd)
534e5788b8fSManos Pitsidianakis {
535e5788b8fSManos Pitsidianakis uint32_t stream_id;
536e5788b8fSManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
537e5788b8fSManos Pitsidianakis cmd->elem->out_num,
538e5788b8fSManos Pitsidianakis sizeof(virtio_snd_hdr),
539e5788b8fSManos Pitsidianakis &stream_id,
540e5788b8fSManos Pitsidianakis sizeof(stream_id));
541e5788b8fSManos Pitsidianakis
542e5788b8fSManos Pitsidianakis stream_id = le32_to_cpu(stream_id);
543e5788b8fSManos Pitsidianakis cmd->resp.code = msg_sz == sizeof(stream_id)
544e5788b8fSManos Pitsidianakis ? virtio_snd_pcm_prepare(s, stream_id)
545e5788b8fSManos Pitsidianakis : cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
546e5788b8fSManos Pitsidianakis }
547e5788b8fSManos Pitsidianakis
548e5788b8fSManos Pitsidianakis /*
549fa131d4aSManos Pitsidianakis * Handles VIRTIO_SND_R_PCM_START.
550fa131d4aSManos Pitsidianakis *
551fa131d4aSManos Pitsidianakis * @s: VirtIOSound device
552fa131d4aSManos Pitsidianakis * @cmd: The request command queue element from VirtIOSound cmdq field
553fa131d4aSManos Pitsidianakis * @start: whether to start or stop the device
554fa131d4aSManos Pitsidianakis */
virtio_snd_handle_pcm_start_stop(VirtIOSound * s,virtio_snd_ctrl_command * cmd,bool start)555fa131d4aSManos Pitsidianakis static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
556fa131d4aSManos Pitsidianakis virtio_snd_ctrl_command *cmd,
557fa131d4aSManos Pitsidianakis bool start)
558fa131d4aSManos Pitsidianakis {
559fa131d4aSManos Pitsidianakis VirtIOSoundPCMStream *stream;
560fa131d4aSManos Pitsidianakis virtio_snd_pcm_hdr req;
561fa131d4aSManos Pitsidianakis uint32_t stream_id;
562fa131d4aSManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
563fa131d4aSManos Pitsidianakis cmd->elem->out_num,
564fa131d4aSManos Pitsidianakis 0,
565fa131d4aSManos Pitsidianakis &req,
566fa131d4aSManos Pitsidianakis sizeof(virtio_snd_pcm_hdr));
567fa131d4aSManos Pitsidianakis
568fa131d4aSManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_pcm_hdr)) {
569fa131d4aSManos Pitsidianakis qemu_log_mask(LOG_GUEST_ERROR,
570fa131d4aSManos Pitsidianakis "%s: virtio-snd command size incorrect %zu vs \
571fa131d4aSManos Pitsidianakis %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_hdr));
572fa131d4aSManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
573fa131d4aSManos Pitsidianakis return;
574fa131d4aSManos Pitsidianakis }
575fa131d4aSManos Pitsidianakis
576fa131d4aSManos Pitsidianakis stream_id = le32_to_cpu(req.stream_id);
577fa131d4aSManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
578fa131d4aSManos Pitsidianakis trace_virtio_snd_handle_pcm_start_stop(start ? "VIRTIO_SND_R_PCM_START" :
579fa131d4aSManos Pitsidianakis "VIRTIO_SND_R_PCM_STOP", stream_id);
58018a75281SManos Pitsidianakis
581fa131d4aSManos Pitsidianakis stream = virtio_snd_pcm_get_stream(s, stream_id);
58218a75281SManos Pitsidianakis if (stream) {
58318a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
58418a75281SManos Pitsidianakis stream->active = start;
58518a75281SManos Pitsidianakis }
58618a75281SManos Pitsidianakis if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
58718a75281SManos Pitsidianakis AUD_set_active_out(stream->voice.out, start);
588d8d64acbSManos Pitsidianakis } else {
589d8d64acbSManos Pitsidianakis AUD_set_active_in(stream->voice.in, start);
59018a75281SManos Pitsidianakis }
59118a75281SManos Pitsidianakis } else {
59218a75281SManos Pitsidianakis error_report("Invalid stream id: %"PRIu32, stream_id);
593fa131d4aSManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
594fa131d4aSManos Pitsidianakis return;
595fa131d4aSManos Pitsidianakis }
596fa131d4aSManos Pitsidianakis stream->active = start;
597fa131d4aSManos Pitsidianakis }
598fa131d4aSManos Pitsidianakis
599fa131d4aSManos Pitsidianakis /*
60018a75281SManos Pitsidianakis * Returns the number of I/O messages that are being processed.
60118a75281SManos Pitsidianakis *
60218a75281SManos Pitsidianakis * @stream: VirtIOSoundPCMStream
60318a75281SManos Pitsidianakis */
virtio_snd_pcm_get_io_msgs_count(VirtIOSoundPCMStream * stream)60418a75281SManos Pitsidianakis static size_t virtio_snd_pcm_get_io_msgs_count(VirtIOSoundPCMStream *stream)
60518a75281SManos Pitsidianakis {
60618a75281SManos Pitsidianakis VirtIOSoundPCMBuffer *buffer, *next;
60718a75281SManos Pitsidianakis size_t count = 0;
60818a75281SManos Pitsidianakis
60918a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
61018a75281SManos Pitsidianakis QSIMPLEQ_FOREACH_SAFE(buffer, &stream->queue, entry, next) {
61118a75281SManos Pitsidianakis count += 1;
61218a75281SManos Pitsidianakis }
61318a75281SManos Pitsidianakis }
61418a75281SManos Pitsidianakis return count;
61518a75281SManos Pitsidianakis }
61618a75281SManos Pitsidianakis
61718a75281SManos Pitsidianakis /*
61818a75281SManos Pitsidianakis * Handles VIRTIO_SND_R_PCM_RELEASE.
619d48800d7SManos Pitsidianakis *
620d48800d7SManos Pitsidianakis * @s: VirtIOSound device
621d48800d7SManos Pitsidianakis * @cmd: The request command queue element from VirtIOSound cmdq field
622d48800d7SManos Pitsidianakis */
virtio_snd_handle_pcm_release(VirtIOSound * s,virtio_snd_ctrl_command * cmd)623d48800d7SManos Pitsidianakis static void virtio_snd_handle_pcm_release(VirtIOSound *s,
624d48800d7SManos Pitsidianakis virtio_snd_ctrl_command *cmd)
625d48800d7SManos Pitsidianakis {
626d48800d7SManos Pitsidianakis uint32_t stream_id;
627d48800d7SManos Pitsidianakis VirtIOSoundPCMStream *stream;
628d48800d7SManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
629d48800d7SManos Pitsidianakis cmd->elem->out_num,
630d48800d7SManos Pitsidianakis sizeof(virtio_snd_hdr),
631d48800d7SManos Pitsidianakis &stream_id,
632d48800d7SManos Pitsidianakis sizeof(stream_id));
633d48800d7SManos Pitsidianakis
634d48800d7SManos Pitsidianakis if (msg_sz != sizeof(stream_id)) {
635d48800d7SManos Pitsidianakis /*
636d48800d7SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET?
637d48800d7SManos Pitsidianakis */
638d48800d7SManos Pitsidianakis qemu_log_mask(LOG_GUEST_ERROR,
639d48800d7SManos Pitsidianakis "%s: virtio-snd command size incorrect %zu vs \
640d48800d7SManos Pitsidianakis %zu\n", __func__, msg_sz, sizeof(stream_id));
641d48800d7SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
642d48800d7SManos Pitsidianakis return;
643d48800d7SManos Pitsidianakis }
644d48800d7SManos Pitsidianakis
645d48800d7SManos Pitsidianakis stream_id = le32_to_cpu(stream_id);
646d48800d7SManos Pitsidianakis trace_virtio_snd_handle_pcm_release(stream_id);
647d48800d7SManos Pitsidianakis stream = virtio_snd_pcm_get_stream(s, stream_id);
648d48800d7SManos Pitsidianakis if (stream == NULL) {
649d48800d7SManos Pitsidianakis /*
650d48800d7SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET?
651d48800d7SManos Pitsidianakis */
652d48800d7SManos Pitsidianakis error_report("already released stream %"PRIu32, stream_id);
653d48800d7SManos Pitsidianakis virtio_error(VIRTIO_DEVICE(s),
654d48800d7SManos Pitsidianakis "already released stream %"PRIu32,
655d48800d7SManos Pitsidianakis stream_id);
656d48800d7SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
657d48800d7SManos Pitsidianakis return;
658d48800d7SManos Pitsidianakis }
65918a75281SManos Pitsidianakis
66018a75281SManos Pitsidianakis if (virtio_snd_pcm_get_io_msgs_count(stream)) {
66118a75281SManos Pitsidianakis /*
66218a75281SManos Pitsidianakis * virtio-v1.2-csd01, 5.14.6.6.5.1,
66318a75281SManos Pitsidianakis * Device Requirements: Stream Release
66418a75281SManos Pitsidianakis *
66518a75281SManos Pitsidianakis * - The device MUST complete all pending I/O messages for the
66618a75281SManos Pitsidianakis * specified stream ID.
66718a75281SManos Pitsidianakis * - The device MUST NOT complete the control request while there
66818a75281SManos Pitsidianakis * are pending I/O messages for the specified stream ID.
66918a75281SManos Pitsidianakis */
67018a75281SManos Pitsidianakis trace_virtio_snd_pcm_stream_flush(stream_id);
67118a75281SManos Pitsidianakis virtio_snd_pcm_flush(stream);
67218a75281SManos Pitsidianakis }
67318a75281SManos Pitsidianakis
674d48800d7SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
675d48800d7SManos Pitsidianakis }
676d48800d7SManos Pitsidianakis
677d48800d7SManos Pitsidianakis /*
678eb9ad377SManos Pitsidianakis * The actual processing done in virtio_snd_process_cmdq().
679eb9ad377SManos Pitsidianakis *
680eb9ad377SManos Pitsidianakis * @s: VirtIOSound device
681eb9ad377SManos Pitsidianakis * @cmd: control command request
682eb9ad377SManos Pitsidianakis */
683eb9ad377SManos Pitsidianakis static inline void
process_cmd(VirtIOSound * s,virtio_snd_ctrl_command * cmd)684eb9ad377SManos Pitsidianakis process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd)
685eb9ad377SManos Pitsidianakis {
686eb9ad377SManos Pitsidianakis uint32_t code;
687eb9ad377SManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
688eb9ad377SManos Pitsidianakis cmd->elem->out_num,
689eb9ad377SManos Pitsidianakis 0,
690eb9ad377SManos Pitsidianakis &cmd->ctrl,
691eb9ad377SManos Pitsidianakis sizeof(virtio_snd_hdr));
692eb9ad377SManos Pitsidianakis
693eb9ad377SManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_hdr)) {
694eb9ad377SManos Pitsidianakis /*
695eb9ad377SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET?
696eb9ad377SManos Pitsidianakis */
697eb9ad377SManos Pitsidianakis qemu_log_mask(LOG_GUEST_ERROR,
698eb9ad377SManos Pitsidianakis "%s: virtio-snd command size incorrect %zu vs \
699eb9ad377SManos Pitsidianakis %zu\n", __func__, msg_sz, sizeof(virtio_snd_hdr));
700eb9ad377SManos Pitsidianakis return;
701eb9ad377SManos Pitsidianakis }
702eb9ad377SManos Pitsidianakis
703eb9ad377SManos Pitsidianakis code = le32_to_cpu(cmd->ctrl.code);
704eb9ad377SManos Pitsidianakis
705eb9ad377SManos Pitsidianakis trace_virtio_snd_handle_code(code, print_code(code));
706eb9ad377SManos Pitsidianakis
707eb9ad377SManos Pitsidianakis switch (code) {
708eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_JACK_INFO:
709eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_JACK_REMAP:
710eb9ad377SManos Pitsidianakis qemu_log_mask(LOG_UNIMP,
711eb9ad377SManos Pitsidianakis "virtio_snd: jack functionality is unimplemented.\n");
712eb9ad377SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
713eb9ad377SManos Pitsidianakis break;
714eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_INFO:
7150ff05dd2SManos Pitsidianakis virtio_snd_handle_pcm_info(s, cmd);
7160ff05dd2SManos Pitsidianakis break;
717fa131d4aSManos Pitsidianakis case VIRTIO_SND_R_PCM_START:
718fa131d4aSManos Pitsidianakis virtio_snd_handle_pcm_start_stop(s, cmd, true);
719fa131d4aSManos Pitsidianakis break;
720fa131d4aSManos Pitsidianakis case VIRTIO_SND_R_PCM_STOP:
721fa131d4aSManos Pitsidianakis virtio_snd_handle_pcm_start_stop(s, cmd, false);
722fa131d4aSManos Pitsidianakis break;
723eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_SET_PARAMS:
72464704ce0SManos Pitsidianakis virtio_snd_handle_pcm_set_params(s, cmd);
72564704ce0SManos Pitsidianakis break;
726eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_PREPARE:
727e5788b8fSManos Pitsidianakis virtio_snd_handle_pcm_prepare(s, cmd);
728e5788b8fSManos Pitsidianakis break;
729eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_RELEASE:
730d48800d7SManos Pitsidianakis virtio_snd_handle_pcm_release(s, cmd);
731eb9ad377SManos Pitsidianakis break;
732eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_CHMAP_INFO:
733eb9ad377SManos Pitsidianakis qemu_log_mask(LOG_UNIMP,
734eb9ad377SManos Pitsidianakis "virtio_snd: chmap info functionality is unimplemented.\n");
735eb9ad377SManos Pitsidianakis trace_virtio_snd_handle_chmap_info();
736eb9ad377SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
737eb9ad377SManos Pitsidianakis break;
738eb9ad377SManos Pitsidianakis default:
739eb9ad377SManos Pitsidianakis /* error */
740eb9ad377SManos Pitsidianakis error_report("virtio snd header not recognized: %"PRIu32, code);
741eb9ad377SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
742eb9ad377SManos Pitsidianakis }
743eb9ad377SManos Pitsidianakis
744eb9ad377SManos Pitsidianakis iov_from_buf(cmd->elem->in_sg,
745eb9ad377SManos Pitsidianakis cmd->elem->in_num,
746eb9ad377SManos Pitsidianakis 0,
747eb9ad377SManos Pitsidianakis &cmd->resp,
748eb9ad377SManos Pitsidianakis sizeof(virtio_snd_hdr));
749633487dfSVolker Rümelin virtqueue_push(cmd->vq, cmd->elem,
750633487dfSVolker Rümelin sizeof(virtio_snd_hdr) + cmd->payload_size);
751eb9ad377SManos Pitsidianakis virtio_notify(VIRTIO_DEVICE(s), cmd->vq);
752eb9ad377SManos Pitsidianakis }
753eb9ad377SManos Pitsidianakis
754eb9ad377SManos Pitsidianakis /*
755eb9ad377SManos Pitsidianakis * Consume all elements in command queue.
756eb9ad377SManos Pitsidianakis *
757eb9ad377SManos Pitsidianakis * @s: VirtIOSound device
758eb9ad377SManos Pitsidianakis */
virtio_snd_process_cmdq(VirtIOSound * s)759eb9ad377SManos Pitsidianakis static void virtio_snd_process_cmdq(VirtIOSound *s)
760eb9ad377SManos Pitsidianakis {
761eb9ad377SManos Pitsidianakis virtio_snd_ctrl_command *cmd;
762eb9ad377SManos Pitsidianakis
763eb9ad377SManos Pitsidianakis if (unlikely(qatomic_read(&s->processing_cmdq))) {
764eb9ad377SManos Pitsidianakis return;
765eb9ad377SManos Pitsidianakis }
766eb9ad377SManos Pitsidianakis
767eb9ad377SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&s->cmdq_mutex) {
768eb9ad377SManos Pitsidianakis qatomic_set(&s->processing_cmdq, true);
769eb9ad377SManos Pitsidianakis while (!QTAILQ_EMPTY(&s->cmdq)) {
770eb9ad377SManos Pitsidianakis cmd = QTAILQ_FIRST(&s->cmdq);
771eb9ad377SManos Pitsidianakis
772eb9ad377SManos Pitsidianakis /* process command */
773eb9ad377SManos Pitsidianakis process_cmd(s, cmd);
774eb9ad377SManos Pitsidianakis
775eb9ad377SManos Pitsidianakis QTAILQ_REMOVE(&s->cmdq, cmd, next);
776eb9ad377SManos Pitsidianakis
777eb9ad377SManos Pitsidianakis virtio_snd_ctrl_cmd_free(cmd);
778eb9ad377SManos Pitsidianakis }
779eb9ad377SManos Pitsidianakis qatomic_set(&s->processing_cmdq, false);
780eb9ad377SManos Pitsidianakis }
781eb9ad377SManos Pitsidianakis }
782eb9ad377SManos Pitsidianakis
783eb9ad377SManos Pitsidianakis /*
784eb9ad377SManos Pitsidianakis * The control message handler. Pops an element from the control virtqueue,
785eb9ad377SManos Pitsidianakis * and stores them to VirtIOSound's cmdq queue and finally calls
786eb9ad377SManos Pitsidianakis * virtio_snd_process_cmdq() for processing.
787eb9ad377SManos Pitsidianakis *
788eb9ad377SManos Pitsidianakis * @vdev: VirtIOSound device
789eb9ad377SManos Pitsidianakis * @vq: Control virtqueue
790eb9ad377SManos Pitsidianakis */
virtio_snd_handle_ctrl(VirtIODevice * vdev,VirtQueue * vq)791eb9ad377SManos Pitsidianakis static void virtio_snd_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
792eb9ad377SManos Pitsidianakis {
793eb9ad377SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev);
794eb9ad377SManos Pitsidianakis VirtQueueElement *elem;
795eb9ad377SManos Pitsidianakis virtio_snd_ctrl_command *cmd;
796eb9ad377SManos Pitsidianakis
797eb9ad377SManos Pitsidianakis trace_virtio_snd_handle_ctrl(vdev, vq);
798eb9ad377SManos Pitsidianakis
799eb9ad377SManos Pitsidianakis if (!virtio_queue_ready(vq)) {
800eb9ad377SManos Pitsidianakis return;
801eb9ad377SManos Pitsidianakis }
802eb9ad377SManos Pitsidianakis
803eb9ad377SManos Pitsidianakis elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
804eb9ad377SManos Pitsidianakis while (elem) {
805eb9ad377SManos Pitsidianakis cmd = g_new0(virtio_snd_ctrl_command, 1);
806eb9ad377SManos Pitsidianakis cmd->elem = elem;
807eb9ad377SManos Pitsidianakis cmd->vq = vq;
808eb9ad377SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
809633487dfSVolker Rümelin /* implicit cmd->payload_size = 0; */
810eb9ad377SManos Pitsidianakis QTAILQ_INSERT_TAIL(&s->cmdq, cmd, next);
811eb9ad377SManos Pitsidianakis elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
812eb9ad377SManos Pitsidianakis }
813eb9ad377SManos Pitsidianakis
814eb9ad377SManos Pitsidianakis virtio_snd_process_cmdq(s);
815eb9ad377SManos Pitsidianakis }
816eb9ad377SManos Pitsidianakis
817eb9ad377SManos Pitsidianakis /*
818eb9ad377SManos Pitsidianakis * The event virtqueue handler.
819eb9ad377SManos Pitsidianakis * Not implemented yet.
820eb9ad377SManos Pitsidianakis *
821eb9ad377SManos Pitsidianakis * @vdev: VirtIOSound device
822eb9ad377SManos Pitsidianakis * @vq: event vq
823eb9ad377SManos Pitsidianakis */
virtio_snd_handle_event(VirtIODevice * vdev,VirtQueue * vq)824eb9ad377SManos Pitsidianakis static void virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq)
825eb9ad377SManos Pitsidianakis {
826eb9ad377SManos Pitsidianakis qemu_log_mask(LOG_UNIMP, "virtio_snd: event queue is unimplemented.\n");
827eb9ad377SManos Pitsidianakis trace_virtio_snd_handle_event();
828eb9ad377SManos Pitsidianakis }
829eb9ad377SManos Pitsidianakis
830731655f8SManos Pitsidianakis /*
831731655f8SManos Pitsidianakis * Must only be called if vsnd->invalid is not empty.
832731655f8SManos Pitsidianakis */
empty_invalid_queue(VirtIODevice * vdev,VirtQueue * vq)833d8d64acbSManos Pitsidianakis static inline void empty_invalid_queue(VirtIODevice *vdev, VirtQueue *vq)
834d8d64acbSManos Pitsidianakis {
835d8d64acbSManos Pitsidianakis VirtIOSoundPCMBuffer *buffer = NULL;
836d8d64acbSManos Pitsidianakis virtio_snd_pcm_status resp = { 0 };
837d8d64acbSManos Pitsidianakis VirtIOSound *vsnd = VIRTIO_SND(vdev);
838d8d64acbSManos Pitsidianakis
839731655f8SManos Pitsidianakis g_assert(!QSIMPLEQ_EMPTY(&vsnd->invalid));
840731655f8SManos Pitsidianakis
841731655f8SManos Pitsidianakis while (!QSIMPLEQ_EMPTY(&vsnd->invalid)) {
842731655f8SManos Pitsidianakis buffer = QSIMPLEQ_FIRST(&vsnd->invalid);
843731655f8SManos Pitsidianakis /* If buffer->vq != vq, our logic is fundamentally wrong, so bail out */
844731655f8SManos Pitsidianakis g_assert(buffer->vq == vq);
845731655f8SManos Pitsidianakis
846d8d64acbSManos Pitsidianakis resp.status = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
847d8d64acbSManos Pitsidianakis iov_from_buf(buffer->elem->in_sg,
848d8d64acbSManos Pitsidianakis buffer->elem->in_num,
849d8d64acbSManos Pitsidianakis 0,
850d8d64acbSManos Pitsidianakis &resp,
851d8d64acbSManos Pitsidianakis sizeof(virtio_snd_pcm_status));
852d8d64acbSManos Pitsidianakis virtqueue_push(vq,
853d8d64acbSManos Pitsidianakis buffer->elem,
854d8d64acbSManos Pitsidianakis sizeof(virtio_snd_pcm_status));
855731655f8SManos Pitsidianakis QSIMPLEQ_REMOVE_HEAD(&vsnd->invalid, entry);
856d8d64acbSManos Pitsidianakis virtio_snd_pcm_buffer_free(buffer);
857d8d64acbSManos Pitsidianakis }
858731655f8SManos Pitsidianakis /* Notify vq about virtio_snd_pcm_status responses. */
859d8d64acbSManos Pitsidianakis virtio_notify(vdev, vq);
860d8d64acbSManos Pitsidianakis }
861d8d64acbSManos Pitsidianakis
862eb9ad377SManos Pitsidianakis /*
86318a75281SManos Pitsidianakis * The tx virtqueue handler. Makes the buffers available to their respective
86418a75281SManos Pitsidianakis * streams for consumption.
86518a75281SManos Pitsidianakis *
86618a75281SManos Pitsidianakis * @vdev: VirtIOSound device
86718a75281SManos Pitsidianakis * @vq: tx virtqueue
86818a75281SManos Pitsidianakis */
virtio_snd_handle_tx_xfer(VirtIODevice * vdev,VirtQueue * vq)869d8d64acbSManos Pitsidianakis static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq)
87018a75281SManos Pitsidianakis {
871731655f8SManos Pitsidianakis VirtIOSound *vsnd = VIRTIO_SND(vdev);
87218a75281SManos Pitsidianakis VirtIOSoundPCMBuffer *buffer;
87318a75281SManos Pitsidianakis VirtQueueElement *elem;
87418a75281SManos Pitsidianakis size_t msg_sz, size;
87518a75281SManos Pitsidianakis virtio_snd_pcm_xfer hdr;
87618a75281SManos Pitsidianakis uint32_t stream_id;
87718a75281SManos Pitsidianakis /*
878731655f8SManos Pitsidianakis * If any of the I/O messages are invalid, put them in vsnd->invalid and
87918a75281SManos Pitsidianakis * return them after the for loop.
88018a75281SManos Pitsidianakis */
88118a75281SManos Pitsidianakis bool must_empty_invalid_queue = false;
88218a75281SManos Pitsidianakis
88318a75281SManos Pitsidianakis if (!virtio_queue_ready(vq)) {
88418a75281SManos Pitsidianakis return;
88518a75281SManos Pitsidianakis }
886d8d64acbSManos Pitsidianakis trace_virtio_snd_handle_tx_xfer();
88718a75281SManos Pitsidianakis
888dcb0a1acSPhilippe Mathieu-Daudé for (;;) {
889dcb0a1acSPhilippe Mathieu-Daudé VirtIOSoundPCMStream *stream;
890dcb0a1acSPhilippe Mathieu-Daudé
89118a75281SManos Pitsidianakis elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
89218a75281SManos Pitsidianakis if (!elem) {
89318a75281SManos Pitsidianakis break;
89418a75281SManos Pitsidianakis }
89518a75281SManos Pitsidianakis /* get the message hdr object */
89618a75281SManos Pitsidianakis msg_sz = iov_to_buf(elem->out_sg,
89718a75281SManos Pitsidianakis elem->out_num,
89818a75281SManos Pitsidianakis 0,
89918a75281SManos Pitsidianakis &hdr,
90018a75281SManos Pitsidianakis sizeof(virtio_snd_pcm_xfer));
90118a75281SManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_pcm_xfer)) {
902731655f8SManos Pitsidianakis goto tx_err;
90318a75281SManos Pitsidianakis }
90418a75281SManos Pitsidianakis stream_id = le32_to_cpu(hdr.stream_id);
90518a75281SManos Pitsidianakis
906731655f8SManos Pitsidianakis if (stream_id >= vsnd->snd_conf.streams
907731655f8SManos Pitsidianakis || vsnd->pcm->streams[stream_id] == NULL) {
908731655f8SManos Pitsidianakis goto tx_err;
90918a75281SManos Pitsidianakis }
91018a75281SManos Pitsidianakis
911731655f8SManos Pitsidianakis stream = vsnd->pcm->streams[stream_id];
91218a75281SManos Pitsidianakis if (stream->info.direction != VIRTIO_SND_D_OUTPUT) {
91318a75281SManos Pitsidianakis goto tx_err;
91418a75281SManos Pitsidianakis }
91518a75281SManos Pitsidianakis
91618a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
91718a75281SManos Pitsidianakis size = iov_size(elem->out_sg, elem->out_num) - msg_sz;
91818a75281SManos Pitsidianakis
91918a75281SManos Pitsidianakis buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
92018a75281SManos Pitsidianakis buffer->elem = elem;
92118a75281SManos Pitsidianakis buffer->populated = false;
92218a75281SManos Pitsidianakis buffer->vq = vq;
92318a75281SManos Pitsidianakis buffer->size = size;
92418a75281SManos Pitsidianakis buffer->offset = 0;
92518a75281SManos Pitsidianakis
92618a75281SManos Pitsidianakis QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry);
92718a75281SManos Pitsidianakis }
92818a75281SManos Pitsidianakis continue;
92918a75281SManos Pitsidianakis
93018a75281SManos Pitsidianakis tx_err:
93118a75281SManos Pitsidianakis must_empty_invalid_queue = true;
93218a75281SManos Pitsidianakis buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer));
93318a75281SManos Pitsidianakis buffer->elem = elem;
93418a75281SManos Pitsidianakis buffer->vq = vq;
935731655f8SManos Pitsidianakis QSIMPLEQ_INSERT_TAIL(&vsnd->invalid, buffer, entry);
93618a75281SManos Pitsidianakis }
93718a75281SManos Pitsidianakis
93818a75281SManos Pitsidianakis if (must_empty_invalid_queue) {
939d8d64acbSManos Pitsidianakis empty_invalid_queue(vdev, vq);
94018a75281SManos Pitsidianakis }
94118a75281SManos Pitsidianakis }
94218a75281SManos Pitsidianakis
94318a75281SManos Pitsidianakis /*
944d8d64acbSManos Pitsidianakis * The rx virtqueue handler. Makes the buffers available to their respective
945d8d64acbSManos Pitsidianakis * streams for consumption.
9462880e676SManos Pitsidianakis *
9472880e676SManos Pitsidianakis * @vdev: VirtIOSound device
948d8d64acbSManos Pitsidianakis * @vq: rx virtqueue
9492880e676SManos Pitsidianakis */
virtio_snd_handle_rx_xfer(VirtIODevice * vdev,VirtQueue * vq)950d8d64acbSManos Pitsidianakis static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq)
951d8d64acbSManos Pitsidianakis {
952731655f8SManos Pitsidianakis VirtIOSound *vsnd = VIRTIO_SND(vdev);
953d8d64acbSManos Pitsidianakis VirtIOSoundPCMBuffer *buffer;
954d8d64acbSManos Pitsidianakis VirtQueueElement *elem;
955d8d64acbSManos Pitsidianakis size_t msg_sz, size;
956d8d64acbSManos Pitsidianakis virtio_snd_pcm_xfer hdr;
957d8d64acbSManos Pitsidianakis uint32_t stream_id;
958d8d64acbSManos Pitsidianakis /*
959731655f8SManos Pitsidianakis * if any of the I/O messages are invalid, put them in vsnd->invalid and
960d8d64acbSManos Pitsidianakis * return them after the for loop.
961d8d64acbSManos Pitsidianakis */
962d8d64acbSManos Pitsidianakis bool must_empty_invalid_queue = false;
963d8d64acbSManos Pitsidianakis
964d8d64acbSManos Pitsidianakis if (!virtio_queue_ready(vq)) {
965d8d64acbSManos Pitsidianakis return;
966d8d64acbSManos Pitsidianakis }
967d8d64acbSManos Pitsidianakis trace_virtio_snd_handle_rx_xfer();
968d8d64acbSManos Pitsidianakis
969dcb0a1acSPhilippe Mathieu-Daudé for (;;) {
970dcb0a1acSPhilippe Mathieu-Daudé VirtIOSoundPCMStream *stream;
971dcb0a1acSPhilippe Mathieu-Daudé
972d8d64acbSManos Pitsidianakis elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
973d8d64acbSManos Pitsidianakis if (!elem) {
974d8d64acbSManos Pitsidianakis break;
975d8d64acbSManos Pitsidianakis }
976d8d64acbSManos Pitsidianakis /* get the message hdr object */
977d8d64acbSManos Pitsidianakis msg_sz = iov_to_buf(elem->out_sg,
978d8d64acbSManos Pitsidianakis elem->out_num,
979d8d64acbSManos Pitsidianakis 0,
980d8d64acbSManos Pitsidianakis &hdr,
981d8d64acbSManos Pitsidianakis sizeof(virtio_snd_pcm_xfer));
982d8d64acbSManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_pcm_xfer)) {
983731655f8SManos Pitsidianakis goto rx_err;
984d8d64acbSManos Pitsidianakis }
985d8d64acbSManos Pitsidianakis stream_id = le32_to_cpu(hdr.stream_id);
986d8d64acbSManos Pitsidianakis
987731655f8SManos Pitsidianakis if (stream_id >= vsnd->snd_conf.streams
988731655f8SManos Pitsidianakis || !vsnd->pcm->streams[stream_id]) {
989731655f8SManos Pitsidianakis goto rx_err;
990d8d64acbSManos Pitsidianakis }
991d8d64acbSManos Pitsidianakis
992731655f8SManos Pitsidianakis stream = vsnd->pcm->streams[stream_id];
993d8d64acbSManos Pitsidianakis if (stream == NULL || stream->info.direction != VIRTIO_SND_D_INPUT) {
994d8d64acbSManos Pitsidianakis goto rx_err;
995d8d64acbSManos Pitsidianakis }
996d8d64acbSManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
997d8d64acbSManos Pitsidianakis size = iov_size(elem->in_sg, elem->in_num) -
998d8d64acbSManos Pitsidianakis sizeof(virtio_snd_pcm_status);
999d8d64acbSManos Pitsidianakis buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
1000d8d64acbSManos Pitsidianakis buffer->elem = elem;
1001d8d64acbSManos Pitsidianakis buffer->vq = vq;
1002d8d64acbSManos Pitsidianakis buffer->size = 0;
1003d8d64acbSManos Pitsidianakis buffer->offset = 0;
1004d8d64acbSManos Pitsidianakis QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry);
1005d8d64acbSManos Pitsidianakis }
1006d8d64acbSManos Pitsidianakis continue;
1007d8d64acbSManos Pitsidianakis
1008d8d64acbSManos Pitsidianakis rx_err:
1009d8d64acbSManos Pitsidianakis must_empty_invalid_queue = true;
1010d8d64acbSManos Pitsidianakis buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer));
1011d8d64acbSManos Pitsidianakis buffer->elem = elem;
1012d8d64acbSManos Pitsidianakis buffer->vq = vq;
1013731655f8SManos Pitsidianakis QSIMPLEQ_INSERT_TAIL(&vsnd->invalid, buffer, entry);
1014d8d64acbSManos Pitsidianakis }
1015d8d64acbSManos Pitsidianakis
1016d8d64acbSManos Pitsidianakis if (must_empty_invalid_queue) {
1017d8d64acbSManos Pitsidianakis empty_invalid_queue(vdev, vq);
1018d8d64acbSManos Pitsidianakis }
1019d8d64acbSManos Pitsidianakis }
10202880e676SManos Pitsidianakis
get_features(VirtIODevice * vdev,uint64_t features,Error ** errp)10212880e676SManos Pitsidianakis static uint64_t get_features(VirtIODevice *vdev, uint64_t features,
10222880e676SManos Pitsidianakis Error **errp)
10232880e676SManos Pitsidianakis {
10242880e676SManos Pitsidianakis /*
10252880e676SManos Pitsidianakis * virtio-v1.2-csd01, 5.14.3,
10262880e676SManos Pitsidianakis * Feature Bits
10272880e676SManos Pitsidianakis * None currently defined.
10282880e676SManos Pitsidianakis */
10292880e676SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev);
10302880e676SManos Pitsidianakis features |= s->features;
10312880e676SManos Pitsidianakis
10322880e676SManos Pitsidianakis trace_virtio_snd_get_features(vdev, features);
10332880e676SManos Pitsidianakis
10342880e676SManos Pitsidianakis return features;
10352880e676SManos Pitsidianakis }
10362880e676SManos Pitsidianakis
10372880e676SManos Pitsidianakis static void
virtio_snd_vm_state_change(void * opaque,bool running,RunState state)10382880e676SManos Pitsidianakis virtio_snd_vm_state_change(void *opaque, bool running,
10392880e676SManos Pitsidianakis RunState state)
10402880e676SManos Pitsidianakis {
10412880e676SManos Pitsidianakis if (running) {
10422880e676SManos Pitsidianakis trace_virtio_snd_vm_state_running();
10432880e676SManos Pitsidianakis } else {
10442880e676SManos Pitsidianakis trace_virtio_snd_vm_state_stopped();
10452880e676SManos Pitsidianakis }
10462880e676SManos Pitsidianakis }
10472880e676SManos Pitsidianakis
virtio_snd_realize(DeviceState * dev,Error ** errp)10482880e676SManos Pitsidianakis static void virtio_snd_realize(DeviceState *dev, Error **errp)
10492880e676SManos Pitsidianakis {
10502880e676SManos Pitsidianakis ERRP_GUARD();
10512880e676SManos Pitsidianakis VirtIOSound *vsnd = VIRTIO_SND(dev);
10522880e676SManos Pitsidianakis VirtIODevice *vdev = VIRTIO_DEVICE(dev);
1053eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params default_params = { 0 };
1054eb9ad377SManos Pitsidianakis uint32_t status;
10552880e676SManos Pitsidianakis
10562880e676SManos Pitsidianakis trace_virtio_snd_realize(vsnd);
10572880e676SManos Pitsidianakis
1058f7856181SManos Pitsidianakis /* check number of jacks and streams */
10592880e676SManos Pitsidianakis if (vsnd->snd_conf.jacks > 8) {
10602880e676SManos Pitsidianakis error_setg(errp,
10612880e676SManos Pitsidianakis "Invalid number of jacks: %"PRIu32,
10622880e676SManos Pitsidianakis vsnd->snd_conf.jacks);
10632880e676SManos Pitsidianakis return;
10642880e676SManos Pitsidianakis }
10652880e676SManos Pitsidianakis if (vsnd->snd_conf.streams < 1 || vsnd->snd_conf.streams > 10) {
10662880e676SManos Pitsidianakis error_setg(errp,
10672880e676SManos Pitsidianakis "Invalid number of streams: %"PRIu32,
10682880e676SManos Pitsidianakis vsnd->snd_conf.streams);
10692880e676SManos Pitsidianakis return;
10702880e676SManos Pitsidianakis }
10712880e676SManos Pitsidianakis
10722880e676SManos Pitsidianakis if (vsnd->snd_conf.chmaps > VIRTIO_SND_CHMAP_MAX_SIZE) {
10732880e676SManos Pitsidianakis error_setg(errp,
10742880e676SManos Pitsidianakis "Invalid number of channel maps: %"PRIu32,
10752880e676SManos Pitsidianakis vsnd->snd_conf.chmaps);
10762880e676SManos Pitsidianakis return;
10772880e676SManos Pitsidianakis }
10782880e676SManos Pitsidianakis
1079691d3d8bSManos Pitsidianakis if (!AUD_register_card("virtio-sound", &vsnd->card, errp)) {
1080691d3d8bSManos Pitsidianakis return;
1081691d3d8bSManos Pitsidianakis }
10822880e676SManos Pitsidianakis
1083f7856181SManos Pitsidianakis vsnd->vmstate =
1084f7856181SManos Pitsidianakis qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd);
1085f7856181SManos Pitsidianakis
1086f7856181SManos Pitsidianakis vsnd->pcm = g_new0(VirtIOSoundPCM, 1);
1087f7856181SManos Pitsidianakis vsnd->pcm->snd = vsnd;
1088f7856181SManos Pitsidianakis vsnd->pcm->streams =
1089f7856181SManos Pitsidianakis g_new0(VirtIOSoundPCMStream *, vsnd->snd_conf.streams);
1090f7856181SManos Pitsidianakis vsnd->pcm->pcm_params =
1091f7856181SManos Pitsidianakis g_new0(virtio_snd_pcm_set_params, vsnd->snd_conf.streams);
1092f7856181SManos Pitsidianakis
1093f7856181SManos Pitsidianakis virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config));
1094f7856181SManos Pitsidianakis virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1);
1095f7856181SManos Pitsidianakis
1096eb9ad377SManos Pitsidianakis /* set default params for all streams */
1097eb9ad377SManos Pitsidianakis default_params.features = 0;
1098eb9ad377SManos Pitsidianakis default_params.buffer_bytes = cpu_to_le32(8192);
1099eb9ad377SManos Pitsidianakis default_params.period_bytes = cpu_to_le32(2048);
1100eb9ad377SManos Pitsidianakis default_params.channels = 2;
1101eb9ad377SManos Pitsidianakis default_params.format = VIRTIO_SND_PCM_FMT_S16;
1102eb9ad377SManos Pitsidianakis default_params.rate = VIRTIO_SND_PCM_RATE_48000;
11032880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_CONTROL] =
1104eb9ad377SManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_ctrl);
11052880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_EVENT] =
1106eb9ad377SManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_event);
11072880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_TX] =
1108d8d64acbSManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_tx_xfer);
11092880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_RX] =
1110d8d64acbSManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_rx_xfer);
1111eb9ad377SManos Pitsidianakis qemu_mutex_init(&vsnd->cmdq_mutex);
1112eb9ad377SManos Pitsidianakis QTAILQ_INIT(&vsnd->cmdq);
1113731655f8SManos Pitsidianakis QSIMPLEQ_INIT(&vsnd->invalid);
1114eb9ad377SManos Pitsidianakis
1115eb9ad377SManos Pitsidianakis for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
1116eb9ad377SManos Pitsidianakis status = virtio_snd_set_pcm_params(vsnd, i, &default_params);
1117eb9ad377SManos Pitsidianakis if (status != cpu_to_le32(VIRTIO_SND_S_OK)) {
1118eb9ad377SManos Pitsidianakis error_setg(errp,
1119aaf851a2SMichael Tokarev "Can't initialize stream params, device responded with %s.",
1120eb9ad377SManos Pitsidianakis print_code(status));
1121f7856181SManos Pitsidianakis goto error_cleanup;
1122eb9ad377SManos Pitsidianakis }
1123eb9ad377SManos Pitsidianakis status = virtio_snd_pcm_prepare(vsnd, i);
1124eb9ad377SManos Pitsidianakis if (status != cpu_to_le32(VIRTIO_SND_S_OK)) {
1125eb9ad377SManos Pitsidianakis error_setg(errp,
1126eb9ad377SManos Pitsidianakis "Can't prepare streams, device responded with %s.",
1127eb9ad377SManos Pitsidianakis print_code(status));
1128f7856181SManos Pitsidianakis goto error_cleanup;
1129f7856181SManos Pitsidianakis }
1130f7856181SManos Pitsidianakis }
1131f7856181SManos Pitsidianakis
1132eb9ad377SManos Pitsidianakis return;
1133f7856181SManos Pitsidianakis
1134f7856181SManos Pitsidianakis error_cleanup:
1135f7856181SManos Pitsidianakis virtio_snd_unrealize(dev);
11362880e676SManos Pitsidianakis }
11372880e676SManos Pitsidianakis
return_tx_buffer(VirtIOSoundPCMStream * stream,VirtIOSoundPCMBuffer * buffer)113818a75281SManos Pitsidianakis static inline void return_tx_buffer(VirtIOSoundPCMStream *stream,
113918a75281SManos Pitsidianakis VirtIOSoundPCMBuffer *buffer)
114018a75281SManos Pitsidianakis {
114118a75281SManos Pitsidianakis virtio_snd_pcm_status resp = { 0 };
114218a75281SManos Pitsidianakis resp.status = cpu_to_le32(VIRTIO_SND_S_OK);
114318a75281SManos Pitsidianakis resp.latency_bytes = cpu_to_le32((uint32_t)buffer->size);
114418a75281SManos Pitsidianakis iov_from_buf(buffer->elem->in_sg,
114518a75281SManos Pitsidianakis buffer->elem->in_num,
114618a75281SManos Pitsidianakis 0,
114718a75281SManos Pitsidianakis &resp,
114818a75281SManos Pitsidianakis sizeof(virtio_snd_pcm_status));
114918a75281SManos Pitsidianakis virtqueue_push(buffer->vq,
115018a75281SManos Pitsidianakis buffer->elem,
115118a75281SManos Pitsidianakis sizeof(virtio_snd_pcm_status));
115218a75281SManos Pitsidianakis virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq);
115318a75281SManos Pitsidianakis QSIMPLEQ_REMOVE(&stream->queue,
115418a75281SManos Pitsidianakis buffer,
115518a75281SManos Pitsidianakis VirtIOSoundPCMBuffer,
115618a75281SManos Pitsidianakis entry);
115718a75281SManos Pitsidianakis virtio_snd_pcm_buffer_free(buffer);
115818a75281SManos Pitsidianakis }
115918a75281SManos Pitsidianakis
116018a75281SManos Pitsidianakis /*
116118a75281SManos Pitsidianakis * AUD_* output callback.
116218a75281SManos Pitsidianakis *
116318a75281SManos Pitsidianakis * @data: VirtIOSoundPCMStream stream
116418a75281SManos Pitsidianakis * @available: number of bytes that can be written with AUD_write()
116518a75281SManos Pitsidianakis */
virtio_snd_pcm_out_cb(void * data,int available)116618a75281SManos Pitsidianakis static void virtio_snd_pcm_out_cb(void *data, int available)
116718a75281SManos Pitsidianakis {
116818a75281SManos Pitsidianakis VirtIOSoundPCMStream *stream = data;
116918a75281SManos Pitsidianakis VirtIOSoundPCMBuffer *buffer;
117018a75281SManos Pitsidianakis size_t size;
117118a75281SManos Pitsidianakis
117218a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
117318a75281SManos Pitsidianakis while (!QSIMPLEQ_EMPTY(&stream->queue)) {
117418a75281SManos Pitsidianakis buffer = QSIMPLEQ_FIRST(&stream->queue);
117518a75281SManos Pitsidianakis if (!virtio_queue_ready(buffer->vq)) {
117618a75281SManos Pitsidianakis return;
117718a75281SManos Pitsidianakis }
117818a75281SManos Pitsidianakis if (!stream->active) {
117918a75281SManos Pitsidianakis /* Stream has stopped, so do not perform AUD_write. */
118018a75281SManos Pitsidianakis return_tx_buffer(stream, buffer);
118118a75281SManos Pitsidianakis continue;
118218a75281SManos Pitsidianakis }
118318a75281SManos Pitsidianakis if (!buffer->populated) {
118418a75281SManos Pitsidianakis iov_to_buf(buffer->elem->out_sg,
118518a75281SManos Pitsidianakis buffer->elem->out_num,
118618a75281SManos Pitsidianakis sizeof(virtio_snd_pcm_xfer),
118718a75281SManos Pitsidianakis buffer->data,
118818a75281SManos Pitsidianakis buffer->size);
118918a75281SManos Pitsidianakis buffer->populated = true;
119018a75281SManos Pitsidianakis }
119118a75281SManos Pitsidianakis for (;;) {
119218a75281SManos Pitsidianakis size = AUD_write(stream->voice.out,
119318a75281SManos Pitsidianakis buffer->data + buffer->offset,
119418a75281SManos Pitsidianakis MIN(buffer->size, available));
119518a75281SManos Pitsidianakis assert(size <= MIN(buffer->size, available));
119618a75281SManos Pitsidianakis if (size == 0) {
119718a75281SManos Pitsidianakis /* break out of both loops */
119818a75281SManos Pitsidianakis available = 0;
119918a75281SManos Pitsidianakis break;
120018a75281SManos Pitsidianakis }
120118a75281SManos Pitsidianakis buffer->size -= size;
120218a75281SManos Pitsidianakis buffer->offset += size;
120318a75281SManos Pitsidianakis available -= size;
120418a75281SManos Pitsidianakis if (buffer->size < 1) {
120518a75281SManos Pitsidianakis return_tx_buffer(stream, buffer);
120618a75281SManos Pitsidianakis break;
120718a75281SManos Pitsidianakis }
120818a75281SManos Pitsidianakis if (!available) {
120918a75281SManos Pitsidianakis break;
121018a75281SManos Pitsidianakis }
121118a75281SManos Pitsidianakis }
121218a75281SManos Pitsidianakis if (!available) {
121318a75281SManos Pitsidianakis break;
121418a75281SManos Pitsidianakis }
121518a75281SManos Pitsidianakis }
121618a75281SManos Pitsidianakis }
121718a75281SManos Pitsidianakis }
121818a75281SManos Pitsidianakis
121918a75281SManos Pitsidianakis /*
1220d8d64acbSManos Pitsidianakis * Flush all buffer data from this input stream's queue into the driver's
1221d8d64acbSManos Pitsidianakis * virtual queue.
1222d8d64acbSManos Pitsidianakis *
1223d8d64acbSManos Pitsidianakis * @stream: VirtIOSoundPCMStream *stream
1224d8d64acbSManos Pitsidianakis */
return_rx_buffer(VirtIOSoundPCMStream * stream,VirtIOSoundPCMBuffer * buffer)1225d8d64acbSManos Pitsidianakis static inline void return_rx_buffer(VirtIOSoundPCMStream *stream,
1226d8d64acbSManos Pitsidianakis VirtIOSoundPCMBuffer *buffer)
1227d8d64acbSManos Pitsidianakis {
1228d8d64acbSManos Pitsidianakis virtio_snd_pcm_status resp = { 0 };
1229d8d64acbSManos Pitsidianakis resp.status = cpu_to_le32(VIRTIO_SND_S_OK);
1230d8d64acbSManos Pitsidianakis resp.latency_bytes = 0;
1231d8d64acbSManos Pitsidianakis /* Copy data -if any- to guest */
1232d8d64acbSManos Pitsidianakis iov_from_buf(buffer->elem->in_sg,
1233d8d64acbSManos Pitsidianakis buffer->elem->in_num,
1234d8d64acbSManos Pitsidianakis 0,
1235d8d64acbSManos Pitsidianakis buffer->data,
1236d8d64acbSManos Pitsidianakis buffer->size);
1237d8d64acbSManos Pitsidianakis iov_from_buf(buffer->elem->in_sg,
1238d8d64acbSManos Pitsidianakis buffer->elem->in_num,
1239d8d64acbSManos Pitsidianakis buffer->size,
1240d8d64acbSManos Pitsidianakis &resp,
1241d8d64acbSManos Pitsidianakis sizeof(virtio_snd_pcm_status));
1242d8d64acbSManos Pitsidianakis virtqueue_push(buffer->vq,
1243d8d64acbSManos Pitsidianakis buffer->elem,
1244d8d64acbSManos Pitsidianakis sizeof(virtio_snd_pcm_status) + buffer->size);
1245d8d64acbSManos Pitsidianakis virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq);
1246d8d64acbSManos Pitsidianakis QSIMPLEQ_REMOVE(&stream->queue,
1247d8d64acbSManos Pitsidianakis buffer,
1248d8d64acbSManos Pitsidianakis VirtIOSoundPCMBuffer,
1249d8d64acbSManos Pitsidianakis entry);
1250d8d64acbSManos Pitsidianakis virtio_snd_pcm_buffer_free(buffer);
1251d8d64acbSManos Pitsidianakis }
1252d8d64acbSManos Pitsidianakis
1253d8d64acbSManos Pitsidianakis
1254d8d64acbSManos Pitsidianakis /*
1255d8d64acbSManos Pitsidianakis * AUD_* input callback.
1256d8d64acbSManos Pitsidianakis *
1257d8d64acbSManos Pitsidianakis * @data: VirtIOSoundPCMStream stream
1258d8d64acbSManos Pitsidianakis * @available: number of bytes that can be read with AUD_read()
1259d8d64acbSManos Pitsidianakis */
virtio_snd_pcm_in_cb(void * data,int available)1260d8d64acbSManos Pitsidianakis static void virtio_snd_pcm_in_cb(void *data, int available)
1261d8d64acbSManos Pitsidianakis {
1262d8d64acbSManos Pitsidianakis VirtIOSoundPCMStream *stream = data;
1263d8d64acbSManos Pitsidianakis VirtIOSoundPCMBuffer *buffer;
1264d8d64acbSManos Pitsidianakis size_t size;
1265d8d64acbSManos Pitsidianakis
1266d8d64acbSManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
1267d8d64acbSManos Pitsidianakis while (!QSIMPLEQ_EMPTY(&stream->queue)) {
1268d8d64acbSManos Pitsidianakis buffer = QSIMPLEQ_FIRST(&stream->queue);
1269d8d64acbSManos Pitsidianakis if (!virtio_queue_ready(buffer->vq)) {
1270d8d64acbSManos Pitsidianakis return;
1271d8d64acbSManos Pitsidianakis }
1272d8d64acbSManos Pitsidianakis if (!stream->active) {
1273d8d64acbSManos Pitsidianakis /* Stream has stopped, so do not perform AUD_read. */
1274d8d64acbSManos Pitsidianakis return_rx_buffer(stream, buffer);
1275d8d64acbSManos Pitsidianakis continue;
1276d8d64acbSManos Pitsidianakis }
1277d8d64acbSManos Pitsidianakis
1278d8d64acbSManos Pitsidianakis for (;;) {
1279d8d64acbSManos Pitsidianakis size = AUD_read(stream->voice.in,
1280d8d64acbSManos Pitsidianakis buffer->data + buffer->size,
1281d8d64acbSManos Pitsidianakis MIN(available, (stream->params.period_bytes -
1282d8d64acbSManos Pitsidianakis buffer->size)));
1283d8d64acbSManos Pitsidianakis if (!size) {
1284d8d64acbSManos Pitsidianakis available = 0;
1285d8d64acbSManos Pitsidianakis break;
1286d8d64acbSManos Pitsidianakis }
1287d8d64acbSManos Pitsidianakis buffer->size += size;
1288d8d64acbSManos Pitsidianakis available -= size;
1289d8d64acbSManos Pitsidianakis if (buffer->size >= stream->params.period_bytes) {
1290d8d64acbSManos Pitsidianakis return_rx_buffer(stream, buffer);
1291d8d64acbSManos Pitsidianakis break;
1292d8d64acbSManos Pitsidianakis }
1293d8d64acbSManos Pitsidianakis if (!available) {
1294d8d64acbSManos Pitsidianakis break;
1295d8d64acbSManos Pitsidianakis }
1296d8d64acbSManos Pitsidianakis }
1297d8d64acbSManos Pitsidianakis if (!available) {
1298d8d64acbSManos Pitsidianakis break;
1299d8d64acbSManos Pitsidianakis }
1300d8d64acbSManos Pitsidianakis }
1301d8d64acbSManos Pitsidianakis }
1302d8d64acbSManos Pitsidianakis }
1303d8d64acbSManos Pitsidianakis
1304d8d64acbSManos Pitsidianakis /*
1305d8d64acbSManos Pitsidianakis * Flush all buffer data from this output stream's queue into the driver's
1306d8d64acbSManos Pitsidianakis * virtual queue.
130718a75281SManos Pitsidianakis *
130818a75281SManos Pitsidianakis * @stream: VirtIOSoundPCMStream *stream
130918a75281SManos Pitsidianakis */
virtio_snd_pcm_flush(VirtIOSoundPCMStream * stream)131018a75281SManos Pitsidianakis static inline void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream)
131118a75281SManos Pitsidianakis {
131218a75281SManos Pitsidianakis VirtIOSoundPCMBuffer *buffer;
1313d8d64acbSManos Pitsidianakis void (*cb)(VirtIOSoundPCMStream *, VirtIOSoundPCMBuffer *) =
1314d8d64acbSManos Pitsidianakis (stream->info.direction == VIRTIO_SND_D_OUTPUT) ? return_tx_buffer :
1315d8d64acbSManos Pitsidianakis return_rx_buffer;
131618a75281SManos Pitsidianakis
131718a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
131818a75281SManos Pitsidianakis while (!QSIMPLEQ_EMPTY(&stream->queue)) {
131918a75281SManos Pitsidianakis buffer = QSIMPLEQ_FIRST(&stream->queue);
1320d8d64acbSManos Pitsidianakis cb(stream, buffer);
132118a75281SManos Pitsidianakis }
132218a75281SManos Pitsidianakis }
132318a75281SManos Pitsidianakis }
132418a75281SManos Pitsidianakis
virtio_snd_unrealize(DeviceState * dev)13252880e676SManos Pitsidianakis static void virtio_snd_unrealize(DeviceState *dev)
13262880e676SManos Pitsidianakis {
13272880e676SManos Pitsidianakis VirtIODevice *vdev = VIRTIO_DEVICE(dev);
13282880e676SManos Pitsidianakis VirtIOSound *vsnd = VIRTIO_SND(dev);
1329eb9ad377SManos Pitsidianakis VirtIOSoundPCMStream *stream;
13302880e676SManos Pitsidianakis
13312880e676SManos Pitsidianakis qemu_del_vm_change_state_handler(vsnd->vmstate);
13322880e676SManos Pitsidianakis trace_virtio_snd_unrealize(vsnd);
13332880e676SManos Pitsidianakis
1334eb9ad377SManos Pitsidianakis if (vsnd->pcm) {
1335eb9ad377SManos Pitsidianakis if (vsnd->pcm->streams) {
1336eb9ad377SManos Pitsidianakis for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
1337eb9ad377SManos Pitsidianakis stream = vsnd->pcm->streams[i];
1338eb9ad377SManos Pitsidianakis if (stream) {
1339eb9ad377SManos Pitsidianakis virtio_snd_process_cmdq(stream->s);
1340eb9ad377SManos Pitsidianakis virtio_snd_pcm_close(stream);
134118a75281SManos Pitsidianakis qemu_mutex_destroy(&stream->queue_mutex);
1342eb9ad377SManos Pitsidianakis g_free(stream);
1343eb9ad377SManos Pitsidianakis }
1344eb9ad377SManos Pitsidianakis }
1345eb9ad377SManos Pitsidianakis g_free(vsnd->pcm->streams);
1346eb9ad377SManos Pitsidianakis }
1347eb9ad377SManos Pitsidianakis g_free(vsnd->pcm->pcm_params);
1348eb9ad377SManos Pitsidianakis g_free(vsnd->pcm);
1349eb9ad377SManos Pitsidianakis vsnd->pcm = NULL;
1350eb9ad377SManos Pitsidianakis }
13512880e676SManos Pitsidianakis AUD_remove_card(&vsnd->card);
1352eb9ad377SManos Pitsidianakis qemu_mutex_destroy(&vsnd->cmdq_mutex);
13532880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]);
13542880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]);
13552880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]);
13562880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_RX]);
13572880e676SManos Pitsidianakis virtio_cleanup(vdev);
13582880e676SManos Pitsidianakis }
13592880e676SManos Pitsidianakis
13602880e676SManos Pitsidianakis
virtio_snd_reset(VirtIODevice * vdev)1361eb9ad377SManos Pitsidianakis static void virtio_snd_reset(VirtIODevice *vdev)
1362eb9ad377SManos Pitsidianakis {
1363731655f8SManos Pitsidianakis VirtIOSound *vsnd = VIRTIO_SND(vdev);
1364eb9ad377SManos Pitsidianakis virtio_snd_ctrl_command *cmd;
1365eb9ad377SManos Pitsidianakis
1366731655f8SManos Pitsidianakis /*
1367731655f8SManos Pitsidianakis * Sanity check that the invalid buffer message queue is emptied at the end
1368731655f8SManos Pitsidianakis * of every virtio_snd_handle_tx_xfer/virtio_snd_handle_rx_xfer call, and
1369731655f8SManos Pitsidianakis * must be empty otherwise.
1370731655f8SManos Pitsidianakis */
1371731655f8SManos Pitsidianakis g_assert(QSIMPLEQ_EMPTY(&vsnd->invalid));
1372731655f8SManos Pitsidianakis
1373731655f8SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&vsnd->cmdq_mutex) {
1374731655f8SManos Pitsidianakis while (!QTAILQ_EMPTY(&vsnd->cmdq)) {
1375731655f8SManos Pitsidianakis cmd = QTAILQ_FIRST(&vsnd->cmdq);
1376731655f8SManos Pitsidianakis QTAILQ_REMOVE(&vsnd->cmdq, cmd, next);
1377eb9ad377SManos Pitsidianakis virtio_snd_ctrl_cmd_free(cmd);
1378eb9ad377SManos Pitsidianakis }
1379eb9ad377SManos Pitsidianakis }
1380eb9ad377SManos Pitsidianakis }
13812880e676SManos Pitsidianakis
virtio_snd_class_init(ObjectClass * klass,void * data)13822880e676SManos Pitsidianakis static void virtio_snd_class_init(ObjectClass *klass, void *data)
13832880e676SManos Pitsidianakis {
13842880e676SManos Pitsidianakis DeviceClass *dc = DEVICE_CLASS(klass);
13852880e676SManos Pitsidianakis VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
13862880e676SManos Pitsidianakis
13872880e676SManos Pitsidianakis
13882880e676SManos Pitsidianakis set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
13892880e676SManos Pitsidianakis device_class_set_props(dc, virtio_snd_properties);
13902880e676SManos Pitsidianakis
13912880e676SManos Pitsidianakis dc->vmsd = &vmstate_virtio_snd;
13922880e676SManos Pitsidianakis vdc->vmsd = &vmstate_virtio_snd_device;
13932880e676SManos Pitsidianakis vdc->realize = virtio_snd_realize;
13942880e676SManos Pitsidianakis vdc->unrealize = virtio_snd_unrealize;
13952880e676SManos Pitsidianakis vdc->get_config = virtio_snd_get_config;
13962880e676SManos Pitsidianakis vdc->set_config = virtio_snd_set_config;
13972880e676SManos Pitsidianakis vdc->get_features = get_features;
13982880e676SManos Pitsidianakis vdc->reset = virtio_snd_reset;
13992880e676SManos Pitsidianakis vdc->legacy_features = 0;
14002880e676SManos Pitsidianakis }
14012880e676SManos Pitsidianakis
14022880e676SManos Pitsidianakis static const TypeInfo virtio_snd_types[] = {
14032880e676SManos Pitsidianakis {
14042880e676SManos Pitsidianakis .name = TYPE_VIRTIO_SND,
14052880e676SManos Pitsidianakis .parent = TYPE_VIRTIO_DEVICE,
14062880e676SManos Pitsidianakis .instance_size = sizeof(VirtIOSound),
14072880e676SManos Pitsidianakis .class_init = virtio_snd_class_init,
14082880e676SManos Pitsidianakis }
14092880e676SManos Pitsidianakis };
14102880e676SManos Pitsidianakis
14112880e676SManos Pitsidianakis DEFINE_TYPES(virtio_snd_types)
1412