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 * Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org> 92880e676SManos Pitsidianakis * Copyright (C) 2019 OpenSynergy GmbH 102880e676SManos Pitsidianakis * 112880e676SManos Pitsidianakis * This work is licensed under the terms of the GNU GPL, version 2 or 122880e676SManos Pitsidianakis * (at your option) any later version. See the COPYING file in the 132880e676SManos Pitsidianakis * top-level directory. 142880e676SManos Pitsidianakis */ 152880e676SManos Pitsidianakis 162880e676SManos Pitsidianakis #ifndef QEMU_VIRTIO_SOUND_H 172880e676SManos Pitsidianakis #define QEMU_VIRTIO_SOUND_H 182880e676SManos Pitsidianakis 192880e676SManos Pitsidianakis #include "hw/virtio/virtio.h" 202880e676SManos Pitsidianakis #include "audio/audio.h" 212880e676SManos Pitsidianakis #include "standard-headers/linux/virtio_ids.h" 222880e676SManos Pitsidianakis #include "standard-headers/linux/virtio_snd.h" 232880e676SManos Pitsidianakis 242880e676SManos Pitsidianakis #define TYPE_VIRTIO_SND "virtio-sound-device" 252880e676SManos Pitsidianakis #define VIRTIO_SND(obj) \ 262880e676SManos Pitsidianakis OBJECT_CHECK(VirtIOSound, (obj), TYPE_VIRTIO_SND) 272880e676SManos Pitsidianakis 282880e676SManos Pitsidianakis /* CONFIGURATION SPACE */ 292880e676SManos Pitsidianakis 302880e676SManos Pitsidianakis typedef struct virtio_snd_config virtio_snd_config; 312880e676SManos Pitsidianakis 322880e676SManos Pitsidianakis /* COMMON DEFINITIONS */ 332880e676SManos Pitsidianakis 342880e676SManos Pitsidianakis /* common header for request/response*/ 352880e676SManos Pitsidianakis typedef struct virtio_snd_hdr virtio_snd_hdr; 362880e676SManos Pitsidianakis 372880e676SManos Pitsidianakis /* event notification */ 382880e676SManos Pitsidianakis typedef struct virtio_snd_event virtio_snd_event; 392880e676SManos Pitsidianakis 402880e676SManos Pitsidianakis /* common control request to query an item information */ 412880e676SManos Pitsidianakis typedef struct virtio_snd_query_info virtio_snd_query_info; 422880e676SManos Pitsidianakis 432880e676SManos Pitsidianakis /* JACK CONTROL MESSAGES */ 442880e676SManos Pitsidianakis 452880e676SManos Pitsidianakis typedef struct virtio_snd_jack_hdr virtio_snd_jack_hdr; 462880e676SManos Pitsidianakis 472880e676SManos Pitsidianakis /* jack information structure */ 482880e676SManos Pitsidianakis typedef struct virtio_snd_jack_info virtio_snd_jack_info; 492880e676SManos Pitsidianakis 502880e676SManos Pitsidianakis /* jack remapping control request */ 512880e676SManos Pitsidianakis typedef struct virtio_snd_jack_remap virtio_snd_jack_remap; 522880e676SManos Pitsidianakis 532880e676SManos Pitsidianakis /* 542880e676SManos Pitsidianakis * PCM CONTROL MESSAGES 552880e676SManos Pitsidianakis */ 562880e676SManos Pitsidianakis typedef struct virtio_snd_pcm_hdr virtio_snd_pcm_hdr; 572880e676SManos Pitsidianakis 582880e676SManos Pitsidianakis /* PCM stream info structure */ 592880e676SManos Pitsidianakis typedef struct virtio_snd_pcm_info virtio_snd_pcm_info; 602880e676SManos Pitsidianakis 612880e676SManos Pitsidianakis /* set PCM stream params */ 622880e676SManos Pitsidianakis typedef struct virtio_snd_pcm_set_params virtio_snd_pcm_set_params; 632880e676SManos Pitsidianakis 642880e676SManos Pitsidianakis /* I/O request header */ 652880e676SManos Pitsidianakis typedef struct virtio_snd_pcm_xfer virtio_snd_pcm_xfer; 662880e676SManos Pitsidianakis 672880e676SManos Pitsidianakis /* I/O request status */ 682880e676SManos Pitsidianakis typedef struct virtio_snd_pcm_status virtio_snd_pcm_status; 692880e676SManos Pitsidianakis 70eb9ad377SManos Pitsidianakis /* device structs */ 71eb9ad377SManos Pitsidianakis 72eb9ad377SManos Pitsidianakis typedef struct VirtIOSound VirtIOSound; 73eb9ad377SManos Pitsidianakis 74eb9ad377SManos Pitsidianakis typedef struct VirtIOSoundPCMStream VirtIOSoundPCMStream; 75eb9ad377SManos Pitsidianakis 76eb9ad377SManos Pitsidianakis typedef struct virtio_snd_ctrl_command virtio_snd_ctrl_command; 77eb9ad377SManos Pitsidianakis 78eb9ad377SManos Pitsidianakis typedef struct VirtIOSoundPCM VirtIOSoundPCM; 79eb9ad377SManos Pitsidianakis 8018a75281SManos Pitsidianakis typedef struct VirtIOSoundPCMBuffer VirtIOSoundPCMBuffer; 8118a75281SManos Pitsidianakis 8218a75281SManos Pitsidianakis /* 8318a75281SManos Pitsidianakis * The VirtIO sound spec reuses layouts and values from the High Definition 8418a75281SManos Pitsidianakis * Audio spec (virtio/v1.2: 5.14 Sound Device). This struct handles each I/O 8518a75281SManos Pitsidianakis * message's buffer (virtio/v1.2: 5.14.6.8 PCM I/O Messages). 8618a75281SManos Pitsidianakis * 8718a75281SManos Pitsidianakis * In the case of TX (i.e. playback) buffers, we defer reading the raw PCM data 8818a75281SManos Pitsidianakis * from the virtqueue until QEMU's sound backsystem calls the output callback. 8918a75281SManos Pitsidianakis * This is tracked by the `bool populated;` field, which is set to true when 9018a75281SManos Pitsidianakis * data has been read into our own buffer for consumption. 9118a75281SManos Pitsidianakis * 9218a75281SManos Pitsidianakis * VirtIOSoundPCMBuffer has a dynamic size since it includes the raw PCM data 9318a75281SManos Pitsidianakis * in its allocation. It must be initialized and destroyed as follows: 9418a75281SManos Pitsidianakis * 9518a75281SManos Pitsidianakis * size_t size = [[derived from owned VQ element descriptor sizes]]; 9618a75281SManos Pitsidianakis * buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size); 9718a75281SManos Pitsidianakis * buffer->elem = [[owned VQ element]]; 9818a75281SManos Pitsidianakis * 9918a75281SManos Pitsidianakis * [..] 10018a75281SManos Pitsidianakis * 10118a75281SManos Pitsidianakis * g_free(buffer->elem); 10218a75281SManos Pitsidianakis * g_free(buffer); 10318a75281SManos Pitsidianakis */ 10418a75281SManos Pitsidianakis struct VirtIOSoundPCMBuffer { 10518a75281SManos Pitsidianakis QSIMPLEQ_ENTRY(VirtIOSoundPCMBuffer) entry; 10618a75281SManos Pitsidianakis VirtQueueElement *elem; 10718a75281SManos Pitsidianakis VirtQueue *vq; 10818a75281SManos Pitsidianakis size_t size; 10918a75281SManos Pitsidianakis /* 11018a75281SManos Pitsidianakis * In TX / Plaback, `offset` represents the first unused position inside 11118a75281SManos Pitsidianakis * `data`. If `offset == size` then there are no unused data left. 11218a75281SManos Pitsidianakis */ 11318a75281SManos Pitsidianakis uint64_t offset; 11418a75281SManos Pitsidianakis /* Used for the TX queue for lazy I/O copy from `elem` */ 11518a75281SManos Pitsidianakis bool populated; 11618a75281SManos Pitsidianakis /* 11718a75281SManos Pitsidianakis * VirtIOSoundPCMBuffer is an unsized type because it ends with an array of 11818a75281SManos Pitsidianakis * bytes. The size of `data` is determined from the I/O message's read-only 11918a75281SManos Pitsidianakis * or write-only size when allocating VirtIOSoundPCMBuffer. 12018a75281SManos Pitsidianakis */ 12118a75281SManos Pitsidianakis uint8_t data[]; 12218a75281SManos Pitsidianakis }; 12318a75281SManos Pitsidianakis 124eb9ad377SManos Pitsidianakis struct VirtIOSoundPCM { 125eb9ad377SManos Pitsidianakis VirtIOSound *snd; 126eb9ad377SManos Pitsidianakis /* 127eb9ad377SManos Pitsidianakis * PCM parameters are a separate field instead of a VirtIOSoundPCMStream 128eb9ad377SManos Pitsidianakis * field, because the operation of PCM control requests is first 129eb9ad377SManos Pitsidianakis * VIRTIO_SND_R_PCM_SET_PARAMS and then VIRTIO_SND_R_PCM_PREPARE; this 130eb9ad377SManos Pitsidianakis * means that some times we get parameters without having an allocated 131eb9ad377SManos Pitsidianakis * stream yet. 132eb9ad377SManos Pitsidianakis */ 133eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params *pcm_params; 134eb9ad377SManos Pitsidianakis VirtIOSoundPCMStream **streams; 135eb9ad377SManos Pitsidianakis }; 136eb9ad377SManos Pitsidianakis 137eb9ad377SManos Pitsidianakis struct VirtIOSoundPCMStream { 138eb9ad377SManos Pitsidianakis VirtIOSoundPCM *pcm; 139eb9ad377SManos Pitsidianakis virtio_snd_pcm_info info; 140eb9ad377SManos Pitsidianakis virtio_snd_pcm_set_params params; 141eb9ad377SManos Pitsidianakis uint32_t id; 142eb9ad377SManos Pitsidianakis /* channel position values (VIRTIO_SND_CHMAP_XXX) */ 143eb9ad377SManos Pitsidianakis uint8_t positions[VIRTIO_SND_CHMAP_MAX_SIZE]; 144eb9ad377SManos Pitsidianakis VirtIOSound *s; 145eb9ad377SManos Pitsidianakis bool flushing; 146eb9ad377SManos Pitsidianakis audsettings as; 147eb9ad377SManos Pitsidianakis union { 148eb9ad377SManos Pitsidianakis SWVoiceIn *in; 149eb9ad377SManos Pitsidianakis SWVoiceOut *out; 150eb9ad377SManos Pitsidianakis } voice; 15118a75281SManos Pitsidianakis QemuMutex queue_mutex; 152eb9ad377SManos Pitsidianakis bool active; 15318a75281SManos Pitsidianakis QSIMPLEQ_HEAD(, VirtIOSoundPCMBuffer) queue; 154eb9ad377SManos Pitsidianakis }; 155eb9ad377SManos Pitsidianakis 156eb9ad377SManos Pitsidianakis /* 157eb9ad377SManos Pitsidianakis * PCM stream state machine. 158eb9ad377SManos Pitsidianakis * ------------------------- 159eb9ad377SManos Pitsidianakis * 160eb9ad377SManos Pitsidianakis * 5.14.6.6.1 PCM Command Lifecycle 161eb9ad377SManos Pitsidianakis * ================================ 162eb9ad377SManos Pitsidianakis * 163eb9ad377SManos Pitsidianakis * A PCM stream has the following command lifecycle: 164eb9ad377SManos Pitsidianakis * - `SET PARAMETERS` 165eb9ad377SManos Pitsidianakis * The driver negotiates the stream parameters (format, transport, etc) with 166eb9ad377SManos Pitsidianakis * the device. 167eb9ad377SManos Pitsidianakis * Possible valid transitions: `SET PARAMETERS`, `PREPARE`. 168eb9ad377SManos Pitsidianakis * - `PREPARE` 169eb9ad377SManos Pitsidianakis * The device prepares the stream (allocates resources, etc). 170eb9ad377SManos Pitsidianakis * Possible valid transitions: `SET PARAMETERS`, `PREPARE`, `START`, 171eb9ad377SManos Pitsidianakis * `RELEASE`. Output only: the driver transfers data for pre-buffing. 172eb9ad377SManos Pitsidianakis * - `START` 173eb9ad377SManos Pitsidianakis * The device starts the stream (unmute, putting into running state, etc). 174eb9ad377SManos Pitsidianakis * Possible valid transitions: `STOP`. 175eb9ad377SManos Pitsidianakis * The driver transfers data to/from the stream. 176eb9ad377SManos Pitsidianakis * - `STOP` 177eb9ad377SManos Pitsidianakis * The device stops the stream (mute, putting into non-running state, etc). 178eb9ad377SManos Pitsidianakis * Possible valid transitions: `START`, `RELEASE`. 179eb9ad377SManos Pitsidianakis * - `RELEASE` 180eb9ad377SManos Pitsidianakis * The device releases the stream (frees resources, etc). 181eb9ad377SManos Pitsidianakis * Possible valid transitions: `SET PARAMETERS`, `PREPARE`. 182eb9ad377SManos Pitsidianakis * 183eb9ad377SManos Pitsidianakis * +---------------+ +---------+ +---------+ +-------+ +-------+ 184eb9ad377SManos Pitsidianakis * | SetParameters | | Prepare | | Release | | Start | | Stop | 185eb9ad377SManos Pitsidianakis * +---------------+ +---------+ +---------+ +-------+ +-------+ 186eb9ad377SManos Pitsidianakis * |- | | | | 187eb9ad377SManos Pitsidianakis * || | | | | 188eb9ad377SManos Pitsidianakis * |< | | | | 189eb9ad377SManos Pitsidianakis * |------------->| | | | 190eb9ad377SManos Pitsidianakis * |<-------------| | | | 191eb9ad377SManos Pitsidianakis * | |- | | | 192eb9ad377SManos Pitsidianakis * | || | | | 193eb9ad377SManos Pitsidianakis * | |< | | | 194eb9ad377SManos Pitsidianakis * | |--------------------->| | 195eb9ad377SManos Pitsidianakis * | |---------->| | | 196eb9ad377SManos Pitsidianakis * | | | |-------->| 197eb9ad377SManos Pitsidianakis * | | | |<--------| 198eb9ad377SManos Pitsidianakis * | | |<-------------------| 199eb9ad377SManos Pitsidianakis * |<-------------------------| | | 200eb9ad377SManos Pitsidianakis * | |<----------| | | 201eb9ad377SManos Pitsidianakis * 202eb9ad377SManos Pitsidianakis * CTRL in the VirtIOSound device 203eb9ad377SManos Pitsidianakis * ============================== 204eb9ad377SManos Pitsidianakis * 205eb9ad377SManos Pitsidianakis * The control messages that affect the state of a stream arrive in the 206eb9ad377SManos Pitsidianakis * `virtio_snd_handle_ctrl()` queue callback and are of type `struct 207eb9ad377SManos Pitsidianakis * virtio_snd_ctrl_command`. They are stored in a queue field in the device 208eb9ad377SManos Pitsidianakis * type, `VirtIOSound`. This allows deferring the CTRL request completion if 209eb9ad377SManos Pitsidianakis * it's not immediately possible due to locking/state reasons. 210eb9ad377SManos Pitsidianakis * 211eb9ad377SManos Pitsidianakis * The CTRL message is finally handled in `process_cmd()`. 212eb9ad377SManos Pitsidianakis */ 213eb9ad377SManos Pitsidianakis struct VirtIOSound { 2142880e676SManos Pitsidianakis VirtIODevice parent_obj; 2152880e676SManos Pitsidianakis 2162880e676SManos Pitsidianakis VirtQueue *queues[VIRTIO_SND_VQ_MAX]; 2172880e676SManos Pitsidianakis uint64_t features; 218eb9ad377SManos Pitsidianakis VirtIOSoundPCM *pcm; 2192880e676SManos Pitsidianakis QEMUSoundCard card; 2202880e676SManos Pitsidianakis VMChangeStateEntry *vmstate; 2212880e676SManos Pitsidianakis virtio_snd_config snd_conf; 222eb9ad377SManos Pitsidianakis QemuMutex cmdq_mutex; 223eb9ad377SManos Pitsidianakis QTAILQ_HEAD(, virtio_snd_ctrl_command) cmdq; 224eb9ad377SManos Pitsidianakis bool processing_cmdq; 225*731655f8SManos Pitsidianakis /* 226*731655f8SManos Pitsidianakis * Convenience queue to keep track of invalid tx/rx queue messages inside 227*731655f8SManos Pitsidianakis * the tx/rx callbacks. 228*731655f8SManos Pitsidianakis * 229*731655f8SManos Pitsidianakis * In the callbacks as a first step we are emptying the virtqueue to handle 230*731655f8SManos Pitsidianakis * each message and we cannot add an invalid message back to the queue: we 231*731655f8SManos Pitsidianakis * would re-process it in subsequent loop iterations. 232*731655f8SManos Pitsidianakis * 233*731655f8SManos Pitsidianakis * Instead, we add them to this queue and after finishing examining every 234*731655f8SManos Pitsidianakis * virtqueue element, we inform the guest for each invalid message. 235*731655f8SManos Pitsidianakis * 236*731655f8SManos Pitsidianakis * This queue must be empty at all times except for inside the tx/rx 237*731655f8SManos Pitsidianakis * callbacks. 238*731655f8SManos Pitsidianakis */ 239*731655f8SManos Pitsidianakis QSIMPLEQ_HEAD(, VirtIOSoundPCMBuffer) invalid; 240eb9ad377SManos Pitsidianakis }; 241eb9ad377SManos Pitsidianakis 242eb9ad377SManos Pitsidianakis struct virtio_snd_ctrl_command { 243eb9ad377SManos Pitsidianakis VirtQueueElement *elem; 244eb9ad377SManos Pitsidianakis VirtQueue *vq; 245eb9ad377SManos Pitsidianakis virtio_snd_hdr ctrl; 246eb9ad377SManos Pitsidianakis virtio_snd_hdr resp; 247633487dfSVolker Rümelin size_t payload_size; 248eb9ad377SManos Pitsidianakis QTAILQ_ENTRY(virtio_snd_ctrl_command) next; 249eb9ad377SManos Pitsidianakis }; 2502880e676SManos Pitsidianakis #endif 251