18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Routines for driver control interface
48c2ecf20Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/threads.h>
88c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
128c2ecf20Sopenharmony_ci#include <linux/time.h>
138c2ecf20Sopenharmony_ci#include <linux/mm.h>
148c2ecf20Sopenharmony_ci#include <linux/math64.h>
158c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
168c2ecf20Sopenharmony_ci#include <sound/core.h>
178c2ecf20Sopenharmony_ci#include <sound/minors.h>
188c2ecf20Sopenharmony_ci#include <sound/info.h>
198c2ecf20Sopenharmony_ci#include <sound/control.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/* max number of user-defined controls */
228c2ecf20Sopenharmony_ci#define MAX_USER_CONTROLS	32
238c2ecf20Sopenharmony_ci#define MAX_CONTROL_COUNT	1028
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistruct snd_kctl_ioctl {
268c2ecf20Sopenharmony_ci	struct list_head list;		/* list of all ioctls */
278c2ecf20Sopenharmony_ci	snd_kctl_ioctl_func_t fioctl;
288c2ecf20Sopenharmony_ci};
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic DECLARE_RWSEM(snd_ioctl_rwsem);
318c2ecf20Sopenharmony_cistatic LIST_HEAD(snd_control_ioctls);
328c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
338c2ecf20Sopenharmony_cistatic LIST_HEAD(snd_control_compat_ioctls);
348c2ecf20Sopenharmony_ci#endif
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int snd_ctl_open(struct inode *inode, struct file *file)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	unsigned long flags;
398c2ecf20Sopenharmony_ci	struct snd_card *card;
408c2ecf20Sopenharmony_ci	struct snd_ctl_file *ctl;
418c2ecf20Sopenharmony_ci	int i, err;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	err = stream_open(inode, file);
448c2ecf20Sopenharmony_ci	if (err < 0)
458c2ecf20Sopenharmony_ci		return err;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	card = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_CONTROL);
488c2ecf20Sopenharmony_ci	if (!card) {
498c2ecf20Sopenharmony_ci		err = -ENODEV;
508c2ecf20Sopenharmony_ci		goto __error1;
518c2ecf20Sopenharmony_ci	}
528c2ecf20Sopenharmony_ci	err = snd_card_file_add(card, file);
538c2ecf20Sopenharmony_ci	if (err < 0) {
548c2ecf20Sopenharmony_ci		err = -ENODEV;
558c2ecf20Sopenharmony_ci		goto __error1;
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci	if (!try_module_get(card->module)) {
588c2ecf20Sopenharmony_ci		err = -EFAULT;
598c2ecf20Sopenharmony_ci		goto __error2;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci	ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
628c2ecf20Sopenharmony_ci	if (ctl == NULL) {
638c2ecf20Sopenharmony_ci		err = -ENOMEM;
648c2ecf20Sopenharmony_ci		goto __error;
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ctl->events);
678c2ecf20Sopenharmony_ci	init_waitqueue_head(&ctl->change_sleep);
688c2ecf20Sopenharmony_ci	spin_lock_init(&ctl->read_lock);
698c2ecf20Sopenharmony_ci	ctl->card = card;
708c2ecf20Sopenharmony_ci	for (i = 0; i < SND_CTL_SUBDEV_ITEMS; i++)
718c2ecf20Sopenharmony_ci		ctl->preferred_subdevice[i] = -1;
728c2ecf20Sopenharmony_ci	ctl->pid = get_pid(task_pid(current));
738c2ecf20Sopenharmony_ci	file->private_data = ctl;
748c2ecf20Sopenharmony_ci	write_lock_irqsave(&card->ctl_files_rwlock, flags);
758c2ecf20Sopenharmony_ci	list_add_tail(&ctl->list, &card->ctl_files);
768c2ecf20Sopenharmony_ci	write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
778c2ecf20Sopenharmony_ci	snd_card_unref(card);
788c2ecf20Sopenharmony_ci	return 0;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci      __error:
818c2ecf20Sopenharmony_ci	module_put(card->module);
828c2ecf20Sopenharmony_ci      __error2:
838c2ecf20Sopenharmony_ci	snd_card_file_remove(card, file);
848c2ecf20Sopenharmony_ci      __error1:
858c2ecf20Sopenharmony_ci	if (card)
868c2ecf20Sopenharmony_ci		snd_card_unref(card);
878c2ecf20Sopenharmony_ci      	return err;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	unsigned long flags;
938c2ecf20Sopenharmony_ci	struct snd_kctl_event *cread;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctl->read_lock, flags);
968c2ecf20Sopenharmony_ci	while (!list_empty(&ctl->events)) {
978c2ecf20Sopenharmony_ci		cread = snd_kctl_event(ctl->events.next);
988c2ecf20Sopenharmony_ci		list_del(&cread->list);
998c2ecf20Sopenharmony_ci		kfree(cread);
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctl->read_lock, flags);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int snd_ctl_release(struct inode *inode, struct file *file)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	unsigned long flags;
1078c2ecf20Sopenharmony_ci	struct snd_card *card;
1088c2ecf20Sopenharmony_ci	struct snd_ctl_file *ctl;
1098c2ecf20Sopenharmony_ci	struct snd_kcontrol *control;
1108c2ecf20Sopenharmony_ci	unsigned int idx;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	ctl = file->private_data;
1138c2ecf20Sopenharmony_ci	file->private_data = NULL;
1148c2ecf20Sopenharmony_ci	card = ctl->card;
1158c2ecf20Sopenharmony_ci	write_lock_irqsave(&card->ctl_files_rwlock, flags);
1168c2ecf20Sopenharmony_ci	list_del(&ctl->list);
1178c2ecf20Sopenharmony_ci	write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
1188c2ecf20Sopenharmony_ci	down_write(&card->controls_rwsem);
1198c2ecf20Sopenharmony_ci	list_for_each_entry(control, &card->controls, list)
1208c2ecf20Sopenharmony_ci		for (idx = 0; idx < control->count; idx++)
1218c2ecf20Sopenharmony_ci			if (control->vd[idx].owner == ctl)
1228c2ecf20Sopenharmony_ci				control->vd[idx].owner = NULL;
1238c2ecf20Sopenharmony_ci	up_write(&card->controls_rwsem);
1248c2ecf20Sopenharmony_ci	snd_fasync_free(ctl->fasync);
1258c2ecf20Sopenharmony_ci	snd_ctl_empty_read_queue(ctl);
1268c2ecf20Sopenharmony_ci	put_pid(ctl->pid);
1278c2ecf20Sopenharmony_ci	kfree(ctl);
1288c2ecf20Sopenharmony_ci	module_put(card->module);
1298c2ecf20Sopenharmony_ci	snd_card_file_remove(card, file);
1308c2ecf20Sopenharmony_ci	return 0;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/**
1348c2ecf20Sopenharmony_ci * snd_ctl_notify - Send notification to user-space for a control change
1358c2ecf20Sopenharmony_ci * @card: the card to send notification
1368c2ecf20Sopenharmony_ci * @mask: the event mask, SNDRV_CTL_EVENT_*
1378c2ecf20Sopenharmony_ci * @id: the ctl element id to send notification
1388c2ecf20Sopenharmony_ci *
1398c2ecf20Sopenharmony_ci * This function adds an event record with the given id and mask, appends
1408c2ecf20Sopenharmony_ci * to the list and wakes up the user-space for notification.  This can be
1418c2ecf20Sopenharmony_ci * called in the atomic context.
1428c2ecf20Sopenharmony_ci */
1438c2ecf20Sopenharmony_civoid snd_ctl_notify(struct snd_card *card, unsigned int mask,
1448c2ecf20Sopenharmony_ci		    struct snd_ctl_elem_id *id)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	unsigned long flags;
1478c2ecf20Sopenharmony_ci	struct snd_ctl_file *ctl;
1488c2ecf20Sopenharmony_ci	struct snd_kctl_event *ev;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!card || !id))
1518c2ecf20Sopenharmony_ci		return;
1528c2ecf20Sopenharmony_ci	if (card->shutdown)
1538c2ecf20Sopenharmony_ci		return;
1548c2ecf20Sopenharmony_ci	read_lock_irqsave(&card->ctl_files_rwlock, flags);
1558c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
1568c2ecf20Sopenharmony_ci	card->mixer_oss_change_count++;
1578c2ecf20Sopenharmony_ci#endif
1588c2ecf20Sopenharmony_ci	list_for_each_entry(ctl, &card->ctl_files, list) {
1598c2ecf20Sopenharmony_ci		if (!ctl->subscribed)
1608c2ecf20Sopenharmony_ci			continue;
1618c2ecf20Sopenharmony_ci		spin_lock(&ctl->read_lock);
1628c2ecf20Sopenharmony_ci		list_for_each_entry(ev, &ctl->events, list) {
1638c2ecf20Sopenharmony_ci			if (ev->id.numid == id->numid) {
1648c2ecf20Sopenharmony_ci				ev->mask |= mask;
1658c2ecf20Sopenharmony_ci				goto _found;
1668c2ecf20Sopenharmony_ci			}
1678c2ecf20Sopenharmony_ci		}
1688c2ecf20Sopenharmony_ci		ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
1698c2ecf20Sopenharmony_ci		if (ev) {
1708c2ecf20Sopenharmony_ci			ev->id = *id;
1718c2ecf20Sopenharmony_ci			ev->mask = mask;
1728c2ecf20Sopenharmony_ci			list_add_tail(&ev->list, &ctl->events);
1738c2ecf20Sopenharmony_ci		} else {
1748c2ecf20Sopenharmony_ci			dev_err(card->dev, "No memory available to allocate event\n");
1758c2ecf20Sopenharmony_ci		}
1768c2ecf20Sopenharmony_ci	_found:
1778c2ecf20Sopenharmony_ci		wake_up(&ctl->change_sleep);
1788c2ecf20Sopenharmony_ci		spin_unlock(&ctl->read_lock);
1798c2ecf20Sopenharmony_ci		snd_kill_fasync(ctl->fasync, SIGIO, POLL_IN);
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci	read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_notify);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci/**
1868c2ecf20Sopenharmony_ci * snd_ctl_new - create a new control instance with some elements
1878c2ecf20Sopenharmony_ci * @kctl: the pointer to store new control instance
1888c2ecf20Sopenharmony_ci * @count: the number of elements in this control
1898c2ecf20Sopenharmony_ci * @access: the default access flags for elements in this control
1908c2ecf20Sopenharmony_ci * @file: given when locking these elements
1918c2ecf20Sopenharmony_ci *
1928c2ecf20Sopenharmony_ci * Allocates a memory object for a new control instance. The instance has
1938c2ecf20Sopenharmony_ci * elements as many as the given number (@count). Each element has given
1948c2ecf20Sopenharmony_ci * access permissions (@access). Each element is locked when @file is given.
1958c2ecf20Sopenharmony_ci *
1968c2ecf20Sopenharmony_ci * Return: 0 on success, error code on failure
1978c2ecf20Sopenharmony_ci */
1988c2ecf20Sopenharmony_cistatic int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count,
1998c2ecf20Sopenharmony_ci		       unsigned int access, struct snd_ctl_file *file)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	unsigned int idx;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (count == 0 || count > MAX_CONTROL_COUNT)
2048c2ecf20Sopenharmony_ci		return -EINVAL;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	*kctl = kzalloc(struct_size(*kctl, vd, count), GFP_KERNEL);
2078c2ecf20Sopenharmony_ci	if (!*kctl)
2088c2ecf20Sopenharmony_ci		return -ENOMEM;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	for (idx = 0; idx < count; idx++) {
2118c2ecf20Sopenharmony_ci		(*kctl)->vd[idx].access = access;
2128c2ecf20Sopenharmony_ci		(*kctl)->vd[idx].owner = file;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci	(*kctl)->count = count;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return 0;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/**
2208c2ecf20Sopenharmony_ci * snd_ctl_new1 - create a control instance from the template
2218c2ecf20Sopenharmony_ci * @ncontrol: the initialization record
2228c2ecf20Sopenharmony_ci * @private_data: the private data to set
2238c2ecf20Sopenharmony_ci *
2248c2ecf20Sopenharmony_ci * Allocates a new struct snd_kcontrol instance and initialize from the given
2258c2ecf20Sopenharmony_ci * template.  When the access field of ncontrol is 0, it's assumed as
2268c2ecf20Sopenharmony_ci * READWRITE access. When the count field is 0, it's assumes as one.
2278c2ecf20Sopenharmony_ci *
2288c2ecf20Sopenharmony_ci * Return: The pointer of the newly generated instance, or %NULL on failure.
2298c2ecf20Sopenharmony_ci */
2308c2ecf20Sopenharmony_cistruct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
2318c2ecf20Sopenharmony_ci				  void *private_data)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
2348c2ecf20Sopenharmony_ci	unsigned int count;
2358c2ecf20Sopenharmony_ci	unsigned int access;
2368c2ecf20Sopenharmony_ci	int err;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!ncontrol || !ncontrol->info))
2398c2ecf20Sopenharmony_ci		return NULL;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	count = ncontrol->count;
2428c2ecf20Sopenharmony_ci	if (count == 0)
2438c2ecf20Sopenharmony_ci		count = 1;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	access = ncontrol->access;
2468c2ecf20Sopenharmony_ci	if (access == 0)
2478c2ecf20Sopenharmony_ci		access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
2488c2ecf20Sopenharmony_ci	access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
2498c2ecf20Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_VOLATILE |
2508c2ecf20Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_INACTIVE |
2518c2ecf20Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE |
2528c2ecf20Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND |
2538c2ecf20Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK |
2548c2ecf20Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	err = snd_ctl_new(&kctl, count, access, NULL);
2578c2ecf20Sopenharmony_ci	if (err < 0)
2588c2ecf20Sopenharmony_ci		return NULL;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	/* The 'numid' member is decided when calling snd_ctl_add(). */
2618c2ecf20Sopenharmony_ci	kctl->id.iface = ncontrol->iface;
2628c2ecf20Sopenharmony_ci	kctl->id.device = ncontrol->device;
2638c2ecf20Sopenharmony_ci	kctl->id.subdevice = ncontrol->subdevice;
2648c2ecf20Sopenharmony_ci	if (ncontrol->name) {
2658c2ecf20Sopenharmony_ci		strlcpy(kctl->id.name, ncontrol->name, sizeof(kctl->id.name));
2668c2ecf20Sopenharmony_ci		if (strcmp(ncontrol->name, kctl->id.name) != 0)
2678c2ecf20Sopenharmony_ci			pr_warn("ALSA: Control name '%s' truncated to '%s'\n",
2688c2ecf20Sopenharmony_ci				ncontrol->name, kctl->id.name);
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci	kctl->id.index = ncontrol->index;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	kctl->info = ncontrol->info;
2738c2ecf20Sopenharmony_ci	kctl->get = ncontrol->get;
2748c2ecf20Sopenharmony_ci	kctl->put = ncontrol->put;
2758c2ecf20Sopenharmony_ci	kctl->tlv.p = ncontrol->tlv.p;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	kctl->private_value = ncontrol->private_value;
2788c2ecf20Sopenharmony_ci	kctl->private_data = private_data;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return kctl;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_new1);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci/**
2858c2ecf20Sopenharmony_ci * snd_ctl_free_one - release the control instance
2868c2ecf20Sopenharmony_ci * @kcontrol: the control instance
2878c2ecf20Sopenharmony_ci *
2888c2ecf20Sopenharmony_ci * Releases the control instance created via snd_ctl_new()
2898c2ecf20Sopenharmony_ci * or snd_ctl_new1().
2908c2ecf20Sopenharmony_ci * Don't call this after the control was added to the card.
2918c2ecf20Sopenharmony_ci */
2928c2ecf20Sopenharmony_civoid snd_ctl_free_one(struct snd_kcontrol *kcontrol)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	if (kcontrol) {
2958c2ecf20Sopenharmony_ci		if (kcontrol->private_free)
2968c2ecf20Sopenharmony_ci			kcontrol->private_free(kcontrol);
2978c2ecf20Sopenharmony_ci		kfree(kcontrol);
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_free_one);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic bool snd_ctl_remove_numid_conflict(struct snd_card *card,
3038c2ecf20Sopenharmony_ci					  unsigned int count)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	/* Make sure that the ids assigned to the control do not wrap around */
3088c2ecf20Sopenharmony_ci	if (card->last_numid >= UINT_MAX - count)
3098c2ecf20Sopenharmony_ci		card->last_numid = 0;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	list_for_each_entry(kctl, &card->controls, list) {
3128c2ecf20Sopenharmony_ci		if (kctl->id.numid < card->last_numid + 1 + count &&
3138c2ecf20Sopenharmony_ci		    kctl->id.numid + kctl->count > card->last_numid + 1) {
3148c2ecf20Sopenharmony_ci		    	card->last_numid = kctl->id.numid + kctl->count - 1;
3158c2ecf20Sopenharmony_ci			return true;
3168c2ecf20Sopenharmony_ci		}
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci	return false;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic int snd_ctl_find_hole(struct snd_card *card, unsigned int count)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	unsigned int iter = 100000;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	while (snd_ctl_remove_numid_conflict(card, count)) {
3268c2ecf20Sopenharmony_ci		if (--iter == 0) {
3278c2ecf20Sopenharmony_ci			/* this situation is very unlikely */
3288c2ecf20Sopenharmony_ci			dev_err(card->dev, "unable to allocate new control numid\n");
3298c2ecf20Sopenharmony_ci			return -ENOMEM;
3308c2ecf20Sopenharmony_ci		}
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci	return 0;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cienum snd_ctl_add_mode {
3368c2ecf20Sopenharmony_ci	CTL_ADD_EXCLUSIVE, CTL_REPLACE, CTL_ADD_ON_REPLACE,
3378c2ecf20Sopenharmony_ci};
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci/* add/replace a new kcontrol object; call with card->controls_rwsem locked */
3408c2ecf20Sopenharmony_cistatic int __snd_ctl_add_replace(struct snd_card *card,
3418c2ecf20Sopenharmony_ci				 struct snd_kcontrol *kcontrol,
3428c2ecf20Sopenharmony_ci				 enum snd_ctl_add_mode mode)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	struct snd_ctl_elem_id id;
3458c2ecf20Sopenharmony_ci	unsigned int idx;
3468c2ecf20Sopenharmony_ci	unsigned int count;
3478c2ecf20Sopenharmony_ci	struct snd_kcontrol *old;
3488c2ecf20Sopenharmony_ci	int err;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	id = kcontrol->id;
3518c2ecf20Sopenharmony_ci	if (id.index > UINT_MAX - kcontrol->count)
3528c2ecf20Sopenharmony_ci		return -EINVAL;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	old = snd_ctl_find_id(card, &id);
3558c2ecf20Sopenharmony_ci	if (!old) {
3568c2ecf20Sopenharmony_ci		if (mode == CTL_REPLACE)
3578c2ecf20Sopenharmony_ci			return -EINVAL;
3588c2ecf20Sopenharmony_ci	} else {
3598c2ecf20Sopenharmony_ci		if (mode == CTL_ADD_EXCLUSIVE) {
3608c2ecf20Sopenharmony_ci			dev_err(card->dev,
3618c2ecf20Sopenharmony_ci				"control %i:%i:%i:%s:%i is already present\n",
3628c2ecf20Sopenharmony_ci				id.iface, id.device, id.subdevice, id.name,
3638c2ecf20Sopenharmony_ci				id.index);
3648c2ecf20Sopenharmony_ci			return -EBUSY;
3658c2ecf20Sopenharmony_ci		}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci		err = snd_ctl_remove(card, old);
3688c2ecf20Sopenharmony_ci		if (err < 0)
3698c2ecf20Sopenharmony_ci			return err;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (snd_ctl_find_hole(card, kcontrol->count) < 0)
3738c2ecf20Sopenharmony_ci		return -ENOMEM;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	list_add_tail(&kcontrol->list, &card->controls);
3768c2ecf20Sopenharmony_ci	card->controls_count += kcontrol->count;
3778c2ecf20Sopenharmony_ci	kcontrol->id.numid = card->last_numid + 1;
3788c2ecf20Sopenharmony_ci	card->last_numid += kcontrol->count;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	id = kcontrol->id;
3818c2ecf20Sopenharmony_ci	count = kcontrol->count;
3828c2ecf20Sopenharmony_ci	for (idx = 0; idx < count; idx++, id.index++, id.numid++)
3838c2ecf20Sopenharmony_ci		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	return 0;
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic int snd_ctl_add_replace(struct snd_card *card,
3898c2ecf20Sopenharmony_ci			       struct snd_kcontrol *kcontrol,
3908c2ecf20Sopenharmony_ci			       enum snd_ctl_add_mode mode)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	int err = -EINVAL;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (! kcontrol)
3958c2ecf20Sopenharmony_ci		return err;
3968c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!card || !kcontrol->info))
3978c2ecf20Sopenharmony_ci		goto error;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	down_write(&card->controls_rwsem);
4008c2ecf20Sopenharmony_ci	err = __snd_ctl_add_replace(card, kcontrol, mode);
4018c2ecf20Sopenharmony_ci	up_write(&card->controls_rwsem);
4028c2ecf20Sopenharmony_ci	if (err < 0)
4038c2ecf20Sopenharmony_ci		goto error;
4048c2ecf20Sopenharmony_ci	return 0;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci error:
4078c2ecf20Sopenharmony_ci	snd_ctl_free_one(kcontrol);
4088c2ecf20Sopenharmony_ci	return err;
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci/**
4128c2ecf20Sopenharmony_ci * snd_ctl_add - add the control instance to the card
4138c2ecf20Sopenharmony_ci * @card: the card instance
4148c2ecf20Sopenharmony_ci * @kcontrol: the control instance to add
4158c2ecf20Sopenharmony_ci *
4168c2ecf20Sopenharmony_ci * Adds the control instance created via snd_ctl_new() or
4178c2ecf20Sopenharmony_ci * snd_ctl_new1() to the given card. Assigns also an unique
4188c2ecf20Sopenharmony_ci * numid used for fast search.
4198c2ecf20Sopenharmony_ci *
4208c2ecf20Sopenharmony_ci * It frees automatically the control which cannot be added.
4218c2ecf20Sopenharmony_ci *
4228c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure.
4238c2ecf20Sopenharmony_ci *
4248c2ecf20Sopenharmony_ci */
4258c2ecf20Sopenharmony_ciint snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	return snd_ctl_add_replace(card, kcontrol, CTL_ADD_EXCLUSIVE);
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_add);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci/**
4328c2ecf20Sopenharmony_ci * snd_ctl_replace - replace the control instance of the card
4338c2ecf20Sopenharmony_ci * @card: the card instance
4348c2ecf20Sopenharmony_ci * @kcontrol: the control instance to replace
4358c2ecf20Sopenharmony_ci * @add_on_replace: add the control if not already added
4368c2ecf20Sopenharmony_ci *
4378c2ecf20Sopenharmony_ci * Replaces the given control.  If the given control does not exist
4388c2ecf20Sopenharmony_ci * and the add_on_replace flag is set, the control is added.  If the
4398c2ecf20Sopenharmony_ci * control exists, it is destroyed first.
4408c2ecf20Sopenharmony_ci *
4418c2ecf20Sopenharmony_ci * It frees automatically the control which cannot be added or replaced.
4428c2ecf20Sopenharmony_ci *
4438c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure.
4448c2ecf20Sopenharmony_ci */
4458c2ecf20Sopenharmony_ciint snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol,
4468c2ecf20Sopenharmony_ci		    bool add_on_replace)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	return snd_ctl_add_replace(card, kcontrol,
4498c2ecf20Sopenharmony_ci				   add_on_replace ? CTL_ADD_ON_REPLACE : CTL_REPLACE);
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_replace);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci/**
4548c2ecf20Sopenharmony_ci * snd_ctl_remove - remove the control from the card and release it
4558c2ecf20Sopenharmony_ci * @card: the card instance
4568c2ecf20Sopenharmony_ci * @kcontrol: the control instance to remove
4578c2ecf20Sopenharmony_ci *
4588c2ecf20Sopenharmony_ci * Removes the control from the card and then releases the instance.
4598c2ecf20Sopenharmony_ci * You don't need to call snd_ctl_free_one(). You must be in
4608c2ecf20Sopenharmony_ci * the write lock - down_write(&card->controls_rwsem).
4618c2ecf20Sopenharmony_ci *
4628c2ecf20Sopenharmony_ci * Return: 0 if successful, or a negative error code on failure.
4638c2ecf20Sopenharmony_ci */
4648c2ecf20Sopenharmony_ciint snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	struct snd_ctl_elem_id id;
4678c2ecf20Sopenharmony_ci	unsigned int idx;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!card || !kcontrol))
4708c2ecf20Sopenharmony_ci		return -EINVAL;
4718c2ecf20Sopenharmony_ci	list_del(&kcontrol->list);
4728c2ecf20Sopenharmony_ci	card->controls_count -= kcontrol->count;
4738c2ecf20Sopenharmony_ci	id = kcontrol->id;
4748c2ecf20Sopenharmony_ci	for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
4758c2ecf20Sopenharmony_ci		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_REMOVE, &id);
4768c2ecf20Sopenharmony_ci	snd_ctl_free_one(kcontrol);
4778c2ecf20Sopenharmony_ci	return 0;
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_remove);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci/**
4828c2ecf20Sopenharmony_ci * snd_ctl_remove_id - remove the control of the given id and release it
4838c2ecf20Sopenharmony_ci * @card: the card instance
4848c2ecf20Sopenharmony_ci * @id: the control id to remove
4858c2ecf20Sopenharmony_ci *
4868c2ecf20Sopenharmony_ci * Finds the control instance with the given id, removes it from the
4878c2ecf20Sopenharmony_ci * card list and releases it.
4888c2ecf20Sopenharmony_ci *
4898c2ecf20Sopenharmony_ci * Return: 0 if successful, or a negative error code on failure.
4908c2ecf20Sopenharmony_ci */
4918c2ecf20Sopenharmony_ciint snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
4948c2ecf20Sopenharmony_ci	int ret;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	down_write(&card->controls_rwsem);
4978c2ecf20Sopenharmony_ci	kctl = snd_ctl_find_id(card, id);
4988c2ecf20Sopenharmony_ci	if (kctl == NULL) {
4998c2ecf20Sopenharmony_ci		up_write(&card->controls_rwsem);
5008c2ecf20Sopenharmony_ci		return -ENOENT;
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci	ret = snd_ctl_remove(card, kctl);
5038c2ecf20Sopenharmony_ci	up_write(&card->controls_rwsem);
5048c2ecf20Sopenharmony_ci	return ret;
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_remove_id);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci/**
5098c2ecf20Sopenharmony_ci * snd_ctl_remove_user_ctl - remove and release the unlocked user control
5108c2ecf20Sopenharmony_ci * @file: active control handle
5118c2ecf20Sopenharmony_ci * @id: the control id to remove
5128c2ecf20Sopenharmony_ci *
5138c2ecf20Sopenharmony_ci * Finds the control instance with the given id, removes it from the
5148c2ecf20Sopenharmony_ci * card list and releases it.
5158c2ecf20Sopenharmony_ci *
5168c2ecf20Sopenharmony_ci * Return: 0 if successful, or a negative error code on failure.
5178c2ecf20Sopenharmony_ci */
5188c2ecf20Sopenharmony_cistatic int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
5198c2ecf20Sopenharmony_ci				   struct snd_ctl_elem_id *id)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	struct snd_card *card = file->card;
5228c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
5238c2ecf20Sopenharmony_ci	int idx, ret;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	down_write(&card->controls_rwsem);
5268c2ecf20Sopenharmony_ci	kctl = snd_ctl_find_id(card, id);
5278c2ecf20Sopenharmony_ci	if (kctl == NULL) {
5288c2ecf20Sopenharmony_ci		ret = -ENOENT;
5298c2ecf20Sopenharmony_ci		goto error;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci	if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) {
5328c2ecf20Sopenharmony_ci		ret = -EINVAL;
5338c2ecf20Sopenharmony_ci		goto error;
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci	for (idx = 0; idx < kctl->count; idx++)
5368c2ecf20Sopenharmony_ci		if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) {
5378c2ecf20Sopenharmony_ci			ret = -EBUSY;
5388c2ecf20Sopenharmony_ci			goto error;
5398c2ecf20Sopenharmony_ci		}
5408c2ecf20Sopenharmony_ci	ret = snd_ctl_remove(card, kctl);
5418c2ecf20Sopenharmony_ci	if (ret < 0)
5428c2ecf20Sopenharmony_ci		goto error;
5438c2ecf20Sopenharmony_ci	card->user_ctl_count--;
5448c2ecf20Sopenharmony_cierror:
5458c2ecf20Sopenharmony_ci	up_write(&card->controls_rwsem);
5468c2ecf20Sopenharmony_ci	return ret;
5478c2ecf20Sopenharmony_ci}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci/**
5508c2ecf20Sopenharmony_ci * snd_ctl_activate_id - activate/inactivate the control of the given id
5518c2ecf20Sopenharmony_ci * @card: the card instance
5528c2ecf20Sopenharmony_ci * @id: the control id to activate/inactivate
5538c2ecf20Sopenharmony_ci * @active: non-zero to activate
5548c2ecf20Sopenharmony_ci *
5558c2ecf20Sopenharmony_ci * Finds the control instance with the given id, and activate or
5568c2ecf20Sopenharmony_ci * inactivate the control together with notification, if changed.
5578c2ecf20Sopenharmony_ci * The given ID data is filled with full information.
5588c2ecf20Sopenharmony_ci *
5598c2ecf20Sopenharmony_ci * Return: 0 if unchanged, 1 if changed, or a negative error code on failure.
5608c2ecf20Sopenharmony_ci */
5618c2ecf20Sopenharmony_ciint snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
5628c2ecf20Sopenharmony_ci			int active)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
5658c2ecf20Sopenharmony_ci	struct snd_kcontrol_volatile *vd;
5668c2ecf20Sopenharmony_ci	unsigned int index_offset;
5678c2ecf20Sopenharmony_ci	int ret;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	down_write(&card->controls_rwsem);
5708c2ecf20Sopenharmony_ci	kctl = snd_ctl_find_id(card, id);
5718c2ecf20Sopenharmony_ci	if (kctl == NULL) {
5728c2ecf20Sopenharmony_ci		ret = -ENOENT;
5738c2ecf20Sopenharmony_ci		goto unlock;
5748c2ecf20Sopenharmony_ci	}
5758c2ecf20Sopenharmony_ci	index_offset = snd_ctl_get_ioff(kctl, id);
5768c2ecf20Sopenharmony_ci	vd = &kctl->vd[index_offset];
5778c2ecf20Sopenharmony_ci	ret = 0;
5788c2ecf20Sopenharmony_ci	if (active) {
5798c2ecf20Sopenharmony_ci		if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE))
5808c2ecf20Sopenharmony_ci			goto unlock;
5818c2ecf20Sopenharmony_ci		vd->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
5828c2ecf20Sopenharmony_ci	} else {
5838c2ecf20Sopenharmony_ci		if (vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)
5848c2ecf20Sopenharmony_ci			goto unlock;
5858c2ecf20Sopenharmony_ci		vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci	snd_ctl_build_ioff(id, kctl, index_offset);
5888c2ecf20Sopenharmony_ci	ret = 1;
5898c2ecf20Sopenharmony_ci unlock:
5908c2ecf20Sopenharmony_ci	up_write(&card->controls_rwsem);
5918c2ecf20Sopenharmony_ci	if (ret > 0)
5928c2ecf20Sopenharmony_ci		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, id);
5938c2ecf20Sopenharmony_ci	return ret;
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_ctl_activate_id);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci/**
5988c2ecf20Sopenharmony_ci * snd_ctl_rename_id - replace the id of a control on the card
5998c2ecf20Sopenharmony_ci * @card: the card instance
6008c2ecf20Sopenharmony_ci * @src_id: the old id
6018c2ecf20Sopenharmony_ci * @dst_id: the new id
6028c2ecf20Sopenharmony_ci *
6038c2ecf20Sopenharmony_ci * Finds the control with the old id from the card, and replaces the
6048c2ecf20Sopenharmony_ci * id with the new one.
6058c2ecf20Sopenharmony_ci *
6068c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure.
6078c2ecf20Sopenharmony_ci */
6088c2ecf20Sopenharmony_ciint snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
6098c2ecf20Sopenharmony_ci		      struct snd_ctl_elem_id *dst_id)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	down_write(&card->controls_rwsem);
6148c2ecf20Sopenharmony_ci	kctl = snd_ctl_find_id(card, src_id);
6158c2ecf20Sopenharmony_ci	if (kctl == NULL) {
6168c2ecf20Sopenharmony_ci		up_write(&card->controls_rwsem);
6178c2ecf20Sopenharmony_ci		return -ENOENT;
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci	kctl->id = *dst_id;
6208c2ecf20Sopenharmony_ci	kctl->id.numid = card->last_numid + 1;
6218c2ecf20Sopenharmony_ci	card->last_numid += kctl->count;
6228c2ecf20Sopenharmony_ci	up_write(&card->controls_rwsem);
6238c2ecf20Sopenharmony_ci	return 0;
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_rename_id);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci/**
6288c2ecf20Sopenharmony_ci * snd_ctl_find_numid - find the control instance with the given number-id
6298c2ecf20Sopenharmony_ci * @card: the card instance
6308c2ecf20Sopenharmony_ci * @numid: the number-id to search
6318c2ecf20Sopenharmony_ci *
6328c2ecf20Sopenharmony_ci * Finds the control instance with the given number-id from the card.
6338c2ecf20Sopenharmony_ci *
6348c2ecf20Sopenharmony_ci * The caller must down card->controls_rwsem before calling this function
6358c2ecf20Sopenharmony_ci * (if the race condition can happen).
6368c2ecf20Sopenharmony_ci *
6378c2ecf20Sopenharmony_ci * Return: The pointer of the instance if found, or %NULL if not.
6388c2ecf20Sopenharmony_ci *
6398c2ecf20Sopenharmony_ci */
6408c2ecf20Sopenharmony_cistruct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid)
6418c2ecf20Sopenharmony_ci{
6428c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!card || !numid))
6458c2ecf20Sopenharmony_ci		return NULL;
6468c2ecf20Sopenharmony_ci	list_for_each_entry(kctl, &card->controls, list) {
6478c2ecf20Sopenharmony_ci		if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
6488c2ecf20Sopenharmony_ci			return kctl;
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci	return NULL;
6518c2ecf20Sopenharmony_ci}
6528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_find_numid);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci/**
6558c2ecf20Sopenharmony_ci * snd_ctl_find_id - find the control instance with the given id
6568c2ecf20Sopenharmony_ci * @card: the card instance
6578c2ecf20Sopenharmony_ci * @id: the id to search
6588c2ecf20Sopenharmony_ci *
6598c2ecf20Sopenharmony_ci * Finds the control instance with the given id from the card.
6608c2ecf20Sopenharmony_ci *
6618c2ecf20Sopenharmony_ci * The caller must down card->controls_rwsem before calling this function
6628c2ecf20Sopenharmony_ci * (if the race condition can happen).
6638c2ecf20Sopenharmony_ci *
6648c2ecf20Sopenharmony_ci * Return: The pointer of the instance if found, or %NULL if not.
6658c2ecf20Sopenharmony_ci *
6668c2ecf20Sopenharmony_ci */
6678c2ecf20Sopenharmony_cistruct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
6688c2ecf20Sopenharmony_ci				     struct snd_ctl_elem_id *id)
6698c2ecf20Sopenharmony_ci{
6708c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!card || !id))
6738c2ecf20Sopenharmony_ci		return NULL;
6748c2ecf20Sopenharmony_ci	if (id->numid != 0)
6758c2ecf20Sopenharmony_ci		return snd_ctl_find_numid(card, id->numid);
6768c2ecf20Sopenharmony_ci	list_for_each_entry(kctl, &card->controls, list) {
6778c2ecf20Sopenharmony_ci		if (kctl->id.iface != id->iface)
6788c2ecf20Sopenharmony_ci			continue;
6798c2ecf20Sopenharmony_ci		if (kctl->id.device != id->device)
6808c2ecf20Sopenharmony_ci			continue;
6818c2ecf20Sopenharmony_ci		if (kctl->id.subdevice != id->subdevice)
6828c2ecf20Sopenharmony_ci			continue;
6838c2ecf20Sopenharmony_ci		if (strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)))
6848c2ecf20Sopenharmony_ci			continue;
6858c2ecf20Sopenharmony_ci		if (kctl->id.index > id->index)
6868c2ecf20Sopenharmony_ci			continue;
6878c2ecf20Sopenharmony_ci		if (kctl->id.index + kctl->count <= id->index)
6888c2ecf20Sopenharmony_ci			continue;
6898c2ecf20Sopenharmony_ci		return kctl;
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci	return NULL;
6928c2ecf20Sopenharmony_ci}
6938c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_find_id);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_cistatic int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
6968c2ecf20Sopenharmony_ci			     unsigned int cmd, void __user *arg)
6978c2ecf20Sopenharmony_ci{
6988c2ecf20Sopenharmony_ci	struct snd_ctl_card_info *info;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	info = kzalloc(sizeof(*info), GFP_KERNEL);
7018c2ecf20Sopenharmony_ci	if (! info)
7028c2ecf20Sopenharmony_ci		return -ENOMEM;
7038c2ecf20Sopenharmony_ci	down_read(&snd_ioctl_rwsem);
7048c2ecf20Sopenharmony_ci	info->card = card->number;
7058c2ecf20Sopenharmony_ci	strlcpy(info->id, card->id, sizeof(info->id));
7068c2ecf20Sopenharmony_ci	strlcpy(info->driver, card->driver, sizeof(info->driver));
7078c2ecf20Sopenharmony_ci	strlcpy(info->name, card->shortname, sizeof(info->name));
7088c2ecf20Sopenharmony_ci	strlcpy(info->longname, card->longname, sizeof(info->longname));
7098c2ecf20Sopenharmony_ci	strlcpy(info->mixername, card->mixername, sizeof(info->mixername));
7108c2ecf20Sopenharmony_ci	strlcpy(info->components, card->components, sizeof(info->components));
7118c2ecf20Sopenharmony_ci	up_read(&snd_ioctl_rwsem);
7128c2ecf20Sopenharmony_ci	if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) {
7138c2ecf20Sopenharmony_ci		kfree(info);
7148c2ecf20Sopenharmony_ci		return -EFAULT;
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci	kfree(info);
7178c2ecf20Sopenharmony_ci	return 0;
7188c2ecf20Sopenharmony_ci}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_cistatic int snd_ctl_elem_list(struct snd_card *card,
7218c2ecf20Sopenharmony_ci			     struct snd_ctl_elem_list *list)
7228c2ecf20Sopenharmony_ci{
7238c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
7248c2ecf20Sopenharmony_ci	struct snd_ctl_elem_id id;
7258c2ecf20Sopenharmony_ci	unsigned int offset, space, jidx;
7268c2ecf20Sopenharmony_ci	int err = 0;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	offset = list->offset;
7298c2ecf20Sopenharmony_ci	space = list->space;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	down_read(&card->controls_rwsem);
7328c2ecf20Sopenharmony_ci	list->count = card->controls_count;
7338c2ecf20Sopenharmony_ci	list->used = 0;
7348c2ecf20Sopenharmony_ci	if (space > 0) {
7358c2ecf20Sopenharmony_ci		list_for_each_entry(kctl, &card->controls, list) {
7368c2ecf20Sopenharmony_ci			if (offset >= kctl->count) {
7378c2ecf20Sopenharmony_ci				offset -= kctl->count;
7388c2ecf20Sopenharmony_ci				continue;
7398c2ecf20Sopenharmony_ci			}
7408c2ecf20Sopenharmony_ci			for (jidx = offset; jidx < kctl->count; jidx++) {
7418c2ecf20Sopenharmony_ci				snd_ctl_build_ioff(&id, kctl, jidx);
7428c2ecf20Sopenharmony_ci				if (copy_to_user(list->pids + list->used, &id,
7438c2ecf20Sopenharmony_ci						 sizeof(id))) {
7448c2ecf20Sopenharmony_ci					err = -EFAULT;
7458c2ecf20Sopenharmony_ci					goto out;
7468c2ecf20Sopenharmony_ci				}
7478c2ecf20Sopenharmony_ci				list->used++;
7488c2ecf20Sopenharmony_ci				if (!--space)
7498c2ecf20Sopenharmony_ci					goto out;
7508c2ecf20Sopenharmony_ci			}
7518c2ecf20Sopenharmony_ci			offset = 0;
7528c2ecf20Sopenharmony_ci		}
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci out:
7558c2ecf20Sopenharmony_ci	up_read(&card->controls_rwsem);
7568c2ecf20Sopenharmony_ci	return err;
7578c2ecf20Sopenharmony_ci}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_cistatic int snd_ctl_elem_list_user(struct snd_card *card,
7608c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_list __user *_list)
7618c2ecf20Sopenharmony_ci{
7628c2ecf20Sopenharmony_ci	struct snd_ctl_elem_list list;
7638c2ecf20Sopenharmony_ci	int err;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	if (copy_from_user(&list, _list, sizeof(list)))
7668c2ecf20Sopenharmony_ci		return -EFAULT;
7678c2ecf20Sopenharmony_ci	err = snd_ctl_elem_list(card, &list);
7688c2ecf20Sopenharmony_ci	if (err)
7698c2ecf20Sopenharmony_ci		return err;
7708c2ecf20Sopenharmony_ci	if (copy_to_user(_list, &list, sizeof(list)))
7718c2ecf20Sopenharmony_ci		return -EFAULT;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	return 0;
7748c2ecf20Sopenharmony_ci}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci/* Check whether the given kctl info is valid */
7778c2ecf20Sopenharmony_cistatic int snd_ctl_check_elem_info(struct snd_card *card,
7788c2ecf20Sopenharmony_ci				   const struct snd_ctl_elem_info *info)
7798c2ecf20Sopenharmony_ci{
7808c2ecf20Sopenharmony_ci	static const unsigned int max_value_counts[] = {
7818c2ecf20Sopenharmony_ci		[SNDRV_CTL_ELEM_TYPE_BOOLEAN]	= 128,
7828c2ecf20Sopenharmony_ci		[SNDRV_CTL_ELEM_TYPE_INTEGER]	= 128,
7838c2ecf20Sopenharmony_ci		[SNDRV_CTL_ELEM_TYPE_ENUMERATED] = 128,
7848c2ecf20Sopenharmony_ci		[SNDRV_CTL_ELEM_TYPE_BYTES]	= 512,
7858c2ecf20Sopenharmony_ci		[SNDRV_CTL_ELEM_TYPE_IEC958]	= 1,
7868c2ecf20Sopenharmony_ci		[SNDRV_CTL_ELEM_TYPE_INTEGER64] = 64,
7878c2ecf20Sopenharmony_ci	};
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
7908c2ecf20Sopenharmony_ci	    info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64) {
7918c2ecf20Sopenharmony_ci		if (card)
7928c2ecf20Sopenharmony_ci			dev_err(card->dev,
7938c2ecf20Sopenharmony_ci				"control %i:%i:%i:%s:%i: invalid type %d\n",
7948c2ecf20Sopenharmony_ci				info->id.iface, info->id.device,
7958c2ecf20Sopenharmony_ci				info->id.subdevice, info->id.name,
7968c2ecf20Sopenharmony_ci				info->id.index, info->type);
7978c2ecf20Sopenharmony_ci		return -EINVAL;
7988c2ecf20Sopenharmony_ci	}
7998c2ecf20Sopenharmony_ci	if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED &&
8008c2ecf20Sopenharmony_ci	    info->value.enumerated.items == 0) {
8018c2ecf20Sopenharmony_ci		if (card)
8028c2ecf20Sopenharmony_ci			dev_err(card->dev,
8038c2ecf20Sopenharmony_ci				"control %i:%i:%i:%s:%i: zero enum items\n",
8048c2ecf20Sopenharmony_ci				info->id.iface, info->id.device,
8058c2ecf20Sopenharmony_ci				info->id.subdevice, info->id.name,
8068c2ecf20Sopenharmony_ci				info->id.index);
8078c2ecf20Sopenharmony_ci		return -EINVAL;
8088c2ecf20Sopenharmony_ci	}
8098c2ecf20Sopenharmony_ci	if (info->count > max_value_counts[info->type]) {
8108c2ecf20Sopenharmony_ci		if (card)
8118c2ecf20Sopenharmony_ci			dev_err(card->dev,
8128c2ecf20Sopenharmony_ci				"control %i:%i:%i:%s:%i: invalid count %d\n",
8138c2ecf20Sopenharmony_ci				info->id.iface, info->id.device,
8148c2ecf20Sopenharmony_ci				info->id.subdevice, info->id.name,
8158c2ecf20Sopenharmony_ci				info->id.index, info->count);
8168c2ecf20Sopenharmony_ci		return -EINVAL;
8178c2ecf20Sopenharmony_ci	}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	return 0;
8208c2ecf20Sopenharmony_ci}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci/* The capacity of struct snd_ctl_elem_value.value.*/
8238c2ecf20Sopenharmony_cistatic const unsigned int value_sizes[] = {
8248c2ecf20Sopenharmony_ci	[SNDRV_CTL_ELEM_TYPE_BOOLEAN]	= sizeof(long),
8258c2ecf20Sopenharmony_ci	[SNDRV_CTL_ELEM_TYPE_INTEGER]	= sizeof(long),
8268c2ecf20Sopenharmony_ci	[SNDRV_CTL_ELEM_TYPE_ENUMERATED] = sizeof(unsigned int),
8278c2ecf20Sopenharmony_ci	[SNDRV_CTL_ELEM_TYPE_BYTES]	= sizeof(unsigned char),
8288c2ecf20Sopenharmony_ci	[SNDRV_CTL_ELEM_TYPE_IEC958]	= sizeof(struct snd_aes_iec958),
8298c2ecf20Sopenharmony_ci	[SNDRV_CTL_ELEM_TYPE_INTEGER64] = sizeof(long long),
8308c2ecf20Sopenharmony_ci};
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_CTL_VALIDATION
8338c2ecf20Sopenharmony_ci/* fill the remaining snd_ctl_elem_value data with the given pattern */
8348c2ecf20Sopenharmony_cistatic void fill_remaining_elem_value(struct snd_ctl_elem_value *control,
8358c2ecf20Sopenharmony_ci				      struct snd_ctl_elem_info *info,
8368c2ecf20Sopenharmony_ci				      u32 pattern)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	size_t offset = value_sizes[info->type] * info->count;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	offset = (offset + sizeof(u32) - 1) / sizeof(u32);
8418c2ecf20Sopenharmony_ci	memset32((u32 *)control->value.bytes.data + offset, pattern,
8428c2ecf20Sopenharmony_ci		 sizeof(control->value) / sizeof(u32) - offset);
8438c2ecf20Sopenharmony_ci}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci/* check whether the given integer ctl value is valid */
8468c2ecf20Sopenharmony_cistatic int sanity_check_int_value(struct snd_card *card,
8478c2ecf20Sopenharmony_ci				  const struct snd_ctl_elem_value *control,
8488c2ecf20Sopenharmony_ci				  const struct snd_ctl_elem_info *info,
8498c2ecf20Sopenharmony_ci				  int i)
8508c2ecf20Sopenharmony_ci{
8518c2ecf20Sopenharmony_ci	long long lval, lmin, lmax, lstep;
8528c2ecf20Sopenharmony_ci	u64 rem;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	switch (info->type) {
8558c2ecf20Sopenharmony_ci	default:
8568c2ecf20Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
8578c2ecf20Sopenharmony_ci		lval = control->value.integer.value[i];
8588c2ecf20Sopenharmony_ci		lmin = 0;
8598c2ecf20Sopenharmony_ci		lmax = 1;
8608c2ecf20Sopenharmony_ci		lstep = 0;
8618c2ecf20Sopenharmony_ci		break;
8628c2ecf20Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_INTEGER:
8638c2ecf20Sopenharmony_ci		lval = control->value.integer.value[i];
8648c2ecf20Sopenharmony_ci		lmin = info->value.integer.min;
8658c2ecf20Sopenharmony_ci		lmax = info->value.integer.max;
8668c2ecf20Sopenharmony_ci		lstep = info->value.integer.step;
8678c2ecf20Sopenharmony_ci		break;
8688c2ecf20Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_INTEGER64:
8698c2ecf20Sopenharmony_ci		lval = control->value.integer64.value[i];
8708c2ecf20Sopenharmony_ci		lmin = info->value.integer64.min;
8718c2ecf20Sopenharmony_ci		lmax = info->value.integer64.max;
8728c2ecf20Sopenharmony_ci		lstep = info->value.integer64.step;
8738c2ecf20Sopenharmony_ci		break;
8748c2ecf20Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
8758c2ecf20Sopenharmony_ci		lval = control->value.enumerated.item[i];
8768c2ecf20Sopenharmony_ci		lmin = 0;
8778c2ecf20Sopenharmony_ci		lmax = info->value.enumerated.items - 1;
8788c2ecf20Sopenharmony_ci		lstep = 0;
8798c2ecf20Sopenharmony_ci		break;
8808c2ecf20Sopenharmony_ci	}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	if (lval < lmin || lval > lmax) {
8838c2ecf20Sopenharmony_ci		dev_err(card->dev,
8848c2ecf20Sopenharmony_ci			"control %i:%i:%i:%s:%i: value out of range %lld (%lld/%lld) at count %i\n",
8858c2ecf20Sopenharmony_ci			control->id.iface, control->id.device,
8868c2ecf20Sopenharmony_ci			control->id.subdevice, control->id.name,
8878c2ecf20Sopenharmony_ci			control->id.index, lval, lmin, lmax, i);
8888c2ecf20Sopenharmony_ci		return -EINVAL;
8898c2ecf20Sopenharmony_ci	}
8908c2ecf20Sopenharmony_ci	if (lstep) {
8918c2ecf20Sopenharmony_ci		div64_u64_rem(lval, lstep, &rem);
8928c2ecf20Sopenharmony_ci		if (rem) {
8938c2ecf20Sopenharmony_ci			dev_err(card->dev,
8948c2ecf20Sopenharmony_ci				"control %i:%i:%i:%s:%i: unaligned value %lld (step %lld) at count %i\n",
8958c2ecf20Sopenharmony_ci				control->id.iface, control->id.device,
8968c2ecf20Sopenharmony_ci				control->id.subdevice, control->id.name,
8978c2ecf20Sopenharmony_ci				control->id.index, lval, lstep, i);
8988c2ecf20Sopenharmony_ci			return -EINVAL;
8998c2ecf20Sopenharmony_ci		}
9008c2ecf20Sopenharmony_ci	}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	return 0;
9038c2ecf20Sopenharmony_ci}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci/* perform sanity checks to the given snd_ctl_elem_value object */
9068c2ecf20Sopenharmony_cistatic int sanity_check_elem_value(struct snd_card *card,
9078c2ecf20Sopenharmony_ci				   const struct snd_ctl_elem_value *control,
9088c2ecf20Sopenharmony_ci				   const struct snd_ctl_elem_info *info,
9098c2ecf20Sopenharmony_ci				   u32 pattern)
9108c2ecf20Sopenharmony_ci{
9118c2ecf20Sopenharmony_ci	size_t offset;
9128c2ecf20Sopenharmony_ci	int i, ret = 0;
9138c2ecf20Sopenharmony_ci	u32 *p;
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	switch (info->type) {
9168c2ecf20Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
9178c2ecf20Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_INTEGER:
9188c2ecf20Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_INTEGER64:
9198c2ecf20Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
9208c2ecf20Sopenharmony_ci		for (i = 0; i < info->count; i++) {
9218c2ecf20Sopenharmony_ci			ret = sanity_check_int_value(card, control, info, i);
9228c2ecf20Sopenharmony_ci			if (ret < 0)
9238c2ecf20Sopenharmony_ci				return ret;
9248c2ecf20Sopenharmony_ci		}
9258c2ecf20Sopenharmony_ci		break;
9268c2ecf20Sopenharmony_ci	default:
9278c2ecf20Sopenharmony_ci		break;
9288c2ecf20Sopenharmony_ci	}
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	/* check whether the remaining area kept untouched */
9318c2ecf20Sopenharmony_ci	offset = value_sizes[info->type] * info->count;
9328c2ecf20Sopenharmony_ci	offset = (offset + sizeof(u32) - 1) / sizeof(u32);
9338c2ecf20Sopenharmony_ci	p = (u32 *)control->value.bytes.data + offset;
9348c2ecf20Sopenharmony_ci	for (; offset < sizeof(control->value) / sizeof(u32); offset++, p++) {
9358c2ecf20Sopenharmony_ci		if (*p != pattern) {
9368c2ecf20Sopenharmony_ci			ret = -EINVAL;
9378c2ecf20Sopenharmony_ci			break;
9388c2ecf20Sopenharmony_ci		}
9398c2ecf20Sopenharmony_ci		*p = 0; /* clear the checked area */
9408c2ecf20Sopenharmony_ci	}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	return ret;
9438c2ecf20Sopenharmony_ci}
9448c2ecf20Sopenharmony_ci#else
9458c2ecf20Sopenharmony_cistatic inline void fill_remaining_elem_value(struct snd_ctl_elem_value *control,
9468c2ecf20Sopenharmony_ci					     struct snd_ctl_elem_info *info,
9478c2ecf20Sopenharmony_ci					     u32 pattern)
9488c2ecf20Sopenharmony_ci{
9498c2ecf20Sopenharmony_ci}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_cistatic inline int sanity_check_elem_value(struct snd_card *card,
9528c2ecf20Sopenharmony_ci					  struct snd_ctl_elem_value *control,
9538c2ecf20Sopenharmony_ci					  struct snd_ctl_elem_info *info,
9548c2ecf20Sopenharmony_ci					  u32 pattern)
9558c2ecf20Sopenharmony_ci{
9568c2ecf20Sopenharmony_ci	return 0;
9578c2ecf20Sopenharmony_ci}
9588c2ecf20Sopenharmony_ci#endif
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_cistatic int __snd_ctl_elem_info(struct snd_card *card,
9618c2ecf20Sopenharmony_ci			       struct snd_kcontrol *kctl,
9628c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_info *info,
9638c2ecf20Sopenharmony_ci			       struct snd_ctl_file *ctl)
9648c2ecf20Sopenharmony_ci{
9658c2ecf20Sopenharmony_ci	struct snd_kcontrol_volatile *vd;
9668c2ecf20Sopenharmony_ci	unsigned int index_offset;
9678c2ecf20Sopenharmony_ci	int result;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
9708c2ecf20Sopenharmony_ci	info->access = 0;
9718c2ecf20Sopenharmony_ci#endif
9728c2ecf20Sopenharmony_ci	result = kctl->info(kctl, info);
9738c2ecf20Sopenharmony_ci	if (result >= 0) {
9748c2ecf20Sopenharmony_ci		snd_BUG_ON(info->access);
9758c2ecf20Sopenharmony_ci		index_offset = snd_ctl_get_ioff(kctl, &info->id);
9768c2ecf20Sopenharmony_ci		vd = &kctl->vd[index_offset];
9778c2ecf20Sopenharmony_ci		snd_ctl_build_ioff(&info->id, kctl, index_offset);
9788c2ecf20Sopenharmony_ci		info->access = vd->access;
9798c2ecf20Sopenharmony_ci		if (vd->owner) {
9808c2ecf20Sopenharmony_ci			info->access |= SNDRV_CTL_ELEM_ACCESS_LOCK;
9818c2ecf20Sopenharmony_ci			if (vd->owner == ctl)
9828c2ecf20Sopenharmony_ci				info->access |= SNDRV_CTL_ELEM_ACCESS_OWNER;
9838c2ecf20Sopenharmony_ci			info->owner = pid_vnr(vd->owner->pid);
9848c2ecf20Sopenharmony_ci		} else {
9858c2ecf20Sopenharmony_ci			info->owner = -1;
9868c2ecf20Sopenharmony_ci		}
9878c2ecf20Sopenharmony_ci		if (!snd_ctl_skip_validation(info) &&
9888c2ecf20Sopenharmony_ci		    snd_ctl_check_elem_info(card, info) < 0)
9898c2ecf20Sopenharmony_ci			result = -EINVAL;
9908c2ecf20Sopenharmony_ci	}
9918c2ecf20Sopenharmony_ci	return result;
9928c2ecf20Sopenharmony_ci}
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_cistatic int snd_ctl_elem_info(struct snd_ctl_file *ctl,
9958c2ecf20Sopenharmony_ci			     struct snd_ctl_elem_info *info)
9968c2ecf20Sopenharmony_ci{
9978c2ecf20Sopenharmony_ci	struct snd_card *card = ctl->card;
9988c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
9998c2ecf20Sopenharmony_ci	int result;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	down_read(&card->controls_rwsem);
10028c2ecf20Sopenharmony_ci	kctl = snd_ctl_find_id(card, &info->id);
10038c2ecf20Sopenharmony_ci	if (kctl == NULL)
10048c2ecf20Sopenharmony_ci		result = -ENOENT;
10058c2ecf20Sopenharmony_ci	else
10068c2ecf20Sopenharmony_ci		result = __snd_ctl_elem_info(card, kctl, info, ctl);
10078c2ecf20Sopenharmony_ci	up_read(&card->controls_rwsem);
10088c2ecf20Sopenharmony_ci	return result;
10098c2ecf20Sopenharmony_ci}
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_cistatic int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
10128c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_info __user *_info)
10138c2ecf20Sopenharmony_ci{
10148c2ecf20Sopenharmony_ci	struct snd_ctl_elem_info info;
10158c2ecf20Sopenharmony_ci	int result;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	if (copy_from_user(&info, _info, sizeof(info)))
10188c2ecf20Sopenharmony_ci		return -EFAULT;
10198c2ecf20Sopenharmony_ci	result = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
10208c2ecf20Sopenharmony_ci	if (result < 0)
10218c2ecf20Sopenharmony_ci		return result;
10228c2ecf20Sopenharmony_ci	result = snd_ctl_elem_info(ctl, &info);
10238c2ecf20Sopenharmony_ci	if (result < 0)
10248c2ecf20Sopenharmony_ci		return result;
10258c2ecf20Sopenharmony_ci	/* drop internal access flags */
10268c2ecf20Sopenharmony_ci	info.access &= ~SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK;
10278c2ecf20Sopenharmony_ci	if (copy_to_user(_info, &info, sizeof(info)))
10288c2ecf20Sopenharmony_ci		return -EFAULT;
10298c2ecf20Sopenharmony_ci	return result;
10308c2ecf20Sopenharmony_ci}
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_cistatic int snd_ctl_elem_read(struct snd_card *card,
10338c2ecf20Sopenharmony_ci			     struct snd_ctl_elem_value *control)
10348c2ecf20Sopenharmony_ci{
10358c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
10368c2ecf20Sopenharmony_ci	struct snd_kcontrol_volatile *vd;
10378c2ecf20Sopenharmony_ci	unsigned int index_offset;
10388c2ecf20Sopenharmony_ci	struct snd_ctl_elem_info info;
10398c2ecf20Sopenharmony_ci	const u32 pattern = 0xdeadbeef;
10408c2ecf20Sopenharmony_ci	int ret;
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	kctl = snd_ctl_find_id(card, &control->id);
10438c2ecf20Sopenharmony_ci	if (kctl == NULL)
10448c2ecf20Sopenharmony_ci		return -ENOENT;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	index_offset = snd_ctl_get_ioff(kctl, &control->id);
10478c2ecf20Sopenharmony_ci	vd = &kctl->vd[index_offset];
10488c2ecf20Sopenharmony_ci	if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_READ) || kctl->get == NULL)
10498c2ecf20Sopenharmony_ci		return -EPERM;
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	snd_ctl_build_ioff(&control->id, kctl, index_offset);
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_CTL_VALIDATION
10548c2ecf20Sopenharmony_ci	/* info is needed only for validation */
10558c2ecf20Sopenharmony_ci	memset(&info, 0, sizeof(info));
10568c2ecf20Sopenharmony_ci	info.id = control->id;
10578c2ecf20Sopenharmony_ci	ret = __snd_ctl_elem_info(card, kctl, &info, NULL);
10588c2ecf20Sopenharmony_ci	if (ret < 0)
10598c2ecf20Sopenharmony_ci		return ret;
10608c2ecf20Sopenharmony_ci#endif
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	if (!snd_ctl_skip_validation(&info))
10638c2ecf20Sopenharmony_ci		fill_remaining_elem_value(control, &info, pattern);
10648c2ecf20Sopenharmony_ci	ret = kctl->get(kctl, control);
10658c2ecf20Sopenharmony_ci	if (ret < 0)
10668c2ecf20Sopenharmony_ci		return ret;
10678c2ecf20Sopenharmony_ci	if (!snd_ctl_skip_validation(&info) &&
10688c2ecf20Sopenharmony_ci	    sanity_check_elem_value(card, control, &info, pattern) < 0) {
10698c2ecf20Sopenharmony_ci		dev_err(card->dev,
10708c2ecf20Sopenharmony_ci			"control %i:%i:%i:%s:%i: access overflow\n",
10718c2ecf20Sopenharmony_ci			control->id.iface, control->id.device,
10728c2ecf20Sopenharmony_ci			control->id.subdevice, control->id.name,
10738c2ecf20Sopenharmony_ci			control->id.index);
10748c2ecf20Sopenharmony_ci		return -EINVAL;
10758c2ecf20Sopenharmony_ci	}
10768c2ecf20Sopenharmony_ci	return ret;
10778c2ecf20Sopenharmony_ci}
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_cistatic int snd_ctl_elem_read_user(struct snd_card *card,
10808c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_value __user *_control)
10818c2ecf20Sopenharmony_ci{
10828c2ecf20Sopenharmony_ci	struct snd_ctl_elem_value *control;
10838c2ecf20Sopenharmony_ci	int result;
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	control = memdup_user(_control, sizeof(*control));
10868c2ecf20Sopenharmony_ci	if (IS_ERR(control))
10878c2ecf20Sopenharmony_ci		return PTR_ERR(control);
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
10908c2ecf20Sopenharmony_ci	if (result < 0)
10918c2ecf20Sopenharmony_ci		goto error;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	down_read(&card->controls_rwsem);
10948c2ecf20Sopenharmony_ci	result = snd_ctl_elem_read(card, control);
10958c2ecf20Sopenharmony_ci	up_read(&card->controls_rwsem);
10968c2ecf20Sopenharmony_ci	if (result < 0)
10978c2ecf20Sopenharmony_ci		goto error;
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	if (copy_to_user(_control, control, sizeof(*control)))
11008c2ecf20Sopenharmony_ci		result = -EFAULT;
11018c2ecf20Sopenharmony_ci error:
11028c2ecf20Sopenharmony_ci	kfree(control);
11038c2ecf20Sopenharmony_ci	return result;
11048c2ecf20Sopenharmony_ci}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_cistatic int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
11078c2ecf20Sopenharmony_ci			      struct snd_ctl_elem_value *control)
11088c2ecf20Sopenharmony_ci{
11098c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
11108c2ecf20Sopenharmony_ci	struct snd_kcontrol_volatile *vd;
11118c2ecf20Sopenharmony_ci	unsigned int index_offset;
11128c2ecf20Sopenharmony_ci	int result;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	kctl = snd_ctl_find_id(card, &control->id);
11158c2ecf20Sopenharmony_ci	if (kctl == NULL)
11168c2ecf20Sopenharmony_ci		return -ENOENT;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	index_offset = snd_ctl_get_ioff(kctl, &control->id);
11198c2ecf20Sopenharmony_ci	vd = &kctl->vd[index_offset];
11208c2ecf20Sopenharmony_ci	if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || kctl->put == NULL ||
11218c2ecf20Sopenharmony_ci	    (file && vd->owner && vd->owner != file)) {
11228c2ecf20Sopenharmony_ci		return -EPERM;
11238c2ecf20Sopenharmony_ci	}
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	snd_ctl_build_ioff(&control->id, kctl, index_offset);
11268c2ecf20Sopenharmony_ci	result = kctl->put(kctl, control);
11278c2ecf20Sopenharmony_ci	if (result < 0)
11288c2ecf20Sopenharmony_ci		return result;
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	if (result > 0) {
11318c2ecf20Sopenharmony_ci		struct snd_ctl_elem_id id = control->id;
11328c2ecf20Sopenharmony_ci		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &id);
11338c2ecf20Sopenharmony_ci	}
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	return 0;
11368c2ecf20Sopenharmony_ci}
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_cistatic int snd_ctl_elem_write_user(struct snd_ctl_file *file,
11398c2ecf20Sopenharmony_ci				   struct snd_ctl_elem_value __user *_control)
11408c2ecf20Sopenharmony_ci{
11418c2ecf20Sopenharmony_ci	struct snd_ctl_elem_value *control;
11428c2ecf20Sopenharmony_ci	struct snd_card *card;
11438c2ecf20Sopenharmony_ci	int result;
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	control = memdup_user(_control, sizeof(*control));
11468c2ecf20Sopenharmony_ci	if (IS_ERR(control))
11478c2ecf20Sopenharmony_ci		return PTR_ERR(control);
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	card = file->card;
11508c2ecf20Sopenharmony_ci	result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
11518c2ecf20Sopenharmony_ci	if (result < 0)
11528c2ecf20Sopenharmony_ci		goto error;
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	down_write(&card->controls_rwsem);
11558c2ecf20Sopenharmony_ci	result = snd_ctl_elem_write(card, file, control);
11568c2ecf20Sopenharmony_ci	up_write(&card->controls_rwsem);
11578c2ecf20Sopenharmony_ci	if (result < 0)
11588c2ecf20Sopenharmony_ci		goto error;
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	if (copy_to_user(_control, control, sizeof(*control)))
11618c2ecf20Sopenharmony_ci		result = -EFAULT;
11628c2ecf20Sopenharmony_ci error:
11638c2ecf20Sopenharmony_ci	kfree(control);
11648c2ecf20Sopenharmony_ci	return result;
11658c2ecf20Sopenharmony_ci}
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_cistatic int snd_ctl_elem_lock(struct snd_ctl_file *file,
11688c2ecf20Sopenharmony_ci			     struct snd_ctl_elem_id __user *_id)
11698c2ecf20Sopenharmony_ci{
11708c2ecf20Sopenharmony_ci	struct snd_card *card = file->card;
11718c2ecf20Sopenharmony_ci	struct snd_ctl_elem_id id;
11728c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
11738c2ecf20Sopenharmony_ci	struct snd_kcontrol_volatile *vd;
11748c2ecf20Sopenharmony_ci	int result;
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	if (copy_from_user(&id, _id, sizeof(id)))
11778c2ecf20Sopenharmony_ci		return -EFAULT;
11788c2ecf20Sopenharmony_ci	down_write(&card->controls_rwsem);
11798c2ecf20Sopenharmony_ci	kctl = snd_ctl_find_id(card, &id);
11808c2ecf20Sopenharmony_ci	if (kctl == NULL) {
11818c2ecf20Sopenharmony_ci		result = -ENOENT;
11828c2ecf20Sopenharmony_ci	} else {
11838c2ecf20Sopenharmony_ci		vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
11848c2ecf20Sopenharmony_ci		if (vd->owner != NULL)
11858c2ecf20Sopenharmony_ci			result = -EBUSY;
11868c2ecf20Sopenharmony_ci		else {
11878c2ecf20Sopenharmony_ci			vd->owner = file;
11888c2ecf20Sopenharmony_ci			result = 0;
11898c2ecf20Sopenharmony_ci		}
11908c2ecf20Sopenharmony_ci	}
11918c2ecf20Sopenharmony_ci	up_write(&card->controls_rwsem);
11928c2ecf20Sopenharmony_ci	return result;
11938c2ecf20Sopenharmony_ci}
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_cistatic int snd_ctl_elem_unlock(struct snd_ctl_file *file,
11968c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_id __user *_id)
11978c2ecf20Sopenharmony_ci{
11988c2ecf20Sopenharmony_ci	struct snd_card *card = file->card;
11998c2ecf20Sopenharmony_ci	struct snd_ctl_elem_id id;
12008c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
12018c2ecf20Sopenharmony_ci	struct snd_kcontrol_volatile *vd;
12028c2ecf20Sopenharmony_ci	int result;
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	if (copy_from_user(&id, _id, sizeof(id)))
12058c2ecf20Sopenharmony_ci		return -EFAULT;
12068c2ecf20Sopenharmony_ci	down_write(&card->controls_rwsem);
12078c2ecf20Sopenharmony_ci	kctl = snd_ctl_find_id(card, &id);
12088c2ecf20Sopenharmony_ci	if (kctl == NULL) {
12098c2ecf20Sopenharmony_ci		result = -ENOENT;
12108c2ecf20Sopenharmony_ci	} else {
12118c2ecf20Sopenharmony_ci		vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
12128c2ecf20Sopenharmony_ci		if (vd->owner == NULL)
12138c2ecf20Sopenharmony_ci			result = -EINVAL;
12148c2ecf20Sopenharmony_ci		else if (vd->owner != file)
12158c2ecf20Sopenharmony_ci			result = -EPERM;
12168c2ecf20Sopenharmony_ci		else {
12178c2ecf20Sopenharmony_ci			vd->owner = NULL;
12188c2ecf20Sopenharmony_ci			result = 0;
12198c2ecf20Sopenharmony_ci		}
12208c2ecf20Sopenharmony_ci	}
12218c2ecf20Sopenharmony_ci	up_write(&card->controls_rwsem);
12228c2ecf20Sopenharmony_ci	return result;
12238c2ecf20Sopenharmony_ci}
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_cistruct user_element {
12268c2ecf20Sopenharmony_ci	struct snd_ctl_elem_info info;
12278c2ecf20Sopenharmony_ci	struct snd_card *card;
12288c2ecf20Sopenharmony_ci	char *elem_data;		/* element data */
12298c2ecf20Sopenharmony_ci	unsigned long elem_data_size;	/* size of element data in bytes */
12308c2ecf20Sopenharmony_ci	void *tlv_data;			/* TLV data */
12318c2ecf20Sopenharmony_ci	unsigned long tlv_data_size;	/* TLV data size */
12328c2ecf20Sopenharmony_ci	void *priv_data;		/* private data (like strings for enumerated type) */
12338c2ecf20Sopenharmony_ci};
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_cistatic int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,
12368c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
12378c2ecf20Sopenharmony_ci{
12388c2ecf20Sopenharmony_ci	struct user_element *ue = kcontrol->private_data;
12398c2ecf20Sopenharmony_ci	unsigned int offset;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	offset = snd_ctl_get_ioff(kcontrol, &uinfo->id);
12428c2ecf20Sopenharmony_ci	*uinfo = ue->info;
12438c2ecf20Sopenharmony_ci	snd_ctl_build_ioff(&uinfo->id, kcontrol, offset);
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	return 0;
12468c2ecf20Sopenharmony_ci}
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_cistatic int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol,
12498c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_info *uinfo)
12508c2ecf20Sopenharmony_ci{
12518c2ecf20Sopenharmony_ci	struct user_element *ue = kcontrol->private_data;
12528c2ecf20Sopenharmony_ci	const char *names;
12538c2ecf20Sopenharmony_ci	unsigned int item;
12548c2ecf20Sopenharmony_ci	unsigned int offset;
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	item = uinfo->value.enumerated.item;
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	offset = snd_ctl_get_ioff(kcontrol, &uinfo->id);
12598c2ecf20Sopenharmony_ci	*uinfo = ue->info;
12608c2ecf20Sopenharmony_ci	snd_ctl_build_ioff(&uinfo->id, kcontrol, offset);
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	item = min(item, uinfo->value.enumerated.items - 1);
12638c2ecf20Sopenharmony_ci	uinfo->value.enumerated.item = item;
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	names = ue->priv_data;
12668c2ecf20Sopenharmony_ci	for (; item > 0; --item)
12678c2ecf20Sopenharmony_ci		names += strlen(names) + 1;
12688c2ecf20Sopenharmony_ci	strcpy(uinfo->value.enumerated.name, names);
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	return 0;
12718c2ecf20Sopenharmony_ci}
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_cistatic int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,
12748c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
12758c2ecf20Sopenharmony_ci{
12768c2ecf20Sopenharmony_ci	struct user_element *ue = kcontrol->private_data;
12778c2ecf20Sopenharmony_ci	unsigned int size = ue->elem_data_size;
12788c2ecf20Sopenharmony_ci	char *src = ue->elem_data +
12798c2ecf20Sopenharmony_ci			snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size;
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci	memcpy(&ucontrol->value, src, size);
12828c2ecf20Sopenharmony_ci	return 0;
12838c2ecf20Sopenharmony_ci}
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_cistatic int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
12868c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
12878c2ecf20Sopenharmony_ci{
12888c2ecf20Sopenharmony_ci	int change;
12898c2ecf20Sopenharmony_ci	struct user_element *ue = kcontrol->private_data;
12908c2ecf20Sopenharmony_ci	unsigned int size = ue->elem_data_size;
12918c2ecf20Sopenharmony_ci	char *dst = ue->elem_data +
12928c2ecf20Sopenharmony_ci			snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size;
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci	change = memcmp(&ucontrol->value, dst, size) != 0;
12958c2ecf20Sopenharmony_ci	if (change)
12968c2ecf20Sopenharmony_ci		memcpy(dst, &ucontrol->value, size);
12978c2ecf20Sopenharmony_ci	return change;
12988c2ecf20Sopenharmony_ci}
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_cistatic int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
13018c2ecf20Sopenharmony_ci			    unsigned int size)
13028c2ecf20Sopenharmony_ci{
13038c2ecf20Sopenharmony_ci	struct user_element *ue = kctl->private_data;
13048c2ecf20Sopenharmony_ci	unsigned int *container;
13058c2ecf20Sopenharmony_ci	struct snd_ctl_elem_id id;
13068c2ecf20Sopenharmony_ci	unsigned int mask = 0;
13078c2ecf20Sopenharmony_ci	int i;
13088c2ecf20Sopenharmony_ci	int change;
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	if (size > 1024 * 128)	/* sane value */
13118c2ecf20Sopenharmony_ci		return -EINVAL;
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	container = vmemdup_user(buf, size);
13148c2ecf20Sopenharmony_ci	if (IS_ERR(container))
13158c2ecf20Sopenharmony_ci		return PTR_ERR(container);
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	change = ue->tlv_data_size != size;
13188c2ecf20Sopenharmony_ci	if (!change)
13198c2ecf20Sopenharmony_ci		change = memcmp(ue->tlv_data, container, size) != 0;
13208c2ecf20Sopenharmony_ci	if (!change) {
13218c2ecf20Sopenharmony_ci		kvfree(container);
13228c2ecf20Sopenharmony_ci		return 0;
13238c2ecf20Sopenharmony_ci	}
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	if (ue->tlv_data == NULL) {
13268c2ecf20Sopenharmony_ci		/* Now TLV data is available. */
13278c2ecf20Sopenharmony_ci		for (i = 0; i < kctl->count; ++i)
13288c2ecf20Sopenharmony_ci			kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
13298c2ecf20Sopenharmony_ci		mask = SNDRV_CTL_EVENT_MASK_INFO;
13308c2ecf20Sopenharmony_ci	}
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci	kvfree(ue->tlv_data);
13338c2ecf20Sopenharmony_ci	ue->tlv_data = container;
13348c2ecf20Sopenharmony_ci	ue->tlv_data_size = size;
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	mask |= SNDRV_CTL_EVENT_MASK_TLV;
13378c2ecf20Sopenharmony_ci	for (i = 0; i < kctl->count; ++i) {
13388c2ecf20Sopenharmony_ci		snd_ctl_build_ioff(&id, kctl, i);
13398c2ecf20Sopenharmony_ci		snd_ctl_notify(ue->card, mask, &id);
13408c2ecf20Sopenharmony_ci	}
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	return change;
13438c2ecf20Sopenharmony_ci}
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_cistatic int read_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
13468c2ecf20Sopenharmony_ci			 unsigned int size)
13478c2ecf20Sopenharmony_ci{
13488c2ecf20Sopenharmony_ci	struct user_element *ue = kctl->private_data;
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	if (ue->tlv_data_size == 0 || ue->tlv_data == NULL)
13518c2ecf20Sopenharmony_ci		return -ENXIO;
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	if (size < ue->tlv_data_size)
13548c2ecf20Sopenharmony_ci		return -ENOSPC;
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci	if (copy_to_user(buf, ue->tlv_data, ue->tlv_data_size))
13578c2ecf20Sopenharmony_ci		return -EFAULT;
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	return 0;
13608c2ecf20Sopenharmony_ci}
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_cistatic int snd_ctl_elem_user_tlv(struct snd_kcontrol *kctl, int op_flag,
13638c2ecf20Sopenharmony_ci				 unsigned int size, unsigned int __user *buf)
13648c2ecf20Sopenharmony_ci{
13658c2ecf20Sopenharmony_ci	if (op_flag == SNDRV_CTL_TLV_OP_WRITE)
13668c2ecf20Sopenharmony_ci		return replace_user_tlv(kctl, buf, size);
13678c2ecf20Sopenharmony_ci	else
13688c2ecf20Sopenharmony_ci		return read_user_tlv(kctl, buf, size);
13698c2ecf20Sopenharmony_ci}
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_cistatic int snd_ctl_elem_init_enum_names(struct user_element *ue)
13728c2ecf20Sopenharmony_ci{
13738c2ecf20Sopenharmony_ci	char *names, *p;
13748c2ecf20Sopenharmony_ci	size_t buf_len, name_len;
13758c2ecf20Sopenharmony_ci	unsigned int i;
13768c2ecf20Sopenharmony_ci	const uintptr_t user_ptrval = ue->info.value.enumerated.names_ptr;
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	if (ue->info.value.enumerated.names_length > 64 * 1024)
13798c2ecf20Sopenharmony_ci		return -EINVAL;
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_ci	names = vmemdup_user((const void __user *)user_ptrval,
13828c2ecf20Sopenharmony_ci		ue->info.value.enumerated.names_length);
13838c2ecf20Sopenharmony_ci	if (IS_ERR(names))
13848c2ecf20Sopenharmony_ci		return PTR_ERR(names);
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	/* check that there are enough valid names */
13878c2ecf20Sopenharmony_ci	buf_len = ue->info.value.enumerated.names_length;
13888c2ecf20Sopenharmony_ci	p = names;
13898c2ecf20Sopenharmony_ci	for (i = 0; i < ue->info.value.enumerated.items; ++i) {
13908c2ecf20Sopenharmony_ci		name_len = strnlen(p, buf_len);
13918c2ecf20Sopenharmony_ci		if (name_len == 0 || name_len >= 64 || name_len == buf_len) {
13928c2ecf20Sopenharmony_ci			kvfree(names);
13938c2ecf20Sopenharmony_ci			return -EINVAL;
13948c2ecf20Sopenharmony_ci		}
13958c2ecf20Sopenharmony_ci		p += name_len + 1;
13968c2ecf20Sopenharmony_ci		buf_len -= name_len + 1;
13978c2ecf20Sopenharmony_ci	}
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	ue->priv_data = names;
14008c2ecf20Sopenharmony_ci	ue->info.value.enumerated.names_ptr = 0;
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci	return 0;
14038c2ecf20Sopenharmony_ci}
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_cistatic void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
14068c2ecf20Sopenharmony_ci{
14078c2ecf20Sopenharmony_ci	struct user_element *ue = kcontrol->private_data;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	kvfree(ue->tlv_data);
14108c2ecf20Sopenharmony_ci	kvfree(ue->priv_data);
14118c2ecf20Sopenharmony_ci	kfree(ue);
14128c2ecf20Sopenharmony_ci}
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_cistatic int snd_ctl_elem_add(struct snd_ctl_file *file,
14158c2ecf20Sopenharmony_ci			    struct snd_ctl_elem_info *info, int replace)
14168c2ecf20Sopenharmony_ci{
14178c2ecf20Sopenharmony_ci	struct snd_card *card = file->card;
14188c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
14198c2ecf20Sopenharmony_ci	unsigned int count;
14208c2ecf20Sopenharmony_ci	unsigned int access;
14218c2ecf20Sopenharmony_ci	long private_size;
14228c2ecf20Sopenharmony_ci	struct user_element *ue;
14238c2ecf20Sopenharmony_ci	unsigned int offset;
14248c2ecf20Sopenharmony_ci	int err;
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	if (!*info->id.name)
14278c2ecf20Sopenharmony_ci		return -EINVAL;
14288c2ecf20Sopenharmony_ci	if (strnlen(info->id.name, sizeof(info->id.name)) >= sizeof(info->id.name))
14298c2ecf20Sopenharmony_ci		return -EINVAL;
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci	/* Delete a control to replace them if needed. */
14328c2ecf20Sopenharmony_ci	if (replace) {
14338c2ecf20Sopenharmony_ci		info->id.numid = 0;
14348c2ecf20Sopenharmony_ci		err = snd_ctl_remove_user_ctl(file, &info->id);
14358c2ecf20Sopenharmony_ci		if (err)
14368c2ecf20Sopenharmony_ci			return err;
14378c2ecf20Sopenharmony_ci	}
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	/*
14408c2ecf20Sopenharmony_ci	 * The number of userspace controls are counted control by control,
14418c2ecf20Sopenharmony_ci	 * not element by element.
14428c2ecf20Sopenharmony_ci	 */
14438c2ecf20Sopenharmony_ci	if (card->user_ctl_count + 1 > MAX_USER_CONTROLS)
14448c2ecf20Sopenharmony_ci		return -ENOMEM;
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci	/* Check the number of elements for this userspace control. */
14478c2ecf20Sopenharmony_ci	count = info->owner;
14488c2ecf20Sopenharmony_ci	if (count == 0)
14498c2ecf20Sopenharmony_ci		count = 1;
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	/* Arrange access permissions if needed. */
14528c2ecf20Sopenharmony_ci	access = info->access;
14538c2ecf20Sopenharmony_ci	if (access == 0)
14548c2ecf20Sopenharmony_ci		access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
14558c2ecf20Sopenharmony_ci	access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
14568c2ecf20Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_INACTIVE |
14578c2ecf20Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_TLV_WRITE);
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	/* In initial state, nothing is available as TLV container. */
14608c2ecf20Sopenharmony_ci	if (access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
14618c2ecf20Sopenharmony_ci		access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
14628c2ecf20Sopenharmony_ci	access |= SNDRV_CTL_ELEM_ACCESS_USER;
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci	/*
14658c2ecf20Sopenharmony_ci	 * Check information and calculate the size of data specific to
14668c2ecf20Sopenharmony_ci	 * this userspace control.
14678c2ecf20Sopenharmony_ci	 */
14688c2ecf20Sopenharmony_ci	/* pass NULL to card for suppressing error messages */
14698c2ecf20Sopenharmony_ci	err = snd_ctl_check_elem_info(NULL, info);
14708c2ecf20Sopenharmony_ci	if (err < 0)
14718c2ecf20Sopenharmony_ci		return err;
14728c2ecf20Sopenharmony_ci	/* user-space control doesn't allow zero-size data */
14738c2ecf20Sopenharmony_ci	if (info->count < 1)
14748c2ecf20Sopenharmony_ci		return -EINVAL;
14758c2ecf20Sopenharmony_ci	private_size = value_sizes[info->type] * info->count;
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	/*
14788c2ecf20Sopenharmony_ci	 * Keep memory object for this userspace control. After passing this
14798c2ecf20Sopenharmony_ci	 * code block, the instance should be freed by snd_ctl_free_one().
14808c2ecf20Sopenharmony_ci	 *
14818c2ecf20Sopenharmony_ci	 * Note that these elements in this control are locked.
14828c2ecf20Sopenharmony_ci	 */
14838c2ecf20Sopenharmony_ci	err = snd_ctl_new(&kctl, count, access, file);
14848c2ecf20Sopenharmony_ci	if (err < 0)
14858c2ecf20Sopenharmony_ci		return err;
14868c2ecf20Sopenharmony_ci	memcpy(&kctl->id, &info->id, sizeof(kctl->id));
14878c2ecf20Sopenharmony_ci	kctl->private_data = kzalloc(sizeof(struct user_element) + private_size * count,
14888c2ecf20Sopenharmony_ci				     GFP_KERNEL);
14898c2ecf20Sopenharmony_ci	if (kctl->private_data == NULL) {
14908c2ecf20Sopenharmony_ci		kfree(kctl);
14918c2ecf20Sopenharmony_ci		return -ENOMEM;
14928c2ecf20Sopenharmony_ci	}
14938c2ecf20Sopenharmony_ci	kctl->private_free = snd_ctl_elem_user_free;
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	/* Set private data for this userspace control. */
14968c2ecf20Sopenharmony_ci	ue = (struct user_element *)kctl->private_data;
14978c2ecf20Sopenharmony_ci	ue->card = card;
14988c2ecf20Sopenharmony_ci	ue->info = *info;
14998c2ecf20Sopenharmony_ci	ue->info.access = 0;
15008c2ecf20Sopenharmony_ci	ue->elem_data = (char *)ue + sizeof(*ue);
15018c2ecf20Sopenharmony_ci	ue->elem_data_size = private_size;
15028c2ecf20Sopenharmony_ci	if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
15038c2ecf20Sopenharmony_ci		err = snd_ctl_elem_init_enum_names(ue);
15048c2ecf20Sopenharmony_ci		if (err < 0) {
15058c2ecf20Sopenharmony_ci			snd_ctl_free_one(kctl);
15068c2ecf20Sopenharmony_ci			return err;
15078c2ecf20Sopenharmony_ci		}
15088c2ecf20Sopenharmony_ci	}
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	/* Set callback functions. */
15118c2ecf20Sopenharmony_ci	if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED)
15128c2ecf20Sopenharmony_ci		kctl->info = snd_ctl_elem_user_enum_info;
15138c2ecf20Sopenharmony_ci	else
15148c2ecf20Sopenharmony_ci		kctl->info = snd_ctl_elem_user_info;
15158c2ecf20Sopenharmony_ci	if (access & SNDRV_CTL_ELEM_ACCESS_READ)
15168c2ecf20Sopenharmony_ci		kctl->get = snd_ctl_elem_user_get;
15178c2ecf20Sopenharmony_ci	if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
15188c2ecf20Sopenharmony_ci		kctl->put = snd_ctl_elem_user_put;
15198c2ecf20Sopenharmony_ci	if (access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
15208c2ecf20Sopenharmony_ci		kctl->tlv.c = snd_ctl_elem_user_tlv;
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	/* This function manage to free the instance on failure. */
15238c2ecf20Sopenharmony_ci	down_write(&card->controls_rwsem);
15248c2ecf20Sopenharmony_ci	err = __snd_ctl_add_replace(card, kctl, CTL_ADD_EXCLUSIVE);
15258c2ecf20Sopenharmony_ci	if (err < 0) {
15268c2ecf20Sopenharmony_ci		snd_ctl_free_one(kctl);
15278c2ecf20Sopenharmony_ci		goto unlock;
15288c2ecf20Sopenharmony_ci	}
15298c2ecf20Sopenharmony_ci	offset = snd_ctl_get_ioff(kctl, &info->id);
15308c2ecf20Sopenharmony_ci	snd_ctl_build_ioff(&info->id, kctl, offset);
15318c2ecf20Sopenharmony_ci	/*
15328c2ecf20Sopenharmony_ci	 * Here we cannot fill any field for the number of elements added by
15338c2ecf20Sopenharmony_ci	 * this operation because there're no specific fields. The usage of
15348c2ecf20Sopenharmony_ci	 * 'owner' field for this purpose may cause any bugs to userspace
15358c2ecf20Sopenharmony_ci	 * applications because the field originally means PID of a process
15368c2ecf20Sopenharmony_ci	 * which locks the element.
15378c2ecf20Sopenharmony_ci	 */
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	card->user_ctl_count++;
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci unlock:
15428c2ecf20Sopenharmony_ci	up_write(&card->controls_rwsem);
15438c2ecf20Sopenharmony_ci	return err;
15448c2ecf20Sopenharmony_ci}
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_cistatic int snd_ctl_elem_add_user(struct snd_ctl_file *file,
15478c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_info __user *_info, int replace)
15488c2ecf20Sopenharmony_ci{
15498c2ecf20Sopenharmony_ci	struct snd_ctl_elem_info info;
15508c2ecf20Sopenharmony_ci	int err;
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	if (copy_from_user(&info, _info, sizeof(info)))
15538c2ecf20Sopenharmony_ci		return -EFAULT;
15548c2ecf20Sopenharmony_ci	err = snd_ctl_elem_add(file, &info, replace);
15558c2ecf20Sopenharmony_ci	if (err < 0)
15568c2ecf20Sopenharmony_ci		return err;
15578c2ecf20Sopenharmony_ci	if (copy_to_user(_info, &info, sizeof(info))) {
15588c2ecf20Sopenharmony_ci		snd_ctl_remove_user_ctl(file, &info.id);
15598c2ecf20Sopenharmony_ci		return -EFAULT;
15608c2ecf20Sopenharmony_ci	}
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	return 0;
15638c2ecf20Sopenharmony_ci}
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_cistatic int snd_ctl_elem_remove(struct snd_ctl_file *file,
15668c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_id __user *_id)
15678c2ecf20Sopenharmony_ci{
15688c2ecf20Sopenharmony_ci	struct snd_ctl_elem_id id;
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	if (copy_from_user(&id, _id, sizeof(id)))
15718c2ecf20Sopenharmony_ci		return -EFAULT;
15728c2ecf20Sopenharmony_ci	return snd_ctl_remove_user_ctl(file, &id);
15738c2ecf20Sopenharmony_ci}
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_cistatic int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)
15768c2ecf20Sopenharmony_ci{
15778c2ecf20Sopenharmony_ci	int subscribe;
15788c2ecf20Sopenharmony_ci	if (get_user(subscribe, ptr))
15798c2ecf20Sopenharmony_ci		return -EFAULT;
15808c2ecf20Sopenharmony_ci	if (subscribe < 0) {
15818c2ecf20Sopenharmony_ci		subscribe = file->subscribed;
15828c2ecf20Sopenharmony_ci		if (put_user(subscribe, ptr))
15838c2ecf20Sopenharmony_ci			return -EFAULT;
15848c2ecf20Sopenharmony_ci		return 0;
15858c2ecf20Sopenharmony_ci	}
15868c2ecf20Sopenharmony_ci	if (subscribe) {
15878c2ecf20Sopenharmony_ci		file->subscribed = 1;
15888c2ecf20Sopenharmony_ci		return 0;
15898c2ecf20Sopenharmony_ci	} else if (file->subscribed) {
15908c2ecf20Sopenharmony_ci		snd_ctl_empty_read_queue(file);
15918c2ecf20Sopenharmony_ci		file->subscribed = 0;
15928c2ecf20Sopenharmony_ci	}
15938c2ecf20Sopenharmony_ci	return 0;
15948c2ecf20Sopenharmony_ci}
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_cistatic int call_tlv_handler(struct snd_ctl_file *file, int op_flag,
15978c2ecf20Sopenharmony_ci			    struct snd_kcontrol *kctl,
15988c2ecf20Sopenharmony_ci			    struct snd_ctl_elem_id *id,
15998c2ecf20Sopenharmony_ci			    unsigned int __user *buf, unsigned int size)
16008c2ecf20Sopenharmony_ci{
16018c2ecf20Sopenharmony_ci	static const struct {
16028c2ecf20Sopenharmony_ci		int op;
16038c2ecf20Sopenharmony_ci		int perm;
16048c2ecf20Sopenharmony_ci	} pairs[] = {
16058c2ecf20Sopenharmony_ci		{SNDRV_CTL_TLV_OP_READ,  SNDRV_CTL_ELEM_ACCESS_TLV_READ},
16068c2ecf20Sopenharmony_ci		{SNDRV_CTL_TLV_OP_WRITE, SNDRV_CTL_ELEM_ACCESS_TLV_WRITE},
16078c2ecf20Sopenharmony_ci		{SNDRV_CTL_TLV_OP_CMD,   SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND},
16088c2ecf20Sopenharmony_ci	};
16098c2ecf20Sopenharmony_ci	struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)];
16108c2ecf20Sopenharmony_ci	int i;
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci	/* Check support of the request for this element. */
16138c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
16148c2ecf20Sopenharmony_ci		if (op_flag == pairs[i].op && (vd->access & pairs[i].perm))
16158c2ecf20Sopenharmony_ci			break;
16168c2ecf20Sopenharmony_ci	}
16178c2ecf20Sopenharmony_ci	if (i == ARRAY_SIZE(pairs))
16188c2ecf20Sopenharmony_ci		return -ENXIO;
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	if (kctl->tlv.c == NULL)
16218c2ecf20Sopenharmony_ci		return -ENXIO;
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci	/* Write and command operations are not allowed for locked element. */
16248c2ecf20Sopenharmony_ci	if (op_flag != SNDRV_CTL_TLV_OP_READ &&
16258c2ecf20Sopenharmony_ci	    vd->owner != NULL && vd->owner != file)
16268c2ecf20Sopenharmony_ci		return -EPERM;
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	return kctl->tlv.c(kctl, op_flag, size, buf);
16298c2ecf20Sopenharmony_ci}
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_cistatic int read_tlv_buf(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id,
16328c2ecf20Sopenharmony_ci			unsigned int __user *buf, unsigned int size)
16338c2ecf20Sopenharmony_ci{
16348c2ecf20Sopenharmony_ci	struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)];
16358c2ecf20Sopenharmony_ci	unsigned int len;
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ))
16388c2ecf20Sopenharmony_ci		return -ENXIO;
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci	if (kctl->tlv.p == NULL)
16418c2ecf20Sopenharmony_ci		return -ENXIO;
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	len = sizeof(unsigned int) * 2 + kctl->tlv.p[1];
16448c2ecf20Sopenharmony_ci	if (size < len)
16458c2ecf20Sopenharmony_ci		return -ENOMEM;
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	if (copy_to_user(buf, kctl->tlv.p, len))
16488c2ecf20Sopenharmony_ci		return -EFAULT;
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	return 0;
16518c2ecf20Sopenharmony_ci}
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_cistatic int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
16548c2ecf20Sopenharmony_ci			     struct snd_ctl_tlv __user *buf,
16558c2ecf20Sopenharmony_ci                             int op_flag)
16568c2ecf20Sopenharmony_ci{
16578c2ecf20Sopenharmony_ci	struct snd_ctl_tlv header;
16588c2ecf20Sopenharmony_ci	unsigned int __user *container;
16598c2ecf20Sopenharmony_ci	unsigned int container_size;
16608c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
16618c2ecf20Sopenharmony_ci	struct snd_ctl_elem_id id;
16628c2ecf20Sopenharmony_ci	struct snd_kcontrol_volatile *vd;
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ci	if (copy_from_user(&header, buf, sizeof(header)))
16658c2ecf20Sopenharmony_ci		return -EFAULT;
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ci	/* In design of control core, numerical ID starts at 1. */
16688c2ecf20Sopenharmony_ci	if (header.numid == 0)
16698c2ecf20Sopenharmony_ci		return -EINVAL;
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci	/* At least, container should include type and length fields.  */
16728c2ecf20Sopenharmony_ci	if (header.length < sizeof(unsigned int) * 2)
16738c2ecf20Sopenharmony_ci		return -EINVAL;
16748c2ecf20Sopenharmony_ci	container_size = header.length;
16758c2ecf20Sopenharmony_ci	container = buf->tlv;
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci	kctl = snd_ctl_find_numid(file->card, header.numid);
16788c2ecf20Sopenharmony_ci	if (kctl == NULL)
16798c2ecf20Sopenharmony_ci		return -ENOENT;
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	/* Calculate index of the element in this set. */
16828c2ecf20Sopenharmony_ci	id = kctl->id;
16838c2ecf20Sopenharmony_ci	snd_ctl_build_ioff(&id, kctl, header.numid - id.numid);
16848c2ecf20Sopenharmony_ci	vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci	if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
16878c2ecf20Sopenharmony_ci		return call_tlv_handler(file, op_flag, kctl, &id, container,
16888c2ecf20Sopenharmony_ci					container_size);
16898c2ecf20Sopenharmony_ci	} else {
16908c2ecf20Sopenharmony_ci		if (op_flag == SNDRV_CTL_TLV_OP_READ) {
16918c2ecf20Sopenharmony_ci			return read_tlv_buf(kctl, &id, container,
16928c2ecf20Sopenharmony_ci					    container_size);
16938c2ecf20Sopenharmony_ci		}
16948c2ecf20Sopenharmony_ci	}
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci	/* Not supported. */
16978c2ecf20Sopenharmony_ci	return -ENXIO;
16988c2ecf20Sopenharmony_ci}
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_cistatic long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
17018c2ecf20Sopenharmony_ci{
17028c2ecf20Sopenharmony_ci	struct snd_ctl_file *ctl;
17038c2ecf20Sopenharmony_ci	struct snd_card *card;
17048c2ecf20Sopenharmony_ci	struct snd_kctl_ioctl *p;
17058c2ecf20Sopenharmony_ci	void __user *argp = (void __user *)arg;
17068c2ecf20Sopenharmony_ci	int __user *ip = argp;
17078c2ecf20Sopenharmony_ci	int err;
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_ci	ctl = file->private_data;
17108c2ecf20Sopenharmony_ci	card = ctl->card;
17118c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!card))
17128c2ecf20Sopenharmony_ci		return -ENXIO;
17138c2ecf20Sopenharmony_ci	switch (cmd) {
17148c2ecf20Sopenharmony_ci	case SNDRV_CTL_IOCTL_PVERSION:
17158c2ecf20Sopenharmony_ci		return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
17168c2ecf20Sopenharmony_ci	case SNDRV_CTL_IOCTL_CARD_INFO:
17178c2ecf20Sopenharmony_ci		return snd_ctl_card_info(card, ctl, cmd, argp);
17188c2ecf20Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_LIST:
17198c2ecf20Sopenharmony_ci		return snd_ctl_elem_list_user(card, argp);
17208c2ecf20Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_INFO:
17218c2ecf20Sopenharmony_ci		return snd_ctl_elem_info_user(ctl, argp);
17228c2ecf20Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_READ:
17238c2ecf20Sopenharmony_ci		return snd_ctl_elem_read_user(card, argp);
17248c2ecf20Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_WRITE:
17258c2ecf20Sopenharmony_ci		return snd_ctl_elem_write_user(ctl, argp);
17268c2ecf20Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_LOCK:
17278c2ecf20Sopenharmony_ci		return snd_ctl_elem_lock(ctl, argp);
17288c2ecf20Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
17298c2ecf20Sopenharmony_ci		return snd_ctl_elem_unlock(ctl, argp);
17308c2ecf20Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_ADD:
17318c2ecf20Sopenharmony_ci		return snd_ctl_elem_add_user(ctl, argp, 0);
17328c2ecf20Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_REPLACE:
17338c2ecf20Sopenharmony_ci		return snd_ctl_elem_add_user(ctl, argp, 1);
17348c2ecf20Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_REMOVE:
17358c2ecf20Sopenharmony_ci		return snd_ctl_elem_remove(ctl, argp);
17368c2ecf20Sopenharmony_ci	case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
17378c2ecf20Sopenharmony_ci		return snd_ctl_subscribe_events(ctl, ip);
17388c2ecf20Sopenharmony_ci	case SNDRV_CTL_IOCTL_TLV_READ:
17398c2ecf20Sopenharmony_ci		down_read(&ctl->card->controls_rwsem);
17408c2ecf20Sopenharmony_ci		err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ);
17418c2ecf20Sopenharmony_ci		up_read(&ctl->card->controls_rwsem);
17428c2ecf20Sopenharmony_ci		return err;
17438c2ecf20Sopenharmony_ci	case SNDRV_CTL_IOCTL_TLV_WRITE:
17448c2ecf20Sopenharmony_ci		down_write(&ctl->card->controls_rwsem);
17458c2ecf20Sopenharmony_ci		err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE);
17468c2ecf20Sopenharmony_ci		up_write(&ctl->card->controls_rwsem);
17478c2ecf20Sopenharmony_ci		return err;
17488c2ecf20Sopenharmony_ci	case SNDRV_CTL_IOCTL_TLV_COMMAND:
17498c2ecf20Sopenharmony_ci		down_write(&ctl->card->controls_rwsem);
17508c2ecf20Sopenharmony_ci		err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD);
17518c2ecf20Sopenharmony_ci		up_write(&ctl->card->controls_rwsem);
17528c2ecf20Sopenharmony_ci		return err;
17538c2ecf20Sopenharmony_ci	case SNDRV_CTL_IOCTL_POWER:
17548c2ecf20Sopenharmony_ci		return -ENOPROTOOPT;
17558c2ecf20Sopenharmony_ci	case SNDRV_CTL_IOCTL_POWER_STATE:
17568c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
17578c2ecf20Sopenharmony_ci		return put_user(card->power_state, ip) ? -EFAULT : 0;
17588c2ecf20Sopenharmony_ci#else
17598c2ecf20Sopenharmony_ci		return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;
17608c2ecf20Sopenharmony_ci#endif
17618c2ecf20Sopenharmony_ci	}
17628c2ecf20Sopenharmony_ci	down_read(&snd_ioctl_rwsem);
17638c2ecf20Sopenharmony_ci	list_for_each_entry(p, &snd_control_ioctls, list) {
17648c2ecf20Sopenharmony_ci		err = p->fioctl(card, ctl, cmd, arg);
17658c2ecf20Sopenharmony_ci		if (err != -ENOIOCTLCMD) {
17668c2ecf20Sopenharmony_ci			up_read(&snd_ioctl_rwsem);
17678c2ecf20Sopenharmony_ci			return err;
17688c2ecf20Sopenharmony_ci		}
17698c2ecf20Sopenharmony_ci	}
17708c2ecf20Sopenharmony_ci	up_read(&snd_ioctl_rwsem);
17718c2ecf20Sopenharmony_ci	dev_dbg(card->dev, "unknown ioctl = 0x%x\n", cmd);
17728c2ecf20Sopenharmony_ci	return -ENOTTY;
17738c2ecf20Sopenharmony_ci}
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_cistatic ssize_t snd_ctl_read(struct file *file, char __user *buffer,
17768c2ecf20Sopenharmony_ci			    size_t count, loff_t * offset)
17778c2ecf20Sopenharmony_ci{
17788c2ecf20Sopenharmony_ci	struct snd_ctl_file *ctl;
17798c2ecf20Sopenharmony_ci	int err = 0;
17808c2ecf20Sopenharmony_ci	ssize_t result = 0;
17818c2ecf20Sopenharmony_ci
17828c2ecf20Sopenharmony_ci	ctl = file->private_data;
17838c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!ctl || !ctl->card))
17848c2ecf20Sopenharmony_ci		return -ENXIO;
17858c2ecf20Sopenharmony_ci	if (!ctl->subscribed)
17868c2ecf20Sopenharmony_ci		return -EBADFD;
17878c2ecf20Sopenharmony_ci	if (count < sizeof(struct snd_ctl_event))
17888c2ecf20Sopenharmony_ci		return -EINVAL;
17898c2ecf20Sopenharmony_ci	spin_lock_irq(&ctl->read_lock);
17908c2ecf20Sopenharmony_ci	while (count >= sizeof(struct snd_ctl_event)) {
17918c2ecf20Sopenharmony_ci		struct snd_ctl_event ev;
17928c2ecf20Sopenharmony_ci		struct snd_kctl_event *kev;
17938c2ecf20Sopenharmony_ci		while (list_empty(&ctl->events)) {
17948c2ecf20Sopenharmony_ci			wait_queue_entry_t wait;
17958c2ecf20Sopenharmony_ci			if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
17968c2ecf20Sopenharmony_ci				err = -EAGAIN;
17978c2ecf20Sopenharmony_ci				goto __end_lock;
17988c2ecf20Sopenharmony_ci			}
17998c2ecf20Sopenharmony_ci			init_waitqueue_entry(&wait, current);
18008c2ecf20Sopenharmony_ci			add_wait_queue(&ctl->change_sleep, &wait);
18018c2ecf20Sopenharmony_ci			set_current_state(TASK_INTERRUPTIBLE);
18028c2ecf20Sopenharmony_ci			spin_unlock_irq(&ctl->read_lock);
18038c2ecf20Sopenharmony_ci			schedule();
18048c2ecf20Sopenharmony_ci			remove_wait_queue(&ctl->change_sleep, &wait);
18058c2ecf20Sopenharmony_ci			if (ctl->card->shutdown)
18068c2ecf20Sopenharmony_ci				return -ENODEV;
18078c2ecf20Sopenharmony_ci			if (signal_pending(current))
18088c2ecf20Sopenharmony_ci				return -ERESTARTSYS;
18098c2ecf20Sopenharmony_ci			spin_lock_irq(&ctl->read_lock);
18108c2ecf20Sopenharmony_ci		}
18118c2ecf20Sopenharmony_ci		kev = snd_kctl_event(ctl->events.next);
18128c2ecf20Sopenharmony_ci		ev.type = SNDRV_CTL_EVENT_ELEM;
18138c2ecf20Sopenharmony_ci		ev.data.elem.mask = kev->mask;
18148c2ecf20Sopenharmony_ci		ev.data.elem.id = kev->id;
18158c2ecf20Sopenharmony_ci		list_del(&kev->list);
18168c2ecf20Sopenharmony_ci		spin_unlock_irq(&ctl->read_lock);
18178c2ecf20Sopenharmony_ci		kfree(kev);
18188c2ecf20Sopenharmony_ci		if (copy_to_user(buffer, &ev, sizeof(struct snd_ctl_event))) {
18198c2ecf20Sopenharmony_ci			err = -EFAULT;
18208c2ecf20Sopenharmony_ci			goto __end;
18218c2ecf20Sopenharmony_ci		}
18228c2ecf20Sopenharmony_ci		spin_lock_irq(&ctl->read_lock);
18238c2ecf20Sopenharmony_ci		buffer += sizeof(struct snd_ctl_event);
18248c2ecf20Sopenharmony_ci		count -= sizeof(struct snd_ctl_event);
18258c2ecf20Sopenharmony_ci		result += sizeof(struct snd_ctl_event);
18268c2ecf20Sopenharmony_ci	}
18278c2ecf20Sopenharmony_ci      __end_lock:
18288c2ecf20Sopenharmony_ci	spin_unlock_irq(&ctl->read_lock);
18298c2ecf20Sopenharmony_ci      __end:
18308c2ecf20Sopenharmony_ci      	return result > 0 ? result : err;
18318c2ecf20Sopenharmony_ci}
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_cistatic __poll_t snd_ctl_poll(struct file *file, poll_table * wait)
18348c2ecf20Sopenharmony_ci{
18358c2ecf20Sopenharmony_ci	__poll_t mask;
18368c2ecf20Sopenharmony_ci	struct snd_ctl_file *ctl;
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci	ctl = file->private_data;
18398c2ecf20Sopenharmony_ci	if (!ctl->subscribed)
18408c2ecf20Sopenharmony_ci		return 0;
18418c2ecf20Sopenharmony_ci	poll_wait(file, &ctl->change_sleep, wait);
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ci	mask = 0;
18448c2ecf20Sopenharmony_ci	if (!list_empty(&ctl->events))
18458c2ecf20Sopenharmony_ci		mask |= EPOLLIN | EPOLLRDNORM;
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_ci	return mask;
18488c2ecf20Sopenharmony_ci}
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci/*
18518c2ecf20Sopenharmony_ci * register the device-specific control-ioctls.
18528c2ecf20Sopenharmony_ci * called from each device manager like pcm.c, hwdep.c, etc.
18538c2ecf20Sopenharmony_ci */
18548c2ecf20Sopenharmony_cistatic int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *lists)
18558c2ecf20Sopenharmony_ci{
18568c2ecf20Sopenharmony_ci	struct snd_kctl_ioctl *pn;
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_ci	pn = kzalloc(sizeof(struct snd_kctl_ioctl), GFP_KERNEL);
18598c2ecf20Sopenharmony_ci	if (pn == NULL)
18608c2ecf20Sopenharmony_ci		return -ENOMEM;
18618c2ecf20Sopenharmony_ci	pn->fioctl = fcn;
18628c2ecf20Sopenharmony_ci	down_write(&snd_ioctl_rwsem);
18638c2ecf20Sopenharmony_ci	list_add_tail(&pn->list, lists);
18648c2ecf20Sopenharmony_ci	up_write(&snd_ioctl_rwsem);
18658c2ecf20Sopenharmony_ci	return 0;
18668c2ecf20Sopenharmony_ci}
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci/**
18698c2ecf20Sopenharmony_ci * snd_ctl_register_ioctl - register the device-specific control-ioctls
18708c2ecf20Sopenharmony_ci * @fcn: ioctl callback function
18718c2ecf20Sopenharmony_ci *
18728c2ecf20Sopenharmony_ci * called from each device manager like pcm.c, hwdep.c, etc.
18738c2ecf20Sopenharmony_ci */
18748c2ecf20Sopenharmony_ciint snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn)
18758c2ecf20Sopenharmony_ci{
18768c2ecf20Sopenharmony_ci	return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls);
18778c2ecf20Sopenharmony_ci}
18788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_register_ioctl);
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
18818c2ecf20Sopenharmony_ci/**
18828c2ecf20Sopenharmony_ci * snd_ctl_register_ioctl_compat - register the device-specific 32bit compat
18838c2ecf20Sopenharmony_ci * control-ioctls
18848c2ecf20Sopenharmony_ci * @fcn: ioctl callback function
18858c2ecf20Sopenharmony_ci */
18868c2ecf20Sopenharmony_ciint snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn)
18878c2ecf20Sopenharmony_ci{
18888c2ecf20Sopenharmony_ci	return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls);
18898c2ecf20Sopenharmony_ci}
18908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_register_ioctl_compat);
18918c2ecf20Sopenharmony_ci#endif
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_ci/*
18948c2ecf20Sopenharmony_ci * de-register the device-specific control-ioctls.
18958c2ecf20Sopenharmony_ci */
18968c2ecf20Sopenharmony_cistatic int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,
18978c2ecf20Sopenharmony_ci				     struct list_head *lists)
18988c2ecf20Sopenharmony_ci{
18998c2ecf20Sopenharmony_ci	struct snd_kctl_ioctl *p;
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!fcn))
19028c2ecf20Sopenharmony_ci		return -EINVAL;
19038c2ecf20Sopenharmony_ci	down_write(&snd_ioctl_rwsem);
19048c2ecf20Sopenharmony_ci	list_for_each_entry(p, lists, list) {
19058c2ecf20Sopenharmony_ci		if (p->fioctl == fcn) {
19068c2ecf20Sopenharmony_ci			list_del(&p->list);
19078c2ecf20Sopenharmony_ci			up_write(&snd_ioctl_rwsem);
19088c2ecf20Sopenharmony_ci			kfree(p);
19098c2ecf20Sopenharmony_ci			return 0;
19108c2ecf20Sopenharmony_ci		}
19118c2ecf20Sopenharmony_ci	}
19128c2ecf20Sopenharmony_ci	up_write(&snd_ioctl_rwsem);
19138c2ecf20Sopenharmony_ci	snd_BUG();
19148c2ecf20Sopenharmony_ci	return -EINVAL;
19158c2ecf20Sopenharmony_ci}
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci/**
19188c2ecf20Sopenharmony_ci * snd_ctl_unregister_ioctl - de-register the device-specific control-ioctls
19198c2ecf20Sopenharmony_ci * @fcn: ioctl callback function to unregister
19208c2ecf20Sopenharmony_ci */
19218c2ecf20Sopenharmony_ciint snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn)
19228c2ecf20Sopenharmony_ci{
19238c2ecf20Sopenharmony_ci	return _snd_ctl_unregister_ioctl(fcn, &snd_control_ioctls);
19248c2ecf20Sopenharmony_ci}
19258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_unregister_ioctl);
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
19288c2ecf20Sopenharmony_ci/**
19298c2ecf20Sopenharmony_ci * snd_ctl_unregister_ioctl_compat - de-register the device-specific compat
19308c2ecf20Sopenharmony_ci * 32bit control-ioctls
19318c2ecf20Sopenharmony_ci * @fcn: ioctl callback function to unregister
19328c2ecf20Sopenharmony_ci */
19338c2ecf20Sopenharmony_ciint snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn)
19348c2ecf20Sopenharmony_ci{
19358c2ecf20Sopenharmony_ci	return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls);
19368c2ecf20Sopenharmony_ci}
19378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat);
19388c2ecf20Sopenharmony_ci#endif
19398c2ecf20Sopenharmony_ci
19408c2ecf20Sopenharmony_cistatic int snd_ctl_fasync(int fd, struct file * file, int on)
19418c2ecf20Sopenharmony_ci{
19428c2ecf20Sopenharmony_ci	struct snd_ctl_file *ctl;
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_ci	ctl = file->private_data;
19458c2ecf20Sopenharmony_ci	return snd_fasync_helper(fd, file, on, &ctl->fasync);
19468c2ecf20Sopenharmony_ci}
19478c2ecf20Sopenharmony_ci
19488c2ecf20Sopenharmony_ci/* return the preferred subdevice number if already assigned;
19498c2ecf20Sopenharmony_ci * otherwise return -1
19508c2ecf20Sopenharmony_ci */
19518c2ecf20Sopenharmony_ciint snd_ctl_get_preferred_subdevice(struct snd_card *card, int type)
19528c2ecf20Sopenharmony_ci{
19538c2ecf20Sopenharmony_ci	struct snd_ctl_file *kctl;
19548c2ecf20Sopenharmony_ci	int subdevice = -1;
19558c2ecf20Sopenharmony_ci	unsigned long flags;
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci	read_lock_irqsave(&card->ctl_files_rwlock, flags);
19588c2ecf20Sopenharmony_ci	list_for_each_entry(kctl, &card->ctl_files, list) {
19598c2ecf20Sopenharmony_ci		if (kctl->pid == task_pid(current)) {
19608c2ecf20Sopenharmony_ci			subdevice = kctl->preferred_subdevice[type];
19618c2ecf20Sopenharmony_ci			if (subdevice != -1)
19628c2ecf20Sopenharmony_ci				break;
19638c2ecf20Sopenharmony_ci		}
19648c2ecf20Sopenharmony_ci	}
19658c2ecf20Sopenharmony_ci	read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
19668c2ecf20Sopenharmony_ci	return subdevice;
19678c2ecf20Sopenharmony_ci}
19688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_ctl_get_preferred_subdevice);
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci/*
19718c2ecf20Sopenharmony_ci * ioctl32 compat
19728c2ecf20Sopenharmony_ci */
19738c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
19748c2ecf20Sopenharmony_ci#include "control_compat.c"
19758c2ecf20Sopenharmony_ci#else
19768c2ecf20Sopenharmony_ci#define snd_ctl_ioctl_compat	NULL
19778c2ecf20Sopenharmony_ci#endif
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci/*
19808c2ecf20Sopenharmony_ci *  INIT PART
19818c2ecf20Sopenharmony_ci */
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_cistatic const struct file_operations snd_ctl_f_ops =
19848c2ecf20Sopenharmony_ci{
19858c2ecf20Sopenharmony_ci	.owner =	THIS_MODULE,
19868c2ecf20Sopenharmony_ci	.read =		snd_ctl_read,
19878c2ecf20Sopenharmony_ci	.open =		snd_ctl_open,
19888c2ecf20Sopenharmony_ci	.release =	snd_ctl_release,
19898c2ecf20Sopenharmony_ci	.llseek =	no_llseek,
19908c2ecf20Sopenharmony_ci	.poll =		snd_ctl_poll,
19918c2ecf20Sopenharmony_ci	.unlocked_ioctl =	snd_ctl_ioctl,
19928c2ecf20Sopenharmony_ci	.compat_ioctl =	snd_ctl_ioctl_compat,
19938c2ecf20Sopenharmony_ci	.fasync =	snd_ctl_fasync,
19948c2ecf20Sopenharmony_ci};
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci/*
19978c2ecf20Sopenharmony_ci * registration of the control device
19988c2ecf20Sopenharmony_ci */
19998c2ecf20Sopenharmony_cistatic int snd_ctl_dev_register(struct snd_device *device)
20008c2ecf20Sopenharmony_ci{
20018c2ecf20Sopenharmony_ci	struct snd_card *card = device->device_data;
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci	return snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
20048c2ecf20Sopenharmony_ci				   &snd_ctl_f_ops, card, &card->ctl_dev);
20058c2ecf20Sopenharmony_ci}
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci/*
20088c2ecf20Sopenharmony_ci * disconnection of the control device
20098c2ecf20Sopenharmony_ci */
20108c2ecf20Sopenharmony_cistatic int snd_ctl_dev_disconnect(struct snd_device *device)
20118c2ecf20Sopenharmony_ci{
20128c2ecf20Sopenharmony_ci	struct snd_card *card = device->device_data;
20138c2ecf20Sopenharmony_ci	struct snd_ctl_file *ctl;
20148c2ecf20Sopenharmony_ci	unsigned long flags;
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci	read_lock_irqsave(&card->ctl_files_rwlock, flags);
20178c2ecf20Sopenharmony_ci	list_for_each_entry(ctl, &card->ctl_files, list) {
20188c2ecf20Sopenharmony_ci		wake_up(&ctl->change_sleep);
20198c2ecf20Sopenharmony_ci		snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR);
20208c2ecf20Sopenharmony_ci	}
20218c2ecf20Sopenharmony_ci	read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
20228c2ecf20Sopenharmony_ci
20238c2ecf20Sopenharmony_ci	return snd_unregister_device(&card->ctl_dev);
20248c2ecf20Sopenharmony_ci}
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_ci/*
20278c2ecf20Sopenharmony_ci * free all controls
20288c2ecf20Sopenharmony_ci */
20298c2ecf20Sopenharmony_cistatic int snd_ctl_dev_free(struct snd_device *device)
20308c2ecf20Sopenharmony_ci{
20318c2ecf20Sopenharmony_ci	struct snd_card *card = device->device_data;
20328c2ecf20Sopenharmony_ci	struct snd_kcontrol *control;
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci	down_write(&card->controls_rwsem);
20358c2ecf20Sopenharmony_ci	while (!list_empty(&card->controls)) {
20368c2ecf20Sopenharmony_ci		control = snd_kcontrol(card->controls.next);
20378c2ecf20Sopenharmony_ci		snd_ctl_remove(card, control);
20388c2ecf20Sopenharmony_ci	}
20398c2ecf20Sopenharmony_ci	up_write(&card->controls_rwsem);
20408c2ecf20Sopenharmony_ci	put_device(&card->ctl_dev);
20418c2ecf20Sopenharmony_ci	return 0;
20428c2ecf20Sopenharmony_ci}
20438c2ecf20Sopenharmony_ci
20448c2ecf20Sopenharmony_ci/*
20458c2ecf20Sopenharmony_ci * create control core:
20468c2ecf20Sopenharmony_ci * called from init.c
20478c2ecf20Sopenharmony_ci */
20488c2ecf20Sopenharmony_ciint snd_ctl_create(struct snd_card *card)
20498c2ecf20Sopenharmony_ci{
20508c2ecf20Sopenharmony_ci	static const struct snd_device_ops ops = {
20518c2ecf20Sopenharmony_ci		.dev_free = snd_ctl_dev_free,
20528c2ecf20Sopenharmony_ci		.dev_register =	snd_ctl_dev_register,
20538c2ecf20Sopenharmony_ci		.dev_disconnect = snd_ctl_dev_disconnect,
20548c2ecf20Sopenharmony_ci	};
20558c2ecf20Sopenharmony_ci	int err;
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!card))
20588c2ecf20Sopenharmony_ci		return -ENXIO;
20598c2ecf20Sopenharmony_ci	if (snd_BUG_ON(card->number < 0 || card->number >= SNDRV_CARDS))
20608c2ecf20Sopenharmony_ci		return -ENXIO;
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci	snd_device_initialize(&card->ctl_dev, card);
20638c2ecf20Sopenharmony_ci	dev_set_name(&card->ctl_dev, "controlC%d", card->number);
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_ci	err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
20668c2ecf20Sopenharmony_ci	if (err < 0)
20678c2ecf20Sopenharmony_ci		put_device(&card->ctl_dev);
20688c2ecf20Sopenharmony_ci	return err;
20698c2ecf20Sopenharmony_ci}
20708c2ecf20Sopenharmony_ci
20718c2ecf20Sopenharmony_ci/*
20728c2ecf20Sopenharmony_ci * Frequently used control callbacks/helpers
20738c2ecf20Sopenharmony_ci */
20748c2ecf20Sopenharmony_ci
20758c2ecf20Sopenharmony_ci/**
20768c2ecf20Sopenharmony_ci * snd_ctl_boolean_mono_info - Helper function for a standard boolean info
20778c2ecf20Sopenharmony_ci * callback with a mono channel
20788c2ecf20Sopenharmony_ci * @kcontrol: the kcontrol instance
20798c2ecf20Sopenharmony_ci * @uinfo: info to store
20808c2ecf20Sopenharmony_ci *
20818c2ecf20Sopenharmony_ci * This is a function that can be used as info callback for a standard
20828c2ecf20Sopenharmony_ci * boolean control with a single mono channel.
20838c2ecf20Sopenharmony_ci */
20848c2ecf20Sopenharmony_ciint snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
20858c2ecf20Sopenharmony_ci			      struct snd_ctl_elem_info *uinfo)
20868c2ecf20Sopenharmony_ci{
20878c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
20888c2ecf20Sopenharmony_ci	uinfo->count = 1;
20898c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
20908c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 1;
20918c2ecf20Sopenharmony_ci	return 0;
20928c2ecf20Sopenharmony_ci}
20938c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_boolean_mono_info);
20948c2ecf20Sopenharmony_ci
20958c2ecf20Sopenharmony_ci/**
20968c2ecf20Sopenharmony_ci * snd_ctl_boolean_stereo_info - Helper function for a standard boolean info
20978c2ecf20Sopenharmony_ci * callback with stereo two channels
20988c2ecf20Sopenharmony_ci * @kcontrol: the kcontrol instance
20998c2ecf20Sopenharmony_ci * @uinfo: info to store
21008c2ecf20Sopenharmony_ci *
21018c2ecf20Sopenharmony_ci * This is a function that can be used as info callback for a standard
21028c2ecf20Sopenharmony_ci * boolean control with stereo two channels.
21038c2ecf20Sopenharmony_ci */
21048c2ecf20Sopenharmony_ciint snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
21058c2ecf20Sopenharmony_ci				struct snd_ctl_elem_info *uinfo)
21068c2ecf20Sopenharmony_ci{
21078c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
21088c2ecf20Sopenharmony_ci	uinfo->count = 2;
21098c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
21108c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 1;
21118c2ecf20Sopenharmony_ci	return 0;
21128c2ecf20Sopenharmony_ci}
21138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_boolean_stereo_info);
21148c2ecf20Sopenharmony_ci
21158c2ecf20Sopenharmony_ci/**
21168c2ecf20Sopenharmony_ci * snd_ctl_enum_info - fills the info structure for an enumerated control
21178c2ecf20Sopenharmony_ci * @info: the structure to be filled
21188c2ecf20Sopenharmony_ci * @channels: the number of the control's channels; often one
21198c2ecf20Sopenharmony_ci * @items: the number of control values; also the size of @names
21208c2ecf20Sopenharmony_ci * @names: an array containing the names of all control values
21218c2ecf20Sopenharmony_ci *
21228c2ecf20Sopenharmony_ci * Sets all required fields in @info to their appropriate values.
21238c2ecf20Sopenharmony_ci * If the control's accessibility is not the default (readable and writable),
21248c2ecf20Sopenharmony_ci * the caller has to fill @info->access.
21258c2ecf20Sopenharmony_ci *
21268c2ecf20Sopenharmony_ci * Return: Zero.
21278c2ecf20Sopenharmony_ci */
21288c2ecf20Sopenharmony_ciint snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
21298c2ecf20Sopenharmony_ci		      unsigned int items, const char *const names[])
21308c2ecf20Sopenharmony_ci{
21318c2ecf20Sopenharmony_ci	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
21328c2ecf20Sopenharmony_ci	info->count = channels;
21338c2ecf20Sopenharmony_ci	info->value.enumerated.items = items;
21348c2ecf20Sopenharmony_ci	if (!items)
21358c2ecf20Sopenharmony_ci		return 0;
21368c2ecf20Sopenharmony_ci	if (info->value.enumerated.item >= items)
21378c2ecf20Sopenharmony_ci		info->value.enumerated.item = items - 1;
21388c2ecf20Sopenharmony_ci	WARN(strlen(names[info->value.enumerated.item]) >= sizeof(info->value.enumerated.name),
21398c2ecf20Sopenharmony_ci	     "ALSA: too long item name '%s'\n",
21408c2ecf20Sopenharmony_ci	     names[info->value.enumerated.item]);
21418c2ecf20Sopenharmony_ci	strlcpy(info->value.enumerated.name,
21428c2ecf20Sopenharmony_ci		names[info->value.enumerated.item],
21438c2ecf20Sopenharmony_ci		sizeof(info->value.enumerated.name));
21448c2ecf20Sopenharmony_ci	return 0;
21458c2ecf20Sopenharmony_ci}
21468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_enum_info);
2147