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> 241da177e4SLinus Torvalds #include <linux/file.h> 251da177e4SLinus Torvalds #include <linux/slab.h> 261da177e4SLinus Torvalds #include <linux/time.h> 271da177e4SLinus Torvalds #include <linux/ctype.h> 281da177e4SLinus Torvalds #include <linux/pm.h> 29d052d1beSRussell King 301da177e4SLinus Torvalds #include <sound/core.h> 311da177e4SLinus Torvalds #include <sound/control.h> 321da177e4SLinus Torvalds #include <sound/info.h> 331da177e4SLinus Torvalds 34a9edfc60SKarsten Wiese static DEFINE_SPINLOCK(shutdown_lock); 35a9edfc60SKarsten Wiese static LIST_HEAD(shutdown_files); 36a9edfc60SKarsten Wiese 379c2e08c5SArjan van de Ven static const struct file_operations snd_shutdown_f_ops; 381da177e4SLinus Torvalds 396581f4e7STakashi Iwai static unsigned int snd_cards_lock; /* locked for registering/using */ 406581f4e7STakashi Iwai struct snd_card *snd_cards[SNDRV_CARDS]; 41c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_cards); 42c0d3fb39STakashi Iwai 43746df948STakashi Iwai static DEFINE_MUTEX(snd_card_mutex); 441da177e4SLinus Torvalds 45304cd07fSTakashi Iwai static char *slots[SNDRV_CARDS]; 46304cd07fSTakashi Iwai module_param_array(slots, charp, NULL, 0444); 47304cd07fSTakashi Iwai MODULE_PARM_DESC(slots, "Module names assigned to the slots."); 48304cd07fSTakashi Iwai 49304cd07fSTakashi Iwai /* return non-zero if the given index is already reserved for another 50304cd07fSTakashi Iwai * module via slots option 51304cd07fSTakashi Iwai */ 52304cd07fSTakashi Iwai static int module_slot_mismatch(struct module *module, int idx) 53304cd07fSTakashi Iwai { 54304cd07fSTakashi Iwai #ifdef MODULE 55304cd07fSTakashi Iwai char *s1, *s2; 56304cd07fSTakashi Iwai if (!module || !module->name || !slots[idx]) 57304cd07fSTakashi Iwai return 0; 58304cd07fSTakashi Iwai s1 = slots[idx]; 59304cd07fSTakashi Iwai s2 = module->name; 60304cd07fSTakashi Iwai /* compare module name strings 61304cd07fSTakashi Iwai * hyphens are handled as equivalent with underscore 62304cd07fSTakashi Iwai */ 63304cd07fSTakashi Iwai for (;;) { 64304cd07fSTakashi Iwai char c1 = *s1++; 65304cd07fSTakashi Iwai char c2 = *s2++; 66304cd07fSTakashi Iwai if (c1 == '-') 67304cd07fSTakashi Iwai c1 = '_'; 68304cd07fSTakashi Iwai if (c2 == '-') 69304cd07fSTakashi Iwai c2 = '_'; 70304cd07fSTakashi Iwai if (c1 != c2) 71304cd07fSTakashi Iwai return 1; 72304cd07fSTakashi Iwai if (!c1) 73304cd07fSTakashi Iwai break; 74304cd07fSTakashi Iwai } 75304cd07fSTakashi Iwai #endif 76304cd07fSTakashi Iwai return 0; 77304cd07fSTakashi Iwai } 78304cd07fSTakashi Iwai 791da177e4SLinus Torvalds #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) 80512bbd6aSTakashi Iwai int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag); 81c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_mixer_oss_notify_callback); 821da177e4SLinus Torvalds #endif 831da177e4SLinus Torvalds 84e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS 85512bbd6aSTakashi Iwai static void snd_card_id_read(struct snd_info_entry *entry, 86512bbd6aSTakashi Iwai struct snd_info_buffer *buffer) 871da177e4SLinus Torvalds { 881da177e4SLinus Torvalds snd_iprintf(buffer, "%s\n", entry->card->id); 891da177e4SLinus Torvalds } 901da177e4SLinus Torvalds 91e28563ccSTakashi Iwai static inline int init_info_for_card(struct snd_card *card) 92e28563ccSTakashi Iwai { 93e28563ccSTakashi Iwai int err; 94e28563ccSTakashi Iwai struct snd_info_entry *entry; 95e28563ccSTakashi Iwai 96e28563ccSTakashi Iwai if ((err = snd_info_card_register(card)) < 0) { 97e28563ccSTakashi Iwai snd_printd("unable to create card info\n"); 98e28563ccSTakashi Iwai return err; 99e28563ccSTakashi Iwai } 100e28563ccSTakashi Iwai if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) { 101e28563ccSTakashi Iwai snd_printd("unable to create card entry\n"); 102e28563ccSTakashi Iwai return err; 103e28563ccSTakashi Iwai } 104e28563ccSTakashi Iwai entry->c.text.read = snd_card_id_read; 105e28563ccSTakashi Iwai if (snd_info_register(entry) < 0) { 106e28563ccSTakashi Iwai snd_info_free_entry(entry); 107e28563ccSTakashi Iwai entry = NULL; 108e28563ccSTakashi Iwai } 109e28563ccSTakashi Iwai card->proc_id = entry; 110e28563ccSTakashi Iwai return 0; 111e28563ccSTakashi Iwai } 112e28563ccSTakashi Iwai #else /* !CONFIG_PROC_FS */ 113e28563ccSTakashi Iwai #define init_info_for_card(card) 114e28563ccSTakashi Iwai #endif 115e28563ccSTakashi Iwai 1161da177e4SLinus Torvalds /** 1171da177e4SLinus Torvalds * snd_card_new - create and initialize a soundcard structure 1181da177e4SLinus Torvalds * @idx: card index (address) [0 ... (SNDRV_CARDS-1)] 1191da177e4SLinus Torvalds * @xid: card identification (ASCII string) 1201da177e4SLinus Torvalds * @module: top level module for locking 1211da177e4SLinus Torvalds * @extra_size: allocate this extra size after the main soundcard structure 1221da177e4SLinus Torvalds * 1231da177e4SLinus Torvalds * Creates and initializes a soundcard structure. 1241da177e4SLinus Torvalds * 125512bbd6aSTakashi Iwai * Returns kmallocated snd_card structure. Creates the ALSA control interface 1261da177e4SLinus Torvalds * (which is blocked until snd_card_register function is called). 1271da177e4SLinus Torvalds */ 128512bbd6aSTakashi Iwai struct snd_card *snd_card_new(int idx, const char *xid, 1291da177e4SLinus Torvalds struct module *module, int extra_size) 1301da177e4SLinus Torvalds { 131512bbd6aSTakashi Iwai struct snd_card *card; 1321da177e4SLinus Torvalds int err; 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds if (extra_size < 0) 1351da177e4SLinus Torvalds extra_size = 0; 136ca2c0966STakashi Iwai card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL); 1371da177e4SLinus Torvalds if (card == NULL) 1381da177e4SLinus Torvalds return NULL; 1391da177e4SLinus Torvalds if (xid) { 1401da177e4SLinus Torvalds if (!snd_info_check_reserved_words(xid)) 1411da177e4SLinus Torvalds goto __error; 1421da177e4SLinus Torvalds strlcpy(card->id, xid, sizeof(card->id)); 1431da177e4SLinus Torvalds } 1441da177e4SLinus Torvalds err = 0; 145746df948STakashi Iwai mutex_lock(&snd_card_mutex); 1461da177e4SLinus Torvalds if (idx < 0) { 1471da177e4SLinus Torvalds int idx2; 1481da177e4SLinus Torvalds for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) 1495c33dd70SOliver Neukum /* idx == -1 == 0xffff means: take any free slot */ 1501da177e4SLinus Torvalds if (~snd_cards_lock & idx & 1<<idx2) { 151304cd07fSTakashi Iwai if (module_slot_mismatch(module, idx2)) 152304cd07fSTakashi Iwai continue; 1531da177e4SLinus Torvalds idx = idx2; 1541da177e4SLinus Torvalds if (idx >= snd_ecards_limit) 1551da177e4SLinus Torvalds snd_ecards_limit = idx + 1; 1561da177e4SLinus Torvalds break; 1571da177e4SLinus Torvalds } 1585c33dd70SOliver Neukum } else { 1595c33dd70SOliver Neukum if (idx < snd_ecards_limit) { 1601da177e4SLinus Torvalds if (snd_cards_lock & (1 << idx)) 1615c33dd70SOliver Neukum err = -EBUSY; /* invalid */ 1625c33dd70SOliver Neukum } else { 1635c33dd70SOliver Neukum if (idx < SNDRV_CARDS) 1641da177e4SLinus Torvalds snd_ecards_limit = idx + 1; /* increase the limit */ 1651da177e4SLinus Torvalds else 1661da177e4SLinus Torvalds err = -ENODEV; 1675c33dd70SOliver Neukum } 1685c33dd70SOliver Neukum } 1691da177e4SLinus Torvalds if (idx < 0 || err < 0) { 170746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 1715c33dd70SOliver Neukum snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i), error: %d\n", 1725c33dd70SOliver Neukum idx, snd_ecards_limit - 1, err); 1731da177e4SLinus Torvalds goto __error; 1741da177e4SLinus Torvalds } 1751da177e4SLinus Torvalds snd_cards_lock |= 1 << idx; /* lock it */ 176746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 1771da177e4SLinus Torvalds card->number = idx; 1781da177e4SLinus Torvalds card->module = module; 1791da177e4SLinus Torvalds INIT_LIST_HEAD(&card->devices); 1801da177e4SLinus Torvalds init_rwsem(&card->controls_rwsem); 1811da177e4SLinus Torvalds rwlock_init(&card->ctl_files_rwlock); 1821da177e4SLinus Torvalds INIT_LIST_HEAD(&card->controls); 1831da177e4SLinus Torvalds INIT_LIST_HEAD(&card->ctl_files); 1841da177e4SLinus Torvalds spin_lock_init(&card->files_lock); 1851da177e4SLinus Torvalds init_waitqueue_head(&card->shutdown_sleep); 1861da177e4SLinus Torvalds #ifdef CONFIG_PM 1871a60d4c5SIngo Molnar mutex_init(&card->power_lock); 1881da177e4SLinus Torvalds init_waitqueue_head(&card->power_sleep); 1891da177e4SLinus Torvalds #endif 1901da177e4SLinus Torvalds /* the control interface cannot be accessed from the user space until */ 1911da177e4SLinus Torvalds /* snd_cards_bitmask and snd_cards are set with snd_card_register */ 1921da177e4SLinus Torvalds if ((err = snd_ctl_create(card)) < 0) { 1931da177e4SLinus Torvalds snd_printd("unable to register control minors\n"); 1941da177e4SLinus Torvalds goto __error; 1951da177e4SLinus Torvalds } 1961da177e4SLinus Torvalds if ((err = snd_info_card_create(card)) < 0) { 1971da177e4SLinus Torvalds snd_printd("unable to create card info\n"); 1981da177e4SLinus Torvalds goto __error_ctl; 1991da177e4SLinus Torvalds } 2001da177e4SLinus Torvalds if (extra_size > 0) 201512bbd6aSTakashi Iwai card->private_data = (char *)card + sizeof(struct snd_card); 2021da177e4SLinus Torvalds return card; 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds __error_ctl: 2051da177e4SLinus Torvalds snd_device_free_all(card, SNDRV_DEV_CMD_PRE); 2061da177e4SLinus Torvalds __error: 2071da177e4SLinus Torvalds kfree(card); 2081da177e4SLinus Torvalds return NULL; 2091da177e4SLinus Torvalds } 2101da177e4SLinus Torvalds 211c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_new); 212c0d3fb39STakashi Iwai 213746df948STakashi Iwai /* return non-zero if a card is already locked */ 214746df948STakashi Iwai int snd_card_locked(int card) 215746df948STakashi Iwai { 216746df948STakashi Iwai int locked; 217746df948STakashi Iwai 218746df948STakashi Iwai mutex_lock(&snd_card_mutex); 219746df948STakashi Iwai locked = snd_cards_lock & (1 << card); 220746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 221746df948STakashi Iwai return locked; 222746df948STakashi Iwai } 223746df948STakashi Iwai 224b3b0abe1SClemens Ladisch static loff_t snd_disconnect_llseek(struct file *file, loff_t offset, int orig) 225b3b0abe1SClemens Ladisch { 226b3b0abe1SClemens Ladisch return -ENODEV; 227b3b0abe1SClemens Ladisch } 228b3b0abe1SClemens Ladisch 229b3b0abe1SClemens Ladisch static ssize_t snd_disconnect_read(struct file *file, char __user *buf, 230b3b0abe1SClemens Ladisch size_t count, loff_t *offset) 231b3b0abe1SClemens Ladisch { 232b3b0abe1SClemens Ladisch return -ENODEV; 233b3b0abe1SClemens Ladisch } 234b3b0abe1SClemens Ladisch 235b3b0abe1SClemens Ladisch static ssize_t snd_disconnect_write(struct file *file, const char __user *buf, 236b3b0abe1SClemens Ladisch size_t count, loff_t *offset) 237b3b0abe1SClemens Ladisch { 238b3b0abe1SClemens Ladisch return -ENODEV; 239b3b0abe1SClemens Ladisch } 240b3b0abe1SClemens Ladisch 241a9edfc60SKarsten Wiese static int snd_disconnect_release(struct inode *inode, struct file *file) 242a9edfc60SKarsten Wiese { 243a9edfc60SKarsten Wiese struct snd_monitor_file *df = NULL, *_df; 244a9edfc60SKarsten Wiese 245a9edfc60SKarsten Wiese spin_lock(&shutdown_lock); 246a9edfc60SKarsten Wiese list_for_each_entry(_df, &shutdown_files, shutdown_list) { 247a9edfc60SKarsten Wiese if (_df->file == file) { 248a9edfc60SKarsten Wiese df = _df; 249a9edfc60SKarsten Wiese break; 250a9edfc60SKarsten Wiese } 251a9edfc60SKarsten Wiese } 252a9edfc60SKarsten Wiese spin_unlock(&shutdown_lock); 253a9edfc60SKarsten Wiese 254a9edfc60SKarsten Wiese if (likely(df)) 255a9edfc60SKarsten Wiese return df->disconnected_f_op->release(inode, file); 256a9edfc60SKarsten Wiese 2579bf8e7ddSHarvey Harrison panic("%s(%p, %p) failed!", __func__, inode, file); 258a9edfc60SKarsten Wiese } 259a9edfc60SKarsten Wiese 2601da177e4SLinus Torvalds static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait) 2611da177e4SLinus Torvalds { 2621da177e4SLinus Torvalds return POLLERR | POLLNVAL; 2631da177e4SLinus Torvalds } 2641da177e4SLinus Torvalds 265b3b0abe1SClemens Ladisch static long snd_disconnect_ioctl(struct file *file, 266b3b0abe1SClemens Ladisch unsigned int cmd, unsigned long arg) 267b3b0abe1SClemens Ladisch { 268b3b0abe1SClemens Ladisch return -ENODEV; 269b3b0abe1SClemens Ladisch } 270b3b0abe1SClemens Ladisch 271b3b0abe1SClemens Ladisch static int snd_disconnect_mmap(struct file *file, struct vm_area_struct *vma) 272b3b0abe1SClemens Ladisch { 273b3b0abe1SClemens Ladisch return -ENODEV; 274b3b0abe1SClemens Ladisch } 275b3b0abe1SClemens Ladisch 276b3b0abe1SClemens Ladisch static int snd_disconnect_fasync(int fd, struct file *file, int on) 277b3b0abe1SClemens Ladisch { 278b3b0abe1SClemens Ladisch return -ENODEV; 279b3b0abe1SClemens Ladisch } 280b3b0abe1SClemens Ladisch 2819c2e08c5SArjan van de Ven static const struct file_operations snd_shutdown_f_ops = 282a9edfc60SKarsten Wiese { 283a9edfc60SKarsten Wiese .owner = THIS_MODULE, 284a9edfc60SKarsten Wiese .llseek = snd_disconnect_llseek, 285a9edfc60SKarsten Wiese .read = snd_disconnect_read, 286a9edfc60SKarsten Wiese .write = snd_disconnect_write, 287a9edfc60SKarsten Wiese .release = snd_disconnect_release, 288a9edfc60SKarsten Wiese .poll = snd_disconnect_poll, 289a9edfc60SKarsten Wiese .unlocked_ioctl = snd_disconnect_ioctl, 290a9edfc60SKarsten Wiese #ifdef CONFIG_COMPAT 291a9edfc60SKarsten Wiese .compat_ioctl = snd_disconnect_ioctl, 292a9edfc60SKarsten Wiese #endif 293a9edfc60SKarsten Wiese .mmap = snd_disconnect_mmap, 294a9edfc60SKarsten Wiese .fasync = snd_disconnect_fasync 295a9edfc60SKarsten Wiese }; 296a9edfc60SKarsten Wiese 2971da177e4SLinus Torvalds /** 2981da177e4SLinus Torvalds * snd_card_disconnect - disconnect all APIs from the file-operations (user space) 2991da177e4SLinus Torvalds * @card: soundcard structure 3001da177e4SLinus Torvalds * 3011da177e4SLinus Torvalds * Disconnects all APIs from the file-operations (user space). 3021da177e4SLinus Torvalds * 3031da177e4SLinus Torvalds * Returns zero, otherwise a negative error code. 3041da177e4SLinus Torvalds * 3051da177e4SLinus Torvalds * Note: The current implementation replaces all active file->f_op with special 3061da177e4SLinus Torvalds * dummy file operations (they do nothing except release). 3071da177e4SLinus Torvalds */ 308512bbd6aSTakashi Iwai int snd_card_disconnect(struct snd_card *card) 3091da177e4SLinus Torvalds { 3101da177e4SLinus Torvalds struct snd_monitor_file *mfile; 3111da177e4SLinus Torvalds struct file *file; 3121da177e4SLinus Torvalds int err; 3131da177e4SLinus Torvalds 3141da177e4SLinus Torvalds spin_lock(&card->files_lock); 3151da177e4SLinus Torvalds if (card->shutdown) { 3161da177e4SLinus Torvalds spin_unlock(&card->files_lock); 3171da177e4SLinus Torvalds return 0; 3181da177e4SLinus Torvalds } 3191da177e4SLinus Torvalds card->shutdown = 1; 3201da177e4SLinus Torvalds spin_unlock(&card->files_lock); 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds /* phase 1: disable fops (user space) operations for ALSA API */ 323746df948STakashi Iwai mutex_lock(&snd_card_mutex); 3241da177e4SLinus Torvalds snd_cards[card->number] = NULL; 325746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 3261da177e4SLinus Torvalds 3271da177e4SLinus Torvalds /* phase 2: replace file->f_op with special dummy operations */ 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds spin_lock(&card->files_lock); 3301da177e4SLinus Torvalds mfile = card->files; 3311da177e4SLinus Torvalds while (mfile) { 3321da177e4SLinus Torvalds file = mfile->file; 3331da177e4SLinus Torvalds 3341da177e4SLinus Torvalds /* it's critical part, use endless loop */ 3351da177e4SLinus Torvalds /* we have no room to fail */ 336a9edfc60SKarsten Wiese mfile->disconnected_f_op = mfile->file->f_op; 3371da177e4SLinus Torvalds 338a9edfc60SKarsten Wiese spin_lock(&shutdown_lock); 339a9edfc60SKarsten Wiese list_add(&mfile->shutdown_list, &shutdown_files); 340a9edfc60SKarsten Wiese spin_unlock(&shutdown_lock); 3411da177e4SLinus Torvalds 342a9edfc60SKarsten Wiese mfile->file->f_op = &snd_shutdown_f_ops; 343bc9abce0SMiguel Boton fops_get(mfile->file->f_op); 3441da177e4SLinus Torvalds 3451da177e4SLinus Torvalds mfile = mfile->next; 3461da177e4SLinus Torvalds } 3471da177e4SLinus Torvalds spin_unlock(&card->files_lock); 3481da177e4SLinus Torvalds 3491da177e4SLinus Torvalds /* phase 3: notify all connected devices about disconnection */ 3501da177e4SLinus Torvalds /* at this point, they cannot respond to any calls except release() */ 3511da177e4SLinus Torvalds 3521da177e4SLinus Torvalds #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) 3531da177e4SLinus Torvalds if (snd_mixer_oss_notify_callback) 3541da177e4SLinus Torvalds snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT); 3551da177e4SLinus Torvalds #endif 3561da177e4SLinus Torvalds 3571da177e4SLinus Torvalds /* notify all devices that we are disconnected */ 3581da177e4SLinus Torvalds err = snd_device_disconnect_all(card); 3591da177e4SLinus Torvalds if (err < 0) 3601da177e4SLinus Torvalds snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number); 3611da177e4SLinus Torvalds 362746d4a02STakashi Iwai snd_info_card_disconnect(card); 3631da177e4SLinus Torvalds return 0; 3641da177e4SLinus Torvalds } 3651da177e4SLinus Torvalds 366c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_disconnect); 367c0d3fb39STakashi Iwai 3681da177e4SLinus Torvalds /** 3691da177e4SLinus Torvalds * snd_card_free - frees given soundcard structure 3701da177e4SLinus Torvalds * @card: soundcard structure 3711da177e4SLinus Torvalds * 3721da177e4SLinus Torvalds * This function releases the soundcard structure and the all assigned 3731da177e4SLinus Torvalds * devices automatically. That is, you don't have to release the devices 3741da177e4SLinus Torvalds * by yourself. 3751da177e4SLinus Torvalds * 3761da177e4SLinus Torvalds * Returns zero. Frees all associated devices and frees the control 3771da177e4SLinus Torvalds * interface associated to given soundcard. 3781da177e4SLinus Torvalds */ 379c461482cSTakashi Iwai static int snd_card_do_free(struct snd_card *card) 3801da177e4SLinus Torvalds { 3811da177e4SLinus Torvalds #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) 3821da177e4SLinus Torvalds if (snd_mixer_oss_notify_callback) 3831da177e4SLinus Torvalds snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); 3841da177e4SLinus Torvalds #endif 3851da177e4SLinus Torvalds if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) { 3861da177e4SLinus Torvalds snd_printk(KERN_ERR "unable to free all devices (pre)\n"); 3871da177e4SLinus Torvalds /* Fatal, but this situation should never occur */ 3881da177e4SLinus Torvalds } 3891da177e4SLinus Torvalds if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) { 3901da177e4SLinus Torvalds snd_printk(KERN_ERR "unable to free all devices (normal)\n"); 3911da177e4SLinus Torvalds /* Fatal, but this situation should never occur */ 3921da177e4SLinus Torvalds } 3931da177e4SLinus Torvalds if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) { 3941da177e4SLinus Torvalds snd_printk(KERN_ERR "unable to free all devices (post)\n"); 3951da177e4SLinus Torvalds /* Fatal, but this situation should never occur */ 3961da177e4SLinus Torvalds } 3971da177e4SLinus Torvalds if (card->private_free) 3981da177e4SLinus Torvalds card->private_free(card); 399746d4a02STakashi Iwai snd_info_free_entry(card->proc_id); 4001da177e4SLinus Torvalds if (snd_info_card_free(card) < 0) { 4011da177e4SLinus Torvalds snd_printk(KERN_WARNING "unable to free card info\n"); 4021da177e4SLinus Torvalds /* Not fatal error */ 4031da177e4SLinus Torvalds } 4047d2aae1eSTakashi Iwai #ifndef CONFIG_SYSFS_DEPRECATED 4057d2aae1eSTakashi Iwai if (card->card_dev) 4067d2aae1eSTakashi Iwai device_unregister(card->card_dev); 4077d2aae1eSTakashi Iwai #endif 408c461482cSTakashi Iwai kfree(card); 409c461482cSTakashi Iwai return 0; 410c461482cSTakashi Iwai } 411c461482cSTakashi Iwai 412c461482cSTakashi Iwai static int snd_card_free_prepare(struct snd_card *card) 413c461482cSTakashi Iwai { 414c461482cSTakashi Iwai if (card == NULL) 415c461482cSTakashi Iwai return -EINVAL; 416c461482cSTakashi Iwai (void) snd_card_disconnect(card); 417746df948STakashi Iwai mutex_lock(&snd_card_mutex); 418c461482cSTakashi Iwai snd_cards[card->number] = NULL; 4191da177e4SLinus Torvalds snd_cards_lock &= ~(1 << card->number); 420746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 421c461482cSTakashi Iwai #ifdef CONFIG_PM 422c461482cSTakashi Iwai wake_up(&card->power_sleep); 423c461482cSTakashi Iwai #endif 424c461482cSTakashi Iwai return 0; 425c461482cSTakashi Iwai } 426c461482cSTakashi Iwai 427c461482cSTakashi Iwai int snd_card_free_when_closed(struct snd_card *card) 428c461482cSTakashi Iwai { 429c461482cSTakashi Iwai int free_now = 0; 430c461482cSTakashi Iwai int ret = snd_card_free_prepare(card); 431c461482cSTakashi Iwai if (ret) 432c461482cSTakashi Iwai return ret; 433c461482cSTakashi Iwai 434c461482cSTakashi Iwai spin_lock(&card->files_lock); 435c461482cSTakashi Iwai if (card->files == NULL) 436c461482cSTakashi Iwai free_now = 1; 437c461482cSTakashi Iwai else 438c461482cSTakashi Iwai card->free_on_last_close = 1; 439c461482cSTakashi Iwai spin_unlock(&card->files_lock); 440c461482cSTakashi Iwai 441c461482cSTakashi Iwai if (free_now) 442c461482cSTakashi Iwai snd_card_do_free(card); 443c461482cSTakashi Iwai return 0; 444c461482cSTakashi Iwai } 445c461482cSTakashi Iwai 446c461482cSTakashi Iwai EXPORT_SYMBOL(snd_card_free_when_closed); 447c461482cSTakashi Iwai 448c461482cSTakashi Iwai int snd_card_free(struct snd_card *card) 449c461482cSTakashi Iwai { 450c461482cSTakashi Iwai int ret = snd_card_free_prepare(card); 451c461482cSTakashi Iwai if (ret) 452c461482cSTakashi Iwai return ret; 453c461482cSTakashi Iwai 454c461482cSTakashi Iwai /* wait, until all devices are ready for the free operation */ 455c461482cSTakashi Iwai wait_event(card->shutdown_sleep, card->files == NULL); 456c461482cSTakashi Iwai snd_card_do_free(card); 4571da177e4SLinus Torvalds return 0; 4581da177e4SLinus Torvalds } 4591da177e4SLinus Torvalds 460c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_free); 461c0d3fb39STakashi Iwai 462512bbd6aSTakashi Iwai static void choose_default_id(struct snd_card *card) 4631da177e4SLinus Torvalds { 464d001544dSClemens Ladisch int i, len, idx_flag = 0, loops = SNDRV_CARDS; 4651da177e4SLinus Torvalds char *id, *spos; 4661da177e4SLinus Torvalds 4671da177e4SLinus Torvalds id = spos = card->shortname; 4681da177e4SLinus Torvalds while (*id != '\0') { 4691da177e4SLinus Torvalds if (*id == ' ') 4701da177e4SLinus Torvalds spos = id + 1; 4711da177e4SLinus Torvalds id++; 4721da177e4SLinus Torvalds } 4731da177e4SLinus Torvalds id = card->id; 4741da177e4SLinus Torvalds while (*spos != '\0' && !isalnum(*spos)) 4751da177e4SLinus Torvalds spos++; 4761da177e4SLinus Torvalds if (isdigit(*spos)) 4771da177e4SLinus Torvalds *id++ = isalpha(card->shortname[0]) ? card->shortname[0] : 'D'; 4781da177e4SLinus Torvalds while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) { 4791da177e4SLinus Torvalds if (isalnum(*spos)) 4801da177e4SLinus Torvalds *id++ = *spos; 4811da177e4SLinus Torvalds spos++; 4821da177e4SLinus Torvalds } 4831da177e4SLinus Torvalds *id = '\0'; 4841da177e4SLinus Torvalds 4851da177e4SLinus Torvalds id = card->id; 4861da177e4SLinus Torvalds 4871da177e4SLinus Torvalds if (*id == '\0') 4881da177e4SLinus Torvalds strcpy(id, "default"); 4891da177e4SLinus Torvalds 4901da177e4SLinus Torvalds while (1) { 4911da177e4SLinus Torvalds if (loops-- == 0) { 4921da177e4SLinus Torvalds snd_printk(KERN_ERR "unable to choose default card id (%s)\n", id); 4931da177e4SLinus Torvalds strcpy(card->id, card->proc_root->name); 4941da177e4SLinus Torvalds return; 4951da177e4SLinus Torvalds } 4961da177e4SLinus Torvalds if (!snd_info_check_reserved_words(id)) 4971da177e4SLinus Torvalds goto __change; 4981da177e4SLinus Torvalds for (i = 0; i < snd_ecards_limit; i++) { 4991da177e4SLinus Torvalds if (snd_cards[i] && !strcmp(snd_cards[i]->id, id)) 5001da177e4SLinus Torvalds goto __change; 5011da177e4SLinus Torvalds } 5021da177e4SLinus Torvalds break; 5031da177e4SLinus Torvalds 5041da177e4SLinus Torvalds __change: 5051da177e4SLinus Torvalds len = strlen(id); 506d001544dSClemens Ladisch if (idx_flag) { 507d001544dSClemens Ladisch if (id[len-1] != '9') 5081da177e4SLinus Torvalds id[len-1]++; 509d001544dSClemens Ladisch else 510d001544dSClemens Ladisch id[len-1] = 'A'; 511d001544dSClemens Ladisch } else if ((size_t)len <= sizeof(card->id) - 3) { 5121da177e4SLinus Torvalds strcat(id, "_1"); 5131da177e4SLinus Torvalds idx_flag++; 5141da177e4SLinus Torvalds } else { 5151da177e4SLinus Torvalds spos = id + len - 2; 5161da177e4SLinus Torvalds if ((size_t)len <= sizeof(card->id) - 2) 5171da177e4SLinus Torvalds spos++; 5181da177e4SLinus Torvalds *spos++ = '_'; 5191da177e4SLinus Torvalds *spos++ = '1'; 5201da177e4SLinus Torvalds *spos++ = '\0'; 5211da177e4SLinus Torvalds idx_flag++; 5221da177e4SLinus Torvalds } 5231da177e4SLinus Torvalds } 5241da177e4SLinus Torvalds } 5251da177e4SLinus Torvalds 5261da177e4SLinus Torvalds /** 5271da177e4SLinus Torvalds * snd_card_register - register the soundcard 5281da177e4SLinus Torvalds * @card: soundcard structure 5291da177e4SLinus Torvalds * 5301da177e4SLinus Torvalds * This function registers all the devices assigned to the soundcard. 5311da177e4SLinus Torvalds * Until calling this, the ALSA control interface is blocked from the 5321da177e4SLinus Torvalds * external accesses. Thus, you should call this function at the end 5331da177e4SLinus Torvalds * of the initialization of the card. 5341da177e4SLinus Torvalds * 5351da177e4SLinus Torvalds * Returns zero otherwise a negative error code if the registrain failed. 5361da177e4SLinus Torvalds */ 537512bbd6aSTakashi Iwai int snd_card_register(struct snd_card *card) 5381da177e4SLinus Torvalds { 5391da177e4SLinus Torvalds int err; 5401da177e4SLinus Torvalds 5417c22f1aaSTakashi Iwai snd_assert(card != NULL, return -EINVAL); 5427d2aae1eSTakashi Iwai #ifndef CONFIG_SYSFS_DEPRECATED 5437d2aae1eSTakashi Iwai if (!card->card_dev) { 5447d2aae1eSTakashi Iwai card->card_dev = device_create(sound_class, card->dev, 0, 545d80f19faSGreg Kroah-Hartman "card%i", card->number); 5467d2aae1eSTakashi Iwai if (IS_ERR(card->card_dev)) 5477d2aae1eSTakashi Iwai card->card_dev = NULL; 548d80f19faSGreg Kroah-Hartman } 5497d2aae1eSTakashi Iwai #endif 5501da177e4SLinus Torvalds if ((err = snd_device_register_all(card)) < 0) 5511da177e4SLinus Torvalds return err; 552746df948STakashi Iwai mutex_lock(&snd_card_mutex); 5531da177e4SLinus Torvalds if (snd_cards[card->number]) { 5541da177e4SLinus Torvalds /* already registered */ 555746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 5561da177e4SLinus Torvalds return 0; 5571da177e4SLinus Torvalds } 5581da177e4SLinus Torvalds if (card->id[0] == '\0') 5591da177e4SLinus Torvalds choose_default_id(card); 5601da177e4SLinus Torvalds snd_cards[card->number] = card; 561746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 562e28563ccSTakashi Iwai init_info_for_card(card); 5631da177e4SLinus Torvalds #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) 5641da177e4SLinus Torvalds if (snd_mixer_oss_notify_callback) 5651da177e4SLinus Torvalds snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER); 5661da177e4SLinus Torvalds #endif 5671da177e4SLinus Torvalds return 0; 5681da177e4SLinus Torvalds } 5691da177e4SLinus Torvalds 570c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_register); 571c0d3fb39STakashi Iwai 572e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS 5736581f4e7STakashi Iwai static struct snd_info_entry *snd_card_info_entry; 5741da177e4SLinus Torvalds 575a381a7a6STakashi Iwai static void snd_card_info_read(struct snd_info_entry *entry, 576a381a7a6STakashi Iwai struct snd_info_buffer *buffer) 5771da177e4SLinus Torvalds { 5781da177e4SLinus Torvalds int idx, count; 579512bbd6aSTakashi Iwai struct snd_card *card; 5801da177e4SLinus Torvalds 5811da177e4SLinus Torvalds for (idx = count = 0; idx < SNDRV_CARDS; idx++) { 582746df948STakashi Iwai mutex_lock(&snd_card_mutex); 5831da177e4SLinus Torvalds if ((card = snd_cards[idx]) != NULL) { 5841da177e4SLinus Torvalds count++; 585d001544dSClemens Ladisch snd_iprintf(buffer, "%2i [%-15s]: %s - %s\n", 5861da177e4SLinus Torvalds idx, 5871da177e4SLinus Torvalds card->id, 5881da177e4SLinus Torvalds card->driver, 5891da177e4SLinus Torvalds card->shortname); 5901da177e4SLinus Torvalds snd_iprintf(buffer, " %s\n", 5911da177e4SLinus Torvalds card->longname); 5921da177e4SLinus Torvalds } 593746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 5941da177e4SLinus Torvalds } 5951da177e4SLinus Torvalds if (!count) 5961da177e4SLinus Torvalds snd_iprintf(buffer, "--- no soundcards ---\n"); 5971da177e4SLinus Torvalds } 5981da177e4SLinus Torvalds 599e28563ccSTakashi Iwai #ifdef CONFIG_SND_OSSEMUL 6001da177e4SLinus Torvalds 601512bbd6aSTakashi Iwai void snd_card_info_read_oss(struct snd_info_buffer *buffer) 6021da177e4SLinus Torvalds { 6031da177e4SLinus Torvalds int idx, count; 604512bbd6aSTakashi Iwai struct snd_card *card; 6051da177e4SLinus Torvalds 6061da177e4SLinus Torvalds for (idx = count = 0; idx < SNDRV_CARDS; idx++) { 607746df948STakashi Iwai mutex_lock(&snd_card_mutex); 6081da177e4SLinus Torvalds if ((card = snd_cards[idx]) != NULL) { 6091da177e4SLinus Torvalds count++; 6101da177e4SLinus Torvalds snd_iprintf(buffer, "%s\n", card->longname); 6111da177e4SLinus Torvalds } 612746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 6131da177e4SLinus Torvalds } 6141da177e4SLinus Torvalds if (!count) { 6151da177e4SLinus Torvalds snd_iprintf(buffer, "--- no soundcards ---\n"); 6161da177e4SLinus Torvalds } 6171da177e4SLinus Torvalds } 6181da177e4SLinus Torvalds 6191da177e4SLinus Torvalds #endif 6201da177e4SLinus Torvalds 6211da177e4SLinus Torvalds #ifdef MODULE 622512bbd6aSTakashi Iwai static struct snd_info_entry *snd_card_module_info_entry; 623512bbd6aSTakashi Iwai static void snd_card_module_info_read(struct snd_info_entry *entry, 624512bbd6aSTakashi Iwai struct snd_info_buffer *buffer) 6251da177e4SLinus Torvalds { 6261da177e4SLinus Torvalds int idx; 627512bbd6aSTakashi Iwai struct snd_card *card; 6281da177e4SLinus Torvalds 6291da177e4SLinus Torvalds for (idx = 0; idx < SNDRV_CARDS; idx++) { 630746df948STakashi Iwai mutex_lock(&snd_card_mutex); 6311da177e4SLinus Torvalds if ((card = snd_cards[idx]) != NULL) 632d001544dSClemens Ladisch snd_iprintf(buffer, "%2i %s\n", 633d001544dSClemens Ladisch idx, card->module->name); 634746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 6351da177e4SLinus Torvalds } 6361da177e4SLinus Torvalds } 6371da177e4SLinus Torvalds #endif 6381da177e4SLinus Torvalds 6391da177e4SLinus Torvalds int __init snd_card_info_init(void) 6401da177e4SLinus Torvalds { 641512bbd6aSTakashi Iwai struct snd_info_entry *entry; 6421da177e4SLinus Torvalds 6431da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL); 6447c22f1aaSTakashi Iwai if (! entry) 6457c22f1aaSTakashi Iwai return -ENOMEM; 6461da177e4SLinus Torvalds entry->c.text.read = snd_card_info_read; 6471da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 6481da177e4SLinus Torvalds snd_info_free_entry(entry); 6491da177e4SLinus Torvalds return -ENOMEM; 6501da177e4SLinus Torvalds } 6511da177e4SLinus Torvalds snd_card_info_entry = entry; 6521da177e4SLinus Torvalds 6531da177e4SLinus Torvalds #ifdef MODULE 6541da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL); 6551da177e4SLinus Torvalds if (entry) { 6561da177e4SLinus Torvalds entry->c.text.read = snd_card_module_info_read; 6571da177e4SLinus Torvalds if (snd_info_register(entry) < 0) 6581da177e4SLinus Torvalds snd_info_free_entry(entry); 6591da177e4SLinus Torvalds else 6601da177e4SLinus Torvalds snd_card_module_info_entry = entry; 6611da177e4SLinus Torvalds } 6621da177e4SLinus Torvalds #endif 6631da177e4SLinus Torvalds 6641da177e4SLinus Torvalds return 0; 6651da177e4SLinus Torvalds } 6661da177e4SLinus Torvalds 6671da177e4SLinus Torvalds int __exit snd_card_info_done(void) 6681da177e4SLinus Torvalds { 669746d4a02STakashi Iwai snd_info_free_entry(snd_card_info_entry); 6701da177e4SLinus Torvalds #ifdef MODULE 671746d4a02STakashi Iwai snd_info_free_entry(snd_card_module_info_entry); 6721da177e4SLinus Torvalds #endif 6731da177e4SLinus Torvalds return 0; 6741da177e4SLinus Torvalds } 6751da177e4SLinus Torvalds 676e28563ccSTakashi Iwai #endif /* CONFIG_PROC_FS */ 677e28563ccSTakashi Iwai 6781da177e4SLinus Torvalds /** 6791da177e4SLinus Torvalds * snd_component_add - add a component string 6801da177e4SLinus Torvalds * @card: soundcard structure 6811da177e4SLinus Torvalds * @component: the component id string 6821da177e4SLinus Torvalds * 6831da177e4SLinus Torvalds * This function adds the component id string to the supported list. 6841da177e4SLinus Torvalds * The component can be referred from the alsa-lib. 6851da177e4SLinus Torvalds * 6861da177e4SLinus Torvalds * Returns zero otherwise a negative error code. 6871da177e4SLinus Torvalds */ 6881da177e4SLinus Torvalds 689512bbd6aSTakashi Iwai int snd_component_add(struct snd_card *card, const char *component) 6901da177e4SLinus Torvalds { 6911da177e4SLinus Torvalds char *ptr; 6921da177e4SLinus Torvalds int len = strlen(component); 6931da177e4SLinus Torvalds 6941da177e4SLinus Torvalds ptr = strstr(card->components, component); 6951da177e4SLinus Torvalds if (ptr != NULL) { 6961da177e4SLinus Torvalds if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */ 6971da177e4SLinus Torvalds return 1; 6981da177e4SLinus Torvalds } 6991da177e4SLinus Torvalds if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) { 7001da177e4SLinus Torvalds snd_BUG(); 7011da177e4SLinus Torvalds return -ENOMEM; 7021da177e4SLinus Torvalds } 7031da177e4SLinus Torvalds if (card->components[0] != '\0') 7041da177e4SLinus Torvalds strcat(card->components, " "); 7051da177e4SLinus Torvalds strcat(card->components, component); 7061da177e4SLinus Torvalds return 0; 7071da177e4SLinus Torvalds } 7081da177e4SLinus Torvalds 709c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_component_add); 710c0d3fb39STakashi Iwai 7111da177e4SLinus Torvalds /** 7121da177e4SLinus Torvalds * snd_card_file_add - add the file to the file list of the card 7131da177e4SLinus Torvalds * @card: soundcard structure 7141da177e4SLinus Torvalds * @file: file pointer 7151da177e4SLinus Torvalds * 7161da177e4SLinus Torvalds * This function adds the file to the file linked-list of the card. 7171da177e4SLinus Torvalds * This linked-list is used to keep tracking the connection state, 7181da177e4SLinus Torvalds * and to avoid the release of busy resources by hotplug. 7191da177e4SLinus Torvalds * 7201da177e4SLinus Torvalds * Returns zero or a negative error code. 7211da177e4SLinus Torvalds */ 722512bbd6aSTakashi Iwai int snd_card_file_add(struct snd_card *card, struct file *file) 7231da177e4SLinus Torvalds { 7241da177e4SLinus Torvalds struct snd_monitor_file *mfile; 7251da177e4SLinus Torvalds 7261da177e4SLinus Torvalds mfile = kmalloc(sizeof(*mfile), GFP_KERNEL); 7271da177e4SLinus Torvalds if (mfile == NULL) 7281da177e4SLinus Torvalds return -ENOMEM; 7291da177e4SLinus Torvalds mfile->file = file; 730a9edfc60SKarsten Wiese mfile->disconnected_f_op = NULL; 7311da177e4SLinus Torvalds mfile->next = NULL; 7321da177e4SLinus Torvalds spin_lock(&card->files_lock); 7331da177e4SLinus Torvalds if (card->shutdown) { 7341da177e4SLinus Torvalds spin_unlock(&card->files_lock); 7351da177e4SLinus Torvalds kfree(mfile); 7361da177e4SLinus Torvalds return -ENODEV; 7371da177e4SLinus Torvalds } 7381da177e4SLinus Torvalds mfile->next = card->files; 7391da177e4SLinus Torvalds card->files = mfile; 7401da177e4SLinus Torvalds spin_unlock(&card->files_lock); 7411da177e4SLinus Torvalds return 0; 7421da177e4SLinus Torvalds } 7431da177e4SLinus Torvalds 744c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_file_add); 745c0d3fb39STakashi Iwai 7461da177e4SLinus Torvalds /** 7471da177e4SLinus Torvalds * snd_card_file_remove - remove the file from the file list 7481da177e4SLinus Torvalds * @card: soundcard structure 7491da177e4SLinus Torvalds * @file: file pointer 7501da177e4SLinus Torvalds * 7511da177e4SLinus Torvalds * This function removes the file formerly added to the card via 7521da177e4SLinus Torvalds * snd_card_file_add() function. 7532b29b13cSTakashi Iwai * If all files are removed and snd_card_free_when_closed() was 7542b29b13cSTakashi Iwai * called beforehand, it processes the pending release of 7552b29b13cSTakashi Iwai * resources. 7561da177e4SLinus Torvalds * 7571da177e4SLinus Torvalds * Returns zero or a negative error code. 7581da177e4SLinus Torvalds */ 759512bbd6aSTakashi Iwai int snd_card_file_remove(struct snd_card *card, struct file *file) 7601da177e4SLinus Torvalds { 7611da177e4SLinus Torvalds struct snd_monitor_file *mfile, *pfile = NULL; 762c461482cSTakashi Iwai int last_close = 0; 7631da177e4SLinus Torvalds 7641da177e4SLinus Torvalds spin_lock(&card->files_lock); 7651da177e4SLinus Torvalds mfile = card->files; 7661da177e4SLinus Torvalds while (mfile) { 7671da177e4SLinus Torvalds if (mfile->file == file) { 7681da177e4SLinus Torvalds if (pfile) 7691da177e4SLinus Torvalds pfile->next = mfile->next; 7701da177e4SLinus Torvalds else 7711da177e4SLinus Torvalds card->files = mfile->next; 7721da177e4SLinus Torvalds break; 7731da177e4SLinus Torvalds } 7741da177e4SLinus Torvalds pfile = mfile; 7751da177e4SLinus Torvalds mfile = mfile->next; 7761da177e4SLinus Torvalds } 777a9edfc60SKarsten Wiese if (mfile && mfile->disconnected_f_op) { 778a9edfc60SKarsten Wiese fops_put(mfile->disconnected_f_op); 779a9edfc60SKarsten Wiese spin_lock(&shutdown_lock); 780a9edfc60SKarsten Wiese list_del(&mfile->shutdown_list); 781a9edfc60SKarsten Wiese spin_unlock(&shutdown_lock); 782a9edfc60SKarsten Wiese } 7831da177e4SLinus Torvalds if (card->files == NULL) 784c461482cSTakashi Iwai last_close = 1; 785c461482cSTakashi Iwai spin_unlock(&card->files_lock); 786c461482cSTakashi Iwai if (last_close) { 7871da177e4SLinus Torvalds wake_up(&card->shutdown_sleep); 788c461482cSTakashi Iwai if (card->free_on_last_close) 789c461482cSTakashi Iwai snd_card_do_free(card); 790c461482cSTakashi Iwai } 7911da177e4SLinus Torvalds if (!mfile) { 7921da177e4SLinus Torvalds snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file); 7931da177e4SLinus Torvalds return -ENOENT; 7941da177e4SLinus Torvalds } 7951da177e4SLinus Torvalds kfree(mfile); 7961da177e4SLinus Torvalds return 0; 7971da177e4SLinus Torvalds } 7981da177e4SLinus Torvalds 799c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_file_remove); 800c0d3fb39STakashi Iwai 8011da177e4SLinus Torvalds #ifdef CONFIG_PM 8021da177e4SLinus Torvalds /** 8031da177e4SLinus Torvalds * snd_power_wait - wait until the power-state is changed. 8041da177e4SLinus Torvalds * @card: soundcard structure 8051da177e4SLinus Torvalds * @power_state: expected power state 8061da177e4SLinus Torvalds * 8071da177e4SLinus Torvalds * Waits until the power-state is changed. 8081da177e4SLinus Torvalds * 8091da177e4SLinus Torvalds * Note: the power lock must be active before call. 8101da177e4SLinus Torvalds */ 811cbac4b0cSTakashi Iwai int snd_power_wait(struct snd_card *card, unsigned int power_state) 8121da177e4SLinus Torvalds { 8131da177e4SLinus Torvalds wait_queue_t wait; 8141da177e4SLinus Torvalds int result = 0; 8151da177e4SLinus Torvalds 8161da177e4SLinus Torvalds /* fastpath */ 8171da177e4SLinus Torvalds if (snd_power_get_state(card) == power_state) 8181da177e4SLinus Torvalds return 0; 8191da177e4SLinus Torvalds init_waitqueue_entry(&wait, current); 8201da177e4SLinus Torvalds add_wait_queue(&card->power_sleep, &wait); 8211da177e4SLinus Torvalds while (1) { 8221da177e4SLinus Torvalds if (card->shutdown) { 8231da177e4SLinus Torvalds result = -ENODEV; 8241da177e4SLinus Torvalds break; 8251da177e4SLinus Torvalds } 8261da177e4SLinus Torvalds if (snd_power_get_state(card) == power_state) 8271da177e4SLinus Torvalds break; 8281da177e4SLinus Torvalds set_current_state(TASK_UNINTERRUPTIBLE); 8291da177e4SLinus Torvalds snd_power_unlock(card); 8301da177e4SLinus Torvalds schedule_timeout(30 * HZ); 8311da177e4SLinus Torvalds snd_power_lock(card); 8321da177e4SLinus Torvalds } 8331da177e4SLinus Torvalds remove_wait_queue(&card->power_sleep, &wait); 8341da177e4SLinus Torvalds return result; 8351da177e4SLinus Torvalds } 8361da177e4SLinus Torvalds 837c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_power_wait); 8381da177e4SLinus Torvalds #endif /* CONFIG_PM */ 839