]>
Commit | Line | Data |
---|---|---|
2880e676 MP |
1 | /* |
2 | * VIRTIO Sound Device conforming to | |
3 | * | |
4 | * "Virtual I/O Device (VIRTIO) Version 1.2 | |
5 | * Committee Specification Draft 01 | |
6 | * 09 May 2022" | |
7 | * | |
8 | * <https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-52900014> | |
9 | * | |
10 | * Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org> | |
11 | * Copyright (C) 2019 OpenSynergy GmbH | |
12 | * | |
13 | * This work is licensed under the terms of the GNU GPL, version 2 or | |
14 | * (at your option) any later version. See the COPYING file in the | |
15 | * top-level directory. | |
16 | */ | |
17 | ||
18 | #include "qemu/osdep.h" | |
19 | #include "qemu/iov.h" | |
20 | #include "qemu/log.h" | |
21 | #include "qemu/error-report.h" | |
22 | #include "include/qemu/lockable.h" | |
23 | #include "sysemu/runstate.h" | |
24 | #include "trace.h" | |
25 | #include "qapi/error.h" | |
26 | #include "hw/audio/virtio-snd.h" | |
27 | #include "hw/core/cpu.h" | |
28 | ||
29 | #define VIRTIO_SOUND_VM_VERSION 1 | |
30 | #define VIRTIO_SOUND_JACK_DEFAULT 0 | |
31 | #define VIRTIO_SOUND_STREAM_DEFAULT 1 | |
32 | #define VIRTIO_SOUND_CHMAP_DEFAULT 0 | |
33 | #define VIRTIO_SOUND_HDA_FN_NID 0 | |
34 | ||
35 | static const VMStateDescription vmstate_virtio_snd_device = { | |
36 | .name = TYPE_VIRTIO_SND, | |
37 | .version_id = VIRTIO_SOUND_VM_VERSION, | |
38 | .minimum_version_id = VIRTIO_SOUND_VM_VERSION, | |
39 | }; | |
40 | ||
41 | static const VMStateDescription vmstate_virtio_snd = { | |
42 | .name = TYPE_VIRTIO_SND, | |
43 | .minimum_version_id = VIRTIO_SOUND_VM_VERSION, | |
44 | .version_id = VIRTIO_SOUND_VM_VERSION, | |
45 | .fields = (VMStateField[]) { | |
46 | VMSTATE_VIRTIO_DEVICE, | |
47 | VMSTATE_END_OF_LIST() | |
48 | }, | |
49 | }; | |
50 | ||
51 | static Property virtio_snd_properties[] = { | |
52 | DEFINE_AUDIO_PROPERTIES(VirtIOSound, card), | |
53 | DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks, | |
54 | VIRTIO_SOUND_JACK_DEFAULT), | |
55 | DEFINE_PROP_UINT32("streams", VirtIOSound, snd_conf.streams, | |
56 | VIRTIO_SOUND_STREAM_DEFAULT), | |
57 | DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps, | |
58 | VIRTIO_SOUND_CHMAP_DEFAULT), | |
59 | DEFINE_PROP_END_OF_LIST(), | |
60 | }; | |
61 | ||
62 | static void | |
63 | virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config) | |
64 | { | |
65 | VirtIOSound *s = VIRTIO_SND(vdev); | |
66 | virtio_snd_config *sndconfig = | |
67 | (virtio_snd_config *)config; | |
68 | trace_virtio_snd_get_config(vdev, | |
69 | s->snd_conf.jacks, | |
70 | s->snd_conf.streams, | |
71 | s->snd_conf.chmaps); | |
72 | ||
73 | memcpy(sndconfig, &s->snd_conf, sizeof(s->snd_conf)); | |
74 | cpu_to_le32s(&sndconfig->jacks); | |
75 | cpu_to_le32s(&sndconfig->streams); | |
76 | cpu_to_le32s(&sndconfig->chmaps); | |
77 | ||
78 | } | |
79 | ||
80 | static void | |
81 | virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config) | |
82 | { | |
83 | VirtIOSound *s = VIRTIO_SND(vdev); | |
84 | const virtio_snd_config *sndconfig = | |
85 | (const virtio_snd_config *)config; | |
86 | ||
87 | ||
88 | trace_virtio_snd_set_config(vdev, | |
89 | s->snd_conf.jacks, | |
90 | sndconfig->jacks, | |
91 | s->snd_conf.streams, | |
92 | sndconfig->streams, | |
93 | s->snd_conf.chmaps, | |
94 | sndconfig->chmaps); | |
95 | ||
96 | memcpy(&s->snd_conf, sndconfig, sizeof(virtio_snd_config)); | |
97 | le32_to_cpus(&s->snd_conf.jacks); | |
98 | le32_to_cpus(&s->snd_conf.streams); | |
99 | le32_to_cpus(&s->snd_conf.chmaps); | |
100 | ||
101 | } | |
102 | ||
103 | /* | |
104 | * Queue handler stub. | |
105 | * | |
106 | * @vdev: VirtIOSound device | |
107 | * @vq: virtqueue | |
108 | */ | |
109 | static void virtio_snd_handle_queue(VirtIODevice *vdev, VirtQueue *vq) {} | |
110 | ||
111 | static uint64_t get_features(VirtIODevice *vdev, uint64_t features, | |
112 | Error **errp) | |
113 | { | |
114 | /* | |
115 | * virtio-v1.2-csd01, 5.14.3, | |
116 | * Feature Bits | |
117 | * None currently defined. | |
118 | */ | |
119 | VirtIOSound *s = VIRTIO_SND(vdev); | |
120 | features |= s->features; | |
121 | ||
122 | trace_virtio_snd_get_features(vdev, features); | |
123 | ||
124 | return features; | |
125 | } | |
126 | ||
127 | static void | |
128 | virtio_snd_vm_state_change(void *opaque, bool running, | |
129 | RunState state) | |
130 | { | |
131 | if (running) { | |
132 | trace_virtio_snd_vm_state_running(); | |
133 | } else { | |
134 | trace_virtio_snd_vm_state_stopped(); | |
135 | } | |
136 | } | |
137 | ||
138 | static void virtio_snd_realize(DeviceState *dev, Error **errp) | |
139 | { | |
140 | ERRP_GUARD(); | |
141 | VirtIOSound *vsnd = VIRTIO_SND(dev); | |
142 | VirtIODevice *vdev = VIRTIO_DEVICE(dev); | |
143 | ||
144 | vsnd->vmstate = | |
145 | qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd); | |
146 | ||
147 | trace_virtio_snd_realize(vsnd); | |
148 | ||
149 | virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config)); | |
150 | virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1); | |
151 | ||
152 | /* set number of jacks and streams */ | |
153 | if (vsnd->snd_conf.jacks > 8) { | |
154 | error_setg(errp, | |
155 | "Invalid number of jacks: %"PRIu32, | |
156 | vsnd->snd_conf.jacks); | |
157 | return; | |
158 | } | |
159 | if (vsnd->snd_conf.streams < 1 || vsnd->snd_conf.streams > 10) { | |
160 | error_setg(errp, | |
161 | "Invalid number of streams: %"PRIu32, | |
162 | vsnd->snd_conf.streams); | |
163 | return; | |
164 | } | |
165 | ||
166 | if (vsnd->snd_conf.chmaps > VIRTIO_SND_CHMAP_MAX_SIZE) { | |
167 | error_setg(errp, | |
168 | "Invalid number of channel maps: %"PRIu32, | |
169 | vsnd->snd_conf.chmaps); | |
170 | return; | |
171 | } | |
172 | ||
173 | AUD_register_card("virtio-sound", &vsnd->card, errp); | |
174 | ||
175 | vsnd->queues[VIRTIO_SND_VQ_CONTROL] = | |
176 | virtio_add_queue(vdev, 64, virtio_snd_handle_queue); | |
177 | vsnd->queues[VIRTIO_SND_VQ_EVENT] = | |
178 | virtio_add_queue(vdev, 64, virtio_snd_handle_queue); | |
179 | vsnd->queues[VIRTIO_SND_VQ_TX] = | |
180 | virtio_add_queue(vdev, 64, virtio_snd_handle_queue); | |
181 | vsnd->queues[VIRTIO_SND_VQ_RX] = | |
182 | virtio_add_queue(vdev, 64, virtio_snd_handle_queue); | |
183 | } | |
184 | ||
185 | static void virtio_snd_unrealize(DeviceState *dev) | |
186 | { | |
187 | VirtIODevice *vdev = VIRTIO_DEVICE(dev); | |
188 | VirtIOSound *vsnd = VIRTIO_SND(dev); | |
189 | ||
190 | qemu_del_vm_change_state_handler(vsnd->vmstate); | |
191 | trace_virtio_snd_unrealize(vsnd); | |
192 | ||
193 | AUD_remove_card(&vsnd->card); | |
194 | virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]); | |
195 | virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]); | |
196 | virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]); | |
197 | virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_RX]); | |
198 | virtio_cleanup(vdev); | |
199 | } | |
200 | ||
201 | ||
202 | static void virtio_snd_reset(VirtIODevice *vdev) {} | |
203 | ||
204 | static void virtio_snd_class_init(ObjectClass *klass, void *data) | |
205 | { | |
206 | DeviceClass *dc = DEVICE_CLASS(klass); | |
207 | VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); | |
208 | ||
209 | ||
210 | set_bit(DEVICE_CATEGORY_SOUND, dc->categories); | |
211 | device_class_set_props(dc, virtio_snd_properties); | |
212 | ||
213 | dc->vmsd = &vmstate_virtio_snd; | |
214 | vdc->vmsd = &vmstate_virtio_snd_device; | |
215 | vdc->realize = virtio_snd_realize; | |
216 | vdc->unrealize = virtio_snd_unrealize; | |
217 | vdc->get_config = virtio_snd_get_config; | |
218 | vdc->set_config = virtio_snd_set_config; | |
219 | vdc->get_features = get_features; | |
220 | vdc->reset = virtio_snd_reset; | |
221 | vdc->legacy_features = 0; | |
222 | } | |
223 | ||
224 | static const TypeInfo virtio_snd_types[] = { | |
225 | { | |
226 | .name = TYPE_VIRTIO_SND, | |
227 | .parent = TYPE_VIRTIO_DEVICE, | |
228 | .instance_size = sizeof(VirtIOSound), | |
229 | .class_init = virtio_snd_class_init, | |
230 | } | |
231 | }; | |
232 | ||
233 | DEFINE_TYPES(virtio_snd_types) |