1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * virtio-snd: Virtio sound device |
4 | * Copyright (C) 2022 OpenSynergy GmbH |
5 | */ |
6 | #include <sound/control.h> |
7 | #include <linux/virtio_config.h> |
8 | |
9 | #include "virtio_card.h" |
10 | |
11 | /* Map for converting VirtIO types to ALSA types. */ |
12 | static const snd_ctl_elem_type_t g_v2a_type_map[] = { |
13 | [VIRTIO_SND_CTL_TYPE_BOOLEAN] = SNDRV_CTL_ELEM_TYPE_BOOLEAN, |
14 | [VIRTIO_SND_CTL_TYPE_INTEGER] = SNDRV_CTL_ELEM_TYPE_INTEGER, |
15 | [VIRTIO_SND_CTL_TYPE_INTEGER64] = SNDRV_CTL_ELEM_TYPE_INTEGER64, |
16 | [VIRTIO_SND_CTL_TYPE_ENUMERATED] = SNDRV_CTL_ELEM_TYPE_ENUMERATED, |
17 | [VIRTIO_SND_CTL_TYPE_BYTES] = SNDRV_CTL_ELEM_TYPE_BYTES, |
18 | [VIRTIO_SND_CTL_TYPE_IEC958] = SNDRV_CTL_ELEM_TYPE_IEC958 |
19 | }; |
20 | |
21 | /* Map for converting VirtIO access rights to ALSA access rights. */ |
22 | static const unsigned int g_v2a_access_map[] = { |
23 | [VIRTIO_SND_CTL_ACCESS_READ] = SNDRV_CTL_ELEM_ACCESS_READ, |
24 | [VIRTIO_SND_CTL_ACCESS_WRITE] = SNDRV_CTL_ELEM_ACCESS_WRITE, |
25 | [VIRTIO_SND_CTL_ACCESS_VOLATILE] = SNDRV_CTL_ELEM_ACCESS_VOLATILE, |
26 | [VIRTIO_SND_CTL_ACCESS_INACTIVE] = SNDRV_CTL_ELEM_ACCESS_INACTIVE, |
27 | [VIRTIO_SND_CTL_ACCESS_TLV_READ] = SNDRV_CTL_ELEM_ACCESS_TLV_READ, |
28 | [VIRTIO_SND_CTL_ACCESS_TLV_WRITE] = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE, |
29 | [VIRTIO_SND_CTL_ACCESS_TLV_COMMAND] = SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND |
30 | }; |
31 | |
32 | /* Map for converting VirtIO event masks to ALSA event masks. */ |
33 | static const unsigned int g_v2a_mask_map[] = { |
34 | [VIRTIO_SND_CTL_EVT_MASK_VALUE] = SNDRV_CTL_EVENT_MASK_VALUE, |
35 | [VIRTIO_SND_CTL_EVT_MASK_INFO] = SNDRV_CTL_EVENT_MASK_INFO, |
36 | [VIRTIO_SND_CTL_EVT_MASK_TLV] = SNDRV_CTL_EVENT_MASK_TLV |
37 | }; |
38 | |
39 | /** |
40 | * virtsnd_kctl_info() - Returns information about the control. |
41 | * @kcontrol: ALSA control element. |
42 | * @uinfo: Element information. |
43 | * |
44 | * Context: Process context. |
45 | * Return: 0 on success, -errno on failure. |
46 | */ |
47 | static int virtsnd_kctl_info(struct snd_kcontrol *kcontrol, |
48 | struct snd_ctl_elem_info *uinfo) |
49 | { |
50 | struct virtio_snd *snd = kcontrol->private_data; |
51 | struct virtio_kctl *kctl = &snd->kctls[kcontrol->private_value]; |
52 | struct virtio_snd_ctl_info *kinfo = |
53 | &snd->kctl_infos[kcontrol->private_value]; |
54 | unsigned int i; |
55 | |
56 | uinfo->type = g_v2a_type_map[le32_to_cpu(kinfo->type)]; |
57 | uinfo->count = le32_to_cpu(kinfo->count); |
58 | |
59 | switch (uinfo->type) { |
60 | case SNDRV_CTL_ELEM_TYPE_INTEGER: |
61 | uinfo->value.integer.min = |
62 | le32_to_cpu(kinfo->value.integer.min); |
63 | uinfo->value.integer.max = |
64 | le32_to_cpu(kinfo->value.integer.max); |
65 | uinfo->value.integer.step = |
66 | le32_to_cpu(kinfo->value.integer.step); |
67 | |
68 | break; |
69 | case SNDRV_CTL_ELEM_TYPE_INTEGER64: |
70 | uinfo->value.integer64.min = |
71 | le64_to_cpu(kinfo->value.integer64.min); |
72 | uinfo->value.integer64.max = |
73 | le64_to_cpu(kinfo->value.integer64.max); |
74 | uinfo->value.integer64.step = |
75 | le64_to_cpu(kinfo->value.integer64.step); |
76 | |
77 | break; |
78 | case SNDRV_CTL_ELEM_TYPE_ENUMERATED: |
79 | uinfo->value.enumerated.items = |
80 | le32_to_cpu(kinfo->value.enumerated.items); |
81 | i = uinfo->value.enumerated.item; |
82 | if (i >= uinfo->value.enumerated.items) |
83 | return -EINVAL; |
84 | |
85 | strscpy(uinfo->value.enumerated.name, kctl->items[i].item, |
86 | sizeof(uinfo->value.enumerated.name)); |
87 | |
88 | break; |
89 | } |
90 | |
91 | return 0; |
92 | } |
93 | |
94 | /** |
95 | * virtsnd_kctl_get() - Read the value from the control. |
96 | * @kcontrol: ALSA control element. |
97 | * @uvalue: Element value. |
98 | * |
99 | * Context: Process context. |
100 | * Return: 0 on success, -errno on failure. |
101 | */ |
102 | static int virtsnd_kctl_get(struct snd_kcontrol *kcontrol, |
103 | struct snd_ctl_elem_value *uvalue) |
104 | { |
105 | struct virtio_snd *snd = kcontrol->private_data; |
106 | struct virtio_snd_ctl_info *kinfo = |
107 | &snd->kctl_infos[kcontrol->private_value]; |
108 | unsigned int type = le32_to_cpu(kinfo->type); |
109 | unsigned int count = le32_to_cpu(kinfo->count); |
110 | struct virtio_snd_msg *msg; |
111 | struct virtio_snd_ctl_hdr *hdr; |
112 | struct virtio_snd_ctl_value *kvalue; |
113 | size_t request_size = sizeof(*hdr); |
114 | size_t response_size = sizeof(struct virtio_snd_hdr) + sizeof(*kvalue); |
115 | unsigned int i; |
116 | int rc; |
117 | |
118 | msg = virtsnd_ctl_msg_alloc(request_size, response_size, GFP_KERNEL); |
119 | if (!msg) |
120 | return -ENOMEM; |
121 | |
122 | virtsnd_ctl_msg_ref(msg); |
123 | |
124 | hdr = virtsnd_ctl_msg_request(msg); |
125 | hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_READ); |
126 | hdr->control_id = cpu_to_le32(kcontrol->private_value); |
127 | |
128 | rc = virtsnd_ctl_msg_send_sync(snd, msg); |
129 | if (rc) |
130 | goto on_failure; |
131 | |
132 | kvalue = (void *)((u8 *)virtsnd_ctl_msg_response(msg) + |
133 | sizeof(struct virtio_snd_hdr)); |
134 | |
135 | switch (type) { |
136 | case VIRTIO_SND_CTL_TYPE_BOOLEAN: |
137 | case VIRTIO_SND_CTL_TYPE_INTEGER: |
138 | for (i = 0; i < count; ++i) |
139 | uvalue->value.integer.value[i] = |
140 | le32_to_cpu(kvalue->value.integer[i]); |
141 | break; |
142 | case VIRTIO_SND_CTL_TYPE_INTEGER64: |
143 | for (i = 0; i < count; ++i) |
144 | uvalue->value.integer64.value[i] = |
145 | le64_to_cpu(kvalue->value.integer64[i]); |
146 | break; |
147 | case VIRTIO_SND_CTL_TYPE_ENUMERATED: |
148 | for (i = 0; i < count; ++i) |
149 | uvalue->value.enumerated.item[i] = |
150 | le32_to_cpu(kvalue->value.enumerated[i]); |
151 | break; |
152 | case VIRTIO_SND_CTL_TYPE_BYTES: |
153 | memcpy(uvalue->value.bytes.data, kvalue->value.bytes, count); |
154 | break; |
155 | case VIRTIO_SND_CTL_TYPE_IEC958: |
156 | memcpy(&uvalue->value.iec958, &kvalue->value.iec958, |
157 | sizeof(uvalue->value.iec958)); |
158 | break; |
159 | } |
160 | |
161 | on_failure: |
162 | virtsnd_ctl_msg_unref(msg); |
163 | |
164 | return rc; |
165 | } |
166 | |
167 | /** |
168 | * virtsnd_kctl_put() - Write the value to the control. |
169 | * @kcontrol: ALSA control element. |
170 | * @uvalue: Element value. |
171 | * |
172 | * Context: Process context. |
173 | * Return: 0 on success, -errno on failure. |
174 | */ |
175 | static int virtsnd_kctl_put(struct snd_kcontrol *kcontrol, |
176 | struct snd_ctl_elem_value *uvalue) |
177 | { |
178 | struct virtio_snd *snd = kcontrol->private_data; |
179 | struct virtio_snd_ctl_info *kinfo = |
180 | &snd->kctl_infos[kcontrol->private_value]; |
181 | unsigned int type = le32_to_cpu(kinfo->type); |
182 | unsigned int count = le32_to_cpu(kinfo->count); |
183 | struct virtio_snd_msg *msg; |
184 | struct virtio_snd_ctl_hdr *hdr; |
185 | struct virtio_snd_ctl_value *kvalue; |
186 | size_t request_size = sizeof(*hdr) + sizeof(*kvalue); |
187 | size_t response_size = sizeof(struct virtio_snd_hdr); |
188 | unsigned int i; |
189 | |
190 | msg = virtsnd_ctl_msg_alloc(request_size, response_size, GFP_KERNEL); |
191 | if (!msg) |
192 | return -ENOMEM; |
193 | |
194 | hdr = virtsnd_ctl_msg_request(msg); |
195 | hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_WRITE); |
196 | hdr->control_id = cpu_to_le32(kcontrol->private_value); |
197 | |
198 | kvalue = (void *)((u8 *)hdr + sizeof(*hdr)); |
199 | |
200 | switch (type) { |
201 | case VIRTIO_SND_CTL_TYPE_BOOLEAN: |
202 | case VIRTIO_SND_CTL_TYPE_INTEGER: |
203 | for (i = 0; i < count; ++i) |
204 | kvalue->value.integer[i] = |
205 | cpu_to_le32(uvalue->value.integer.value[i]); |
206 | break; |
207 | case VIRTIO_SND_CTL_TYPE_INTEGER64: |
208 | for (i = 0; i < count; ++i) |
209 | kvalue->value.integer64[i] = |
210 | cpu_to_le64(uvalue->value.integer64.value[i]); |
211 | break; |
212 | case VIRTIO_SND_CTL_TYPE_ENUMERATED: |
213 | for (i = 0; i < count; ++i) |
214 | kvalue->value.enumerated[i] = |
215 | cpu_to_le32(uvalue->value.enumerated.item[i]); |
216 | break; |
217 | case VIRTIO_SND_CTL_TYPE_BYTES: |
218 | memcpy(kvalue->value.bytes, uvalue->value.bytes.data, count); |
219 | break; |
220 | case VIRTIO_SND_CTL_TYPE_IEC958: |
221 | memcpy(&kvalue->value.iec958, &uvalue->value.iec958, |
222 | sizeof(kvalue->value.iec958)); |
223 | break; |
224 | } |
225 | |
226 | return virtsnd_ctl_msg_send_sync(snd, msg); |
227 | } |
228 | |
229 | /** |
230 | * virtsnd_kctl_tlv_op() - Perform an operation on the control's metadata. |
231 | * @kcontrol: ALSA control element. |
232 | * @op_flag: Operation code (SNDRV_CTL_TLV_OP_XXX). |
233 | * @size: Size of the TLV data in bytes. |
234 | * @utlv: TLV data. |
235 | * |
236 | * Context: Process context. |
237 | * Return: 0 on success, -errno on failure. |
238 | */ |
239 | static int virtsnd_kctl_tlv_op(struct snd_kcontrol *kcontrol, int op_flag, |
240 | unsigned int size, unsigned int __user *utlv) |
241 | { |
242 | struct virtio_snd *snd = kcontrol->private_data; |
243 | struct virtio_snd_msg *msg; |
244 | struct virtio_snd_ctl_hdr *hdr; |
245 | unsigned int *tlv; |
246 | struct scatterlist sg; |
247 | int rc; |
248 | |
249 | msg = virtsnd_ctl_msg_alloc(request_size: sizeof(*hdr), response_size: sizeof(struct virtio_snd_hdr), |
250 | GFP_KERNEL); |
251 | if (!msg) |
252 | return -ENOMEM; |
253 | |
254 | tlv = kzalloc(size, GFP_KERNEL); |
255 | if (!tlv) { |
256 | rc = -ENOMEM; |
257 | goto on_msg_unref; |
258 | } |
259 | |
260 | sg_init_one(&sg, tlv, size); |
261 | |
262 | hdr = virtsnd_ctl_msg_request(msg); |
263 | hdr->control_id = cpu_to_le32(kcontrol->private_value); |
264 | |
265 | switch (op_flag) { |
266 | case SNDRV_CTL_TLV_OP_READ: |
267 | hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_TLV_READ); |
268 | |
269 | rc = virtsnd_ctl_msg_send(snd, msg, NULL, in_sgs: &sg, nowait: false); |
270 | if (!rc) { |
271 | if (copy_to_user(to: utlv, from: tlv, n: size)) |
272 | rc = -EFAULT; |
273 | } |
274 | |
275 | break; |
276 | case SNDRV_CTL_TLV_OP_WRITE: |
277 | case SNDRV_CTL_TLV_OP_CMD: |
278 | if (op_flag == SNDRV_CTL_TLV_OP_WRITE) |
279 | hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_TLV_WRITE); |
280 | else |
281 | hdr->hdr.code = |
282 | cpu_to_le32(VIRTIO_SND_R_CTL_TLV_COMMAND); |
283 | |
284 | if (copy_from_user(to: tlv, from: utlv, n: size)) { |
285 | rc = -EFAULT; |
286 | goto on_msg_unref; |
287 | } else { |
288 | rc = virtsnd_ctl_msg_send(snd, msg, out_sgs: &sg, NULL, nowait: false); |
289 | } |
290 | |
291 | break; |
292 | default: |
293 | rc = -EINVAL; |
294 | /* We never get here - we listed all values for op_flag */ |
295 | WARN_ON(1); |
296 | goto on_msg_unref; |
297 | } |
298 | kfree(objp: tlv); |
299 | return rc; |
300 | |
301 | on_msg_unref: |
302 | virtsnd_ctl_msg_unref(msg); |
303 | kfree(objp: tlv); |
304 | |
305 | return rc; |
306 | } |
307 | |
308 | /** |
309 | * virtsnd_kctl_get_enum_items() - Query items for the ENUMERATED element type. |
310 | * @snd: VirtIO sound device. |
311 | * @cid: Control element ID. |
312 | * |
313 | * This function is called during initial device initialization. |
314 | * |
315 | * Context: Any context that permits to sleep. |
316 | * Return: 0 on success, -errno on failure. |
317 | */ |
318 | static int virtsnd_kctl_get_enum_items(struct virtio_snd *snd, unsigned int cid) |
319 | { |
320 | struct virtio_device *vdev = snd->vdev; |
321 | struct virtio_snd_ctl_info *kinfo = &snd->kctl_infos[cid]; |
322 | struct virtio_kctl *kctl = &snd->kctls[cid]; |
323 | struct virtio_snd_msg *msg; |
324 | struct virtio_snd_ctl_hdr *hdr; |
325 | unsigned int n = le32_to_cpu(kinfo->value.enumerated.items); |
326 | struct scatterlist sg; |
327 | |
328 | msg = virtsnd_ctl_msg_alloc(request_size: sizeof(*hdr), |
329 | response_size: sizeof(struct virtio_snd_hdr), GFP_KERNEL); |
330 | if (!msg) |
331 | return -ENOMEM; |
332 | |
333 | kctl->items = devm_kcalloc(dev: &vdev->dev, n, size: sizeof(*kctl->items), |
334 | GFP_KERNEL); |
335 | if (!kctl->items) { |
336 | virtsnd_ctl_msg_unref(msg); |
337 | return -ENOMEM; |
338 | } |
339 | |
340 | sg_init_one(&sg, kctl->items, n * sizeof(*kctl->items)); |
341 | |
342 | hdr = virtsnd_ctl_msg_request(msg); |
343 | hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_ENUM_ITEMS); |
344 | hdr->control_id = cpu_to_le32(cid); |
345 | |
346 | return virtsnd_ctl_msg_send(snd, msg, NULL, in_sgs: &sg, nowait: false); |
347 | } |
348 | |
349 | /** |
350 | * virtsnd_kctl_parse_cfg() - Parse the control element configuration. |
351 | * @snd: VirtIO sound device. |
352 | * |
353 | * This function is called during initial device initialization. |
354 | * |
355 | * Context: Any context that permits to sleep. |
356 | * Return: 0 on success, -errno on failure. |
357 | */ |
358 | int virtsnd_kctl_parse_cfg(struct virtio_snd *snd) |
359 | { |
360 | struct virtio_device *vdev = snd->vdev; |
361 | u32 i; |
362 | int rc; |
363 | |
364 | virtio_cread_le(vdev, struct virtio_snd_config, controls, |
365 | &snd->nkctls); |
366 | if (!snd->nkctls) |
367 | return 0; |
368 | |
369 | snd->kctl_infos = devm_kcalloc(dev: &vdev->dev, n: snd->nkctls, |
370 | size: sizeof(*snd->kctl_infos), GFP_KERNEL); |
371 | if (!snd->kctl_infos) |
372 | return -ENOMEM; |
373 | |
374 | snd->kctls = devm_kcalloc(dev: &vdev->dev, n: snd->nkctls, size: sizeof(*snd->kctls), |
375 | GFP_KERNEL); |
376 | if (!snd->kctls) |
377 | return -ENOMEM; |
378 | |
379 | rc = virtsnd_ctl_query_info(snd, command: VIRTIO_SND_R_CTL_INFO, start_id: 0, count: snd->nkctls, |
380 | size: sizeof(*snd->kctl_infos), info: snd->kctl_infos); |
381 | if (rc) |
382 | return rc; |
383 | |
384 | for (i = 0; i < snd->nkctls; ++i) { |
385 | struct virtio_snd_ctl_info *kinfo = &snd->kctl_infos[i]; |
386 | unsigned int type = le32_to_cpu(kinfo->type); |
387 | |
388 | if (type == VIRTIO_SND_CTL_TYPE_ENUMERATED) { |
389 | rc = virtsnd_kctl_get_enum_items(snd, cid: i); |
390 | if (rc) |
391 | return rc; |
392 | } |
393 | } |
394 | |
395 | return 0; |
396 | } |
397 | |
398 | /** |
399 | * virtsnd_kctl_build_devs() - Build ALSA control elements. |
400 | * @snd: VirtIO sound device. |
401 | * |
402 | * Context: Any context that permits to sleep. |
403 | * Return: 0 on success, -errno on failure. |
404 | */ |
405 | int virtsnd_kctl_build_devs(struct virtio_snd *snd) |
406 | { |
407 | unsigned int cid; |
408 | |
409 | for (cid = 0; cid < snd->nkctls; ++cid) { |
410 | struct virtio_snd_ctl_info *kinfo = &snd->kctl_infos[cid]; |
411 | struct virtio_kctl *kctl = &snd->kctls[cid]; |
412 | struct snd_kcontrol_new kctl_new; |
413 | unsigned int i; |
414 | int rc; |
415 | |
416 | memset(&kctl_new, 0, sizeof(kctl_new)); |
417 | |
418 | kctl_new.iface = SNDRV_CTL_ELEM_IFACE_MIXER; |
419 | kctl_new.name = kinfo->name; |
420 | kctl_new.index = le32_to_cpu(kinfo->index); |
421 | |
422 | for (i = 0; i < ARRAY_SIZE(g_v2a_access_map); ++i) |
423 | if (le32_to_cpu(kinfo->access) & (1 << i)) |
424 | kctl_new.access |= g_v2a_access_map[i]; |
425 | |
426 | if (kctl_new.access & (SNDRV_CTL_ELEM_ACCESS_TLV_READ | |
427 | SNDRV_CTL_ELEM_ACCESS_TLV_WRITE | |
428 | SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND)) { |
429 | kctl_new.access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; |
430 | kctl_new.tlv.c = virtsnd_kctl_tlv_op; |
431 | } |
432 | |
433 | kctl_new.info = virtsnd_kctl_info; |
434 | kctl_new.get = virtsnd_kctl_get; |
435 | kctl_new.put = virtsnd_kctl_put; |
436 | kctl_new.private_value = cid; |
437 | |
438 | kctl->kctl = snd_ctl_new1(kcontrolnew: &kctl_new, private_data: snd); |
439 | if (!kctl->kctl) |
440 | return -ENOMEM; |
441 | |
442 | rc = snd_ctl_add(card: snd->card, kcontrol: kctl->kctl); |
443 | if (rc) |
444 | return rc; |
445 | } |
446 | |
447 | return 0; |
448 | } |
449 | |
450 | /** |
451 | * virtsnd_kctl_event() - Handle the control element event notification. |
452 | * @snd: VirtIO sound device. |
453 | * @event: VirtIO sound event. |
454 | * |
455 | * Context: Interrupt context. |
456 | */ |
457 | void virtsnd_kctl_event(struct virtio_snd *snd, struct virtio_snd_event *event) |
458 | { |
459 | struct virtio_snd_ctl_event *kevent = |
460 | (struct virtio_snd_ctl_event *)event; |
461 | struct virtio_kctl *kctl; |
462 | unsigned int cid = le16_to_cpu(kevent->control_id); |
463 | unsigned int mask = 0; |
464 | unsigned int i; |
465 | |
466 | if (cid >= snd->nkctls) |
467 | return; |
468 | |
469 | for (i = 0; i < ARRAY_SIZE(g_v2a_mask_map); ++i) |
470 | if (le16_to_cpu(kevent->mask) & (1 << i)) |
471 | mask |= g_v2a_mask_map[i]; |
472 | |
473 | |
474 | kctl = &snd->kctls[cid]; |
475 | |
476 | snd_ctl_notify(card: snd->card, mask, id: &kctl->kctl->id); |
477 | } |
478 | |