162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Routines for driver control interface
462306a36Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/threads.h>
862306a36Sopenharmony_ci#include <linux/interrupt.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/moduleparam.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/vmalloc.h>
1362306a36Sopenharmony_ci#include <linux/time.h>
1462306a36Sopenharmony_ci#include <linux/mm.h>
1562306a36Sopenharmony_ci#include <linux/math64.h>
1662306a36Sopenharmony_ci#include <linux/sched/signal.h>
1762306a36Sopenharmony_ci#include <sound/core.h>
1862306a36Sopenharmony_ci#include <sound/minors.h>
1962306a36Sopenharmony_ci#include <sound/info.h>
2062306a36Sopenharmony_ci#include <sound/control.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci// Max allocation size for user controls.
2362306a36Sopenharmony_cistatic int max_user_ctl_alloc_size = 8 * 1024 * 1024;
2462306a36Sopenharmony_cimodule_param_named(max_user_ctl_alloc_size, max_user_ctl_alloc_size, int, 0444);
2562306a36Sopenharmony_ciMODULE_PARM_DESC(max_user_ctl_alloc_size, "Max allocation size for user controls");
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define MAX_CONTROL_COUNT	1028
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct snd_kctl_ioctl {
3062306a36Sopenharmony_ci	struct list_head list;		/* list of all ioctls */
3162306a36Sopenharmony_ci	snd_kctl_ioctl_func_t fioctl;
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic DECLARE_RWSEM(snd_ioctl_rwsem);
3562306a36Sopenharmony_cistatic DECLARE_RWSEM(snd_ctl_layer_rwsem);
3662306a36Sopenharmony_cistatic LIST_HEAD(snd_control_ioctls);
3762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
3862306a36Sopenharmony_cistatic LIST_HEAD(snd_control_compat_ioctls);
3962306a36Sopenharmony_ci#endif
4062306a36Sopenharmony_cistatic struct snd_ctl_layer_ops *snd_ctl_layer;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic int snd_ctl_remove_locked(struct snd_card *card,
4362306a36Sopenharmony_ci				 struct snd_kcontrol *kcontrol);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int snd_ctl_open(struct inode *inode, struct file *file)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	unsigned long flags;
4862306a36Sopenharmony_ci	struct snd_card *card;
4962306a36Sopenharmony_ci	struct snd_ctl_file *ctl;
5062306a36Sopenharmony_ci	int i, err;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	err = stream_open(inode, file);
5362306a36Sopenharmony_ci	if (err < 0)
5462306a36Sopenharmony_ci		return err;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	card = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_CONTROL);
5762306a36Sopenharmony_ci	if (!card) {
5862306a36Sopenharmony_ci		err = -ENODEV;
5962306a36Sopenharmony_ci		goto __error1;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci	err = snd_card_file_add(card, file);
6262306a36Sopenharmony_ci	if (err < 0) {
6362306a36Sopenharmony_ci		err = -ENODEV;
6462306a36Sopenharmony_ci		goto __error1;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci	if (!try_module_get(card->module)) {
6762306a36Sopenharmony_ci		err = -EFAULT;
6862306a36Sopenharmony_ci		goto __error2;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci	ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
7162306a36Sopenharmony_ci	if (ctl == NULL) {
7262306a36Sopenharmony_ci		err = -ENOMEM;
7362306a36Sopenharmony_ci		goto __error;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci	INIT_LIST_HEAD(&ctl->events);
7662306a36Sopenharmony_ci	init_waitqueue_head(&ctl->change_sleep);
7762306a36Sopenharmony_ci	spin_lock_init(&ctl->read_lock);
7862306a36Sopenharmony_ci	ctl->card = card;
7962306a36Sopenharmony_ci	for (i = 0; i < SND_CTL_SUBDEV_ITEMS; i++)
8062306a36Sopenharmony_ci		ctl->preferred_subdevice[i] = -1;
8162306a36Sopenharmony_ci	ctl->pid = get_pid(task_pid(current));
8262306a36Sopenharmony_ci	file->private_data = ctl;
8362306a36Sopenharmony_ci	write_lock_irqsave(&card->ctl_files_rwlock, flags);
8462306a36Sopenharmony_ci	list_add_tail(&ctl->list, &card->ctl_files);
8562306a36Sopenharmony_ci	write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
8662306a36Sopenharmony_ci	snd_card_unref(card);
8762306a36Sopenharmony_ci	return 0;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci      __error:
9062306a36Sopenharmony_ci	module_put(card->module);
9162306a36Sopenharmony_ci      __error2:
9262306a36Sopenharmony_ci	snd_card_file_remove(card, file);
9362306a36Sopenharmony_ci      __error1:
9462306a36Sopenharmony_ci	if (card)
9562306a36Sopenharmony_ci		snd_card_unref(card);
9662306a36Sopenharmony_ci      	return err;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	unsigned long flags;
10262306a36Sopenharmony_ci	struct snd_kctl_event *cread;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	spin_lock_irqsave(&ctl->read_lock, flags);
10562306a36Sopenharmony_ci	while (!list_empty(&ctl->events)) {
10662306a36Sopenharmony_ci		cread = snd_kctl_event(ctl->events.next);
10762306a36Sopenharmony_ci		list_del(&cread->list);
10862306a36Sopenharmony_ci		kfree(cread);
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci	spin_unlock_irqrestore(&ctl->read_lock, flags);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int snd_ctl_release(struct inode *inode, struct file *file)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	unsigned long flags;
11662306a36Sopenharmony_ci	struct snd_card *card;
11762306a36Sopenharmony_ci	struct snd_ctl_file *ctl;
11862306a36Sopenharmony_ci	struct snd_kcontrol *control;
11962306a36Sopenharmony_ci	unsigned int idx;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	ctl = file->private_data;
12262306a36Sopenharmony_ci	file->private_data = NULL;
12362306a36Sopenharmony_ci	card = ctl->card;
12462306a36Sopenharmony_ci	write_lock_irqsave(&card->ctl_files_rwlock, flags);
12562306a36Sopenharmony_ci	list_del(&ctl->list);
12662306a36Sopenharmony_ci	write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
12762306a36Sopenharmony_ci	down_write(&card->controls_rwsem);
12862306a36Sopenharmony_ci	list_for_each_entry(control, &card->controls, list)
12962306a36Sopenharmony_ci		for (idx = 0; idx < control->count; idx++)
13062306a36Sopenharmony_ci			if (control->vd[idx].owner == ctl)
13162306a36Sopenharmony_ci				control->vd[idx].owner = NULL;
13262306a36Sopenharmony_ci	up_write(&card->controls_rwsem);
13362306a36Sopenharmony_ci	snd_fasync_free(ctl->fasync);
13462306a36Sopenharmony_ci	snd_ctl_empty_read_queue(ctl);
13562306a36Sopenharmony_ci	put_pid(ctl->pid);
13662306a36Sopenharmony_ci	kfree(ctl);
13762306a36Sopenharmony_ci	module_put(card->module);
13862306a36Sopenharmony_ci	snd_card_file_remove(card, file);
13962306a36Sopenharmony_ci	return 0;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/**
14362306a36Sopenharmony_ci * snd_ctl_notify - Send notification to user-space for a control change
14462306a36Sopenharmony_ci * @card: the card to send notification
14562306a36Sopenharmony_ci * @mask: the event mask, SNDRV_CTL_EVENT_*
14662306a36Sopenharmony_ci * @id: the ctl element id to send notification
14762306a36Sopenharmony_ci *
14862306a36Sopenharmony_ci * This function adds an event record with the given id and mask, appends
14962306a36Sopenharmony_ci * to the list and wakes up the user-space for notification.  This can be
15062306a36Sopenharmony_ci * called in the atomic context.
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_civoid snd_ctl_notify(struct snd_card *card, unsigned int mask,
15362306a36Sopenharmony_ci		    struct snd_ctl_elem_id *id)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	unsigned long flags;
15662306a36Sopenharmony_ci	struct snd_ctl_file *ctl;
15762306a36Sopenharmony_ci	struct snd_kctl_event *ev;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (snd_BUG_ON(!card || !id))
16062306a36Sopenharmony_ci		return;
16162306a36Sopenharmony_ci	if (card->shutdown)
16262306a36Sopenharmony_ci		return;
16362306a36Sopenharmony_ci	read_lock_irqsave(&card->ctl_files_rwlock, flags);
16462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
16562306a36Sopenharmony_ci	card->mixer_oss_change_count++;
16662306a36Sopenharmony_ci#endif
16762306a36Sopenharmony_ci	list_for_each_entry(ctl, &card->ctl_files, list) {
16862306a36Sopenharmony_ci		if (!ctl->subscribed)
16962306a36Sopenharmony_ci			continue;
17062306a36Sopenharmony_ci		spin_lock(&ctl->read_lock);
17162306a36Sopenharmony_ci		list_for_each_entry(ev, &ctl->events, list) {
17262306a36Sopenharmony_ci			if (ev->id.numid == id->numid) {
17362306a36Sopenharmony_ci				ev->mask |= mask;
17462306a36Sopenharmony_ci				goto _found;
17562306a36Sopenharmony_ci			}
17662306a36Sopenharmony_ci		}
17762306a36Sopenharmony_ci		ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
17862306a36Sopenharmony_ci		if (ev) {
17962306a36Sopenharmony_ci			ev->id = *id;
18062306a36Sopenharmony_ci			ev->mask = mask;
18162306a36Sopenharmony_ci			list_add_tail(&ev->list, &ctl->events);
18262306a36Sopenharmony_ci		} else {
18362306a36Sopenharmony_ci			dev_err(card->dev, "No memory available to allocate event\n");
18462306a36Sopenharmony_ci		}
18562306a36Sopenharmony_ci	_found:
18662306a36Sopenharmony_ci		wake_up(&ctl->change_sleep);
18762306a36Sopenharmony_ci		spin_unlock(&ctl->read_lock);
18862306a36Sopenharmony_ci		snd_kill_fasync(ctl->fasync, SIGIO, POLL_IN);
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci	read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_notify);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/**
19562306a36Sopenharmony_ci * snd_ctl_notify_one - Send notification to user-space for a control change
19662306a36Sopenharmony_ci * @card: the card to send notification
19762306a36Sopenharmony_ci * @mask: the event mask, SNDRV_CTL_EVENT_*
19862306a36Sopenharmony_ci * @kctl: the pointer with the control instance
19962306a36Sopenharmony_ci * @ioff: the additional offset to the control index
20062306a36Sopenharmony_ci *
20162306a36Sopenharmony_ci * This function calls snd_ctl_notify() and does additional jobs
20262306a36Sopenharmony_ci * like LED state changes.
20362306a36Sopenharmony_ci */
20462306a36Sopenharmony_civoid snd_ctl_notify_one(struct snd_card *card, unsigned int mask,
20562306a36Sopenharmony_ci			struct snd_kcontrol *kctl, unsigned int ioff)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct snd_ctl_elem_id id = kctl->id;
20862306a36Sopenharmony_ci	struct snd_ctl_layer_ops *lops;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	id.index += ioff;
21162306a36Sopenharmony_ci	id.numid += ioff;
21262306a36Sopenharmony_ci	snd_ctl_notify(card, mask, &id);
21362306a36Sopenharmony_ci	down_read(&snd_ctl_layer_rwsem);
21462306a36Sopenharmony_ci	for (lops = snd_ctl_layer; lops; lops = lops->next)
21562306a36Sopenharmony_ci		lops->lnotify(card, mask, kctl, ioff);
21662306a36Sopenharmony_ci	up_read(&snd_ctl_layer_rwsem);
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_notify_one);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci/**
22162306a36Sopenharmony_ci * snd_ctl_new - create a new control instance with some elements
22262306a36Sopenharmony_ci * @kctl: the pointer to store new control instance
22362306a36Sopenharmony_ci * @count: the number of elements in this control
22462306a36Sopenharmony_ci * @access: the default access flags for elements in this control
22562306a36Sopenharmony_ci * @file: given when locking these elements
22662306a36Sopenharmony_ci *
22762306a36Sopenharmony_ci * Allocates a memory object for a new control instance. The instance has
22862306a36Sopenharmony_ci * elements as many as the given number (@count). Each element has given
22962306a36Sopenharmony_ci * access permissions (@access). Each element is locked when @file is given.
23062306a36Sopenharmony_ci *
23162306a36Sopenharmony_ci * Return: 0 on success, error code on failure
23262306a36Sopenharmony_ci */
23362306a36Sopenharmony_cistatic int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count,
23462306a36Sopenharmony_ci		       unsigned int access, struct snd_ctl_file *file)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	unsigned int idx;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (count == 0 || count > MAX_CONTROL_COUNT)
23962306a36Sopenharmony_ci		return -EINVAL;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	*kctl = kzalloc(struct_size(*kctl, vd, count), GFP_KERNEL);
24262306a36Sopenharmony_ci	if (!*kctl)
24362306a36Sopenharmony_ci		return -ENOMEM;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	for (idx = 0; idx < count; idx++) {
24662306a36Sopenharmony_ci		(*kctl)->vd[idx].access = access;
24762306a36Sopenharmony_ci		(*kctl)->vd[idx].owner = file;
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci	(*kctl)->count = count;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return 0;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci/**
25562306a36Sopenharmony_ci * snd_ctl_new1 - create a control instance from the template
25662306a36Sopenharmony_ci * @ncontrol: the initialization record
25762306a36Sopenharmony_ci * @private_data: the private data to set
25862306a36Sopenharmony_ci *
25962306a36Sopenharmony_ci * Allocates a new struct snd_kcontrol instance and initialize from the given
26062306a36Sopenharmony_ci * template.  When the access field of ncontrol is 0, it's assumed as
26162306a36Sopenharmony_ci * READWRITE access. When the count field is 0, it's assumes as one.
26262306a36Sopenharmony_ci *
26362306a36Sopenharmony_ci * Return: The pointer of the newly generated instance, or %NULL on failure.
26462306a36Sopenharmony_ci */
26562306a36Sopenharmony_cistruct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
26662306a36Sopenharmony_ci				  void *private_data)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
26962306a36Sopenharmony_ci	unsigned int count;
27062306a36Sopenharmony_ci	unsigned int access;
27162306a36Sopenharmony_ci	int err;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	if (snd_BUG_ON(!ncontrol || !ncontrol->info))
27462306a36Sopenharmony_ci		return NULL;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	count = ncontrol->count;
27762306a36Sopenharmony_ci	if (count == 0)
27862306a36Sopenharmony_ci		count = 1;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	access = ncontrol->access;
28162306a36Sopenharmony_ci	if (access == 0)
28262306a36Sopenharmony_ci		access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
28362306a36Sopenharmony_ci	access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
28462306a36Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_VOLATILE |
28562306a36Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_INACTIVE |
28662306a36Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE |
28762306a36Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND |
28862306a36Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK |
28962306a36Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_LED_MASK |
29062306a36Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	err = snd_ctl_new(&kctl, count, access, NULL);
29362306a36Sopenharmony_ci	if (err < 0)
29462306a36Sopenharmony_ci		return NULL;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/* The 'numid' member is decided when calling snd_ctl_add(). */
29762306a36Sopenharmony_ci	kctl->id.iface = ncontrol->iface;
29862306a36Sopenharmony_ci	kctl->id.device = ncontrol->device;
29962306a36Sopenharmony_ci	kctl->id.subdevice = ncontrol->subdevice;
30062306a36Sopenharmony_ci	if (ncontrol->name) {
30162306a36Sopenharmony_ci		strscpy(kctl->id.name, ncontrol->name, sizeof(kctl->id.name));
30262306a36Sopenharmony_ci		if (strcmp(ncontrol->name, kctl->id.name) != 0)
30362306a36Sopenharmony_ci			pr_warn("ALSA: Control name '%s' truncated to '%s'\n",
30462306a36Sopenharmony_ci				ncontrol->name, kctl->id.name);
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci	kctl->id.index = ncontrol->index;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	kctl->info = ncontrol->info;
30962306a36Sopenharmony_ci	kctl->get = ncontrol->get;
31062306a36Sopenharmony_ci	kctl->put = ncontrol->put;
31162306a36Sopenharmony_ci	kctl->tlv.p = ncontrol->tlv.p;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	kctl->private_value = ncontrol->private_value;
31462306a36Sopenharmony_ci	kctl->private_data = private_data;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	return kctl;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_new1);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci/**
32162306a36Sopenharmony_ci * snd_ctl_free_one - release the control instance
32262306a36Sopenharmony_ci * @kcontrol: the control instance
32362306a36Sopenharmony_ci *
32462306a36Sopenharmony_ci * Releases the control instance created via snd_ctl_new()
32562306a36Sopenharmony_ci * or snd_ctl_new1().
32662306a36Sopenharmony_ci * Don't call this after the control was added to the card.
32762306a36Sopenharmony_ci */
32862306a36Sopenharmony_civoid snd_ctl_free_one(struct snd_kcontrol *kcontrol)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	if (kcontrol) {
33162306a36Sopenharmony_ci		if (kcontrol->private_free)
33262306a36Sopenharmony_ci			kcontrol->private_free(kcontrol);
33362306a36Sopenharmony_ci		kfree(kcontrol);
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_free_one);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic bool snd_ctl_remove_numid_conflict(struct snd_card *card,
33962306a36Sopenharmony_ci					  unsigned int count)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/* Make sure that the ids assigned to the control do not wrap around */
34462306a36Sopenharmony_ci	if (card->last_numid >= UINT_MAX - count)
34562306a36Sopenharmony_ci		card->last_numid = 0;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	list_for_each_entry(kctl, &card->controls, list) {
34862306a36Sopenharmony_ci		if (kctl->id.numid < card->last_numid + 1 + count &&
34962306a36Sopenharmony_ci		    kctl->id.numid + kctl->count > card->last_numid + 1) {
35062306a36Sopenharmony_ci		    	card->last_numid = kctl->id.numid + kctl->count - 1;
35162306a36Sopenharmony_ci			return true;
35262306a36Sopenharmony_ci		}
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci	return false;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic int snd_ctl_find_hole(struct snd_card *card, unsigned int count)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	unsigned int iter = 100000;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	while (snd_ctl_remove_numid_conflict(card, count)) {
36262306a36Sopenharmony_ci		if (--iter == 0) {
36362306a36Sopenharmony_ci			/* this situation is very unlikely */
36462306a36Sopenharmony_ci			dev_err(card->dev, "unable to allocate new control numid\n");
36562306a36Sopenharmony_ci			return -ENOMEM;
36662306a36Sopenharmony_ci		}
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci	return 0;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci/* check whether the given id is contained in the given kctl */
37262306a36Sopenharmony_cistatic bool elem_id_matches(const struct snd_kcontrol *kctl,
37362306a36Sopenharmony_ci			    const struct snd_ctl_elem_id *id)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	return kctl->id.iface == id->iface &&
37662306a36Sopenharmony_ci		kctl->id.device == id->device &&
37762306a36Sopenharmony_ci		kctl->id.subdevice == id->subdevice &&
37862306a36Sopenharmony_ci		!strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)) &&
37962306a36Sopenharmony_ci		kctl->id.index <= id->index &&
38062306a36Sopenharmony_ci		kctl->id.index + kctl->count > id->index;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci#ifdef CONFIG_SND_CTL_FAST_LOOKUP
38462306a36Sopenharmony_ci/* Compute a hash key for the corresponding ctl id
38562306a36Sopenharmony_ci * It's for the name lookup, hence the numid is excluded.
38662306a36Sopenharmony_ci * The hash key is bound in LONG_MAX to be used for Xarray key.
38762306a36Sopenharmony_ci */
38862306a36Sopenharmony_ci#define MULTIPLIER	37
38962306a36Sopenharmony_cistatic unsigned long get_ctl_id_hash(const struct snd_ctl_elem_id *id)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	int i;
39262306a36Sopenharmony_ci	unsigned long h;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	h = id->iface;
39562306a36Sopenharmony_ci	h = MULTIPLIER * h + id->device;
39662306a36Sopenharmony_ci	h = MULTIPLIER * h + id->subdevice;
39762306a36Sopenharmony_ci	for (i = 0; i < SNDRV_CTL_ELEM_ID_NAME_MAXLEN && id->name[i]; i++)
39862306a36Sopenharmony_ci		h = MULTIPLIER * h + id->name[i];
39962306a36Sopenharmony_ci	h = MULTIPLIER * h + id->index;
40062306a36Sopenharmony_ci	h &= LONG_MAX;
40162306a36Sopenharmony_ci	return h;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci/* add hash entries to numid and ctl xarray tables */
40562306a36Sopenharmony_cistatic void add_hash_entries(struct snd_card *card,
40662306a36Sopenharmony_ci			     struct snd_kcontrol *kcontrol)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct snd_ctl_elem_id id = kcontrol->id;
40962306a36Sopenharmony_ci	int i;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	xa_store_range(&card->ctl_numids, kcontrol->id.numid,
41262306a36Sopenharmony_ci		       kcontrol->id.numid + kcontrol->count - 1,
41362306a36Sopenharmony_ci		       kcontrol, GFP_KERNEL);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	for (i = 0; i < kcontrol->count; i++) {
41662306a36Sopenharmony_ci		id.index = kcontrol->id.index + i;
41762306a36Sopenharmony_ci		if (xa_insert(&card->ctl_hash, get_ctl_id_hash(&id),
41862306a36Sopenharmony_ci			      kcontrol, GFP_KERNEL)) {
41962306a36Sopenharmony_ci			/* skip hash for this entry, noting we had collision */
42062306a36Sopenharmony_ci			card->ctl_hash_collision = true;
42162306a36Sopenharmony_ci			dev_dbg(card->dev, "ctl_hash collision %d:%s:%d\n",
42262306a36Sopenharmony_ci				id.iface, id.name, id.index);
42362306a36Sopenharmony_ci		}
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci/* remove hash entries that have been added */
42862306a36Sopenharmony_cistatic void remove_hash_entries(struct snd_card *card,
42962306a36Sopenharmony_ci				struct snd_kcontrol *kcontrol)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	struct snd_ctl_elem_id id = kcontrol->id;
43262306a36Sopenharmony_ci	struct snd_kcontrol *matched;
43362306a36Sopenharmony_ci	unsigned long h;
43462306a36Sopenharmony_ci	int i;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	for (i = 0; i < kcontrol->count; i++) {
43762306a36Sopenharmony_ci		xa_erase(&card->ctl_numids, id.numid);
43862306a36Sopenharmony_ci		h = get_ctl_id_hash(&id);
43962306a36Sopenharmony_ci		matched = xa_load(&card->ctl_hash, h);
44062306a36Sopenharmony_ci		if (matched && (matched == kcontrol ||
44162306a36Sopenharmony_ci				elem_id_matches(matched, &id)))
44262306a36Sopenharmony_ci			xa_erase(&card->ctl_hash, h);
44362306a36Sopenharmony_ci		id.index++;
44462306a36Sopenharmony_ci		id.numid++;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci#else /* CONFIG_SND_CTL_FAST_LOOKUP */
44862306a36Sopenharmony_cistatic inline void add_hash_entries(struct snd_card *card,
44962306a36Sopenharmony_ci				    struct snd_kcontrol *kcontrol)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_cistatic inline void remove_hash_entries(struct snd_card *card,
45362306a36Sopenharmony_ci				       struct snd_kcontrol *kcontrol)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci#endif /* CONFIG_SND_CTL_FAST_LOOKUP */
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cienum snd_ctl_add_mode {
45962306a36Sopenharmony_ci	CTL_ADD_EXCLUSIVE, CTL_REPLACE, CTL_ADD_ON_REPLACE,
46062306a36Sopenharmony_ci};
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci/* add/replace a new kcontrol object; call with card->controls_rwsem locked */
46362306a36Sopenharmony_cistatic int __snd_ctl_add_replace(struct snd_card *card,
46462306a36Sopenharmony_ci				 struct snd_kcontrol *kcontrol,
46562306a36Sopenharmony_ci				 enum snd_ctl_add_mode mode)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct snd_ctl_elem_id id;
46862306a36Sopenharmony_ci	unsigned int idx;
46962306a36Sopenharmony_ci	struct snd_kcontrol *old;
47062306a36Sopenharmony_ci	int err;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	lockdep_assert_held_write(&card->controls_rwsem);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	id = kcontrol->id;
47562306a36Sopenharmony_ci	if (id.index > UINT_MAX - kcontrol->count)
47662306a36Sopenharmony_ci		return -EINVAL;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	old = snd_ctl_find_id_locked(card, &id);
47962306a36Sopenharmony_ci	if (!old) {
48062306a36Sopenharmony_ci		if (mode == CTL_REPLACE)
48162306a36Sopenharmony_ci			return -EINVAL;
48262306a36Sopenharmony_ci	} else {
48362306a36Sopenharmony_ci		if (mode == CTL_ADD_EXCLUSIVE) {
48462306a36Sopenharmony_ci			dev_err(card->dev,
48562306a36Sopenharmony_ci				"control %i:%i:%i:%s:%i is already present\n",
48662306a36Sopenharmony_ci				id.iface, id.device, id.subdevice, id.name,
48762306a36Sopenharmony_ci				id.index);
48862306a36Sopenharmony_ci			return -EBUSY;
48962306a36Sopenharmony_ci		}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		err = snd_ctl_remove_locked(card, old);
49262306a36Sopenharmony_ci		if (err < 0)
49362306a36Sopenharmony_ci			return err;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (snd_ctl_find_hole(card, kcontrol->count) < 0)
49762306a36Sopenharmony_ci		return -ENOMEM;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	list_add_tail(&kcontrol->list, &card->controls);
50062306a36Sopenharmony_ci	card->controls_count += kcontrol->count;
50162306a36Sopenharmony_ci	kcontrol->id.numid = card->last_numid + 1;
50262306a36Sopenharmony_ci	card->last_numid += kcontrol->count;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	add_hash_entries(card, kcontrol);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	for (idx = 0; idx < kcontrol->count; idx++)
50762306a36Sopenharmony_ci		snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_ADD, kcontrol, idx);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	return 0;
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic int snd_ctl_add_replace(struct snd_card *card,
51362306a36Sopenharmony_ci			       struct snd_kcontrol *kcontrol,
51462306a36Sopenharmony_ci			       enum snd_ctl_add_mode mode)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	int err = -EINVAL;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	if (! kcontrol)
51962306a36Sopenharmony_ci		return err;
52062306a36Sopenharmony_ci	if (snd_BUG_ON(!card || !kcontrol->info))
52162306a36Sopenharmony_ci		goto error;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	down_write(&card->controls_rwsem);
52462306a36Sopenharmony_ci	err = __snd_ctl_add_replace(card, kcontrol, mode);
52562306a36Sopenharmony_ci	up_write(&card->controls_rwsem);
52662306a36Sopenharmony_ci	if (err < 0)
52762306a36Sopenharmony_ci		goto error;
52862306a36Sopenharmony_ci	return 0;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci error:
53162306a36Sopenharmony_ci	snd_ctl_free_one(kcontrol);
53262306a36Sopenharmony_ci	return err;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci/**
53662306a36Sopenharmony_ci * snd_ctl_add - add the control instance to the card
53762306a36Sopenharmony_ci * @card: the card instance
53862306a36Sopenharmony_ci * @kcontrol: the control instance to add
53962306a36Sopenharmony_ci *
54062306a36Sopenharmony_ci * Adds the control instance created via snd_ctl_new() or
54162306a36Sopenharmony_ci * snd_ctl_new1() to the given card. Assigns also an unique
54262306a36Sopenharmony_ci * numid used for fast search.
54362306a36Sopenharmony_ci *
54462306a36Sopenharmony_ci * It frees automatically the control which cannot be added.
54562306a36Sopenharmony_ci *
54662306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure.
54762306a36Sopenharmony_ci *
54862306a36Sopenharmony_ci */
54962306a36Sopenharmony_ciint snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	return snd_ctl_add_replace(card, kcontrol, CTL_ADD_EXCLUSIVE);
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_add);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci/**
55662306a36Sopenharmony_ci * snd_ctl_replace - replace the control instance of the card
55762306a36Sopenharmony_ci * @card: the card instance
55862306a36Sopenharmony_ci * @kcontrol: the control instance to replace
55962306a36Sopenharmony_ci * @add_on_replace: add the control if not already added
56062306a36Sopenharmony_ci *
56162306a36Sopenharmony_ci * Replaces the given control.  If the given control does not exist
56262306a36Sopenharmony_ci * and the add_on_replace flag is set, the control is added.  If the
56362306a36Sopenharmony_ci * control exists, it is destroyed first.
56462306a36Sopenharmony_ci *
56562306a36Sopenharmony_ci * It frees automatically the control which cannot be added or replaced.
56662306a36Sopenharmony_ci *
56762306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure.
56862306a36Sopenharmony_ci */
56962306a36Sopenharmony_ciint snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol,
57062306a36Sopenharmony_ci		    bool add_on_replace)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	return snd_ctl_add_replace(card, kcontrol,
57362306a36Sopenharmony_ci				   add_on_replace ? CTL_ADD_ON_REPLACE : CTL_REPLACE);
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_replace);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic int __snd_ctl_remove(struct snd_card *card,
57862306a36Sopenharmony_ci			    struct snd_kcontrol *kcontrol,
57962306a36Sopenharmony_ci			    bool remove_hash)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	unsigned int idx;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	lockdep_assert_held_write(&card->controls_rwsem);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	if (snd_BUG_ON(!card || !kcontrol))
58662306a36Sopenharmony_ci		return -EINVAL;
58762306a36Sopenharmony_ci	list_del(&kcontrol->list);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	if (remove_hash)
59062306a36Sopenharmony_ci		remove_hash_entries(card, kcontrol);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	card->controls_count -= kcontrol->count;
59362306a36Sopenharmony_ci	for (idx = 0; idx < kcontrol->count; idx++)
59462306a36Sopenharmony_ci		snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_REMOVE, kcontrol, idx);
59562306a36Sopenharmony_ci	snd_ctl_free_one(kcontrol);
59662306a36Sopenharmony_ci	return 0;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic inline int snd_ctl_remove_locked(struct snd_card *card,
60062306a36Sopenharmony_ci					struct snd_kcontrol *kcontrol)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	return __snd_ctl_remove(card, kcontrol, true);
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci/**
60662306a36Sopenharmony_ci * snd_ctl_remove - remove the control from the card and release it
60762306a36Sopenharmony_ci * @card: the card instance
60862306a36Sopenharmony_ci * @kcontrol: the control instance to remove
60962306a36Sopenharmony_ci *
61062306a36Sopenharmony_ci * Removes the control from the card and then releases the instance.
61162306a36Sopenharmony_ci * You don't need to call snd_ctl_free_one().
61262306a36Sopenharmony_ci *
61362306a36Sopenharmony_ci * Return: 0 if successful, or a negative error code on failure.
61462306a36Sopenharmony_ci *
61562306a36Sopenharmony_ci * Note that this function takes card->controls_rwsem lock internally.
61662306a36Sopenharmony_ci */
61762306a36Sopenharmony_ciint snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
61862306a36Sopenharmony_ci{
61962306a36Sopenharmony_ci	int ret;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	down_write(&card->controls_rwsem);
62262306a36Sopenharmony_ci	ret = snd_ctl_remove_locked(card, kcontrol);
62362306a36Sopenharmony_ci	up_write(&card->controls_rwsem);
62462306a36Sopenharmony_ci	return ret;
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_remove);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci/**
62962306a36Sopenharmony_ci * snd_ctl_remove_id - remove the control of the given id and release it
63062306a36Sopenharmony_ci * @card: the card instance
63162306a36Sopenharmony_ci * @id: the control id to remove
63262306a36Sopenharmony_ci *
63362306a36Sopenharmony_ci * Finds the control instance with the given id, removes it from the
63462306a36Sopenharmony_ci * card list and releases it.
63562306a36Sopenharmony_ci *
63662306a36Sopenharmony_ci * Return: 0 if successful, or a negative error code on failure.
63762306a36Sopenharmony_ci */
63862306a36Sopenharmony_ciint snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
63962306a36Sopenharmony_ci{
64062306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
64162306a36Sopenharmony_ci	int ret;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	down_write(&card->controls_rwsem);
64462306a36Sopenharmony_ci	kctl = snd_ctl_find_id_locked(card, id);
64562306a36Sopenharmony_ci	if (kctl == NULL) {
64662306a36Sopenharmony_ci		up_write(&card->controls_rwsem);
64762306a36Sopenharmony_ci		return -ENOENT;
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci	ret = snd_ctl_remove_locked(card, kctl);
65062306a36Sopenharmony_ci	up_write(&card->controls_rwsem);
65162306a36Sopenharmony_ci	return ret;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_remove_id);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci/**
65662306a36Sopenharmony_ci * snd_ctl_remove_user_ctl - remove and release the unlocked user control
65762306a36Sopenharmony_ci * @file: active control handle
65862306a36Sopenharmony_ci * @id: the control id to remove
65962306a36Sopenharmony_ci *
66062306a36Sopenharmony_ci * Finds the control instance with the given id, removes it from the
66162306a36Sopenharmony_ci * card list and releases it.
66262306a36Sopenharmony_ci *
66362306a36Sopenharmony_ci * Return: 0 if successful, or a negative error code on failure.
66462306a36Sopenharmony_ci */
66562306a36Sopenharmony_cistatic int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
66662306a36Sopenharmony_ci				   struct snd_ctl_elem_id *id)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	struct snd_card *card = file->card;
66962306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
67062306a36Sopenharmony_ci	int idx, ret;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	down_write(&card->controls_rwsem);
67362306a36Sopenharmony_ci	kctl = snd_ctl_find_id_locked(card, id);
67462306a36Sopenharmony_ci	if (kctl == NULL) {
67562306a36Sopenharmony_ci		ret = -ENOENT;
67662306a36Sopenharmony_ci		goto error;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci	if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) {
67962306a36Sopenharmony_ci		ret = -EINVAL;
68062306a36Sopenharmony_ci		goto error;
68162306a36Sopenharmony_ci	}
68262306a36Sopenharmony_ci	for (idx = 0; idx < kctl->count; idx++)
68362306a36Sopenharmony_ci		if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) {
68462306a36Sopenharmony_ci			ret = -EBUSY;
68562306a36Sopenharmony_ci			goto error;
68662306a36Sopenharmony_ci		}
68762306a36Sopenharmony_ci	ret = snd_ctl_remove_locked(card, kctl);
68862306a36Sopenharmony_cierror:
68962306a36Sopenharmony_ci	up_write(&card->controls_rwsem);
69062306a36Sopenharmony_ci	return ret;
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci/**
69462306a36Sopenharmony_ci * snd_ctl_activate_id - activate/inactivate the control of the given id
69562306a36Sopenharmony_ci * @card: the card instance
69662306a36Sopenharmony_ci * @id: the control id to activate/inactivate
69762306a36Sopenharmony_ci * @active: non-zero to activate
69862306a36Sopenharmony_ci *
69962306a36Sopenharmony_ci * Finds the control instance with the given id, and activate or
70062306a36Sopenharmony_ci * inactivate the control together with notification, if changed.
70162306a36Sopenharmony_ci * The given ID data is filled with full information.
70262306a36Sopenharmony_ci *
70362306a36Sopenharmony_ci * Return: 0 if unchanged, 1 if changed, or a negative error code on failure.
70462306a36Sopenharmony_ci */
70562306a36Sopenharmony_ciint snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
70662306a36Sopenharmony_ci			int active)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
70962306a36Sopenharmony_ci	struct snd_kcontrol_volatile *vd;
71062306a36Sopenharmony_ci	unsigned int index_offset;
71162306a36Sopenharmony_ci	int ret;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	down_write(&card->controls_rwsem);
71462306a36Sopenharmony_ci	kctl = snd_ctl_find_id_locked(card, id);
71562306a36Sopenharmony_ci	if (kctl == NULL) {
71662306a36Sopenharmony_ci		ret = -ENOENT;
71762306a36Sopenharmony_ci		goto unlock;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci	index_offset = snd_ctl_get_ioff(kctl, id);
72062306a36Sopenharmony_ci	vd = &kctl->vd[index_offset];
72162306a36Sopenharmony_ci	ret = 0;
72262306a36Sopenharmony_ci	if (active) {
72362306a36Sopenharmony_ci		if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE))
72462306a36Sopenharmony_ci			goto unlock;
72562306a36Sopenharmony_ci		vd->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
72662306a36Sopenharmony_ci	} else {
72762306a36Sopenharmony_ci		if (vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)
72862306a36Sopenharmony_ci			goto unlock;
72962306a36Sopenharmony_ci		vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci	snd_ctl_build_ioff(id, kctl, index_offset);
73262306a36Sopenharmony_ci	downgrade_write(&card->controls_rwsem);
73362306a36Sopenharmony_ci	snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_INFO, kctl, index_offset);
73462306a36Sopenharmony_ci	up_read(&card->controls_rwsem);
73562306a36Sopenharmony_ci	return 1;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci unlock:
73862306a36Sopenharmony_ci	up_write(&card->controls_rwsem);
73962306a36Sopenharmony_ci	return ret;
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_ctl_activate_id);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci/**
74462306a36Sopenharmony_ci * snd_ctl_rename_id - replace the id of a control on the card
74562306a36Sopenharmony_ci * @card: the card instance
74662306a36Sopenharmony_ci * @src_id: the old id
74762306a36Sopenharmony_ci * @dst_id: the new id
74862306a36Sopenharmony_ci *
74962306a36Sopenharmony_ci * Finds the control with the old id from the card, and replaces the
75062306a36Sopenharmony_ci * id with the new one.
75162306a36Sopenharmony_ci *
75262306a36Sopenharmony_ci * The function tries to keep the already assigned numid while replacing
75362306a36Sopenharmony_ci * the rest.
75462306a36Sopenharmony_ci *
75562306a36Sopenharmony_ci * Note that this function should be used only in the card initialization
75662306a36Sopenharmony_ci * phase.  Calling after the card instantiation may cause issues with
75762306a36Sopenharmony_ci * user-space expecting persistent numids.
75862306a36Sopenharmony_ci *
75962306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure.
76062306a36Sopenharmony_ci */
76162306a36Sopenharmony_ciint snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
76262306a36Sopenharmony_ci		      struct snd_ctl_elem_id *dst_id)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
76562306a36Sopenharmony_ci	int saved_numid;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	down_write(&card->controls_rwsem);
76862306a36Sopenharmony_ci	kctl = snd_ctl_find_id_locked(card, src_id);
76962306a36Sopenharmony_ci	if (kctl == NULL) {
77062306a36Sopenharmony_ci		up_write(&card->controls_rwsem);
77162306a36Sopenharmony_ci		return -ENOENT;
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci	saved_numid = kctl->id.numid;
77462306a36Sopenharmony_ci	remove_hash_entries(card, kctl);
77562306a36Sopenharmony_ci	kctl->id = *dst_id;
77662306a36Sopenharmony_ci	kctl->id.numid = saved_numid;
77762306a36Sopenharmony_ci	add_hash_entries(card, kctl);
77862306a36Sopenharmony_ci	up_write(&card->controls_rwsem);
77962306a36Sopenharmony_ci	return 0;
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_rename_id);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci/**
78462306a36Sopenharmony_ci * snd_ctl_rename - rename the control on the card
78562306a36Sopenharmony_ci * @card: the card instance
78662306a36Sopenharmony_ci * @kctl: the control to rename
78762306a36Sopenharmony_ci * @name: the new name
78862306a36Sopenharmony_ci *
78962306a36Sopenharmony_ci * Renames the specified control on the card to the new name.
79062306a36Sopenharmony_ci *
79162306a36Sopenharmony_ci * Note that this function takes card->controls_rwsem lock internally.
79262306a36Sopenharmony_ci */
79362306a36Sopenharmony_civoid snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl,
79462306a36Sopenharmony_ci		    const char *name)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	down_write(&card->controls_rwsem);
79762306a36Sopenharmony_ci	remove_hash_entries(card, kctl);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	if (strscpy(kctl->id.name, name, sizeof(kctl->id.name)) < 0)
80062306a36Sopenharmony_ci		pr_warn("ALSA: Renamed control new name '%s' truncated to '%s'\n",
80162306a36Sopenharmony_ci			name, kctl->id.name);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	add_hash_entries(card, kctl);
80462306a36Sopenharmony_ci	up_write(&card->controls_rwsem);
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_rename);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci#ifndef CONFIG_SND_CTL_FAST_LOOKUP
80962306a36Sopenharmony_cistatic struct snd_kcontrol *
81062306a36Sopenharmony_cisnd_ctl_find_numid_slow(struct snd_card *card, unsigned int numid)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	list_for_each_entry(kctl, &card->controls, list) {
81562306a36Sopenharmony_ci		if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
81662306a36Sopenharmony_ci			return kctl;
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci	return NULL;
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci#endif /* !CONFIG_SND_CTL_FAST_LOOKUP */
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci/**
82362306a36Sopenharmony_ci * snd_ctl_find_numid_locked - find the control instance with the given number-id
82462306a36Sopenharmony_ci * @card: the card instance
82562306a36Sopenharmony_ci * @numid: the number-id to search
82662306a36Sopenharmony_ci *
82762306a36Sopenharmony_ci * Finds the control instance with the given number-id from the card.
82862306a36Sopenharmony_ci *
82962306a36Sopenharmony_ci * The caller must down card->controls_rwsem before calling this function
83062306a36Sopenharmony_ci * (if the race condition can happen).
83162306a36Sopenharmony_ci *
83262306a36Sopenharmony_ci * Return: The pointer of the instance if found, or %NULL if not.
83362306a36Sopenharmony_ci */
83462306a36Sopenharmony_cistruct snd_kcontrol *
83562306a36Sopenharmony_cisnd_ctl_find_numid_locked(struct snd_card *card, unsigned int numid)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	if (snd_BUG_ON(!card || !numid))
83862306a36Sopenharmony_ci		return NULL;
83962306a36Sopenharmony_ci	lockdep_assert_held(&card->controls_rwsem);
84062306a36Sopenharmony_ci#ifdef CONFIG_SND_CTL_FAST_LOOKUP
84162306a36Sopenharmony_ci	return xa_load(&card->ctl_numids, numid);
84262306a36Sopenharmony_ci#else
84362306a36Sopenharmony_ci	return snd_ctl_find_numid_slow(card, numid);
84462306a36Sopenharmony_ci#endif
84562306a36Sopenharmony_ci}
84662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_find_numid_locked);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci/**
84962306a36Sopenharmony_ci * snd_ctl_find_numid - find the control instance with the given number-id
85062306a36Sopenharmony_ci * @card: the card instance
85162306a36Sopenharmony_ci * @numid: the number-id to search
85262306a36Sopenharmony_ci *
85362306a36Sopenharmony_ci * Finds the control instance with the given number-id from the card.
85462306a36Sopenharmony_ci *
85562306a36Sopenharmony_ci * Return: The pointer of the instance if found, or %NULL if not.
85662306a36Sopenharmony_ci *
85762306a36Sopenharmony_ci * Note that this function takes card->controls_rwsem lock internally.
85862306a36Sopenharmony_ci */
85962306a36Sopenharmony_cistruct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card,
86062306a36Sopenharmony_ci					unsigned int numid)
86162306a36Sopenharmony_ci{
86262306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	down_read(&card->controls_rwsem);
86562306a36Sopenharmony_ci	kctl = snd_ctl_find_numid_locked(card, numid);
86662306a36Sopenharmony_ci	up_read(&card->controls_rwsem);
86762306a36Sopenharmony_ci	return kctl;
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_find_numid);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci/**
87262306a36Sopenharmony_ci * snd_ctl_find_id_locked - find the control instance with the given id
87362306a36Sopenharmony_ci * @card: the card instance
87462306a36Sopenharmony_ci * @id: the id to search
87562306a36Sopenharmony_ci *
87662306a36Sopenharmony_ci * Finds the control instance with the given id from the card.
87762306a36Sopenharmony_ci *
87862306a36Sopenharmony_ci * The caller must down card->controls_rwsem before calling this function
87962306a36Sopenharmony_ci * (if the race condition can happen).
88062306a36Sopenharmony_ci *
88162306a36Sopenharmony_ci * Return: The pointer of the instance if found, or %NULL if not.
88262306a36Sopenharmony_ci */
88362306a36Sopenharmony_cistruct snd_kcontrol *snd_ctl_find_id_locked(struct snd_card *card,
88462306a36Sopenharmony_ci					    const struct snd_ctl_elem_id *id)
88562306a36Sopenharmony_ci{
88662306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	if (snd_BUG_ON(!card || !id))
88962306a36Sopenharmony_ci		return NULL;
89062306a36Sopenharmony_ci	lockdep_assert_held(&card->controls_rwsem);
89162306a36Sopenharmony_ci	if (id->numid != 0)
89262306a36Sopenharmony_ci		return snd_ctl_find_numid_locked(card, id->numid);
89362306a36Sopenharmony_ci#ifdef CONFIG_SND_CTL_FAST_LOOKUP
89462306a36Sopenharmony_ci	kctl = xa_load(&card->ctl_hash, get_ctl_id_hash(id));
89562306a36Sopenharmony_ci	if (kctl && elem_id_matches(kctl, id))
89662306a36Sopenharmony_ci		return kctl;
89762306a36Sopenharmony_ci	if (!card->ctl_hash_collision)
89862306a36Sopenharmony_ci		return NULL; /* we can rely on only hash table */
89962306a36Sopenharmony_ci#endif
90062306a36Sopenharmony_ci	/* no matching in hash table - try all as the last resort */
90162306a36Sopenharmony_ci	list_for_each_entry(kctl, &card->controls, list)
90262306a36Sopenharmony_ci		if (elem_id_matches(kctl, id))
90362306a36Sopenharmony_ci			return kctl;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	return NULL;
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_find_id_locked);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci/**
91062306a36Sopenharmony_ci * snd_ctl_find_id - find the control instance with the given id
91162306a36Sopenharmony_ci * @card: the card instance
91262306a36Sopenharmony_ci * @id: the id to search
91362306a36Sopenharmony_ci *
91462306a36Sopenharmony_ci * Finds the control instance with the given id from the card.
91562306a36Sopenharmony_ci *
91662306a36Sopenharmony_ci * Return: The pointer of the instance if found, or %NULL if not.
91762306a36Sopenharmony_ci *
91862306a36Sopenharmony_ci * Note that this function takes card->controls_rwsem lock internally.
91962306a36Sopenharmony_ci */
92062306a36Sopenharmony_cistruct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
92162306a36Sopenharmony_ci				     const struct snd_ctl_elem_id *id)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	down_read(&card->controls_rwsem);
92662306a36Sopenharmony_ci	kctl = snd_ctl_find_id_locked(card, id);
92762306a36Sopenharmony_ci	up_read(&card->controls_rwsem);
92862306a36Sopenharmony_ci	return kctl;
92962306a36Sopenharmony_ci}
93062306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_find_id);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_cistatic int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
93362306a36Sopenharmony_ci			     unsigned int cmd, void __user *arg)
93462306a36Sopenharmony_ci{
93562306a36Sopenharmony_ci	struct snd_ctl_card_info *info;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	info = kzalloc(sizeof(*info), GFP_KERNEL);
93862306a36Sopenharmony_ci	if (! info)
93962306a36Sopenharmony_ci		return -ENOMEM;
94062306a36Sopenharmony_ci	down_read(&snd_ioctl_rwsem);
94162306a36Sopenharmony_ci	info->card = card->number;
94262306a36Sopenharmony_ci	strscpy(info->id, card->id, sizeof(info->id));
94362306a36Sopenharmony_ci	strscpy(info->driver, card->driver, sizeof(info->driver));
94462306a36Sopenharmony_ci	strscpy(info->name, card->shortname, sizeof(info->name));
94562306a36Sopenharmony_ci	strscpy(info->longname, card->longname, sizeof(info->longname));
94662306a36Sopenharmony_ci	strscpy(info->mixername, card->mixername, sizeof(info->mixername));
94762306a36Sopenharmony_ci	strscpy(info->components, card->components, sizeof(info->components));
94862306a36Sopenharmony_ci	up_read(&snd_ioctl_rwsem);
94962306a36Sopenharmony_ci	if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) {
95062306a36Sopenharmony_ci		kfree(info);
95162306a36Sopenharmony_ci		return -EFAULT;
95262306a36Sopenharmony_ci	}
95362306a36Sopenharmony_ci	kfree(info);
95462306a36Sopenharmony_ci	return 0;
95562306a36Sopenharmony_ci}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_cistatic int snd_ctl_elem_list(struct snd_card *card,
95862306a36Sopenharmony_ci			     struct snd_ctl_elem_list *list)
95962306a36Sopenharmony_ci{
96062306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
96162306a36Sopenharmony_ci	struct snd_ctl_elem_id id;
96262306a36Sopenharmony_ci	unsigned int offset, space, jidx;
96362306a36Sopenharmony_ci	int err = 0;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	offset = list->offset;
96662306a36Sopenharmony_ci	space = list->space;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	down_read(&card->controls_rwsem);
96962306a36Sopenharmony_ci	list->count = card->controls_count;
97062306a36Sopenharmony_ci	list->used = 0;
97162306a36Sopenharmony_ci	if (space > 0) {
97262306a36Sopenharmony_ci		list_for_each_entry(kctl, &card->controls, list) {
97362306a36Sopenharmony_ci			if (offset >= kctl->count) {
97462306a36Sopenharmony_ci				offset -= kctl->count;
97562306a36Sopenharmony_ci				continue;
97662306a36Sopenharmony_ci			}
97762306a36Sopenharmony_ci			for (jidx = offset; jidx < kctl->count; jidx++) {
97862306a36Sopenharmony_ci				snd_ctl_build_ioff(&id, kctl, jidx);
97962306a36Sopenharmony_ci				if (copy_to_user(list->pids + list->used, &id,
98062306a36Sopenharmony_ci						 sizeof(id))) {
98162306a36Sopenharmony_ci					err = -EFAULT;
98262306a36Sopenharmony_ci					goto out;
98362306a36Sopenharmony_ci				}
98462306a36Sopenharmony_ci				list->used++;
98562306a36Sopenharmony_ci				if (!--space)
98662306a36Sopenharmony_ci					goto out;
98762306a36Sopenharmony_ci			}
98862306a36Sopenharmony_ci			offset = 0;
98962306a36Sopenharmony_ci		}
99062306a36Sopenharmony_ci	}
99162306a36Sopenharmony_ci out:
99262306a36Sopenharmony_ci	up_read(&card->controls_rwsem);
99362306a36Sopenharmony_ci	return err;
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_cistatic int snd_ctl_elem_list_user(struct snd_card *card,
99762306a36Sopenharmony_ci				  struct snd_ctl_elem_list __user *_list)
99862306a36Sopenharmony_ci{
99962306a36Sopenharmony_ci	struct snd_ctl_elem_list list;
100062306a36Sopenharmony_ci	int err;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	if (copy_from_user(&list, _list, sizeof(list)))
100362306a36Sopenharmony_ci		return -EFAULT;
100462306a36Sopenharmony_ci	err = snd_ctl_elem_list(card, &list);
100562306a36Sopenharmony_ci	if (err)
100662306a36Sopenharmony_ci		return err;
100762306a36Sopenharmony_ci	if (copy_to_user(_list, &list, sizeof(list)))
100862306a36Sopenharmony_ci		return -EFAULT;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	return 0;
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci/* Check whether the given kctl info is valid */
101462306a36Sopenharmony_cistatic int snd_ctl_check_elem_info(struct snd_card *card,
101562306a36Sopenharmony_ci				   const struct snd_ctl_elem_info *info)
101662306a36Sopenharmony_ci{
101762306a36Sopenharmony_ci	static const unsigned int max_value_counts[] = {
101862306a36Sopenharmony_ci		[SNDRV_CTL_ELEM_TYPE_BOOLEAN]	= 128,
101962306a36Sopenharmony_ci		[SNDRV_CTL_ELEM_TYPE_INTEGER]	= 128,
102062306a36Sopenharmony_ci		[SNDRV_CTL_ELEM_TYPE_ENUMERATED] = 128,
102162306a36Sopenharmony_ci		[SNDRV_CTL_ELEM_TYPE_BYTES]	= 512,
102262306a36Sopenharmony_ci		[SNDRV_CTL_ELEM_TYPE_IEC958]	= 1,
102362306a36Sopenharmony_ci		[SNDRV_CTL_ELEM_TYPE_INTEGER64] = 64,
102462306a36Sopenharmony_ci	};
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
102762306a36Sopenharmony_ci	    info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64) {
102862306a36Sopenharmony_ci		if (card)
102962306a36Sopenharmony_ci			dev_err(card->dev,
103062306a36Sopenharmony_ci				"control %i:%i:%i:%s:%i: invalid type %d\n",
103162306a36Sopenharmony_ci				info->id.iface, info->id.device,
103262306a36Sopenharmony_ci				info->id.subdevice, info->id.name,
103362306a36Sopenharmony_ci				info->id.index, info->type);
103462306a36Sopenharmony_ci		return -EINVAL;
103562306a36Sopenharmony_ci	}
103662306a36Sopenharmony_ci	if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED &&
103762306a36Sopenharmony_ci	    info->value.enumerated.items == 0) {
103862306a36Sopenharmony_ci		if (card)
103962306a36Sopenharmony_ci			dev_err(card->dev,
104062306a36Sopenharmony_ci				"control %i:%i:%i:%s:%i: zero enum items\n",
104162306a36Sopenharmony_ci				info->id.iface, info->id.device,
104262306a36Sopenharmony_ci				info->id.subdevice, info->id.name,
104362306a36Sopenharmony_ci				info->id.index);
104462306a36Sopenharmony_ci		return -EINVAL;
104562306a36Sopenharmony_ci	}
104662306a36Sopenharmony_ci	if (info->count > max_value_counts[info->type]) {
104762306a36Sopenharmony_ci		if (card)
104862306a36Sopenharmony_ci			dev_err(card->dev,
104962306a36Sopenharmony_ci				"control %i:%i:%i:%s:%i: invalid count %d\n",
105062306a36Sopenharmony_ci				info->id.iface, info->id.device,
105162306a36Sopenharmony_ci				info->id.subdevice, info->id.name,
105262306a36Sopenharmony_ci				info->id.index, info->count);
105362306a36Sopenharmony_ci		return -EINVAL;
105462306a36Sopenharmony_ci	}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	return 0;
105762306a36Sopenharmony_ci}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci/* The capacity of struct snd_ctl_elem_value.value.*/
106062306a36Sopenharmony_cistatic const unsigned int value_sizes[] = {
106162306a36Sopenharmony_ci	[SNDRV_CTL_ELEM_TYPE_BOOLEAN]	= sizeof(long),
106262306a36Sopenharmony_ci	[SNDRV_CTL_ELEM_TYPE_INTEGER]	= sizeof(long),
106362306a36Sopenharmony_ci	[SNDRV_CTL_ELEM_TYPE_ENUMERATED] = sizeof(unsigned int),
106462306a36Sopenharmony_ci	[SNDRV_CTL_ELEM_TYPE_BYTES]	= sizeof(unsigned char),
106562306a36Sopenharmony_ci	[SNDRV_CTL_ELEM_TYPE_IEC958]	= sizeof(struct snd_aes_iec958),
106662306a36Sopenharmony_ci	[SNDRV_CTL_ELEM_TYPE_INTEGER64] = sizeof(long long),
106762306a36Sopenharmony_ci};
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci/* fill the remaining snd_ctl_elem_value data with the given pattern */
107062306a36Sopenharmony_cistatic void fill_remaining_elem_value(struct snd_ctl_elem_value *control,
107162306a36Sopenharmony_ci				      struct snd_ctl_elem_info *info,
107262306a36Sopenharmony_ci				      u32 pattern)
107362306a36Sopenharmony_ci{
107462306a36Sopenharmony_ci	size_t offset = value_sizes[info->type] * info->count;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	offset = DIV_ROUND_UP(offset, sizeof(u32));
107762306a36Sopenharmony_ci	memset32((u32 *)control->value.bytes.data + offset, pattern,
107862306a36Sopenharmony_ci		 sizeof(control->value) / sizeof(u32) - offset);
107962306a36Sopenharmony_ci}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci/* check whether the given integer ctl value is valid */
108262306a36Sopenharmony_cistatic int sanity_check_int_value(struct snd_card *card,
108362306a36Sopenharmony_ci				  const struct snd_ctl_elem_value *control,
108462306a36Sopenharmony_ci				  const struct snd_ctl_elem_info *info,
108562306a36Sopenharmony_ci				  int i, bool print_error)
108662306a36Sopenharmony_ci{
108762306a36Sopenharmony_ci	long long lval, lmin, lmax, lstep;
108862306a36Sopenharmony_ci	u64 rem;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	switch (info->type) {
109162306a36Sopenharmony_ci	default:
109262306a36Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
109362306a36Sopenharmony_ci		lval = control->value.integer.value[i];
109462306a36Sopenharmony_ci		lmin = 0;
109562306a36Sopenharmony_ci		lmax = 1;
109662306a36Sopenharmony_ci		lstep = 0;
109762306a36Sopenharmony_ci		break;
109862306a36Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_INTEGER:
109962306a36Sopenharmony_ci		lval = control->value.integer.value[i];
110062306a36Sopenharmony_ci		lmin = info->value.integer.min;
110162306a36Sopenharmony_ci		lmax = info->value.integer.max;
110262306a36Sopenharmony_ci		lstep = info->value.integer.step;
110362306a36Sopenharmony_ci		break;
110462306a36Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_INTEGER64:
110562306a36Sopenharmony_ci		lval = control->value.integer64.value[i];
110662306a36Sopenharmony_ci		lmin = info->value.integer64.min;
110762306a36Sopenharmony_ci		lmax = info->value.integer64.max;
110862306a36Sopenharmony_ci		lstep = info->value.integer64.step;
110962306a36Sopenharmony_ci		break;
111062306a36Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
111162306a36Sopenharmony_ci		lval = control->value.enumerated.item[i];
111262306a36Sopenharmony_ci		lmin = 0;
111362306a36Sopenharmony_ci		lmax = info->value.enumerated.items - 1;
111462306a36Sopenharmony_ci		lstep = 0;
111562306a36Sopenharmony_ci		break;
111662306a36Sopenharmony_ci	}
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	if (lval < lmin || lval > lmax) {
111962306a36Sopenharmony_ci		if (print_error)
112062306a36Sopenharmony_ci			dev_err(card->dev,
112162306a36Sopenharmony_ci				"control %i:%i:%i:%s:%i: value out of range %lld (%lld/%lld) at count %i\n",
112262306a36Sopenharmony_ci				control->id.iface, control->id.device,
112362306a36Sopenharmony_ci				control->id.subdevice, control->id.name,
112462306a36Sopenharmony_ci				control->id.index, lval, lmin, lmax, i);
112562306a36Sopenharmony_ci		return -EINVAL;
112662306a36Sopenharmony_ci	}
112762306a36Sopenharmony_ci	if (lstep) {
112862306a36Sopenharmony_ci		div64_u64_rem(lval, lstep, &rem);
112962306a36Sopenharmony_ci		if (rem) {
113062306a36Sopenharmony_ci			if (print_error)
113162306a36Sopenharmony_ci				dev_err(card->dev,
113262306a36Sopenharmony_ci					"control %i:%i:%i:%s:%i: unaligned value %lld (step %lld) at count %i\n",
113362306a36Sopenharmony_ci					control->id.iface, control->id.device,
113462306a36Sopenharmony_ci					control->id.subdevice, control->id.name,
113562306a36Sopenharmony_ci					control->id.index, lval, lstep, i);
113662306a36Sopenharmony_ci			return -EINVAL;
113762306a36Sopenharmony_ci		}
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	return 0;
114162306a36Sopenharmony_ci}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci/* check whether the all input values are valid for the given elem value */
114462306a36Sopenharmony_cistatic int sanity_check_input_values(struct snd_card *card,
114562306a36Sopenharmony_ci				     const struct snd_ctl_elem_value *control,
114662306a36Sopenharmony_ci				     const struct snd_ctl_elem_info *info,
114762306a36Sopenharmony_ci				     bool print_error)
114862306a36Sopenharmony_ci{
114962306a36Sopenharmony_ci	int i, ret;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	switch (info->type) {
115262306a36Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
115362306a36Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_INTEGER:
115462306a36Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_INTEGER64:
115562306a36Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
115662306a36Sopenharmony_ci		for (i = 0; i < info->count; i++) {
115762306a36Sopenharmony_ci			ret = sanity_check_int_value(card, control, info, i,
115862306a36Sopenharmony_ci						     print_error);
115962306a36Sopenharmony_ci			if (ret < 0)
116062306a36Sopenharmony_ci				return ret;
116162306a36Sopenharmony_ci		}
116262306a36Sopenharmony_ci		break;
116362306a36Sopenharmony_ci	default:
116462306a36Sopenharmony_ci		break;
116562306a36Sopenharmony_ci	}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	return 0;
116862306a36Sopenharmony_ci}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci/* perform sanity checks to the given snd_ctl_elem_value object */
117162306a36Sopenharmony_cistatic int sanity_check_elem_value(struct snd_card *card,
117262306a36Sopenharmony_ci				   const struct snd_ctl_elem_value *control,
117362306a36Sopenharmony_ci				   const struct snd_ctl_elem_info *info,
117462306a36Sopenharmony_ci				   u32 pattern)
117562306a36Sopenharmony_ci{
117662306a36Sopenharmony_ci	size_t offset;
117762306a36Sopenharmony_ci	int ret;
117862306a36Sopenharmony_ci	u32 *p;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	ret = sanity_check_input_values(card, control, info, true);
118162306a36Sopenharmony_ci	if (ret < 0)
118262306a36Sopenharmony_ci		return ret;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	/* check whether the remaining area kept untouched */
118562306a36Sopenharmony_ci	offset = value_sizes[info->type] * info->count;
118662306a36Sopenharmony_ci	offset = DIV_ROUND_UP(offset, sizeof(u32));
118762306a36Sopenharmony_ci	p = (u32 *)control->value.bytes.data + offset;
118862306a36Sopenharmony_ci	for (; offset < sizeof(control->value) / sizeof(u32); offset++, p++) {
118962306a36Sopenharmony_ci		if (*p != pattern) {
119062306a36Sopenharmony_ci			ret = -EINVAL;
119162306a36Sopenharmony_ci			break;
119262306a36Sopenharmony_ci		}
119362306a36Sopenharmony_ci		*p = 0; /* clear the checked area */
119462306a36Sopenharmony_ci	}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	return ret;
119762306a36Sopenharmony_ci}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_cistatic int __snd_ctl_elem_info(struct snd_card *card,
120062306a36Sopenharmony_ci			       struct snd_kcontrol *kctl,
120162306a36Sopenharmony_ci			       struct snd_ctl_elem_info *info,
120262306a36Sopenharmony_ci			       struct snd_ctl_file *ctl)
120362306a36Sopenharmony_ci{
120462306a36Sopenharmony_ci	struct snd_kcontrol_volatile *vd;
120562306a36Sopenharmony_ci	unsigned int index_offset;
120662306a36Sopenharmony_ci	int result;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
120962306a36Sopenharmony_ci	info->access = 0;
121062306a36Sopenharmony_ci#endif
121162306a36Sopenharmony_ci	result = snd_power_ref_and_wait(card);
121262306a36Sopenharmony_ci	if (!result)
121362306a36Sopenharmony_ci		result = kctl->info(kctl, info);
121462306a36Sopenharmony_ci	snd_power_unref(card);
121562306a36Sopenharmony_ci	if (result >= 0) {
121662306a36Sopenharmony_ci		snd_BUG_ON(info->access);
121762306a36Sopenharmony_ci		index_offset = snd_ctl_get_ioff(kctl, &info->id);
121862306a36Sopenharmony_ci		vd = &kctl->vd[index_offset];
121962306a36Sopenharmony_ci		snd_ctl_build_ioff(&info->id, kctl, index_offset);
122062306a36Sopenharmony_ci		info->access = vd->access;
122162306a36Sopenharmony_ci		if (vd->owner) {
122262306a36Sopenharmony_ci			info->access |= SNDRV_CTL_ELEM_ACCESS_LOCK;
122362306a36Sopenharmony_ci			if (vd->owner == ctl)
122462306a36Sopenharmony_ci				info->access |= SNDRV_CTL_ELEM_ACCESS_OWNER;
122562306a36Sopenharmony_ci			info->owner = pid_vnr(vd->owner->pid);
122662306a36Sopenharmony_ci		} else {
122762306a36Sopenharmony_ci			info->owner = -1;
122862306a36Sopenharmony_ci		}
122962306a36Sopenharmony_ci		if (!snd_ctl_skip_validation(info) &&
123062306a36Sopenharmony_ci		    snd_ctl_check_elem_info(card, info) < 0)
123162306a36Sopenharmony_ci			result = -EINVAL;
123262306a36Sopenharmony_ci	}
123362306a36Sopenharmony_ci	return result;
123462306a36Sopenharmony_ci}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_cistatic int snd_ctl_elem_info(struct snd_ctl_file *ctl,
123762306a36Sopenharmony_ci			     struct snd_ctl_elem_info *info)
123862306a36Sopenharmony_ci{
123962306a36Sopenharmony_ci	struct snd_card *card = ctl->card;
124062306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
124162306a36Sopenharmony_ci	int result;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	down_read(&card->controls_rwsem);
124462306a36Sopenharmony_ci	kctl = snd_ctl_find_id_locked(card, &info->id);
124562306a36Sopenharmony_ci	if (kctl == NULL)
124662306a36Sopenharmony_ci		result = -ENOENT;
124762306a36Sopenharmony_ci	else
124862306a36Sopenharmony_ci		result = __snd_ctl_elem_info(card, kctl, info, ctl);
124962306a36Sopenharmony_ci	up_read(&card->controls_rwsem);
125062306a36Sopenharmony_ci	return result;
125162306a36Sopenharmony_ci}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_cistatic int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
125462306a36Sopenharmony_ci				  struct snd_ctl_elem_info __user *_info)
125562306a36Sopenharmony_ci{
125662306a36Sopenharmony_ci	struct snd_ctl_elem_info info;
125762306a36Sopenharmony_ci	int result;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	if (copy_from_user(&info, _info, sizeof(info)))
126062306a36Sopenharmony_ci		return -EFAULT;
126162306a36Sopenharmony_ci	result = snd_ctl_elem_info(ctl, &info);
126262306a36Sopenharmony_ci	if (result < 0)
126362306a36Sopenharmony_ci		return result;
126462306a36Sopenharmony_ci	/* drop internal access flags */
126562306a36Sopenharmony_ci	info.access &= ~(SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK|
126662306a36Sopenharmony_ci			 SNDRV_CTL_ELEM_ACCESS_LED_MASK);
126762306a36Sopenharmony_ci	if (copy_to_user(_info, &info, sizeof(info)))
126862306a36Sopenharmony_ci		return -EFAULT;
126962306a36Sopenharmony_ci	return result;
127062306a36Sopenharmony_ci}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_cistatic int snd_ctl_elem_read(struct snd_card *card,
127362306a36Sopenharmony_ci			     struct snd_ctl_elem_value *control)
127462306a36Sopenharmony_ci{
127562306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
127662306a36Sopenharmony_ci	struct snd_kcontrol_volatile *vd;
127762306a36Sopenharmony_ci	unsigned int index_offset;
127862306a36Sopenharmony_ci	struct snd_ctl_elem_info info;
127962306a36Sopenharmony_ci	const u32 pattern = 0xdeadbeef;
128062306a36Sopenharmony_ci	int ret;
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	down_read(&card->controls_rwsem);
128362306a36Sopenharmony_ci	kctl = snd_ctl_find_id_locked(card, &control->id);
128462306a36Sopenharmony_ci	if (kctl == NULL) {
128562306a36Sopenharmony_ci		ret = -ENOENT;
128662306a36Sopenharmony_ci		goto unlock;
128762306a36Sopenharmony_ci	}
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	index_offset = snd_ctl_get_ioff(kctl, &control->id);
129062306a36Sopenharmony_ci	vd = &kctl->vd[index_offset];
129162306a36Sopenharmony_ci	if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_READ) || kctl->get == NULL) {
129262306a36Sopenharmony_ci		ret = -EPERM;
129362306a36Sopenharmony_ci		goto unlock;
129462306a36Sopenharmony_ci	}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	snd_ctl_build_ioff(&control->id, kctl, index_offset);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci#ifdef CONFIG_SND_CTL_DEBUG
129962306a36Sopenharmony_ci	/* info is needed only for validation */
130062306a36Sopenharmony_ci	memset(&info, 0, sizeof(info));
130162306a36Sopenharmony_ci	info.id = control->id;
130262306a36Sopenharmony_ci	ret = __snd_ctl_elem_info(card, kctl, &info, NULL);
130362306a36Sopenharmony_ci	if (ret < 0)
130462306a36Sopenharmony_ci		goto unlock;
130562306a36Sopenharmony_ci#endif
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	if (!snd_ctl_skip_validation(&info))
130862306a36Sopenharmony_ci		fill_remaining_elem_value(control, &info, pattern);
130962306a36Sopenharmony_ci	ret = snd_power_ref_and_wait(card);
131062306a36Sopenharmony_ci	if (!ret)
131162306a36Sopenharmony_ci		ret = kctl->get(kctl, control);
131262306a36Sopenharmony_ci	snd_power_unref(card);
131362306a36Sopenharmony_ci	if (ret < 0)
131462306a36Sopenharmony_ci		goto unlock;
131562306a36Sopenharmony_ci	if (!snd_ctl_skip_validation(&info) &&
131662306a36Sopenharmony_ci	    sanity_check_elem_value(card, control, &info, pattern) < 0) {
131762306a36Sopenharmony_ci		dev_err(card->dev,
131862306a36Sopenharmony_ci			"control %i:%i:%i:%s:%i: access overflow\n",
131962306a36Sopenharmony_ci			control->id.iface, control->id.device,
132062306a36Sopenharmony_ci			control->id.subdevice, control->id.name,
132162306a36Sopenharmony_ci			control->id.index);
132262306a36Sopenharmony_ci		ret = -EINVAL;
132362306a36Sopenharmony_ci		goto unlock;
132462306a36Sopenharmony_ci	}
132562306a36Sopenharmony_ciunlock:
132662306a36Sopenharmony_ci	up_read(&card->controls_rwsem);
132762306a36Sopenharmony_ci	return ret;
132862306a36Sopenharmony_ci}
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_cistatic int snd_ctl_elem_read_user(struct snd_card *card,
133162306a36Sopenharmony_ci				  struct snd_ctl_elem_value __user *_control)
133262306a36Sopenharmony_ci{
133362306a36Sopenharmony_ci	struct snd_ctl_elem_value *control;
133462306a36Sopenharmony_ci	int result;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	control = memdup_user(_control, sizeof(*control));
133762306a36Sopenharmony_ci	if (IS_ERR(control))
133862306a36Sopenharmony_ci		return PTR_ERR(control);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	result = snd_ctl_elem_read(card, control);
134162306a36Sopenharmony_ci	if (result < 0)
134262306a36Sopenharmony_ci		goto error;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	if (copy_to_user(_control, control, sizeof(*control)))
134562306a36Sopenharmony_ci		result = -EFAULT;
134662306a36Sopenharmony_ci error:
134762306a36Sopenharmony_ci	kfree(control);
134862306a36Sopenharmony_ci	return result;
134962306a36Sopenharmony_ci}
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_cistatic int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
135262306a36Sopenharmony_ci			      struct snd_ctl_elem_value *control)
135362306a36Sopenharmony_ci{
135462306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
135562306a36Sopenharmony_ci	struct snd_kcontrol_volatile *vd;
135662306a36Sopenharmony_ci	unsigned int index_offset;
135762306a36Sopenharmony_ci	int result;
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	down_write(&card->controls_rwsem);
136062306a36Sopenharmony_ci	kctl = snd_ctl_find_id_locked(card, &control->id);
136162306a36Sopenharmony_ci	if (kctl == NULL) {
136262306a36Sopenharmony_ci		up_write(&card->controls_rwsem);
136362306a36Sopenharmony_ci		return -ENOENT;
136462306a36Sopenharmony_ci	}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	index_offset = snd_ctl_get_ioff(kctl, &control->id);
136762306a36Sopenharmony_ci	vd = &kctl->vd[index_offset];
136862306a36Sopenharmony_ci	if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || kctl->put == NULL ||
136962306a36Sopenharmony_ci	    (file && vd->owner && vd->owner != file)) {
137062306a36Sopenharmony_ci		up_write(&card->controls_rwsem);
137162306a36Sopenharmony_ci		return -EPERM;
137262306a36Sopenharmony_ci	}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	snd_ctl_build_ioff(&control->id, kctl, index_offset);
137562306a36Sopenharmony_ci	result = snd_power_ref_and_wait(card);
137662306a36Sopenharmony_ci	/* validate input values */
137762306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_SND_CTL_INPUT_VALIDATION) && !result) {
137862306a36Sopenharmony_ci		struct snd_ctl_elem_info info;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci		memset(&info, 0, sizeof(info));
138162306a36Sopenharmony_ci		info.id = control->id;
138262306a36Sopenharmony_ci		result = __snd_ctl_elem_info(card, kctl, &info, NULL);
138362306a36Sopenharmony_ci		if (!result)
138462306a36Sopenharmony_ci			result = sanity_check_input_values(card, control, &info,
138562306a36Sopenharmony_ci							   false);
138662306a36Sopenharmony_ci	}
138762306a36Sopenharmony_ci	if (!result)
138862306a36Sopenharmony_ci		result = kctl->put(kctl, control);
138962306a36Sopenharmony_ci	snd_power_unref(card);
139062306a36Sopenharmony_ci	if (result < 0) {
139162306a36Sopenharmony_ci		up_write(&card->controls_rwsem);
139262306a36Sopenharmony_ci		return result;
139362306a36Sopenharmony_ci	}
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	if (result > 0) {
139662306a36Sopenharmony_ci		downgrade_write(&card->controls_rwsem);
139762306a36Sopenharmony_ci		snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_VALUE, kctl, index_offset);
139862306a36Sopenharmony_ci		up_read(&card->controls_rwsem);
139962306a36Sopenharmony_ci	} else {
140062306a36Sopenharmony_ci		up_write(&card->controls_rwsem);
140162306a36Sopenharmony_ci	}
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	return 0;
140462306a36Sopenharmony_ci}
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_cistatic int snd_ctl_elem_write_user(struct snd_ctl_file *file,
140762306a36Sopenharmony_ci				   struct snd_ctl_elem_value __user *_control)
140862306a36Sopenharmony_ci{
140962306a36Sopenharmony_ci	struct snd_ctl_elem_value *control;
141062306a36Sopenharmony_ci	struct snd_card *card;
141162306a36Sopenharmony_ci	int result;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	control = memdup_user(_control, sizeof(*control));
141462306a36Sopenharmony_ci	if (IS_ERR(control))
141562306a36Sopenharmony_ci		return PTR_ERR(control);
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	card = file->card;
141862306a36Sopenharmony_ci	result = snd_ctl_elem_write(card, file, control);
141962306a36Sopenharmony_ci	if (result < 0)
142062306a36Sopenharmony_ci		goto error;
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	if (copy_to_user(_control, control, sizeof(*control)))
142362306a36Sopenharmony_ci		result = -EFAULT;
142462306a36Sopenharmony_ci error:
142562306a36Sopenharmony_ci	kfree(control);
142662306a36Sopenharmony_ci	return result;
142762306a36Sopenharmony_ci}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_cistatic int snd_ctl_elem_lock(struct snd_ctl_file *file,
143062306a36Sopenharmony_ci			     struct snd_ctl_elem_id __user *_id)
143162306a36Sopenharmony_ci{
143262306a36Sopenharmony_ci	struct snd_card *card = file->card;
143362306a36Sopenharmony_ci	struct snd_ctl_elem_id id;
143462306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
143562306a36Sopenharmony_ci	struct snd_kcontrol_volatile *vd;
143662306a36Sopenharmony_ci	int result;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	if (copy_from_user(&id, _id, sizeof(id)))
143962306a36Sopenharmony_ci		return -EFAULT;
144062306a36Sopenharmony_ci	down_write(&card->controls_rwsem);
144162306a36Sopenharmony_ci	kctl = snd_ctl_find_id_locked(card, &id);
144262306a36Sopenharmony_ci	if (kctl == NULL) {
144362306a36Sopenharmony_ci		result = -ENOENT;
144462306a36Sopenharmony_ci	} else {
144562306a36Sopenharmony_ci		vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
144662306a36Sopenharmony_ci		if (vd->owner != NULL)
144762306a36Sopenharmony_ci			result = -EBUSY;
144862306a36Sopenharmony_ci		else {
144962306a36Sopenharmony_ci			vd->owner = file;
145062306a36Sopenharmony_ci			result = 0;
145162306a36Sopenharmony_ci		}
145262306a36Sopenharmony_ci	}
145362306a36Sopenharmony_ci	up_write(&card->controls_rwsem);
145462306a36Sopenharmony_ci	return result;
145562306a36Sopenharmony_ci}
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_cistatic int snd_ctl_elem_unlock(struct snd_ctl_file *file,
145862306a36Sopenharmony_ci			       struct snd_ctl_elem_id __user *_id)
145962306a36Sopenharmony_ci{
146062306a36Sopenharmony_ci	struct snd_card *card = file->card;
146162306a36Sopenharmony_ci	struct snd_ctl_elem_id id;
146262306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
146362306a36Sopenharmony_ci	struct snd_kcontrol_volatile *vd;
146462306a36Sopenharmony_ci	int result;
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	if (copy_from_user(&id, _id, sizeof(id)))
146762306a36Sopenharmony_ci		return -EFAULT;
146862306a36Sopenharmony_ci	down_write(&card->controls_rwsem);
146962306a36Sopenharmony_ci	kctl = snd_ctl_find_id_locked(card, &id);
147062306a36Sopenharmony_ci	if (kctl == NULL) {
147162306a36Sopenharmony_ci		result = -ENOENT;
147262306a36Sopenharmony_ci	} else {
147362306a36Sopenharmony_ci		vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
147462306a36Sopenharmony_ci		if (vd->owner == NULL)
147562306a36Sopenharmony_ci			result = -EINVAL;
147662306a36Sopenharmony_ci		else if (vd->owner != file)
147762306a36Sopenharmony_ci			result = -EPERM;
147862306a36Sopenharmony_ci		else {
147962306a36Sopenharmony_ci			vd->owner = NULL;
148062306a36Sopenharmony_ci			result = 0;
148162306a36Sopenharmony_ci		}
148262306a36Sopenharmony_ci	}
148362306a36Sopenharmony_ci	up_write(&card->controls_rwsem);
148462306a36Sopenharmony_ci	return result;
148562306a36Sopenharmony_ci}
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_cistruct user_element {
148862306a36Sopenharmony_ci	struct snd_ctl_elem_info info;
148962306a36Sopenharmony_ci	struct snd_card *card;
149062306a36Sopenharmony_ci	char *elem_data;		/* element data */
149162306a36Sopenharmony_ci	unsigned long elem_data_size;	/* size of element data in bytes */
149262306a36Sopenharmony_ci	void *tlv_data;			/* TLV data */
149362306a36Sopenharmony_ci	unsigned long tlv_data_size;	/* TLV data size */
149462306a36Sopenharmony_ci	void *priv_data;		/* private data (like strings for enumerated type) */
149562306a36Sopenharmony_ci};
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci// check whether the addition (in bytes) of user ctl element may overflow the limit.
149862306a36Sopenharmony_cistatic bool check_user_elem_overflow(struct snd_card *card, ssize_t add)
149962306a36Sopenharmony_ci{
150062306a36Sopenharmony_ci	return (ssize_t)card->user_ctl_alloc_size + add > max_user_ctl_alloc_size;
150162306a36Sopenharmony_ci}
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_cistatic int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,
150462306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
150562306a36Sopenharmony_ci{
150662306a36Sopenharmony_ci	struct user_element *ue = kcontrol->private_data;
150762306a36Sopenharmony_ci	unsigned int offset;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	offset = snd_ctl_get_ioff(kcontrol, &uinfo->id);
151062306a36Sopenharmony_ci	*uinfo = ue->info;
151162306a36Sopenharmony_ci	snd_ctl_build_ioff(&uinfo->id, kcontrol, offset);
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	return 0;
151462306a36Sopenharmony_ci}
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_cistatic int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol,
151762306a36Sopenharmony_ci				       struct snd_ctl_elem_info *uinfo)
151862306a36Sopenharmony_ci{
151962306a36Sopenharmony_ci	struct user_element *ue = kcontrol->private_data;
152062306a36Sopenharmony_ci	const char *names;
152162306a36Sopenharmony_ci	unsigned int item;
152262306a36Sopenharmony_ci	unsigned int offset;
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	item = uinfo->value.enumerated.item;
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	offset = snd_ctl_get_ioff(kcontrol, &uinfo->id);
152762306a36Sopenharmony_ci	*uinfo = ue->info;
152862306a36Sopenharmony_ci	snd_ctl_build_ioff(&uinfo->id, kcontrol, offset);
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	item = min(item, uinfo->value.enumerated.items - 1);
153162306a36Sopenharmony_ci	uinfo->value.enumerated.item = item;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	names = ue->priv_data;
153462306a36Sopenharmony_ci	for (; item > 0; --item)
153562306a36Sopenharmony_ci		names += strlen(names) + 1;
153662306a36Sopenharmony_ci	strcpy(uinfo->value.enumerated.name, names);
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	return 0;
153962306a36Sopenharmony_ci}
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_cistatic int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,
154262306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
154362306a36Sopenharmony_ci{
154462306a36Sopenharmony_ci	struct user_element *ue = kcontrol->private_data;
154562306a36Sopenharmony_ci	unsigned int size = ue->elem_data_size;
154662306a36Sopenharmony_ci	char *src = ue->elem_data +
154762306a36Sopenharmony_ci			snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	memcpy(&ucontrol->value, src, size);
155062306a36Sopenharmony_ci	return 0;
155162306a36Sopenharmony_ci}
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_cistatic int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
155462306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
155562306a36Sopenharmony_ci{
155662306a36Sopenharmony_ci	int change;
155762306a36Sopenharmony_ci	struct user_element *ue = kcontrol->private_data;
155862306a36Sopenharmony_ci	unsigned int size = ue->elem_data_size;
155962306a36Sopenharmony_ci	char *dst = ue->elem_data +
156062306a36Sopenharmony_ci			snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	change = memcmp(&ucontrol->value, dst, size) != 0;
156362306a36Sopenharmony_ci	if (change)
156462306a36Sopenharmony_ci		memcpy(dst, &ucontrol->value, size);
156562306a36Sopenharmony_ci	return change;
156662306a36Sopenharmony_ci}
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci/* called in controls_rwsem write lock */
156962306a36Sopenharmony_cistatic int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
157062306a36Sopenharmony_ci			    unsigned int size)
157162306a36Sopenharmony_ci{
157262306a36Sopenharmony_ci	struct user_element *ue = kctl->private_data;
157362306a36Sopenharmony_ci	unsigned int *container;
157462306a36Sopenharmony_ci	unsigned int mask = 0;
157562306a36Sopenharmony_ci	int i;
157662306a36Sopenharmony_ci	int change;
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	lockdep_assert_held_write(&ue->card->controls_rwsem);
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	if (size > 1024 * 128)	/* sane value */
158162306a36Sopenharmony_ci		return -EINVAL;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	// does the TLV size change cause overflow?
158462306a36Sopenharmony_ci	if (check_user_elem_overflow(ue->card, (ssize_t)(size - ue->tlv_data_size)))
158562306a36Sopenharmony_ci		return -ENOMEM;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	container = vmemdup_user(buf, size);
158862306a36Sopenharmony_ci	if (IS_ERR(container))
158962306a36Sopenharmony_ci		return PTR_ERR(container);
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	change = ue->tlv_data_size != size;
159262306a36Sopenharmony_ci	if (!change)
159362306a36Sopenharmony_ci		change = memcmp(ue->tlv_data, container, size) != 0;
159462306a36Sopenharmony_ci	if (!change) {
159562306a36Sopenharmony_ci		kvfree(container);
159662306a36Sopenharmony_ci		return 0;
159762306a36Sopenharmony_ci	}
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	if (ue->tlv_data == NULL) {
160062306a36Sopenharmony_ci		/* Now TLV data is available. */
160162306a36Sopenharmony_ci		for (i = 0; i < kctl->count; ++i)
160262306a36Sopenharmony_ci			kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
160362306a36Sopenharmony_ci		mask = SNDRV_CTL_EVENT_MASK_INFO;
160462306a36Sopenharmony_ci	} else {
160562306a36Sopenharmony_ci		ue->card->user_ctl_alloc_size -= ue->tlv_data_size;
160662306a36Sopenharmony_ci		ue->tlv_data_size = 0;
160762306a36Sopenharmony_ci		kvfree(ue->tlv_data);
160862306a36Sopenharmony_ci	}
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	ue->tlv_data = container;
161162306a36Sopenharmony_ci	ue->tlv_data_size = size;
161262306a36Sopenharmony_ci	// decremented at private_free.
161362306a36Sopenharmony_ci	ue->card->user_ctl_alloc_size += size;
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci	mask |= SNDRV_CTL_EVENT_MASK_TLV;
161662306a36Sopenharmony_ci	for (i = 0; i < kctl->count; ++i)
161762306a36Sopenharmony_ci		snd_ctl_notify_one(ue->card, mask, kctl, i);
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	return change;
162062306a36Sopenharmony_ci}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_cistatic int read_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
162362306a36Sopenharmony_ci			 unsigned int size)
162462306a36Sopenharmony_ci{
162562306a36Sopenharmony_ci	struct user_element *ue = kctl->private_data;
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	if (ue->tlv_data_size == 0 || ue->tlv_data == NULL)
162862306a36Sopenharmony_ci		return -ENXIO;
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	if (size < ue->tlv_data_size)
163162306a36Sopenharmony_ci		return -ENOSPC;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	if (copy_to_user(buf, ue->tlv_data, ue->tlv_data_size))
163462306a36Sopenharmony_ci		return -EFAULT;
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	return 0;
163762306a36Sopenharmony_ci}
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_cistatic int snd_ctl_elem_user_tlv(struct snd_kcontrol *kctl, int op_flag,
164062306a36Sopenharmony_ci				 unsigned int size, unsigned int __user *buf)
164162306a36Sopenharmony_ci{
164262306a36Sopenharmony_ci	if (op_flag == SNDRV_CTL_TLV_OP_WRITE)
164362306a36Sopenharmony_ci		return replace_user_tlv(kctl, buf, size);
164462306a36Sopenharmony_ci	else
164562306a36Sopenharmony_ci		return read_user_tlv(kctl, buf, size);
164662306a36Sopenharmony_ci}
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci/* called in controls_rwsem write lock */
164962306a36Sopenharmony_cistatic int snd_ctl_elem_init_enum_names(struct user_element *ue)
165062306a36Sopenharmony_ci{
165162306a36Sopenharmony_ci	char *names, *p;
165262306a36Sopenharmony_ci	size_t buf_len, name_len;
165362306a36Sopenharmony_ci	unsigned int i;
165462306a36Sopenharmony_ci	const uintptr_t user_ptrval = ue->info.value.enumerated.names_ptr;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	lockdep_assert_held_write(&ue->card->controls_rwsem);
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci	buf_len = ue->info.value.enumerated.names_length;
165962306a36Sopenharmony_ci	if (buf_len > 64 * 1024)
166062306a36Sopenharmony_ci		return -EINVAL;
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	if (check_user_elem_overflow(ue->card, buf_len))
166362306a36Sopenharmony_ci		return -ENOMEM;
166462306a36Sopenharmony_ci	names = vmemdup_user((const void __user *)user_ptrval, buf_len);
166562306a36Sopenharmony_ci	if (IS_ERR(names))
166662306a36Sopenharmony_ci		return PTR_ERR(names);
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	/* check that there are enough valid names */
166962306a36Sopenharmony_ci	p = names;
167062306a36Sopenharmony_ci	for (i = 0; i < ue->info.value.enumerated.items; ++i) {
167162306a36Sopenharmony_ci		name_len = strnlen(p, buf_len);
167262306a36Sopenharmony_ci		if (name_len == 0 || name_len >= 64 || name_len == buf_len) {
167362306a36Sopenharmony_ci			kvfree(names);
167462306a36Sopenharmony_ci			return -EINVAL;
167562306a36Sopenharmony_ci		}
167662306a36Sopenharmony_ci		p += name_len + 1;
167762306a36Sopenharmony_ci		buf_len -= name_len + 1;
167862306a36Sopenharmony_ci	}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci	ue->priv_data = names;
168162306a36Sopenharmony_ci	ue->info.value.enumerated.names_ptr = 0;
168262306a36Sopenharmony_ci	// increment the allocation size; decremented again at private_free.
168362306a36Sopenharmony_ci	ue->card->user_ctl_alloc_size += ue->info.value.enumerated.names_length;
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	return 0;
168662306a36Sopenharmony_ci}
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_cistatic size_t compute_user_elem_size(size_t size, unsigned int count)
168962306a36Sopenharmony_ci{
169062306a36Sopenharmony_ci	return sizeof(struct user_element) + size * count;
169162306a36Sopenharmony_ci}
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_cistatic void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
169462306a36Sopenharmony_ci{
169562306a36Sopenharmony_ci	struct user_element *ue = kcontrol->private_data;
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	// decrement the allocation size.
169862306a36Sopenharmony_ci	ue->card->user_ctl_alloc_size -= compute_user_elem_size(ue->elem_data_size, kcontrol->count);
169962306a36Sopenharmony_ci	ue->card->user_ctl_alloc_size -= ue->tlv_data_size;
170062306a36Sopenharmony_ci	if (ue->priv_data)
170162306a36Sopenharmony_ci		ue->card->user_ctl_alloc_size -= ue->info.value.enumerated.names_length;
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	kvfree(ue->tlv_data);
170462306a36Sopenharmony_ci	kvfree(ue->priv_data);
170562306a36Sopenharmony_ci	kfree(ue);
170662306a36Sopenharmony_ci}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_cistatic int snd_ctl_elem_add(struct snd_ctl_file *file,
170962306a36Sopenharmony_ci			    struct snd_ctl_elem_info *info, int replace)
171062306a36Sopenharmony_ci{
171162306a36Sopenharmony_ci	struct snd_card *card = file->card;
171262306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
171362306a36Sopenharmony_ci	unsigned int count;
171462306a36Sopenharmony_ci	unsigned int access;
171562306a36Sopenharmony_ci	long private_size;
171662306a36Sopenharmony_ci	size_t alloc_size;
171762306a36Sopenharmony_ci	struct user_element *ue;
171862306a36Sopenharmony_ci	unsigned int offset;
171962306a36Sopenharmony_ci	int err;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	if (!*info->id.name)
172262306a36Sopenharmony_ci		return -EINVAL;
172362306a36Sopenharmony_ci	if (strnlen(info->id.name, sizeof(info->id.name)) >= sizeof(info->id.name))
172462306a36Sopenharmony_ci		return -EINVAL;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	/* Delete a control to replace them if needed. */
172762306a36Sopenharmony_ci	if (replace) {
172862306a36Sopenharmony_ci		info->id.numid = 0;
172962306a36Sopenharmony_ci		err = snd_ctl_remove_user_ctl(file, &info->id);
173062306a36Sopenharmony_ci		if (err)
173162306a36Sopenharmony_ci			return err;
173262306a36Sopenharmony_ci	}
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	/* Check the number of elements for this userspace control. */
173562306a36Sopenharmony_ci	count = info->owner;
173662306a36Sopenharmony_ci	if (count == 0)
173762306a36Sopenharmony_ci		count = 1;
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	/* Arrange access permissions if needed. */
174062306a36Sopenharmony_ci	access = info->access;
174162306a36Sopenharmony_ci	if (access == 0)
174262306a36Sopenharmony_ci		access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
174362306a36Sopenharmony_ci	access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
174462306a36Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_INACTIVE |
174562306a36Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_TLV_WRITE);
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	/* In initial state, nothing is available as TLV container. */
174862306a36Sopenharmony_ci	if (access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
174962306a36Sopenharmony_ci		access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
175062306a36Sopenharmony_ci	access |= SNDRV_CTL_ELEM_ACCESS_USER;
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	/*
175362306a36Sopenharmony_ci	 * Check information and calculate the size of data specific to
175462306a36Sopenharmony_ci	 * this userspace control.
175562306a36Sopenharmony_ci	 */
175662306a36Sopenharmony_ci	/* pass NULL to card for suppressing error messages */
175762306a36Sopenharmony_ci	err = snd_ctl_check_elem_info(NULL, info);
175862306a36Sopenharmony_ci	if (err < 0)
175962306a36Sopenharmony_ci		return err;
176062306a36Sopenharmony_ci	/* user-space control doesn't allow zero-size data */
176162306a36Sopenharmony_ci	if (info->count < 1)
176262306a36Sopenharmony_ci		return -EINVAL;
176362306a36Sopenharmony_ci	private_size = value_sizes[info->type] * info->count;
176462306a36Sopenharmony_ci	alloc_size = compute_user_elem_size(private_size, count);
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	down_write(&card->controls_rwsem);
176762306a36Sopenharmony_ci	if (check_user_elem_overflow(card, alloc_size)) {
176862306a36Sopenharmony_ci		err = -ENOMEM;
176962306a36Sopenharmony_ci		goto unlock;
177062306a36Sopenharmony_ci	}
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	/*
177362306a36Sopenharmony_ci	 * Keep memory object for this userspace control. After passing this
177462306a36Sopenharmony_ci	 * code block, the instance should be freed by snd_ctl_free_one().
177562306a36Sopenharmony_ci	 *
177662306a36Sopenharmony_ci	 * Note that these elements in this control are locked.
177762306a36Sopenharmony_ci	 */
177862306a36Sopenharmony_ci	err = snd_ctl_new(&kctl, count, access, file);
177962306a36Sopenharmony_ci	if (err < 0)
178062306a36Sopenharmony_ci		goto unlock;
178162306a36Sopenharmony_ci	memcpy(&kctl->id, &info->id, sizeof(kctl->id));
178262306a36Sopenharmony_ci	ue = kzalloc(alloc_size, GFP_KERNEL);
178362306a36Sopenharmony_ci	if (!ue) {
178462306a36Sopenharmony_ci		kfree(kctl);
178562306a36Sopenharmony_ci		err = -ENOMEM;
178662306a36Sopenharmony_ci		goto unlock;
178762306a36Sopenharmony_ci	}
178862306a36Sopenharmony_ci	kctl->private_data = ue;
178962306a36Sopenharmony_ci	kctl->private_free = snd_ctl_elem_user_free;
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	// increment the allocated size; decremented again at private_free.
179262306a36Sopenharmony_ci	card->user_ctl_alloc_size += alloc_size;
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	/* Set private data for this userspace control. */
179562306a36Sopenharmony_ci	ue->card = card;
179662306a36Sopenharmony_ci	ue->info = *info;
179762306a36Sopenharmony_ci	ue->info.access = 0;
179862306a36Sopenharmony_ci	ue->elem_data = (char *)ue + sizeof(*ue);
179962306a36Sopenharmony_ci	ue->elem_data_size = private_size;
180062306a36Sopenharmony_ci	if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
180162306a36Sopenharmony_ci		err = snd_ctl_elem_init_enum_names(ue);
180262306a36Sopenharmony_ci		if (err < 0) {
180362306a36Sopenharmony_ci			snd_ctl_free_one(kctl);
180462306a36Sopenharmony_ci			goto unlock;
180562306a36Sopenharmony_ci		}
180662306a36Sopenharmony_ci	}
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	/* Set callback functions. */
180962306a36Sopenharmony_ci	if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED)
181062306a36Sopenharmony_ci		kctl->info = snd_ctl_elem_user_enum_info;
181162306a36Sopenharmony_ci	else
181262306a36Sopenharmony_ci		kctl->info = snd_ctl_elem_user_info;
181362306a36Sopenharmony_ci	if (access & SNDRV_CTL_ELEM_ACCESS_READ)
181462306a36Sopenharmony_ci		kctl->get = snd_ctl_elem_user_get;
181562306a36Sopenharmony_ci	if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
181662306a36Sopenharmony_ci		kctl->put = snd_ctl_elem_user_put;
181762306a36Sopenharmony_ci	if (access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
181862306a36Sopenharmony_ci		kctl->tlv.c = snd_ctl_elem_user_tlv;
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	/* This function manage to free the instance on failure. */
182162306a36Sopenharmony_ci	err = __snd_ctl_add_replace(card, kctl, CTL_ADD_EXCLUSIVE);
182262306a36Sopenharmony_ci	if (err < 0) {
182362306a36Sopenharmony_ci		snd_ctl_free_one(kctl);
182462306a36Sopenharmony_ci		goto unlock;
182562306a36Sopenharmony_ci	}
182662306a36Sopenharmony_ci	offset = snd_ctl_get_ioff(kctl, &info->id);
182762306a36Sopenharmony_ci	snd_ctl_build_ioff(&info->id, kctl, offset);
182862306a36Sopenharmony_ci	/*
182962306a36Sopenharmony_ci	 * Here we cannot fill any field for the number of elements added by
183062306a36Sopenharmony_ci	 * this operation because there're no specific fields. The usage of
183162306a36Sopenharmony_ci	 * 'owner' field for this purpose may cause any bugs to userspace
183262306a36Sopenharmony_ci	 * applications because the field originally means PID of a process
183362306a36Sopenharmony_ci	 * which locks the element.
183462306a36Sopenharmony_ci	 */
183562306a36Sopenharmony_ci unlock:
183662306a36Sopenharmony_ci	up_write(&card->controls_rwsem);
183762306a36Sopenharmony_ci	return err;
183862306a36Sopenharmony_ci}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_cistatic int snd_ctl_elem_add_user(struct snd_ctl_file *file,
184162306a36Sopenharmony_ci				 struct snd_ctl_elem_info __user *_info, int replace)
184262306a36Sopenharmony_ci{
184362306a36Sopenharmony_ci	struct snd_ctl_elem_info info;
184462306a36Sopenharmony_ci	int err;
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	if (copy_from_user(&info, _info, sizeof(info)))
184762306a36Sopenharmony_ci		return -EFAULT;
184862306a36Sopenharmony_ci	err = snd_ctl_elem_add(file, &info, replace);
184962306a36Sopenharmony_ci	if (err < 0)
185062306a36Sopenharmony_ci		return err;
185162306a36Sopenharmony_ci	if (copy_to_user(_info, &info, sizeof(info))) {
185262306a36Sopenharmony_ci		snd_ctl_remove_user_ctl(file, &info.id);
185362306a36Sopenharmony_ci		return -EFAULT;
185462306a36Sopenharmony_ci	}
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	return 0;
185762306a36Sopenharmony_ci}
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_cistatic int snd_ctl_elem_remove(struct snd_ctl_file *file,
186062306a36Sopenharmony_ci			       struct snd_ctl_elem_id __user *_id)
186162306a36Sopenharmony_ci{
186262306a36Sopenharmony_ci	struct snd_ctl_elem_id id;
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	if (copy_from_user(&id, _id, sizeof(id)))
186562306a36Sopenharmony_ci		return -EFAULT;
186662306a36Sopenharmony_ci	return snd_ctl_remove_user_ctl(file, &id);
186762306a36Sopenharmony_ci}
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_cistatic int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)
187062306a36Sopenharmony_ci{
187162306a36Sopenharmony_ci	int subscribe;
187262306a36Sopenharmony_ci	if (get_user(subscribe, ptr))
187362306a36Sopenharmony_ci		return -EFAULT;
187462306a36Sopenharmony_ci	if (subscribe < 0) {
187562306a36Sopenharmony_ci		subscribe = file->subscribed;
187662306a36Sopenharmony_ci		if (put_user(subscribe, ptr))
187762306a36Sopenharmony_ci			return -EFAULT;
187862306a36Sopenharmony_ci		return 0;
187962306a36Sopenharmony_ci	}
188062306a36Sopenharmony_ci	if (subscribe) {
188162306a36Sopenharmony_ci		file->subscribed = 1;
188262306a36Sopenharmony_ci		return 0;
188362306a36Sopenharmony_ci	} else if (file->subscribed) {
188462306a36Sopenharmony_ci		snd_ctl_empty_read_queue(file);
188562306a36Sopenharmony_ci		file->subscribed = 0;
188662306a36Sopenharmony_ci	}
188762306a36Sopenharmony_ci	return 0;
188862306a36Sopenharmony_ci}
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_cistatic int call_tlv_handler(struct snd_ctl_file *file, int op_flag,
189162306a36Sopenharmony_ci			    struct snd_kcontrol *kctl,
189262306a36Sopenharmony_ci			    struct snd_ctl_elem_id *id,
189362306a36Sopenharmony_ci			    unsigned int __user *buf, unsigned int size)
189462306a36Sopenharmony_ci{
189562306a36Sopenharmony_ci	static const struct {
189662306a36Sopenharmony_ci		int op;
189762306a36Sopenharmony_ci		int perm;
189862306a36Sopenharmony_ci	} pairs[] = {
189962306a36Sopenharmony_ci		{SNDRV_CTL_TLV_OP_READ,  SNDRV_CTL_ELEM_ACCESS_TLV_READ},
190062306a36Sopenharmony_ci		{SNDRV_CTL_TLV_OP_WRITE, SNDRV_CTL_ELEM_ACCESS_TLV_WRITE},
190162306a36Sopenharmony_ci		{SNDRV_CTL_TLV_OP_CMD,   SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND},
190262306a36Sopenharmony_ci	};
190362306a36Sopenharmony_ci	struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)];
190462306a36Sopenharmony_ci	int i, ret;
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	/* Check support of the request for this element. */
190762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
190862306a36Sopenharmony_ci		if (op_flag == pairs[i].op && (vd->access & pairs[i].perm))
190962306a36Sopenharmony_ci			break;
191062306a36Sopenharmony_ci	}
191162306a36Sopenharmony_ci	if (i == ARRAY_SIZE(pairs))
191262306a36Sopenharmony_ci		return -ENXIO;
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	if (kctl->tlv.c == NULL)
191562306a36Sopenharmony_ci		return -ENXIO;
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci	/* Write and command operations are not allowed for locked element. */
191862306a36Sopenharmony_ci	if (op_flag != SNDRV_CTL_TLV_OP_READ &&
191962306a36Sopenharmony_ci	    vd->owner != NULL && vd->owner != file)
192062306a36Sopenharmony_ci		return -EPERM;
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci	ret = snd_power_ref_and_wait(file->card);
192362306a36Sopenharmony_ci	if (!ret)
192462306a36Sopenharmony_ci		ret = kctl->tlv.c(kctl, op_flag, size, buf);
192562306a36Sopenharmony_ci	snd_power_unref(file->card);
192662306a36Sopenharmony_ci	return ret;
192762306a36Sopenharmony_ci}
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_cistatic int read_tlv_buf(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id,
193062306a36Sopenharmony_ci			unsigned int __user *buf, unsigned int size)
193162306a36Sopenharmony_ci{
193262306a36Sopenharmony_ci	struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)];
193362306a36Sopenharmony_ci	unsigned int len;
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ))
193662306a36Sopenharmony_ci		return -ENXIO;
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci	if (kctl->tlv.p == NULL)
193962306a36Sopenharmony_ci		return -ENXIO;
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	len = sizeof(unsigned int) * 2 + kctl->tlv.p[1];
194262306a36Sopenharmony_ci	if (size < len)
194362306a36Sopenharmony_ci		return -ENOMEM;
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	if (copy_to_user(buf, kctl->tlv.p, len))
194662306a36Sopenharmony_ci		return -EFAULT;
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_ci	return 0;
194962306a36Sopenharmony_ci}
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_cistatic int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
195262306a36Sopenharmony_ci			     struct snd_ctl_tlv __user *buf,
195362306a36Sopenharmony_ci                             int op_flag)
195462306a36Sopenharmony_ci{
195562306a36Sopenharmony_ci	struct snd_ctl_tlv header;
195662306a36Sopenharmony_ci	unsigned int __user *container;
195762306a36Sopenharmony_ci	unsigned int container_size;
195862306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
195962306a36Sopenharmony_ci	struct snd_ctl_elem_id id;
196062306a36Sopenharmony_ci	struct snd_kcontrol_volatile *vd;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	lockdep_assert_held(&file->card->controls_rwsem);
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	if (copy_from_user(&header, buf, sizeof(header)))
196562306a36Sopenharmony_ci		return -EFAULT;
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	/* In design of control core, numerical ID starts at 1. */
196862306a36Sopenharmony_ci	if (header.numid == 0)
196962306a36Sopenharmony_ci		return -EINVAL;
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	/* At least, container should include type and length fields.  */
197262306a36Sopenharmony_ci	if (header.length < sizeof(unsigned int) * 2)
197362306a36Sopenharmony_ci		return -EINVAL;
197462306a36Sopenharmony_ci	container_size = header.length;
197562306a36Sopenharmony_ci	container = buf->tlv;
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci	kctl = snd_ctl_find_numid_locked(file->card, header.numid);
197862306a36Sopenharmony_ci	if (kctl == NULL)
197962306a36Sopenharmony_ci		return -ENOENT;
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	/* Calculate index of the element in this set. */
198262306a36Sopenharmony_ci	id = kctl->id;
198362306a36Sopenharmony_ci	snd_ctl_build_ioff(&id, kctl, header.numid - id.numid);
198462306a36Sopenharmony_ci	vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci	if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
198762306a36Sopenharmony_ci		return call_tlv_handler(file, op_flag, kctl, &id, container,
198862306a36Sopenharmony_ci					container_size);
198962306a36Sopenharmony_ci	} else {
199062306a36Sopenharmony_ci		if (op_flag == SNDRV_CTL_TLV_OP_READ) {
199162306a36Sopenharmony_ci			return read_tlv_buf(kctl, &id, container,
199262306a36Sopenharmony_ci					    container_size);
199362306a36Sopenharmony_ci		}
199462306a36Sopenharmony_ci	}
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	/* Not supported. */
199762306a36Sopenharmony_ci	return -ENXIO;
199862306a36Sopenharmony_ci}
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_cistatic long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
200162306a36Sopenharmony_ci{
200262306a36Sopenharmony_ci	struct snd_ctl_file *ctl;
200362306a36Sopenharmony_ci	struct snd_card *card;
200462306a36Sopenharmony_ci	struct snd_kctl_ioctl *p;
200562306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
200662306a36Sopenharmony_ci	int __user *ip = argp;
200762306a36Sopenharmony_ci	int err;
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci	ctl = file->private_data;
201062306a36Sopenharmony_ci	card = ctl->card;
201162306a36Sopenharmony_ci	if (snd_BUG_ON(!card))
201262306a36Sopenharmony_ci		return -ENXIO;
201362306a36Sopenharmony_ci	switch (cmd) {
201462306a36Sopenharmony_ci	case SNDRV_CTL_IOCTL_PVERSION:
201562306a36Sopenharmony_ci		return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
201662306a36Sopenharmony_ci	case SNDRV_CTL_IOCTL_CARD_INFO:
201762306a36Sopenharmony_ci		return snd_ctl_card_info(card, ctl, cmd, argp);
201862306a36Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_LIST:
201962306a36Sopenharmony_ci		return snd_ctl_elem_list_user(card, argp);
202062306a36Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_INFO:
202162306a36Sopenharmony_ci		return snd_ctl_elem_info_user(ctl, argp);
202262306a36Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_READ:
202362306a36Sopenharmony_ci		return snd_ctl_elem_read_user(card, argp);
202462306a36Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_WRITE:
202562306a36Sopenharmony_ci		return snd_ctl_elem_write_user(ctl, argp);
202662306a36Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_LOCK:
202762306a36Sopenharmony_ci		return snd_ctl_elem_lock(ctl, argp);
202862306a36Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
202962306a36Sopenharmony_ci		return snd_ctl_elem_unlock(ctl, argp);
203062306a36Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_ADD:
203162306a36Sopenharmony_ci		return snd_ctl_elem_add_user(ctl, argp, 0);
203262306a36Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_REPLACE:
203362306a36Sopenharmony_ci		return snd_ctl_elem_add_user(ctl, argp, 1);
203462306a36Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_REMOVE:
203562306a36Sopenharmony_ci		return snd_ctl_elem_remove(ctl, argp);
203662306a36Sopenharmony_ci	case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
203762306a36Sopenharmony_ci		return snd_ctl_subscribe_events(ctl, ip);
203862306a36Sopenharmony_ci	case SNDRV_CTL_IOCTL_TLV_READ:
203962306a36Sopenharmony_ci		down_read(&ctl->card->controls_rwsem);
204062306a36Sopenharmony_ci		err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ);
204162306a36Sopenharmony_ci		up_read(&ctl->card->controls_rwsem);
204262306a36Sopenharmony_ci		return err;
204362306a36Sopenharmony_ci	case SNDRV_CTL_IOCTL_TLV_WRITE:
204462306a36Sopenharmony_ci		down_write(&ctl->card->controls_rwsem);
204562306a36Sopenharmony_ci		err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE);
204662306a36Sopenharmony_ci		up_write(&ctl->card->controls_rwsem);
204762306a36Sopenharmony_ci		return err;
204862306a36Sopenharmony_ci	case SNDRV_CTL_IOCTL_TLV_COMMAND:
204962306a36Sopenharmony_ci		down_write(&ctl->card->controls_rwsem);
205062306a36Sopenharmony_ci		err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD);
205162306a36Sopenharmony_ci		up_write(&ctl->card->controls_rwsem);
205262306a36Sopenharmony_ci		return err;
205362306a36Sopenharmony_ci	case SNDRV_CTL_IOCTL_POWER:
205462306a36Sopenharmony_ci		return -ENOPROTOOPT;
205562306a36Sopenharmony_ci	case SNDRV_CTL_IOCTL_POWER_STATE:
205662306a36Sopenharmony_ci		return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;
205762306a36Sopenharmony_ci	}
205862306a36Sopenharmony_ci	down_read(&snd_ioctl_rwsem);
205962306a36Sopenharmony_ci	list_for_each_entry(p, &snd_control_ioctls, list) {
206062306a36Sopenharmony_ci		err = p->fioctl(card, ctl, cmd, arg);
206162306a36Sopenharmony_ci		if (err != -ENOIOCTLCMD) {
206262306a36Sopenharmony_ci			up_read(&snd_ioctl_rwsem);
206362306a36Sopenharmony_ci			return err;
206462306a36Sopenharmony_ci		}
206562306a36Sopenharmony_ci	}
206662306a36Sopenharmony_ci	up_read(&snd_ioctl_rwsem);
206762306a36Sopenharmony_ci	dev_dbg(card->dev, "unknown ioctl = 0x%x\n", cmd);
206862306a36Sopenharmony_ci	return -ENOTTY;
206962306a36Sopenharmony_ci}
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_cistatic ssize_t snd_ctl_read(struct file *file, char __user *buffer,
207262306a36Sopenharmony_ci			    size_t count, loff_t * offset)
207362306a36Sopenharmony_ci{
207462306a36Sopenharmony_ci	struct snd_ctl_file *ctl;
207562306a36Sopenharmony_ci	int err = 0;
207662306a36Sopenharmony_ci	ssize_t result = 0;
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	ctl = file->private_data;
207962306a36Sopenharmony_ci	if (snd_BUG_ON(!ctl || !ctl->card))
208062306a36Sopenharmony_ci		return -ENXIO;
208162306a36Sopenharmony_ci	if (!ctl->subscribed)
208262306a36Sopenharmony_ci		return -EBADFD;
208362306a36Sopenharmony_ci	if (count < sizeof(struct snd_ctl_event))
208462306a36Sopenharmony_ci		return -EINVAL;
208562306a36Sopenharmony_ci	spin_lock_irq(&ctl->read_lock);
208662306a36Sopenharmony_ci	while (count >= sizeof(struct snd_ctl_event)) {
208762306a36Sopenharmony_ci		struct snd_ctl_event ev;
208862306a36Sopenharmony_ci		struct snd_kctl_event *kev;
208962306a36Sopenharmony_ci		while (list_empty(&ctl->events)) {
209062306a36Sopenharmony_ci			wait_queue_entry_t wait;
209162306a36Sopenharmony_ci			if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
209262306a36Sopenharmony_ci				err = -EAGAIN;
209362306a36Sopenharmony_ci				goto __end_lock;
209462306a36Sopenharmony_ci			}
209562306a36Sopenharmony_ci			init_waitqueue_entry(&wait, current);
209662306a36Sopenharmony_ci			add_wait_queue(&ctl->change_sleep, &wait);
209762306a36Sopenharmony_ci			set_current_state(TASK_INTERRUPTIBLE);
209862306a36Sopenharmony_ci			spin_unlock_irq(&ctl->read_lock);
209962306a36Sopenharmony_ci			schedule();
210062306a36Sopenharmony_ci			remove_wait_queue(&ctl->change_sleep, &wait);
210162306a36Sopenharmony_ci			if (ctl->card->shutdown)
210262306a36Sopenharmony_ci				return -ENODEV;
210362306a36Sopenharmony_ci			if (signal_pending(current))
210462306a36Sopenharmony_ci				return -ERESTARTSYS;
210562306a36Sopenharmony_ci			spin_lock_irq(&ctl->read_lock);
210662306a36Sopenharmony_ci		}
210762306a36Sopenharmony_ci		kev = snd_kctl_event(ctl->events.next);
210862306a36Sopenharmony_ci		ev.type = SNDRV_CTL_EVENT_ELEM;
210962306a36Sopenharmony_ci		ev.data.elem.mask = kev->mask;
211062306a36Sopenharmony_ci		ev.data.elem.id = kev->id;
211162306a36Sopenharmony_ci		list_del(&kev->list);
211262306a36Sopenharmony_ci		spin_unlock_irq(&ctl->read_lock);
211362306a36Sopenharmony_ci		kfree(kev);
211462306a36Sopenharmony_ci		if (copy_to_user(buffer, &ev, sizeof(struct snd_ctl_event))) {
211562306a36Sopenharmony_ci			err = -EFAULT;
211662306a36Sopenharmony_ci			goto __end;
211762306a36Sopenharmony_ci		}
211862306a36Sopenharmony_ci		spin_lock_irq(&ctl->read_lock);
211962306a36Sopenharmony_ci		buffer += sizeof(struct snd_ctl_event);
212062306a36Sopenharmony_ci		count -= sizeof(struct snd_ctl_event);
212162306a36Sopenharmony_ci		result += sizeof(struct snd_ctl_event);
212262306a36Sopenharmony_ci	}
212362306a36Sopenharmony_ci      __end_lock:
212462306a36Sopenharmony_ci	spin_unlock_irq(&ctl->read_lock);
212562306a36Sopenharmony_ci      __end:
212662306a36Sopenharmony_ci      	return result > 0 ? result : err;
212762306a36Sopenharmony_ci}
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_cistatic __poll_t snd_ctl_poll(struct file *file, poll_table * wait)
213062306a36Sopenharmony_ci{
213162306a36Sopenharmony_ci	__poll_t mask;
213262306a36Sopenharmony_ci	struct snd_ctl_file *ctl;
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci	ctl = file->private_data;
213562306a36Sopenharmony_ci	if (!ctl->subscribed)
213662306a36Sopenharmony_ci		return 0;
213762306a36Sopenharmony_ci	poll_wait(file, &ctl->change_sleep, wait);
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	mask = 0;
214062306a36Sopenharmony_ci	if (!list_empty(&ctl->events))
214162306a36Sopenharmony_ci		mask |= EPOLLIN | EPOLLRDNORM;
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	return mask;
214462306a36Sopenharmony_ci}
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci/*
214762306a36Sopenharmony_ci * register the device-specific control-ioctls.
214862306a36Sopenharmony_ci * called from each device manager like pcm.c, hwdep.c, etc.
214962306a36Sopenharmony_ci */
215062306a36Sopenharmony_cistatic int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *lists)
215162306a36Sopenharmony_ci{
215262306a36Sopenharmony_ci	struct snd_kctl_ioctl *pn;
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	pn = kzalloc(sizeof(struct snd_kctl_ioctl), GFP_KERNEL);
215562306a36Sopenharmony_ci	if (pn == NULL)
215662306a36Sopenharmony_ci		return -ENOMEM;
215762306a36Sopenharmony_ci	pn->fioctl = fcn;
215862306a36Sopenharmony_ci	down_write(&snd_ioctl_rwsem);
215962306a36Sopenharmony_ci	list_add_tail(&pn->list, lists);
216062306a36Sopenharmony_ci	up_write(&snd_ioctl_rwsem);
216162306a36Sopenharmony_ci	return 0;
216262306a36Sopenharmony_ci}
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci/**
216562306a36Sopenharmony_ci * snd_ctl_register_ioctl - register the device-specific control-ioctls
216662306a36Sopenharmony_ci * @fcn: ioctl callback function
216762306a36Sopenharmony_ci *
216862306a36Sopenharmony_ci * called from each device manager like pcm.c, hwdep.c, etc.
216962306a36Sopenharmony_ci *
217062306a36Sopenharmony_ci * Return: zero if successful, or a negative error code
217162306a36Sopenharmony_ci */
217262306a36Sopenharmony_ciint snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn)
217362306a36Sopenharmony_ci{
217462306a36Sopenharmony_ci	return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls);
217562306a36Sopenharmony_ci}
217662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_register_ioctl);
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
217962306a36Sopenharmony_ci/**
218062306a36Sopenharmony_ci * snd_ctl_register_ioctl_compat - register the device-specific 32bit compat
218162306a36Sopenharmony_ci * control-ioctls
218262306a36Sopenharmony_ci * @fcn: ioctl callback function
218362306a36Sopenharmony_ci *
218462306a36Sopenharmony_ci * Return: zero if successful, or a negative error code
218562306a36Sopenharmony_ci */
218662306a36Sopenharmony_ciint snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn)
218762306a36Sopenharmony_ci{
218862306a36Sopenharmony_ci	return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls);
218962306a36Sopenharmony_ci}
219062306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_register_ioctl_compat);
219162306a36Sopenharmony_ci#endif
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci/*
219462306a36Sopenharmony_ci * de-register the device-specific control-ioctls.
219562306a36Sopenharmony_ci */
219662306a36Sopenharmony_cistatic int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,
219762306a36Sopenharmony_ci				     struct list_head *lists)
219862306a36Sopenharmony_ci{
219962306a36Sopenharmony_ci	struct snd_kctl_ioctl *p;
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci	if (snd_BUG_ON(!fcn))
220262306a36Sopenharmony_ci		return -EINVAL;
220362306a36Sopenharmony_ci	down_write(&snd_ioctl_rwsem);
220462306a36Sopenharmony_ci	list_for_each_entry(p, lists, list) {
220562306a36Sopenharmony_ci		if (p->fioctl == fcn) {
220662306a36Sopenharmony_ci			list_del(&p->list);
220762306a36Sopenharmony_ci			up_write(&snd_ioctl_rwsem);
220862306a36Sopenharmony_ci			kfree(p);
220962306a36Sopenharmony_ci			return 0;
221062306a36Sopenharmony_ci		}
221162306a36Sopenharmony_ci	}
221262306a36Sopenharmony_ci	up_write(&snd_ioctl_rwsem);
221362306a36Sopenharmony_ci	snd_BUG();
221462306a36Sopenharmony_ci	return -EINVAL;
221562306a36Sopenharmony_ci}
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci/**
221862306a36Sopenharmony_ci * snd_ctl_unregister_ioctl - de-register the device-specific control-ioctls
221962306a36Sopenharmony_ci * @fcn: ioctl callback function to unregister
222062306a36Sopenharmony_ci *
222162306a36Sopenharmony_ci * Return: zero if successful, or a negative error code
222262306a36Sopenharmony_ci */
222362306a36Sopenharmony_ciint snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn)
222462306a36Sopenharmony_ci{
222562306a36Sopenharmony_ci	return _snd_ctl_unregister_ioctl(fcn, &snd_control_ioctls);
222662306a36Sopenharmony_ci}
222762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_unregister_ioctl);
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
223062306a36Sopenharmony_ci/**
223162306a36Sopenharmony_ci * snd_ctl_unregister_ioctl_compat - de-register the device-specific compat
223262306a36Sopenharmony_ci * 32bit control-ioctls
223362306a36Sopenharmony_ci * @fcn: ioctl callback function to unregister
223462306a36Sopenharmony_ci *
223562306a36Sopenharmony_ci * Return: zero if successful, or a negative error code
223662306a36Sopenharmony_ci */
223762306a36Sopenharmony_ciint snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn)
223862306a36Sopenharmony_ci{
223962306a36Sopenharmony_ci	return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls);
224062306a36Sopenharmony_ci}
224162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat);
224262306a36Sopenharmony_ci#endif
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_cistatic int snd_ctl_fasync(int fd, struct file * file, int on)
224562306a36Sopenharmony_ci{
224662306a36Sopenharmony_ci	struct snd_ctl_file *ctl;
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci	ctl = file->private_data;
224962306a36Sopenharmony_ci	return snd_fasync_helper(fd, file, on, &ctl->fasync);
225062306a36Sopenharmony_ci}
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci/* return the preferred subdevice number if already assigned;
225362306a36Sopenharmony_ci * otherwise return -1
225462306a36Sopenharmony_ci */
225562306a36Sopenharmony_ciint snd_ctl_get_preferred_subdevice(struct snd_card *card, int type)
225662306a36Sopenharmony_ci{
225762306a36Sopenharmony_ci	struct snd_ctl_file *kctl;
225862306a36Sopenharmony_ci	int subdevice = -1;
225962306a36Sopenharmony_ci	unsigned long flags;
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci	read_lock_irqsave(&card->ctl_files_rwlock, flags);
226262306a36Sopenharmony_ci	list_for_each_entry(kctl, &card->ctl_files, list) {
226362306a36Sopenharmony_ci		if (kctl->pid == task_pid(current)) {
226462306a36Sopenharmony_ci			subdevice = kctl->preferred_subdevice[type];
226562306a36Sopenharmony_ci			if (subdevice != -1)
226662306a36Sopenharmony_ci				break;
226762306a36Sopenharmony_ci		}
226862306a36Sopenharmony_ci	}
226962306a36Sopenharmony_ci	read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
227062306a36Sopenharmony_ci	return subdevice;
227162306a36Sopenharmony_ci}
227262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_ctl_get_preferred_subdevice);
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci/*
227562306a36Sopenharmony_ci * ioctl32 compat
227662306a36Sopenharmony_ci */
227762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
227862306a36Sopenharmony_ci#include "control_compat.c"
227962306a36Sopenharmony_ci#else
228062306a36Sopenharmony_ci#define snd_ctl_ioctl_compat	NULL
228162306a36Sopenharmony_ci#endif
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci/*
228462306a36Sopenharmony_ci * control layers (audio LED etc.)
228562306a36Sopenharmony_ci */
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci/**
228862306a36Sopenharmony_ci * snd_ctl_request_layer - request to use the layer
228962306a36Sopenharmony_ci * @module_name: Name of the kernel module (NULL == build-in)
229062306a36Sopenharmony_ci *
229162306a36Sopenharmony_ci * Return: zero if successful, or an error code when the module cannot be loaded
229262306a36Sopenharmony_ci */
229362306a36Sopenharmony_ciint snd_ctl_request_layer(const char *module_name)
229462306a36Sopenharmony_ci{
229562306a36Sopenharmony_ci	struct snd_ctl_layer_ops *lops;
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci	if (module_name == NULL)
229862306a36Sopenharmony_ci		return 0;
229962306a36Sopenharmony_ci	down_read(&snd_ctl_layer_rwsem);
230062306a36Sopenharmony_ci	for (lops = snd_ctl_layer; lops; lops = lops->next)
230162306a36Sopenharmony_ci		if (strcmp(lops->module_name, module_name) == 0)
230262306a36Sopenharmony_ci			break;
230362306a36Sopenharmony_ci	up_read(&snd_ctl_layer_rwsem);
230462306a36Sopenharmony_ci	if (lops)
230562306a36Sopenharmony_ci		return 0;
230662306a36Sopenharmony_ci	return request_module(module_name);
230762306a36Sopenharmony_ci}
230862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_ctl_request_layer);
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_ci/**
231162306a36Sopenharmony_ci * snd_ctl_register_layer - register new control layer
231262306a36Sopenharmony_ci * @lops: operation structure
231362306a36Sopenharmony_ci *
231462306a36Sopenharmony_ci * The new layer can track all control elements and do additional
231562306a36Sopenharmony_ci * operations on top (like audio LED handling).
231662306a36Sopenharmony_ci */
231762306a36Sopenharmony_civoid snd_ctl_register_layer(struct snd_ctl_layer_ops *lops)
231862306a36Sopenharmony_ci{
231962306a36Sopenharmony_ci	struct snd_card *card;
232062306a36Sopenharmony_ci	int card_number;
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_ci	down_write(&snd_ctl_layer_rwsem);
232362306a36Sopenharmony_ci	lops->next = snd_ctl_layer;
232462306a36Sopenharmony_ci	snd_ctl_layer = lops;
232562306a36Sopenharmony_ci	up_write(&snd_ctl_layer_rwsem);
232662306a36Sopenharmony_ci	for (card_number = 0; card_number < SNDRV_CARDS; card_number++) {
232762306a36Sopenharmony_ci		card = snd_card_ref(card_number);
232862306a36Sopenharmony_ci		if (card) {
232962306a36Sopenharmony_ci			down_read(&card->controls_rwsem);
233062306a36Sopenharmony_ci			lops->lregister(card);
233162306a36Sopenharmony_ci			up_read(&card->controls_rwsem);
233262306a36Sopenharmony_ci			snd_card_unref(card);
233362306a36Sopenharmony_ci		}
233462306a36Sopenharmony_ci	}
233562306a36Sopenharmony_ci}
233662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_ctl_register_layer);
233762306a36Sopenharmony_ci
233862306a36Sopenharmony_ci/**
233962306a36Sopenharmony_ci * snd_ctl_disconnect_layer - disconnect control layer
234062306a36Sopenharmony_ci * @lops: operation structure
234162306a36Sopenharmony_ci *
234262306a36Sopenharmony_ci * It is expected that the information about tracked cards
234362306a36Sopenharmony_ci * is freed before this call (the disconnect callback is
234462306a36Sopenharmony_ci * not called here).
234562306a36Sopenharmony_ci */
234662306a36Sopenharmony_civoid snd_ctl_disconnect_layer(struct snd_ctl_layer_ops *lops)
234762306a36Sopenharmony_ci{
234862306a36Sopenharmony_ci	struct snd_ctl_layer_ops *lops2, *prev_lops2;
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci	down_write(&snd_ctl_layer_rwsem);
235162306a36Sopenharmony_ci	for (lops2 = snd_ctl_layer, prev_lops2 = NULL; lops2; lops2 = lops2->next) {
235262306a36Sopenharmony_ci		if (lops2 == lops) {
235362306a36Sopenharmony_ci			if (!prev_lops2)
235462306a36Sopenharmony_ci				snd_ctl_layer = lops->next;
235562306a36Sopenharmony_ci			else
235662306a36Sopenharmony_ci				prev_lops2->next = lops->next;
235762306a36Sopenharmony_ci			break;
235862306a36Sopenharmony_ci		}
235962306a36Sopenharmony_ci		prev_lops2 = lops2;
236062306a36Sopenharmony_ci	}
236162306a36Sopenharmony_ci	up_write(&snd_ctl_layer_rwsem);
236262306a36Sopenharmony_ci}
236362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_ctl_disconnect_layer);
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci/*
236662306a36Sopenharmony_ci *  INIT PART
236762306a36Sopenharmony_ci */
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_cistatic const struct file_operations snd_ctl_f_ops =
237062306a36Sopenharmony_ci{
237162306a36Sopenharmony_ci	.owner =	THIS_MODULE,
237262306a36Sopenharmony_ci	.read =		snd_ctl_read,
237362306a36Sopenharmony_ci	.open =		snd_ctl_open,
237462306a36Sopenharmony_ci	.release =	snd_ctl_release,
237562306a36Sopenharmony_ci	.llseek =	no_llseek,
237662306a36Sopenharmony_ci	.poll =		snd_ctl_poll,
237762306a36Sopenharmony_ci	.unlocked_ioctl =	snd_ctl_ioctl,
237862306a36Sopenharmony_ci	.compat_ioctl =	snd_ctl_ioctl_compat,
237962306a36Sopenharmony_ci	.fasync =	snd_ctl_fasync,
238062306a36Sopenharmony_ci};
238162306a36Sopenharmony_ci
238262306a36Sopenharmony_ci/*
238362306a36Sopenharmony_ci * registration of the control device
238462306a36Sopenharmony_ci */
238562306a36Sopenharmony_cistatic int snd_ctl_dev_register(struct snd_device *device)
238662306a36Sopenharmony_ci{
238762306a36Sopenharmony_ci	struct snd_card *card = device->device_data;
238862306a36Sopenharmony_ci	struct snd_ctl_layer_ops *lops;
238962306a36Sopenharmony_ci	int err;
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci	err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
239262306a36Sopenharmony_ci				  &snd_ctl_f_ops, card, card->ctl_dev);
239362306a36Sopenharmony_ci	if (err < 0)
239462306a36Sopenharmony_ci		return err;
239562306a36Sopenharmony_ci	down_read(&card->controls_rwsem);
239662306a36Sopenharmony_ci	down_read(&snd_ctl_layer_rwsem);
239762306a36Sopenharmony_ci	for (lops = snd_ctl_layer; lops; lops = lops->next)
239862306a36Sopenharmony_ci		lops->lregister(card);
239962306a36Sopenharmony_ci	up_read(&snd_ctl_layer_rwsem);
240062306a36Sopenharmony_ci	up_read(&card->controls_rwsem);
240162306a36Sopenharmony_ci	return 0;
240262306a36Sopenharmony_ci}
240362306a36Sopenharmony_ci
240462306a36Sopenharmony_ci/*
240562306a36Sopenharmony_ci * disconnection of the control device
240662306a36Sopenharmony_ci */
240762306a36Sopenharmony_cistatic int snd_ctl_dev_disconnect(struct snd_device *device)
240862306a36Sopenharmony_ci{
240962306a36Sopenharmony_ci	struct snd_card *card = device->device_data;
241062306a36Sopenharmony_ci	struct snd_ctl_file *ctl;
241162306a36Sopenharmony_ci	struct snd_ctl_layer_ops *lops;
241262306a36Sopenharmony_ci	unsigned long flags;
241362306a36Sopenharmony_ci
241462306a36Sopenharmony_ci	read_lock_irqsave(&card->ctl_files_rwlock, flags);
241562306a36Sopenharmony_ci	list_for_each_entry(ctl, &card->ctl_files, list) {
241662306a36Sopenharmony_ci		wake_up(&ctl->change_sleep);
241762306a36Sopenharmony_ci		snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR);
241862306a36Sopenharmony_ci	}
241962306a36Sopenharmony_ci	read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_ci	down_read(&card->controls_rwsem);
242262306a36Sopenharmony_ci	down_read(&snd_ctl_layer_rwsem);
242362306a36Sopenharmony_ci	for (lops = snd_ctl_layer; lops; lops = lops->next)
242462306a36Sopenharmony_ci		lops->ldisconnect(card);
242562306a36Sopenharmony_ci	up_read(&snd_ctl_layer_rwsem);
242662306a36Sopenharmony_ci	up_read(&card->controls_rwsem);
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_ci	return snd_unregister_device(card->ctl_dev);
242962306a36Sopenharmony_ci}
243062306a36Sopenharmony_ci
243162306a36Sopenharmony_ci/*
243262306a36Sopenharmony_ci * free all controls
243362306a36Sopenharmony_ci */
243462306a36Sopenharmony_cistatic int snd_ctl_dev_free(struct snd_device *device)
243562306a36Sopenharmony_ci{
243662306a36Sopenharmony_ci	struct snd_card *card = device->device_data;
243762306a36Sopenharmony_ci	struct snd_kcontrol *control;
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci	down_write(&card->controls_rwsem);
244062306a36Sopenharmony_ci	while (!list_empty(&card->controls)) {
244162306a36Sopenharmony_ci		control = snd_kcontrol(card->controls.next);
244262306a36Sopenharmony_ci		__snd_ctl_remove(card, control, false);
244362306a36Sopenharmony_ci	}
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci#ifdef CONFIG_SND_CTL_FAST_LOOKUP
244662306a36Sopenharmony_ci	xa_destroy(&card->ctl_numids);
244762306a36Sopenharmony_ci	xa_destroy(&card->ctl_hash);
244862306a36Sopenharmony_ci#endif
244962306a36Sopenharmony_ci	up_write(&card->controls_rwsem);
245062306a36Sopenharmony_ci	put_device(card->ctl_dev);
245162306a36Sopenharmony_ci	return 0;
245262306a36Sopenharmony_ci}
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci/*
245562306a36Sopenharmony_ci * create control core:
245662306a36Sopenharmony_ci * called from init.c
245762306a36Sopenharmony_ci */
245862306a36Sopenharmony_ciint snd_ctl_create(struct snd_card *card)
245962306a36Sopenharmony_ci{
246062306a36Sopenharmony_ci	static const struct snd_device_ops ops = {
246162306a36Sopenharmony_ci		.dev_free = snd_ctl_dev_free,
246262306a36Sopenharmony_ci		.dev_register =	snd_ctl_dev_register,
246362306a36Sopenharmony_ci		.dev_disconnect = snd_ctl_dev_disconnect,
246462306a36Sopenharmony_ci	};
246562306a36Sopenharmony_ci	int err;
246662306a36Sopenharmony_ci
246762306a36Sopenharmony_ci	if (snd_BUG_ON(!card))
246862306a36Sopenharmony_ci		return -ENXIO;
246962306a36Sopenharmony_ci	if (snd_BUG_ON(card->number < 0 || card->number >= SNDRV_CARDS))
247062306a36Sopenharmony_ci		return -ENXIO;
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_ci	err = snd_device_alloc(&card->ctl_dev, card);
247362306a36Sopenharmony_ci	if (err < 0)
247462306a36Sopenharmony_ci		return err;
247562306a36Sopenharmony_ci	dev_set_name(card->ctl_dev, "controlC%d", card->number);
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci	err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
247862306a36Sopenharmony_ci	if (err < 0)
247962306a36Sopenharmony_ci		put_device(card->ctl_dev);
248062306a36Sopenharmony_ci	return err;
248162306a36Sopenharmony_ci}
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci/*
248462306a36Sopenharmony_ci * Frequently used control callbacks/helpers
248562306a36Sopenharmony_ci */
248662306a36Sopenharmony_ci
248762306a36Sopenharmony_ci/**
248862306a36Sopenharmony_ci * snd_ctl_boolean_mono_info - Helper function for a standard boolean info
248962306a36Sopenharmony_ci * callback with a mono channel
249062306a36Sopenharmony_ci * @kcontrol: the kcontrol instance
249162306a36Sopenharmony_ci * @uinfo: info to store
249262306a36Sopenharmony_ci *
249362306a36Sopenharmony_ci * This is a function that can be used as info callback for a standard
249462306a36Sopenharmony_ci * boolean control with a single mono channel.
249562306a36Sopenharmony_ci *
249662306a36Sopenharmony_ci * Return: Zero (always successful)
249762306a36Sopenharmony_ci */
249862306a36Sopenharmony_ciint snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
249962306a36Sopenharmony_ci			      struct snd_ctl_elem_info *uinfo)
250062306a36Sopenharmony_ci{
250162306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
250262306a36Sopenharmony_ci	uinfo->count = 1;
250362306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
250462306a36Sopenharmony_ci	uinfo->value.integer.max = 1;
250562306a36Sopenharmony_ci	return 0;
250662306a36Sopenharmony_ci}
250762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_boolean_mono_info);
250862306a36Sopenharmony_ci
250962306a36Sopenharmony_ci/**
251062306a36Sopenharmony_ci * snd_ctl_boolean_stereo_info - Helper function for a standard boolean info
251162306a36Sopenharmony_ci * callback with stereo two channels
251262306a36Sopenharmony_ci * @kcontrol: the kcontrol instance
251362306a36Sopenharmony_ci * @uinfo: info to store
251462306a36Sopenharmony_ci *
251562306a36Sopenharmony_ci * This is a function that can be used as info callback for a standard
251662306a36Sopenharmony_ci * boolean control with stereo two channels.
251762306a36Sopenharmony_ci *
251862306a36Sopenharmony_ci * Return: Zero (always successful)
251962306a36Sopenharmony_ci */
252062306a36Sopenharmony_ciint snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
252162306a36Sopenharmony_ci				struct snd_ctl_elem_info *uinfo)
252262306a36Sopenharmony_ci{
252362306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
252462306a36Sopenharmony_ci	uinfo->count = 2;
252562306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
252662306a36Sopenharmony_ci	uinfo->value.integer.max = 1;
252762306a36Sopenharmony_ci	return 0;
252862306a36Sopenharmony_ci}
252962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_boolean_stereo_info);
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_ci/**
253262306a36Sopenharmony_ci * snd_ctl_enum_info - fills the info structure for an enumerated control
253362306a36Sopenharmony_ci * @info: the structure to be filled
253462306a36Sopenharmony_ci * @channels: the number of the control's channels; often one
253562306a36Sopenharmony_ci * @items: the number of control values; also the size of @names
253662306a36Sopenharmony_ci * @names: an array containing the names of all control values
253762306a36Sopenharmony_ci *
253862306a36Sopenharmony_ci * Sets all required fields in @info to their appropriate values.
253962306a36Sopenharmony_ci * If the control's accessibility is not the default (readable and writable),
254062306a36Sopenharmony_ci * the caller has to fill @info->access.
254162306a36Sopenharmony_ci *
254262306a36Sopenharmony_ci * Return: Zero (always successful)
254362306a36Sopenharmony_ci */
254462306a36Sopenharmony_ciint snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
254562306a36Sopenharmony_ci		      unsigned int items, const char *const names[])
254662306a36Sopenharmony_ci{
254762306a36Sopenharmony_ci	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
254862306a36Sopenharmony_ci	info->count = channels;
254962306a36Sopenharmony_ci	info->value.enumerated.items = items;
255062306a36Sopenharmony_ci	if (!items)
255162306a36Sopenharmony_ci		return 0;
255262306a36Sopenharmony_ci	if (info->value.enumerated.item >= items)
255362306a36Sopenharmony_ci		info->value.enumerated.item = items - 1;
255462306a36Sopenharmony_ci	WARN(strlen(names[info->value.enumerated.item]) >= sizeof(info->value.enumerated.name),
255562306a36Sopenharmony_ci	     "ALSA: too long item name '%s'\n",
255662306a36Sopenharmony_ci	     names[info->value.enumerated.item]);
255762306a36Sopenharmony_ci	strscpy(info->value.enumerated.name,
255862306a36Sopenharmony_ci		names[info->value.enumerated.item],
255962306a36Sopenharmony_ci		sizeof(info->value.enumerated.name));
256062306a36Sopenharmony_ci	return 0;
256162306a36Sopenharmony_ci}
256262306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_enum_info);
2563