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