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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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