478 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * virtio-snd: Virtio sound device4 * Copyright (C) 2022 OpenSynergy GmbH5 */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_IEC95819};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_COMMAND30};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_TLV37};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 = kcontrol->private_data;51 struct virtio_kctl *kctl = &snd->kctls[kcontrol->private_value];52 struct virtio_snd_ctl_info *kinfo =53 &snd->kctl_infos[kcontrol->private_value];54 unsigned int i;55 56 uinfo->type = g_v2a_type_map[le32_to_cpu(kinfo->type)];57 uinfo->count = le32_to_cpu(kinfo->count);58 59 switch (uinfo->type) {60 case SNDRV_CTL_ELEM_TYPE_INTEGER:61 uinfo->value.integer.min =62 le32_to_cpu(kinfo->value.integer.min);63 uinfo->value.integer.max =64 le32_to_cpu(kinfo->value.integer.max);65 uinfo->value.integer.step =66 le32_to_cpu(kinfo->value.integer.step);67 68 break;69 case SNDRV_CTL_ELEM_TYPE_INTEGER64:70 uinfo->value.integer64.min =71 le64_to_cpu(kinfo->value.integer64.min);72 uinfo->value.integer64.max =73 le64_to_cpu(kinfo->value.integer64.max);74 uinfo->value.integer64.step =75 le64_to_cpu(kinfo->value.integer64.step);76 77 break;78 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:79 uinfo->value.enumerated.items =80 le32_to_cpu(kinfo->value.enumerated.items);81 i = uinfo->value.enumerated.item;82 if (i >= uinfo->value.enumerated.items)83 return -EINVAL;84 85 strscpy(uinfo->value.enumerated.name, kctl->items[i].item,86 sizeof(uinfo->value.enumerated.name));87 88 break;89 }90 91 return 0;92}93 94/**95 * virtsnd_kctl_get() - Read the value from the control.96 * @kcontrol: ALSA control element.97 * @uvalue: Element value.98 *99 * Context: Process context.100 * Return: 0 on success, -errno on failure.101 */102static int virtsnd_kctl_get(struct snd_kcontrol *kcontrol,103 struct snd_ctl_elem_value *uvalue)104{105 struct virtio_snd *snd = kcontrol->private_data;106 struct virtio_snd_ctl_info *kinfo =107 &snd->kctl_infos[kcontrol->private_value];108 unsigned int type = le32_to_cpu(kinfo->type);109 unsigned int count = le32_to_cpu(kinfo->count);110 struct virtio_snd_msg *msg;111 struct virtio_snd_ctl_hdr *hdr;112 struct virtio_snd_ctl_value *kvalue;113 size_t request_size = sizeof(*hdr);114 size_t response_size = sizeof(struct virtio_snd_hdr) + sizeof(*kvalue);115 unsigned int i;116 int rc;117 118 msg = virtsnd_ctl_msg_alloc(request_size, response_size, GFP_KERNEL);119 if (!msg)120 return -ENOMEM;121 122 virtsnd_ctl_msg_ref(msg);123 124 hdr = virtsnd_ctl_msg_request(msg);125 hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_READ);126 hdr->control_id = cpu_to_le32(kcontrol->private_value);127 128 rc = virtsnd_ctl_msg_send_sync(snd, msg);129 if (rc)130 goto on_failure;131 132 kvalue = (void *)((u8 *)virtsnd_ctl_msg_response(msg) +133 sizeof(struct virtio_snd_hdr));134 135 switch (type) {136 case VIRTIO_SND_CTL_TYPE_BOOLEAN:137 case VIRTIO_SND_CTL_TYPE_INTEGER:138 for (i = 0; i < count; ++i)139 uvalue->value.integer.value[i] =140 le32_to_cpu(kvalue->value.integer[i]);141 break;142 case VIRTIO_SND_CTL_TYPE_INTEGER64:143 for (i = 0; i < count; ++i)144 uvalue->value.integer64.value[i] =145 le64_to_cpu(kvalue->value.integer64[i]);146 break;147 case VIRTIO_SND_CTL_TYPE_ENUMERATED:148 for (i = 0; i < count; ++i)149 uvalue->value.enumerated.item[i] =150 le32_to_cpu(kvalue->value.enumerated[i]);151 break;152 case VIRTIO_SND_CTL_TYPE_BYTES:153 memcpy(uvalue->value.bytes.data, kvalue->value.bytes, count);154 break;155 case VIRTIO_SND_CTL_TYPE_IEC958:156 memcpy(&uvalue->value.iec958, &kvalue->value.iec958,157 sizeof(uvalue->value.iec958));158 break;159 }160 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 = kcontrol->private_data;179 struct virtio_snd_ctl_info *kinfo =180 &snd->kctl_infos[kcontrol->private_value];181 unsigned int type = le32_to_cpu(kinfo->type);182 unsigned int count = le32_to_cpu(kinfo->count);183 struct virtio_snd_msg *msg;184 struct virtio_snd_ctl_hdr *hdr;185 struct virtio_snd_ctl_value *kvalue;186 size_t request_size = sizeof(*hdr) + sizeof(*kvalue);187 size_t response_size = sizeof(struct virtio_snd_hdr);188 unsigned int i;189 190 msg = virtsnd_ctl_msg_alloc(request_size, response_size, GFP_KERNEL);191 if (!msg)192 return -ENOMEM;193 194 hdr = virtsnd_ctl_msg_request(msg);195 hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_WRITE);196 hdr->control_id = cpu_to_le32(kcontrol->private_value);197 198 kvalue = (void *)((u8 *)hdr + sizeof(*hdr));199 200 switch (type) {201 case VIRTIO_SND_CTL_TYPE_BOOLEAN:202 case VIRTIO_SND_CTL_TYPE_INTEGER:203 for (i = 0; i < count; ++i)204 kvalue->value.integer[i] =205 cpu_to_le32(uvalue->value.integer.value[i]);206 break;207 case VIRTIO_SND_CTL_TYPE_INTEGER64:208 for (i = 0; i < count; ++i)209 kvalue->value.integer64[i] =210 cpu_to_le64(uvalue->value.integer64.value[i]);211 break;212 case VIRTIO_SND_CTL_TYPE_ENUMERATED:213 for (i = 0; i < count; ++i)214 kvalue->value.enumerated[i] =215 cpu_to_le32(uvalue->value.enumerated.item[i]);216 break;217 case VIRTIO_SND_CTL_TYPE_BYTES:218 memcpy(kvalue->value.bytes, uvalue->value.bytes.data, count);219 break;220 case VIRTIO_SND_CTL_TYPE_IEC958:221 memcpy(&kvalue->value.iec958, &uvalue->value.iec958,222 sizeof(kvalue->value.iec958));223 break;224 }225 226 return virtsnd_ctl_msg_send_sync(snd, msg);227}228 229/**230 * virtsnd_kctl_tlv_op() - Perform an operation on the control's metadata.231 * @kcontrol: ALSA control element.232 * @op_flag: Operation code (SNDRV_CTL_TLV_OP_XXX).233 * @size: Size of the TLV data in bytes.234 * @utlv: TLV data.235 *236 * Context: Process context.237 * Return: 0 on success, -errno on failure.238 */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 = kcontrol->private_data;243 struct virtio_snd_msg *msg;244 struct virtio_snd_ctl_hdr *hdr;245 unsigned int *tlv;246 struct scatterlist sg;247 int rc;248 249 msg = virtsnd_ctl_msg_alloc(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 else281 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}478