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 35eb9ad377SManos Pitsidianakis static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8) 36eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_U8) 37eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_S16) 38eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_U16) 39eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_S32) 40eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_U32) 41eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_FMT_FLOAT); 42eb9ad377SManos Pitsidianakis 43eb9ad377SManos Pitsidianakis static uint32_t supported_rates = BIT(VIRTIO_SND_PCM_RATE_5512) 44eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_8000) 45eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_11025) 46eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_16000) 47eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_22050) 48eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_32000) 49eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_44100) 50eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_48000) 51eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_64000) 52eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_88200) 53eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_96000) 54eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_176400) 55eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_192000) 56eb9ad377SManos Pitsidianakis | BIT(VIRTIO_SND_PCM_RATE_384000); 57eb9ad377SManos Pitsidianakis 582880e676SManos Pitsidianakis static const VMStateDescription vmstate_virtio_snd_device = { 592880e676SManos Pitsidianakis .name = TYPE_VIRTIO_SND, 602880e676SManos Pitsidianakis .version_id = VIRTIO_SOUND_VM_VERSION, 612880e676SManos Pitsidianakis .minimum_version_id = VIRTIO_SOUND_VM_VERSION, 622880e676SManos Pitsidianakis }; 632880e676SManos Pitsidianakis 642880e676SManos Pitsidianakis static const VMStateDescription vmstate_virtio_snd = { 652880e676SManos Pitsidianakis .name = TYPE_VIRTIO_SND, 662880e676SManos Pitsidianakis .minimum_version_id = VIRTIO_SOUND_VM_VERSION, 672880e676SManos Pitsidianakis .version_id = VIRTIO_SOUND_VM_VERSION, 682880e676SManos Pitsidianakis .fields = (VMStateField[]) { 692880e676SManos Pitsidianakis VMSTATE_VIRTIO_DEVICE, 702880e676SManos Pitsidianakis VMSTATE_END_OF_LIST() 712880e676SManos Pitsidianakis }, 722880e676SManos Pitsidianakis }; 732880e676SManos Pitsidianakis 742880e676SManos Pitsidianakis static Property virtio_snd_properties[] = { 752880e676SManos Pitsidianakis DEFINE_AUDIO_PROPERTIES(VirtIOSound, card), 762880e676SManos Pitsidianakis DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks, 772880e676SManos Pitsidianakis VIRTIO_SOUND_JACK_DEFAULT), 782880e676SManos Pitsidianakis DEFINE_PROP_UINT32("streams", VirtIOSound, snd_conf.streams, 792880e676SManos Pitsidianakis VIRTIO_SOUND_STREAM_DEFAULT), 802880e676SManos Pitsidianakis DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps, 812880e676SManos Pitsidianakis VIRTIO_SOUND_CHMAP_DEFAULT), 822880e676SManos Pitsidianakis DEFINE_PROP_END_OF_LIST(), 832880e676SManos Pitsidianakis }; 842880e676SManos Pitsidianakis 852880e676SManos Pitsidianakis static void 862880e676SManos Pitsidianakis virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config) 872880e676SManos Pitsidianakis { 882880e676SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 892880e676SManos Pitsidianakis virtio_snd_config *sndconfig = 902880e676SManos Pitsidianakis (virtio_snd_config *)config; 912880e676SManos Pitsidianakis trace_virtio_snd_get_config(vdev, 922880e676SManos Pitsidianakis s->snd_conf.jacks, 932880e676SManos Pitsidianakis s->snd_conf.streams, 942880e676SManos Pitsidianakis s->snd_conf.chmaps); 952880e676SManos Pitsidianakis 962880e676SManos Pitsidianakis memcpy(sndconfig, &s->snd_conf, sizeof(s->snd_conf)); 972880e676SManos Pitsidianakis cpu_to_le32s(&sndconfig->jacks); 982880e676SManos Pitsidianakis cpu_to_le32s(&sndconfig->streams); 992880e676SManos Pitsidianakis cpu_to_le32s(&sndconfig->chmaps); 1002880e676SManos Pitsidianakis 1012880e676SManos Pitsidianakis } 1022880e676SManos Pitsidianakis 1032880e676SManos Pitsidianakis static void 1042880e676SManos Pitsidianakis virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config) 1052880e676SManos Pitsidianakis { 1062880e676SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 1072880e676SManos Pitsidianakis const virtio_snd_config *sndconfig = 1082880e676SManos Pitsidianakis (const virtio_snd_config *)config; 1092880e676SManos Pitsidianakis 1102880e676SManos Pitsidianakis 1112880e676SManos Pitsidianakis trace_virtio_snd_set_config(vdev, 1122880e676SManos Pitsidianakis s->snd_conf.jacks, 1132880e676SManos Pitsidianakis sndconfig->jacks, 1142880e676SManos Pitsidianakis s->snd_conf.streams, 1152880e676SManos Pitsidianakis sndconfig->streams, 1162880e676SManos Pitsidianakis s->snd_conf.chmaps, 1172880e676SManos Pitsidianakis sndconfig->chmaps); 1182880e676SManos Pitsidianakis 1192880e676SManos Pitsidianakis memcpy(&s->snd_conf, sndconfig, sizeof(virtio_snd_config)); 1202880e676SManos Pitsidianakis le32_to_cpus(&s->snd_conf.jacks); 1212880e676SManos Pitsidianakis le32_to_cpus(&s->snd_conf.streams); 1222880e676SManos Pitsidianakis le32_to_cpus(&s->snd_conf.chmaps); 1232880e676SManos Pitsidianakis 1242880e676SManos Pitsidianakis } 1252880e676SManos Pitsidianakis 126eb9ad377SManos Pitsidianakis static void 127eb9ad377SManos Pitsidianakis virtio_snd_ctrl_cmd_free(virtio_snd_ctrl_command *cmd) 128eb9ad377SManos Pitsidianakis { 129eb9ad377SManos Pitsidianakis g_free(cmd->elem); 130eb9ad377SManos Pitsidianakis g_free(cmd); 131eb9ad377SManos Pitsidianakis } 132eb9ad377SManos Pitsidianakis 1332880e676SManos Pitsidianakis /* 134eb9ad377SManos Pitsidianakis * Get a specific stream from the virtio sound card device. 135eb9ad377SManos Pitsidianakis * Returns NULL if @stream_id is invalid or not allocated. 136eb9ad377SManos Pitsidianakis * 137eb9ad377SManos Pitsidianakis * @s: VirtIOSound device 138eb9ad377SManos Pitsidianakis * @stream_id: stream id 139eb9ad377SManos Pitsidianakis */ 140eb9ad377SManos Pitsidianakis static VirtIOSoundPCMStream *virtio_snd_pcm_get_stream(VirtIOSound *s, 141eb9ad377SManos Pitsidianakis uint32_t stream_id) 142eb9ad377SManos Pitsidianakis { 143eb9ad377SManos Pitsidianakis return stream_id >= s->snd_conf.streams ? NULL : 144eb9ad377SManos Pitsidianakis s->pcm->streams[stream_id]; 145eb9ad377SManos Pitsidianakis } 146eb9ad377SManos Pitsidianakis 147eb9ad377SManos Pitsidianakis /* 148eb9ad377SManos Pitsidianakis * Get params for a specific stream. 149eb9ad377SManos Pitsidianakis * 150eb9ad377SManos Pitsidianakis * @s: VirtIOSound device 151eb9ad377SManos Pitsidianakis * @stream_id: stream id 152eb9ad377SManos Pitsidianakis */ 153eb9ad377SManos Pitsidianakis static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s, 154eb9ad377SManos Pitsidianakis uint32_t stream_id) 155eb9ad377SManos Pitsidianakis { 156eb9ad377SManos Pitsidianakis return stream_id >= s->snd_conf.streams ? NULL 157eb9ad377SManos Pitsidianakis : &s->pcm->pcm_params[stream_id]; 158eb9ad377SManos Pitsidianakis } 159eb9ad377SManos Pitsidianakis 160eb9ad377SManos Pitsidianakis /* 1610ff05dd2SManos Pitsidianakis * Handle the VIRTIO_SND_R_PCM_INFO request. 1620ff05dd2SManos Pitsidianakis * The function writes the info structs to the request element. 1630ff05dd2SManos Pitsidianakis * 1640ff05dd2SManos Pitsidianakis * @s: VirtIOSound device 1650ff05dd2SManos Pitsidianakis * @cmd: The request command queue element from VirtIOSound cmdq field 1660ff05dd2SManos Pitsidianakis */ 1670ff05dd2SManos Pitsidianakis static void virtio_snd_handle_pcm_info(VirtIOSound *s, 1680ff05dd2SManos Pitsidianakis virtio_snd_ctrl_command *cmd) 1690ff05dd2SManos Pitsidianakis { 1700ff05dd2SManos Pitsidianakis uint32_t stream_id, start_id, count, size; 1710ff05dd2SManos Pitsidianakis virtio_snd_pcm_info val; 1720ff05dd2SManos Pitsidianakis virtio_snd_query_info req; 1730ff05dd2SManos Pitsidianakis VirtIOSoundPCMStream *stream = NULL; 1740ff05dd2SManos Pitsidianakis g_autofree virtio_snd_pcm_info *pcm_info = NULL; 1750ff05dd2SManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg, 1760ff05dd2SManos Pitsidianakis cmd->elem->out_num, 1770ff05dd2SManos Pitsidianakis 0, 1780ff05dd2SManos Pitsidianakis &req, 1790ff05dd2SManos Pitsidianakis sizeof(virtio_snd_query_info)); 1800ff05dd2SManos Pitsidianakis 1810ff05dd2SManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_query_info)) { 1820ff05dd2SManos Pitsidianakis /* 1830ff05dd2SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 1840ff05dd2SManos Pitsidianakis */ 1850ff05dd2SManos Pitsidianakis qemu_log_mask(LOG_GUEST_ERROR, 1860ff05dd2SManos Pitsidianakis "%s: virtio-snd command size incorrect %zu vs \ 1870ff05dd2SManos Pitsidianakis %zu\n", __func__, msg_sz, sizeof(virtio_snd_query_info)); 1880ff05dd2SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 1890ff05dd2SManos Pitsidianakis return; 1900ff05dd2SManos Pitsidianakis } 1910ff05dd2SManos Pitsidianakis 1920ff05dd2SManos Pitsidianakis start_id = le32_to_cpu(req.start_id); 1930ff05dd2SManos Pitsidianakis count = le32_to_cpu(req.count); 1940ff05dd2SManos Pitsidianakis size = le32_to_cpu(req.size); 1950ff05dd2SManos Pitsidianakis 1960ff05dd2SManos Pitsidianakis if (iov_size(cmd->elem->in_sg, cmd->elem->in_num) < 1970ff05dd2SManos Pitsidianakis sizeof(virtio_snd_hdr) + size * count) { 1980ff05dd2SManos Pitsidianakis /* 1990ff05dd2SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 2000ff05dd2SManos Pitsidianakis */ 2010ff05dd2SManos Pitsidianakis error_report("pcm info: buffer too small, got: %zu, needed: %zu", 2020ff05dd2SManos Pitsidianakis iov_size(cmd->elem->in_sg, cmd->elem->in_num), 2030ff05dd2SManos Pitsidianakis sizeof(virtio_snd_pcm_info)); 2040ff05dd2SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 2050ff05dd2SManos Pitsidianakis return; 2060ff05dd2SManos Pitsidianakis } 2070ff05dd2SManos Pitsidianakis 2080ff05dd2SManos Pitsidianakis pcm_info = g_new0(virtio_snd_pcm_info, count); 2090ff05dd2SManos Pitsidianakis for (uint32_t i = 0; i < count; i++) { 2100ff05dd2SManos Pitsidianakis stream_id = i + start_id; 2110ff05dd2SManos Pitsidianakis trace_virtio_snd_handle_pcm_info(stream_id); 2120ff05dd2SManos Pitsidianakis stream = virtio_snd_pcm_get_stream(s, stream_id); 2130ff05dd2SManos Pitsidianakis if (!stream) { 2140ff05dd2SManos Pitsidianakis error_report("Invalid stream id: %"PRIu32, stream_id); 2150ff05dd2SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 2160ff05dd2SManos Pitsidianakis return; 2170ff05dd2SManos Pitsidianakis } 2180ff05dd2SManos Pitsidianakis val = stream->info; 2190ff05dd2SManos Pitsidianakis val.hdr.hda_fn_nid = cpu_to_le32(val.hdr.hda_fn_nid); 2200ff05dd2SManos Pitsidianakis val.features = cpu_to_le32(val.features); 2210ff05dd2SManos Pitsidianakis val.formats = cpu_to_le64(val.formats); 2220ff05dd2SManos Pitsidianakis val.rates = cpu_to_le64(val.rates); 2230ff05dd2SManos Pitsidianakis /* 2240ff05dd2SManos Pitsidianakis * 5.14.6.6.2.1 Device Requirements: Stream Information The device MUST 2250ff05dd2SManos Pitsidianakis * NOT set undefined feature, format, rate and direction values. The 2260ff05dd2SManos Pitsidianakis * device MUST initialize the padding bytes to 0. 2270ff05dd2SManos Pitsidianakis */ 2280ff05dd2SManos Pitsidianakis pcm_info[i] = val; 2290ff05dd2SManos Pitsidianakis memset(&pcm_info[i].padding, 0, 5); 2300ff05dd2SManos Pitsidianakis } 2310ff05dd2SManos Pitsidianakis 2320ff05dd2SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); 2330ff05dd2SManos Pitsidianakis iov_from_buf(cmd->elem->in_sg, 2340ff05dd2SManos Pitsidianakis cmd->elem->in_num, 2350ff05dd2SManos Pitsidianakis sizeof(virtio_snd_hdr), 2360ff05dd2SManos Pitsidianakis pcm_info, 2370ff05dd2SManos Pitsidianakis sizeof(virtio_snd_pcm_info) * count); 2380ff05dd2SManos Pitsidianakis } 2390ff05dd2SManos Pitsidianakis 2400ff05dd2SManos Pitsidianakis /* 241eb9ad377SManos Pitsidianakis * Set the given stream params. 242eb9ad377SManos Pitsidianakis * Called by both virtio_snd_handle_pcm_set_params and during device 243eb9ad377SManos Pitsidianakis * initialization. 244eb9ad377SManos Pitsidianakis * Returns the response status code. (VIRTIO_SND_S_*). 245eb9ad377SManos Pitsidianakis * 246eb9ad377SManos Pitsidianakis * @s: VirtIOSound device 247eb9ad377SManos Pitsidianakis * @params: The PCM params as defined in the virtio specification 248eb9ad377SManos Pitsidianakis */ 249eb9ad377SManos Pitsidianakis static 250eb9ad377SManos Pitsidianakis uint32_t virtio_snd_set_pcm_params(VirtIOSound *s, 251eb9ad377SManos Pitsidianakis uint32_t stream_id, 252eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params *params) 253eb9ad377SManos Pitsidianakis { 254eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params *st_params; 255eb9ad377SManos Pitsidianakis 256eb9ad377SManos Pitsidianakis if (stream_id >= s->snd_conf.streams || s->pcm->pcm_params == NULL) { 257eb9ad377SManos Pitsidianakis /* 258eb9ad377SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 259eb9ad377SManos Pitsidianakis */ 260eb9ad377SManos Pitsidianakis virtio_error(VIRTIO_DEVICE(s), "Streams have not been initialized.\n"); 261eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 262eb9ad377SManos Pitsidianakis } 263eb9ad377SManos Pitsidianakis 264eb9ad377SManos Pitsidianakis st_params = virtio_snd_pcm_get_params(s, stream_id); 265eb9ad377SManos Pitsidianakis 266eb9ad377SManos Pitsidianakis if (params->channels < 1 || params->channels > AUDIO_MAX_CHANNELS) { 267eb9ad377SManos Pitsidianakis error_report("Number of channels is not supported."); 268eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); 269eb9ad377SManos Pitsidianakis } 270eb9ad377SManos Pitsidianakis if (!(supported_formats & BIT(params->format))) { 271eb9ad377SManos Pitsidianakis error_report("Stream format is not supported."); 272eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); 273eb9ad377SManos Pitsidianakis } 274eb9ad377SManos Pitsidianakis if (!(supported_rates & BIT(params->rate))) { 275eb9ad377SManos Pitsidianakis error_report("Stream rate is not supported."); 276eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); 277eb9ad377SManos Pitsidianakis } 278eb9ad377SManos Pitsidianakis 279eb9ad377SManos Pitsidianakis st_params->buffer_bytes = le32_to_cpu(params->buffer_bytes); 280eb9ad377SManos Pitsidianakis st_params->period_bytes = le32_to_cpu(params->period_bytes); 281eb9ad377SManos Pitsidianakis st_params->features = le32_to_cpu(params->features); 282eb9ad377SManos Pitsidianakis /* the following are uint8_t, so there's no need to bswap the values. */ 283eb9ad377SManos Pitsidianakis st_params->channels = params->channels; 284eb9ad377SManos Pitsidianakis st_params->format = params->format; 285eb9ad377SManos Pitsidianakis st_params->rate = params->rate; 286eb9ad377SManos Pitsidianakis 287eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_OK); 288eb9ad377SManos Pitsidianakis } 289eb9ad377SManos Pitsidianakis 290eb9ad377SManos Pitsidianakis /* 291eb9ad377SManos Pitsidianakis * Get a QEMU Audiosystem compatible format value from a VIRTIO_SND_PCM_FMT_* 292eb9ad377SManos Pitsidianakis */ 293eb9ad377SManos Pitsidianakis static AudioFormat virtio_snd_get_qemu_format(uint32_t format) 294eb9ad377SManos Pitsidianakis { 295eb9ad377SManos Pitsidianakis #define CASE(FMT) \ 296eb9ad377SManos Pitsidianakis case VIRTIO_SND_PCM_FMT_##FMT: \ 297eb9ad377SManos Pitsidianakis return AUDIO_FORMAT_##FMT; 298eb9ad377SManos Pitsidianakis 299eb9ad377SManos Pitsidianakis switch (format) { 300eb9ad377SManos Pitsidianakis CASE(U8) 301eb9ad377SManos Pitsidianakis CASE(S8) 302eb9ad377SManos Pitsidianakis CASE(U16) 303eb9ad377SManos Pitsidianakis CASE(S16) 304eb9ad377SManos Pitsidianakis CASE(U32) 305eb9ad377SManos Pitsidianakis CASE(S32) 306eb9ad377SManos Pitsidianakis case VIRTIO_SND_PCM_FMT_FLOAT: 307eb9ad377SManos Pitsidianakis return AUDIO_FORMAT_F32; 308eb9ad377SManos Pitsidianakis default: 309eb9ad377SManos Pitsidianakis g_assert_not_reached(); 310eb9ad377SManos Pitsidianakis } 311eb9ad377SManos Pitsidianakis 312eb9ad377SManos Pitsidianakis #undef CASE 313eb9ad377SManos Pitsidianakis } 314eb9ad377SManos Pitsidianakis 315eb9ad377SManos Pitsidianakis /* 316eb9ad377SManos Pitsidianakis * Get a QEMU Audiosystem compatible frequency value from a 317eb9ad377SManos Pitsidianakis * VIRTIO_SND_PCM_RATE_* 318eb9ad377SManos Pitsidianakis */ 319eb9ad377SManos Pitsidianakis static uint32_t virtio_snd_get_qemu_freq(uint32_t rate) 320eb9ad377SManos Pitsidianakis { 321eb9ad377SManos Pitsidianakis #define CASE(RATE) \ 322eb9ad377SManos Pitsidianakis case VIRTIO_SND_PCM_RATE_##RATE: \ 323eb9ad377SManos Pitsidianakis return RATE; 324eb9ad377SManos Pitsidianakis 325eb9ad377SManos Pitsidianakis switch (rate) { 326eb9ad377SManos Pitsidianakis CASE(5512) 327eb9ad377SManos Pitsidianakis CASE(8000) 328eb9ad377SManos Pitsidianakis CASE(11025) 329eb9ad377SManos Pitsidianakis CASE(16000) 330eb9ad377SManos Pitsidianakis CASE(22050) 331eb9ad377SManos Pitsidianakis CASE(32000) 332eb9ad377SManos Pitsidianakis CASE(44100) 333eb9ad377SManos Pitsidianakis CASE(48000) 334eb9ad377SManos Pitsidianakis CASE(64000) 335eb9ad377SManos Pitsidianakis CASE(88200) 336eb9ad377SManos Pitsidianakis CASE(96000) 337eb9ad377SManos Pitsidianakis CASE(176400) 338eb9ad377SManos Pitsidianakis CASE(192000) 339eb9ad377SManos Pitsidianakis CASE(384000) 340eb9ad377SManos Pitsidianakis default: 341eb9ad377SManos Pitsidianakis g_assert_not_reached(); 342eb9ad377SManos Pitsidianakis } 343eb9ad377SManos Pitsidianakis 344eb9ad377SManos Pitsidianakis #undef CASE 345eb9ad377SManos Pitsidianakis } 346eb9ad377SManos Pitsidianakis 347eb9ad377SManos Pitsidianakis /* 348eb9ad377SManos Pitsidianakis * Get QEMU Audiosystem compatible audsettings from virtio based pcm stream 349eb9ad377SManos Pitsidianakis * params. 350eb9ad377SManos Pitsidianakis */ 351eb9ad377SManos Pitsidianakis static void virtio_snd_get_qemu_audsettings(audsettings *as, 352eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params *params) 353eb9ad377SManos Pitsidianakis { 354eb9ad377SManos Pitsidianakis as->nchannels = MIN(AUDIO_MAX_CHANNELS, params->channels); 355eb9ad377SManos Pitsidianakis as->fmt = virtio_snd_get_qemu_format(params->format); 356eb9ad377SManos Pitsidianakis as->freq = virtio_snd_get_qemu_freq(params->rate); 357eb9ad377SManos Pitsidianakis as->endianness = target_words_bigendian() ? 1 : 0; 358eb9ad377SManos Pitsidianakis } 359eb9ad377SManos Pitsidianakis 360eb9ad377SManos Pitsidianakis /* 361eb9ad377SManos Pitsidianakis * Close a stream and free all its resources. 362eb9ad377SManos Pitsidianakis * 363eb9ad377SManos Pitsidianakis * @stream: VirtIOSoundPCMStream *stream 364eb9ad377SManos Pitsidianakis */ 365eb9ad377SManos Pitsidianakis static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream) 366eb9ad377SManos Pitsidianakis { 367eb9ad377SManos Pitsidianakis } 368eb9ad377SManos Pitsidianakis 369eb9ad377SManos Pitsidianakis /* 370eb9ad377SManos Pitsidianakis * Prepares a VirtIOSound card stream. 371eb9ad377SManos Pitsidianakis * Returns the response status code. (VIRTIO_SND_S_*). 372eb9ad377SManos Pitsidianakis * 373eb9ad377SManos Pitsidianakis * @s: VirtIOSound device 374eb9ad377SManos Pitsidianakis * @stream_id: stream id 375eb9ad377SManos Pitsidianakis */ 376eb9ad377SManos Pitsidianakis static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id) 377eb9ad377SManos Pitsidianakis { 378eb9ad377SManos Pitsidianakis audsettings as; 379eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params *params; 380eb9ad377SManos Pitsidianakis VirtIOSoundPCMStream *stream; 381eb9ad377SManos Pitsidianakis 382eb9ad377SManos Pitsidianakis if (s->pcm->streams == NULL || 383eb9ad377SManos Pitsidianakis s->pcm->pcm_params == NULL || 384eb9ad377SManos Pitsidianakis stream_id >= s->snd_conf.streams) { 385eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 386eb9ad377SManos Pitsidianakis } 387eb9ad377SManos Pitsidianakis 388eb9ad377SManos Pitsidianakis params = virtio_snd_pcm_get_params(s, stream_id); 389eb9ad377SManos Pitsidianakis if (params == NULL) { 390eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 391eb9ad377SManos Pitsidianakis } 392eb9ad377SManos Pitsidianakis 393eb9ad377SManos Pitsidianakis stream = virtio_snd_pcm_get_stream(s, stream_id); 394eb9ad377SManos Pitsidianakis if (stream == NULL) { 395eb9ad377SManos Pitsidianakis stream = g_new0(VirtIOSoundPCMStream, 1); 396eb9ad377SManos Pitsidianakis stream->active = false; 397eb9ad377SManos Pitsidianakis stream->id = stream_id; 398eb9ad377SManos Pitsidianakis stream->pcm = s->pcm; 399eb9ad377SManos Pitsidianakis stream->s = s; 400eb9ad377SManos Pitsidianakis 401eb9ad377SManos Pitsidianakis /* 402eb9ad377SManos Pitsidianakis * stream_id >= s->snd_conf.streams was checked before so this is 403eb9ad377SManos Pitsidianakis * in-bounds 404eb9ad377SManos Pitsidianakis */ 405eb9ad377SManos Pitsidianakis s->pcm->streams[stream_id] = stream; 406eb9ad377SManos Pitsidianakis } 407eb9ad377SManos Pitsidianakis 408eb9ad377SManos Pitsidianakis virtio_snd_get_qemu_audsettings(&as, params); 409eb9ad377SManos Pitsidianakis stream->info.direction = stream_id < s->snd_conf.streams / 2 + 410eb9ad377SManos Pitsidianakis (s->snd_conf.streams & 1) ? VIRTIO_SND_D_OUTPUT : VIRTIO_SND_D_INPUT; 411eb9ad377SManos Pitsidianakis stream->info.hdr.hda_fn_nid = VIRTIO_SOUND_HDA_FN_NID; 412eb9ad377SManos Pitsidianakis stream->info.features = 0; 413eb9ad377SManos Pitsidianakis stream->info.channels_min = 1; 414eb9ad377SManos Pitsidianakis stream->info.channels_max = as.nchannels; 415eb9ad377SManos Pitsidianakis stream->info.formats = supported_formats; 416eb9ad377SManos Pitsidianakis stream->info.rates = supported_rates; 417eb9ad377SManos Pitsidianakis stream->params = *params; 418eb9ad377SManos Pitsidianakis 419eb9ad377SManos Pitsidianakis stream->positions[0] = VIRTIO_SND_CHMAP_FL; 420eb9ad377SManos Pitsidianakis stream->positions[1] = VIRTIO_SND_CHMAP_FR; 421eb9ad377SManos Pitsidianakis stream->as = as; 422eb9ad377SManos Pitsidianakis 423eb9ad377SManos Pitsidianakis return cpu_to_le32(VIRTIO_SND_S_OK); 424eb9ad377SManos Pitsidianakis } 425eb9ad377SManos Pitsidianakis 426eb9ad377SManos Pitsidianakis static const char *print_code(uint32_t code) 427eb9ad377SManos Pitsidianakis { 428eb9ad377SManos Pitsidianakis #define CASE(CODE) \ 429eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_##CODE: \ 430eb9ad377SManos Pitsidianakis return "VIRTIO_SND_R_"#CODE 431eb9ad377SManos Pitsidianakis 432eb9ad377SManos Pitsidianakis switch (code) { 433eb9ad377SManos Pitsidianakis CASE(JACK_INFO); 434eb9ad377SManos Pitsidianakis CASE(JACK_REMAP); 435eb9ad377SManos Pitsidianakis CASE(PCM_INFO); 436eb9ad377SManos Pitsidianakis CASE(PCM_SET_PARAMS); 437eb9ad377SManos Pitsidianakis CASE(PCM_PREPARE); 438eb9ad377SManos Pitsidianakis CASE(PCM_RELEASE); 439eb9ad377SManos Pitsidianakis CASE(PCM_START); 440eb9ad377SManos Pitsidianakis CASE(PCM_STOP); 441eb9ad377SManos Pitsidianakis CASE(CHMAP_INFO); 442eb9ad377SManos Pitsidianakis default: 443eb9ad377SManos Pitsidianakis return "invalid code"; 444eb9ad377SManos Pitsidianakis } 445eb9ad377SManos Pitsidianakis 446eb9ad377SManos Pitsidianakis #undef CASE 447eb9ad377SManos Pitsidianakis }; 448eb9ad377SManos Pitsidianakis 449eb9ad377SManos Pitsidianakis /* 450eb9ad377SManos Pitsidianakis * The actual processing done in virtio_snd_process_cmdq(). 451eb9ad377SManos Pitsidianakis * 452eb9ad377SManos Pitsidianakis * @s: VirtIOSound device 453eb9ad377SManos Pitsidianakis * @cmd: control command request 454eb9ad377SManos Pitsidianakis */ 455eb9ad377SManos Pitsidianakis static inline void 456eb9ad377SManos Pitsidianakis process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd) 457eb9ad377SManos Pitsidianakis { 458eb9ad377SManos Pitsidianakis uint32_t code; 459eb9ad377SManos Pitsidianakis size_t msg_sz = iov_to_buf(cmd->elem->out_sg, 460eb9ad377SManos Pitsidianakis cmd->elem->out_num, 461eb9ad377SManos Pitsidianakis 0, 462eb9ad377SManos Pitsidianakis &cmd->ctrl, 463eb9ad377SManos Pitsidianakis sizeof(virtio_snd_hdr)); 464eb9ad377SManos Pitsidianakis 465eb9ad377SManos Pitsidianakis if (msg_sz != sizeof(virtio_snd_hdr)) { 466eb9ad377SManos Pitsidianakis /* 467eb9ad377SManos Pitsidianakis * TODO: do we need to set DEVICE_NEEDS_RESET? 468eb9ad377SManos Pitsidianakis */ 469eb9ad377SManos Pitsidianakis qemu_log_mask(LOG_GUEST_ERROR, 470eb9ad377SManos Pitsidianakis "%s: virtio-snd command size incorrect %zu vs \ 471eb9ad377SManos Pitsidianakis %zu\n", __func__, msg_sz, sizeof(virtio_snd_hdr)); 472eb9ad377SManos Pitsidianakis return; 473eb9ad377SManos Pitsidianakis } 474eb9ad377SManos Pitsidianakis 475eb9ad377SManos Pitsidianakis code = le32_to_cpu(cmd->ctrl.code); 476eb9ad377SManos Pitsidianakis 477eb9ad377SManos Pitsidianakis trace_virtio_snd_handle_code(code, print_code(code)); 478eb9ad377SManos Pitsidianakis 479eb9ad377SManos Pitsidianakis switch (code) { 480eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_JACK_INFO: 481eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_JACK_REMAP: 482eb9ad377SManos Pitsidianakis qemu_log_mask(LOG_UNIMP, 483eb9ad377SManos Pitsidianakis "virtio_snd: jack functionality is unimplemented.\n"); 484eb9ad377SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); 485eb9ad377SManos Pitsidianakis break; 486eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_INFO: 4870ff05dd2SManos Pitsidianakis virtio_snd_handle_pcm_info(s, cmd); 4880ff05dd2SManos Pitsidianakis break; 489eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_SET_PARAMS: 490eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_PREPARE: 491eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_START: 492eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_STOP: 493eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_PCM_RELEASE: 494eb9ad377SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); 495eb9ad377SManos Pitsidianakis break; 496eb9ad377SManos Pitsidianakis case VIRTIO_SND_R_CHMAP_INFO: 497eb9ad377SManos Pitsidianakis qemu_log_mask(LOG_UNIMP, 498eb9ad377SManos Pitsidianakis "virtio_snd: chmap info functionality is unimplemented.\n"); 499eb9ad377SManos Pitsidianakis trace_virtio_snd_handle_chmap_info(); 500eb9ad377SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); 501eb9ad377SManos Pitsidianakis break; 502eb9ad377SManos Pitsidianakis default: 503eb9ad377SManos Pitsidianakis /* error */ 504eb9ad377SManos Pitsidianakis error_report("virtio snd header not recognized: %"PRIu32, code); 505eb9ad377SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); 506eb9ad377SManos Pitsidianakis } 507eb9ad377SManos Pitsidianakis 508eb9ad377SManos Pitsidianakis iov_from_buf(cmd->elem->in_sg, 509eb9ad377SManos Pitsidianakis cmd->elem->in_num, 510eb9ad377SManos Pitsidianakis 0, 511eb9ad377SManos Pitsidianakis &cmd->resp, 512eb9ad377SManos Pitsidianakis sizeof(virtio_snd_hdr)); 513eb9ad377SManos Pitsidianakis virtqueue_push(cmd->vq, cmd->elem, sizeof(virtio_snd_hdr)); 514eb9ad377SManos Pitsidianakis virtio_notify(VIRTIO_DEVICE(s), cmd->vq); 515eb9ad377SManos Pitsidianakis } 516eb9ad377SManos Pitsidianakis 517eb9ad377SManos Pitsidianakis /* 518eb9ad377SManos Pitsidianakis * Consume all elements in command queue. 519eb9ad377SManos Pitsidianakis * 520eb9ad377SManos Pitsidianakis * @s: VirtIOSound device 521eb9ad377SManos Pitsidianakis */ 522eb9ad377SManos Pitsidianakis static void virtio_snd_process_cmdq(VirtIOSound *s) 523eb9ad377SManos Pitsidianakis { 524eb9ad377SManos Pitsidianakis virtio_snd_ctrl_command *cmd; 525eb9ad377SManos Pitsidianakis 526eb9ad377SManos Pitsidianakis if (unlikely(qatomic_read(&s->processing_cmdq))) { 527eb9ad377SManos Pitsidianakis return; 528eb9ad377SManos Pitsidianakis } 529eb9ad377SManos Pitsidianakis 530eb9ad377SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&s->cmdq_mutex) { 531eb9ad377SManos Pitsidianakis qatomic_set(&s->processing_cmdq, true); 532eb9ad377SManos Pitsidianakis while (!QTAILQ_EMPTY(&s->cmdq)) { 533eb9ad377SManos Pitsidianakis cmd = QTAILQ_FIRST(&s->cmdq); 534eb9ad377SManos Pitsidianakis 535eb9ad377SManos Pitsidianakis /* process command */ 536eb9ad377SManos Pitsidianakis process_cmd(s, cmd); 537eb9ad377SManos Pitsidianakis 538eb9ad377SManos Pitsidianakis QTAILQ_REMOVE(&s->cmdq, cmd, next); 539eb9ad377SManos Pitsidianakis 540eb9ad377SManos Pitsidianakis virtio_snd_ctrl_cmd_free(cmd); 541eb9ad377SManos Pitsidianakis } 542eb9ad377SManos Pitsidianakis qatomic_set(&s->processing_cmdq, false); 543eb9ad377SManos Pitsidianakis } 544eb9ad377SManos Pitsidianakis } 545eb9ad377SManos Pitsidianakis 546eb9ad377SManos Pitsidianakis /* 547eb9ad377SManos Pitsidianakis * The control message handler. Pops an element from the control virtqueue, 548eb9ad377SManos Pitsidianakis * and stores them to VirtIOSound's cmdq queue and finally calls 549eb9ad377SManos Pitsidianakis * virtio_snd_process_cmdq() for processing. 550eb9ad377SManos Pitsidianakis * 551eb9ad377SManos Pitsidianakis * @vdev: VirtIOSound device 552eb9ad377SManos Pitsidianakis * @vq: Control virtqueue 553eb9ad377SManos Pitsidianakis */ 554eb9ad377SManos Pitsidianakis static void virtio_snd_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) 555eb9ad377SManos Pitsidianakis { 556eb9ad377SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 557eb9ad377SManos Pitsidianakis VirtQueueElement *elem; 558eb9ad377SManos Pitsidianakis virtio_snd_ctrl_command *cmd; 559eb9ad377SManos Pitsidianakis 560eb9ad377SManos Pitsidianakis trace_virtio_snd_handle_ctrl(vdev, vq); 561eb9ad377SManos Pitsidianakis 562eb9ad377SManos Pitsidianakis if (!virtio_queue_ready(vq)) { 563eb9ad377SManos Pitsidianakis return; 564eb9ad377SManos Pitsidianakis } 565eb9ad377SManos Pitsidianakis 566eb9ad377SManos Pitsidianakis elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); 567eb9ad377SManos Pitsidianakis while (elem) { 568eb9ad377SManos Pitsidianakis cmd = g_new0(virtio_snd_ctrl_command, 1); 569eb9ad377SManos Pitsidianakis cmd->elem = elem; 570eb9ad377SManos Pitsidianakis cmd->vq = vq; 571eb9ad377SManos Pitsidianakis cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); 572eb9ad377SManos Pitsidianakis QTAILQ_INSERT_TAIL(&s->cmdq, cmd, next); 573eb9ad377SManos Pitsidianakis elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); 574eb9ad377SManos Pitsidianakis } 575eb9ad377SManos Pitsidianakis 576eb9ad377SManos Pitsidianakis virtio_snd_process_cmdq(s); 577eb9ad377SManos Pitsidianakis } 578eb9ad377SManos Pitsidianakis 579eb9ad377SManos Pitsidianakis /* 580eb9ad377SManos Pitsidianakis * The event virtqueue handler. 581eb9ad377SManos Pitsidianakis * Not implemented yet. 582eb9ad377SManos Pitsidianakis * 583eb9ad377SManos Pitsidianakis * @vdev: VirtIOSound device 584eb9ad377SManos Pitsidianakis * @vq: event vq 585eb9ad377SManos Pitsidianakis */ 586eb9ad377SManos Pitsidianakis static void virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq) 587eb9ad377SManos Pitsidianakis { 588eb9ad377SManos Pitsidianakis qemu_log_mask(LOG_UNIMP, "virtio_snd: event queue is unimplemented.\n"); 589eb9ad377SManos Pitsidianakis trace_virtio_snd_handle_event(); 590eb9ad377SManos Pitsidianakis } 591eb9ad377SManos Pitsidianakis 592eb9ad377SManos Pitsidianakis /* 593eb9ad377SManos Pitsidianakis * Stub buffer virtqueue handler. 5942880e676SManos Pitsidianakis * 5952880e676SManos Pitsidianakis * @vdev: VirtIOSound device 5962880e676SManos Pitsidianakis * @vq: virtqueue 5972880e676SManos Pitsidianakis */ 598eb9ad377SManos Pitsidianakis static void virtio_snd_handle_xfer(VirtIODevice *vdev, VirtQueue *vq) {} 5992880e676SManos Pitsidianakis 6002880e676SManos Pitsidianakis static uint64_t get_features(VirtIODevice *vdev, uint64_t features, 6012880e676SManos Pitsidianakis Error **errp) 6022880e676SManos Pitsidianakis { 6032880e676SManos Pitsidianakis /* 6042880e676SManos Pitsidianakis * virtio-v1.2-csd01, 5.14.3, 6052880e676SManos Pitsidianakis * Feature Bits 6062880e676SManos Pitsidianakis * None currently defined. 6072880e676SManos Pitsidianakis */ 6082880e676SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 6092880e676SManos Pitsidianakis features |= s->features; 6102880e676SManos Pitsidianakis 6112880e676SManos Pitsidianakis trace_virtio_snd_get_features(vdev, features); 6122880e676SManos Pitsidianakis 6132880e676SManos Pitsidianakis return features; 6142880e676SManos Pitsidianakis } 6152880e676SManos Pitsidianakis 6162880e676SManos Pitsidianakis static void 6172880e676SManos Pitsidianakis virtio_snd_vm_state_change(void *opaque, bool running, 6182880e676SManos Pitsidianakis RunState state) 6192880e676SManos Pitsidianakis { 6202880e676SManos Pitsidianakis if (running) { 6212880e676SManos Pitsidianakis trace_virtio_snd_vm_state_running(); 6222880e676SManos Pitsidianakis } else { 6232880e676SManos Pitsidianakis trace_virtio_snd_vm_state_stopped(); 6242880e676SManos Pitsidianakis } 6252880e676SManos Pitsidianakis } 6262880e676SManos Pitsidianakis 6272880e676SManos Pitsidianakis static void virtio_snd_realize(DeviceState *dev, Error **errp) 6282880e676SManos Pitsidianakis { 6292880e676SManos Pitsidianakis ERRP_GUARD(); 6302880e676SManos Pitsidianakis VirtIOSound *vsnd = VIRTIO_SND(dev); 6312880e676SManos Pitsidianakis VirtIODevice *vdev = VIRTIO_DEVICE(dev); 632eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params default_params = { 0 }; 633eb9ad377SManos Pitsidianakis uint32_t status; 6342880e676SManos Pitsidianakis 635eb9ad377SManos Pitsidianakis vsnd->pcm = NULL; 6362880e676SManos Pitsidianakis vsnd->vmstate = 6372880e676SManos Pitsidianakis qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd); 6382880e676SManos Pitsidianakis 6392880e676SManos Pitsidianakis trace_virtio_snd_realize(vsnd); 6402880e676SManos Pitsidianakis 641eb9ad377SManos Pitsidianakis vsnd->pcm = g_new0(VirtIOSoundPCM, 1); 642eb9ad377SManos Pitsidianakis vsnd->pcm->snd = vsnd; 643eb9ad377SManos Pitsidianakis vsnd->pcm->streams = 644eb9ad377SManos Pitsidianakis g_new0(VirtIOSoundPCMStream *, vsnd->snd_conf.streams); 645eb9ad377SManos Pitsidianakis vsnd->pcm->pcm_params = 646eb9ad377SManos Pitsidianakis g_new0(virtio_snd_pcm_set_params, vsnd->snd_conf.streams); 647eb9ad377SManos Pitsidianakis 6482880e676SManos Pitsidianakis virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config)); 6492880e676SManos Pitsidianakis virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1); 6502880e676SManos Pitsidianakis 6512880e676SManos Pitsidianakis /* set number of jacks and streams */ 6522880e676SManos Pitsidianakis if (vsnd->snd_conf.jacks > 8) { 6532880e676SManos Pitsidianakis error_setg(errp, 6542880e676SManos Pitsidianakis "Invalid number of jacks: %"PRIu32, 6552880e676SManos Pitsidianakis vsnd->snd_conf.jacks); 6562880e676SManos Pitsidianakis return; 6572880e676SManos Pitsidianakis } 6582880e676SManos Pitsidianakis if (vsnd->snd_conf.streams < 1 || vsnd->snd_conf.streams > 10) { 6592880e676SManos Pitsidianakis error_setg(errp, 6602880e676SManos Pitsidianakis "Invalid number of streams: %"PRIu32, 6612880e676SManos Pitsidianakis vsnd->snd_conf.streams); 6622880e676SManos Pitsidianakis return; 6632880e676SManos Pitsidianakis } 6642880e676SManos Pitsidianakis 6652880e676SManos Pitsidianakis if (vsnd->snd_conf.chmaps > VIRTIO_SND_CHMAP_MAX_SIZE) { 6662880e676SManos Pitsidianakis error_setg(errp, 6672880e676SManos Pitsidianakis "Invalid number of channel maps: %"PRIu32, 6682880e676SManos Pitsidianakis vsnd->snd_conf.chmaps); 6692880e676SManos Pitsidianakis return; 6702880e676SManos Pitsidianakis } 6712880e676SManos Pitsidianakis 6722880e676SManos Pitsidianakis AUD_register_card("virtio-sound", &vsnd->card, errp); 6732880e676SManos Pitsidianakis 674eb9ad377SManos Pitsidianakis /* set default params for all streams */ 675eb9ad377SManos Pitsidianakis default_params.features = 0; 676eb9ad377SManos Pitsidianakis default_params.buffer_bytes = cpu_to_le32(8192); 677eb9ad377SManos Pitsidianakis default_params.period_bytes = cpu_to_le32(2048); 678eb9ad377SManos Pitsidianakis default_params.channels = 2; 679eb9ad377SManos Pitsidianakis default_params.format = VIRTIO_SND_PCM_FMT_S16; 680eb9ad377SManos Pitsidianakis default_params.rate = VIRTIO_SND_PCM_RATE_48000; 6812880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_CONTROL] = 682eb9ad377SManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_ctrl); 6832880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_EVENT] = 684eb9ad377SManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_event); 6852880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_TX] = 686eb9ad377SManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_xfer); 6872880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_RX] = 688eb9ad377SManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_xfer); 689eb9ad377SManos Pitsidianakis qemu_mutex_init(&vsnd->cmdq_mutex); 690eb9ad377SManos Pitsidianakis QTAILQ_INIT(&vsnd->cmdq); 691eb9ad377SManos Pitsidianakis 692eb9ad377SManos Pitsidianakis for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) { 693eb9ad377SManos Pitsidianakis status = virtio_snd_set_pcm_params(vsnd, i, &default_params); 694eb9ad377SManos Pitsidianakis if (status != cpu_to_le32(VIRTIO_SND_S_OK)) { 695eb9ad377SManos Pitsidianakis error_setg(errp, 696eb9ad377SManos Pitsidianakis "Can't initalize stream params, device responded with %s.", 697eb9ad377SManos Pitsidianakis print_code(status)); 698eb9ad377SManos Pitsidianakis return; 699eb9ad377SManos Pitsidianakis } 700eb9ad377SManos Pitsidianakis status = virtio_snd_pcm_prepare(vsnd, i); 701eb9ad377SManos Pitsidianakis if (status != cpu_to_le32(VIRTIO_SND_S_OK)) { 702eb9ad377SManos Pitsidianakis error_setg(errp, 703eb9ad377SManos Pitsidianakis "Can't prepare streams, device responded with %s.", 704eb9ad377SManos Pitsidianakis print_code(status)); 705eb9ad377SManos Pitsidianakis return; 706eb9ad377SManos Pitsidianakis } 707eb9ad377SManos Pitsidianakis } 7082880e676SManos Pitsidianakis } 7092880e676SManos Pitsidianakis 7102880e676SManos Pitsidianakis static void virtio_snd_unrealize(DeviceState *dev) 7112880e676SManos Pitsidianakis { 7122880e676SManos Pitsidianakis VirtIODevice *vdev = VIRTIO_DEVICE(dev); 7132880e676SManos Pitsidianakis VirtIOSound *vsnd = VIRTIO_SND(dev); 714eb9ad377SManos Pitsidianakis VirtIOSoundPCMStream *stream; 7152880e676SManos Pitsidianakis 7162880e676SManos Pitsidianakis qemu_del_vm_change_state_handler(vsnd->vmstate); 7172880e676SManos Pitsidianakis trace_virtio_snd_unrealize(vsnd); 7182880e676SManos Pitsidianakis 719eb9ad377SManos Pitsidianakis if (vsnd->pcm) { 720eb9ad377SManos Pitsidianakis if (vsnd->pcm->streams) { 721eb9ad377SManos Pitsidianakis for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) { 722eb9ad377SManos Pitsidianakis stream = vsnd->pcm->streams[i]; 723eb9ad377SManos Pitsidianakis if (stream) { 724eb9ad377SManos Pitsidianakis virtio_snd_process_cmdq(stream->s); 725eb9ad377SManos Pitsidianakis virtio_snd_pcm_close(stream); 726eb9ad377SManos Pitsidianakis g_free(stream); 727eb9ad377SManos Pitsidianakis } 728eb9ad377SManos Pitsidianakis } 729eb9ad377SManos Pitsidianakis g_free(vsnd->pcm->streams); 730eb9ad377SManos Pitsidianakis } 731eb9ad377SManos Pitsidianakis g_free(vsnd->pcm->pcm_params); 732eb9ad377SManos Pitsidianakis g_free(vsnd->pcm); 733eb9ad377SManos Pitsidianakis vsnd->pcm = NULL; 734eb9ad377SManos Pitsidianakis } 7352880e676SManos Pitsidianakis AUD_remove_card(&vsnd->card); 736eb9ad377SManos Pitsidianakis qemu_mutex_destroy(&vsnd->cmdq_mutex); 7372880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]); 7382880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]); 7392880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]); 7402880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_RX]); 7412880e676SManos Pitsidianakis virtio_cleanup(vdev); 7422880e676SManos Pitsidianakis } 7432880e676SManos Pitsidianakis 7442880e676SManos Pitsidianakis 745eb9ad377SManos Pitsidianakis static void virtio_snd_reset(VirtIODevice *vdev) 746eb9ad377SManos Pitsidianakis { 747eb9ad377SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 748eb9ad377SManos Pitsidianakis virtio_snd_ctrl_command *cmd; 749eb9ad377SManos Pitsidianakis 750eb9ad377SManos Pitsidianakis WITH_QEMU_LOCK_GUARD(&s->cmdq_mutex) { 751eb9ad377SManos Pitsidianakis while (!QTAILQ_EMPTY(&s->cmdq)) { 752eb9ad377SManos Pitsidianakis cmd = QTAILQ_FIRST(&s->cmdq); 753eb9ad377SManos Pitsidianakis QTAILQ_REMOVE(&s->cmdq, cmd, next); 754eb9ad377SManos Pitsidianakis virtio_snd_ctrl_cmd_free(cmd); 755eb9ad377SManos Pitsidianakis } 756eb9ad377SManos Pitsidianakis } 757eb9ad377SManos Pitsidianakis } 7582880e676SManos Pitsidianakis 7592880e676SManos Pitsidianakis static void virtio_snd_class_init(ObjectClass *klass, void *data) 7602880e676SManos Pitsidianakis { 7612880e676SManos Pitsidianakis DeviceClass *dc = DEVICE_CLASS(klass); 7622880e676SManos Pitsidianakis VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 7632880e676SManos Pitsidianakis 7642880e676SManos Pitsidianakis 7652880e676SManos Pitsidianakis set_bit(DEVICE_CATEGORY_SOUND, dc->categories); 7662880e676SManos Pitsidianakis device_class_set_props(dc, virtio_snd_properties); 7672880e676SManos Pitsidianakis 7682880e676SManos Pitsidianakis dc->vmsd = &vmstate_virtio_snd; 7692880e676SManos Pitsidianakis vdc->vmsd = &vmstate_virtio_snd_device; 7702880e676SManos Pitsidianakis vdc->realize = virtio_snd_realize; 7712880e676SManos Pitsidianakis vdc->unrealize = virtio_snd_unrealize; 7722880e676SManos Pitsidianakis vdc->get_config = virtio_snd_get_config; 7732880e676SManos Pitsidianakis vdc->set_config = virtio_snd_set_config; 7742880e676SManos Pitsidianakis vdc->get_features = get_features; 7752880e676SManos Pitsidianakis vdc->reset = virtio_snd_reset; 7762880e676SManos Pitsidianakis vdc->legacy_features = 0; 7772880e676SManos Pitsidianakis } 7782880e676SManos Pitsidianakis 7792880e676SManos Pitsidianakis static const TypeInfo virtio_snd_types[] = { 7802880e676SManos Pitsidianakis { 7812880e676SManos Pitsidianakis .name = TYPE_VIRTIO_SND, 7822880e676SManos Pitsidianakis .parent = TYPE_VIRTIO_DEVICE, 7832880e676SManos Pitsidianakis .instance_size = sizeof(VirtIOSound), 7842880e676SManos Pitsidianakis .class_init = virtio_snd_class_init, 7852880e676SManos Pitsidianakis } 7862880e676SManos Pitsidianakis }; 7872880e676SManos Pitsidianakis 7882880e676SManos Pitsidianakis DEFINE_TYPES(virtio_snd_types) 789