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