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