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" 222880e676SManos Pitsidianakis #include "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 #include "hw/core/cpu.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); 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, 712880e676SManos Pitsidianakis .minimum_version_id = VIRTIO_SOUND_VM_VERSION, 722880e676SManos Pitsidianakis .version_id = VIRTIO_SOUND_VM_VERSION, 732880e676SManos Pitsidianakis .fields = (VMStateField[]) { 742880e676SManos Pitsidianakis VMSTATE_VIRTIO_DEVICE, 752880e676SManos Pitsidianakis VMSTATE_END_OF_LIST() 762880e676SManos Pitsidianakis }, 772880e676SManos Pitsidianakis }; 782880e676SManos Pitsidianakis 792880e676SManos Pitsidianakis static Property virtio_snd_properties[] = { 802880e676SManos Pitsidianakis DEFINE_AUDIO_PROPERTIES(VirtIOSound, card), 812880e676SManos Pitsidianakis DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks, 822880e676SManos Pitsidianakis VIRTIO_SOUND_JACK_DEFAULT), 832880e676SManos Pitsidianakis DEFINE_PROP_UINT32("streams", VirtIOSound, snd_conf.streams, 842880e676SManos Pitsidianakis VIRTIO_SOUND_STREAM_DEFAULT), 852880e676SManos Pitsidianakis DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps, 862880e676SManos Pitsidianakis VIRTIO_SOUND_CHMAP_DEFAULT), 872880e676SManos Pitsidianakis DEFINE_PROP_END_OF_LIST(), 882880e676SManos Pitsidianakis }; 892880e676SManos Pitsidianakis 902880e676SManos Pitsidianakis static void 912880e676SManos Pitsidianakis virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config) 922880e676SManos Pitsidianakis { 932880e676SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 942880e676SManos Pitsidianakis virtio_snd_config *sndconfig = 952880e676SManos Pitsidianakis (virtio_snd_config *)config; 962880e676SManos Pitsidianakis trace_virtio_snd_get_config(vdev, 972880e676SManos Pitsidianakis s->snd_conf.jacks, 982880e676SManos Pitsidianakis s->snd_conf.streams, 992880e676SManos Pitsidianakis s->snd_conf.chmaps); 1002880e676SManos Pitsidianakis 1012880e676SManos Pitsidianakis memcpy(sndconfig, &s->snd_conf, sizeof(s->snd_conf)); 1022880e676SManos Pitsidianakis cpu_to_le32s(&sndconfig->jacks); 1032880e676SManos Pitsidianakis cpu_to_le32s(&sndconfig->streams); 1042880e676SManos Pitsidianakis cpu_to_le32s(&sndconfig->chmaps); 1052880e676SManos Pitsidianakis 1062880e676SManos Pitsidianakis } 1072880e676SManos Pitsidianakis 1082880e676SManos Pitsidianakis static void 1092880e676SManos Pitsidianakis virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config) 1102880e676SManos Pitsidianakis { 1112880e676SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 1122880e676SManos Pitsidianakis const virtio_snd_config *sndconfig = 1132880e676SManos Pitsidianakis (const virtio_snd_config *)config; 1142880e676SManos Pitsidianakis 1152880e676SManos Pitsidianakis 1162880e676SManos Pitsidianakis trace_virtio_snd_set_config(vdev, 1172880e676SManos Pitsidianakis s->snd_conf.jacks, 1182880e676SManos Pitsidianakis sndconfig->jacks, 1192880e676SManos Pitsidianakis s->snd_conf.streams, 1202880e676SManos Pitsidianakis sndconfig->streams, 1212880e676SManos Pitsidianakis s->snd_conf.chmaps, 1222880e676SManos Pitsidianakis sndconfig->chmaps); 1232880e676SManos Pitsidianakis 1242880e676SManos Pitsidianakis memcpy(&s->snd_conf, sndconfig, sizeof(virtio_snd_config)); 1252880e676SManos Pitsidianakis le32_to_cpus(&s->snd_conf.jacks); 1262880e676SManos Pitsidianakis le32_to_cpus(&s->snd_conf.streams); 1272880e676SManos Pitsidianakis le32_to_cpus(&s->snd_conf.chmaps); 1282880e676SManos Pitsidianakis 1292880e676SManos Pitsidianakis } 1302880e676SManos Pitsidianakis 131eb9ad377SManos Pitsidianakis static void 13218a75281SManos Pitsidianakis virtio_snd_pcm_buffer_free(VirtIOSoundPCMBuffer *buffer) 13318a75281SManos Pitsidianakis { 13418a75281SManos Pitsidianakis g_free(buffer->elem); 13518a75281SManos Pitsidianakis g_free(buffer); 13618a75281SManos Pitsidianakis } 13718a75281SManos Pitsidianakis 13818a75281SManos Pitsidianakis static void 139eb9ad377SManos Pitsidianakis virtio_snd_ctrl_cmd_free(virtio_snd_ctrl_command *cmd) 140eb9ad377SManos Pitsidianakis { 141eb9ad377SManos Pitsidianakis g_free(cmd->elem); 142eb9ad377SManos Pitsidianakis g_free(cmd); 143eb9ad377SManos Pitsidianakis } 144eb9ad377SManos Pitsidianakis 1452880e676SManos Pitsidianakis /* 146eb9ad377SManos Pitsidianakis * Get a specific stream from the virtio sound card device. 147eb9ad377SManos Pitsidianakis * Returns NULL if @stream_id is invalid or not allocated. 148eb9ad377SManos Pitsidianakis * 149eb9ad377SManos Pitsidianakis * @s: VirtIOSound device 150eb9ad377SManos Pitsidianakis * @stream_id: stream id 151eb9ad377SManos Pitsidianakis */ 152eb9ad377SManos Pitsidianakis static VirtIOSoundPCMStream *virtio_snd_pcm_get_stream(VirtIOSound *s, 153eb9ad377SManos Pitsidianakis uint32_t stream_id) 154eb9ad377SManos Pitsidianakis { 155eb9ad377SManos Pitsidianakis return stream_id >= s->snd_conf.streams ? NULL : 156eb9ad377SManos Pitsidianakis s->pcm->streams[stream_id]; 157eb9ad377SManos Pitsidianakis } 158eb9ad377SManos Pitsidianakis 159eb9ad377SManos Pitsidianakis /* 160eb9ad377SManos Pitsidianakis * Get params for a specific stream. 161eb9ad377SManos Pitsidianakis * 162eb9ad377SManos Pitsidianakis * @s: VirtIOSound device 163eb9ad377SManos Pitsidianakis * @stream_id: stream id 164eb9ad377SManos Pitsidianakis */ 165eb9ad377SManos Pitsidianakis static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s, 166eb9ad377SManos Pitsidianakis uint32_t stream_id) 167eb9ad377SManos Pitsidianakis { 168eb9ad377SManos Pitsidianakis return stream_id >= s->snd_conf.streams ? NULL 169eb9ad377SManos Pitsidianakis : &s->pcm->pcm_params[stream_id]; 170eb9ad377SManos Pitsidianakis } 171eb9ad377SManos Pitsidianakis 172eb9ad377SManos Pitsidianakis /* 1730ff05dd2SManos Pitsidianakis * Handle the VIRTIO_SND_R_PCM_INFO request. 1740ff05dd2SManos Pitsidianakis * The function writes the info structs to the request element. 1750ff05dd2SManos Pitsidianakis * 1760ff05dd2SManos Pitsidianakis * @s: VirtIOSound device 1770ff05dd2SManos Pitsidianakis * @cmd: The request command queue element from VirtIOSound cmdq field 1780ff05dd2SManos Pitsidianakis */ 1790ff05dd2SManos Pitsidianakis static void virtio_snd_handle_pcm_info(VirtIOSound *s, 1800ff05dd2SManos Pitsidianakis virtio_snd_ctrl_command *cmd) 1810ff05dd2SManos Pitsidianakis { 1820ff05dd2SManos Pitsidianakis uint32_t stream_id, start_id, count, size; 1830ff05dd2SManos Pitsidianakis virtio_snd_pcm_info val; 1840ff05dd2SManos Pitsidianakis virtio_snd_query_info req; 1850ff05dd2SManos Pitsidianakis VirtIOSoundPCMStream *stream = NULL; 1860ff05dd2SManos Pitsidianakis g_autofree virtio_snd_pcm_info *pcm_info = NULL; 1870ff05dd2SManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg, 1880ff05dd2SManos Pitsidianakis cmd->elem->out_num, 1890ff05dd2SManos Pitsidianakis 0, 1900ff05dd2SManos Pitsidianakis &req, 1910ff05dd2SManos Pitsidianakis sizeof(virtio_snd_query_info)); 1920ff05dd2SManos Pitsidianakis 1930ff05dd2SManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_query_info)) { 1940ff05dd2SManos Pitsidianakis /* 1950ff05dd2SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 1960ff05dd2SManos Pitsidianakis */ 1970ff05dd2SManos Pitsidianakis qemu_log_mask(LOG_GUEST_ERROR, 1980ff05dd2SManos Pitsidianakis "%s: virtio-snd command size incorrect %zu vs \ 1990ff05dd2SManos Pitsidianakis %zu\n", __func__, msg_sz, sizeof(virtio_snd_query_info)); 2000ff05dd2SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 2010ff05dd2SManos Pitsidianakis return; 2020ff05dd2SManos Pitsidianakis } 2030ff05dd2SManos Pitsidianakis 2040ff05dd2SManos Pitsidianakis start_id = le32_to_cpu(req.start_id); 2050ff05dd2SManos Pitsidianakis count = le32_to_cpu(req.count); 2060ff05dd2SManos Pitsidianakis size = le32_to_cpu(req.size); 2070ff05dd2SManos Pitsidianakis 2080ff05dd2SManos Pitsidianakis if (iov_size(cmd->elem->in_sg, cmd->elem->in_num) < 2090ff05dd2SManos Pitsidianakis sizeof(virtio_snd_hdr) + size * count) { 2100ff05dd2SManos Pitsidianakis /* 2110ff05dd2SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 2120ff05dd2SManos Pitsidianakis */ 2130ff05dd2SManos Pitsidianakis error_report("pcm info: buffer too small, got: %zu, needed: %zu", 2140ff05dd2SManos Pitsidianakis iov_size(cmd->elem->in_sg, cmd->elem->in_num), 2150ff05dd2SManos Pitsidianakis sizeof(virtio_snd_pcm_info)); 2160ff05dd2SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 2170ff05dd2SManos Pitsidianakis return; 2180ff05dd2SManos Pitsidianakis } 2190ff05dd2SManos Pitsidianakis 2200ff05dd2SManos Pitsidianakis pcm_info = g_new0(virtio_snd_pcm_info, count); 2210ff05dd2SManos Pitsidianakis for (uint32_t i = 0; i < count; i++) { 2220ff05dd2SManos Pitsidianakis stream_id = i + start_id; 2230ff05dd2SManos Pitsidianakis trace_virtio_snd_handle_pcm_info(stream_id); 2240ff05dd2SManos Pitsidianakis stream = virtio_snd_pcm_get_stream(s, stream_id); 2250ff05dd2SManos Pitsidianakis if (!stream) { 2260ff05dd2SManos Pitsidianakis error_report("Invalid stream id: %"PRIu32, stream_id); 2270ff05dd2SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 2280ff05dd2SManos Pitsidianakis return; 2290ff05dd2SManos Pitsidianakis } 2300ff05dd2SManos Pitsidianakis val = stream->info; 2310ff05dd2SManos Pitsidianakis val.hdr.hda_fn_nid = cpu_to_le32(val.hdr.hda_fn_nid); 2320ff05dd2SManos Pitsidianakis val.features = cpu_to_le32(val.features); 2330ff05dd2SManos Pitsidianakis val.formats = cpu_to_le64(val.formats); 2340ff05dd2SManos Pitsidianakis val.rates = cpu_to_le64(val.rates); 2350ff05dd2SManos Pitsidianakis /* 2360ff05dd2SManos Pitsidianakis * 5.14.6.6.2.1 Device Requirements: Stream Information The device MUST 2370ff05dd2SManos Pitsidianakis * NOT set undefined feature, format, rate and direction values. The 2380ff05dd2SManos Pitsidianakis * device MUST initialize the padding bytes to 0. 2390ff05dd2SManos Pitsidianakis */ 2400ff05dd2SManos Pitsidianakis pcm_info[i] = val; 2410ff05dd2SManos Pitsidianakis memset(&pcm_info[i].padding, 0, 5); 2420ff05dd2SManos Pitsidianakis } 2430ff05dd2SManos Pitsidianakis 2440ff05dd2SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); 2450ff05dd2SManos Pitsidianakis iov_from_buf(cmd->elem->in_sg, 2460ff05dd2SManos Pitsidianakis cmd->elem->in_num, 2470ff05dd2SManos Pitsidianakis sizeof(virtio_snd_hdr), 2480ff05dd2SManos Pitsidianakis pcm_info, 2490ff05dd2SManos Pitsidianakis sizeof(virtio_snd_pcm_info) * count); 2500ff05dd2SManos Pitsidianakis } 2510ff05dd2SManos Pitsidianakis 2520ff05dd2SManos Pitsidianakis /* 253eb9ad377SManos Pitsidianakis * Set the given stream params. 254eb9ad377SManos Pitsidianakis * Called by both virtio_snd_handle_pcm_set_params and during device 255eb9ad377SManos Pitsidianakis * initialization. 256eb9ad377SManos Pitsidianakis * Returns the response status code. (VIRTIO_SND_S_*). 257eb9ad377SManos Pitsidianakis * 258eb9ad377SManos Pitsidianakis * @s: VirtIOSound device 259eb9ad377SManos Pitsidianakis * @params: The PCM params as defined in the virtio specification 260eb9ad377SManos Pitsidianakis */ 261eb9ad377SManos Pitsidianakis static 262eb9ad377SManos Pitsidianakis uint32_t virtio_snd_set_pcm_params(VirtIOSound *s, 263eb9ad377SManos Pitsidianakis uint32_t stream_id, 264eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params *params) 265eb9ad377SManos Pitsidianakis { 266eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params *st_params; 267eb9ad377SManos Pitsidianakis 268eb9ad377SManos Pitsidianakis if (stream_id >= s->snd_conf.streams || s->pcm->pcm_params == NULL) { 269eb9ad377SManos Pitsidianakis /* 270eb9ad377SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 271eb9ad377SManos Pitsidianakis */ 272eb9ad377SManos Pitsidianakis virtio_error(VIRTIO_DEVICE(s), "Streams have not been initialized.\n"); 273eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 274eb9ad377SManos Pitsidianakis } 275eb9ad377SManos Pitsidianakis 276eb9ad377SManos Pitsidianakis st_params = virtio_snd_pcm_get_params(s, stream_id); 277eb9ad377SManos Pitsidianakis 278eb9ad377SManos Pitsidianakis if (params->channels < 1 || params->channels > AUDIO_MAX_CHANNELS) { 279eb9ad377SManos Pitsidianakis error_report("Number of channels is not supported."); 280eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); 281eb9ad377SManos Pitsidianakis } 282eb9ad377SManos Pitsidianakis if (!(supported_formats & BIT(params->format))) { 283eb9ad377SManos Pitsidianakis error_report("Stream format is not supported."); 284eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); 285eb9ad377SManos Pitsidianakis } 286eb9ad377SManos Pitsidianakis if (!(supported_rates & BIT(params->rate))) { 287eb9ad377SManos Pitsidianakis error_report("Stream rate is not supported."); 288eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); 289eb9ad377SManos Pitsidianakis } 290eb9ad377SManos Pitsidianakis 291eb9ad377SManos Pitsidianakis st_params->buffer_bytes = le32_to_cpu(params->buffer_bytes); 292eb9ad377SManos Pitsidianakis st_params->period_bytes = le32_to_cpu(params->period_bytes); 293eb9ad377SManos Pitsidianakis st_params->features = le32_to_cpu(params->features); 294eb9ad377SManos Pitsidianakis /* the following are uint8_t, so there's no need to bswap the values. */ 295eb9ad377SManos Pitsidianakis st_params->channels = params->channels; 296eb9ad377SManos Pitsidianakis st_params->format = params->format; 297eb9ad377SManos Pitsidianakis st_params->rate = params->rate; 298eb9ad377SManos Pitsidianakis 299eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_OK); 300eb9ad377SManos Pitsidianakis } 301eb9ad377SManos Pitsidianakis 302eb9ad377SManos Pitsidianakis /* 30364704ce0SManos Pitsidianakis * Handles the VIRTIO_SND_R_PCM_SET_PARAMS request. 30464704ce0SManos Pitsidianakis * 30564704ce0SManos Pitsidianakis * @s: VirtIOSound device 30664704ce0SManos Pitsidianakis * @cmd: The request command queue element from VirtIOSound cmdq field 30764704ce0SManos Pitsidianakis */ 30864704ce0SManos Pitsidianakis static void virtio_snd_handle_pcm_set_params(VirtIOSound *s, 30964704ce0SManos Pitsidianakis virtio_snd_ctrl_command *cmd) 31064704ce0SManos Pitsidianakis { 31164704ce0SManos Pitsidianakis virtio_snd_pcm_set_params req = { 0 }; 31264704ce0SManos Pitsidianakis uint32_t stream_id; 31364704ce0SManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg, 31464704ce0SManos Pitsidianakis cmd->elem->out_num, 31564704ce0SManos Pitsidianakis 0, 31664704ce0SManos Pitsidianakis &req, 31764704ce0SManos Pitsidianakis sizeof(virtio_snd_pcm_set_params)); 31864704ce0SManos Pitsidianakis 31964704ce0SManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_pcm_set_params)) { 32064704ce0SManos Pitsidianakis /* 32164704ce0SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 32264704ce0SManos Pitsidianakis */ 32364704ce0SManos Pitsidianakis qemu_log_mask(LOG_GUEST_ERROR, 32464704ce0SManos Pitsidianakis "%s: virtio-snd command size incorrect %zu vs \ 32564704ce0SManos Pitsidianakis %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_set_params)); 32664704ce0SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 32764704ce0SManos Pitsidianakis return; 32864704ce0SManos Pitsidianakis } 32964704ce0SManos Pitsidianakis stream_id = le32_to_cpu(req.hdr.stream_id); 33064704ce0SManos Pitsidianakis trace_virtio_snd_handle_pcm_set_params(stream_id); 33164704ce0SManos Pitsidianakis cmd->resp.code = virtio_snd_set_pcm_params(s, stream_id, &req); 33264704ce0SManos Pitsidianakis } 33364704ce0SManos Pitsidianakis 33464704ce0SManos Pitsidianakis /* 335eb9ad377SManos Pitsidianakis * Get a QEMU Audiosystem compatible format value from a VIRTIO_SND_PCM_FMT_* 336eb9ad377SManos Pitsidianakis */ 337eb9ad377SManos Pitsidianakis static AudioFormat virtio_snd_get_qemu_format(uint32_t format) 338eb9ad377SManos Pitsidianakis { 339eb9ad377SManos Pitsidianakis #define CASE(FMT) \ 340eb9ad377SManos Pitsidianakis case VIRTIO_SND_PCM_FMT_##FMT: \ 341eb9ad377SManos Pitsidianakis return AUDIO_FORMAT_##FMT; 342eb9ad377SManos Pitsidianakis 343eb9ad377SManos Pitsidianakis switch (format) { 344eb9ad377SManos Pitsidianakis CASE(U8) 345eb9ad377SManos Pitsidianakis CASE(S8) 346eb9ad377SManos Pitsidianakis CASE(U16) 347eb9ad377SManos Pitsidianakis CASE(S16) 348eb9ad377SManos Pitsidianakis CASE(U32) 349eb9ad377SManos Pitsidianakis CASE(S32) 350eb9ad377SManos Pitsidianakis case VIRTIO_SND_PCM_FMT_FLOAT: 351eb9ad377SManos Pitsidianakis return AUDIO_FORMAT_F32; 352eb9ad377SManos Pitsidianakis default: 353eb9ad377SManos Pitsidianakis g_assert_not_reached(); 354eb9ad377SManos Pitsidianakis } 355eb9ad377SManos Pitsidianakis 356eb9ad377SManos Pitsidianakis #undef CASE 357eb9ad377SManos Pitsidianakis } 358eb9ad377SManos Pitsidianakis 359eb9ad377SManos Pitsidianakis /* 360eb9ad377SManos Pitsidianakis * Get a QEMU Audiosystem compatible frequency value from a 361eb9ad377SManos Pitsidianakis * VIRTIO_SND_PCM_RATE_* 362eb9ad377SManos Pitsidianakis */ 363eb9ad377SManos Pitsidianakis static uint32_t virtio_snd_get_qemu_freq(uint32_t rate) 364eb9ad377SManos Pitsidianakis { 365eb9ad377SManos Pitsidianakis #define CASE(RATE) \ 366eb9ad377SManos Pitsidianakis case VIRTIO_SND_PCM_RATE_##RATE: \ 367eb9ad377SManos Pitsidianakis return RATE; 368eb9ad377SManos Pitsidianakis 369eb9ad377SManos Pitsidianakis switch (rate) { 370eb9ad377SManos Pitsidianakis CASE(5512) 371eb9ad377SManos Pitsidianakis CASE(8000) 372eb9ad377SManos Pitsidianakis CASE(11025) 373eb9ad377SManos Pitsidianakis CASE(16000) 374eb9ad377SManos Pitsidianakis CASE(22050) 375eb9ad377SManos Pitsidianakis CASE(32000) 376eb9ad377SManos Pitsidianakis CASE(44100) 377eb9ad377SManos Pitsidianakis CASE(48000) 378eb9ad377SManos Pitsidianakis CASE(64000) 379eb9ad377SManos Pitsidianakis CASE(88200) 380eb9ad377SManos Pitsidianakis CASE(96000) 381eb9ad377SManos Pitsidianakis CASE(176400) 382eb9ad377SManos Pitsidianakis CASE(192000) 383eb9ad377SManos Pitsidianakis CASE(384000) 384eb9ad377SManos Pitsidianakis default: 385eb9ad377SManos Pitsidianakis g_assert_not_reached(); 386eb9ad377SManos Pitsidianakis } 387eb9ad377SManos Pitsidianakis 388eb9ad377SManos Pitsidianakis #undef CASE 389eb9ad377SManos Pitsidianakis } 390eb9ad377SManos Pitsidianakis 391eb9ad377SManos Pitsidianakis /* 392eb9ad377SManos Pitsidianakis * Get QEMU Audiosystem compatible audsettings from virtio based pcm stream 393eb9ad377SManos Pitsidianakis * params. 394eb9ad377SManos Pitsidianakis */ 395eb9ad377SManos Pitsidianakis static void virtio_snd_get_qemu_audsettings(audsettings *as, 396eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params *params) 397eb9ad377SManos Pitsidianakis { 398eb9ad377SManos Pitsidianakis as->nchannels = MIN(AUDIO_MAX_CHANNELS, params->channels); 399eb9ad377SManos Pitsidianakis as->fmt = virtio_snd_get_qemu_format(params->format); 400eb9ad377SManos Pitsidianakis as->freq = virtio_snd_get_qemu_freq(params->rate); 401eb9ad377SManos Pitsidianakis as->endianness = target_words_bigendian() ? 1 : 0; 402eb9ad377SManos Pitsidianakis } 403eb9ad377SManos Pitsidianakis 404eb9ad377SManos Pitsidianakis /* 405eb9ad377SManos Pitsidianakis * Close a stream and free all its resources. 406eb9ad377SManos Pitsidianakis * 407eb9ad377SManos Pitsidianakis * @stream: VirtIOSoundPCMStream *stream 408eb9ad377SManos Pitsidianakis */ 409eb9ad377SManos Pitsidianakis static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream) 410eb9ad377SManos Pitsidianakis { 41118a75281SManos Pitsidianakis if (stream) { 41218a75281SManos Pitsidianakis virtio_snd_pcm_flush(stream); 413d8d64acbSManos Pitsidianakis if (stream->info.direction == VIRTIO_SND_D_OUTPUT) { 41418a75281SManos Pitsidianakis AUD_close_out(&stream->pcm->snd->card, stream->voice.out); 41518a75281SManos Pitsidianakis stream->voice.out = NULL; 416d8d64acbSManos Pitsidianakis } else if (stream->info.direction == VIRTIO_SND_D_INPUT) { 417d8d64acbSManos Pitsidianakis AUD_close_in(&stream->pcm->snd->card, stream->voice.in); 418d8d64acbSManos Pitsidianakis stream->voice.in = NULL; 41918a75281SManos Pitsidianakis } 42018a75281SManos Pitsidianakis } 421eb9ad377SManos Pitsidianakis } 422eb9ad377SManos Pitsidianakis 423eb9ad377SManos Pitsidianakis /* 424eb9ad377SManos Pitsidianakis * Prepares a VirtIOSound card stream. 425eb9ad377SManos Pitsidianakis * Returns the response status code. (VIRTIO_SND_S_*). 426eb9ad377SManos Pitsidianakis * 427eb9ad377SManos Pitsidianakis * @s: VirtIOSound device 428eb9ad377SManos Pitsidianakis * @stream_id: stream id 429eb9ad377SManos Pitsidianakis */ 430eb9ad377SManos Pitsidianakis static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id) 431eb9ad377SManos Pitsidianakis { 432eb9ad377SManos Pitsidianakis audsettings as; 433eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params *params; 434eb9ad377SManos Pitsidianakis VirtIOSoundPCMStream *stream; 435eb9ad377SManos Pitsidianakis 436eb9ad377SManos Pitsidianakis if (s->pcm->streams == NULL || 437eb9ad377SManos Pitsidianakis s->pcm->pcm_params == NULL || 438eb9ad377SManos Pitsidianakis stream_id >= s->snd_conf.streams) { 439eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 440eb9ad377SManos Pitsidianakis } 441eb9ad377SManos Pitsidianakis 442eb9ad377SManos Pitsidianakis params = virtio_snd_pcm_get_params(s, stream_id); 443eb9ad377SManos Pitsidianakis if (params == NULL) { 444eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 445eb9ad377SManos Pitsidianakis } 446eb9ad377SManos Pitsidianakis 447eb9ad377SManos Pitsidianakis stream = virtio_snd_pcm_get_stream(s, stream_id); 448eb9ad377SManos Pitsidianakis if (stream == NULL) { 449eb9ad377SManos Pitsidianakis stream = g_new0(VirtIOSoundPCMStream, 1); 450eb9ad377SManos Pitsidianakis stream->active = false; 451eb9ad377SManos Pitsidianakis stream->id = stream_id; 452eb9ad377SManos Pitsidianakis stream->pcm = s->pcm; 453eb9ad377SManos Pitsidianakis stream->s = s; 45418a75281SManos Pitsidianakis qemu_mutex_init(&stream->queue_mutex); 45518a75281SManos Pitsidianakis QSIMPLEQ_INIT(&stream->queue); 45618a75281SManos Pitsidianakis QSIMPLEQ_INIT(&stream->invalid); 457eb9ad377SManos Pitsidianakis 458eb9ad377SManos Pitsidianakis /* 459eb9ad377SManos Pitsidianakis * stream_id >= s->snd_conf.streams was checked before so this is 460eb9ad377SManos Pitsidianakis * in-bounds 461eb9ad377SManos Pitsidianakis */ 462eb9ad377SManos Pitsidianakis s->pcm->streams[stream_id] = stream; 463eb9ad377SManos Pitsidianakis } 464eb9ad377SManos Pitsidianakis 465eb9ad377SManos Pitsidianakis virtio_snd_get_qemu_audsettings(&as, params); 466eb9ad377SManos Pitsidianakis stream->info.direction = stream_id < s->snd_conf.streams / 2 + 467eb9ad377SManos Pitsidianakis (s->snd_conf.streams & 1) ? VIRTIO_SND_D_OUTPUT : VIRTIO_SND_D_INPUT; 468eb9ad377SManos Pitsidianakis stream->info.hdr.hda_fn_nid = VIRTIO_SOUND_HDA_FN_NID; 469eb9ad377SManos Pitsidianakis stream->info.features = 0; 470eb9ad377SManos Pitsidianakis stream->info.channels_min = 1; 471eb9ad377SManos Pitsidianakis stream->info.channels_max = as.nchannels; 472eb9ad377SManos Pitsidianakis stream->info.formats = supported_formats; 473eb9ad377SManos Pitsidianakis stream->info.rates = supported_rates; 474eb9ad377SManos Pitsidianakis stream->params = *params; 475eb9ad377SManos Pitsidianakis 476eb9ad377SManos Pitsidianakis stream->positions[0] = VIRTIO_SND_CHMAP_FL; 477eb9ad377SManos Pitsidianakis stream->positions[1] = VIRTIO_SND_CHMAP_FR; 478eb9ad377SManos Pitsidianakis stream->as = as; 479eb9ad377SManos Pitsidianakis 48018a75281SManos Pitsidianakis if (stream->info.direction == VIRTIO_SND_D_OUTPUT) { 48118a75281SManos Pitsidianakis stream->voice.out = AUD_open_out(&s->card, 48218a75281SManos Pitsidianakis stream->voice.out, 48318a75281SManos Pitsidianakis "virtio-sound.out", 48418a75281SManos Pitsidianakis stream, 48518a75281SManos Pitsidianakis virtio_snd_pcm_out_cb, 48618a75281SManos Pitsidianakis &as); 48718a75281SManos Pitsidianakis AUD_set_volume_out(stream->voice.out, 0, 255, 255); 48818a75281SManos Pitsidianakis } else { 489d8d64acbSManos Pitsidianakis stream->voice.in = AUD_open_in(&s->card, 490d8d64acbSManos Pitsidianakis stream->voice.in, 491d8d64acbSManos Pitsidianakis "virtio-sound.in", 492d8d64acbSManos Pitsidianakis stream, 493d8d64acbSManos Pitsidianakis virtio_snd_pcm_in_cb, 494d8d64acbSManos Pitsidianakis &as); 495d8d64acbSManos Pitsidianakis AUD_set_volume_in(stream->voice.in, 0, 255, 255); 49618a75281SManos Pitsidianakis } 49718a75281SManos Pitsidianakis 498eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_OK); 499eb9ad377SManos Pitsidianakis } 500eb9ad377SManos Pitsidianakis 501eb9ad377SManos Pitsidianakis static const char *print_code(uint32_t code) 502eb9ad377SManos Pitsidianakis { 503eb9ad377SManos Pitsidianakis #define CASE(CODE) \ 504eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_##CODE: \ 505eb9ad377SManos Pitsidianakis return "VIRTIO_SND_R_"#CODE 506eb9ad377SManos Pitsidianakis 507eb9ad377SManos Pitsidianakis switch (code) { 508eb9ad377SManos Pitsidianakis CASE(JACK_INFO); 509eb9ad377SManos Pitsidianakis CASE(JACK_REMAP); 510eb9ad377SManos Pitsidianakis CASE(PCM_INFO); 511eb9ad377SManos Pitsidianakis CASE(PCM_SET_PARAMS); 512eb9ad377SManos Pitsidianakis CASE(PCM_PREPARE); 513eb9ad377SManos Pitsidianakis CASE(PCM_RELEASE); 514eb9ad377SManos Pitsidianakis CASE(PCM_START); 515eb9ad377SManos Pitsidianakis CASE(PCM_STOP); 516eb9ad377SManos Pitsidianakis CASE(CHMAP_INFO); 517eb9ad377SManos Pitsidianakis default: 518eb9ad377SManos Pitsidianakis return "invalid code"; 519eb9ad377SManos Pitsidianakis } 520eb9ad377SManos Pitsidianakis 521eb9ad377SManos Pitsidianakis #undef CASE 522eb9ad377SManos Pitsidianakis }; 523eb9ad377SManos Pitsidianakis 524eb9ad377SManos Pitsidianakis /* 525e5788b8fSManos Pitsidianakis * Handles VIRTIO_SND_R_PCM_PREPARE. 526e5788b8fSManos Pitsidianakis * 527e5788b8fSManos Pitsidianakis * @s: VirtIOSound device 528e5788b8fSManos Pitsidianakis * @cmd: The request command queue element from VirtIOSound cmdq field 529e5788b8fSManos Pitsidianakis */ 530e5788b8fSManos Pitsidianakis static void virtio_snd_handle_pcm_prepare(VirtIOSound *s, 531e5788b8fSManos Pitsidianakis virtio_snd_ctrl_command *cmd) 532e5788b8fSManos Pitsidianakis { 533e5788b8fSManos Pitsidianakis uint32_t stream_id; 534e5788b8fSManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg, 535e5788b8fSManos Pitsidianakis cmd->elem->out_num, 536e5788b8fSManos Pitsidianakis sizeof(virtio_snd_hdr), 537e5788b8fSManos Pitsidianakis &stream_id, 538e5788b8fSManos Pitsidianakis sizeof(stream_id)); 539e5788b8fSManos Pitsidianakis 540e5788b8fSManos Pitsidianakis stream_id = le32_to_cpu(stream_id); 541e5788b8fSManos Pitsidianakis cmd->resp.code = msg_sz == sizeof(stream_id) 542e5788b8fSManos Pitsidianakis ? virtio_snd_pcm_prepare(s, stream_id) 543e5788b8fSManos Pitsidianakis : cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 544e5788b8fSManos Pitsidianakis } 545e5788b8fSManos Pitsidianakis 546e5788b8fSManos Pitsidianakis /* 547fa131d4aSManos Pitsidianakis * Handles VIRTIO_SND_R_PCM_START. 548fa131d4aSManos Pitsidianakis * 549fa131d4aSManos Pitsidianakis * @s: VirtIOSound device 550fa131d4aSManos Pitsidianakis * @cmd: The request command queue element from VirtIOSound cmdq field 551fa131d4aSManos Pitsidianakis * @start: whether to start or stop the device 552fa131d4aSManos Pitsidianakis */ 553fa131d4aSManos Pitsidianakis static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s, 554fa131d4aSManos Pitsidianakis virtio_snd_ctrl_command *cmd, 555fa131d4aSManos Pitsidianakis bool start) 556fa131d4aSManos Pitsidianakis { 557fa131d4aSManos Pitsidianakis VirtIOSoundPCMStream *stream; 558fa131d4aSManos Pitsidianakis virtio_snd_pcm_hdr req; 559fa131d4aSManos Pitsidianakis uint32_t stream_id; 560fa131d4aSManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg, 561fa131d4aSManos Pitsidianakis cmd->elem->out_num, 562fa131d4aSManos Pitsidianakis 0, 563fa131d4aSManos Pitsidianakis &req, 564fa131d4aSManos Pitsidianakis sizeof(virtio_snd_pcm_hdr)); 565fa131d4aSManos Pitsidianakis 566fa131d4aSManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_pcm_hdr)) { 567fa131d4aSManos Pitsidianakis qemu_log_mask(LOG_GUEST_ERROR, 568fa131d4aSManos Pitsidianakis "%s: virtio-snd command size incorrect %zu vs \ 569fa131d4aSManos Pitsidianakis %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_hdr)); 570fa131d4aSManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 571fa131d4aSManos Pitsidianakis return; 572fa131d4aSManos Pitsidianakis } 573fa131d4aSManos Pitsidianakis 574fa131d4aSManos Pitsidianakis stream_id = le32_to_cpu(req.stream_id); 575fa131d4aSManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); 576fa131d4aSManos Pitsidianakis trace_virtio_snd_handle_pcm_start_stop(start ? "VIRTIO_SND_R_PCM_START" : 577fa131d4aSManos Pitsidianakis "VIRTIO_SND_R_PCM_STOP", stream_id); 57818a75281SManos Pitsidianakis 579fa131d4aSManos Pitsidianakis stream = virtio_snd_pcm_get_stream(s, stream_id); 58018a75281SManos Pitsidianakis if (stream) { 58118a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { 58218a75281SManos Pitsidianakis stream->active = start; 58318a75281SManos Pitsidianakis } 58418a75281SManos Pitsidianakis if (stream->info.direction == VIRTIO_SND_D_OUTPUT) { 58518a75281SManos Pitsidianakis AUD_set_active_out(stream->voice.out, start); 586d8d64acbSManos Pitsidianakis } else { 587d8d64acbSManos Pitsidianakis AUD_set_active_in(stream->voice.in, start); 58818a75281SManos Pitsidianakis } 58918a75281SManos Pitsidianakis } else { 59018a75281SManos Pitsidianakis error_report("Invalid stream id: %"PRIu32, stream_id); 591fa131d4aSManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 592fa131d4aSManos Pitsidianakis return; 593fa131d4aSManos Pitsidianakis } 594fa131d4aSManos Pitsidianakis stream->active = start; 595fa131d4aSManos Pitsidianakis } 596fa131d4aSManos Pitsidianakis 597fa131d4aSManos Pitsidianakis /* 59818a75281SManos Pitsidianakis * Returns the number of I/O messages that are being processed. 59918a75281SManos Pitsidianakis * 60018a75281SManos Pitsidianakis * @stream: VirtIOSoundPCMStream 60118a75281SManos Pitsidianakis */ 60218a75281SManos Pitsidianakis static size_t virtio_snd_pcm_get_io_msgs_count(VirtIOSoundPCMStream *stream) 60318a75281SManos Pitsidianakis { 60418a75281SManos Pitsidianakis VirtIOSoundPCMBuffer *buffer, *next; 60518a75281SManos Pitsidianakis size_t count = 0; 60618a75281SManos Pitsidianakis 60718a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { 60818a75281SManos Pitsidianakis QSIMPLEQ_FOREACH_SAFE(buffer, &stream->queue, entry, next) { 60918a75281SManos Pitsidianakis count += 1; 61018a75281SManos Pitsidianakis } 61118a75281SManos Pitsidianakis QSIMPLEQ_FOREACH_SAFE(buffer, &stream->invalid, entry, next) { 61218a75281SManos Pitsidianakis count += 1; 61318a75281SManos Pitsidianakis } 61418a75281SManos Pitsidianakis } 61518a75281SManos Pitsidianakis return count; 61618a75281SManos Pitsidianakis } 61718a75281SManos Pitsidianakis 61818a75281SManos Pitsidianakis /* 61918a75281SManos Pitsidianakis * Handles VIRTIO_SND_R_PCM_RELEASE. 620d48800d7SManos Pitsidianakis * 621d48800d7SManos Pitsidianakis * @s: VirtIOSound device 622d48800d7SManos Pitsidianakis * @cmd: The request command queue element from VirtIOSound cmdq field 623d48800d7SManos Pitsidianakis */ 624d48800d7SManos Pitsidianakis static void virtio_snd_handle_pcm_release(VirtIOSound *s, 625d48800d7SManos Pitsidianakis virtio_snd_ctrl_command *cmd) 626d48800d7SManos Pitsidianakis { 627d48800d7SManos Pitsidianakis uint32_t stream_id; 628d48800d7SManos Pitsidianakis VirtIOSoundPCMStream *stream; 629d48800d7SManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg, 630d48800d7SManos Pitsidianakis cmd->elem->out_num, 631d48800d7SManos Pitsidianakis sizeof(virtio_snd_hdr), 632d48800d7SManos Pitsidianakis &stream_id, 633d48800d7SManos Pitsidianakis sizeof(stream_id)); 634d48800d7SManos Pitsidianakis 635d48800d7SManos Pitsidianakis if (msg_sz != sizeof(stream_id)) { 636d48800d7SManos Pitsidianakis /* 637d48800d7SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 638d48800d7SManos Pitsidianakis */ 639d48800d7SManos Pitsidianakis qemu_log_mask(LOG_GUEST_ERROR, 640d48800d7SManos Pitsidianakis "%s: virtio-snd command size incorrect %zu vs \ 641d48800d7SManos Pitsidianakis %zu\n", __func__, msg_sz, sizeof(stream_id)); 642d48800d7SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 643d48800d7SManos Pitsidianakis return; 644d48800d7SManos Pitsidianakis } 645d48800d7SManos Pitsidianakis 646d48800d7SManos Pitsidianakis stream_id = le32_to_cpu(stream_id); 647d48800d7SManos Pitsidianakis trace_virtio_snd_handle_pcm_release(stream_id); 648d48800d7SManos Pitsidianakis stream = virtio_snd_pcm_get_stream(s, stream_id); 649d48800d7SManos Pitsidianakis if (stream == NULL) { 650d48800d7SManos Pitsidianakis /* 651d48800d7SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 652d48800d7SManos Pitsidianakis */ 653d48800d7SManos Pitsidianakis error_report("already released stream %"PRIu32, stream_id); 654d48800d7SManos Pitsidianakis virtio_error(VIRTIO_DEVICE(s), 655d48800d7SManos Pitsidianakis "already released stream %"PRIu32, 656d48800d7SManos Pitsidianakis stream_id); 657d48800d7SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 658d48800d7SManos Pitsidianakis return; 659d48800d7SManos Pitsidianakis } 66018a75281SManos Pitsidianakis 66118a75281SManos Pitsidianakis if (virtio_snd_pcm_get_io_msgs_count(stream)) { 66218a75281SManos Pitsidianakis /* 66318a75281SManos Pitsidianakis * virtio-v1.2-csd01, 5.14.6.6.5.1, 66418a75281SManos Pitsidianakis * Device Requirements: Stream Release 66518a75281SManos Pitsidianakis * 66618a75281SManos Pitsidianakis * - The device MUST complete all pending I/O messages for the 66718a75281SManos Pitsidianakis * specified stream ID. 66818a75281SManos Pitsidianakis * - The device MUST NOT complete the control request while there 66918a75281SManos Pitsidianakis * are pending I/O messages for the specified stream ID. 67018a75281SManos Pitsidianakis */ 67118a75281SManos Pitsidianakis trace_virtio_snd_pcm_stream_flush(stream_id); 67218a75281SManos Pitsidianakis virtio_snd_pcm_flush(stream); 67318a75281SManos Pitsidianakis } 67418a75281SManos Pitsidianakis 675d48800d7SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); 676d48800d7SManos Pitsidianakis } 677d48800d7SManos Pitsidianakis 678d48800d7SManos Pitsidianakis /* 679eb9ad377SManos Pitsidianakis * The actual processing done in virtio_snd_process_cmdq(). 680eb9ad377SManos Pitsidianakis * 681eb9ad377SManos Pitsidianakis * @s: VirtIOSound device 682eb9ad377SManos Pitsidianakis * @cmd: control command request 683eb9ad377SManos Pitsidianakis */ 684eb9ad377SManos Pitsidianakis static inline void 685eb9ad377SManos Pitsidianakis process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd) 686eb9ad377SManos Pitsidianakis { 687eb9ad377SManos Pitsidianakis uint32_t code; 688eb9ad377SManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg, 689eb9ad377SManos Pitsidianakis cmd->elem->out_num, 690eb9ad377SManos Pitsidianakis 0, 691eb9ad377SManos Pitsidianakis &cmd->ctrl, 692eb9ad377SManos Pitsidianakis sizeof(virtio_snd_hdr)); 693eb9ad377SManos Pitsidianakis 694eb9ad377SManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_hdr)) { 695eb9ad377SManos Pitsidianakis /* 696eb9ad377SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 697eb9ad377SManos Pitsidianakis */ 698eb9ad377SManos Pitsidianakis qemu_log_mask(LOG_GUEST_ERROR, 699eb9ad377SManos Pitsidianakis "%s: virtio-snd command size incorrect %zu vs \ 700eb9ad377SManos Pitsidianakis %zu\n", __func__, msg_sz, sizeof(virtio_snd_hdr)); 701eb9ad377SManos Pitsidianakis return; 702eb9ad377SManos Pitsidianakis } 703eb9ad377SManos Pitsidianakis 704eb9ad377SManos Pitsidianakis code = le32_to_cpu(cmd->ctrl.code); 705eb9ad377SManos Pitsidianakis 706eb9ad377SManos Pitsidianakis trace_virtio_snd_handle_code(code, print_code(code)); 707eb9ad377SManos Pitsidianakis 708eb9ad377SManos Pitsidianakis switch (code) { 709eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_JACK_INFO: 710eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_JACK_REMAP: 711eb9ad377SManos Pitsidianakis qemu_log_mask(LOG_UNIMP, 712eb9ad377SManos Pitsidianakis "virtio_snd: jack functionality is unimplemented.\n"); 713eb9ad377SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); 714eb9ad377SManos Pitsidianakis break; 715eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_INFO: 7160ff05dd2SManos Pitsidianakis virtio_snd_handle_pcm_info(s, cmd); 7170ff05dd2SManos Pitsidianakis break; 718fa131d4aSManos Pitsidianakis case VIRTIO_SND_R_PCM_START: 719fa131d4aSManos Pitsidianakis virtio_snd_handle_pcm_start_stop(s, cmd, true); 720fa131d4aSManos Pitsidianakis break; 721fa131d4aSManos Pitsidianakis case VIRTIO_SND_R_PCM_STOP: 722fa131d4aSManos Pitsidianakis virtio_snd_handle_pcm_start_stop(s, cmd, false); 723fa131d4aSManos Pitsidianakis break; 724eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_SET_PARAMS: 72564704ce0SManos Pitsidianakis virtio_snd_handle_pcm_set_params(s, cmd); 72664704ce0SManos Pitsidianakis break; 727eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_PREPARE: 728e5788b8fSManos Pitsidianakis virtio_snd_handle_pcm_prepare(s, cmd); 729e5788b8fSManos Pitsidianakis break; 730eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_RELEASE: 731d48800d7SManos Pitsidianakis virtio_snd_handle_pcm_release(s, cmd); 732eb9ad377SManos Pitsidianakis break; 733eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_CHMAP_INFO: 734eb9ad377SManos Pitsidianakis qemu_log_mask(LOG_UNIMP, 735eb9ad377SManos Pitsidianakis "virtio_snd: chmap info functionality is unimplemented.\n"); 736eb9ad377SManos Pitsidianakis trace_virtio_snd_handle_chmap_info(); 737eb9ad377SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); 738eb9ad377SManos Pitsidianakis break; 739eb9ad377SManos Pitsidianakis default: 740eb9ad377SManos Pitsidianakis /* error */ 741eb9ad377SManos Pitsidianakis error_report("virtio snd header not recognized: %"PRIu32, code); 742eb9ad377SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 743eb9ad377SManos Pitsidianakis } 744eb9ad377SManos Pitsidianakis 745eb9ad377SManos Pitsidianakis iov_from_buf(cmd->elem->in_sg, 746eb9ad377SManos Pitsidianakis cmd->elem->in_num, 747eb9ad377SManos Pitsidianakis 0, 748eb9ad377SManos Pitsidianakis &cmd->resp, 749eb9ad377SManos Pitsidianakis sizeof(virtio_snd_hdr)); 750eb9ad377SManos Pitsidianakis virtqueue_push(cmd->vq, cmd->elem, sizeof(virtio_snd_hdr)); 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 */ 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 */ 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); 809eb9ad377SManos Pitsidianakis QTAILQ_INSERT_TAIL(&s->cmdq, cmd, next); 810eb9ad377SManos Pitsidianakis elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); 811eb9ad377SManos Pitsidianakis } 812eb9ad377SManos Pitsidianakis 813eb9ad377SManos Pitsidianakis virtio_snd_process_cmdq(s); 814eb9ad377SManos Pitsidianakis } 815eb9ad377SManos Pitsidianakis 816eb9ad377SManos Pitsidianakis /* 817eb9ad377SManos Pitsidianakis * The event virtqueue handler. 818eb9ad377SManos Pitsidianakis * Not implemented yet. 819eb9ad377SManos Pitsidianakis * 820eb9ad377SManos Pitsidianakis * @vdev: VirtIOSound device 821eb9ad377SManos Pitsidianakis * @vq: event vq 822eb9ad377SManos Pitsidianakis */ 823eb9ad377SManos Pitsidianakis static void virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq) 824eb9ad377SManos Pitsidianakis { 825eb9ad377SManos Pitsidianakis qemu_log_mask(LOG_UNIMP, "virtio_snd: event queue is unimplemented.\n"); 826eb9ad377SManos Pitsidianakis trace_virtio_snd_handle_event(); 827eb9ad377SManos Pitsidianakis } 828eb9ad377SManos Pitsidianakis 829d8d64acbSManos Pitsidianakis static inline void empty_invalid_queue(VirtIODevice *vdev, VirtQueue *vq) 830d8d64acbSManos Pitsidianakis { 831d8d64acbSManos Pitsidianakis VirtIOSoundPCMBuffer *buffer = NULL; 832d8d64acbSManos Pitsidianakis VirtIOSoundPCMStream *stream = NULL; 833d8d64acbSManos Pitsidianakis virtio_snd_pcm_status resp = { 0 }; 834d8d64acbSManos Pitsidianakis VirtIOSound *vsnd = VIRTIO_SND(vdev); 835d8d64acbSManos Pitsidianakis bool any = false; 836d8d64acbSManos Pitsidianakis 837d8d64acbSManos Pitsidianakis for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) { 838d8d64acbSManos Pitsidianakis stream = vsnd->pcm->streams[i]; 839d8d64acbSManos Pitsidianakis if (stream) { 840d8d64acbSManos Pitsidianakis any = false; 841d8d64acbSManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { 842d8d64acbSManos Pitsidianakis while (!QSIMPLEQ_EMPTY(&stream->invalid)) { 843d8d64acbSManos Pitsidianakis buffer = QSIMPLEQ_FIRST(&stream->invalid); 844d8d64acbSManos Pitsidianakis if (buffer->vq != vq) { 845d8d64acbSManos Pitsidianakis break; 846d8d64acbSManos Pitsidianakis } 847d8d64acbSManos Pitsidianakis any = true; 848d8d64acbSManos Pitsidianakis resp.status = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 849d8d64acbSManos Pitsidianakis iov_from_buf(buffer->elem->in_sg, 850d8d64acbSManos Pitsidianakis buffer->elem->in_num, 851d8d64acbSManos Pitsidianakis 0, 852d8d64acbSManos Pitsidianakis &resp, 853d8d64acbSManos Pitsidianakis sizeof(virtio_snd_pcm_status)); 854d8d64acbSManos Pitsidianakis virtqueue_push(vq, 855d8d64acbSManos Pitsidianakis buffer->elem, 856d8d64acbSManos Pitsidianakis sizeof(virtio_snd_pcm_status)); 857d8d64acbSManos Pitsidianakis QSIMPLEQ_REMOVE_HEAD(&stream->invalid, entry); 858d8d64acbSManos Pitsidianakis virtio_snd_pcm_buffer_free(buffer); 859d8d64acbSManos Pitsidianakis } 860d8d64acbSManos Pitsidianakis if (any) { 861d8d64acbSManos Pitsidianakis /* 862d8d64acbSManos Pitsidianakis * Notify vq about virtio_snd_pcm_status responses. 863d8d64acbSManos Pitsidianakis * Buffer responses must be notified separately later. 864d8d64acbSManos Pitsidianakis */ 865d8d64acbSManos Pitsidianakis virtio_notify(vdev, vq); 866d8d64acbSManos Pitsidianakis } 867d8d64acbSManos Pitsidianakis } 868d8d64acbSManos Pitsidianakis } 869d8d64acbSManos Pitsidianakis } 870d8d64acbSManos Pitsidianakis } 871d8d64acbSManos Pitsidianakis 872eb9ad377SManos Pitsidianakis /* 87318a75281SManos Pitsidianakis * The tx virtqueue handler. Makes the buffers available to their respective 87418a75281SManos Pitsidianakis * streams for consumption. 87518a75281SManos Pitsidianakis * 87618a75281SManos Pitsidianakis * @vdev: VirtIOSound device 87718a75281SManos Pitsidianakis * @vq: tx virtqueue 87818a75281SManos Pitsidianakis */ 879d8d64acbSManos Pitsidianakis static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq) 88018a75281SManos Pitsidianakis { 88118a75281SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 88218a75281SManos Pitsidianakis VirtIOSoundPCMStream *stream = NULL; 88318a75281SManos Pitsidianakis VirtIOSoundPCMBuffer *buffer; 88418a75281SManos Pitsidianakis VirtQueueElement *elem; 88518a75281SManos Pitsidianakis size_t msg_sz, size; 88618a75281SManos Pitsidianakis virtio_snd_pcm_xfer hdr; 88718a75281SManos Pitsidianakis uint32_t stream_id; 88818a75281SManos Pitsidianakis /* 88918a75281SManos Pitsidianakis * If any of the I/O messages are invalid, put them in stream->invalid and 89018a75281SManos Pitsidianakis * return them after the for loop. 89118a75281SManos Pitsidianakis */ 89218a75281SManos Pitsidianakis bool must_empty_invalid_queue = false; 89318a75281SManos Pitsidianakis 89418a75281SManos Pitsidianakis if (!virtio_queue_ready(vq)) { 89518a75281SManos Pitsidianakis return; 89618a75281SManos Pitsidianakis } 897d8d64acbSManos Pitsidianakis trace_virtio_snd_handle_tx_xfer(); 89818a75281SManos Pitsidianakis 89918a75281SManos Pitsidianakis for (;;) { 90018a75281SManos Pitsidianakis elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); 90118a75281SManos Pitsidianakis if (!elem) { 90218a75281SManos Pitsidianakis break; 90318a75281SManos Pitsidianakis } 90418a75281SManos Pitsidianakis /* get the message hdr object */ 90518a75281SManos Pitsidianakis msg_sz = iov_to_buf(elem->out_sg, 90618a75281SManos Pitsidianakis elem->out_num, 90718a75281SManos Pitsidianakis 0, 90818a75281SManos Pitsidianakis &hdr, 90918a75281SManos Pitsidianakis sizeof(virtio_snd_pcm_xfer)); 91018a75281SManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_pcm_xfer)) { 91118a75281SManos Pitsidianakis goto tx_err; 91218a75281SManos Pitsidianakis } 91318a75281SManos Pitsidianakis stream_id = le32_to_cpu(hdr.stream_id); 91418a75281SManos Pitsidianakis 91518a75281SManos Pitsidianakis if (stream_id >= s->snd_conf.streams 91618a75281SManos Pitsidianakis || s->pcm->streams[stream_id] == NULL) { 91718a75281SManos Pitsidianakis goto tx_err; 91818a75281SManos Pitsidianakis } 91918a75281SManos Pitsidianakis 92018a75281SManos Pitsidianakis stream = s->pcm->streams[stream_id]; 92118a75281SManos Pitsidianakis if (stream->info.direction != VIRTIO_SND_D_OUTPUT) { 92218a75281SManos Pitsidianakis goto tx_err; 92318a75281SManos Pitsidianakis } 92418a75281SManos Pitsidianakis 92518a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { 92618a75281SManos Pitsidianakis size = iov_size(elem->out_sg, elem->out_num) - msg_sz; 92718a75281SManos Pitsidianakis 92818a75281SManos Pitsidianakis buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size); 92918a75281SManos Pitsidianakis buffer->elem = elem; 93018a75281SManos Pitsidianakis buffer->populated = false; 93118a75281SManos Pitsidianakis buffer->vq = vq; 93218a75281SManos Pitsidianakis buffer->size = size; 93318a75281SManos Pitsidianakis buffer->offset = 0; 93418a75281SManos Pitsidianakis 93518a75281SManos Pitsidianakis QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry); 93618a75281SManos Pitsidianakis } 93718a75281SManos Pitsidianakis continue; 93818a75281SManos Pitsidianakis 93918a75281SManos Pitsidianakis tx_err: 94018a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { 94118a75281SManos Pitsidianakis must_empty_invalid_queue = true; 94218a75281SManos Pitsidianakis buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer)); 94318a75281SManos Pitsidianakis buffer->elem = elem; 94418a75281SManos Pitsidianakis buffer->vq = vq; 94518a75281SManos Pitsidianakis QSIMPLEQ_INSERT_TAIL(&stream->invalid, buffer, entry); 94618a75281SManos Pitsidianakis } 94718a75281SManos Pitsidianakis } 94818a75281SManos Pitsidianakis 94918a75281SManos Pitsidianakis if (must_empty_invalid_queue) { 950d8d64acbSManos Pitsidianakis empty_invalid_queue(vdev, vq); 95118a75281SManos Pitsidianakis } 95218a75281SManos Pitsidianakis } 95318a75281SManos Pitsidianakis 95418a75281SManos Pitsidianakis /* 955d8d64acbSManos Pitsidianakis * The rx virtqueue handler. Makes the buffers available to their respective 956d8d64acbSManos Pitsidianakis * streams for consumption. 9572880e676SManos Pitsidianakis * 9582880e676SManos Pitsidianakis * @vdev: VirtIOSound device 959d8d64acbSManos Pitsidianakis * @vq: rx virtqueue 9602880e676SManos Pitsidianakis */ 961d8d64acbSManos Pitsidianakis static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq) 962d8d64acbSManos Pitsidianakis { 963d8d64acbSManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 964d8d64acbSManos Pitsidianakis VirtIOSoundPCMStream *stream = NULL; 965d8d64acbSManos Pitsidianakis VirtIOSoundPCMBuffer *buffer; 966d8d64acbSManos Pitsidianakis VirtQueueElement *elem; 967d8d64acbSManos Pitsidianakis size_t msg_sz, size; 968d8d64acbSManos Pitsidianakis virtio_snd_pcm_xfer hdr; 969d8d64acbSManos Pitsidianakis uint32_t stream_id; 970d8d64acbSManos Pitsidianakis /* 971d8d64acbSManos Pitsidianakis * if any of the I/O messages are invalid, put them in stream->invalid and 972d8d64acbSManos Pitsidianakis * return them after the for loop. 973d8d64acbSManos Pitsidianakis */ 974d8d64acbSManos Pitsidianakis bool must_empty_invalid_queue = false; 975d8d64acbSManos Pitsidianakis 976d8d64acbSManos Pitsidianakis if (!virtio_queue_ready(vq)) { 977d8d64acbSManos Pitsidianakis return; 978d8d64acbSManos Pitsidianakis } 979d8d64acbSManos Pitsidianakis trace_virtio_snd_handle_rx_xfer(); 980d8d64acbSManos Pitsidianakis 981d8d64acbSManos Pitsidianakis for (;;) { 982d8d64acbSManos Pitsidianakis elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); 983d8d64acbSManos Pitsidianakis if (!elem) { 984d8d64acbSManos Pitsidianakis break; 985d8d64acbSManos Pitsidianakis } 986d8d64acbSManos Pitsidianakis /* get the message hdr object */ 987d8d64acbSManos Pitsidianakis msg_sz = iov_to_buf(elem->out_sg, 988d8d64acbSManos Pitsidianakis elem->out_num, 989d8d64acbSManos Pitsidianakis 0, 990d8d64acbSManos Pitsidianakis &hdr, 991d8d64acbSManos Pitsidianakis sizeof(virtio_snd_pcm_xfer)); 992d8d64acbSManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_pcm_xfer)) { 993d8d64acbSManos Pitsidianakis goto rx_err; 994d8d64acbSManos Pitsidianakis } 995d8d64acbSManos Pitsidianakis stream_id = le32_to_cpu(hdr.stream_id); 996d8d64acbSManos Pitsidianakis 997d8d64acbSManos Pitsidianakis if (stream_id >= s->snd_conf.streams 998d8d64acbSManos Pitsidianakis || !s->pcm->streams[stream_id]) { 999d8d64acbSManos Pitsidianakis goto rx_err; 1000d8d64acbSManos Pitsidianakis } 1001d8d64acbSManos Pitsidianakis 1002d8d64acbSManos Pitsidianakis stream = s->pcm->streams[stream_id]; 1003d8d64acbSManos Pitsidianakis if (stream == NULL || stream->info.direction != VIRTIO_SND_D_INPUT) { 1004d8d64acbSManos Pitsidianakis goto rx_err; 1005d8d64acbSManos Pitsidianakis } 1006d8d64acbSManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { 1007d8d64acbSManos Pitsidianakis size = iov_size(elem->in_sg, elem->in_num) - 1008d8d64acbSManos Pitsidianakis sizeof(virtio_snd_pcm_status); 1009d8d64acbSManos Pitsidianakis buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size); 1010d8d64acbSManos Pitsidianakis buffer->elem = elem; 1011d8d64acbSManos Pitsidianakis buffer->vq = vq; 1012d8d64acbSManos Pitsidianakis buffer->size = 0; 1013d8d64acbSManos Pitsidianakis buffer->offset = 0; 1014d8d64acbSManos Pitsidianakis QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry); 1015d8d64acbSManos Pitsidianakis } 1016d8d64acbSManos Pitsidianakis continue; 1017d8d64acbSManos Pitsidianakis 1018d8d64acbSManos Pitsidianakis rx_err: 1019d8d64acbSManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { 1020d8d64acbSManos Pitsidianakis must_empty_invalid_queue = true; 1021d8d64acbSManos Pitsidianakis buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer)); 1022d8d64acbSManos Pitsidianakis buffer->elem = elem; 1023d8d64acbSManos Pitsidianakis buffer->vq = vq; 1024d8d64acbSManos Pitsidianakis QSIMPLEQ_INSERT_TAIL(&stream->invalid, buffer, entry); 1025d8d64acbSManos Pitsidianakis } 1026d8d64acbSManos Pitsidianakis } 1027d8d64acbSManos Pitsidianakis 1028d8d64acbSManos Pitsidianakis if (must_empty_invalid_queue) { 1029d8d64acbSManos Pitsidianakis empty_invalid_queue(vdev, vq); 1030d8d64acbSManos Pitsidianakis } 1031d8d64acbSManos Pitsidianakis } 10322880e676SManos Pitsidianakis 10332880e676SManos Pitsidianakis static uint64_t get_features(VirtIODevice *vdev, uint64_t features, 10342880e676SManos Pitsidianakis Error **errp) 10352880e676SManos Pitsidianakis { 10362880e676SManos Pitsidianakis /* 10372880e676SManos Pitsidianakis * virtio-v1.2-csd01, 5.14.3, 10382880e676SManos Pitsidianakis * Feature Bits 10392880e676SManos Pitsidianakis * None currently defined. 10402880e676SManos Pitsidianakis */ 10412880e676SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 10422880e676SManos Pitsidianakis features |= s->features; 10432880e676SManos Pitsidianakis 10442880e676SManos Pitsidianakis trace_virtio_snd_get_features(vdev, features); 10452880e676SManos Pitsidianakis 10462880e676SManos Pitsidianakis return features; 10472880e676SManos Pitsidianakis } 10482880e676SManos Pitsidianakis 10492880e676SManos Pitsidianakis static void 10502880e676SManos Pitsidianakis virtio_snd_vm_state_change(void *opaque, bool running, 10512880e676SManos Pitsidianakis RunState state) 10522880e676SManos Pitsidianakis { 10532880e676SManos Pitsidianakis if (running) { 10542880e676SManos Pitsidianakis trace_virtio_snd_vm_state_running(); 10552880e676SManos Pitsidianakis } else { 10562880e676SManos Pitsidianakis trace_virtio_snd_vm_state_stopped(); 10572880e676SManos Pitsidianakis } 10582880e676SManos Pitsidianakis } 10592880e676SManos Pitsidianakis 10602880e676SManos Pitsidianakis static void virtio_snd_realize(DeviceState *dev, Error **errp) 10612880e676SManos Pitsidianakis { 10622880e676SManos Pitsidianakis ERRP_GUARD(); 10632880e676SManos Pitsidianakis VirtIOSound *vsnd = VIRTIO_SND(dev); 10642880e676SManos Pitsidianakis VirtIODevice *vdev = VIRTIO_DEVICE(dev); 1065eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params default_params = { 0 }; 1066eb9ad377SManos Pitsidianakis uint32_t status; 10672880e676SManos Pitsidianakis 1068eb9ad377SManos Pitsidianakis vsnd->pcm = NULL; 10692880e676SManos Pitsidianakis vsnd->vmstate = 10702880e676SManos Pitsidianakis qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd); 10712880e676SManos Pitsidianakis 10722880e676SManos Pitsidianakis trace_virtio_snd_realize(vsnd); 10732880e676SManos Pitsidianakis 1074eb9ad377SManos Pitsidianakis vsnd->pcm = g_new0(VirtIOSoundPCM, 1); 1075eb9ad377SManos Pitsidianakis vsnd->pcm->snd = vsnd; 1076eb9ad377SManos Pitsidianakis vsnd->pcm->streams = 1077eb9ad377SManos Pitsidianakis g_new0(VirtIOSoundPCMStream *, vsnd->snd_conf.streams); 1078eb9ad377SManos Pitsidianakis vsnd->pcm->pcm_params = 1079eb9ad377SManos Pitsidianakis g_new0(virtio_snd_pcm_set_params, vsnd->snd_conf.streams); 1080eb9ad377SManos Pitsidianakis 10812880e676SManos Pitsidianakis virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config)); 10822880e676SManos Pitsidianakis virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1); 10832880e676SManos Pitsidianakis 10842880e676SManos Pitsidianakis /* set number of jacks and streams */ 10852880e676SManos Pitsidianakis if (vsnd->snd_conf.jacks > 8) { 10862880e676SManos Pitsidianakis error_setg(errp, 10872880e676SManos Pitsidianakis "Invalid number of jacks: %"PRIu32, 10882880e676SManos Pitsidianakis vsnd->snd_conf.jacks); 10892880e676SManos Pitsidianakis return; 10902880e676SManos Pitsidianakis } 10912880e676SManos Pitsidianakis if (vsnd->snd_conf.streams < 1 || vsnd->snd_conf.streams > 10) { 10922880e676SManos Pitsidianakis error_setg(errp, 10932880e676SManos Pitsidianakis "Invalid number of streams: %"PRIu32, 10942880e676SManos Pitsidianakis vsnd->snd_conf.streams); 10952880e676SManos Pitsidianakis return; 10962880e676SManos Pitsidianakis } 10972880e676SManos Pitsidianakis 10982880e676SManos Pitsidianakis if (vsnd->snd_conf.chmaps > VIRTIO_SND_CHMAP_MAX_SIZE) { 10992880e676SManos Pitsidianakis error_setg(errp, 11002880e676SManos Pitsidianakis "Invalid number of channel maps: %"PRIu32, 11012880e676SManos Pitsidianakis vsnd->snd_conf.chmaps); 11022880e676SManos Pitsidianakis return; 11032880e676SManos Pitsidianakis } 11042880e676SManos Pitsidianakis 11052880e676SManos Pitsidianakis AUD_register_card("virtio-sound", &vsnd->card, errp); 11062880e676SManos Pitsidianakis 1107eb9ad377SManos Pitsidianakis /* set default params for all streams */ 1108eb9ad377SManos Pitsidianakis default_params.features = 0; 1109eb9ad377SManos Pitsidianakis default_params.buffer_bytes = cpu_to_le32(8192); 1110eb9ad377SManos Pitsidianakis default_params.period_bytes = cpu_to_le32(2048); 1111eb9ad377SManos Pitsidianakis default_params.channels = 2; 1112eb9ad377SManos Pitsidianakis default_params.format = VIRTIO_SND_PCM_FMT_S16; 1113eb9ad377SManos Pitsidianakis default_params.rate = VIRTIO_SND_PCM_RATE_48000; 11142880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_CONTROL] = 1115eb9ad377SManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_ctrl); 11162880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_EVENT] = 1117eb9ad377SManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_event); 11182880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_TX] = 1119d8d64acbSManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_tx_xfer); 11202880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_RX] = 1121d8d64acbSManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_rx_xfer); 1122eb9ad377SManos Pitsidianakis qemu_mutex_init(&vsnd->cmdq_mutex); 1123eb9ad377SManos Pitsidianakis QTAILQ_INIT(&vsnd->cmdq); 1124eb9ad377SManos Pitsidianakis 1125eb9ad377SManos Pitsidianakis for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) { 1126eb9ad377SManos Pitsidianakis status = virtio_snd_set_pcm_params(vsnd, i, &default_params); 1127eb9ad377SManos Pitsidianakis if (status != cpu_to_le32(VIRTIO_SND_S_OK)) { 1128eb9ad377SManos Pitsidianakis error_setg(errp, 1129*aaf851a2SMichael Tokarev "Can't initialize stream params, device responded with %s.", 1130eb9ad377SManos Pitsidianakis print_code(status)); 1131eb9ad377SManos Pitsidianakis return; 1132eb9ad377SManos Pitsidianakis } 1133eb9ad377SManos Pitsidianakis status = virtio_snd_pcm_prepare(vsnd, i); 1134eb9ad377SManos Pitsidianakis if (status != cpu_to_le32(VIRTIO_SND_S_OK)) { 1135eb9ad377SManos Pitsidianakis error_setg(errp, 1136eb9ad377SManos Pitsidianakis "Can't prepare streams, device responded with %s.", 1137eb9ad377SManos Pitsidianakis print_code(status)); 1138eb9ad377SManos Pitsidianakis return; 1139eb9ad377SManos Pitsidianakis } 1140eb9ad377SManos Pitsidianakis } 11412880e676SManos Pitsidianakis } 11422880e676SManos Pitsidianakis 114318a75281SManos Pitsidianakis static inline void return_tx_buffer(VirtIOSoundPCMStream *stream, 114418a75281SManos Pitsidianakis VirtIOSoundPCMBuffer *buffer) 114518a75281SManos Pitsidianakis { 114618a75281SManos Pitsidianakis virtio_snd_pcm_status resp = { 0 }; 114718a75281SManos Pitsidianakis resp.status = cpu_to_le32(VIRTIO_SND_S_OK); 114818a75281SManos Pitsidianakis resp.latency_bytes = cpu_to_le32((uint32_t)buffer->size); 114918a75281SManos Pitsidianakis iov_from_buf(buffer->elem->in_sg, 115018a75281SManos Pitsidianakis buffer->elem->in_num, 115118a75281SManos Pitsidianakis 0, 115218a75281SManos Pitsidianakis &resp, 115318a75281SManos Pitsidianakis sizeof(virtio_snd_pcm_status)); 115418a75281SManos Pitsidianakis virtqueue_push(buffer->vq, 115518a75281SManos Pitsidianakis buffer->elem, 115618a75281SManos Pitsidianakis sizeof(virtio_snd_pcm_status)); 115718a75281SManos Pitsidianakis virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq); 115818a75281SManos Pitsidianakis QSIMPLEQ_REMOVE(&stream->queue, 115918a75281SManos Pitsidianakis buffer, 116018a75281SManos Pitsidianakis VirtIOSoundPCMBuffer, 116118a75281SManos Pitsidianakis entry); 116218a75281SManos Pitsidianakis virtio_snd_pcm_buffer_free(buffer); 116318a75281SManos Pitsidianakis } 116418a75281SManos Pitsidianakis 116518a75281SManos Pitsidianakis /* 116618a75281SManos Pitsidianakis * AUD_* output callback. 116718a75281SManos Pitsidianakis * 116818a75281SManos Pitsidianakis * @data: VirtIOSoundPCMStream stream 116918a75281SManos Pitsidianakis * @available: number of bytes that can be written with AUD_write() 117018a75281SManos Pitsidianakis */ 117118a75281SManos Pitsidianakis static void virtio_snd_pcm_out_cb(void *data, int available) 117218a75281SManos Pitsidianakis { 117318a75281SManos Pitsidianakis VirtIOSoundPCMStream *stream = data; 117418a75281SManos Pitsidianakis VirtIOSoundPCMBuffer *buffer; 117518a75281SManos Pitsidianakis size_t size; 117618a75281SManos Pitsidianakis 117718a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { 117818a75281SManos Pitsidianakis while (!QSIMPLEQ_EMPTY(&stream->queue)) { 117918a75281SManos Pitsidianakis buffer = QSIMPLEQ_FIRST(&stream->queue); 118018a75281SManos Pitsidianakis if (!virtio_queue_ready(buffer->vq)) { 118118a75281SManos Pitsidianakis return; 118218a75281SManos Pitsidianakis } 118318a75281SManos Pitsidianakis if (!stream->active) { 118418a75281SManos Pitsidianakis /* Stream has stopped, so do not perform AUD_write. */ 118518a75281SManos Pitsidianakis return_tx_buffer(stream, buffer); 118618a75281SManos Pitsidianakis continue; 118718a75281SManos Pitsidianakis } 118818a75281SManos Pitsidianakis if (!buffer->populated) { 118918a75281SManos Pitsidianakis iov_to_buf(buffer->elem->out_sg, 119018a75281SManos Pitsidianakis buffer->elem->out_num, 119118a75281SManos Pitsidianakis sizeof(virtio_snd_pcm_xfer), 119218a75281SManos Pitsidianakis buffer->data, 119318a75281SManos Pitsidianakis buffer->size); 119418a75281SManos Pitsidianakis buffer->populated = true; 119518a75281SManos Pitsidianakis } 119618a75281SManos Pitsidianakis for (;;) { 119718a75281SManos Pitsidianakis size = AUD_write(stream->voice.out, 119818a75281SManos Pitsidianakis buffer->data + buffer->offset, 119918a75281SManos Pitsidianakis MIN(buffer->size, available)); 120018a75281SManos Pitsidianakis assert(size <= MIN(buffer->size, available)); 120118a75281SManos Pitsidianakis if (size == 0) { 120218a75281SManos Pitsidianakis /* break out of both loops */ 120318a75281SManos Pitsidianakis available = 0; 120418a75281SManos Pitsidianakis break; 120518a75281SManos Pitsidianakis } 120618a75281SManos Pitsidianakis buffer->size -= size; 120718a75281SManos Pitsidianakis buffer->offset += size; 120818a75281SManos Pitsidianakis available -= size; 120918a75281SManos Pitsidianakis if (buffer->size < 1) { 121018a75281SManos Pitsidianakis return_tx_buffer(stream, buffer); 121118a75281SManos Pitsidianakis break; 121218a75281SManos Pitsidianakis } 121318a75281SManos Pitsidianakis if (!available) { 121418a75281SManos Pitsidianakis break; 121518a75281SManos Pitsidianakis } 121618a75281SManos Pitsidianakis } 121718a75281SManos Pitsidianakis if (!available) { 121818a75281SManos Pitsidianakis break; 121918a75281SManos Pitsidianakis } 122018a75281SManos Pitsidianakis } 122118a75281SManos Pitsidianakis } 122218a75281SManos Pitsidianakis } 122318a75281SManos Pitsidianakis 122418a75281SManos Pitsidianakis /* 1225d8d64acbSManos Pitsidianakis * Flush all buffer data from this input stream's queue into the driver's 1226d8d64acbSManos Pitsidianakis * virtual queue. 1227d8d64acbSManos Pitsidianakis * 1228d8d64acbSManos Pitsidianakis * @stream: VirtIOSoundPCMStream *stream 1229d8d64acbSManos Pitsidianakis */ 1230d8d64acbSManos Pitsidianakis static inline void return_rx_buffer(VirtIOSoundPCMStream *stream, 1231d8d64acbSManos Pitsidianakis VirtIOSoundPCMBuffer *buffer) 1232d8d64acbSManos Pitsidianakis { 1233d8d64acbSManos Pitsidianakis virtio_snd_pcm_status resp = { 0 }; 1234d8d64acbSManos Pitsidianakis resp.status = cpu_to_le32(VIRTIO_SND_S_OK); 1235d8d64acbSManos Pitsidianakis resp.latency_bytes = 0; 1236d8d64acbSManos Pitsidianakis /* Copy data -if any- to guest */ 1237d8d64acbSManos Pitsidianakis iov_from_buf(buffer->elem->in_sg, 1238d8d64acbSManos Pitsidianakis buffer->elem->in_num, 1239d8d64acbSManos Pitsidianakis 0, 1240d8d64acbSManos Pitsidianakis buffer->data, 1241d8d64acbSManos Pitsidianakis buffer->size); 1242d8d64acbSManos Pitsidianakis iov_from_buf(buffer->elem->in_sg, 1243d8d64acbSManos Pitsidianakis buffer->elem->in_num, 1244d8d64acbSManos Pitsidianakis buffer->size, 1245d8d64acbSManos Pitsidianakis &resp, 1246d8d64acbSManos Pitsidianakis sizeof(virtio_snd_pcm_status)); 1247d8d64acbSManos Pitsidianakis virtqueue_push(buffer->vq, 1248d8d64acbSManos Pitsidianakis buffer->elem, 1249d8d64acbSManos Pitsidianakis sizeof(virtio_snd_pcm_status) + buffer->size); 1250d8d64acbSManos Pitsidianakis virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq); 1251d8d64acbSManos Pitsidianakis QSIMPLEQ_REMOVE(&stream->queue, 1252d8d64acbSManos Pitsidianakis buffer, 1253d8d64acbSManos Pitsidianakis VirtIOSoundPCMBuffer, 1254d8d64acbSManos Pitsidianakis entry); 1255d8d64acbSManos Pitsidianakis virtio_snd_pcm_buffer_free(buffer); 1256d8d64acbSManos Pitsidianakis } 1257d8d64acbSManos Pitsidianakis 1258d8d64acbSManos Pitsidianakis 1259d8d64acbSManos Pitsidianakis /* 1260d8d64acbSManos Pitsidianakis * AUD_* input callback. 1261d8d64acbSManos Pitsidianakis * 1262d8d64acbSManos Pitsidianakis * @data: VirtIOSoundPCMStream stream 1263d8d64acbSManos Pitsidianakis * @available: number of bytes that can be read with AUD_read() 1264d8d64acbSManos Pitsidianakis */ 1265d8d64acbSManos Pitsidianakis static void virtio_snd_pcm_in_cb(void *data, int available) 1266d8d64acbSManos Pitsidianakis { 1267d8d64acbSManos Pitsidianakis VirtIOSoundPCMStream *stream = data; 1268d8d64acbSManos Pitsidianakis VirtIOSoundPCMBuffer *buffer; 1269d8d64acbSManos Pitsidianakis size_t size; 1270d8d64acbSManos Pitsidianakis 1271d8d64acbSManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { 1272d8d64acbSManos Pitsidianakis while (!QSIMPLEQ_EMPTY(&stream->queue)) { 1273d8d64acbSManos Pitsidianakis buffer = QSIMPLEQ_FIRST(&stream->queue); 1274d8d64acbSManos Pitsidianakis if (!virtio_queue_ready(buffer->vq)) { 1275d8d64acbSManos Pitsidianakis return; 1276d8d64acbSManos Pitsidianakis } 1277d8d64acbSManos Pitsidianakis if (!stream->active) { 1278d8d64acbSManos Pitsidianakis /* Stream has stopped, so do not perform AUD_read. */ 1279d8d64acbSManos Pitsidianakis return_rx_buffer(stream, buffer); 1280d8d64acbSManos Pitsidianakis continue; 1281d8d64acbSManos Pitsidianakis } 1282d8d64acbSManos Pitsidianakis 1283d8d64acbSManos Pitsidianakis for (;;) { 1284d8d64acbSManos Pitsidianakis size = AUD_read(stream->voice.in, 1285d8d64acbSManos Pitsidianakis buffer->data + buffer->size, 1286d8d64acbSManos Pitsidianakis MIN(available, (stream->params.period_bytes - 1287d8d64acbSManos Pitsidianakis buffer->size))); 1288d8d64acbSManos Pitsidianakis if (!size) { 1289d8d64acbSManos Pitsidianakis available = 0; 1290d8d64acbSManos Pitsidianakis break; 1291d8d64acbSManos Pitsidianakis } 1292d8d64acbSManos Pitsidianakis buffer->size += size; 1293d8d64acbSManos Pitsidianakis available -= size; 1294d8d64acbSManos Pitsidianakis if (buffer->size >= stream->params.period_bytes) { 1295d8d64acbSManos Pitsidianakis return_rx_buffer(stream, buffer); 1296d8d64acbSManos Pitsidianakis break; 1297d8d64acbSManos Pitsidianakis } 1298d8d64acbSManos Pitsidianakis if (!available) { 1299d8d64acbSManos Pitsidianakis break; 1300d8d64acbSManos Pitsidianakis } 1301d8d64acbSManos Pitsidianakis } 1302d8d64acbSManos Pitsidianakis if (!available) { 1303d8d64acbSManos Pitsidianakis break; 1304d8d64acbSManos Pitsidianakis } 1305d8d64acbSManos Pitsidianakis } 1306d8d64acbSManos Pitsidianakis } 1307d8d64acbSManos Pitsidianakis } 1308d8d64acbSManos Pitsidianakis 1309d8d64acbSManos Pitsidianakis /* 1310d8d64acbSManos Pitsidianakis * Flush all buffer data from this output stream's queue into the driver's 1311d8d64acbSManos Pitsidianakis * virtual queue. 131218a75281SManos Pitsidianakis * 131318a75281SManos Pitsidianakis * @stream: VirtIOSoundPCMStream *stream 131418a75281SManos Pitsidianakis */ 131518a75281SManos Pitsidianakis static inline void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream) 131618a75281SManos Pitsidianakis { 131718a75281SManos Pitsidianakis VirtIOSoundPCMBuffer *buffer; 1318d8d64acbSManos Pitsidianakis void (*cb)(VirtIOSoundPCMStream *, VirtIOSoundPCMBuffer *) = 1319d8d64acbSManos Pitsidianakis (stream->info.direction == VIRTIO_SND_D_OUTPUT) ? return_tx_buffer : 1320d8d64acbSManos Pitsidianakis return_rx_buffer; 132118a75281SManos Pitsidianakis 132218a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { 132318a75281SManos Pitsidianakis while (!QSIMPLEQ_EMPTY(&stream->queue)) { 132418a75281SManos Pitsidianakis buffer = QSIMPLEQ_FIRST(&stream->queue); 1325d8d64acbSManos Pitsidianakis cb(stream, buffer); 132618a75281SManos Pitsidianakis } 132718a75281SManos Pitsidianakis } 132818a75281SManos Pitsidianakis } 132918a75281SManos Pitsidianakis 13302880e676SManos Pitsidianakis static void virtio_snd_unrealize(DeviceState *dev) 13312880e676SManos Pitsidianakis { 13322880e676SManos Pitsidianakis VirtIODevice *vdev = VIRTIO_DEVICE(dev); 13332880e676SManos Pitsidianakis VirtIOSound *vsnd = VIRTIO_SND(dev); 1334eb9ad377SManos Pitsidianakis VirtIOSoundPCMStream *stream; 13352880e676SManos Pitsidianakis 13362880e676SManos Pitsidianakis qemu_del_vm_change_state_handler(vsnd->vmstate); 13372880e676SManos Pitsidianakis trace_virtio_snd_unrealize(vsnd); 13382880e676SManos Pitsidianakis 1339eb9ad377SManos Pitsidianakis if (vsnd->pcm) { 1340eb9ad377SManos Pitsidianakis if (vsnd->pcm->streams) { 1341eb9ad377SManos Pitsidianakis for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) { 1342eb9ad377SManos Pitsidianakis stream = vsnd->pcm->streams[i]; 1343eb9ad377SManos Pitsidianakis if (stream) { 1344eb9ad377SManos Pitsidianakis virtio_snd_process_cmdq(stream->s); 1345eb9ad377SManos Pitsidianakis virtio_snd_pcm_close(stream); 134618a75281SManos Pitsidianakis qemu_mutex_destroy(&stream->queue_mutex); 1347eb9ad377SManos Pitsidianakis g_free(stream); 1348eb9ad377SManos Pitsidianakis } 1349eb9ad377SManos Pitsidianakis } 1350eb9ad377SManos Pitsidianakis g_free(vsnd->pcm->streams); 1351eb9ad377SManos Pitsidianakis } 1352eb9ad377SManos Pitsidianakis g_free(vsnd->pcm->pcm_params); 1353eb9ad377SManos Pitsidianakis g_free(vsnd->pcm); 1354eb9ad377SManos Pitsidianakis vsnd->pcm = NULL; 1355eb9ad377SManos Pitsidianakis } 13562880e676SManos Pitsidianakis AUD_remove_card(&vsnd->card); 1357eb9ad377SManos Pitsidianakis qemu_mutex_destroy(&vsnd->cmdq_mutex); 13582880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]); 13592880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]); 13602880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]); 13612880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_RX]); 13622880e676SManos Pitsidianakis virtio_cleanup(vdev); 13632880e676SManos Pitsidianakis } 13642880e676SManos Pitsidianakis 13652880e676SManos Pitsidianakis 1366eb9ad377SManos Pitsidianakis static void virtio_snd_reset(VirtIODevice *vdev) 1367eb9ad377SManos Pitsidianakis { 1368eb9ad377SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 1369eb9ad377SManos Pitsidianakis virtio_snd_ctrl_command *cmd; 1370eb9ad377SManos Pitsidianakis 1371eb9ad377SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&s->cmdq_mutex) { 1372eb9ad377SManos Pitsidianakis while (!QTAILQ_EMPTY(&s->cmdq)) { 1373eb9ad377SManos Pitsidianakis cmd = QTAILQ_FIRST(&s->cmdq); 1374eb9ad377SManos Pitsidianakis QTAILQ_REMOVE(&s->cmdq, cmd, next); 1375eb9ad377SManos Pitsidianakis virtio_snd_ctrl_cmd_free(cmd); 1376eb9ad377SManos Pitsidianakis } 1377eb9ad377SManos Pitsidianakis } 1378eb9ad377SManos Pitsidianakis } 13792880e676SManos Pitsidianakis 13802880e676SManos Pitsidianakis static void virtio_snd_class_init(ObjectClass *klass, void *data) 13812880e676SManos Pitsidianakis { 13822880e676SManos Pitsidianakis DeviceClass *dc = DEVICE_CLASS(klass); 13832880e676SManos Pitsidianakis VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 13842880e676SManos Pitsidianakis 13852880e676SManos Pitsidianakis 13862880e676SManos Pitsidianakis set_bit(DEVICE_CATEGORY_SOUND, dc->categories); 13872880e676SManos Pitsidianakis device_class_set_props(dc, virtio_snd_properties); 13882880e676SManos Pitsidianakis 13892880e676SManos Pitsidianakis dc->vmsd = &vmstate_virtio_snd; 13902880e676SManos Pitsidianakis vdc->vmsd = &vmstate_virtio_snd_device; 13912880e676SManos Pitsidianakis vdc->realize = virtio_snd_realize; 13922880e676SManos Pitsidianakis vdc->unrealize = virtio_snd_unrealize; 13932880e676SManos Pitsidianakis vdc->get_config = virtio_snd_get_config; 13942880e676SManos Pitsidianakis vdc->set_config = virtio_snd_set_config; 13952880e676SManos Pitsidianakis vdc->get_features = get_features; 13962880e676SManos Pitsidianakis vdc->reset = virtio_snd_reset; 13972880e676SManos Pitsidianakis vdc->legacy_features = 0; 13982880e676SManos Pitsidianakis } 13992880e676SManos Pitsidianakis 14002880e676SManos Pitsidianakis static const TypeInfo virtio_snd_types[] = { 14012880e676SManos Pitsidianakis { 14022880e676SManos Pitsidianakis .name = TYPE_VIRTIO_SND, 14032880e676SManos Pitsidianakis .parent = TYPE_VIRTIO_DEVICE, 14042880e676SManos Pitsidianakis .instance_size = sizeof(VirtIOSound), 14052880e676SManos Pitsidianakis .class_init = virtio_snd_class_init, 14062880e676SManos Pitsidianakis } 14072880e676SManos Pitsidianakis }; 14082880e676SManos Pitsidianakis 14092880e676SManos Pitsidianakis DEFINE_TYPES(virtio_snd_types) 1410