brintos

brintos / linux-shallow public Read only

0
0
Text · 13.0 KiB · 7aa79c0 Raw
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