File Index
Files and directories
Sign in
FILE

virtio_kctl.c

File preview with syntax highlighting.

linux-6.19.14/sound/virtio/virtio_kctl.c C
  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. */
 12static 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. */
 22static 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. */
 33static 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 */
 47static int virtsnd_kctl_info(struct snd_kcontrol *kcontrol,
 48			     struct snd_ctl_elem_info *uinfo)
 49{
 50	struct virtio_snd *snd = snd_kcontrol_chip(kcontrol);
 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 */
102static int virtsnd_kctl_get(struct snd_kcontrol *kcontrol,
103			    struct snd_ctl_elem_value *uvalue)
104{
105	struct virtio_snd *snd = snd_kcontrol_chip(kcontrol);
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
161on_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 */
175static int virtsnd_kctl_put(struct snd_kcontrol *kcontrol,
176			    struct snd_ctl_elem_value *uvalue)
177{
178	struct virtio_snd *snd = snd_kcontrol_chip(kcontrol);
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 */
239static 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 = snd_kcontrol_chip(kcontrol);
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(sizeof(*hdr), 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, &sg, false);
270		if (!rc) {
271			if (copy_to_user(utlv, tlv, 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(tlv, utlv, size)) {
285			rc = -EFAULT;
286			goto on_msg_unref;
287		} else {
288			rc = virtsnd_ctl_msg_send(snd, msg, &sg, NULL, 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(tlv);
299	return rc;
300
301on_msg_unref:
302	virtsnd_ctl_msg_unref(msg);
303	kfree(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 */
318static 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(sizeof(*hdr),
329				    sizeof(struct virtio_snd_hdr), GFP_KERNEL);
330	if (!msg)
331		return -ENOMEM;
332
333	kctl->items = devm_kcalloc(&vdev->dev, n, 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, &sg, 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 */
358int 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(&vdev->dev, snd->nkctls,
370				       sizeof(*snd->kctl_infos), GFP_KERNEL);
371	if (!snd->kctl_infos)
372		return -ENOMEM;
373
374	snd->kctls = devm_kcalloc(&vdev->dev, snd->nkctls, sizeof(*snd->kctls),
375				  GFP_KERNEL);
376	if (!snd->kctls)
377		return -ENOMEM;
378
379	rc = virtsnd_ctl_query_info(snd, VIRTIO_SND_R_CTL_INFO, 0, snd->nkctls,
380				    sizeof(*snd->kctl_infos), 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, 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 */
405int 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(&kctl_new, snd);
439		if (!kctl->kctl)
440			return -ENOMEM;
441
442		rc = snd_ctl_add(snd->card, 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 */
457void 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(snd->card, mask, &kctl->kctl->id);
477}
~/ Read-only file preview.