11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Initialization routines 3c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 71da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 81da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 91da177e4SLinus Torvalds * (at your option) any later version. 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 121da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 131da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 141da177e4SLinus Torvalds * GNU General Public License for more details. 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 171da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 181da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds */ 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #include <linux/init.h> 231da177e4SLinus Torvalds #include <linux/sched.h> 24da155d5bSPaul Gortmaker #include <linux/module.h> 2551990e82SPaul Gortmaker #include <linux/device.h> 261da177e4SLinus Torvalds #include <linux/file.h> 271da177e4SLinus Torvalds #include <linux/slab.h> 281da177e4SLinus Torvalds #include <linux/time.h> 291da177e4SLinus Torvalds #include <linux/ctype.h> 301da177e4SLinus Torvalds #include <linux/pm.h> 31f2464064STakashi Iwai #include <linux/completion.h> 32d052d1beSRussell King 331da177e4SLinus Torvalds #include <sound/core.h> 341da177e4SLinus Torvalds #include <sound/control.h> 351da177e4SLinus Torvalds #include <sound/info.h> 361da177e4SLinus Torvalds 3782a783f4STakashi Iwai /* monitor files for graceful shutdown (hotplug) */ 3882a783f4STakashi Iwai struct snd_monitor_file { 3982a783f4STakashi Iwai struct file *file; 4082a783f4STakashi Iwai const struct file_operations *disconnected_f_op; 4182a783f4STakashi Iwai struct list_head shutdown_list; /* still need to shutdown */ 4282a783f4STakashi Iwai struct list_head list; /* link of monitor files */ 4382a783f4STakashi Iwai }; 4482a783f4STakashi Iwai 45a9edfc60SKarsten Wiese static DEFINE_SPINLOCK(shutdown_lock); 46a9edfc60SKarsten Wiese static LIST_HEAD(shutdown_files); 47a9edfc60SKarsten Wiese 489c2e08c5SArjan van de Ven static const struct file_operations snd_shutdown_f_ops; 491da177e4SLinus Torvalds 507bb2491bSTakashi Iwai /* locked for registering/using */ 517bb2491bSTakashi Iwai static DECLARE_BITMAP(snd_cards_lock, SNDRV_CARDS); 526581f4e7STakashi Iwai struct snd_card *snd_cards[SNDRV_CARDS]; 53c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_cards); 54c0d3fb39STakashi Iwai 55746df948STakashi Iwai static DEFINE_MUTEX(snd_card_mutex); 561da177e4SLinus Torvalds 57304cd07fSTakashi Iwai static char *slots[SNDRV_CARDS]; 58304cd07fSTakashi Iwai module_param_array(slots, charp, NULL, 0444); 59304cd07fSTakashi Iwai MODULE_PARM_DESC(slots, "Module names assigned to the slots."); 60304cd07fSTakashi Iwai 61a93bbaa7STakashi Iwai /* return non-zero if the given index is reserved for the given 62304cd07fSTakashi Iwai * module via slots option 63304cd07fSTakashi Iwai */ 64a93bbaa7STakashi Iwai static int module_slot_match(struct module *module, int idx) 65304cd07fSTakashi Iwai { 66a93bbaa7STakashi Iwai int match = 1; 67304cd07fSTakashi Iwai #ifdef MODULE 68a93bbaa7STakashi Iwai const char *s1, *s2; 69a93bbaa7STakashi Iwai 7060f6fef8STakashi Iwai if (!module || !*module->name || !slots[idx]) 71304cd07fSTakashi Iwai return 0; 72a93bbaa7STakashi Iwai 73a93bbaa7STakashi Iwai s1 = module->name; 74a93bbaa7STakashi Iwai s2 = slots[idx]; 75a93bbaa7STakashi Iwai if (*s2 == '!') { 76a93bbaa7STakashi Iwai match = 0; /* negative match */ 77a93bbaa7STakashi Iwai s2++; 78a93bbaa7STakashi Iwai } 79304cd07fSTakashi Iwai /* compare module name strings 80304cd07fSTakashi Iwai * hyphens are handled as equivalent with underscore 81304cd07fSTakashi Iwai */ 82304cd07fSTakashi Iwai for (;;) { 83304cd07fSTakashi Iwai char c1 = *s1++; 84304cd07fSTakashi Iwai char c2 = *s2++; 85304cd07fSTakashi Iwai if (c1 == '-') 86304cd07fSTakashi Iwai c1 = '_'; 87304cd07fSTakashi Iwai if (c2 == '-') 88304cd07fSTakashi Iwai c2 = '_'; 89304cd07fSTakashi Iwai if (c1 != c2) 90a93bbaa7STakashi Iwai return !match; 91304cd07fSTakashi Iwai if (!c1) 92304cd07fSTakashi Iwai break; 93304cd07fSTakashi Iwai } 94a93bbaa7STakashi Iwai #endif /* MODULE */ 95a93bbaa7STakashi Iwai return match; 96304cd07fSTakashi Iwai } 97304cd07fSTakashi Iwai 988eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_MIXER_OSS) 99512bbd6aSTakashi Iwai int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag); 100c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_mixer_oss_notify_callback); 1011da177e4SLinus Torvalds #endif 1021da177e4SLinus Torvalds 103e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS 104512bbd6aSTakashi Iwai static void snd_card_id_read(struct snd_info_entry *entry, 105512bbd6aSTakashi Iwai struct snd_info_buffer *buffer) 1061da177e4SLinus Torvalds { 1071da177e4SLinus Torvalds snd_iprintf(buffer, "%s\n", entry->card->id); 1081da177e4SLinus Torvalds } 1091da177e4SLinus Torvalds 110e28563ccSTakashi Iwai static inline int init_info_for_card(struct snd_card *card) 111e28563ccSTakashi Iwai { 112e28563ccSTakashi Iwai int err; 113e28563ccSTakashi Iwai struct snd_info_entry *entry; 114e28563ccSTakashi Iwai 115e28563ccSTakashi Iwai if ((err = snd_info_card_register(card)) < 0) { 116f2f9307aSTakashi Iwai dev_dbg(card->dev, "unable to create card info\n"); 117e28563ccSTakashi Iwai return err; 118e28563ccSTakashi Iwai } 119e28563ccSTakashi Iwai if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) { 120f2f9307aSTakashi Iwai dev_dbg(card->dev, "unable to create card entry\n"); 121e28563ccSTakashi Iwai return err; 122e28563ccSTakashi Iwai } 123e28563ccSTakashi Iwai entry->c.text.read = snd_card_id_read; 124e28563ccSTakashi Iwai if (snd_info_register(entry) < 0) { 125e28563ccSTakashi Iwai snd_info_free_entry(entry); 126e28563ccSTakashi Iwai entry = NULL; 127e28563ccSTakashi Iwai } 128e28563ccSTakashi Iwai card->proc_id = entry; 129e28563ccSTakashi Iwai return 0; 130e28563ccSTakashi Iwai } 131e28563ccSTakashi Iwai #else /* !CONFIG_PROC_FS */ 132e28563ccSTakashi Iwai #define init_info_for_card(card) 133e28563ccSTakashi Iwai #endif 134e28563ccSTakashi Iwai 135deb6596fSTakashi Iwai static int check_empty_slot(struct module *module, int slot) 136deb6596fSTakashi Iwai { 137deb6596fSTakashi Iwai return !slots[slot] || !*slots[slot]; 138deb6596fSTakashi Iwai } 139deb6596fSTakashi Iwai 140deb6596fSTakashi Iwai /* return an empty slot number (>= 0) found in the given bitmask @mask. 141deb6596fSTakashi Iwai * @mask == -1 == 0xffffffff means: take any free slot up to 32 142deb6596fSTakashi Iwai * when no slot is available, return the original @mask as is. 143deb6596fSTakashi Iwai */ 144deb6596fSTakashi Iwai static int get_slot_from_bitmask(int mask, int (*check)(struct module *, int), 145deb6596fSTakashi Iwai struct module *module) 146deb6596fSTakashi Iwai { 147deb6596fSTakashi Iwai int slot; 148deb6596fSTakashi Iwai 149deb6596fSTakashi Iwai for (slot = 0; slot < SNDRV_CARDS; slot++) { 150deb6596fSTakashi Iwai if (slot < 32 && !(mask & (1U << slot))) 151deb6596fSTakashi Iwai continue; 152deb6596fSTakashi Iwai if (!test_bit(slot, snd_cards_lock)) { 153deb6596fSTakashi Iwai if (check(module, slot)) 154deb6596fSTakashi Iwai return slot; /* found */ 155deb6596fSTakashi Iwai } 156deb6596fSTakashi Iwai } 157deb6596fSTakashi Iwai return mask; /* unchanged */ 158deb6596fSTakashi Iwai } 159deb6596fSTakashi Iwai 1604b440be6STakashi Iwai /* the default release callback set in snd_device_initialize() below; 1614b440be6STakashi Iwai * this is just NOP for now, as almost all jobs are already done in 1624b440be6STakashi Iwai * dev_free callback of snd_device chain instead. 1634b440be6STakashi Iwai */ 1644b440be6STakashi Iwai static void default_release(struct device *dev) 1654b440be6STakashi Iwai { 1664b440be6STakashi Iwai } 1674b440be6STakashi Iwai 1684b440be6STakashi Iwai /** 1694b440be6STakashi Iwai * snd_device_initialize - Initialize struct device for sound devices 1704b440be6STakashi Iwai * @dev: device to initialize 1714b440be6STakashi Iwai * @card: card to assign, optional 1724b440be6STakashi Iwai */ 1734b440be6STakashi Iwai void snd_device_initialize(struct device *dev, struct snd_card *card) 1744b440be6STakashi Iwai { 1754b440be6STakashi Iwai device_initialize(dev); 1764b440be6STakashi Iwai if (card) 1774b440be6STakashi Iwai dev->parent = &card->card_dev; 1784b440be6STakashi Iwai dev->class = sound_class; 1794b440be6STakashi Iwai dev->release = default_release; 1804b440be6STakashi Iwai } 1814b440be6STakashi Iwai EXPORT_SYMBOL_GPL(snd_device_initialize); 1824b440be6STakashi Iwai 1838bfb181cSTakashi Iwai static int snd_card_do_free(struct snd_card *card); 18434356dbdSTakashi Iwai static const struct attribute_group *card_dev_attr_groups[]; 1858bfb181cSTakashi Iwai 1868bfb181cSTakashi Iwai static void release_card_device(struct device *dev) 1878bfb181cSTakashi Iwai { 1888bfb181cSTakashi Iwai snd_card_do_free(dev_to_snd_card(dev)); 1898bfb181cSTakashi Iwai } 1908bfb181cSTakashi Iwai 1911da177e4SLinus Torvalds /** 192393aa9c1STakashi Iwai * snd_card_new - create and initialize a soundcard structure 193393aa9c1STakashi Iwai * @parent: the parent device object 1941da177e4SLinus Torvalds * @idx: card index (address) [0 ... (SNDRV_CARDS-1)] 1951da177e4SLinus Torvalds * @xid: card identification (ASCII string) 1961da177e4SLinus Torvalds * @module: top level module for locking 1971da177e4SLinus Torvalds * @extra_size: allocate this extra size after the main soundcard structure 19853fb1e63STakashi Iwai * @card_ret: the pointer to store the created card instance 1991da177e4SLinus Torvalds * 2001da177e4SLinus Torvalds * Creates and initializes a soundcard structure. 2011da177e4SLinus Torvalds * 20253fb1e63STakashi Iwai * The function allocates snd_card instance via kzalloc with the given 20353fb1e63STakashi Iwai * space for the driver to use freely. The allocated struct is stored 20453fb1e63STakashi Iwai * in the given card_ret pointer. 20553fb1e63STakashi Iwai * 206eb7c06e8SYacine Belkadi * Return: Zero if successful or a negative error code. 2071da177e4SLinus Torvalds */ 208393aa9c1STakashi Iwai int snd_card_new(struct device *parent, int idx, const char *xid, 20953fb1e63STakashi Iwai struct module *module, int extra_size, 21053fb1e63STakashi Iwai struct snd_card **card_ret) 2111da177e4SLinus Torvalds { 212512bbd6aSTakashi Iwai struct snd_card *card; 213deb6596fSTakashi Iwai int err; 2141da177e4SLinus Torvalds 21553fb1e63STakashi Iwai if (snd_BUG_ON(!card_ret)) 21653fb1e63STakashi Iwai return -EINVAL; 21753fb1e63STakashi Iwai *card_ret = NULL; 21853fb1e63STakashi Iwai 2191da177e4SLinus Torvalds if (extra_size < 0) 2201da177e4SLinus Torvalds extra_size = 0; 221ca2c0966STakashi Iwai card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL); 22253fb1e63STakashi Iwai if (!card) 22353fb1e63STakashi Iwai return -ENOMEM; 2248bfb181cSTakashi Iwai if (extra_size > 0) 2258bfb181cSTakashi Iwai card->private_data = (char *)card + sizeof(struct snd_card); 22610a8ebbbSJaroslav Kysela if (xid) 2275fdc18d9SJaroslav Kysela strlcpy(card->id, xid, sizeof(card->id)); 2281da177e4SLinus Torvalds err = 0; 229746df948STakashi Iwai mutex_lock(&snd_card_mutex); 230deb6596fSTakashi Iwai if (idx < 0) /* first check the matching module-name slot */ 231deb6596fSTakashi Iwai idx = get_slot_from_bitmask(idx, module_slot_match, module); 232deb6596fSTakashi Iwai if (idx < 0) /* if not matched, assign an empty slot */ 233deb6596fSTakashi Iwai idx = get_slot_from_bitmask(idx, check_empty_slot, module); 234a93bbaa7STakashi Iwai if (idx < 0) 235a93bbaa7STakashi Iwai err = -ENODEV; 236a93bbaa7STakashi Iwai else if (idx < snd_ecards_limit) { 2377bb2491bSTakashi Iwai if (test_bit(idx, snd_cards_lock)) 2385c33dd70SOliver Neukum err = -EBUSY; /* invalid */ 239a93bbaa7STakashi Iwai } else if (idx >= SNDRV_CARDS) 2401da177e4SLinus Torvalds err = -ENODEV; 241a93bbaa7STakashi Iwai if (err < 0) { 242746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 243f2f9307aSTakashi Iwai dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n", 2445c33dd70SOliver Neukum idx, snd_ecards_limit - 1, err); 2458bfb181cSTakashi Iwai kfree(card); 2468bfb181cSTakashi Iwai return err; 2471da177e4SLinus Torvalds } 2487bb2491bSTakashi Iwai set_bit(idx, snd_cards_lock); /* lock it */ 249a93bbaa7STakashi Iwai if (idx >= snd_ecards_limit) 250a93bbaa7STakashi Iwai snd_ecards_limit = idx + 1; /* increase the limit */ 251746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 252393aa9c1STakashi Iwai card->dev = parent; 2531da177e4SLinus Torvalds card->number = idx; 2541da177e4SLinus Torvalds card->module = module; 2551da177e4SLinus Torvalds INIT_LIST_HEAD(&card->devices); 2561da177e4SLinus Torvalds init_rwsem(&card->controls_rwsem); 2571da177e4SLinus Torvalds rwlock_init(&card->ctl_files_rwlock); 25807f4d9d7SLars-Peter Clausen mutex_init(&card->user_ctl_lock); 2591da177e4SLinus Torvalds INIT_LIST_HEAD(&card->controls); 2601da177e4SLinus Torvalds INIT_LIST_HEAD(&card->ctl_files); 2611da177e4SLinus Torvalds spin_lock_init(&card->files_lock); 262118dd6bfSTakashi Iwai INIT_LIST_HEAD(&card->files_list); 2631da177e4SLinus Torvalds #ifdef CONFIG_PM 2641a60d4c5SIngo Molnar mutex_init(&card->power_lock); 2651da177e4SLinus Torvalds init_waitqueue_head(&card->power_sleep); 2661da177e4SLinus Torvalds #endif 2678bfb181cSTakashi Iwai 2688bfb181cSTakashi Iwai device_initialize(&card->card_dev); 2698bfb181cSTakashi Iwai card->card_dev.parent = parent; 2708bfb181cSTakashi Iwai card->card_dev.class = sound_class; 2718bfb181cSTakashi Iwai card->card_dev.release = release_card_device; 27234356dbdSTakashi Iwai card->card_dev.groups = card_dev_attr_groups; 2738bfb181cSTakashi Iwai err = kobject_set_name(&card->card_dev.kobj, "card%d", idx); 2748bfb181cSTakashi Iwai if (err < 0) 2758bfb181cSTakashi Iwai goto __error; 2768bfb181cSTakashi Iwai 2771da177e4SLinus Torvalds /* the control interface cannot be accessed from the user space until */ 2781da177e4SLinus Torvalds /* snd_cards_bitmask and snd_cards are set with snd_card_register */ 27953fb1e63STakashi Iwai err = snd_ctl_create(card); 28053fb1e63STakashi Iwai if (err < 0) { 281f2f9307aSTakashi Iwai dev_err(parent, "unable to register control minors\n"); 2821da177e4SLinus Torvalds goto __error; 2831da177e4SLinus Torvalds } 28453fb1e63STakashi Iwai err = snd_info_card_create(card); 28553fb1e63STakashi Iwai if (err < 0) { 286f2f9307aSTakashi Iwai dev_err(parent, "unable to create card info\n"); 2871da177e4SLinus Torvalds goto __error_ctl; 2881da177e4SLinus Torvalds } 28953fb1e63STakashi Iwai *card_ret = card; 29053fb1e63STakashi Iwai return 0; 2911da177e4SLinus Torvalds 2921da177e4SLinus Torvalds __error_ctl: 293289ca025STakashi Iwai snd_device_free_all(card); 2941da177e4SLinus Torvalds __error: 2958bfb181cSTakashi Iwai put_device(&card->card_dev); 29653fb1e63STakashi Iwai return err; 2971da177e4SLinus Torvalds } 298393aa9c1STakashi Iwai EXPORT_SYMBOL(snd_card_new); 299c0d3fb39STakashi Iwai 300746df948STakashi Iwai /* return non-zero if a card is already locked */ 301746df948STakashi Iwai int snd_card_locked(int card) 302746df948STakashi Iwai { 303746df948STakashi Iwai int locked; 304746df948STakashi Iwai 305746df948STakashi Iwai mutex_lock(&snd_card_mutex); 3067bb2491bSTakashi Iwai locked = test_bit(card, snd_cards_lock); 307746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 308746df948STakashi Iwai return locked; 309746df948STakashi Iwai } 310746df948STakashi Iwai 311b3b0abe1SClemens Ladisch static loff_t snd_disconnect_llseek(struct file *file, loff_t offset, int orig) 312b3b0abe1SClemens Ladisch { 313b3b0abe1SClemens Ladisch return -ENODEV; 314b3b0abe1SClemens Ladisch } 315b3b0abe1SClemens Ladisch 316b3b0abe1SClemens Ladisch static ssize_t snd_disconnect_read(struct file *file, char __user *buf, 317b3b0abe1SClemens Ladisch size_t count, loff_t *offset) 318b3b0abe1SClemens Ladisch { 319b3b0abe1SClemens Ladisch return -ENODEV; 320b3b0abe1SClemens Ladisch } 321b3b0abe1SClemens Ladisch 322b3b0abe1SClemens Ladisch static ssize_t snd_disconnect_write(struct file *file, const char __user *buf, 323b3b0abe1SClemens Ladisch size_t count, loff_t *offset) 324b3b0abe1SClemens Ladisch { 325b3b0abe1SClemens Ladisch return -ENODEV; 326b3b0abe1SClemens Ladisch } 327b3b0abe1SClemens Ladisch 328a9edfc60SKarsten Wiese static int snd_disconnect_release(struct inode *inode, struct file *file) 329a9edfc60SKarsten Wiese { 330a9edfc60SKarsten Wiese struct snd_monitor_file *df = NULL, *_df; 331a9edfc60SKarsten Wiese 332a9edfc60SKarsten Wiese spin_lock(&shutdown_lock); 333a9edfc60SKarsten Wiese list_for_each_entry(_df, &shutdown_files, shutdown_list) { 334a9edfc60SKarsten Wiese if (_df->file == file) { 335a9edfc60SKarsten Wiese df = _df; 336118dd6bfSTakashi Iwai list_del_init(&df->shutdown_list); 337a9edfc60SKarsten Wiese break; 338a9edfc60SKarsten Wiese } 339a9edfc60SKarsten Wiese } 340a9edfc60SKarsten Wiese spin_unlock(&shutdown_lock); 341a9edfc60SKarsten Wiese 342233e70f4SAl Viro if (likely(df)) { 343233e70f4SAl Viro if ((file->f_flags & FASYNC) && df->disconnected_f_op->fasync) 344233e70f4SAl Viro df->disconnected_f_op->fasync(-1, file, 0); 345a9edfc60SKarsten Wiese return df->disconnected_f_op->release(inode, file); 346233e70f4SAl Viro } 347a9edfc60SKarsten Wiese 3489bf8e7ddSHarvey Harrison panic("%s(%p, %p) failed!", __func__, inode, file); 349a9edfc60SKarsten Wiese } 350a9edfc60SKarsten Wiese 3511da177e4SLinus Torvalds static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait) 3521da177e4SLinus Torvalds { 3531da177e4SLinus Torvalds return POLLERR | POLLNVAL; 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds 356b3b0abe1SClemens Ladisch static long snd_disconnect_ioctl(struct file *file, 357b3b0abe1SClemens Ladisch unsigned int cmd, unsigned long arg) 358b3b0abe1SClemens Ladisch { 359b3b0abe1SClemens Ladisch return -ENODEV; 360b3b0abe1SClemens Ladisch } 361b3b0abe1SClemens Ladisch 362b3b0abe1SClemens Ladisch static int snd_disconnect_mmap(struct file *file, struct vm_area_struct *vma) 363b3b0abe1SClemens Ladisch { 364b3b0abe1SClemens Ladisch return -ENODEV; 365b3b0abe1SClemens Ladisch } 366b3b0abe1SClemens Ladisch 367b3b0abe1SClemens Ladisch static int snd_disconnect_fasync(int fd, struct file *file, int on) 368b3b0abe1SClemens Ladisch { 369b3b0abe1SClemens Ladisch return -ENODEV; 370b3b0abe1SClemens Ladisch } 371b3b0abe1SClemens Ladisch 3729c2e08c5SArjan van de Ven static const struct file_operations snd_shutdown_f_ops = 373a9edfc60SKarsten Wiese { 374a9edfc60SKarsten Wiese .owner = THIS_MODULE, 375a9edfc60SKarsten Wiese .llseek = snd_disconnect_llseek, 376a9edfc60SKarsten Wiese .read = snd_disconnect_read, 377a9edfc60SKarsten Wiese .write = snd_disconnect_write, 378a9edfc60SKarsten Wiese .release = snd_disconnect_release, 379a9edfc60SKarsten Wiese .poll = snd_disconnect_poll, 380a9edfc60SKarsten Wiese .unlocked_ioctl = snd_disconnect_ioctl, 381a9edfc60SKarsten Wiese #ifdef CONFIG_COMPAT 382a9edfc60SKarsten Wiese .compat_ioctl = snd_disconnect_ioctl, 383a9edfc60SKarsten Wiese #endif 384a9edfc60SKarsten Wiese .mmap = snd_disconnect_mmap, 385a9edfc60SKarsten Wiese .fasync = snd_disconnect_fasync 386a9edfc60SKarsten Wiese }; 387a9edfc60SKarsten Wiese 3881da177e4SLinus Torvalds /** 3891da177e4SLinus Torvalds * snd_card_disconnect - disconnect all APIs from the file-operations (user space) 3901da177e4SLinus Torvalds * @card: soundcard structure 3911da177e4SLinus Torvalds * 3921da177e4SLinus Torvalds * Disconnects all APIs from the file-operations (user space). 3931da177e4SLinus Torvalds * 394eb7c06e8SYacine Belkadi * Return: Zero, otherwise a negative error code. 3951da177e4SLinus Torvalds * 3961da177e4SLinus Torvalds * Note: The current implementation replaces all active file->f_op with special 3971da177e4SLinus Torvalds * dummy file operations (they do nothing except release). 3981da177e4SLinus Torvalds */ 399512bbd6aSTakashi Iwai int snd_card_disconnect(struct snd_card *card) 4001da177e4SLinus Torvalds { 4011da177e4SLinus Torvalds struct snd_monitor_file *mfile; 4021da177e4SLinus Torvalds int err; 4031da177e4SLinus Torvalds 404f18638dcSTakashi Iwai if (!card) 405f18638dcSTakashi Iwai return -EINVAL; 406f18638dcSTakashi Iwai 4071da177e4SLinus Torvalds spin_lock(&card->files_lock); 4081da177e4SLinus Torvalds if (card->shutdown) { 4091da177e4SLinus Torvalds spin_unlock(&card->files_lock); 4101da177e4SLinus Torvalds return 0; 4111da177e4SLinus Torvalds } 4121da177e4SLinus Torvalds card->shutdown = 1; 4131da177e4SLinus Torvalds spin_unlock(&card->files_lock); 4141da177e4SLinus Torvalds 4151da177e4SLinus Torvalds /* phase 1: disable fops (user space) operations for ALSA API */ 416746df948STakashi Iwai mutex_lock(&snd_card_mutex); 4171da177e4SLinus Torvalds snd_cards[card->number] = NULL; 4187bb2491bSTakashi Iwai clear_bit(card->number, snd_cards_lock); 419746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 4201da177e4SLinus Torvalds 4211da177e4SLinus Torvalds /* phase 2: replace file->f_op with special dummy operations */ 4221da177e4SLinus Torvalds 4231da177e4SLinus Torvalds spin_lock(&card->files_lock); 424118dd6bfSTakashi Iwai list_for_each_entry(mfile, &card->files_list, list) { 4251da177e4SLinus Torvalds /* it's critical part, use endless loop */ 4261da177e4SLinus Torvalds /* we have no room to fail */ 427a9edfc60SKarsten Wiese mfile->disconnected_f_op = mfile->file->f_op; 4281da177e4SLinus Torvalds 429a9edfc60SKarsten Wiese spin_lock(&shutdown_lock); 430a9edfc60SKarsten Wiese list_add(&mfile->shutdown_list, &shutdown_files); 431a9edfc60SKarsten Wiese spin_unlock(&shutdown_lock); 4321da177e4SLinus Torvalds 433a9edfc60SKarsten Wiese mfile->file->f_op = &snd_shutdown_f_ops; 434bc9abce0SMiguel Boton fops_get(mfile->file->f_op); 4351da177e4SLinus Torvalds } 4361da177e4SLinus Torvalds spin_unlock(&card->files_lock); 4371da177e4SLinus Torvalds 4381da177e4SLinus Torvalds /* phase 3: notify all connected devices about disconnection */ 4391da177e4SLinus Torvalds /* at this point, they cannot respond to any calls except release() */ 4401da177e4SLinus Torvalds 4418eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_MIXER_OSS) 4421da177e4SLinus Torvalds if (snd_mixer_oss_notify_callback) 4431da177e4SLinus Torvalds snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT); 4441da177e4SLinus Torvalds #endif 4451da177e4SLinus Torvalds 4461da177e4SLinus Torvalds /* notify all devices that we are disconnected */ 4471da177e4SLinus Torvalds err = snd_device_disconnect_all(card); 4481da177e4SLinus Torvalds if (err < 0) 449f2f9307aSTakashi Iwai dev_err(card->dev, "not all devices for card %i can be disconnected\n", card->number); 4501da177e4SLinus Torvalds 451746d4a02STakashi Iwai snd_info_card_disconnect(card); 4528bfb181cSTakashi Iwai if (card->registered) { 4538bfb181cSTakashi Iwai device_del(&card->card_dev); 4548bfb181cSTakashi Iwai card->registered = false; 45573d38b13STakashi Iwai } 456f18638dcSTakashi Iwai #ifdef CONFIG_PM 457f18638dcSTakashi Iwai wake_up(&card->power_sleep); 458f18638dcSTakashi Iwai #endif 4591da177e4SLinus Torvalds return 0; 4601da177e4SLinus Torvalds } 4611da177e4SLinus Torvalds 462c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_disconnect); 463c0d3fb39STakashi Iwai 464c461482cSTakashi Iwai static int snd_card_do_free(struct snd_card *card) 4651da177e4SLinus Torvalds { 4668eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_MIXER_OSS) 4671da177e4SLinus Torvalds if (snd_mixer_oss_notify_callback) 4681da177e4SLinus Torvalds snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); 4691da177e4SLinus Torvalds #endif 47072620d60STakashi Iwai snd_device_free_all(card); 4711da177e4SLinus Torvalds if (card->private_free) 4721da177e4SLinus Torvalds card->private_free(card); 473746d4a02STakashi Iwai snd_info_free_entry(card->proc_id); 4741da177e4SLinus Torvalds if (snd_info_card_free(card) < 0) { 475f2f9307aSTakashi Iwai dev_warn(card->dev, "unable to free card info\n"); 4761da177e4SLinus Torvalds /* Not fatal error */ 4771da177e4SLinus Torvalds } 478f2464064STakashi Iwai if (card->release_completion) 479f2464064STakashi Iwai complete(card->release_completion); 480c461482cSTakashi Iwai kfree(card); 481c461482cSTakashi Iwai return 0; 482c461482cSTakashi Iwai } 483c461482cSTakashi Iwai 484eb9c38d5STakashi Iwai /** 485eb9c38d5STakashi Iwai * snd_card_free_when_closed - Disconnect the card, free it later eventually 486eb9c38d5STakashi Iwai * @card: soundcard structure 487eb9c38d5STakashi Iwai * 488eb9c38d5STakashi Iwai * Unlike snd_card_free(), this function doesn't try to release the card 489eb9c38d5STakashi Iwai * resource immediately, but tries to disconnect at first. When the card 490eb9c38d5STakashi Iwai * is still in use, the function returns before freeing the resources. 491eb9c38d5STakashi Iwai * The card resources will be freed when the refcount gets to zero. 492eb9c38d5STakashi Iwai */ 493c461482cSTakashi Iwai int snd_card_free_when_closed(struct snd_card *card) 494c461482cSTakashi Iwai { 495f18638dcSTakashi Iwai int ret = snd_card_disconnect(card); 496c461482cSTakashi Iwai if (ret) 497c461482cSTakashi Iwai return ret; 4988bfb181cSTakashi Iwai put_device(&card->card_dev); 4991da177e4SLinus Torvalds return 0; 5001da177e4SLinus Torvalds } 501f2464064STakashi Iwai EXPORT_SYMBOL(snd_card_free_when_closed); 5021da177e4SLinus Torvalds 503eb9c38d5STakashi Iwai /** 504eb9c38d5STakashi Iwai * snd_card_free - frees given soundcard structure 505eb9c38d5STakashi Iwai * @card: soundcard structure 506eb9c38d5STakashi Iwai * 507eb9c38d5STakashi Iwai * This function releases the soundcard structure and the all assigned 508eb9c38d5STakashi Iwai * devices automatically. That is, you don't have to release the devices 509eb9c38d5STakashi Iwai * by yourself. 510eb9c38d5STakashi Iwai * 511eb9c38d5STakashi Iwai * This function waits until the all resources are properly released. 512eb9c38d5STakashi Iwai * 513eb9c38d5STakashi Iwai * Return: Zero. Frees all associated devices and frees the control 514eb9c38d5STakashi Iwai * interface associated to given soundcard. 515eb9c38d5STakashi Iwai */ 516f2464064STakashi Iwai int snd_card_free(struct snd_card *card) 517f2464064STakashi Iwai { 518f2464064STakashi Iwai struct completion released; 519f2464064STakashi Iwai int ret; 520f2464064STakashi Iwai 521f2464064STakashi Iwai init_completion(&released); 522f2464064STakashi Iwai card->release_completion = &released; 523f2464064STakashi Iwai ret = snd_card_free_when_closed(card); 524f2464064STakashi Iwai if (ret) 525f2464064STakashi Iwai return ret; 526f2464064STakashi Iwai /* wait, until all devices are ready for the free operation */ 527f2464064STakashi Iwai wait_for_completion(&released); 528f2464064STakashi Iwai return 0; 529f2464064STakashi Iwai } 530c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_free); 531c0d3fb39STakashi Iwai 532e7df2a3aSTakashi Iwai /* retrieve the last word of shortname or longname */ 533e7df2a3aSTakashi Iwai static const char *retrieve_id_from_card_name(const char *name) 5341da177e4SLinus Torvalds { 535e7df2a3aSTakashi Iwai const char *spos = name; 536e7df2a3aSTakashi Iwai 537e7df2a3aSTakashi Iwai while (*name) { 538e7df2a3aSTakashi Iwai if (isspace(*name) && isalnum(name[1])) 539e7df2a3aSTakashi Iwai spos = name + 1; 540e7df2a3aSTakashi Iwai name++; 541e7df2a3aSTakashi Iwai } 542e7df2a3aSTakashi Iwai return spos; 543e7df2a3aSTakashi Iwai } 544e7df2a3aSTakashi Iwai 545e7df2a3aSTakashi Iwai /* return true if the given id string doesn't conflict any other card ids */ 546e7df2a3aSTakashi Iwai static bool card_id_ok(struct snd_card *card, const char *id) 547e7df2a3aSTakashi Iwai { 548e7df2a3aSTakashi Iwai int i; 549e7df2a3aSTakashi Iwai if (!snd_info_check_reserved_words(id)) 550e7df2a3aSTakashi Iwai return false; 551e7df2a3aSTakashi Iwai for (i = 0; i < snd_ecards_limit; i++) { 552e7df2a3aSTakashi Iwai if (snd_cards[i] && snd_cards[i] != card && 553e7df2a3aSTakashi Iwai !strcmp(snd_cards[i]->id, id)) 554e7df2a3aSTakashi Iwai return false; 555e7df2a3aSTakashi Iwai } 556e7df2a3aSTakashi Iwai return true; 557e7df2a3aSTakashi Iwai } 558e7df2a3aSTakashi Iwai 559e7df2a3aSTakashi Iwai /* copy to card->id only with valid letters from nid */ 560e7df2a3aSTakashi Iwai static void copy_valid_id_string(struct snd_card *card, const char *src, 561e7df2a3aSTakashi Iwai const char *nid) 562e7df2a3aSTakashi Iwai { 563e7df2a3aSTakashi Iwai char *id = card->id; 564e7df2a3aSTakashi Iwai 565e7df2a3aSTakashi Iwai while (*nid && !isalnum(*nid)) 566e7df2a3aSTakashi Iwai nid++; 567e7df2a3aSTakashi Iwai if (isdigit(*nid)) 568e7df2a3aSTakashi Iwai *id++ = isalpha(*src) ? *src : 'D'; 569e7df2a3aSTakashi Iwai while (*nid && (size_t)(id - card->id) < sizeof(card->id) - 1) { 570e7df2a3aSTakashi Iwai if (isalnum(*nid)) 571e7df2a3aSTakashi Iwai *id++ = *nid; 572e7df2a3aSTakashi Iwai nid++; 573e7df2a3aSTakashi Iwai } 574e7df2a3aSTakashi Iwai *id = 0; 575e7df2a3aSTakashi Iwai } 576e7df2a3aSTakashi Iwai 577e7df2a3aSTakashi Iwai /* Set card->id from the given string 578e7df2a3aSTakashi Iwai * If the string conflicts with other ids, add a suffix to make it unique. 579e7df2a3aSTakashi Iwai */ 580e7df2a3aSTakashi Iwai static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, 581e7df2a3aSTakashi Iwai const char *nid) 582e7df2a3aSTakashi Iwai { 583e7df2a3aSTakashi Iwai int len, loops; 584e7df2a3aSTakashi Iwai bool is_default = false; 58510a8ebbbSJaroslav Kysela char *id; 5861da177e4SLinus Torvalds 587e7df2a3aSTakashi Iwai copy_valid_id_string(card, src, nid); 5881da177e4SLinus Torvalds id = card->id; 5891da177e4SLinus Torvalds 590e7df2a3aSTakashi Iwai again: 591e7df2a3aSTakashi Iwai /* use "Default" for obviously invalid strings 592e7df2a3aSTakashi Iwai * ("card" conflicts with proc directories) 593e7df2a3aSTakashi Iwai */ 594e7df2a3aSTakashi Iwai if (!*id || !strncmp(id, "card", 4)) { 595d8e4f9aeSTakashi Iwai strcpy(id, "Default"); 596e7df2a3aSTakashi Iwai is_default = true; 5971da177e4SLinus Torvalds } 5981da177e4SLinus Torvalds 5998edbb198STakashi Iwai len = strlen(id); 600e7df2a3aSTakashi Iwai for (loops = 0; loops < SNDRV_CARDS; loops++) { 6018edbb198STakashi Iwai char *spos; 6028edbb198STakashi Iwai char sfxstr[5]; /* "_012" */ 6038edbb198STakashi Iwai int sfxlen; 6048edbb198STakashi Iwai 605e7df2a3aSTakashi Iwai if (card_id_ok(card, id)) 606e7df2a3aSTakashi Iwai return; /* OK */ 607e7df2a3aSTakashi Iwai 6088edbb198STakashi Iwai /* Add _XYZ suffix */ 6098edbb198STakashi Iwai sprintf(sfxstr, "_%X", loops + 1); 6108edbb198STakashi Iwai sfxlen = strlen(sfxstr); 6118edbb198STakashi Iwai if (len + sfxlen >= sizeof(card->id)) 6128edbb198STakashi Iwai spos = id + sizeof(card->id) - sfxlen - 1; 613d001544dSClemens Ladisch else 6148edbb198STakashi Iwai spos = id + len; 6158edbb198STakashi Iwai strcpy(spos, sfxstr); 6161da177e4SLinus Torvalds } 617e7df2a3aSTakashi Iwai /* fallback to the default id */ 618e7df2a3aSTakashi Iwai if (!is_default) { 619e7df2a3aSTakashi Iwai *id = 0; 620e7df2a3aSTakashi Iwai goto again; 621e7df2a3aSTakashi Iwai } 622e7df2a3aSTakashi Iwai /* last resort... */ 623f2f9307aSTakashi Iwai dev_err(card->dev, "unable to set card id (%s)\n", id); 624e7df2a3aSTakashi Iwai if (card->proc_root->name) 62597f44f56STakashi Iwai strlcpy(card->id, card->proc_root->name, sizeof(card->id)); 6261da177e4SLinus Torvalds } 6271da177e4SLinus Torvalds 628872c7820SMark Brown /** 629872c7820SMark Brown * snd_card_set_id - set card identification name 630872c7820SMark Brown * @card: soundcard structure 631872c7820SMark Brown * @nid: new identification string 632872c7820SMark Brown * 633872c7820SMark Brown * This function sets the card identification and checks for name 634872c7820SMark Brown * collisions. 635872c7820SMark Brown */ 636872c7820SMark Brown void snd_card_set_id(struct snd_card *card, const char *nid) 637872c7820SMark Brown { 6385fdc18d9SJaroslav Kysela /* check if user specified own card->id */ 6395fdc18d9SJaroslav Kysela if (card->id[0] != '\0') 6405fdc18d9SJaroslav Kysela return; 6415fdc18d9SJaroslav Kysela mutex_lock(&snd_card_mutex); 642e7df2a3aSTakashi Iwai snd_card_set_id_no_lock(card, nid, nid); 6435fdc18d9SJaroslav Kysela mutex_unlock(&snd_card_mutex); 644872c7820SMark Brown } 64510a8ebbbSJaroslav Kysela EXPORT_SYMBOL(snd_card_set_id); 64610a8ebbbSJaroslav Kysela 6479fb6198eSJaroslav Kysela static ssize_t 6489fb6198eSJaroslav Kysela card_id_show_attr(struct device *dev, 6499fb6198eSJaroslav Kysela struct device_attribute *attr, char *buf) 6509fb6198eSJaroslav Kysela { 651b203dbabSTakashi Iwai struct snd_card *card = container_of(dev, struct snd_card, card_dev); 652b203dbabSTakashi Iwai return snprintf(buf, PAGE_SIZE, "%s\n", card->id); 6539fb6198eSJaroslav Kysela } 6549fb6198eSJaroslav Kysela 6559fb6198eSJaroslav Kysela static ssize_t 6569fb6198eSJaroslav Kysela card_id_store_attr(struct device *dev, struct device_attribute *attr, 6579fb6198eSJaroslav Kysela const char *buf, size_t count) 6589fb6198eSJaroslav Kysela { 659b203dbabSTakashi Iwai struct snd_card *card = container_of(dev, struct snd_card, card_dev); 6609fb6198eSJaroslav Kysela char buf1[sizeof(card->id)]; 6619fb6198eSJaroslav Kysela size_t copy = count > sizeof(card->id) - 1 ? 6629fb6198eSJaroslav Kysela sizeof(card->id) - 1 : count; 6639fb6198eSJaroslav Kysela size_t idx; 6649fb6198eSJaroslav Kysela int c; 6659fb6198eSJaroslav Kysela 6669fb6198eSJaroslav Kysela for (idx = 0; idx < copy; idx++) { 6679fb6198eSJaroslav Kysela c = buf[idx]; 6689fb6198eSJaroslav Kysela if (!isalnum(c) && c != '_' && c != '-') 6699fb6198eSJaroslav Kysela return -EINVAL; 6709fb6198eSJaroslav Kysela } 6719fb6198eSJaroslav Kysela memcpy(buf1, buf, copy); 6729fb6198eSJaroslav Kysela buf1[copy] = '\0'; 6739fb6198eSJaroslav Kysela mutex_lock(&snd_card_mutex); 674e7df2a3aSTakashi Iwai if (!card_id_ok(NULL, buf1)) { 6759fb6198eSJaroslav Kysela mutex_unlock(&snd_card_mutex); 6769fb6198eSJaroslav Kysela return -EEXIST; 6779fb6198eSJaroslav Kysela } 6789fb6198eSJaroslav Kysela strcpy(card->id, buf1); 679c2eb9c4eSJaroslav Kysela snd_info_card_id_change(card); 6809fb6198eSJaroslav Kysela mutex_unlock(&snd_card_mutex); 6819fb6198eSJaroslav Kysela 6829fb6198eSJaroslav Kysela return count; 6839fb6198eSJaroslav Kysela } 6849fb6198eSJaroslav Kysela 68534356dbdSTakashi Iwai static DEVICE_ATTR(id, S_IRUGO | S_IWUSR, card_id_show_attr, card_id_store_attr); 6869fb6198eSJaroslav Kysela 6879fb6198eSJaroslav Kysela static ssize_t 6889fb6198eSJaroslav Kysela card_number_show_attr(struct device *dev, 6899fb6198eSJaroslav Kysela struct device_attribute *attr, char *buf) 6909fb6198eSJaroslav Kysela { 691b203dbabSTakashi Iwai struct snd_card *card = container_of(dev, struct snd_card, card_dev); 692b203dbabSTakashi Iwai return snprintf(buf, PAGE_SIZE, "%i\n", card->number); 6939fb6198eSJaroslav Kysela } 6949fb6198eSJaroslav Kysela 69534356dbdSTakashi Iwai static DEVICE_ATTR(number, S_IRUGO, card_number_show_attr, NULL); 69634356dbdSTakashi Iwai 69734356dbdSTakashi Iwai static struct attribute *card_dev_attrs[] = { 69834356dbdSTakashi Iwai &dev_attr_id.attr, 69934356dbdSTakashi Iwai &dev_attr_number.attr, 70034356dbdSTakashi Iwai NULL 70134356dbdSTakashi Iwai }; 70234356dbdSTakashi Iwai 70334356dbdSTakashi Iwai static struct attribute_group card_dev_attr_group = { 70434356dbdSTakashi Iwai .attrs = card_dev_attrs, 70534356dbdSTakashi Iwai }; 70634356dbdSTakashi Iwai 70734356dbdSTakashi Iwai static const struct attribute_group *card_dev_attr_groups[] = { 70834356dbdSTakashi Iwai &card_dev_attr_group, 70934356dbdSTakashi Iwai NULL 71034356dbdSTakashi Iwai }; 7119fb6198eSJaroslav Kysela 7121da177e4SLinus Torvalds /** 7131da177e4SLinus Torvalds * snd_card_register - register the soundcard 7141da177e4SLinus Torvalds * @card: soundcard structure 7151da177e4SLinus Torvalds * 7161da177e4SLinus Torvalds * This function registers all the devices assigned to the soundcard. 7171da177e4SLinus Torvalds * Until calling this, the ALSA control interface is blocked from the 7181da177e4SLinus Torvalds * external accesses. Thus, you should call this function at the end 7191da177e4SLinus Torvalds * of the initialization of the card. 7201da177e4SLinus Torvalds * 721eb7c06e8SYacine Belkadi * Return: Zero otherwise a negative error code if the registration failed. 7221da177e4SLinus Torvalds */ 723512bbd6aSTakashi Iwai int snd_card_register(struct snd_card *card) 7241da177e4SLinus Torvalds { 7251da177e4SLinus Torvalds int err; 7261da177e4SLinus Torvalds 7277eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 7287eaa943cSTakashi Iwai return -EINVAL; 72939aba963SKay Sievers 7308bfb181cSTakashi Iwai if (!card->registered) { 7318bfb181cSTakashi Iwai err = device_add(&card->card_dev); 7328bfb181cSTakashi Iwai if (err < 0) 7338bfb181cSTakashi Iwai return err; 7348bfb181cSTakashi Iwai card->registered = true; 735d80f19faSGreg Kroah-Hartman } 73639aba963SKay Sievers 7371da177e4SLinus Torvalds if ((err = snd_device_register_all(card)) < 0) 7381da177e4SLinus Torvalds return err; 739746df948STakashi Iwai mutex_lock(&snd_card_mutex); 7401da177e4SLinus Torvalds if (snd_cards[card->number]) { 7411da177e4SLinus Torvalds /* already registered */ 742746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 7431da177e4SLinus Torvalds return 0; 7441da177e4SLinus Torvalds } 745e7df2a3aSTakashi Iwai if (*card->id) { 746e7df2a3aSTakashi Iwai /* make a unique id name from the given string */ 747e7df2a3aSTakashi Iwai char tmpid[sizeof(card->id)]; 748e7df2a3aSTakashi Iwai memcpy(tmpid, card->id, sizeof(card->id)); 749e7df2a3aSTakashi Iwai snd_card_set_id_no_lock(card, tmpid, tmpid); 750e7df2a3aSTakashi Iwai } else { 751e7df2a3aSTakashi Iwai /* create an id from either shortname or longname */ 752e7df2a3aSTakashi Iwai const char *src; 753e7df2a3aSTakashi Iwai src = *card->shortname ? card->shortname : card->longname; 754e7df2a3aSTakashi Iwai snd_card_set_id_no_lock(card, src, 755e7df2a3aSTakashi Iwai retrieve_id_from_card_name(src)); 756e7df2a3aSTakashi Iwai } 7571da177e4SLinus Torvalds snd_cards[card->number] = card; 758746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 759e28563ccSTakashi Iwai init_info_for_card(card); 7608eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_MIXER_OSS) 7611da177e4SLinus Torvalds if (snd_mixer_oss_notify_callback) 7621da177e4SLinus Torvalds snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER); 7631da177e4SLinus Torvalds #endif 7641da177e4SLinus Torvalds return 0; 7651da177e4SLinus Torvalds } 7661da177e4SLinus Torvalds 767c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_register); 768c0d3fb39STakashi Iwai 769e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS 7706581f4e7STakashi Iwai static struct snd_info_entry *snd_card_info_entry; 7711da177e4SLinus Torvalds 772a381a7a6STakashi Iwai static void snd_card_info_read(struct snd_info_entry *entry, 773a381a7a6STakashi Iwai struct snd_info_buffer *buffer) 7741da177e4SLinus Torvalds { 7751da177e4SLinus Torvalds int idx, count; 776512bbd6aSTakashi Iwai struct snd_card *card; 7771da177e4SLinus Torvalds 7781da177e4SLinus Torvalds for (idx = count = 0; idx < SNDRV_CARDS; idx++) { 779746df948STakashi Iwai mutex_lock(&snd_card_mutex); 7801da177e4SLinus Torvalds if ((card = snd_cards[idx]) != NULL) { 7811da177e4SLinus Torvalds count++; 782d001544dSClemens Ladisch snd_iprintf(buffer, "%2i [%-15s]: %s - %s\n", 7831da177e4SLinus Torvalds idx, 7841da177e4SLinus Torvalds card->id, 7851da177e4SLinus Torvalds card->driver, 7861da177e4SLinus Torvalds card->shortname); 7871da177e4SLinus Torvalds snd_iprintf(buffer, " %s\n", 7881da177e4SLinus Torvalds card->longname); 7891da177e4SLinus Torvalds } 790746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 7911da177e4SLinus Torvalds } 7921da177e4SLinus Torvalds if (!count) 7931da177e4SLinus Torvalds snd_iprintf(buffer, "--- no soundcards ---\n"); 7941da177e4SLinus Torvalds } 7951da177e4SLinus Torvalds 796e28563ccSTakashi Iwai #ifdef CONFIG_SND_OSSEMUL 7971da177e4SLinus Torvalds 798512bbd6aSTakashi Iwai void snd_card_info_read_oss(struct snd_info_buffer *buffer) 7991da177e4SLinus Torvalds { 8001da177e4SLinus Torvalds int idx, count; 801512bbd6aSTakashi Iwai struct snd_card *card; 8021da177e4SLinus Torvalds 8031da177e4SLinus Torvalds for (idx = count = 0; idx < SNDRV_CARDS; idx++) { 804746df948STakashi Iwai mutex_lock(&snd_card_mutex); 8051da177e4SLinus Torvalds if ((card = snd_cards[idx]) != NULL) { 8061da177e4SLinus Torvalds count++; 8071da177e4SLinus Torvalds snd_iprintf(buffer, "%s\n", card->longname); 8081da177e4SLinus Torvalds } 809746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 8101da177e4SLinus Torvalds } 8111da177e4SLinus Torvalds if (!count) { 8121da177e4SLinus Torvalds snd_iprintf(buffer, "--- no soundcards ---\n"); 8131da177e4SLinus Torvalds } 8141da177e4SLinus Torvalds } 8151da177e4SLinus Torvalds 8161da177e4SLinus Torvalds #endif 8171da177e4SLinus Torvalds 8181da177e4SLinus Torvalds #ifdef MODULE 819512bbd6aSTakashi Iwai static struct snd_info_entry *snd_card_module_info_entry; 820512bbd6aSTakashi Iwai static void snd_card_module_info_read(struct snd_info_entry *entry, 821512bbd6aSTakashi Iwai struct snd_info_buffer *buffer) 8221da177e4SLinus Torvalds { 8231da177e4SLinus Torvalds int idx; 824512bbd6aSTakashi Iwai struct snd_card *card; 8251da177e4SLinus Torvalds 8261da177e4SLinus Torvalds for (idx = 0; idx < SNDRV_CARDS; idx++) { 827746df948STakashi Iwai mutex_lock(&snd_card_mutex); 8281da177e4SLinus Torvalds if ((card = snd_cards[idx]) != NULL) 829d001544dSClemens Ladisch snd_iprintf(buffer, "%2i %s\n", 830d001544dSClemens Ladisch idx, card->module->name); 831746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 8321da177e4SLinus Torvalds } 8331da177e4SLinus Torvalds } 8341da177e4SLinus Torvalds #endif 8351da177e4SLinus Torvalds 8361da177e4SLinus Torvalds int __init snd_card_info_init(void) 8371da177e4SLinus Torvalds { 838512bbd6aSTakashi Iwai struct snd_info_entry *entry; 8391da177e4SLinus Torvalds 8401da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL); 8417c22f1aaSTakashi Iwai if (! entry) 8427c22f1aaSTakashi Iwai return -ENOMEM; 8431da177e4SLinus Torvalds entry->c.text.read = snd_card_info_read; 8441da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 8451da177e4SLinus Torvalds snd_info_free_entry(entry); 8461da177e4SLinus Torvalds return -ENOMEM; 8471da177e4SLinus Torvalds } 8481da177e4SLinus Torvalds snd_card_info_entry = entry; 8491da177e4SLinus Torvalds 8501da177e4SLinus Torvalds #ifdef MODULE 8511da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL); 8521da177e4SLinus Torvalds if (entry) { 8531da177e4SLinus Torvalds entry->c.text.read = snd_card_module_info_read; 8541da177e4SLinus Torvalds if (snd_info_register(entry) < 0) 8551da177e4SLinus Torvalds snd_info_free_entry(entry); 8561da177e4SLinus Torvalds else 8571da177e4SLinus Torvalds snd_card_module_info_entry = entry; 8581da177e4SLinus Torvalds } 8591da177e4SLinus Torvalds #endif 8601da177e4SLinus Torvalds 8611da177e4SLinus Torvalds return 0; 8621da177e4SLinus Torvalds } 8631da177e4SLinus Torvalds 8641da177e4SLinus Torvalds int __exit snd_card_info_done(void) 8651da177e4SLinus Torvalds { 866746d4a02STakashi Iwai snd_info_free_entry(snd_card_info_entry); 8671da177e4SLinus Torvalds #ifdef MODULE 868746d4a02STakashi Iwai snd_info_free_entry(snd_card_module_info_entry); 8691da177e4SLinus Torvalds #endif 8701da177e4SLinus Torvalds return 0; 8711da177e4SLinus Torvalds } 8721da177e4SLinus Torvalds 873e28563ccSTakashi Iwai #endif /* CONFIG_PROC_FS */ 874e28563ccSTakashi Iwai 8751da177e4SLinus Torvalds /** 8761da177e4SLinus Torvalds * snd_component_add - add a component string 8771da177e4SLinus Torvalds * @card: soundcard structure 8781da177e4SLinus Torvalds * @component: the component id string 8791da177e4SLinus Torvalds * 8801da177e4SLinus Torvalds * This function adds the component id string to the supported list. 8811da177e4SLinus Torvalds * The component can be referred from the alsa-lib. 8821da177e4SLinus Torvalds * 883eb7c06e8SYacine Belkadi * Return: Zero otherwise a negative error code. 8841da177e4SLinus Torvalds */ 8851da177e4SLinus Torvalds 886512bbd6aSTakashi Iwai int snd_component_add(struct snd_card *card, const char *component) 8871da177e4SLinus Torvalds { 8881da177e4SLinus Torvalds char *ptr; 8891da177e4SLinus Torvalds int len = strlen(component); 8901da177e4SLinus Torvalds 8911da177e4SLinus Torvalds ptr = strstr(card->components, component); 8921da177e4SLinus Torvalds if (ptr != NULL) { 8931da177e4SLinus Torvalds if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */ 8941da177e4SLinus Torvalds return 1; 8951da177e4SLinus Torvalds } 8961da177e4SLinus Torvalds if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) { 8971da177e4SLinus Torvalds snd_BUG(); 8981da177e4SLinus Torvalds return -ENOMEM; 8991da177e4SLinus Torvalds } 9001da177e4SLinus Torvalds if (card->components[0] != '\0') 9011da177e4SLinus Torvalds strcat(card->components, " "); 9021da177e4SLinus Torvalds strcat(card->components, component); 9031da177e4SLinus Torvalds return 0; 9041da177e4SLinus Torvalds } 9051da177e4SLinus Torvalds 906c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_component_add); 907c0d3fb39STakashi Iwai 9081da177e4SLinus Torvalds /** 9091da177e4SLinus Torvalds * snd_card_file_add - add the file to the file list of the card 9101da177e4SLinus Torvalds * @card: soundcard structure 9111da177e4SLinus Torvalds * @file: file pointer 9121da177e4SLinus Torvalds * 9131da177e4SLinus Torvalds * This function adds the file to the file linked-list of the card. 9141da177e4SLinus Torvalds * This linked-list is used to keep tracking the connection state, 9151da177e4SLinus Torvalds * and to avoid the release of busy resources by hotplug. 9161da177e4SLinus Torvalds * 917eb7c06e8SYacine Belkadi * Return: zero or a negative error code. 9181da177e4SLinus Torvalds */ 919512bbd6aSTakashi Iwai int snd_card_file_add(struct snd_card *card, struct file *file) 9201da177e4SLinus Torvalds { 9211da177e4SLinus Torvalds struct snd_monitor_file *mfile; 9221da177e4SLinus Torvalds 9231da177e4SLinus Torvalds mfile = kmalloc(sizeof(*mfile), GFP_KERNEL); 9241da177e4SLinus Torvalds if (mfile == NULL) 9251da177e4SLinus Torvalds return -ENOMEM; 9261da177e4SLinus Torvalds mfile->file = file; 927a9edfc60SKarsten Wiese mfile->disconnected_f_op = NULL; 928a45e3d6bSTakashi Iwai INIT_LIST_HEAD(&mfile->shutdown_list); 9291da177e4SLinus Torvalds spin_lock(&card->files_lock); 9301da177e4SLinus Torvalds if (card->shutdown) { 9311da177e4SLinus Torvalds spin_unlock(&card->files_lock); 9321da177e4SLinus Torvalds kfree(mfile); 9331da177e4SLinus Torvalds return -ENODEV; 9341da177e4SLinus Torvalds } 935118dd6bfSTakashi Iwai list_add(&mfile->list, &card->files_list); 936f2464064STakashi Iwai get_device(&card->card_dev); 9371da177e4SLinus Torvalds spin_unlock(&card->files_lock); 9381da177e4SLinus Torvalds return 0; 9391da177e4SLinus Torvalds } 9401da177e4SLinus Torvalds 941c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_file_add); 942c0d3fb39STakashi Iwai 9431da177e4SLinus Torvalds /** 9441da177e4SLinus Torvalds * snd_card_file_remove - remove the file from the file list 9451da177e4SLinus Torvalds * @card: soundcard structure 9461da177e4SLinus Torvalds * @file: file pointer 9471da177e4SLinus Torvalds * 9481da177e4SLinus Torvalds * This function removes the file formerly added to the card via 9491da177e4SLinus Torvalds * snd_card_file_add() function. 9502b29b13cSTakashi Iwai * If all files are removed and snd_card_free_when_closed() was 9512b29b13cSTakashi Iwai * called beforehand, it processes the pending release of 9522b29b13cSTakashi Iwai * resources. 9531da177e4SLinus Torvalds * 954eb7c06e8SYacine Belkadi * Return: Zero or a negative error code. 9551da177e4SLinus Torvalds */ 956512bbd6aSTakashi Iwai int snd_card_file_remove(struct snd_card *card, struct file *file) 9571da177e4SLinus Torvalds { 958118dd6bfSTakashi Iwai struct snd_monitor_file *mfile, *found = NULL; 9591da177e4SLinus Torvalds 9601da177e4SLinus Torvalds spin_lock(&card->files_lock); 961118dd6bfSTakashi Iwai list_for_each_entry(mfile, &card->files_list, list) { 9621da177e4SLinus Torvalds if (mfile->file == file) { 963118dd6bfSTakashi Iwai list_del(&mfile->list); 964a45e3d6bSTakashi Iwai spin_lock(&shutdown_lock); 965a45e3d6bSTakashi Iwai list_del(&mfile->shutdown_list); 966a45e3d6bSTakashi Iwai spin_unlock(&shutdown_lock); 967118dd6bfSTakashi Iwai if (mfile->disconnected_f_op) 968118dd6bfSTakashi Iwai fops_put(mfile->disconnected_f_op); 969118dd6bfSTakashi Iwai found = mfile; 9701da177e4SLinus Torvalds break; 9711da177e4SLinus Torvalds } 9721da177e4SLinus Torvalds } 973c461482cSTakashi Iwai spin_unlock(&card->files_lock); 974118dd6bfSTakashi Iwai if (!found) { 975f2f9307aSTakashi Iwai dev_err(card->dev, "card file remove problem (%p)\n", file); 9761da177e4SLinus Torvalds return -ENOENT; 9771da177e4SLinus Torvalds } 978118dd6bfSTakashi Iwai kfree(found); 979f2464064STakashi Iwai put_device(&card->card_dev); 9801da177e4SLinus Torvalds return 0; 9811da177e4SLinus Torvalds } 9821da177e4SLinus Torvalds 983c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_file_remove); 984c0d3fb39STakashi Iwai 9851da177e4SLinus Torvalds #ifdef CONFIG_PM 9861da177e4SLinus Torvalds /** 9871da177e4SLinus Torvalds * snd_power_wait - wait until the power-state is changed. 9881da177e4SLinus Torvalds * @card: soundcard structure 9891da177e4SLinus Torvalds * @power_state: expected power state 9901da177e4SLinus Torvalds * 9911da177e4SLinus Torvalds * Waits until the power-state is changed. 9921da177e4SLinus Torvalds * 993eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code. 994eb7c06e8SYacine Belkadi * 9951da177e4SLinus Torvalds * Note: the power lock must be active before call. 9961da177e4SLinus Torvalds */ 997cbac4b0cSTakashi Iwai int snd_power_wait(struct snd_card *card, unsigned int power_state) 9981da177e4SLinus Torvalds { 9991da177e4SLinus Torvalds wait_queue_t wait; 10001da177e4SLinus Torvalds int result = 0; 10011da177e4SLinus Torvalds 10021da177e4SLinus Torvalds /* fastpath */ 10031da177e4SLinus Torvalds if (snd_power_get_state(card) == power_state) 10041da177e4SLinus Torvalds return 0; 10051da177e4SLinus Torvalds init_waitqueue_entry(&wait, current); 10061da177e4SLinus Torvalds add_wait_queue(&card->power_sleep, &wait); 10071da177e4SLinus Torvalds while (1) { 10081da177e4SLinus Torvalds if (card->shutdown) { 10091da177e4SLinus Torvalds result = -ENODEV; 10101da177e4SLinus Torvalds break; 10111da177e4SLinus Torvalds } 10121da177e4SLinus Torvalds if (snd_power_get_state(card) == power_state) 10131da177e4SLinus Torvalds break; 10141da177e4SLinus Torvalds set_current_state(TASK_UNINTERRUPTIBLE); 10151da177e4SLinus Torvalds snd_power_unlock(card); 10161da177e4SLinus Torvalds schedule_timeout(30 * HZ); 10171da177e4SLinus Torvalds snd_power_lock(card); 10181da177e4SLinus Torvalds } 10191da177e4SLinus Torvalds remove_wait_queue(&card->power_sleep, &wait); 10201da177e4SLinus Torvalds return result; 10211da177e4SLinus Torvalds } 10221da177e4SLinus Torvalds 1023c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_power_wait); 10241da177e4SLinus Torvalds #endif /* CONFIG_PM */ 1025