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 312880e676SManos Pitsidianakis #define VIRTIO_SOUND_STREAM_DEFAULT 1 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); 3818a75281SManos Pitsidianakis 39eb9ad377SManos Pitsidianakis static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8) 40eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_U8) 41eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_S16) 42eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_U16) 43eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_S32) 44eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_U32) 45eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_FLOAT); 46eb9ad377SManos Pitsidianakis 47eb9ad377SManos Pitsidianakis static uint32_t supported_rates = BIT(VIRTIO_SND_PCM_RATE_5512) 48eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_8000) 49eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_11025) 50eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_16000) 51eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_22050) 52eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_32000) 53eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_44100) 54eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_48000) 55eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_64000) 56eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_88200) 57eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_96000) 58eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_176400) 59eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_192000) 60eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_384000); 61eb9ad377SManos Pitsidianakis 622880e676SManos Pitsidianakis static const VMStateDescription vmstate_virtio_snd_device = { 632880e676SManos Pitsidianakis .name = TYPE_VIRTIO_SND, 642880e676SManos Pitsidianakis .version_id = VIRTIO_SOUND_VM_VERSION, 652880e676SManos Pitsidianakis .minimum_version_id = VIRTIO_SOUND_VM_VERSION, 662880e676SManos Pitsidianakis }; 672880e676SManos Pitsidianakis 682880e676SManos Pitsidianakis static const VMStateDescription vmstate_virtio_snd = { 692880e676SManos Pitsidianakis .name = TYPE_VIRTIO_SND, 702880e676SManos Pitsidianakis .minimum_version_id = VIRTIO_SOUND_VM_VERSION, 712880e676SManos Pitsidianakis .version_id = VIRTIO_SOUND_VM_VERSION, 722880e676SManos Pitsidianakis .fields = (VMStateField[]) { 732880e676SManos Pitsidianakis VMSTATE_VIRTIO_DEVICE, 742880e676SManos Pitsidianakis VMSTATE_END_OF_LIST() 752880e676SManos Pitsidianakis }, 762880e676SManos Pitsidianakis }; 772880e676SManos Pitsidianakis 782880e676SManos Pitsidianakis static Property virtio_snd_properties[] = { 792880e676SManos Pitsidianakis DEFINE_AUDIO_PROPERTIES(VirtIOSound, card), 802880e676SManos Pitsidianakis DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks, 812880e676SManos Pitsidianakis VIRTIO_SOUND_JACK_DEFAULT), 822880e676SManos Pitsidianakis DEFINE_PROP_UINT32("streams", VirtIOSound, snd_conf.streams, 832880e676SManos Pitsidianakis VIRTIO_SOUND_STREAM_DEFAULT), 842880e676SManos Pitsidianakis DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps, 852880e676SManos Pitsidianakis VIRTIO_SOUND_CHMAP_DEFAULT), 862880e676SManos Pitsidianakis DEFINE_PROP_END_OF_LIST(), 872880e676SManos Pitsidianakis }; 882880e676SManos Pitsidianakis 892880e676SManos Pitsidianakis static void 902880e676SManos Pitsidianakis virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config) 912880e676SManos Pitsidianakis { 922880e676SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 932880e676SManos Pitsidianakis virtio_snd_config *sndconfig = 942880e676SManos Pitsidianakis (virtio_snd_config *)config; 952880e676SManos Pitsidianakis trace_virtio_snd_get_config(vdev, 962880e676SManos Pitsidianakis s->snd_conf.jacks, 972880e676SManos Pitsidianakis s->snd_conf.streams, 982880e676SManos Pitsidianakis s->snd_conf.chmaps); 992880e676SManos Pitsidianakis 1002880e676SManos Pitsidianakis memcpy(sndconfig, &s->snd_conf, sizeof(s->snd_conf)); 1012880e676SManos Pitsidianakis cpu_to_le32s(&sndconfig->jacks); 1022880e676SManos Pitsidianakis cpu_to_le32s(&sndconfig->streams); 1032880e676SManos Pitsidianakis cpu_to_le32s(&sndconfig->chmaps); 1042880e676SManos Pitsidianakis 1052880e676SManos Pitsidianakis } 1062880e676SManos Pitsidianakis 1072880e676SManos Pitsidianakis static void 1082880e676SManos Pitsidianakis virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config) 1092880e676SManos Pitsidianakis { 1102880e676SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 1112880e676SManos Pitsidianakis const virtio_snd_config *sndconfig = 1122880e676SManos Pitsidianakis (const virtio_snd_config *)config; 1132880e676SManos Pitsidianakis 1142880e676SManos Pitsidianakis 1152880e676SManos Pitsidianakis trace_virtio_snd_set_config(vdev, 1162880e676SManos Pitsidianakis s->snd_conf.jacks, 1172880e676SManos Pitsidianakis sndconfig->jacks, 1182880e676SManos Pitsidianakis s->snd_conf.streams, 1192880e676SManos Pitsidianakis sndconfig->streams, 1202880e676SManos Pitsidianakis s->snd_conf.chmaps, 1212880e676SManos Pitsidianakis sndconfig->chmaps); 1222880e676SManos Pitsidianakis 1232880e676SManos Pitsidianakis memcpy(&s->snd_conf, sndconfig, sizeof(virtio_snd_config)); 1242880e676SManos Pitsidianakis le32_to_cpus(&s->snd_conf.jacks); 1252880e676SManos Pitsidianakis le32_to_cpus(&s->snd_conf.streams); 1262880e676SManos Pitsidianakis le32_to_cpus(&s->snd_conf.chmaps); 1272880e676SManos Pitsidianakis 1282880e676SManos Pitsidianakis } 1292880e676SManos Pitsidianakis 130eb9ad377SManos Pitsidianakis static void 13118a75281SManos Pitsidianakis virtio_snd_pcm_buffer_free(VirtIOSoundPCMBuffer *buffer) 13218a75281SManos Pitsidianakis { 13318a75281SManos Pitsidianakis g_free(buffer->elem); 13418a75281SManos Pitsidianakis g_free(buffer); 13518a75281SManos Pitsidianakis } 13618a75281SManos Pitsidianakis 13718a75281SManos Pitsidianakis static void 138eb9ad377SManos Pitsidianakis virtio_snd_ctrl_cmd_free(virtio_snd_ctrl_command *cmd) 139eb9ad377SManos Pitsidianakis { 140eb9ad377SManos Pitsidianakis g_free(cmd->elem); 141eb9ad377SManos Pitsidianakis g_free(cmd); 142eb9ad377SManos Pitsidianakis } 143eb9ad377SManos Pitsidianakis 1442880e676SManos Pitsidianakis /* 145eb9ad377SManos Pitsidianakis * Get a specific stream from the virtio sound card device. 146eb9ad377SManos Pitsidianakis * Returns NULL if @stream_id is invalid or not allocated. 147eb9ad377SManos Pitsidianakis * 148eb9ad377SManos Pitsidianakis * @s: VirtIOSound device 149eb9ad377SManos Pitsidianakis * @stream_id: stream id 150eb9ad377SManos Pitsidianakis */ 151eb9ad377SManos Pitsidianakis static VirtIOSoundPCMStream *virtio_snd_pcm_get_stream(VirtIOSound *s, 152eb9ad377SManos Pitsidianakis uint32_t stream_id) 153eb9ad377SManos Pitsidianakis { 154eb9ad377SManos Pitsidianakis return stream_id >= s->snd_conf.streams ? NULL : 155eb9ad377SManos Pitsidianakis s->pcm->streams[stream_id]; 156eb9ad377SManos Pitsidianakis } 157eb9ad377SManos Pitsidianakis 158eb9ad377SManos Pitsidianakis /* 159eb9ad377SManos Pitsidianakis * Get params for a specific stream. 160eb9ad377SManos Pitsidianakis * 161eb9ad377SManos Pitsidianakis * @s: VirtIOSound device 162eb9ad377SManos Pitsidianakis * @stream_id: stream id 163eb9ad377SManos Pitsidianakis */ 164eb9ad377SManos Pitsidianakis static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s, 165eb9ad377SManos Pitsidianakis uint32_t stream_id) 166eb9ad377SManos Pitsidianakis { 167eb9ad377SManos Pitsidianakis return stream_id >= s->snd_conf.streams ? NULL 168eb9ad377SManos Pitsidianakis : &s->pcm->pcm_params[stream_id]; 169eb9ad377SManos Pitsidianakis } 170eb9ad377SManos Pitsidianakis 171eb9ad377SManos Pitsidianakis /* 1720ff05dd2SManos Pitsidianakis * Handle the VIRTIO_SND_R_PCM_INFO request. 1730ff05dd2SManos Pitsidianakis * The function writes the info structs to the request element. 1740ff05dd2SManos Pitsidianakis * 1750ff05dd2SManos Pitsidianakis * @s: VirtIOSound device 1760ff05dd2SManos Pitsidianakis * @cmd: The request command queue element from VirtIOSound cmdq field 1770ff05dd2SManos Pitsidianakis */ 1780ff05dd2SManos Pitsidianakis static void virtio_snd_handle_pcm_info(VirtIOSound *s, 1790ff05dd2SManos Pitsidianakis virtio_snd_ctrl_command *cmd) 1800ff05dd2SManos Pitsidianakis { 1810ff05dd2SManos Pitsidianakis uint32_t stream_id, start_id, count, size; 1820ff05dd2SManos Pitsidianakis virtio_snd_pcm_info val; 1830ff05dd2SManos Pitsidianakis virtio_snd_query_info req; 1840ff05dd2SManos Pitsidianakis VirtIOSoundPCMStream *stream = NULL; 1850ff05dd2SManos Pitsidianakis g_autofree virtio_snd_pcm_info *pcm_info = NULL; 1860ff05dd2SManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg, 1870ff05dd2SManos Pitsidianakis cmd->elem->out_num, 1880ff05dd2SManos Pitsidianakis 0, 1890ff05dd2SManos Pitsidianakis &req, 1900ff05dd2SManos Pitsidianakis sizeof(virtio_snd_query_info)); 1910ff05dd2SManos Pitsidianakis 1920ff05dd2SManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_query_info)) { 1930ff05dd2SManos Pitsidianakis /* 1940ff05dd2SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 1950ff05dd2SManos Pitsidianakis */ 1960ff05dd2SManos Pitsidianakis qemu_log_mask(LOG_GUEST_ERROR, 1970ff05dd2SManos Pitsidianakis "%s: virtio-snd command size incorrect %zu vs \ 1980ff05dd2SManos Pitsidianakis %zu\n", __func__, msg_sz, sizeof(virtio_snd_query_info)); 1990ff05dd2SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 2000ff05dd2SManos Pitsidianakis return; 2010ff05dd2SManos Pitsidianakis } 2020ff05dd2SManos Pitsidianakis 2030ff05dd2SManos Pitsidianakis start_id = le32_to_cpu(req.start_id); 2040ff05dd2SManos Pitsidianakis count = le32_to_cpu(req.count); 2050ff05dd2SManos Pitsidianakis size = le32_to_cpu(req.size); 2060ff05dd2SManos Pitsidianakis 2070ff05dd2SManos Pitsidianakis if (iov_size(cmd->elem->in_sg, cmd->elem->in_num) < 2080ff05dd2SManos Pitsidianakis sizeof(virtio_snd_hdr) + size * count) { 2090ff05dd2SManos Pitsidianakis /* 2100ff05dd2SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 2110ff05dd2SManos Pitsidianakis */ 2120ff05dd2SManos Pitsidianakis error_report("pcm info: buffer too small, got: %zu, needed: %zu", 2130ff05dd2SManos Pitsidianakis iov_size(cmd->elem->in_sg, cmd->elem->in_num), 2140ff05dd2SManos Pitsidianakis sizeof(virtio_snd_pcm_info)); 2150ff05dd2SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 2160ff05dd2SManos Pitsidianakis return; 2170ff05dd2SManos Pitsidianakis } 2180ff05dd2SManos Pitsidianakis 2190ff05dd2SManos Pitsidianakis pcm_info = g_new0(virtio_snd_pcm_info, count); 2200ff05dd2SManos Pitsidianakis for (uint32_t i = 0; i < count; i++) { 2210ff05dd2SManos Pitsidianakis stream_id = i + start_id; 2220ff05dd2SManos Pitsidianakis trace_virtio_snd_handle_pcm_info(stream_id); 2230ff05dd2SManos Pitsidianakis stream = virtio_snd_pcm_get_stream(s, stream_id); 2240ff05dd2SManos Pitsidianakis if (!stream) { 2250ff05dd2SManos Pitsidianakis error_report("Invalid stream id: %"PRIu32, stream_id); 2260ff05dd2SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 2270ff05dd2SManos Pitsidianakis return; 2280ff05dd2SManos Pitsidianakis } 2290ff05dd2SManos Pitsidianakis val = stream->info; 2300ff05dd2SManos Pitsidianakis val.hdr.hda_fn_nid = cpu_to_le32(val.hdr.hda_fn_nid); 2310ff05dd2SManos Pitsidianakis val.features = cpu_to_le32(val.features); 2320ff05dd2SManos Pitsidianakis val.formats = cpu_to_le64(val.formats); 2330ff05dd2SManos Pitsidianakis val.rates = cpu_to_le64(val.rates); 2340ff05dd2SManos Pitsidianakis /* 2350ff05dd2SManos Pitsidianakis * 5.14.6.6.2.1 Device Requirements: Stream Information The device MUST 2360ff05dd2SManos Pitsidianakis * NOT set undefined feature, format, rate and direction values. The 2370ff05dd2SManos Pitsidianakis * device MUST initialize the padding bytes to 0. 2380ff05dd2SManos Pitsidianakis */ 2390ff05dd2SManos Pitsidianakis pcm_info[i] = val; 2400ff05dd2SManos Pitsidianakis memset(&pcm_info[i].padding, 0, 5); 2410ff05dd2SManos Pitsidianakis } 2420ff05dd2SManos Pitsidianakis 2430ff05dd2SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); 2440ff05dd2SManos Pitsidianakis iov_from_buf(cmd->elem->in_sg, 2450ff05dd2SManos Pitsidianakis cmd->elem->in_num, 2460ff05dd2SManos Pitsidianakis sizeof(virtio_snd_hdr), 2470ff05dd2SManos Pitsidianakis pcm_info, 2480ff05dd2SManos Pitsidianakis sizeof(virtio_snd_pcm_info) * count); 2490ff05dd2SManos Pitsidianakis } 2500ff05dd2SManos Pitsidianakis 2510ff05dd2SManos Pitsidianakis /* 252eb9ad377SManos Pitsidianakis * Set the given stream params. 253eb9ad377SManos Pitsidianakis * Called by both virtio_snd_handle_pcm_set_params and during device 254eb9ad377SManos Pitsidianakis * initialization. 255eb9ad377SManos Pitsidianakis * Returns the response status code. (VIRTIO_SND_S_*). 256eb9ad377SManos Pitsidianakis * 257eb9ad377SManos Pitsidianakis * @s: VirtIOSound device 258eb9ad377SManos Pitsidianakis * @params: The PCM params as defined in the virtio specification 259eb9ad377SManos Pitsidianakis */ 260eb9ad377SManos Pitsidianakis static 261eb9ad377SManos Pitsidianakis uint32_t virtio_snd_set_pcm_params(VirtIOSound *s, 262eb9ad377SManos Pitsidianakis uint32_t stream_id, 263eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params *params) 264eb9ad377SManos Pitsidianakis { 265eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params *st_params; 266eb9ad377SManos Pitsidianakis 267eb9ad377SManos Pitsidianakis if (stream_id >= s->snd_conf.streams || s->pcm->pcm_params == NULL) { 268eb9ad377SManos Pitsidianakis /* 269eb9ad377SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 270eb9ad377SManos Pitsidianakis */ 271eb9ad377SManos Pitsidianakis virtio_error(VIRTIO_DEVICE(s), "Streams have not been initialized.\n"); 272eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 273eb9ad377SManos Pitsidianakis } 274eb9ad377SManos Pitsidianakis 275eb9ad377SManos Pitsidianakis st_params = virtio_snd_pcm_get_params(s, stream_id); 276eb9ad377SManos Pitsidianakis 277eb9ad377SManos Pitsidianakis if (params->channels < 1 || params->channels > AUDIO_MAX_CHANNELS) { 278eb9ad377SManos Pitsidianakis error_report("Number of channels is not supported."); 279eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); 280eb9ad377SManos Pitsidianakis } 281eb9ad377SManos Pitsidianakis if (!(supported_formats & BIT(params->format))) { 282eb9ad377SManos Pitsidianakis error_report("Stream format is not supported."); 283eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); 284eb9ad377SManos Pitsidianakis } 285eb9ad377SManos Pitsidianakis if (!(supported_rates & BIT(params->rate))) { 286eb9ad377SManos Pitsidianakis error_report("Stream rate is not supported."); 287eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); 288eb9ad377SManos Pitsidianakis } 289eb9ad377SManos Pitsidianakis 290eb9ad377SManos Pitsidianakis st_params->buffer_bytes = le32_to_cpu(params->buffer_bytes); 291eb9ad377SManos Pitsidianakis st_params->period_bytes = le32_to_cpu(params->period_bytes); 292eb9ad377SManos Pitsidianakis st_params->features = le32_to_cpu(params->features); 293eb9ad377SManos Pitsidianakis /* the following are uint8_t, so there's no need to bswap the values. */ 294eb9ad377SManos Pitsidianakis st_params->channels = params->channels; 295eb9ad377SManos Pitsidianakis st_params->format = params->format; 296eb9ad377SManos Pitsidianakis st_params->rate = params->rate; 297eb9ad377SManos Pitsidianakis 298eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_OK); 299eb9ad377SManos Pitsidianakis } 300eb9ad377SManos Pitsidianakis 301eb9ad377SManos Pitsidianakis /* 30264704ce0SManos Pitsidianakis * Handles the VIRTIO_SND_R_PCM_SET_PARAMS request. 30364704ce0SManos Pitsidianakis * 30464704ce0SManos Pitsidianakis * @s: VirtIOSound device 30564704ce0SManos Pitsidianakis * @cmd: The request command queue element from VirtIOSound cmdq field 30664704ce0SManos Pitsidianakis */ 30764704ce0SManos Pitsidianakis static void virtio_snd_handle_pcm_set_params(VirtIOSound *s, 30864704ce0SManos Pitsidianakis virtio_snd_ctrl_command *cmd) 30964704ce0SManos Pitsidianakis { 31064704ce0SManos Pitsidianakis virtio_snd_pcm_set_params req = { 0 }; 31164704ce0SManos Pitsidianakis uint32_t stream_id; 31264704ce0SManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg, 31364704ce0SManos Pitsidianakis cmd->elem->out_num, 31464704ce0SManos Pitsidianakis 0, 31564704ce0SManos Pitsidianakis &req, 31664704ce0SManos Pitsidianakis sizeof(virtio_snd_pcm_set_params)); 31764704ce0SManos Pitsidianakis 31864704ce0SManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_pcm_set_params)) { 31964704ce0SManos Pitsidianakis /* 32064704ce0SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 32164704ce0SManos Pitsidianakis */ 32264704ce0SManos Pitsidianakis qemu_log_mask(LOG_GUEST_ERROR, 32364704ce0SManos Pitsidianakis "%s: virtio-snd command size incorrect %zu vs \ 32464704ce0SManos Pitsidianakis %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_set_params)); 32564704ce0SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 32664704ce0SManos Pitsidianakis return; 32764704ce0SManos Pitsidianakis } 32864704ce0SManos Pitsidianakis stream_id = le32_to_cpu(req.hdr.stream_id); 32964704ce0SManos Pitsidianakis trace_virtio_snd_handle_pcm_set_params(stream_id); 33064704ce0SManos Pitsidianakis cmd->resp.code = virtio_snd_set_pcm_params(s, stream_id, &req); 33164704ce0SManos Pitsidianakis } 33264704ce0SManos Pitsidianakis 33364704ce0SManos Pitsidianakis /* 334eb9ad377SManos Pitsidianakis * Get a QEMU Audiosystem compatible format value from a VIRTIO_SND_PCM_FMT_* 335eb9ad377SManos Pitsidianakis */ 336eb9ad377SManos Pitsidianakis static AudioFormat virtio_snd_get_qemu_format(uint32_t format) 337eb9ad377SManos Pitsidianakis { 338eb9ad377SManos Pitsidianakis #define CASE(FMT) \ 339eb9ad377SManos Pitsidianakis case VIRTIO_SND_PCM_FMT_##FMT: \ 340eb9ad377SManos Pitsidianakis return AUDIO_FORMAT_##FMT; 341eb9ad377SManos Pitsidianakis 342eb9ad377SManos Pitsidianakis switch (format) { 343eb9ad377SManos Pitsidianakis CASE(U8) 344eb9ad377SManos Pitsidianakis CASE(S8) 345eb9ad377SManos Pitsidianakis CASE(U16) 346eb9ad377SManos Pitsidianakis CASE(S16) 347eb9ad377SManos Pitsidianakis CASE(U32) 348eb9ad377SManos Pitsidianakis CASE(S32) 349eb9ad377SManos Pitsidianakis case VIRTIO_SND_PCM_FMT_FLOAT: 350eb9ad377SManos Pitsidianakis return AUDIO_FORMAT_F32; 351eb9ad377SManos Pitsidianakis default: 352eb9ad377SManos Pitsidianakis g_assert_not_reached(); 353eb9ad377SManos Pitsidianakis } 354eb9ad377SManos Pitsidianakis 355eb9ad377SManos Pitsidianakis #undef CASE 356eb9ad377SManos Pitsidianakis } 357eb9ad377SManos Pitsidianakis 358eb9ad377SManos Pitsidianakis /* 359eb9ad377SManos Pitsidianakis * Get a QEMU Audiosystem compatible frequency value from a 360eb9ad377SManos Pitsidianakis * VIRTIO_SND_PCM_RATE_* 361eb9ad377SManos Pitsidianakis */ 362eb9ad377SManos Pitsidianakis static uint32_t virtio_snd_get_qemu_freq(uint32_t rate) 363eb9ad377SManos Pitsidianakis { 364eb9ad377SManos Pitsidianakis #define CASE(RATE) \ 365eb9ad377SManos Pitsidianakis case VIRTIO_SND_PCM_RATE_##RATE: \ 366eb9ad377SManos Pitsidianakis return RATE; 367eb9ad377SManos Pitsidianakis 368eb9ad377SManos Pitsidianakis switch (rate) { 369eb9ad377SManos Pitsidianakis CASE(5512) 370eb9ad377SManos Pitsidianakis CASE(8000) 371eb9ad377SManos Pitsidianakis CASE(11025) 372eb9ad377SManos Pitsidianakis CASE(16000) 373eb9ad377SManos Pitsidianakis CASE(22050) 374eb9ad377SManos Pitsidianakis CASE(32000) 375eb9ad377SManos Pitsidianakis CASE(44100) 376eb9ad377SManos Pitsidianakis CASE(48000) 377eb9ad377SManos Pitsidianakis CASE(64000) 378eb9ad377SManos Pitsidianakis CASE(88200) 379eb9ad377SManos Pitsidianakis CASE(96000) 380eb9ad377SManos Pitsidianakis CASE(176400) 381eb9ad377SManos Pitsidianakis CASE(192000) 382eb9ad377SManos Pitsidianakis CASE(384000) 383eb9ad377SManos Pitsidianakis default: 384eb9ad377SManos Pitsidianakis g_assert_not_reached(); 385eb9ad377SManos Pitsidianakis } 386eb9ad377SManos Pitsidianakis 387eb9ad377SManos Pitsidianakis #undef CASE 388eb9ad377SManos Pitsidianakis } 389eb9ad377SManos Pitsidianakis 390eb9ad377SManos Pitsidianakis /* 391eb9ad377SManos Pitsidianakis * Get QEMU Audiosystem compatible audsettings from virtio based pcm stream 392eb9ad377SManos Pitsidianakis * params. 393eb9ad377SManos Pitsidianakis */ 394eb9ad377SManos Pitsidianakis static void virtio_snd_get_qemu_audsettings(audsettings *as, 395eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params *params) 396eb9ad377SManos Pitsidianakis { 397eb9ad377SManos Pitsidianakis as->nchannels = MIN(AUDIO_MAX_CHANNELS, params->channels); 398eb9ad377SManos Pitsidianakis as->fmt = virtio_snd_get_qemu_format(params->format); 399eb9ad377SManos Pitsidianakis as->freq = virtio_snd_get_qemu_freq(params->rate); 400eb9ad377SManos Pitsidianakis as->endianness = target_words_bigendian() ? 1 : 0; 401eb9ad377SManos Pitsidianakis } 402eb9ad377SManos Pitsidianakis 403eb9ad377SManos Pitsidianakis /* 404eb9ad377SManos Pitsidianakis * Close a stream and free all its resources. 405eb9ad377SManos Pitsidianakis * 406eb9ad377SManos Pitsidianakis * @stream: VirtIOSoundPCMStream *stream 407eb9ad377SManos Pitsidianakis */ 408eb9ad377SManos Pitsidianakis static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream) 409eb9ad377SManos Pitsidianakis { 41018a75281SManos Pitsidianakis if (stream) { 41118a75281SManos Pitsidianakis if (stream->info.direction == VIRTIO_SND_D_OUTPUT) { 41218a75281SManos Pitsidianakis virtio_snd_pcm_flush(stream); 41318a75281SManos Pitsidianakis AUD_close_out(&stream->pcm->snd->card, stream->voice.out); 41418a75281SManos Pitsidianakis stream->voice.out = NULL; 41518a75281SManos Pitsidianakis } 41618a75281SManos Pitsidianakis } 417eb9ad377SManos Pitsidianakis } 418eb9ad377SManos Pitsidianakis 419eb9ad377SManos Pitsidianakis /* 420eb9ad377SManos Pitsidianakis * Prepares a VirtIOSound card stream. 421eb9ad377SManos Pitsidianakis * Returns the response status code. (VIRTIO_SND_S_*). 422eb9ad377SManos Pitsidianakis * 423eb9ad377SManos Pitsidianakis * @s: VirtIOSound device 424eb9ad377SManos Pitsidianakis * @stream_id: stream id 425eb9ad377SManos Pitsidianakis */ 426eb9ad377SManos Pitsidianakis static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id) 427eb9ad377SManos Pitsidianakis { 428eb9ad377SManos Pitsidianakis audsettings as; 429eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params *params; 430eb9ad377SManos Pitsidianakis VirtIOSoundPCMStream *stream; 431eb9ad377SManos Pitsidianakis 432eb9ad377SManos Pitsidianakis if (s->pcm->streams == NULL || 433eb9ad377SManos Pitsidianakis s->pcm->pcm_params == NULL || 434eb9ad377SManos Pitsidianakis stream_id >= s->snd_conf.streams) { 435eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 436eb9ad377SManos Pitsidianakis } 437eb9ad377SManos Pitsidianakis 438eb9ad377SManos Pitsidianakis params = virtio_snd_pcm_get_params(s, stream_id); 439eb9ad377SManos Pitsidianakis if (params == NULL) { 440eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 441eb9ad377SManos Pitsidianakis } 442eb9ad377SManos Pitsidianakis 443eb9ad377SManos Pitsidianakis stream = virtio_snd_pcm_get_stream(s, stream_id); 444eb9ad377SManos Pitsidianakis if (stream == NULL) { 445eb9ad377SManos Pitsidianakis stream = g_new0(VirtIOSoundPCMStream, 1); 446eb9ad377SManos Pitsidianakis stream->active = false; 447eb9ad377SManos Pitsidianakis stream->id = stream_id; 448eb9ad377SManos Pitsidianakis stream->pcm = s->pcm; 449eb9ad377SManos Pitsidianakis stream->s = s; 45018a75281SManos Pitsidianakis qemu_mutex_init(&stream->queue_mutex); 45118a75281SManos Pitsidianakis QSIMPLEQ_INIT(&stream->queue); 45218a75281SManos Pitsidianakis QSIMPLEQ_INIT(&stream->invalid); 453eb9ad377SManos Pitsidianakis 454eb9ad377SManos Pitsidianakis /* 455eb9ad377SManos Pitsidianakis * stream_id >= s->snd_conf.streams was checked before so this is 456eb9ad377SManos Pitsidianakis * in-bounds 457eb9ad377SManos Pitsidianakis */ 458eb9ad377SManos Pitsidianakis s->pcm->streams[stream_id] = stream; 459eb9ad377SManos Pitsidianakis } 460eb9ad377SManos Pitsidianakis 461eb9ad377SManos Pitsidianakis virtio_snd_get_qemu_audsettings(&as, params); 462eb9ad377SManos Pitsidianakis stream->info.direction = stream_id < s->snd_conf.streams / 2 + 463eb9ad377SManos Pitsidianakis (s->snd_conf.streams & 1) ? VIRTIO_SND_D_OUTPUT : VIRTIO_SND_D_INPUT; 464eb9ad377SManos Pitsidianakis stream->info.hdr.hda_fn_nid = VIRTIO_SOUND_HDA_FN_NID; 465eb9ad377SManos Pitsidianakis stream->info.features = 0; 466eb9ad377SManos Pitsidianakis stream->info.channels_min = 1; 467eb9ad377SManos Pitsidianakis stream->info.channels_max = as.nchannels; 468eb9ad377SManos Pitsidianakis stream->info.formats = supported_formats; 469eb9ad377SManos Pitsidianakis stream->info.rates = supported_rates; 470eb9ad377SManos Pitsidianakis stream->params = *params; 471eb9ad377SManos Pitsidianakis 472eb9ad377SManos Pitsidianakis stream->positions[0] = VIRTIO_SND_CHMAP_FL; 473eb9ad377SManos Pitsidianakis stream->positions[1] = VIRTIO_SND_CHMAP_FR; 474eb9ad377SManos Pitsidianakis stream->as = as; 475eb9ad377SManos Pitsidianakis 47618a75281SManos Pitsidianakis if (stream->info.direction == VIRTIO_SND_D_OUTPUT) { 47718a75281SManos Pitsidianakis stream->voice.out = AUD_open_out(&s->card, 47818a75281SManos Pitsidianakis stream->voice.out, 47918a75281SManos Pitsidianakis "virtio-sound.out", 48018a75281SManos Pitsidianakis stream, 48118a75281SManos Pitsidianakis virtio_snd_pcm_out_cb, 48218a75281SManos Pitsidianakis &as); 48318a75281SManos Pitsidianakis AUD_set_volume_out(stream->voice.out, 0, 255, 255); 48418a75281SManos Pitsidianakis } else { 48518a75281SManos Pitsidianakis qemu_log_mask(LOG_UNIMP, "virtio_snd: input/capture is unimplemented."); 48618a75281SManos Pitsidianakis } 48718a75281SManos Pitsidianakis 488eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_OK); 489eb9ad377SManos Pitsidianakis } 490eb9ad377SManos Pitsidianakis 491eb9ad377SManos Pitsidianakis static const char *print_code(uint32_t code) 492eb9ad377SManos Pitsidianakis { 493eb9ad377SManos Pitsidianakis #define CASE(CODE) \ 494eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_##CODE: \ 495eb9ad377SManos Pitsidianakis return "VIRTIO_SND_R_"#CODE 496eb9ad377SManos Pitsidianakis 497eb9ad377SManos Pitsidianakis switch (code) { 498eb9ad377SManos Pitsidianakis CASE(JACK_INFO); 499eb9ad377SManos Pitsidianakis CASE(JACK_REMAP); 500eb9ad377SManos Pitsidianakis CASE(PCM_INFO); 501eb9ad377SManos Pitsidianakis CASE(PCM_SET_PARAMS); 502eb9ad377SManos Pitsidianakis CASE(PCM_PREPARE); 503eb9ad377SManos Pitsidianakis CASE(PCM_RELEASE); 504eb9ad377SManos Pitsidianakis CASE(PCM_START); 505eb9ad377SManos Pitsidianakis CASE(PCM_STOP); 506eb9ad377SManos Pitsidianakis CASE(CHMAP_INFO); 507eb9ad377SManos Pitsidianakis default: 508eb9ad377SManos Pitsidianakis return "invalid code"; 509eb9ad377SManos Pitsidianakis } 510eb9ad377SManos Pitsidianakis 511eb9ad377SManos Pitsidianakis #undef CASE 512eb9ad377SManos Pitsidianakis }; 513eb9ad377SManos Pitsidianakis 514eb9ad377SManos Pitsidianakis /* 515e5788b8fSManos Pitsidianakis * Handles VIRTIO_SND_R_PCM_PREPARE. 516e5788b8fSManos Pitsidianakis * 517e5788b8fSManos Pitsidianakis * @s: VirtIOSound device 518e5788b8fSManos Pitsidianakis * @cmd: The request command queue element from VirtIOSound cmdq field 519e5788b8fSManos Pitsidianakis */ 520e5788b8fSManos Pitsidianakis static void virtio_snd_handle_pcm_prepare(VirtIOSound *s, 521e5788b8fSManos Pitsidianakis virtio_snd_ctrl_command *cmd) 522e5788b8fSManos Pitsidianakis { 523e5788b8fSManos Pitsidianakis uint32_t stream_id; 524e5788b8fSManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg, 525e5788b8fSManos Pitsidianakis cmd->elem->out_num, 526e5788b8fSManos Pitsidianakis sizeof(virtio_snd_hdr), 527e5788b8fSManos Pitsidianakis &stream_id, 528e5788b8fSManos Pitsidianakis sizeof(stream_id)); 529e5788b8fSManos Pitsidianakis 530e5788b8fSManos Pitsidianakis stream_id = le32_to_cpu(stream_id); 531e5788b8fSManos Pitsidianakis cmd->resp.code = msg_sz == sizeof(stream_id) 532e5788b8fSManos Pitsidianakis ? virtio_snd_pcm_prepare(s, stream_id) 533e5788b8fSManos Pitsidianakis : cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 534e5788b8fSManos Pitsidianakis } 535e5788b8fSManos Pitsidianakis 536e5788b8fSManos Pitsidianakis /* 537fa131d4aSManos Pitsidianakis * Handles VIRTIO_SND_R_PCM_START. 538fa131d4aSManos Pitsidianakis * 539fa131d4aSManos Pitsidianakis * @s: VirtIOSound device 540fa131d4aSManos Pitsidianakis * @cmd: The request command queue element from VirtIOSound cmdq field 541fa131d4aSManos Pitsidianakis * @start: whether to start or stop the device 542fa131d4aSManos Pitsidianakis */ 543fa131d4aSManos Pitsidianakis static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s, 544fa131d4aSManos Pitsidianakis virtio_snd_ctrl_command *cmd, 545fa131d4aSManos Pitsidianakis bool start) 546fa131d4aSManos Pitsidianakis { 547fa131d4aSManos Pitsidianakis VirtIOSoundPCMStream *stream; 548fa131d4aSManos Pitsidianakis virtio_snd_pcm_hdr req; 549fa131d4aSManos Pitsidianakis uint32_t stream_id; 550fa131d4aSManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg, 551fa131d4aSManos Pitsidianakis cmd->elem->out_num, 552fa131d4aSManos Pitsidianakis 0, 553fa131d4aSManos Pitsidianakis &req, 554fa131d4aSManos Pitsidianakis sizeof(virtio_snd_pcm_hdr)); 555fa131d4aSManos Pitsidianakis 556fa131d4aSManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_pcm_hdr)) { 557fa131d4aSManos Pitsidianakis qemu_log_mask(LOG_GUEST_ERROR, 558fa131d4aSManos Pitsidianakis "%s: virtio-snd command size incorrect %zu vs \ 559fa131d4aSManos Pitsidianakis %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_hdr)); 560fa131d4aSManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 561fa131d4aSManos Pitsidianakis return; 562fa131d4aSManos Pitsidianakis } 563fa131d4aSManos Pitsidianakis 564fa131d4aSManos Pitsidianakis stream_id = le32_to_cpu(req.stream_id); 565fa131d4aSManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); 566fa131d4aSManos Pitsidianakis trace_virtio_snd_handle_pcm_start_stop(start ? "VIRTIO_SND_R_PCM_START" : 567fa131d4aSManos Pitsidianakis "VIRTIO_SND_R_PCM_STOP", stream_id); 56818a75281SManos Pitsidianakis 569fa131d4aSManos Pitsidianakis stream = virtio_snd_pcm_get_stream(s, stream_id); 57018a75281SManos Pitsidianakis if (stream) { 57118a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { 57218a75281SManos Pitsidianakis stream->active = start; 57318a75281SManos Pitsidianakis } 57418a75281SManos Pitsidianakis if (stream->info.direction == VIRTIO_SND_D_OUTPUT) { 57518a75281SManos Pitsidianakis AUD_set_active_out(stream->voice.out, start); 57618a75281SManos Pitsidianakis } 57718a75281SManos Pitsidianakis } else { 57818a75281SManos Pitsidianakis error_report("Invalid stream id: %"PRIu32, stream_id); 579fa131d4aSManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 580fa131d4aSManos Pitsidianakis return; 581fa131d4aSManos Pitsidianakis } 582fa131d4aSManos Pitsidianakis stream->active = start; 583fa131d4aSManos Pitsidianakis } 584fa131d4aSManos Pitsidianakis 585fa131d4aSManos Pitsidianakis /* 58618a75281SManos Pitsidianakis * Returns the number of I/O messages that are being processed. 58718a75281SManos Pitsidianakis * 58818a75281SManos Pitsidianakis * @stream: VirtIOSoundPCMStream 58918a75281SManos Pitsidianakis */ 59018a75281SManos Pitsidianakis static size_t virtio_snd_pcm_get_io_msgs_count(VirtIOSoundPCMStream *stream) 59118a75281SManos Pitsidianakis { 59218a75281SManos Pitsidianakis VirtIOSoundPCMBuffer *buffer, *next; 59318a75281SManos Pitsidianakis size_t count = 0; 59418a75281SManos Pitsidianakis 59518a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { 59618a75281SManos Pitsidianakis QSIMPLEQ_FOREACH_SAFE(buffer, &stream->queue, entry, next) { 59718a75281SManos Pitsidianakis count += 1; 59818a75281SManos Pitsidianakis } 59918a75281SManos Pitsidianakis QSIMPLEQ_FOREACH_SAFE(buffer, &stream->invalid, entry, next) { 60018a75281SManos Pitsidianakis count += 1; 60118a75281SManos Pitsidianakis } 60218a75281SManos Pitsidianakis } 60318a75281SManos Pitsidianakis return count; 60418a75281SManos Pitsidianakis } 60518a75281SManos Pitsidianakis 60618a75281SManos Pitsidianakis /* 60718a75281SManos Pitsidianakis * Handles VIRTIO_SND_R_PCM_RELEASE. 608d48800d7SManos Pitsidianakis * 609d48800d7SManos Pitsidianakis * @s: VirtIOSound device 610d48800d7SManos Pitsidianakis * @cmd: The request command queue element from VirtIOSound cmdq field 611d48800d7SManos Pitsidianakis */ 612d48800d7SManos Pitsidianakis static void virtio_snd_handle_pcm_release(VirtIOSound *s, 613d48800d7SManos Pitsidianakis virtio_snd_ctrl_command *cmd) 614d48800d7SManos Pitsidianakis { 615d48800d7SManos Pitsidianakis uint32_t stream_id; 616d48800d7SManos Pitsidianakis VirtIOSoundPCMStream *stream; 617d48800d7SManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg, 618d48800d7SManos Pitsidianakis cmd->elem->out_num, 619d48800d7SManos Pitsidianakis sizeof(virtio_snd_hdr), 620d48800d7SManos Pitsidianakis &stream_id, 621d48800d7SManos Pitsidianakis sizeof(stream_id)); 622d48800d7SManos Pitsidianakis 623d48800d7SManos Pitsidianakis if (msg_sz != sizeof(stream_id)) { 624d48800d7SManos Pitsidianakis /* 625d48800d7SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 626d48800d7SManos Pitsidianakis */ 627d48800d7SManos Pitsidianakis qemu_log_mask(LOG_GUEST_ERROR, 628d48800d7SManos Pitsidianakis "%s: virtio-snd command size incorrect %zu vs \ 629d48800d7SManos Pitsidianakis %zu\n", __func__, msg_sz, sizeof(stream_id)); 630d48800d7SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 631d48800d7SManos Pitsidianakis return; 632d48800d7SManos Pitsidianakis } 633d48800d7SManos Pitsidianakis 634d48800d7SManos Pitsidianakis stream_id = le32_to_cpu(stream_id); 635d48800d7SManos Pitsidianakis trace_virtio_snd_handle_pcm_release(stream_id); 636d48800d7SManos Pitsidianakis stream = virtio_snd_pcm_get_stream(s, stream_id); 637d48800d7SManos Pitsidianakis if (stream == NULL) { 638d48800d7SManos Pitsidianakis /* 639d48800d7SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 640d48800d7SManos Pitsidianakis */ 641d48800d7SManos Pitsidianakis error_report("already released stream %"PRIu32, stream_id); 642d48800d7SManos Pitsidianakis virtio_error(VIRTIO_DEVICE(s), 643d48800d7SManos Pitsidianakis "already released stream %"PRIu32, 644d48800d7SManos Pitsidianakis stream_id); 645d48800d7SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 646d48800d7SManos Pitsidianakis return; 647d48800d7SManos Pitsidianakis } 64818a75281SManos Pitsidianakis 64918a75281SManos Pitsidianakis if (virtio_snd_pcm_get_io_msgs_count(stream)) { 65018a75281SManos Pitsidianakis /* 65118a75281SManos Pitsidianakis * virtio-v1.2-csd01, 5.14.6.6.5.1, 65218a75281SManos Pitsidianakis * Device Requirements: Stream Release 65318a75281SManos Pitsidianakis * 65418a75281SManos Pitsidianakis * - The device MUST complete all pending I/O messages for the 65518a75281SManos Pitsidianakis * specified stream ID. 65618a75281SManos Pitsidianakis * - The device MUST NOT complete the control request while there 65718a75281SManos Pitsidianakis * are pending I/O messages for the specified stream ID. 65818a75281SManos Pitsidianakis */ 65918a75281SManos Pitsidianakis trace_virtio_snd_pcm_stream_flush(stream_id); 66018a75281SManos Pitsidianakis virtio_snd_pcm_flush(stream); 66118a75281SManos Pitsidianakis } 66218a75281SManos Pitsidianakis 663d48800d7SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); 664d48800d7SManos Pitsidianakis } 665d48800d7SManos Pitsidianakis 666d48800d7SManos Pitsidianakis /* 667eb9ad377SManos Pitsidianakis * The actual processing done in virtio_snd_process_cmdq(). 668eb9ad377SManos Pitsidianakis * 669eb9ad377SManos Pitsidianakis * @s: VirtIOSound device 670eb9ad377SManos Pitsidianakis * @cmd: control command request 671eb9ad377SManos Pitsidianakis */ 672eb9ad377SManos Pitsidianakis static inline void 673eb9ad377SManos Pitsidianakis process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd) 674eb9ad377SManos Pitsidianakis { 675eb9ad377SManos Pitsidianakis uint32_t code; 676eb9ad377SManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg, 677eb9ad377SManos Pitsidianakis cmd->elem->out_num, 678eb9ad377SManos Pitsidianakis 0, 679eb9ad377SManos Pitsidianakis &cmd->ctrl, 680eb9ad377SManos Pitsidianakis sizeof(virtio_snd_hdr)); 681eb9ad377SManos Pitsidianakis 682eb9ad377SManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_hdr)) { 683eb9ad377SManos Pitsidianakis /* 684eb9ad377SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 685eb9ad377SManos Pitsidianakis */ 686eb9ad377SManos Pitsidianakis qemu_log_mask(LOG_GUEST_ERROR, 687eb9ad377SManos Pitsidianakis "%s: virtio-snd command size incorrect %zu vs \ 688eb9ad377SManos Pitsidianakis %zu\n", __func__, msg_sz, sizeof(virtio_snd_hdr)); 689eb9ad377SManos Pitsidianakis return; 690eb9ad377SManos Pitsidianakis } 691eb9ad377SManos Pitsidianakis 692eb9ad377SManos Pitsidianakis code = le32_to_cpu(cmd->ctrl.code); 693eb9ad377SManos Pitsidianakis 694eb9ad377SManos Pitsidianakis trace_virtio_snd_handle_code(code, print_code(code)); 695eb9ad377SManos Pitsidianakis 696eb9ad377SManos Pitsidianakis switch (code) { 697eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_JACK_INFO: 698eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_JACK_REMAP: 699eb9ad377SManos Pitsidianakis qemu_log_mask(LOG_UNIMP, 700eb9ad377SManos Pitsidianakis "virtio_snd: jack functionality is unimplemented.\n"); 701eb9ad377SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); 702eb9ad377SManos Pitsidianakis break; 703eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_INFO: 7040ff05dd2SManos Pitsidianakis virtio_snd_handle_pcm_info(s, cmd); 7050ff05dd2SManos Pitsidianakis break; 706fa131d4aSManos Pitsidianakis case VIRTIO_SND_R_PCM_START: 707fa131d4aSManos Pitsidianakis virtio_snd_handle_pcm_start_stop(s, cmd, true); 708fa131d4aSManos Pitsidianakis break; 709fa131d4aSManos Pitsidianakis case VIRTIO_SND_R_PCM_STOP: 710fa131d4aSManos Pitsidianakis virtio_snd_handle_pcm_start_stop(s, cmd, false); 711fa131d4aSManos Pitsidianakis break; 712eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_SET_PARAMS: 71364704ce0SManos Pitsidianakis virtio_snd_handle_pcm_set_params(s, cmd); 71464704ce0SManos Pitsidianakis break; 715eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_PREPARE: 716e5788b8fSManos Pitsidianakis virtio_snd_handle_pcm_prepare(s, cmd); 717e5788b8fSManos Pitsidianakis break; 718eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_RELEASE: 719d48800d7SManos Pitsidianakis virtio_snd_handle_pcm_release(s, cmd); 720eb9ad377SManos Pitsidianakis break; 721eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_CHMAP_INFO: 722eb9ad377SManos Pitsidianakis qemu_log_mask(LOG_UNIMP, 723eb9ad377SManos Pitsidianakis "virtio_snd: chmap info functionality is unimplemented.\n"); 724eb9ad377SManos Pitsidianakis trace_virtio_snd_handle_chmap_info(); 725eb9ad377SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); 726eb9ad377SManos Pitsidianakis break; 727eb9ad377SManos Pitsidianakis default: 728eb9ad377SManos Pitsidianakis /* error */ 729eb9ad377SManos Pitsidianakis error_report("virtio snd header not recognized: %"PRIu32, code); 730eb9ad377SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 731eb9ad377SManos Pitsidianakis } 732eb9ad377SManos Pitsidianakis 733eb9ad377SManos Pitsidianakis iov_from_buf(cmd->elem->in_sg, 734eb9ad377SManos Pitsidianakis cmd->elem->in_num, 735eb9ad377SManos Pitsidianakis 0, 736eb9ad377SManos Pitsidianakis &cmd->resp, 737eb9ad377SManos Pitsidianakis sizeof(virtio_snd_hdr)); 738eb9ad377SManos Pitsidianakis virtqueue_push(cmd->vq, cmd->elem, sizeof(virtio_snd_hdr)); 739eb9ad377SManos Pitsidianakis virtio_notify(VIRTIO_DEVICE(s), cmd->vq); 740eb9ad377SManos Pitsidianakis } 741eb9ad377SManos Pitsidianakis 742eb9ad377SManos Pitsidianakis /* 743eb9ad377SManos Pitsidianakis * Consume all elements in command queue. 744eb9ad377SManos Pitsidianakis * 745eb9ad377SManos Pitsidianakis * @s: VirtIOSound device 746eb9ad377SManos Pitsidianakis */ 747eb9ad377SManos Pitsidianakis static void virtio_snd_process_cmdq(VirtIOSound *s) 748eb9ad377SManos Pitsidianakis { 749eb9ad377SManos Pitsidianakis virtio_snd_ctrl_command *cmd; 750eb9ad377SManos Pitsidianakis 751eb9ad377SManos Pitsidianakis if (unlikely(qatomic_read(&s->processing_cmdq))) { 752eb9ad377SManos Pitsidianakis return; 753eb9ad377SManos Pitsidianakis } 754eb9ad377SManos Pitsidianakis 755eb9ad377SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&s->cmdq_mutex) { 756eb9ad377SManos Pitsidianakis qatomic_set(&s->processing_cmdq, true); 757eb9ad377SManos Pitsidianakis while (!QTAILQ_EMPTY(&s->cmdq)) { 758eb9ad377SManos Pitsidianakis cmd = QTAILQ_FIRST(&s->cmdq); 759eb9ad377SManos Pitsidianakis 760eb9ad377SManos Pitsidianakis /* process command */ 761eb9ad377SManos Pitsidianakis process_cmd(s, cmd); 762eb9ad377SManos Pitsidianakis 763eb9ad377SManos Pitsidianakis QTAILQ_REMOVE(&s->cmdq, cmd, next); 764eb9ad377SManos Pitsidianakis 765eb9ad377SManos Pitsidianakis virtio_snd_ctrl_cmd_free(cmd); 766eb9ad377SManos Pitsidianakis } 767eb9ad377SManos Pitsidianakis qatomic_set(&s->processing_cmdq, false); 768eb9ad377SManos Pitsidianakis } 769eb9ad377SManos Pitsidianakis } 770eb9ad377SManos Pitsidianakis 771eb9ad377SManos Pitsidianakis /* 772eb9ad377SManos Pitsidianakis * The control message handler. Pops an element from the control virtqueue, 773eb9ad377SManos Pitsidianakis * and stores them to VirtIOSound's cmdq queue and finally calls 774eb9ad377SManos Pitsidianakis * virtio_snd_process_cmdq() for processing. 775eb9ad377SManos Pitsidianakis * 776eb9ad377SManos Pitsidianakis * @vdev: VirtIOSound device 777eb9ad377SManos Pitsidianakis * @vq: Control virtqueue 778eb9ad377SManos Pitsidianakis */ 779eb9ad377SManos Pitsidianakis static void virtio_snd_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) 780eb9ad377SManos Pitsidianakis { 781eb9ad377SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 782eb9ad377SManos Pitsidianakis VirtQueueElement *elem; 783eb9ad377SManos Pitsidianakis virtio_snd_ctrl_command *cmd; 784eb9ad377SManos Pitsidianakis 785eb9ad377SManos Pitsidianakis trace_virtio_snd_handle_ctrl(vdev, vq); 786eb9ad377SManos Pitsidianakis 787eb9ad377SManos Pitsidianakis if (!virtio_queue_ready(vq)) { 788eb9ad377SManos Pitsidianakis return; 789eb9ad377SManos Pitsidianakis } 790eb9ad377SManos Pitsidianakis 791eb9ad377SManos Pitsidianakis elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); 792eb9ad377SManos Pitsidianakis while (elem) { 793eb9ad377SManos Pitsidianakis cmd = g_new0(virtio_snd_ctrl_command, 1); 794eb9ad377SManos Pitsidianakis cmd->elem = elem; 795eb9ad377SManos Pitsidianakis cmd->vq = vq; 796eb9ad377SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); 797eb9ad377SManos Pitsidianakis QTAILQ_INSERT_TAIL(&s->cmdq, cmd, next); 798eb9ad377SManos Pitsidianakis elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); 799eb9ad377SManos Pitsidianakis } 800eb9ad377SManos Pitsidianakis 801eb9ad377SManos Pitsidianakis virtio_snd_process_cmdq(s); 802eb9ad377SManos Pitsidianakis } 803eb9ad377SManos Pitsidianakis 804eb9ad377SManos Pitsidianakis /* 805eb9ad377SManos Pitsidianakis * The event virtqueue handler. 806eb9ad377SManos Pitsidianakis * Not implemented yet. 807eb9ad377SManos Pitsidianakis * 808eb9ad377SManos Pitsidianakis * @vdev: VirtIOSound device 809eb9ad377SManos Pitsidianakis * @vq: event vq 810eb9ad377SManos Pitsidianakis */ 811eb9ad377SManos Pitsidianakis static void virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq) 812eb9ad377SManos Pitsidianakis { 813eb9ad377SManos Pitsidianakis qemu_log_mask(LOG_UNIMP, "virtio_snd: event queue is unimplemented.\n"); 814eb9ad377SManos Pitsidianakis trace_virtio_snd_handle_event(); 815eb9ad377SManos Pitsidianakis } 816eb9ad377SManos Pitsidianakis 817eb9ad377SManos Pitsidianakis /* 81818a75281SManos Pitsidianakis * The tx virtqueue handler. Makes the buffers available to their respective 81918a75281SManos Pitsidianakis * streams for consumption. 82018a75281SManos Pitsidianakis * 82118a75281SManos Pitsidianakis * @vdev: VirtIOSound device 82218a75281SManos Pitsidianakis * @vq: tx virtqueue 82318a75281SManos Pitsidianakis */ 82418a75281SManos Pitsidianakis static void virtio_snd_handle_tx(VirtIODevice *vdev, VirtQueue *vq) 82518a75281SManos Pitsidianakis { 82618a75281SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 82718a75281SManos Pitsidianakis VirtIOSoundPCMStream *stream = NULL; 82818a75281SManos Pitsidianakis VirtIOSoundPCMBuffer *buffer; 82918a75281SManos Pitsidianakis VirtQueueElement *elem; 83018a75281SManos Pitsidianakis size_t msg_sz, size; 83118a75281SManos Pitsidianakis virtio_snd_pcm_xfer hdr; 83218a75281SManos Pitsidianakis virtio_snd_pcm_status resp = { 0 }; 83318a75281SManos Pitsidianakis uint32_t stream_id; 83418a75281SManos Pitsidianakis /* 83518a75281SManos Pitsidianakis * If any of the I/O messages are invalid, put them in stream->invalid and 83618a75281SManos Pitsidianakis * return them after the for loop. 83718a75281SManos Pitsidianakis */ 83818a75281SManos Pitsidianakis bool must_empty_invalid_queue = false; 83918a75281SManos Pitsidianakis 84018a75281SManos Pitsidianakis if (!virtio_queue_ready(vq)) { 84118a75281SManos Pitsidianakis return; 84218a75281SManos Pitsidianakis } 84318a75281SManos Pitsidianakis trace_virtio_snd_handle_xfer(); 84418a75281SManos Pitsidianakis 84518a75281SManos Pitsidianakis for (;;) { 84618a75281SManos Pitsidianakis elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); 84718a75281SManos Pitsidianakis if (!elem) { 84818a75281SManos Pitsidianakis break; 84918a75281SManos Pitsidianakis } 85018a75281SManos Pitsidianakis /* get the message hdr object */ 85118a75281SManos Pitsidianakis msg_sz = iov_to_buf(elem->out_sg, 85218a75281SManos Pitsidianakis elem->out_num, 85318a75281SManos Pitsidianakis 0, 85418a75281SManos Pitsidianakis &hdr, 85518a75281SManos Pitsidianakis sizeof(virtio_snd_pcm_xfer)); 85618a75281SManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_pcm_xfer)) { 85718a75281SManos Pitsidianakis goto tx_err; 85818a75281SManos Pitsidianakis } 85918a75281SManos Pitsidianakis stream_id = le32_to_cpu(hdr.stream_id); 86018a75281SManos Pitsidianakis 86118a75281SManos Pitsidianakis if (stream_id >= s->snd_conf.streams 86218a75281SManos Pitsidianakis || s->pcm->streams[stream_id] == NULL) { 86318a75281SManos Pitsidianakis goto tx_err; 86418a75281SManos Pitsidianakis } 86518a75281SManos Pitsidianakis 86618a75281SManos Pitsidianakis stream = s->pcm->streams[stream_id]; 86718a75281SManos Pitsidianakis if (stream->info.direction != VIRTIO_SND_D_OUTPUT) { 86818a75281SManos Pitsidianakis goto tx_err; 86918a75281SManos Pitsidianakis } 87018a75281SManos Pitsidianakis 87118a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { 87218a75281SManos Pitsidianakis size = iov_size(elem->out_sg, elem->out_num) - msg_sz; 87318a75281SManos Pitsidianakis 87418a75281SManos Pitsidianakis buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size); 87518a75281SManos Pitsidianakis buffer->elem = elem; 87618a75281SManos Pitsidianakis buffer->populated = false; 87718a75281SManos Pitsidianakis buffer->vq = vq; 87818a75281SManos Pitsidianakis buffer->size = size; 87918a75281SManos Pitsidianakis buffer->offset = 0; 88018a75281SManos Pitsidianakis 88118a75281SManos Pitsidianakis QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry); 88218a75281SManos Pitsidianakis } 88318a75281SManos Pitsidianakis continue; 88418a75281SManos Pitsidianakis 88518a75281SManos Pitsidianakis tx_err: 88618a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { 88718a75281SManos Pitsidianakis must_empty_invalid_queue = true; 88818a75281SManos Pitsidianakis buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer)); 88918a75281SManos Pitsidianakis buffer->elem = elem; 89018a75281SManos Pitsidianakis buffer->vq = vq; 89118a75281SManos Pitsidianakis QSIMPLEQ_INSERT_TAIL(&stream->invalid, buffer, entry); 89218a75281SManos Pitsidianakis } 89318a75281SManos Pitsidianakis } 89418a75281SManos Pitsidianakis 89518a75281SManos Pitsidianakis if (must_empty_invalid_queue) { 89618a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { 89718a75281SManos Pitsidianakis while (!QSIMPLEQ_EMPTY(&stream->invalid)) { 89818a75281SManos Pitsidianakis buffer = QSIMPLEQ_FIRST(&stream->invalid); 89918a75281SManos Pitsidianakis 90018a75281SManos Pitsidianakis resp.status = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 90118a75281SManos Pitsidianakis iov_from_buf(buffer->elem->in_sg, 90218a75281SManos Pitsidianakis buffer->elem->in_num, 90318a75281SManos Pitsidianakis 0, 90418a75281SManos Pitsidianakis &resp, 90518a75281SManos Pitsidianakis sizeof(virtio_snd_pcm_status)); 90618a75281SManos Pitsidianakis virtqueue_push(vq, buffer->elem, sizeof(virtio_snd_pcm_status)); 90718a75281SManos Pitsidianakis QSIMPLEQ_REMOVE_HEAD(&stream->invalid, entry); 90818a75281SManos Pitsidianakis virtio_snd_pcm_buffer_free(buffer); 90918a75281SManos Pitsidianakis } 91018a75281SManos Pitsidianakis /* 91118a75281SManos Pitsidianakis * Notify vq about virtio_snd_pcm_status responses. 91218a75281SManos Pitsidianakis * Buffer responses must be notified separately later. 91318a75281SManos Pitsidianakis */ 91418a75281SManos Pitsidianakis virtio_notify(vdev, vq); 91518a75281SManos Pitsidianakis } 91618a75281SManos Pitsidianakis } 91718a75281SManos Pitsidianakis } 91818a75281SManos Pitsidianakis 91918a75281SManos Pitsidianakis /* 920eb9ad377SManos Pitsidianakis * Stub buffer virtqueue handler. 9212880e676SManos Pitsidianakis * 9222880e676SManos Pitsidianakis * @vdev: VirtIOSound device 9232880e676SManos Pitsidianakis * @vq: virtqueue 9242880e676SManos Pitsidianakis */ 925eb9ad377SManos Pitsidianakis static void virtio_snd_handle_xfer(VirtIODevice *vdev, VirtQueue *vq) {} 9262880e676SManos Pitsidianakis 9272880e676SManos Pitsidianakis static uint64_t get_features(VirtIODevice *vdev, uint64_t features, 9282880e676SManos Pitsidianakis Error **errp) 9292880e676SManos Pitsidianakis { 9302880e676SManos Pitsidianakis /* 9312880e676SManos Pitsidianakis * virtio-v1.2-csd01, 5.14.3, 9322880e676SManos Pitsidianakis * Feature Bits 9332880e676SManos Pitsidianakis * None currently defined. 9342880e676SManos Pitsidianakis */ 9352880e676SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 9362880e676SManos Pitsidianakis features |= s->features; 9372880e676SManos Pitsidianakis 9382880e676SManos Pitsidianakis trace_virtio_snd_get_features(vdev, features); 9392880e676SManos Pitsidianakis 9402880e676SManos Pitsidianakis return features; 9412880e676SManos Pitsidianakis } 9422880e676SManos Pitsidianakis 9432880e676SManos Pitsidianakis static void 9442880e676SManos Pitsidianakis virtio_snd_vm_state_change(void *opaque, bool running, 9452880e676SManos Pitsidianakis RunState state) 9462880e676SManos Pitsidianakis { 9472880e676SManos Pitsidianakis if (running) { 9482880e676SManos Pitsidianakis trace_virtio_snd_vm_state_running(); 9492880e676SManos Pitsidianakis } else { 9502880e676SManos Pitsidianakis trace_virtio_snd_vm_state_stopped(); 9512880e676SManos Pitsidianakis } 9522880e676SManos Pitsidianakis } 9532880e676SManos Pitsidianakis 9542880e676SManos Pitsidianakis static void virtio_snd_realize(DeviceState *dev, Error **errp) 9552880e676SManos Pitsidianakis { 9562880e676SManos Pitsidianakis ERRP_GUARD(); 9572880e676SManos Pitsidianakis VirtIOSound *vsnd = VIRTIO_SND(dev); 9582880e676SManos Pitsidianakis VirtIODevice *vdev = VIRTIO_DEVICE(dev); 959eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params default_params = { 0 }; 960eb9ad377SManos Pitsidianakis uint32_t status; 9612880e676SManos Pitsidianakis 962eb9ad377SManos Pitsidianakis vsnd->pcm = NULL; 9632880e676SManos Pitsidianakis vsnd->vmstate = 9642880e676SManos Pitsidianakis qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd); 9652880e676SManos Pitsidianakis 9662880e676SManos Pitsidianakis trace_virtio_snd_realize(vsnd); 9672880e676SManos Pitsidianakis 968eb9ad377SManos Pitsidianakis vsnd->pcm = g_new0(VirtIOSoundPCM, 1); 969eb9ad377SManos Pitsidianakis vsnd->pcm->snd = vsnd; 970eb9ad377SManos Pitsidianakis vsnd->pcm->streams = 971eb9ad377SManos Pitsidianakis g_new0(VirtIOSoundPCMStream *, vsnd->snd_conf.streams); 972eb9ad377SManos Pitsidianakis vsnd->pcm->pcm_params = 973eb9ad377SManos Pitsidianakis g_new0(virtio_snd_pcm_set_params, vsnd->snd_conf.streams); 974eb9ad377SManos Pitsidianakis 9752880e676SManos Pitsidianakis virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config)); 9762880e676SManos Pitsidianakis virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1); 9772880e676SManos Pitsidianakis 9782880e676SManos Pitsidianakis /* set number of jacks and streams */ 9792880e676SManos Pitsidianakis if (vsnd->snd_conf.jacks > 8) { 9802880e676SManos Pitsidianakis error_setg(errp, 9812880e676SManos Pitsidianakis "Invalid number of jacks: %"PRIu32, 9822880e676SManos Pitsidianakis vsnd->snd_conf.jacks); 9832880e676SManos Pitsidianakis return; 9842880e676SManos Pitsidianakis } 9852880e676SManos Pitsidianakis if (vsnd->snd_conf.streams < 1 || vsnd->snd_conf.streams > 10) { 9862880e676SManos Pitsidianakis error_setg(errp, 9872880e676SManos Pitsidianakis "Invalid number of streams: %"PRIu32, 9882880e676SManos Pitsidianakis vsnd->snd_conf.streams); 9892880e676SManos Pitsidianakis return; 9902880e676SManos Pitsidianakis } 9912880e676SManos Pitsidianakis 9922880e676SManos Pitsidianakis if (vsnd->snd_conf.chmaps > VIRTIO_SND_CHMAP_MAX_SIZE) { 9932880e676SManos Pitsidianakis error_setg(errp, 9942880e676SManos Pitsidianakis "Invalid number of channel maps: %"PRIu32, 9952880e676SManos Pitsidianakis vsnd->snd_conf.chmaps); 9962880e676SManos Pitsidianakis return; 9972880e676SManos Pitsidianakis } 9982880e676SManos Pitsidianakis 9992880e676SManos Pitsidianakis AUD_register_card("virtio-sound", &vsnd->card, errp); 10002880e676SManos Pitsidianakis 1001eb9ad377SManos Pitsidianakis /* set default params for all streams */ 1002eb9ad377SManos Pitsidianakis default_params.features = 0; 1003eb9ad377SManos Pitsidianakis default_params.buffer_bytes = cpu_to_le32(8192); 1004eb9ad377SManos Pitsidianakis default_params.period_bytes = cpu_to_le32(2048); 1005eb9ad377SManos Pitsidianakis default_params.channels = 2; 1006eb9ad377SManos Pitsidianakis default_params.format = VIRTIO_SND_PCM_FMT_S16; 1007eb9ad377SManos Pitsidianakis default_params.rate = VIRTIO_SND_PCM_RATE_48000; 10082880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_CONTROL] = 1009eb9ad377SManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_ctrl); 10102880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_EVENT] = 1011eb9ad377SManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_event); 10122880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_TX] = 101318a75281SManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_tx); 10142880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_RX] = 1015eb9ad377SManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_xfer); 1016eb9ad377SManos Pitsidianakis qemu_mutex_init(&vsnd->cmdq_mutex); 1017eb9ad377SManos Pitsidianakis QTAILQ_INIT(&vsnd->cmdq); 1018eb9ad377SManos Pitsidianakis 1019eb9ad377SManos Pitsidianakis for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) { 1020eb9ad377SManos Pitsidianakis status = virtio_snd_set_pcm_params(vsnd, i, &default_params); 1021eb9ad377SManos Pitsidianakis if (status != cpu_to_le32(VIRTIO_SND_S_OK)) { 1022eb9ad377SManos Pitsidianakis error_setg(errp, 1023eb9ad377SManos Pitsidianakis "Can't initalize stream params, device responded with %s.", 1024eb9ad377SManos Pitsidianakis print_code(status)); 1025eb9ad377SManos Pitsidianakis return; 1026eb9ad377SManos Pitsidianakis } 1027eb9ad377SManos Pitsidianakis status = virtio_snd_pcm_prepare(vsnd, i); 1028eb9ad377SManos Pitsidianakis if (status != cpu_to_le32(VIRTIO_SND_S_OK)) { 1029eb9ad377SManos Pitsidianakis error_setg(errp, 1030eb9ad377SManos Pitsidianakis "Can't prepare streams, device responded with %s.", 1031eb9ad377SManos Pitsidianakis print_code(status)); 1032eb9ad377SManos Pitsidianakis return; 1033eb9ad377SManos Pitsidianakis } 1034eb9ad377SManos Pitsidianakis } 10352880e676SManos Pitsidianakis } 10362880e676SManos Pitsidianakis 103718a75281SManos Pitsidianakis static inline void return_tx_buffer(VirtIOSoundPCMStream *stream, 103818a75281SManos Pitsidianakis VirtIOSoundPCMBuffer *buffer) 103918a75281SManos Pitsidianakis { 104018a75281SManos Pitsidianakis virtio_snd_pcm_status resp = { 0 }; 104118a75281SManos Pitsidianakis resp.status = cpu_to_le32(VIRTIO_SND_S_OK); 104218a75281SManos Pitsidianakis resp.latency_bytes = cpu_to_le32((uint32_t)buffer->size); 104318a75281SManos Pitsidianakis iov_from_buf(buffer->elem->in_sg, 104418a75281SManos Pitsidianakis buffer->elem->in_num, 104518a75281SManos Pitsidianakis 0, 104618a75281SManos Pitsidianakis &resp, 104718a75281SManos Pitsidianakis sizeof(virtio_snd_pcm_status)); 104818a75281SManos Pitsidianakis virtqueue_push(buffer->vq, 104918a75281SManos Pitsidianakis buffer->elem, 105018a75281SManos Pitsidianakis sizeof(virtio_snd_pcm_status)); 105118a75281SManos Pitsidianakis virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq); 105218a75281SManos Pitsidianakis QSIMPLEQ_REMOVE(&stream->queue, 105318a75281SManos Pitsidianakis buffer, 105418a75281SManos Pitsidianakis VirtIOSoundPCMBuffer, 105518a75281SManos Pitsidianakis entry); 105618a75281SManos Pitsidianakis virtio_snd_pcm_buffer_free(buffer); 105718a75281SManos Pitsidianakis } 105818a75281SManos Pitsidianakis 105918a75281SManos Pitsidianakis /* 106018a75281SManos Pitsidianakis * AUD_* output callback. 106118a75281SManos Pitsidianakis * 106218a75281SManos Pitsidianakis * @data: VirtIOSoundPCMStream stream 106318a75281SManos Pitsidianakis * @available: number of bytes that can be written with AUD_write() 106418a75281SManos Pitsidianakis */ 106518a75281SManos Pitsidianakis static void virtio_snd_pcm_out_cb(void *data, int available) 106618a75281SManos Pitsidianakis { 106718a75281SManos Pitsidianakis VirtIOSoundPCMStream *stream = data; 106818a75281SManos Pitsidianakis VirtIOSoundPCMBuffer *buffer; 106918a75281SManos Pitsidianakis size_t size; 107018a75281SManos Pitsidianakis 107118a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { 107218a75281SManos Pitsidianakis while (!QSIMPLEQ_EMPTY(&stream->queue)) { 107318a75281SManos Pitsidianakis buffer = QSIMPLEQ_FIRST(&stream->queue); 107418a75281SManos Pitsidianakis if (!virtio_queue_ready(buffer->vq)) { 107518a75281SManos Pitsidianakis return; 107618a75281SManos Pitsidianakis } 107718a75281SManos Pitsidianakis if (!stream->active) { 107818a75281SManos Pitsidianakis /* Stream has stopped, so do not perform AUD_write. */ 107918a75281SManos Pitsidianakis return_tx_buffer(stream, buffer); 108018a75281SManos Pitsidianakis continue; 108118a75281SManos Pitsidianakis } 108218a75281SManos Pitsidianakis if (!buffer->populated) { 108318a75281SManos Pitsidianakis iov_to_buf(buffer->elem->out_sg, 108418a75281SManos Pitsidianakis buffer->elem->out_num, 108518a75281SManos Pitsidianakis sizeof(virtio_snd_pcm_xfer), 108618a75281SManos Pitsidianakis buffer->data, 108718a75281SManos Pitsidianakis buffer->size); 108818a75281SManos Pitsidianakis buffer->populated = true; 108918a75281SManos Pitsidianakis } 109018a75281SManos Pitsidianakis for (;;) { 109118a75281SManos Pitsidianakis size = AUD_write(stream->voice.out, 109218a75281SManos Pitsidianakis buffer->data + buffer->offset, 109318a75281SManos Pitsidianakis MIN(buffer->size, available)); 109418a75281SManos Pitsidianakis assert(size <= MIN(buffer->size, available)); 109518a75281SManos Pitsidianakis if (size == 0) { 109618a75281SManos Pitsidianakis /* break out of both loops */ 109718a75281SManos Pitsidianakis available = 0; 109818a75281SManos Pitsidianakis break; 109918a75281SManos Pitsidianakis } 110018a75281SManos Pitsidianakis buffer->size -= size; 110118a75281SManos Pitsidianakis buffer->offset += size; 110218a75281SManos Pitsidianakis available -= size; 110318a75281SManos Pitsidianakis if (buffer->size < 1) { 110418a75281SManos Pitsidianakis return_tx_buffer(stream, buffer); 110518a75281SManos Pitsidianakis break; 110618a75281SManos Pitsidianakis } 110718a75281SManos Pitsidianakis if (!available) { 110818a75281SManos Pitsidianakis break; 110918a75281SManos Pitsidianakis } 111018a75281SManos Pitsidianakis } 111118a75281SManos Pitsidianakis if (!available) { 111218a75281SManos Pitsidianakis break; 111318a75281SManos Pitsidianakis } 111418a75281SManos Pitsidianakis } 111518a75281SManos Pitsidianakis } 111618a75281SManos Pitsidianakis } 111718a75281SManos Pitsidianakis 111818a75281SManos Pitsidianakis /* 111918a75281SManos Pitsidianakis * Flush all buffer data from this stream's queue into the driver's virtual 112018a75281SManos Pitsidianakis * queue. 112118a75281SManos Pitsidianakis * 112218a75281SManos Pitsidianakis * @stream: VirtIOSoundPCMStream *stream 112318a75281SManos Pitsidianakis */ 112418a75281SManos Pitsidianakis static inline void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream) 112518a75281SManos Pitsidianakis { 112618a75281SManos Pitsidianakis VirtIOSoundPCMBuffer *buffer; 112718a75281SManos Pitsidianakis 112818a75281SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { 112918a75281SManos Pitsidianakis while (!QSIMPLEQ_EMPTY(&stream->queue)) { 113018a75281SManos Pitsidianakis buffer = QSIMPLEQ_FIRST(&stream->queue); 113118a75281SManos Pitsidianakis return_tx_buffer(stream, buffer); 113218a75281SManos Pitsidianakis } 113318a75281SManos Pitsidianakis } 113418a75281SManos Pitsidianakis } 113518a75281SManos Pitsidianakis 11362880e676SManos Pitsidianakis static void virtio_snd_unrealize(DeviceState *dev) 11372880e676SManos Pitsidianakis { 11382880e676SManos Pitsidianakis VirtIODevice *vdev = VIRTIO_DEVICE(dev); 11392880e676SManos Pitsidianakis VirtIOSound *vsnd = VIRTIO_SND(dev); 1140eb9ad377SManos Pitsidianakis VirtIOSoundPCMStream *stream; 11412880e676SManos Pitsidianakis 11422880e676SManos Pitsidianakis qemu_del_vm_change_state_handler(vsnd->vmstate); 11432880e676SManos Pitsidianakis trace_virtio_snd_unrealize(vsnd); 11442880e676SManos Pitsidianakis 1145eb9ad377SManos Pitsidianakis if (vsnd->pcm) { 1146eb9ad377SManos Pitsidianakis if (vsnd->pcm->streams) { 1147eb9ad377SManos Pitsidianakis for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) { 1148eb9ad377SManos Pitsidianakis stream = vsnd->pcm->streams[i]; 1149eb9ad377SManos Pitsidianakis if (stream) { 1150eb9ad377SManos Pitsidianakis virtio_snd_process_cmdq(stream->s); 1151eb9ad377SManos Pitsidianakis virtio_snd_pcm_close(stream); 115218a75281SManos Pitsidianakis qemu_mutex_destroy(&stream->queue_mutex); 1153eb9ad377SManos Pitsidianakis g_free(stream); 1154eb9ad377SManos Pitsidianakis } 1155eb9ad377SManos Pitsidianakis } 1156eb9ad377SManos Pitsidianakis g_free(vsnd->pcm->streams); 1157eb9ad377SManos Pitsidianakis } 1158eb9ad377SManos Pitsidianakis g_free(vsnd->pcm->pcm_params); 1159eb9ad377SManos Pitsidianakis g_free(vsnd->pcm); 1160eb9ad377SManos Pitsidianakis vsnd->pcm = NULL; 1161eb9ad377SManos Pitsidianakis } 11622880e676SManos Pitsidianakis AUD_remove_card(&vsnd->card); 1163eb9ad377SManos Pitsidianakis qemu_mutex_destroy(&vsnd->cmdq_mutex); 11642880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]); 11652880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]); 11662880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]); 11672880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_RX]); 11682880e676SManos Pitsidianakis virtio_cleanup(vdev); 11692880e676SManos Pitsidianakis } 11702880e676SManos Pitsidianakis 11712880e676SManos Pitsidianakis 1172eb9ad377SManos Pitsidianakis static void virtio_snd_reset(VirtIODevice *vdev) 1173eb9ad377SManos Pitsidianakis { 1174eb9ad377SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 1175eb9ad377SManos Pitsidianakis virtio_snd_ctrl_command *cmd; 1176eb9ad377SManos Pitsidianakis 1177eb9ad377SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&s->cmdq_mutex) { 1178eb9ad377SManos Pitsidianakis while (!QTAILQ_EMPTY(&s->cmdq)) { 1179eb9ad377SManos Pitsidianakis cmd = QTAILQ_FIRST(&s->cmdq); 1180eb9ad377SManos Pitsidianakis QTAILQ_REMOVE(&s->cmdq, cmd, next); 1181eb9ad377SManos Pitsidianakis virtio_snd_ctrl_cmd_free(cmd); 1182eb9ad377SManos Pitsidianakis } 1183eb9ad377SManos Pitsidianakis } 1184eb9ad377SManos Pitsidianakis } 11852880e676SManos Pitsidianakis 11862880e676SManos Pitsidianakis static void virtio_snd_class_init(ObjectClass *klass, void *data) 11872880e676SManos Pitsidianakis { 11882880e676SManos Pitsidianakis DeviceClass *dc = DEVICE_CLASS(klass); 11892880e676SManos Pitsidianakis VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 11902880e676SManos Pitsidianakis 11912880e676SManos Pitsidianakis 11922880e676SManos Pitsidianakis set_bit(DEVICE_CATEGORY_SOUND, dc->categories); 11932880e676SManos Pitsidianakis device_class_set_props(dc, virtio_snd_properties); 11942880e676SManos Pitsidianakis 11952880e676SManos Pitsidianakis dc->vmsd = &vmstate_virtio_snd; 11962880e676SManos Pitsidianakis vdc->vmsd = &vmstate_virtio_snd_device; 11972880e676SManos Pitsidianakis vdc->realize = virtio_snd_realize; 11982880e676SManos Pitsidianakis vdc->unrealize = virtio_snd_unrealize; 11992880e676SManos Pitsidianakis vdc->get_config = virtio_snd_get_config; 12002880e676SManos Pitsidianakis vdc->set_config = virtio_snd_set_config; 12012880e676SManos Pitsidianakis vdc->get_features = get_features; 12022880e676SManos Pitsidianakis vdc->reset = virtio_snd_reset; 12032880e676SManos Pitsidianakis vdc->legacy_features = 0; 12042880e676SManos Pitsidianakis } 12052880e676SManos Pitsidianakis 12062880e676SManos Pitsidianakis static const TypeInfo virtio_snd_types[] = { 12072880e676SManos Pitsidianakis { 12082880e676SManos Pitsidianakis .name = TYPE_VIRTIO_SND, 12092880e676SManos Pitsidianakis .parent = TYPE_VIRTIO_DEVICE, 12102880e676SManos Pitsidianakis .instance_size = sizeof(VirtIOSound), 12112880e676SManos Pitsidianakis .class_init = virtio_snd_class_init, 12122880e676SManos Pitsidianakis } 12132880e676SManos Pitsidianakis }; 12142880e676SManos Pitsidianakis 12152880e676SManos Pitsidianakis DEFINE_TYPES(virtio_snd_types) 1216