162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Initialization routines
462306a36Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/init.h>
862306a36Sopenharmony_ci#include <linux/sched.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/device.h>
1162306a36Sopenharmony_ci#include <linux/file.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/time.h>
1462306a36Sopenharmony_ci#include <linux/ctype.h>
1562306a36Sopenharmony_ci#include <linux/pm.h>
1662306a36Sopenharmony_ci#include <linux/debugfs.h>
1762306a36Sopenharmony_ci#include <linux/completion.h>
1862306a36Sopenharmony_ci#include <linux/interrupt.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <sound/core.h>
2162306a36Sopenharmony_ci#include <sound/control.h>
2262306a36Sopenharmony_ci#include <sound/info.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* monitor files for graceful shutdown (hotplug) */
2562306a36Sopenharmony_cistruct snd_monitor_file {
2662306a36Sopenharmony_ci	struct file *file;
2762306a36Sopenharmony_ci	const struct file_operations *disconnected_f_op;
2862306a36Sopenharmony_ci	struct list_head shutdown_list;	/* still need to shutdown */
2962306a36Sopenharmony_ci	struct list_head list;	/* link of monitor files */
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic DEFINE_SPINLOCK(shutdown_lock);
3362306a36Sopenharmony_cistatic LIST_HEAD(shutdown_files);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic const struct file_operations snd_shutdown_f_ops;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/* locked for registering/using */
3862306a36Sopenharmony_cistatic DECLARE_BITMAP(snd_cards_lock, SNDRV_CARDS);
3962306a36Sopenharmony_cistatic struct snd_card *snd_cards[SNDRV_CARDS];
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic DEFINE_MUTEX(snd_card_mutex);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic char *slots[SNDRV_CARDS];
4462306a36Sopenharmony_cimodule_param_array(slots, charp, NULL, 0444);
4562306a36Sopenharmony_ciMODULE_PARM_DESC(slots, "Module names assigned to the slots.");
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* return non-zero if the given index is reserved for the given
4862306a36Sopenharmony_ci * module via slots option
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_cistatic int module_slot_match(struct module *module, int idx)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	int match = 1;
5362306a36Sopenharmony_ci#ifdef MODULE
5462306a36Sopenharmony_ci	const char *s1, *s2;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (!module || !*module->name || !slots[idx])
5762306a36Sopenharmony_ci		return 0;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	s1 = module->name;
6062306a36Sopenharmony_ci	s2 = slots[idx];
6162306a36Sopenharmony_ci	if (*s2 == '!') {
6262306a36Sopenharmony_ci		match = 0; /* negative match */
6362306a36Sopenharmony_ci		s2++;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci	/* compare module name strings
6662306a36Sopenharmony_ci	 * hyphens are handled as equivalent with underscore
6762306a36Sopenharmony_ci	 */
6862306a36Sopenharmony_ci	for (;;) {
6962306a36Sopenharmony_ci		char c1 = *s1++;
7062306a36Sopenharmony_ci		char c2 = *s2++;
7162306a36Sopenharmony_ci		if (c1 == '-')
7262306a36Sopenharmony_ci			c1 = '_';
7362306a36Sopenharmony_ci		if (c2 == '-')
7462306a36Sopenharmony_ci			c2 = '_';
7562306a36Sopenharmony_ci		if (c1 != c2)
7662306a36Sopenharmony_ci			return !match;
7762306a36Sopenharmony_ci		if (!c1)
7862306a36Sopenharmony_ci			break;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci#endif /* MODULE */
8162306a36Sopenharmony_ci	return match;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
8562306a36Sopenharmony_ciint (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);
8662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_mixer_oss_notify_callback);
8762306a36Sopenharmony_ci#endif
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic int check_empty_slot(struct module *module, int slot)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	return !slots[slot] || !*slots[slot];
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/* return an empty slot number (>= 0) found in the given bitmask @mask.
9562306a36Sopenharmony_ci * @mask == -1 == 0xffffffff means: take any free slot up to 32
9662306a36Sopenharmony_ci * when no slot is available, return the original @mask as is.
9762306a36Sopenharmony_ci */
9862306a36Sopenharmony_cistatic int get_slot_from_bitmask(int mask, int (*check)(struct module *, int),
9962306a36Sopenharmony_ci				 struct module *module)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	int slot;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	for (slot = 0; slot < SNDRV_CARDS; slot++) {
10462306a36Sopenharmony_ci		if (slot < 32 && !(mask & (1U << slot)))
10562306a36Sopenharmony_ci			continue;
10662306a36Sopenharmony_ci		if (!test_bit(slot, snd_cards_lock)) {
10762306a36Sopenharmony_ci			if (check(module, slot))
10862306a36Sopenharmony_ci				return slot; /* found */
10962306a36Sopenharmony_ci		}
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci	return mask; /* unchanged */
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/* the default release callback set in snd_device_alloc() */
11562306a36Sopenharmony_cistatic void default_release_alloc(struct device *dev)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	kfree(dev);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci/**
12162306a36Sopenharmony_ci * snd_device_alloc - Allocate and initialize struct device for sound devices
12262306a36Sopenharmony_ci * @dev_p: pointer to store the allocated device
12362306a36Sopenharmony_ci * @card: card to assign, optional
12462306a36Sopenharmony_ci *
12562306a36Sopenharmony_ci * For releasing the allocated device, call put_device().
12662306a36Sopenharmony_ci */
12762306a36Sopenharmony_ciint snd_device_alloc(struct device **dev_p, struct snd_card *card)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct device *dev;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	*dev_p = NULL;
13262306a36Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
13362306a36Sopenharmony_ci	if (!dev)
13462306a36Sopenharmony_ci		return -ENOMEM;
13562306a36Sopenharmony_ci	device_initialize(dev);
13662306a36Sopenharmony_ci	if (card)
13762306a36Sopenharmony_ci		dev->parent = &card->card_dev;
13862306a36Sopenharmony_ci	dev->class = &sound_class;
13962306a36Sopenharmony_ci	dev->release = default_release_alloc;
14062306a36Sopenharmony_ci	*dev_p = dev;
14162306a36Sopenharmony_ci	return 0;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_device_alloc);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic int snd_card_init(struct snd_card *card, struct device *parent,
14662306a36Sopenharmony_ci			 int idx, const char *xid, struct module *module,
14762306a36Sopenharmony_ci			 size_t extra_size);
14862306a36Sopenharmony_cistatic int snd_card_do_free(struct snd_card *card);
14962306a36Sopenharmony_cistatic const struct attribute_group card_dev_attr_group;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic void release_card_device(struct device *dev)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	snd_card_do_free(dev_to_snd_card(dev));
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/**
15762306a36Sopenharmony_ci *  snd_card_new - create and initialize a soundcard structure
15862306a36Sopenharmony_ci *  @parent: the parent device object
15962306a36Sopenharmony_ci *  @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
16062306a36Sopenharmony_ci *  @xid: card identification (ASCII string)
16162306a36Sopenharmony_ci *  @module: top level module for locking
16262306a36Sopenharmony_ci *  @extra_size: allocate this extra size after the main soundcard structure
16362306a36Sopenharmony_ci *  @card_ret: the pointer to store the created card instance
16462306a36Sopenharmony_ci *
16562306a36Sopenharmony_ci *  The function allocates snd_card instance via kzalloc with the given
16662306a36Sopenharmony_ci *  space for the driver to use freely.  The allocated struct is stored
16762306a36Sopenharmony_ci *  in the given card_ret pointer.
16862306a36Sopenharmony_ci *
16962306a36Sopenharmony_ci *  Return: Zero if successful or a negative error code.
17062306a36Sopenharmony_ci */
17162306a36Sopenharmony_ciint snd_card_new(struct device *parent, int idx, const char *xid,
17262306a36Sopenharmony_ci		    struct module *module, int extra_size,
17362306a36Sopenharmony_ci		    struct snd_card **card_ret)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct snd_card *card;
17662306a36Sopenharmony_ci	int err;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (snd_BUG_ON(!card_ret))
17962306a36Sopenharmony_ci		return -EINVAL;
18062306a36Sopenharmony_ci	*card_ret = NULL;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (extra_size < 0)
18362306a36Sopenharmony_ci		extra_size = 0;
18462306a36Sopenharmony_ci	card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
18562306a36Sopenharmony_ci	if (!card)
18662306a36Sopenharmony_ci		return -ENOMEM;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	err = snd_card_init(card, parent, idx, xid, module, extra_size);
18962306a36Sopenharmony_ci	if (err < 0)
19062306a36Sopenharmony_ci		return err; /* card is freed by error handler */
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	*card_ret = card;
19362306a36Sopenharmony_ci	return 0;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ciEXPORT_SYMBOL(snd_card_new);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic void __snd_card_release(struct device *dev, void *data)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	snd_card_free(data);
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci/**
20362306a36Sopenharmony_ci * snd_devm_card_new - managed snd_card object creation
20462306a36Sopenharmony_ci * @parent: the parent device object
20562306a36Sopenharmony_ci * @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
20662306a36Sopenharmony_ci * @xid: card identification (ASCII string)
20762306a36Sopenharmony_ci * @module: top level module for locking
20862306a36Sopenharmony_ci * @extra_size: allocate this extra size after the main soundcard structure
20962306a36Sopenharmony_ci * @card_ret: the pointer to store the created card instance
21062306a36Sopenharmony_ci *
21162306a36Sopenharmony_ci * This function works like snd_card_new() but manages the allocated resource
21262306a36Sopenharmony_ci * via devres, i.e. you don't need to free explicitly.
21362306a36Sopenharmony_ci *
21462306a36Sopenharmony_ci * When a snd_card object is created with this function and registered via
21562306a36Sopenharmony_ci * snd_card_register(), the very first devres action to call snd_card_free()
21662306a36Sopenharmony_ci * is added automatically.  In that way, the resource disconnection is assured
21762306a36Sopenharmony_ci * at first, then released in the expected order.
21862306a36Sopenharmony_ci *
21962306a36Sopenharmony_ci * If an error happens at the probe before snd_card_register() is called and
22062306a36Sopenharmony_ci * there have been other devres resources, you'd need to free the card manually
22162306a36Sopenharmony_ci * via snd_card_free() call in the error; otherwise it may lead to UAF due to
22262306a36Sopenharmony_ci * devres call orders.  You can use snd_card_free_on_error() helper for
22362306a36Sopenharmony_ci * handling it more easily.
22462306a36Sopenharmony_ci *
22562306a36Sopenharmony_ci * Return: zero if successful, or a negative error code
22662306a36Sopenharmony_ci */
22762306a36Sopenharmony_ciint snd_devm_card_new(struct device *parent, int idx, const char *xid,
22862306a36Sopenharmony_ci		      struct module *module, size_t extra_size,
22962306a36Sopenharmony_ci		      struct snd_card **card_ret)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct snd_card *card;
23262306a36Sopenharmony_ci	int err;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	*card_ret = NULL;
23562306a36Sopenharmony_ci	card = devres_alloc(__snd_card_release, sizeof(*card) + extra_size,
23662306a36Sopenharmony_ci			    GFP_KERNEL);
23762306a36Sopenharmony_ci	if (!card)
23862306a36Sopenharmony_ci		return -ENOMEM;
23962306a36Sopenharmony_ci	card->managed = true;
24062306a36Sopenharmony_ci	err = snd_card_init(card, parent, idx, xid, module, extra_size);
24162306a36Sopenharmony_ci	if (err < 0) {
24262306a36Sopenharmony_ci		devres_free(card); /* in managed mode, we need to free manually */
24362306a36Sopenharmony_ci		return err;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	devres_add(parent, card);
24762306a36Sopenharmony_ci	*card_ret = card;
24862306a36Sopenharmony_ci	return 0;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_devm_card_new);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci/**
25362306a36Sopenharmony_ci * snd_card_free_on_error - a small helper for handling devm probe errors
25462306a36Sopenharmony_ci * @dev: the managed device object
25562306a36Sopenharmony_ci * @ret: the return code from the probe callback
25662306a36Sopenharmony_ci *
25762306a36Sopenharmony_ci * This function handles the explicit snd_card_free() call at the error from
25862306a36Sopenharmony_ci * the probe callback.  It's just a small helper for simplifying the error
25962306a36Sopenharmony_ci * handling with the managed devices.
26062306a36Sopenharmony_ci *
26162306a36Sopenharmony_ci * Return: zero if successful, or a negative error code
26262306a36Sopenharmony_ci */
26362306a36Sopenharmony_ciint snd_card_free_on_error(struct device *dev, int ret)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct snd_card *card;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (!ret)
26862306a36Sopenharmony_ci		return 0;
26962306a36Sopenharmony_ci	card = devres_find(dev, __snd_card_release, NULL, NULL);
27062306a36Sopenharmony_ci	if (card)
27162306a36Sopenharmony_ci		snd_card_free(card);
27262306a36Sopenharmony_ci	return ret;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_card_free_on_error);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic int snd_card_init(struct snd_card *card, struct device *parent,
27762306a36Sopenharmony_ci			 int idx, const char *xid, struct module *module,
27862306a36Sopenharmony_ci			 size_t extra_size)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	int err;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (extra_size > 0)
28362306a36Sopenharmony_ci		card->private_data = (char *)card + sizeof(struct snd_card);
28462306a36Sopenharmony_ci	if (xid)
28562306a36Sopenharmony_ci		strscpy(card->id, xid, sizeof(card->id));
28662306a36Sopenharmony_ci	err = 0;
28762306a36Sopenharmony_ci	mutex_lock(&snd_card_mutex);
28862306a36Sopenharmony_ci	if (idx < 0) /* first check the matching module-name slot */
28962306a36Sopenharmony_ci		idx = get_slot_from_bitmask(idx, module_slot_match, module);
29062306a36Sopenharmony_ci	if (idx < 0) /* if not matched, assign an empty slot */
29162306a36Sopenharmony_ci		idx = get_slot_from_bitmask(idx, check_empty_slot, module);
29262306a36Sopenharmony_ci	if (idx < 0)
29362306a36Sopenharmony_ci		err = -ENODEV;
29462306a36Sopenharmony_ci	else if (idx < snd_ecards_limit) {
29562306a36Sopenharmony_ci		if (test_bit(idx, snd_cards_lock))
29662306a36Sopenharmony_ci			err = -EBUSY;	/* invalid */
29762306a36Sopenharmony_ci	} else if (idx >= SNDRV_CARDS)
29862306a36Sopenharmony_ci		err = -ENODEV;
29962306a36Sopenharmony_ci	if (err < 0) {
30062306a36Sopenharmony_ci		mutex_unlock(&snd_card_mutex);
30162306a36Sopenharmony_ci		dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n",
30262306a36Sopenharmony_ci			 idx, snd_ecards_limit - 1, err);
30362306a36Sopenharmony_ci		if (!card->managed)
30462306a36Sopenharmony_ci			kfree(card); /* manually free here, as no destructor called */
30562306a36Sopenharmony_ci		return err;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci	set_bit(idx, snd_cards_lock);		/* lock it */
30862306a36Sopenharmony_ci	if (idx >= snd_ecards_limit)
30962306a36Sopenharmony_ci		snd_ecards_limit = idx + 1; /* increase the limit */
31062306a36Sopenharmony_ci	mutex_unlock(&snd_card_mutex);
31162306a36Sopenharmony_ci	card->dev = parent;
31262306a36Sopenharmony_ci	card->number = idx;
31362306a36Sopenharmony_ci#ifdef MODULE
31462306a36Sopenharmony_ci	WARN_ON(!module);
31562306a36Sopenharmony_ci	card->module = module;
31662306a36Sopenharmony_ci#endif
31762306a36Sopenharmony_ci	INIT_LIST_HEAD(&card->devices);
31862306a36Sopenharmony_ci	init_rwsem(&card->controls_rwsem);
31962306a36Sopenharmony_ci	rwlock_init(&card->ctl_files_rwlock);
32062306a36Sopenharmony_ci	INIT_LIST_HEAD(&card->controls);
32162306a36Sopenharmony_ci	INIT_LIST_HEAD(&card->ctl_files);
32262306a36Sopenharmony_ci#ifdef CONFIG_SND_CTL_FAST_LOOKUP
32362306a36Sopenharmony_ci	xa_init(&card->ctl_numids);
32462306a36Sopenharmony_ci	xa_init(&card->ctl_hash);
32562306a36Sopenharmony_ci#endif
32662306a36Sopenharmony_ci	spin_lock_init(&card->files_lock);
32762306a36Sopenharmony_ci	INIT_LIST_HEAD(&card->files_list);
32862306a36Sopenharmony_ci	mutex_init(&card->memory_mutex);
32962306a36Sopenharmony_ci#ifdef CONFIG_PM
33062306a36Sopenharmony_ci	init_waitqueue_head(&card->power_sleep);
33162306a36Sopenharmony_ci	init_waitqueue_head(&card->power_ref_sleep);
33262306a36Sopenharmony_ci	atomic_set(&card->power_ref, 0);
33362306a36Sopenharmony_ci#endif
33462306a36Sopenharmony_ci	init_waitqueue_head(&card->remove_sleep);
33562306a36Sopenharmony_ci	card->sync_irq = -1;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	device_initialize(&card->card_dev);
33862306a36Sopenharmony_ci	card->card_dev.parent = parent;
33962306a36Sopenharmony_ci	card->card_dev.class = &sound_class;
34062306a36Sopenharmony_ci	card->card_dev.release = release_card_device;
34162306a36Sopenharmony_ci	card->card_dev.groups = card->dev_groups;
34262306a36Sopenharmony_ci	card->dev_groups[0] = &card_dev_attr_group;
34362306a36Sopenharmony_ci	err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);
34462306a36Sopenharmony_ci	if (err < 0)
34562306a36Sopenharmony_ci		goto __error;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	snprintf(card->irq_descr, sizeof(card->irq_descr), "%s:%s",
34862306a36Sopenharmony_ci		 dev_driver_string(card->dev), dev_name(&card->card_dev));
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* the control interface cannot be accessed from the user space until */
35162306a36Sopenharmony_ci	/* snd_cards_bitmask and snd_cards are set with snd_card_register */
35262306a36Sopenharmony_ci	err = snd_ctl_create(card);
35362306a36Sopenharmony_ci	if (err < 0) {
35462306a36Sopenharmony_ci		dev_err(parent, "unable to register control minors\n");
35562306a36Sopenharmony_ci		goto __error;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci	err = snd_info_card_create(card);
35862306a36Sopenharmony_ci	if (err < 0) {
35962306a36Sopenharmony_ci		dev_err(parent, "unable to create card info\n");
36062306a36Sopenharmony_ci		goto __error_ctl;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
36462306a36Sopenharmony_ci	card->debugfs_root = debugfs_create_dir(dev_name(&card->card_dev),
36562306a36Sopenharmony_ci						sound_debugfs_root);
36662306a36Sopenharmony_ci#endif
36762306a36Sopenharmony_ci	return 0;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci      __error_ctl:
37062306a36Sopenharmony_ci	snd_device_free_all(card);
37162306a36Sopenharmony_ci      __error:
37262306a36Sopenharmony_ci	put_device(&card->card_dev);
37362306a36Sopenharmony_ci  	return err;
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci/**
37762306a36Sopenharmony_ci * snd_card_ref - Get the card object from the index
37862306a36Sopenharmony_ci * @idx: the card index
37962306a36Sopenharmony_ci *
38062306a36Sopenharmony_ci * Returns a card object corresponding to the given index or NULL if not found.
38162306a36Sopenharmony_ci * Release the object via snd_card_unref().
38262306a36Sopenharmony_ci *
38362306a36Sopenharmony_ci * Return: a card object or NULL
38462306a36Sopenharmony_ci */
38562306a36Sopenharmony_cistruct snd_card *snd_card_ref(int idx)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct snd_card *card;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	mutex_lock(&snd_card_mutex);
39062306a36Sopenharmony_ci	card = snd_cards[idx];
39162306a36Sopenharmony_ci	if (card)
39262306a36Sopenharmony_ci		get_device(&card->card_dev);
39362306a36Sopenharmony_ci	mutex_unlock(&snd_card_mutex);
39462306a36Sopenharmony_ci	return card;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_card_ref);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci/* return non-zero if a card is already locked */
39962306a36Sopenharmony_ciint snd_card_locked(int card)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	int locked;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	mutex_lock(&snd_card_mutex);
40462306a36Sopenharmony_ci	locked = test_bit(card, snd_cards_lock);
40562306a36Sopenharmony_ci	mutex_unlock(&snd_card_mutex);
40662306a36Sopenharmony_ci	return locked;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic loff_t snd_disconnect_llseek(struct file *file, loff_t offset, int orig)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	return -ENODEV;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic ssize_t snd_disconnect_read(struct file *file, char __user *buf,
41562306a36Sopenharmony_ci				   size_t count, loff_t *offset)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	return -ENODEV;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic ssize_t snd_disconnect_write(struct file *file, const char __user *buf,
42162306a36Sopenharmony_ci				    size_t count, loff_t *offset)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	return -ENODEV;
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic int snd_disconnect_release(struct inode *inode, struct file *file)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	struct snd_monitor_file *df = NULL, *_df;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	spin_lock(&shutdown_lock);
43162306a36Sopenharmony_ci	list_for_each_entry(_df, &shutdown_files, shutdown_list) {
43262306a36Sopenharmony_ci		if (_df->file == file) {
43362306a36Sopenharmony_ci			df = _df;
43462306a36Sopenharmony_ci			list_del_init(&df->shutdown_list);
43562306a36Sopenharmony_ci			break;
43662306a36Sopenharmony_ci		}
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci	spin_unlock(&shutdown_lock);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (likely(df)) {
44162306a36Sopenharmony_ci		if ((file->f_flags & FASYNC) && df->disconnected_f_op->fasync)
44262306a36Sopenharmony_ci			df->disconnected_f_op->fasync(-1, file, 0);
44362306a36Sopenharmony_ci		return df->disconnected_f_op->release(inode, file);
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	panic("%s(%p, %p) failed!", __func__, inode, file);
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic __poll_t snd_disconnect_poll(struct file * file, poll_table * wait)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	return EPOLLERR | EPOLLNVAL;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic long snd_disconnect_ioctl(struct file *file,
45562306a36Sopenharmony_ci				 unsigned int cmd, unsigned long arg)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	return -ENODEV;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic int snd_disconnect_mmap(struct file *file, struct vm_area_struct *vma)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	return -ENODEV;
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic int snd_disconnect_fasync(int fd, struct file *file, int on)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	return -ENODEV;
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic const struct file_operations snd_shutdown_f_ops =
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	.owner = 	THIS_MODULE,
47362306a36Sopenharmony_ci	.llseek =	snd_disconnect_llseek,
47462306a36Sopenharmony_ci	.read = 	snd_disconnect_read,
47562306a36Sopenharmony_ci	.write =	snd_disconnect_write,
47662306a36Sopenharmony_ci	.release =	snd_disconnect_release,
47762306a36Sopenharmony_ci	.poll =		snd_disconnect_poll,
47862306a36Sopenharmony_ci	.unlocked_ioctl = snd_disconnect_ioctl,
47962306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
48062306a36Sopenharmony_ci	.compat_ioctl = snd_disconnect_ioctl,
48162306a36Sopenharmony_ci#endif
48262306a36Sopenharmony_ci	.mmap =		snd_disconnect_mmap,
48362306a36Sopenharmony_ci	.fasync =	snd_disconnect_fasync
48462306a36Sopenharmony_ci};
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci/**
48762306a36Sopenharmony_ci *  snd_card_disconnect - disconnect all APIs from the file-operations (user space)
48862306a36Sopenharmony_ci *  @card: soundcard structure
48962306a36Sopenharmony_ci *
49062306a36Sopenharmony_ci *  Disconnects all APIs from the file-operations (user space).
49162306a36Sopenharmony_ci *
49262306a36Sopenharmony_ci *  Return: Zero, otherwise a negative error code.
49362306a36Sopenharmony_ci *
49462306a36Sopenharmony_ci *  Note: The current implementation replaces all active file->f_op with special
49562306a36Sopenharmony_ci *        dummy file operations (they do nothing except release).
49662306a36Sopenharmony_ci */
49762306a36Sopenharmony_civoid snd_card_disconnect(struct snd_card *card)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	struct snd_monitor_file *mfile;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (!card)
50262306a36Sopenharmony_ci		return;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	spin_lock(&card->files_lock);
50562306a36Sopenharmony_ci	if (card->shutdown) {
50662306a36Sopenharmony_ci		spin_unlock(&card->files_lock);
50762306a36Sopenharmony_ci		return;
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci	card->shutdown = 1;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	/* replace file->f_op with special dummy operations */
51262306a36Sopenharmony_ci	list_for_each_entry(mfile, &card->files_list, list) {
51362306a36Sopenharmony_ci		/* it's critical part, use endless loop */
51462306a36Sopenharmony_ci		/* we have no room to fail */
51562306a36Sopenharmony_ci		mfile->disconnected_f_op = mfile->file->f_op;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		spin_lock(&shutdown_lock);
51862306a36Sopenharmony_ci		list_add(&mfile->shutdown_list, &shutdown_files);
51962306a36Sopenharmony_ci		spin_unlock(&shutdown_lock);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		mfile->file->f_op = &snd_shutdown_f_ops;
52262306a36Sopenharmony_ci		fops_get(mfile->file->f_op);
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci	spin_unlock(&card->files_lock);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	/* notify all connected devices about disconnection */
52762306a36Sopenharmony_ci	/* at this point, they cannot respond to any calls except release() */
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
53062306a36Sopenharmony_ci	if (snd_mixer_oss_notify_callback)
53162306a36Sopenharmony_ci		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT);
53262306a36Sopenharmony_ci#endif
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	/* notify all devices that we are disconnected */
53562306a36Sopenharmony_ci	snd_device_disconnect_all(card);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (card->sync_irq > 0)
53862306a36Sopenharmony_ci		synchronize_irq(card->sync_irq);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	snd_info_card_disconnect(card);
54162306a36Sopenharmony_ci	if (card->registered) {
54262306a36Sopenharmony_ci		device_del(&card->card_dev);
54362306a36Sopenharmony_ci		card->registered = false;
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	/* disable fops (user space) operations for ALSA API */
54762306a36Sopenharmony_ci	mutex_lock(&snd_card_mutex);
54862306a36Sopenharmony_ci	snd_cards[card->number] = NULL;
54962306a36Sopenharmony_ci	clear_bit(card->number, snd_cards_lock);
55062306a36Sopenharmony_ci	mutex_unlock(&snd_card_mutex);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci#ifdef CONFIG_PM
55362306a36Sopenharmony_ci	wake_up(&card->power_sleep);
55462306a36Sopenharmony_ci	snd_power_sync_ref(card);
55562306a36Sopenharmony_ci#endif
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_card_disconnect);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci/**
56062306a36Sopenharmony_ci * snd_card_disconnect_sync - disconnect card and wait until files get closed
56162306a36Sopenharmony_ci * @card: card object to disconnect
56262306a36Sopenharmony_ci *
56362306a36Sopenharmony_ci * This calls snd_card_disconnect() for disconnecting all belonging components
56462306a36Sopenharmony_ci * and waits until all pending files get closed.
56562306a36Sopenharmony_ci * It assures that all accesses from user-space finished so that the driver
56662306a36Sopenharmony_ci * can release its resources gracefully.
56762306a36Sopenharmony_ci */
56862306a36Sopenharmony_civoid snd_card_disconnect_sync(struct snd_card *card)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	snd_card_disconnect(card);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	spin_lock_irq(&card->files_lock);
57362306a36Sopenharmony_ci	wait_event_lock_irq(card->remove_sleep,
57462306a36Sopenharmony_ci			    list_empty(&card->files_list),
57562306a36Sopenharmony_ci			    card->files_lock);
57662306a36Sopenharmony_ci	spin_unlock_irq(&card->files_lock);
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_card_disconnect_sync);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic int snd_card_do_free(struct snd_card *card)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	card->releasing = true;
58362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
58462306a36Sopenharmony_ci	if (snd_mixer_oss_notify_callback)
58562306a36Sopenharmony_ci		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
58662306a36Sopenharmony_ci#endif
58762306a36Sopenharmony_ci	snd_device_free_all(card);
58862306a36Sopenharmony_ci	if (card->private_free)
58962306a36Sopenharmony_ci		card->private_free(card);
59062306a36Sopenharmony_ci	if (snd_info_card_free(card) < 0) {
59162306a36Sopenharmony_ci		dev_warn(card->dev, "unable to free card info\n");
59262306a36Sopenharmony_ci		/* Not fatal error */
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
59562306a36Sopenharmony_ci	debugfs_remove(card->debugfs_root);
59662306a36Sopenharmony_ci	card->debugfs_root = NULL;
59762306a36Sopenharmony_ci#endif
59862306a36Sopenharmony_ci	if (card->release_completion)
59962306a36Sopenharmony_ci		complete(card->release_completion);
60062306a36Sopenharmony_ci	if (!card->managed)
60162306a36Sopenharmony_ci		kfree(card);
60262306a36Sopenharmony_ci	return 0;
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci/**
60662306a36Sopenharmony_ci * snd_card_free_when_closed - Disconnect the card, free it later eventually
60762306a36Sopenharmony_ci * @card: soundcard structure
60862306a36Sopenharmony_ci *
60962306a36Sopenharmony_ci * Unlike snd_card_free(), this function doesn't try to release the card
61062306a36Sopenharmony_ci * resource immediately, but tries to disconnect at first.  When the card
61162306a36Sopenharmony_ci * is still in use, the function returns before freeing the resources.
61262306a36Sopenharmony_ci * The card resources will be freed when the refcount gets to zero.
61362306a36Sopenharmony_ci *
61462306a36Sopenharmony_ci * Return: zero if successful, or a negative error code
61562306a36Sopenharmony_ci */
61662306a36Sopenharmony_civoid snd_card_free_when_closed(struct snd_card *card)
61762306a36Sopenharmony_ci{
61862306a36Sopenharmony_ci	if (!card)
61962306a36Sopenharmony_ci		return;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	snd_card_disconnect(card);
62262306a36Sopenharmony_ci	put_device(&card->card_dev);
62362306a36Sopenharmony_ci	return;
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ciEXPORT_SYMBOL(snd_card_free_when_closed);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci/**
62862306a36Sopenharmony_ci * snd_card_free - frees given soundcard structure
62962306a36Sopenharmony_ci * @card: soundcard structure
63062306a36Sopenharmony_ci *
63162306a36Sopenharmony_ci * This function releases the soundcard structure and the all assigned
63262306a36Sopenharmony_ci * devices automatically.  That is, you don't have to release the devices
63362306a36Sopenharmony_ci * by yourself.
63462306a36Sopenharmony_ci *
63562306a36Sopenharmony_ci * This function waits until the all resources are properly released.
63662306a36Sopenharmony_ci *
63762306a36Sopenharmony_ci * Return: Zero. Frees all associated devices and frees the control
63862306a36Sopenharmony_ci * interface associated to given soundcard.
63962306a36Sopenharmony_ci */
64062306a36Sopenharmony_civoid snd_card_free(struct snd_card *card)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(released);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	/* The call of snd_card_free() is allowed from various code paths;
64562306a36Sopenharmony_ci	 * a manual call from the driver and the call via devres_free, and
64662306a36Sopenharmony_ci	 * we need to avoid double-free. Moreover, the release via devres
64762306a36Sopenharmony_ci	 * may call snd_card_free() twice due to its nature, we need to have
64862306a36Sopenharmony_ci	 * the check here at the beginning.
64962306a36Sopenharmony_ci	 */
65062306a36Sopenharmony_ci	if (card->releasing)
65162306a36Sopenharmony_ci		return;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	card->release_completion = &released;
65462306a36Sopenharmony_ci	snd_card_free_when_closed(card);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	/* wait, until all devices are ready for the free operation */
65762306a36Sopenharmony_ci	wait_for_completion(&released);
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_card_free);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci/* retrieve the last word of shortname or longname */
66262306a36Sopenharmony_cistatic const char *retrieve_id_from_card_name(const char *name)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	const char *spos = name;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	while (*name) {
66762306a36Sopenharmony_ci		if (isspace(*name) && isalnum(name[1]))
66862306a36Sopenharmony_ci			spos = name + 1;
66962306a36Sopenharmony_ci		name++;
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci	return spos;
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci/* return true if the given id string doesn't conflict any other card ids */
67562306a36Sopenharmony_cistatic bool card_id_ok(struct snd_card *card, const char *id)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	int i;
67862306a36Sopenharmony_ci	if (!snd_info_check_reserved_words(id))
67962306a36Sopenharmony_ci		return false;
68062306a36Sopenharmony_ci	for (i = 0; i < snd_ecards_limit; i++) {
68162306a36Sopenharmony_ci		if (snd_cards[i] && snd_cards[i] != card &&
68262306a36Sopenharmony_ci		    !strcmp(snd_cards[i]->id, id))
68362306a36Sopenharmony_ci			return false;
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci	return true;
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci/* copy to card->id only with valid letters from nid */
68962306a36Sopenharmony_cistatic void copy_valid_id_string(struct snd_card *card, const char *src,
69062306a36Sopenharmony_ci				 const char *nid)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	char *id = card->id;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	while (*nid && !isalnum(*nid))
69562306a36Sopenharmony_ci		nid++;
69662306a36Sopenharmony_ci	if (isdigit(*nid))
69762306a36Sopenharmony_ci		*id++ = isalpha(*src) ? *src : 'D';
69862306a36Sopenharmony_ci	while (*nid && (size_t)(id - card->id) < sizeof(card->id) - 1) {
69962306a36Sopenharmony_ci		if (isalnum(*nid))
70062306a36Sopenharmony_ci			*id++ = *nid;
70162306a36Sopenharmony_ci		nid++;
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci	*id = 0;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci/* Set card->id from the given string
70762306a36Sopenharmony_ci * If the string conflicts with other ids, add a suffix to make it unique.
70862306a36Sopenharmony_ci */
70962306a36Sopenharmony_cistatic void snd_card_set_id_no_lock(struct snd_card *card, const char *src,
71062306a36Sopenharmony_ci				    const char *nid)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	int len, loops;
71362306a36Sopenharmony_ci	bool is_default = false;
71462306a36Sopenharmony_ci	char *id;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	copy_valid_id_string(card, src, nid);
71762306a36Sopenharmony_ci	id = card->id;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci again:
72062306a36Sopenharmony_ci	/* use "Default" for obviously invalid strings
72162306a36Sopenharmony_ci	 * ("card" conflicts with proc directories)
72262306a36Sopenharmony_ci	 */
72362306a36Sopenharmony_ci	if (!*id || !strncmp(id, "card", 4)) {
72462306a36Sopenharmony_ci		strcpy(id, "Default");
72562306a36Sopenharmony_ci		is_default = true;
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	len = strlen(id);
72962306a36Sopenharmony_ci	for (loops = 0; loops < SNDRV_CARDS; loops++) {
73062306a36Sopenharmony_ci		char *spos;
73162306a36Sopenharmony_ci		char sfxstr[5]; /* "_012" */
73262306a36Sopenharmony_ci		int sfxlen;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci		if (card_id_ok(card, id))
73562306a36Sopenharmony_ci			return; /* OK */
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci		/* Add _XYZ suffix */
73862306a36Sopenharmony_ci		sprintf(sfxstr, "_%X", loops + 1);
73962306a36Sopenharmony_ci		sfxlen = strlen(sfxstr);
74062306a36Sopenharmony_ci		if (len + sfxlen >= sizeof(card->id))
74162306a36Sopenharmony_ci			spos = id + sizeof(card->id) - sfxlen - 1;
74262306a36Sopenharmony_ci		else
74362306a36Sopenharmony_ci			spos = id + len;
74462306a36Sopenharmony_ci		strcpy(spos, sfxstr);
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci	/* fallback to the default id */
74762306a36Sopenharmony_ci	if (!is_default) {
74862306a36Sopenharmony_ci		*id = 0;
74962306a36Sopenharmony_ci		goto again;
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci	/* last resort... */
75262306a36Sopenharmony_ci	dev_err(card->dev, "unable to set card id (%s)\n", id);
75362306a36Sopenharmony_ci	if (card->proc_root->name)
75462306a36Sopenharmony_ci		strscpy(card->id, card->proc_root->name, sizeof(card->id));
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci/**
75862306a36Sopenharmony_ci *  snd_card_set_id - set card identification name
75962306a36Sopenharmony_ci *  @card: soundcard structure
76062306a36Sopenharmony_ci *  @nid: new identification string
76162306a36Sopenharmony_ci *
76262306a36Sopenharmony_ci *  This function sets the card identification and checks for name
76362306a36Sopenharmony_ci *  collisions.
76462306a36Sopenharmony_ci */
76562306a36Sopenharmony_civoid snd_card_set_id(struct snd_card *card, const char *nid)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	/* check if user specified own card->id */
76862306a36Sopenharmony_ci	if (card->id[0] != '\0')
76962306a36Sopenharmony_ci		return;
77062306a36Sopenharmony_ci	mutex_lock(&snd_card_mutex);
77162306a36Sopenharmony_ci	snd_card_set_id_no_lock(card, nid, nid);
77262306a36Sopenharmony_ci	mutex_unlock(&snd_card_mutex);
77362306a36Sopenharmony_ci}
77462306a36Sopenharmony_ciEXPORT_SYMBOL(snd_card_set_id);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_cistatic ssize_t id_show(struct device *dev,
77762306a36Sopenharmony_ci		       struct device_attribute *attr, char *buf)
77862306a36Sopenharmony_ci{
77962306a36Sopenharmony_ci	struct snd_card *card = container_of(dev, struct snd_card, card_dev);
78062306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", card->id);
78162306a36Sopenharmony_ci}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_cistatic ssize_t id_store(struct device *dev, struct device_attribute *attr,
78462306a36Sopenharmony_ci			const char *buf, size_t count)
78562306a36Sopenharmony_ci{
78662306a36Sopenharmony_ci	struct snd_card *card = container_of(dev, struct snd_card, card_dev);
78762306a36Sopenharmony_ci	char buf1[sizeof(card->id)];
78862306a36Sopenharmony_ci	size_t copy = count > sizeof(card->id) - 1 ?
78962306a36Sopenharmony_ci					sizeof(card->id) - 1 : count;
79062306a36Sopenharmony_ci	size_t idx;
79162306a36Sopenharmony_ci	int c;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	for (idx = 0; idx < copy; idx++) {
79462306a36Sopenharmony_ci		c = buf[idx];
79562306a36Sopenharmony_ci		if (!isalnum(c) && c != '_' && c != '-')
79662306a36Sopenharmony_ci			return -EINVAL;
79762306a36Sopenharmony_ci	}
79862306a36Sopenharmony_ci	memcpy(buf1, buf, copy);
79962306a36Sopenharmony_ci	buf1[copy] = '\0';
80062306a36Sopenharmony_ci	mutex_lock(&snd_card_mutex);
80162306a36Sopenharmony_ci	if (!card_id_ok(NULL, buf1)) {
80262306a36Sopenharmony_ci		mutex_unlock(&snd_card_mutex);
80362306a36Sopenharmony_ci		return -EEXIST;
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci	strcpy(card->id, buf1);
80662306a36Sopenharmony_ci	snd_info_card_id_change(card);
80762306a36Sopenharmony_ci	mutex_unlock(&snd_card_mutex);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	return count;
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(id);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic ssize_t number_show(struct device *dev,
81562306a36Sopenharmony_ci			   struct device_attribute *attr, char *buf)
81662306a36Sopenharmony_ci{
81762306a36Sopenharmony_ci	struct snd_card *card = container_of(dev, struct snd_card, card_dev);
81862306a36Sopenharmony_ci	return sysfs_emit(buf, "%i\n", card->number);
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(number);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_cistatic struct attribute *card_dev_attrs[] = {
82462306a36Sopenharmony_ci	&dev_attr_id.attr,
82562306a36Sopenharmony_ci	&dev_attr_number.attr,
82662306a36Sopenharmony_ci	NULL
82762306a36Sopenharmony_ci};
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistatic const struct attribute_group card_dev_attr_group = {
83062306a36Sopenharmony_ci	.attrs	= card_dev_attrs,
83162306a36Sopenharmony_ci};
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci/**
83462306a36Sopenharmony_ci * snd_card_add_dev_attr - Append a new sysfs attribute group to card
83562306a36Sopenharmony_ci * @card: card instance
83662306a36Sopenharmony_ci * @group: attribute group to append
83762306a36Sopenharmony_ci *
83862306a36Sopenharmony_ci * Return: zero if successful, or a negative error code
83962306a36Sopenharmony_ci */
84062306a36Sopenharmony_ciint snd_card_add_dev_attr(struct snd_card *card,
84162306a36Sopenharmony_ci			  const struct attribute_group *group)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	int i;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	/* loop for (arraysize-1) here to keep NULL at the last entry */
84662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(card->dev_groups) - 1; i++) {
84762306a36Sopenharmony_ci		if (!card->dev_groups[i]) {
84862306a36Sopenharmony_ci			card->dev_groups[i] = group;
84962306a36Sopenharmony_ci			return 0;
85062306a36Sopenharmony_ci		}
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	dev_err(card->dev, "Too many groups assigned\n");
85462306a36Sopenharmony_ci	return -ENOSPC;
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_card_add_dev_attr);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cistatic void trigger_card_free(void *data)
85962306a36Sopenharmony_ci{
86062306a36Sopenharmony_ci	snd_card_free(data);
86162306a36Sopenharmony_ci}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci/**
86462306a36Sopenharmony_ci *  snd_card_register - register the soundcard
86562306a36Sopenharmony_ci *  @card: soundcard structure
86662306a36Sopenharmony_ci *
86762306a36Sopenharmony_ci *  This function registers all the devices assigned to the soundcard.
86862306a36Sopenharmony_ci *  Until calling this, the ALSA control interface is blocked from the
86962306a36Sopenharmony_ci *  external accesses.  Thus, you should call this function at the end
87062306a36Sopenharmony_ci *  of the initialization of the card.
87162306a36Sopenharmony_ci *
87262306a36Sopenharmony_ci *  Return: Zero otherwise a negative error code if the registration failed.
87362306a36Sopenharmony_ci */
87462306a36Sopenharmony_ciint snd_card_register(struct snd_card *card)
87562306a36Sopenharmony_ci{
87662306a36Sopenharmony_ci	int err;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if (snd_BUG_ON(!card))
87962306a36Sopenharmony_ci		return -EINVAL;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	if (!card->registered) {
88262306a36Sopenharmony_ci		err = device_add(&card->card_dev);
88362306a36Sopenharmony_ci		if (err < 0)
88462306a36Sopenharmony_ci			return err;
88562306a36Sopenharmony_ci		card->registered = true;
88662306a36Sopenharmony_ci	} else {
88762306a36Sopenharmony_ci		if (card->managed)
88862306a36Sopenharmony_ci			devm_remove_action(card->dev, trigger_card_free, card);
88962306a36Sopenharmony_ci	}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	if (card->managed) {
89262306a36Sopenharmony_ci		err = devm_add_action(card->dev, trigger_card_free, card);
89362306a36Sopenharmony_ci		if (err < 0)
89462306a36Sopenharmony_ci			return err;
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	err = snd_device_register_all(card);
89862306a36Sopenharmony_ci	if (err < 0)
89962306a36Sopenharmony_ci		return err;
90062306a36Sopenharmony_ci	mutex_lock(&snd_card_mutex);
90162306a36Sopenharmony_ci	if (snd_cards[card->number]) {
90262306a36Sopenharmony_ci		/* already registered */
90362306a36Sopenharmony_ci		mutex_unlock(&snd_card_mutex);
90462306a36Sopenharmony_ci		return snd_info_card_register(card); /* register pending info */
90562306a36Sopenharmony_ci	}
90662306a36Sopenharmony_ci	if (*card->id) {
90762306a36Sopenharmony_ci		/* make a unique id name from the given string */
90862306a36Sopenharmony_ci		char tmpid[sizeof(card->id)];
90962306a36Sopenharmony_ci		memcpy(tmpid, card->id, sizeof(card->id));
91062306a36Sopenharmony_ci		snd_card_set_id_no_lock(card, tmpid, tmpid);
91162306a36Sopenharmony_ci	} else {
91262306a36Sopenharmony_ci		/* create an id from either shortname or longname */
91362306a36Sopenharmony_ci		const char *src;
91462306a36Sopenharmony_ci		src = *card->shortname ? card->shortname : card->longname;
91562306a36Sopenharmony_ci		snd_card_set_id_no_lock(card, src,
91662306a36Sopenharmony_ci					retrieve_id_from_card_name(src));
91762306a36Sopenharmony_ci	}
91862306a36Sopenharmony_ci	snd_cards[card->number] = card;
91962306a36Sopenharmony_ci	mutex_unlock(&snd_card_mutex);
92062306a36Sopenharmony_ci	err = snd_info_card_register(card);
92162306a36Sopenharmony_ci	if (err < 0)
92262306a36Sopenharmony_ci		return err;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
92562306a36Sopenharmony_ci	if (snd_mixer_oss_notify_callback)
92662306a36Sopenharmony_ci		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
92762306a36Sopenharmony_ci#endif
92862306a36Sopenharmony_ci	return 0;
92962306a36Sopenharmony_ci}
93062306a36Sopenharmony_ciEXPORT_SYMBOL(snd_card_register);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci#ifdef CONFIG_SND_PROC_FS
93362306a36Sopenharmony_cistatic void snd_card_info_read(struct snd_info_entry *entry,
93462306a36Sopenharmony_ci			       struct snd_info_buffer *buffer)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	int idx, count;
93762306a36Sopenharmony_ci	struct snd_card *card;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
94062306a36Sopenharmony_ci		mutex_lock(&snd_card_mutex);
94162306a36Sopenharmony_ci		card = snd_cards[idx];
94262306a36Sopenharmony_ci		if (card) {
94362306a36Sopenharmony_ci			count++;
94462306a36Sopenharmony_ci			snd_iprintf(buffer, "%2i [%-15s]: %s - %s\n",
94562306a36Sopenharmony_ci					idx,
94662306a36Sopenharmony_ci					card->id,
94762306a36Sopenharmony_ci					card->driver,
94862306a36Sopenharmony_ci					card->shortname);
94962306a36Sopenharmony_ci			snd_iprintf(buffer, "                      %s\n",
95062306a36Sopenharmony_ci					card->longname);
95162306a36Sopenharmony_ci		}
95262306a36Sopenharmony_ci		mutex_unlock(&snd_card_mutex);
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci	if (!count)
95562306a36Sopenharmony_ci		snd_iprintf(buffer, "--- no soundcards ---\n");
95662306a36Sopenharmony_ci}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci#ifdef CONFIG_SND_OSSEMUL
95962306a36Sopenharmony_civoid snd_card_info_read_oss(struct snd_info_buffer *buffer)
96062306a36Sopenharmony_ci{
96162306a36Sopenharmony_ci	int idx, count;
96262306a36Sopenharmony_ci	struct snd_card *card;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
96562306a36Sopenharmony_ci		mutex_lock(&snd_card_mutex);
96662306a36Sopenharmony_ci		card = snd_cards[idx];
96762306a36Sopenharmony_ci		if (card) {
96862306a36Sopenharmony_ci			count++;
96962306a36Sopenharmony_ci			snd_iprintf(buffer, "%s\n", card->longname);
97062306a36Sopenharmony_ci		}
97162306a36Sopenharmony_ci		mutex_unlock(&snd_card_mutex);
97262306a36Sopenharmony_ci	}
97362306a36Sopenharmony_ci	if (!count) {
97462306a36Sopenharmony_ci		snd_iprintf(buffer, "--- no soundcards ---\n");
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci#endif
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci#ifdef MODULE
98162306a36Sopenharmony_cistatic void snd_card_module_info_read(struct snd_info_entry *entry,
98262306a36Sopenharmony_ci				      struct snd_info_buffer *buffer)
98362306a36Sopenharmony_ci{
98462306a36Sopenharmony_ci	int idx;
98562306a36Sopenharmony_ci	struct snd_card *card;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	for (idx = 0; idx < SNDRV_CARDS; idx++) {
98862306a36Sopenharmony_ci		mutex_lock(&snd_card_mutex);
98962306a36Sopenharmony_ci		card = snd_cards[idx];
99062306a36Sopenharmony_ci		if (card)
99162306a36Sopenharmony_ci			snd_iprintf(buffer, "%2i %s\n",
99262306a36Sopenharmony_ci				    idx, card->module->name);
99362306a36Sopenharmony_ci		mutex_unlock(&snd_card_mutex);
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci}
99662306a36Sopenharmony_ci#endif
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ciint __init snd_card_info_init(void)
99962306a36Sopenharmony_ci{
100062306a36Sopenharmony_ci	struct snd_info_entry *entry;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL);
100362306a36Sopenharmony_ci	if (! entry)
100462306a36Sopenharmony_ci		return -ENOMEM;
100562306a36Sopenharmony_ci	entry->c.text.read = snd_card_info_read;
100662306a36Sopenharmony_ci	if (snd_info_register(entry) < 0)
100762306a36Sopenharmony_ci		return -ENOMEM; /* freed in error path */
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci#ifdef MODULE
101062306a36Sopenharmony_ci	entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL);
101162306a36Sopenharmony_ci	if (!entry)
101262306a36Sopenharmony_ci		return -ENOMEM;
101362306a36Sopenharmony_ci	entry->c.text.read = snd_card_module_info_read;
101462306a36Sopenharmony_ci	if (snd_info_register(entry) < 0)
101562306a36Sopenharmony_ci		return -ENOMEM; /* freed in error path */
101662306a36Sopenharmony_ci#endif
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	return 0;
101962306a36Sopenharmony_ci}
102062306a36Sopenharmony_ci#endif /* CONFIG_SND_PROC_FS */
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci/**
102362306a36Sopenharmony_ci *  snd_component_add - add a component string
102462306a36Sopenharmony_ci *  @card: soundcard structure
102562306a36Sopenharmony_ci *  @component: the component id string
102662306a36Sopenharmony_ci *
102762306a36Sopenharmony_ci *  This function adds the component id string to the supported list.
102862306a36Sopenharmony_ci *  The component can be referred from the alsa-lib.
102962306a36Sopenharmony_ci *
103062306a36Sopenharmony_ci *  Return: Zero otherwise a negative error code.
103162306a36Sopenharmony_ci */
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ciint snd_component_add(struct snd_card *card, const char *component)
103462306a36Sopenharmony_ci{
103562306a36Sopenharmony_ci	char *ptr;
103662306a36Sopenharmony_ci	int len = strlen(component);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	ptr = strstr(card->components, component);
103962306a36Sopenharmony_ci	if (ptr != NULL) {
104062306a36Sopenharmony_ci		if (ptr[len] == '\0' || ptr[len] == ' ')	/* already there */
104162306a36Sopenharmony_ci			return 1;
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci	if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) {
104462306a36Sopenharmony_ci		snd_BUG();
104562306a36Sopenharmony_ci		return -ENOMEM;
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci	if (card->components[0] != '\0')
104862306a36Sopenharmony_ci		strcat(card->components, " ");
104962306a36Sopenharmony_ci	strcat(card->components, component);
105062306a36Sopenharmony_ci	return 0;
105162306a36Sopenharmony_ci}
105262306a36Sopenharmony_ciEXPORT_SYMBOL(snd_component_add);
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci/**
105562306a36Sopenharmony_ci *  snd_card_file_add - add the file to the file list of the card
105662306a36Sopenharmony_ci *  @card: soundcard structure
105762306a36Sopenharmony_ci *  @file: file pointer
105862306a36Sopenharmony_ci *
105962306a36Sopenharmony_ci *  This function adds the file to the file linked-list of the card.
106062306a36Sopenharmony_ci *  This linked-list is used to keep tracking the connection state,
106162306a36Sopenharmony_ci *  and to avoid the release of busy resources by hotplug.
106262306a36Sopenharmony_ci *
106362306a36Sopenharmony_ci *  Return: zero or a negative error code.
106462306a36Sopenharmony_ci */
106562306a36Sopenharmony_ciint snd_card_file_add(struct snd_card *card, struct file *file)
106662306a36Sopenharmony_ci{
106762306a36Sopenharmony_ci	struct snd_monitor_file *mfile;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	mfile = kmalloc(sizeof(*mfile), GFP_KERNEL);
107062306a36Sopenharmony_ci	if (mfile == NULL)
107162306a36Sopenharmony_ci		return -ENOMEM;
107262306a36Sopenharmony_ci	mfile->file = file;
107362306a36Sopenharmony_ci	mfile->disconnected_f_op = NULL;
107462306a36Sopenharmony_ci	INIT_LIST_HEAD(&mfile->shutdown_list);
107562306a36Sopenharmony_ci	spin_lock(&card->files_lock);
107662306a36Sopenharmony_ci	if (card->shutdown) {
107762306a36Sopenharmony_ci		spin_unlock(&card->files_lock);
107862306a36Sopenharmony_ci		kfree(mfile);
107962306a36Sopenharmony_ci		return -ENODEV;
108062306a36Sopenharmony_ci	}
108162306a36Sopenharmony_ci	list_add(&mfile->list, &card->files_list);
108262306a36Sopenharmony_ci	get_device(&card->card_dev);
108362306a36Sopenharmony_ci	spin_unlock(&card->files_lock);
108462306a36Sopenharmony_ci	return 0;
108562306a36Sopenharmony_ci}
108662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_card_file_add);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci/**
108962306a36Sopenharmony_ci *  snd_card_file_remove - remove the file from the file list
109062306a36Sopenharmony_ci *  @card: soundcard structure
109162306a36Sopenharmony_ci *  @file: file pointer
109262306a36Sopenharmony_ci *
109362306a36Sopenharmony_ci *  This function removes the file formerly added to the card via
109462306a36Sopenharmony_ci *  snd_card_file_add() function.
109562306a36Sopenharmony_ci *  If all files are removed and snd_card_free_when_closed() was
109662306a36Sopenharmony_ci *  called beforehand, it processes the pending release of
109762306a36Sopenharmony_ci *  resources.
109862306a36Sopenharmony_ci *
109962306a36Sopenharmony_ci *  Return: Zero or a negative error code.
110062306a36Sopenharmony_ci */
110162306a36Sopenharmony_ciint snd_card_file_remove(struct snd_card *card, struct file *file)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	struct snd_monitor_file *mfile, *found = NULL;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	spin_lock(&card->files_lock);
110662306a36Sopenharmony_ci	list_for_each_entry(mfile, &card->files_list, list) {
110762306a36Sopenharmony_ci		if (mfile->file == file) {
110862306a36Sopenharmony_ci			list_del(&mfile->list);
110962306a36Sopenharmony_ci			spin_lock(&shutdown_lock);
111062306a36Sopenharmony_ci			list_del(&mfile->shutdown_list);
111162306a36Sopenharmony_ci			spin_unlock(&shutdown_lock);
111262306a36Sopenharmony_ci			if (mfile->disconnected_f_op)
111362306a36Sopenharmony_ci				fops_put(mfile->disconnected_f_op);
111462306a36Sopenharmony_ci			found = mfile;
111562306a36Sopenharmony_ci			break;
111662306a36Sopenharmony_ci		}
111762306a36Sopenharmony_ci	}
111862306a36Sopenharmony_ci	if (list_empty(&card->files_list))
111962306a36Sopenharmony_ci		wake_up_all(&card->remove_sleep);
112062306a36Sopenharmony_ci	spin_unlock(&card->files_lock);
112162306a36Sopenharmony_ci	if (!found) {
112262306a36Sopenharmony_ci		dev_err(card->dev, "card file remove problem (%p)\n", file);
112362306a36Sopenharmony_ci		return -ENOENT;
112462306a36Sopenharmony_ci	}
112562306a36Sopenharmony_ci	kfree(found);
112662306a36Sopenharmony_ci	put_device(&card->card_dev);
112762306a36Sopenharmony_ci	return 0;
112862306a36Sopenharmony_ci}
112962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_card_file_remove);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci#ifdef CONFIG_PM
113262306a36Sopenharmony_ci/**
113362306a36Sopenharmony_ci * snd_power_ref_and_wait - wait until the card gets powered up
113462306a36Sopenharmony_ci * @card: soundcard structure
113562306a36Sopenharmony_ci *
113662306a36Sopenharmony_ci * Take the power_ref reference count of the given card, and
113762306a36Sopenharmony_ci * wait until the card gets powered up to SNDRV_CTL_POWER_D0 state.
113862306a36Sopenharmony_ci * The refcount is down again while sleeping until power-up, hence this
113962306a36Sopenharmony_ci * function can be used for syncing the floating control ops accesses,
114062306a36Sopenharmony_ci * typically around calling control ops.
114162306a36Sopenharmony_ci *
114262306a36Sopenharmony_ci * The caller needs to pull down the refcount via snd_power_unref() later
114362306a36Sopenharmony_ci * no matter whether the error is returned from this function or not.
114462306a36Sopenharmony_ci *
114562306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code.
114662306a36Sopenharmony_ci */
114762306a36Sopenharmony_ciint snd_power_ref_and_wait(struct snd_card *card)
114862306a36Sopenharmony_ci{
114962306a36Sopenharmony_ci	snd_power_ref(card);
115062306a36Sopenharmony_ci	if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0)
115162306a36Sopenharmony_ci		return 0;
115262306a36Sopenharmony_ci	wait_event_cmd(card->power_sleep,
115362306a36Sopenharmony_ci		       card->shutdown ||
115462306a36Sopenharmony_ci		       snd_power_get_state(card) == SNDRV_CTL_POWER_D0,
115562306a36Sopenharmony_ci		       snd_power_unref(card), snd_power_ref(card));
115662306a36Sopenharmony_ci	return card->shutdown ? -ENODEV : 0;
115762306a36Sopenharmony_ci}
115862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_power_ref_and_wait);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci/**
116162306a36Sopenharmony_ci * snd_power_wait - wait until the card gets powered up (old form)
116262306a36Sopenharmony_ci * @card: soundcard structure
116362306a36Sopenharmony_ci *
116462306a36Sopenharmony_ci * Wait until the card gets powered up to SNDRV_CTL_POWER_D0 state.
116562306a36Sopenharmony_ci *
116662306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code.
116762306a36Sopenharmony_ci */
116862306a36Sopenharmony_ciint snd_power_wait(struct snd_card *card)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	int ret;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	ret = snd_power_ref_and_wait(card);
117362306a36Sopenharmony_ci	snd_power_unref(card);
117462306a36Sopenharmony_ci	return ret;
117562306a36Sopenharmony_ci}
117662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_power_wait);
117762306a36Sopenharmony_ci#endif /* CONFIG_PM */
1178