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 49a93bbaa7STakashi Iwai /* return non-zero if the given index is reserved for the given 50304cd07fSTakashi Iwai * module via slots option 51304cd07fSTakashi Iwai */ 52a93bbaa7STakashi Iwai static int module_slot_match(struct module *module, int idx) 53304cd07fSTakashi Iwai { 54a93bbaa7STakashi Iwai int match = 1; 55304cd07fSTakashi Iwai #ifdef MODULE 56a93bbaa7STakashi Iwai const char *s1, *s2; 57a93bbaa7STakashi Iwai 58304cd07fSTakashi Iwai if (!module || !module->name || !slots[idx]) 59304cd07fSTakashi Iwai return 0; 60a93bbaa7STakashi Iwai 61a93bbaa7STakashi Iwai s1 = module->name; 62a93bbaa7STakashi Iwai s2 = slots[idx]; 63a93bbaa7STakashi Iwai if (*s2 == '!') { 64a93bbaa7STakashi Iwai match = 0; /* negative match */ 65a93bbaa7STakashi Iwai s2++; 66a93bbaa7STakashi Iwai } 67304cd07fSTakashi Iwai /* compare module name strings 68304cd07fSTakashi Iwai * hyphens are handled as equivalent with underscore 69304cd07fSTakashi Iwai */ 70304cd07fSTakashi Iwai for (;;) { 71304cd07fSTakashi Iwai char c1 = *s1++; 72304cd07fSTakashi Iwai char c2 = *s2++; 73304cd07fSTakashi Iwai if (c1 == '-') 74304cd07fSTakashi Iwai c1 = '_'; 75304cd07fSTakashi Iwai if (c2 == '-') 76304cd07fSTakashi Iwai c2 = '_'; 77304cd07fSTakashi Iwai if (c1 != c2) 78a93bbaa7STakashi Iwai return !match; 79304cd07fSTakashi Iwai if (!c1) 80304cd07fSTakashi Iwai break; 81304cd07fSTakashi Iwai } 82a93bbaa7STakashi Iwai #endif /* MODULE */ 83a93bbaa7STakashi Iwai return match; 84304cd07fSTakashi Iwai } 85304cd07fSTakashi Iwai 861da177e4SLinus Torvalds #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) 87512bbd6aSTakashi Iwai int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag); 88c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_mixer_oss_notify_callback); 891da177e4SLinus Torvalds #endif 901da177e4SLinus Torvalds 91e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS 92512bbd6aSTakashi Iwai static void snd_card_id_read(struct snd_info_entry *entry, 93512bbd6aSTakashi Iwai struct snd_info_buffer *buffer) 941da177e4SLinus Torvalds { 951da177e4SLinus Torvalds snd_iprintf(buffer, "%s\n", entry->card->id); 961da177e4SLinus Torvalds } 971da177e4SLinus Torvalds 98e28563ccSTakashi Iwai static inline int init_info_for_card(struct snd_card *card) 99e28563ccSTakashi Iwai { 100e28563ccSTakashi Iwai int err; 101e28563ccSTakashi Iwai struct snd_info_entry *entry; 102e28563ccSTakashi Iwai 103e28563ccSTakashi Iwai if ((err = snd_info_card_register(card)) < 0) { 104e28563ccSTakashi Iwai snd_printd("unable to create card info\n"); 105e28563ccSTakashi Iwai return err; 106e28563ccSTakashi Iwai } 107e28563ccSTakashi Iwai if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) { 108e28563ccSTakashi Iwai snd_printd("unable to create card entry\n"); 109e28563ccSTakashi Iwai return err; 110e28563ccSTakashi Iwai } 111e28563ccSTakashi Iwai entry->c.text.read = snd_card_id_read; 112e28563ccSTakashi Iwai if (snd_info_register(entry) < 0) { 113e28563ccSTakashi Iwai snd_info_free_entry(entry); 114e28563ccSTakashi Iwai entry = NULL; 115e28563ccSTakashi Iwai } 116e28563ccSTakashi Iwai card->proc_id = entry; 117e28563ccSTakashi Iwai return 0; 118e28563ccSTakashi Iwai } 119e28563ccSTakashi Iwai #else /* !CONFIG_PROC_FS */ 120e28563ccSTakashi Iwai #define init_info_for_card(card) 121e28563ccSTakashi Iwai #endif 122e28563ccSTakashi Iwai 1231da177e4SLinus Torvalds /** 1241da177e4SLinus Torvalds * snd_card_new - create and initialize a soundcard structure 1251da177e4SLinus Torvalds * @idx: card index (address) [0 ... (SNDRV_CARDS-1)] 1261da177e4SLinus Torvalds * @xid: card identification (ASCII string) 1271da177e4SLinus Torvalds * @module: top level module for locking 1281da177e4SLinus Torvalds * @extra_size: allocate this extra size after the main soundcard structure 1291da177e4SLinus Torvalds * 1301da177e4SLinus Torvalds * Creates and initializes a soundcard structure. 1311da177e4SLinus Torvalds * 132512bbd6aSTakashi Iwai * Returns kmallocated snd_card structure. Creates the ALSA control interface 1331da177e4SLinus Torvalds * (which is blocked until snd_card_register function is called). 1341da177e4SLinus Torvalds */ 135512bbd6aSTakashi Iwai struct snd_card *snd_card_new(int idx, const char *xid, 1361da177e4SLinus Torvalds struct module *module, int extra_size) 1371da177e4SLinus Torvalds { 138512bbd6aSTakashi Iwai struct snd_card *card; 139a93bbaa7STakashi Iwai int err, idx2; 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds if (extra_size < 0) 1421da177e4SLinus Torvalds extra_size = 0; 143ca2c0966STakashi Iwai card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL); 1441da177e4SLinus Torvalds if (card == NULL) 1451da177e4SLinus Torvalds return NULL; 1461da177e4SLinus Torvalds if (xid) { 1471da177e4SLinus Torvalds if (!snd_info_check_reserved_words(xid)) 1481da177e4SLinus Torvalds goto __error; 1491da177e4SLinus Torvalds strlcpy(card->id, xid, sizeof(card->id)); 1501da177e4SLinus Torvalds } 1511da177e4SLinus Torvalds err = 0; 152746df948STakashi Iwai mutex_lock(&snd_card_mutex); 1531da177e4SLinus Torvalds if (idx < 0) { 1541da177e4SLinus Torvalds for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) 1555c33dd70SOliver Neukum /* idx == -1 == 0xffff means: take any free slot */ 1561da177e4SLinus Torvalds if (~snd_cards_lock & idx & 1<<idx2) { 157a93bbaa7STakashi Iwai if (module_slot_match(module, idx2)) { 1581da177e4SLinus Torvalds idx = idx2; 1591da177e4SLinus Torvalds break; 1601da177e4SLinus Torvalds } 161a93bbaa7STakashi Iwai } 162a93bbaa7STakashi Iwai } 163a93bbaa7STakashi Iwai if (idx < 0) { 164a93bbaa7STakashi Iwai for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) 165a93bbaa7STakashi Iwai /* idx == -1 == 0xffff means: take any free slot */ 166a93bbaa7STakashi Iwai if (~snd_cards_lock & idx & 1<<idx2) { 167a93bbaa7STakashi Iwai if (!slots[idx2] || !*slots[idx2]) { 168a93bbaa7STakashi Iwai idx = idx2; 169a93bbaa7STakashi Iwai break; 170a93bbaa7STakashi Iwai } 171a93bbaa7STakashi Iwai } 172a93bbaa7STakashi Iwai } 173a93bbaa7STakashi Iwai if (idx < 0) 174a93bbaa7STakashi Iwai err = -ENODEV; 175a93bbaa7STakashi Iwai else if (idx < snd_ecards_limit) { 1761da177e4SLinus Torvalds if (snd_cards_lock & (1 << idx)) 1775c33dd70SOliver Neukum err = -EBUSY; /* invalid */ 178a93bbaa7STakashi Iwai } else if (idx >= SNDRV_CARDS) 1791da177e4SLinus Torvalds err = -ENODEV; 180a93bbaa7STakashi Iwai if (err < 0) { 181746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 1825c33dd70SOliver Neukum snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i), error: %d\n", 1835c33dd70SOliver Neukum idx, snd_ecards_limit - 1, err); 1841da177e4SLinus Torvalds goto __error; 1851da177e4SLinus Torvalds } 1861da177e4SLinus Torvalds snd_cards_lock |= 1 << idx; /* lock it */ 187a93bbaa7STakashi Iwai if (idx >= snd_ecards_limit) 188a93bbaa7STakashi Iwai snd_ecards_limit = idx + 1; /* increase the limit */ 189746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 1901da177e4SLinus Torvalds card->number = idx; 1911da177e4SLinus Torvalds card->module = module; 1921da177e4SLinus Torvalds INIT_LIST_HEAD(&card->devices); 1931da177e4SLinus Torvalds init_rwsem(&card->controls_rwsem); 1941da177e4SLinus Torvalds rwlock_init(&card->ctl_files_rwlock); 1951da177e4SLinus Torvalds INIT_LIST_HEAD(&card->controls); 1961da177e4SLinus Torvalds INIT_LIST_HEAD(&card->ctl_files); 1971da177e4SLinus Torvalds spin_lock_init(&card->files_lock); 1981da177e4SLinus Torvalds init_waitqueue_head(&card->shutdown_sleep); 1991da177e4SLinus Torvalds #ifdef CONFIG_PM 2001a60d4c5SIngo Molnar mutex_init(&card->power_lock); 2011da177e4SLinus Torvalds init_waitqueue_head(&card->power_sleep); 2021da177e4SLinus Torvalds #endif 2031da177e4SLinus Torvalds /* the control interface cannot be accessed from the user space until */ 2041da177e4SLinus Torvalds /* snd_cards_bitmask and snd_cards are set with snd_card_register */ 2051da177e4SLinus Torvalds if ((err = snd_ctl_create(card)) < 0) { 2061da177e4SLinus Torvalds snd_printd("unable to register control minors\n"); 2071da177e4SLinus Torvalds goto __error; 2081da177e4SLinus Torvalds } 2091da177e4SLinus Torvalds if ((err = snd_info_card_create(card)) < 0) { 2101da177e4SLinus Torvalds snd_printd("unable to create card info\n"); 2111da177e4SLinus Torvalds goto __error_ctl; 2121da177e4SLinus Torvalds } 2131da177e4SLinus Torvalds if (extra_size > 0) 214512bbd6aSTakashi Iwai card->private_data = (char *)card + sizeof(struct snd_card); 2151da177e4SLinus Torvalds return card; 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds __error_ctl: 2181da177e4SLinus Torvalds snd_device_free_all(card, SNDRV_DEV_CMD_PRE); 2191da177e4SLinus Torvalds __error: 2201da177e4SLinus Torvalds kfree(card); 2211da177e4SLinus Torvalds return NULL; 2221da177e4SLinus Torvalds } 2231da177e4SLinus Torvalds 224c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_new); 225c0d3fb39STakashi Iwai 226746df948STakashi Iwai /* return non-zero if a card is already locked */ 227746df948STakashi Iwai int snd_card_locked(int card) 228746df948STakashi Iwai { 229746df948STakashi Iwai int locked; 230746df948STakashi Iwai 231746df948STakashi Iwai mutex_lock(&snd_card_mutex); 232746df948STakashi Iwai locked = snd_cards_lock & (1 << card); 233746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 234746df948STakashi Iwai return locked; 235746df948STakashi Iwai } 236746df948STakashi Iwai 237b3b0abe1SClemens Ladisch static loff_t snd_disconnect_llseek(struct file *file, loff_t offset, int orig) 238b3b0abe1SClemens Ladisch { 239b3b0abe1SClemens Ladisch return -ENODEV; 240b3b0abe1SClemens Ladisch } 241b3b0abe1SClemens Ladisch 242b3b0abe1SClemens Ladisch static ssize_t snd_disconnect_read(struct file *file, char __user *buf, 243b3b0abe1SClemens Ladisch size_t count, loff_t *offset) 244b3b0abe1SClemens Ladisch { 245b3b0abe1SClemens Ladisch return -ENODEV; 246b3b0abe1SClemens Ladisch } 247b3b0abe1SClemens Ladisch 248b3b0abe1SClemens Ladisch static ssize_t snd_disconnect_write(struct file *file, const char __user *buf, 249b3b0abe1SClemens Ladisch size_t count, loff_t *offset) 250b3b0abe1SClemens Ladisch { 251b3b0abe1SClemens Ladisch return -ENODEV; 252b3b0abe1SClemens Ladisch } 253b3b0abe1SClemens Ladisch 254a9edfc60SKarsten Wiese static int snd_disconnect_release(struct inode *inode, struct file *file) 255a9edfc60SKarsten Wiese { 256a9edfc60SKarsten Wiese struct snd_monitor_file *df = NULL, *_df; 257a9edfc60SKarsten Wiese 258a9edfc60SKarsten Wiese spin_lock(&shutdown_lock); 259a9edfc60SKarsten Wiese list_for_each_entry(_df, &shutdown_files, shutdown_list) { 260a9edfc60SKarsten Wiese if (_df->file == file) { 261a9edfc60SKarsten Wiese df = _df; 262a9edfc60SKarsten Wiese break; 263a9edfc60SKarsten Wiese } 264a9edfc60SKarsten Wiese } 265a9edfc60SKarsten Wiese spin_unlock(&shutdown_lock); 266a9edfc60SKarsten Wiese 267a9edfc60SKarsten Wiese if (likely(df)) 268a9edfc60SKarsten Wiese return df->disconnected_f_op->release(inode, file); 269a9edfc60SKarsten Wiese 2709bf8e7ddSHarvey Harrison panic("%s(%p, %p) failed!", __func__, inode, file); 271a9edfc60SKarsten Wiese } 272a9edfc60SKarsten Wiese 2731da177e4SLinus Torvalds static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait) 2741da177e4SLinus Torvalds { 2751da177e4SLinus Torvalds return POLLERR | POLLNVAL; 2761da177e4SLinus Torvalds } 2771da177e4SLinus Torvalds 278b3b0abe1SClemens Ladisch static long snd_disconnect_ioctl(struct file *file, 279b3b0abe1SClemens Ladisch unsigned int cmd, unsigned long arg) 280b3b0abe1SClemens Ladisch { 281b3b0abe1SClemens Ladisch return -ENODEV; 282b3b0abe1SClemens Ladisch } 283b3b0abe1SClemens Ladisch 284b3b0abe1SClemens Ladisch static int snd_disconnect_mmap(struct file *file, struct vm_area_struct *vma) 285b3b0abe1SClemens Ladisch { 286b3b0abe1SClemens Ladisch return -ENODEV; 287b3b0abe1SClemens Ladisch } 288b3b0abe1SClemens Ladisch 289b3b0abe1SClemens Ladisch static int snd_disconnect_fasync(int fd, struct file *file, int on) 290b3b0abe1SClemens Ladisch { 291b3b0abe1SClemens Ladisch return -ENODEV; 292b3b0abe1SClemens Ladisch } 293b3b0abe1SClemens Ladisch 2949c2e08c5SArjan van de Ven static const struct file_operations snd_shutdown_f_ops = 295a9edfc60SKarsten Wiese { 296a9edfc60SKarsten Wiese .owner = THIS_MODULE, 297a9edfc60SKarsten Wiese .llseek = snd_disconnect_llseek, 298a9edfc60SKarsten Wiese .read = snd_disconnect_read, 299a9edfc60SKarsten Wiese .write = snd_disconnect_write, 300a9edfc60SKarsten Wiese .release = snd_disconnect_release, 301a9edfc60SKarsten Wiese .poll = snd_disconnect_poll, 302a9edfc60SKarsten Wiese .unlocked_ioctl = snd_disconnect_ioctl, 303a9edfc60SKarsten Wiese #ifdef CONFIG_COMPAT 304a9edfc60SKarsten Wiese .compat_ioctl = snd_disconnect_ioctl, 305a9edfc60SKarsten Wiese #endif 306a9edfc60SKarsten Wiese .mmap = snd_disconnect_mmap, 307a9edfc60SKarsten Wiese .fasync = snd_disconnect_fasync 308a9edfc60SKarsten Wiese }; 309a9edfc60SKarsten Wiese 3101da177e4SLinus Torvalds /** 3111da177e4SLinus Torvalds * snd_card_disconnect - disconnect all APIs from the file-operations (user space) 3121da177e4SLinus Torvalds * @card: soundcard structure 3131da177e4SLinus Torvalds * 3141da177e4SLinus Torvalds * Disconnects all APIs from the file-operations (user space). 3151da177e4SLinus Torvalds * 3161da177e4SLinus Torvalds * Returns zero, otherwise a negative error code. 3171da177e4SLinus Torvalds * 3181da177e4SLinus Torvalds * Note: The current implementation replaces all active file->f_op with special 3191da177e4SLinus Torvalds * dummy file operations (they do nothing except release). 3201da177e4SLinus Torvalds */ 321512bbd6aSTakashi Iwai int snd_card_disconnect(struct snd_card *card) 3221da177e4SLinus Torvalds { 3231da177e4SLinus Torvalds struct snd_monitor_file *mfile; 3241da177e4SLinus Torvalds struct file *file; 3251da177e4SLinus Torvalds int err; 3261da177e4SLinus Torvalds 327f18638dcSTakashi Iwai if (!card) 328f18638dcSTakashi Iwai return -EINVAL; 329f18638dcSTakashi Iwai 3301da177e4SLinus Torvalds spin_lock(&card->files_lock); 3311da177e4SLinus Torvalds if (card->shutdown) { 3321da177e4SLinus Torvalds spin_unlock(&card->files_lock); 3331da177e4SLinus Torvalds return 0; 3341da177e4SLinus Torvalds } 3351da177e4SLinus Torvalds card->shutdown = 1; 3361da177e4SLinus Torvalds spin_unlock(&card->files_lock); 3371da177e4SLinus Torvalds 3381da177e4SLinus Torvalds /* phase 1: disable fops (user space) operations for ALSA API */ 339746df948STakashi Iwai mutex_lock(&snd_card_mutex); 3401da177e4SLinus Torvalds snd_cards[card->number] = NULL; 341f18638dcSTakashi Iwai snd_cards_lock &= ~(1 << card->number); 342746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 3431da177e4SLinus Torvalds 3441da177e4SLinus Torvalds /* phase 2: replace file->f_op with special dummy operations */ 3451da177e4SLinus Torvalds 3461da177e4SLinus Torvalds spin_lock(&card->files_lock); 3471da177e4SLinus Torvalds mfile = card->files; 3481da177e4SLinus Torvalds while (mfile) { 3491da177e4SLinus Torvalds file = mfile->file; 3501da177e4SLinus Torvalds 3511da177e4SLinus Torvalds /* it's critical part, use endless loop */ 3521da177e4SLinus Torvalds /* we have no room to fail */ 353a9edfc60SKarsten Wiese mfile->disconnected_f_op = mfile->file->f_op; 3541da177e4SLinus Torvalds 355a9edfc60SKarsten Wiese spin_lock(&shutdown_lock); 356a9edfc60SKarsten Wiese list_add(&mfile->shutdown_list, &shutdown_files); 357a9edfc60SKarsten Wiese spin_unlock(&shutdown_lock); 3581da177e4SLinus Torvalds 359a9edfc60SKarsten Wiese mfile->file->f_op = &snd_shutdown_f_ops; 360bc9abce0SMiguel Boton fops_get(mfile->file->f_op); 3611da177e4SLinus Torvalds 3621da177e4SLinus Torvalds mfile = mfile->next; 3631da177e4SLinus Torvalds } 3641da177e4SLinus Torvalds spin_unlock(&card->files_lock); 3651da177e4SLinus Torvalds 3661da177e4SLinus Torvalds /* phase 3: notify all connected devices about disconnection */ 3671da177e4SLinus Torvalds /* at this point, they cannot respond to any calls except release() */ 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) 3701da177e4SLinus Torvalds if (snd_mixer_oss_notify_callback) 3711da177e4SLinus Torvalds snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT); 3721da177e4SLinus Torvalds #endif 3731da177e4SLinus Torvalds 3741da177e4SLinus Torvalds /* notify all devices that we are disconnected */ 3751da177e4SLinus Torvalds err = snd_device_disconnect_all(card); 3761da177e4SLinus Torvalds if (err < 0) 3771da177e4SLinus Torvalds snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number); 3781da177e4SLinus Torvalds 379746d4a02STakashi Iwai snd_info_card_disconnect(card); 38073d38b13STakashi Iwai #ifndef CONFIG_SYSFS_DEPRECATED 38173d38b13STakashi Iwai if (card->card_dev) { 38273d38b13STakashi Iwai device_unregister(card->card_dev); 38373d38b13STakashi Iwai card->card_dev = NULL; 38473d38b13STakashi Iwai } 38573d38b13STakashi Iwai #endif 386f18638dcSTakashi Iwai #ifdef CONFIG_PM 387f18638dcSTakashi Iwai wake_up(&card->power_sleep); 388f18638dcSTakashi Iwai #endif 3891da177e4SLinus Torvalds return 0; 3901da177e4SLinus Torvalds } 3911da177e4SLinus Torvalds 392c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_disconnect); 393c0d3fb39STakashi Iwai 3941da177e4SLinus Torvalds /** 3951da177e4SLinus Torvalds * snd_card_free - frees given soundcard structure 3961da177e4SLinus Torvalds * @card: soundcard structure 3971da177e4SLinus Torvalds * 3981da177e4SLinus Torvalds * This function releases the soundcard structure and the all assigned 3991da177e4SLinus Torvalds * devices automatically. That is, you don't have to release the devices 4001da177e4SLinus Torvalds * by yourself. 4011da177e4SLinus Torvalds * 4021da177e4SLinus Torvalds * Returns zero. Frees all associated devices and frees the control 4031da177e4SLinus Torvalds * interface associated to given soundcard. 4041da177e4SLinus Torvalds */ 405c461482cSTakashi Iwai static int snd_card_do_free(struct snd_card *card) 4061da177e4SLinus Torvalds { 4071da177e4SLinus Torvalds #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) 4081da177e4SLinus Torvalds if (snd_mixer_oss_notify_callback) 4091da177e4SLinus Torvalds snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); 4101da177e4SLinus Torvalds #endif 4111da177e4SLinus Torvalds if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) { 4121da177e4SLinus Torvalds snd_printk(KERN_ERR "unable to free all devices (pre)\n"); 4131da177e4SLinus Torvalds /* Fatal, but this situation should never occur */ 4141da177e4SLinus Torvalds } 4151da177e4SLinus Torvalds if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) { 4161da177e4SLinus Torvalds snd_printk(KERN_ERR "unable to free all devices (normal)\n"); 4171da177e4SLinus Torvalds /* Fatal, but this situation should never occur */ 4181da177e4SLinus Torvalds } 4191da177e4SLinus Torvalds if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) { 4201da177e4SLinus Torvalds snd_printk(KERN_ERR "unable to free all devices (post)\n"); 4211da177e4SLinus Torvalds /* Fatal, but this situation should never occur */ 4221da177e4SLinus Torvalds } 4231da177e4SLinus Torvalds if (card->private_free) 4241da177e4SLinus Torvalds card->private_free(card); 425746d4a02STakashi Iwai snd_info_free_entry(card->proc_id); 4261da177e4SLinus Torvalds if (snd_info_card_free(card) < 0) { 4271da177e4SLinus Torvalds snd_printk(KERN_WARNING "unable to free card info\n"); 4281da177e4SLinus Torvalds /* Not fatal error */ 4291da177e4SLinus Torvalds } 430c461482cSTakashi Iwai kfree(card); 431c461482cSTakashi Iwai return 0; 432c461482cSTakashi Iwai } 433c461482cSTakashi Iwai 434c461482cSTakashi Iwai int snd_card_free_when_closed(struct snd_card *card) 435c461482cSTakashi Iwai { 436c461482cSTakashi Iwai int free_now = 0; 437f18638dcSTakashi Iwai int ret = snd_card_disconnect(card); 438c461482cSTakashi Iwai if (ret) 439c461482cSTakashi Iwai return ret; 440c461482cSTakashi Iwai 441c461482cSTakashi Iwai spin_lock(&card->files_lock); 442c461482cSTakashi Iwai if (card->files == NULL) 443c461482cSTakashi Iwai free_now = 1; 444c461482cSTakashi Iwai else 445c461482cSTakashi Iwai card->free_on_last_close = 1; 446c461482cSTakashi Iwai spin_unlock(&card->files_lock); 447c461482cSTakashi Iwai 448c461482cSTakashi Iwai if (free_now) 449c461482cSTakashi Iwai snd_card_do_free(card); 450c461482cSTakashi Iwai return 0; 451c461482cSTakashi Iwai } 452c461482cSTakashi Iwai 453c461482cSTakashi Iwai EXPORT_SYMBOL(snd_card_free_when_closed); 454c461482cSTakashi Iwai 455c461482cSTakashi Iwai int snd_card_free(struct snd_card *card) 456c461482cSTakashi Iwai { 457f18638dcSTakashi Iwai int ret = snd_card_disconnect(card); 458c461482cSTakashi Iwai if (ret) 459c461482cSTakashi Iwai return ret; 460c461482cSTakashi Iwai 461c461482cSTakashi Iwai /* wait, until all devices are ready for the free operation */ 462c461482cSTakashi Iwai wait_event(card->shutdown_sleep, card->files == NULL); 463c461482cSTakashi Iwai snd_card_do_free(card); 4641da177e4SLinus Torvalds return 0; 4651da177e4SLinus Torvalds } 4661da177e4SLinus Torvalds 467c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_free); 468c0d3fb39STakashi Iwai 469512bbd6aSTakashi Iwai static void choose_default_id(struct snd_card *card) 4701da177e4SLinus Torvalds { 471d001544dSClemens Ladisch int i, len, idx_flag = 0, loops = SNDRV_CARDS; 4721da177e4SLinus Torvalds char *id, *spos; 4731da177e4SLinus Torvalds 4741da177e4SLinus Torvalds id = spos = card->shortname; 4751da177e4SLinus Torvalds while (*id != '\0') { 4761da177e4SLinus Torvalds if (*id == ' ') 4771da177e4SLinus Torvalds spos = id + 1; 4781da177e4SLinus Torvalds id++; 4791da177e4SLinus Torvalds } 4801da177e4SLinus Torvalds id = card->id; 4811da177e4SLinus Torvalds while (*spos != '\0' && !isalnum(*spos)) 4821da177e4SLinus Torvalds spos++; 4831da177e4SLinus Torvalds if (isdigit(*spos)) 4841da177e4SLinus Torvalds *id++ = isalpha(card->shortname[0]) ? card->shortname[0] : 'D'; 4851da177e4SLinus Torvalds while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) { 4861da177e4SLinus Torvalds if (isalnum(*spos)) 4871da177e4SLinus Torvalds *id++ = *spos; 4881da177e4SLinus Torvalds spos++; 4891da177e4SLinus Torvalds } 4901da177e4SLinus Torvalds *id = '\0'; 4911da177e4SLinus Torvalds 4921da177e4SLinus Torvalds id = card->id; 4931da177e4SLinus Torvalds 4941da177e4SLinus Torvalds if (*id == '\0') 4951da177e4SLinus Torvalds strcpy(id, "default"); 4961da177e4SLinus Torvalds 4971da177e4SLinus Torvalds while (1) { 4981da177e4SLinus Torvalds if (loops-- == 0) { 4991da177e4SLinus Torvalds snd_printk(KERN_ERR "unable to choose default card id (%s)\n", id); 5001da177e4SLinus Torvalds strcpy(card->id, card->proc_root->name); 5011da177e4SLinus Torvalds return; 5021da177e4SLinus Torvalds } 5031da177e4SLinus Torvalds if (!snd_info_check_reserved_words(id)) 5041da177e4SLinus Torvalds goto __change; 5051da177e4SLinus Torvalds for (i = 0; i < snd_ecards_limit; i++) { 5061da177e4SLinus Torvalds if (snd_cards[i] && !strcmp(snd_cards[i]->id, id)) 5071da177e4SLinus Torvalds goto __change; 5081da177e4SLinus Torvalds } 5091da177e4SLinus Torvalds break; 5101da177e4SLinus Torvalds 5111da177e4SLinus Torvalds __change: 5121da177e4SLinus Torvalds len = strlen(id); 513d001544dSClemens Ladisch if (idx_flag) { 514d001544dSClemens Ladisch if (id[len-1] != '9') 5151da177e4SLinus Torvalds id[len-1]++; 516d001544dSClemens Ladisch else 517d001544dSClemens Ladisch id[len-1] = 'A'; 518d001544dSClemens Ladisch } else if ((size_t)len <= sizeof(card->id) - 3) { 5191da177e4SLinus Torvalds strcat(id, "_1"); 5201da177e4SLinus Torvalds idx_flag++; 5211da177e4SLinus Torvalds } else { 5221da177e4SLinus Torvalds spos = id + len - 2; 5231da177e4SLinus Torvalds if ((size_t)len <= sizeof(card->id) - 2) 5241da177e4SLinus Torvalds spos++; 5251da177e4SLinus Torvalds *spos++ = '_'; 5261da177e4SLinus Torvalds *spos++ = '1'; 5271da177e4SLinus Torvalds *spos++ = '\0'; 5281da177e4SLinus Torvalds idx_flag++; 5291da177e4SLinus Torvalds } 5301da177e4SLinus Torvalds } 5311da177e4SLinus Torvalds } 5321da177e4SLinus Torvalds 5331da177e4SLinus Torvalds /** 5341da177e4SLinus Torvalds * snd_card_register - register the soundcard 5351da177e4SLinus Torvalds * @card: soundcard structure 5361da177e4SLinus Torvalds * 5371da177e4SLinus Torvalds * This function registers all the devices assigned to the soundcard. 5381da177e4SLinus Torvalds * Until calling this, the ALSA control interface is blocked from the 5391da177e4SLinus Torvalds * external accesses. Thus, you should call this function at the end 5401da177e4SLinus Torvalds * of the initialization of the card. 5411da177e4SLinus Torvalds * 5421da177e4SLinus Torvalds * Returns zero otherwise a negative error code if the registrain failed. 5431da177e4SLinus Torvalds */ 544512bbd6aSTakashi Iwai int snd_card_register(struct snd_card *card) 5451da177e4SLinus Torvalds { 5461da177e4SLinus Torvalds int err; 5471da177e4SLinus Torvalds 5487eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 5497eaa943cSTakashi Iwai return -EINVAL; 5507d2aae1eSTakashi Iwai #ifndef CONFIG_SYSFS_DEPRECATED 5517d2aae1eSTakashi Iwai if (!card->card_dev) { 552abe9ab8fSGreg Kroah-Hartman card->card_dev = device_create(sound_class, card->dev, 55389409211SGreg Kroah-Hartman MKDEV(0, 0), NULL, 554d80f19faSGreg Kroah-Hartman "card%i", card->number); 5557d2aae1eSTakashi Iwai if (IS_ERR(card->card_dev)) 5567d2aae1eSTakashi Iwai card->card_dev = NULL; 557d80f19faSGreg Kroah-Hartman } 5587d2aae1eSTakashi Iwai #endif 5591da177e4SLinus Torvalds if ((err = snd_device_register_all(card)) < 0) 5601da177e4SLinus Torvalds return err; 561746df948STakashi Iwai mutex_lock(&snd_card_mutex); 5621da177e4SLinus Torvalds if (snd_cards[card->number]) { 5631da177e4SLinus Torvalds /* already registered */ 564746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 5651da177e4SLinus Torvalds return 0; 5661da177e4SLinus Torvalds } 5671da177e4SLinus Torvalds if (card->id[0] == '\0') 5681da177e4SLinus Torvalds choose_default_id(card); 5691da177e4SLinus Torvalds snd_cards[card->number] = card; 570746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 571e28563ccSTakashi Iwai init_info_for_card(card); 5721da177e4SLinus Torvalds #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) 5731da177e4SLinus Torvalds if (snd_mixer_oss_notify_callback) 5741da177e4SLinus Torvalds snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER); 5751da177e4SLinus Torvalds #endif 5761da177e4SLinus Torvalds return 0; 5771da177e4SLinus Torvalds } 5781da177e4SLinus Torvalds 579c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_register); 580c0d3fb39STakashi Iwai 581e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS 5826581f4e7STakashi Iwai static struct snd_info_entry *snd_card_info_entry; 5831da177e4SLinus Torvalds 584a381a7a6STakashi Iwai static void snd_card_info_read(struct snd_info_entry *entry, 585a381a7a6STakashi Iwai struct snd_info_buffer *buffer) 5861da177e4SLinus Torvalds { 5871da177e4SLinus Torvalds int idx, count; 588512bbd6aSTakashi Iwai struct snd_card *card; 5891da177e4SLinus Torvalds 5901da177e4SLinus Torvalds for (idx = count = 0; idx < SNDRV_CARDS; idx++) { 591746df948STakashi Iwai mutex_lock(&snd_card_mutex); 5921da177e4SLinus Torvalds if ((card = snd_cards[idx]) != NULL) { 5931da177e4SLinus Torvalds count++; 594d001544dSClemens Ladisch snd_iprintf(buffer, "%2i [%-15s]: %s - %s\n", 5951da177e4SLinus Torvalds idx, 5961da177e4SLinus Torvalds card->id, 5971da177e4SLinus Torvalds card->driver, 5981da177e4SLinus Torvalds card->shortname); 5991da177e4SLinus Torvalds snd_iprintf(buffer, " %s\n", 6001da177e4SLinus Torvalds card->longname); 6011da177e4SLinus Torvalds } 602746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 6031da177e4SLinus Torvalds } 6041da177e4SLinus Torvalds if (!count) 6051da177e4SLinus Torvalds snd_iprintf(buffer, "--- no soundcards ---\n"); 6061da177e4SLinus Torvalds } 6071da177e4SLinus Torvalds 608e28563ccSTakashi Iwai #ifdef CONFIG_SND_OSSEMUL 6091da177e4SLinus Torvalds 610512bbd6aSTakashi Iwai void snd_card_info_read_oss(struct snd_info_buffer *buffer) 6111da177e4SLinus Torvalds { 6121da177e4SLinus Torvalds int idx, count; 613512bbd6aSTakashi Iwai struct snd_card *card; 6141da177e4SLinus Torvalds 6151da177e4SLinus Torvalds for (idx = count = 0; idx < SNDRV_CARDS; idx++) { 616746df948STakashi Iwai mutex_lock(&snd_card_mutex); 6171da177e4SLinus Torvalds if ((card = snd_cards[idx]) != NULL) { 6181da177e4SLinus Torvalds count++; 6191da177e4SLinus Torvalds snd_iprintf(buffer, "%s\n", card->longname); 6201da177e4SLinus Torvalds } 621746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 6221da177e4SLinus Torvalds } 6231da177e4SLinus Torvalds if (!count) { 6241da177e4SLinus Torvalds snd_iprintf(buffer, "--- no soundcards ---\n"); 6251da177e4SLinus Torvalds } 6261da177e4SLinus Torvalds } 6271da177e4SLinus Torvalds 6281da177e4SLinus Torvalds #endif 6291da177e4SLinus Torvalds 6301da177e4SLinus Torvalds #ifdef MODULE 631512bbd6aSTakashi Iwai static struct snd_info_entry *snd_card_module_info_entry; 632512bbd6aSTakashi Iwai static void snd_card_module_info_read(struct snd_info_entry *entry, 633512bbd6aSTakashi Iwai struct snd_info_buffer *buffer) 6341da177e4SLinus Torvalds { 6351da177e4SLinus Torvalds int idx; 636512bbd6aSTakashi Iwai struct snd_card *card; 6371da177e4SLinus Torvalds 6381da177e4SLinus Torvalds for (idx = 0; idx < SNDRV_CARDS; idx++) { 639746df948STakashi Iwai mutex_lock(&snd_card_mutex); 6401da177e4SLinus Torvalds if ((card = snd_cards[idx]) != NULL) 641d001544dSClemens Ladisch snd_iprintf(buffer, "%2i %s\n", 642d001544dSClemens Ladisch idx, card->module->name); 643746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 6441da177e4SLinus Torvalds } 6451da177e4SLinus Torvalds } 6461da177e4SLinus Torvalds #endif 6471da177e4SLinus Torvalds 6481da177e4SLinus Torvalds int __init snd_card_info_init(void) 6491da177e4SLinus Torvalds { 650512bbd6aSTakashi Iwai struct snd_info_entry *entry; 6511da177e4SLinus Torvalds 6521da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL); 6537c22f1aaSTakashi Iwai if (! entry) 6547c22f1aaSTakashi Iwai return -ENOMEM; 6551da177e4SLinus Torvalds entry->c.text.read = snd_card_info_read; 6561da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 6571da177e4SLinus Torvalds snd_info_free_entry(entry); 6581da177e4SLinus Torvalds return -ENOMEM; 6591da177e4SLinus Torvalds } 6601da177e4SLinus Torvalds snd_card_info_entry = entry; 6611da177e4SLinus Torvalds 6621da177e4SLinus Torvalds #ifdef MODULE 6631da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL); 6641da177e4SLinus Torvalds if (entry) { 6651da177e4SLinus Torvalds entry->c.text.read = snd_card_module_info_read; 6661da177e4SLinus Torvalds if (snd_info_register(entry) < 0) 6671da177e4SLinus Torvalds snd_info_free_entry(entry); 6681da177e4SLinus Torvalds else 6691da177e4SLinus Torvalds snd_card_module_info_entry = entry; 6701da177e4SLinus Torvalds } 6711da177e4SLinus Torvalds #endif 6721da177e4SLinus Torvalds 6731da177e4SLinus Torvalds return 0; 6741da177e4SLinus Torvalds } 6751da177e4SLinus Torvalds 6761da177e4SLinus Torvalds int __exit snd_card_info_done(void) 6771da177e4SLinus Torvalds { 678746d4a02STakashi Iwai snd_info_free_entry(snd_card_info_entry); 6791da177e4SLinus Torvalds #ifdef MODULE 680746d4a02STakashi Iwai snd_info_free_entry(snd_card_module_info_entry); 6811da177e4SLinus Torvalds #endif 6821da177e4SLinus Torvalds return 0; 6831da177e4SLinus Torvalds } 6841da177e4SLinus Torvalds 685e28563ccSTakashi Iwai #endif /* CONFIG_PROC_FS */ 686e28563ccSTakashi Iwai 6871da177e4SLinus Torvalds /** 6881da177e4SLinus Torvalds * snd_component_add - add a component string 6891da177e4SLinus Torvalds * @card: soundcard structure 6901da177e4SLinus Torvalds * @component: the component id string 6911da177e4SLinus Torvalds * 6921da177e4SLinus Torvalds * This function adds the component id string to the supported list. 6931da177e4SLinus Torvalds * The component can be referred from the alsa-lib. 6941da177e4SLinus Torvalds * 6951da177e4SLinus Torvalds * Returns zero otherwise a negative error code. 6961da177e4SLinus Torvalds */ 6971da177e4SLinus Torvalds 698512bbd6aSTakashi Iwai int snd_component_add(struct snd_card *card, const char *component) 6991da177e4SLinus Torvalds { 7001da177e4SLinus Torvalds char *ptr; 7011da177e4SLinus Torvalds int len = strlen(component); 7021da177e4SLinus Torvalds 7031da177e4SLinus Torvalds ptr = strstr(card->components, component); 7041da177e4SLinus Torvalds if (ptr != NULL) { 7051da177e4SLinus Torvalds if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */ 7061da177e4SLinus Torvalds return 1; 7071da177e4SLinus Torvalds } 7081da177e4SLinus Torvalds if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) { 7091da177e4SLinus Torvalds snd_BUG(); 7101da177e4SLinus Torvalds return -ENOMEM; 7111da177e4SLinus Torvalds } 7121da177e4SLinus Torvalds if (card->components[0] != '\0') 7131da177e4SLinus Torvalds strcat(card->components, " "); 7141da177e4SLinus Torvalds strcat(card->components, component); 7151da177e4SLinus Torvalds return 0; 7161da177e4SLinus Torvalds } 7171da177e4SLinus Torvalds 718c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_component_add); 719c0d3fb39STakashi Iwai 7201da177e4SLinus Torvalds /** 7211da177e4SLinus Torvalds * snd_card_file_add - add the file to the file list of the card 7221da177e4SLinus Torvalds * @card: soundcard structure 7231da177e4SLinus Torvalds * @file: file pointer 7241da177e4SLinus Torvalds * 7251da177e4SLinus Torvalds * This function adds the file to the file linked-list of the card. 7261da177e4SLinus Torvalds * This linked-list is used to keep tracking the connection state, 7271da177e4SLinus Torvalds * and to avoid the release of busy resources by hotplug. 7281da177e4SLinus Torvalds * 7291da177e4SLinus Torvalds * Returns zero or a negative error code. 7301da177e4SLinus Torvalds */ 731512bbd6aSTakashi Iwai int snd_card_file_add(struct snd_card *card, struct file *file) 7321da177e4SLinus Torvalds { 7331da177e4SLinus Torvalds struct snd_monitor_file *mfile; 7341da177e4SLinus Torvalds 7351da177e4SLinus Torvalds mfile = kmalloc(sizeof(*mfile), GFP_KERNEL); 7361da177e4SLinus Torvalds if (mfile == NULL) 7371da177e4SLinus Torvalds return -ENOMEM; 7381da177e4SLinus Torvalds mfile->file = file; 739a9edfc60SKarsten Wiese mfile->disconnected_f_op = NULL; 7401da177e4SLinus Torvalds mfile->next = NULL; 7411da177e4SLinus Torvalds spin_lock(&card->files_lock); 7421da177e4SLinus Torvalds if (card->shutdown) { 7431da177e4SLinus Torvalds spin_unlock(&card->files_lock); 7441da177e4SLinus Torvalds kfree(mfile); 7451da177e4SLinus Torvalds return -ENODEV; 7461da177e4SLinus Torvalds } 7471da177e4SLinus Torvalds mfile->next = card->files; 7481da177e4SLinus Torvalds card->files = mfile; 7491da177e4SLinus Torvalds spin_unlock(&card->files_lock); 7501da177e4SLinus Torvalds return 0; 7511da177e4SLinus Torvalds } 7521da177e4SLinus Torvalds 753c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_file_add); 754c0d3fb39STakashi Iwai 7551da177e4SLinus Torvalds /** 7561da177e4SLinus Torvalds * snd_card_file_remove - remove the file from the file list 7571da177e4SLinus Torvalds * @card: soundcard structure 7581da177e4SLinus Torvalds * @file: file pointer 7591da177e4SLinus Torvalds * 7601da177e4SLinus Torvalds * This function removes the file formerly added to the card via 7611da177e4SLinus Torvalds * snd_card_file_add() function. 7622b29b13cSTakashi Iwai * If all files are removed and snd_card_free_when_closed() was 7632b29b13cSTakashi Iwai * called beforehand, it processes the pending release of 7642b29b13cSTakashi Iwai * resources. 7651da177e4SLinus Torvalds * 7661da177e4SLinus Torvalds * Returns zero or a negative error code. 7671da177e4SLinus Torvalds */ 768512bbd6aSTakashi Iwai int snd_card_file_remove(struct snd_card *card, struct file *file) 7691da177e4SLinus Torvalds { 7701da177e4SLinus Torvalds struct snd_monitor_file *mfile, *pfile = NULL; 771c461482cSTakashi Iwai int last_close = 0; 7721da177e4SLinus Torvalds 7731da177e4SLinus Torvalds spin_lock(&card->files_lock); 7741da177e4SLinus Torvalds mfile = card->files; 7751da177e4SLinus Torvalds while (mfile) { 7761da177e4SLinus Torvalds if (mfile->file == file) { 7771da177e4SLinus Torvalds if (pfile) 7781da177e4SLinus Torvalds pfile->next = mfile->next; 7791da177e4SLinus Torvalds else 7801da177e4SLinus Torvalds card->files = mfile->next; 7811da177e4SLinus Torvalds break; 7821da177e4SLinus Torvalds } 7831da177e4SLinus Torvalds pfile = mfile; 7841da177e4SLinus Torvalds mfile = mfile->next; 7851da177e4SLinus Torvalds } 786a9edfc60SKarsten Wiese if (mfile && mfile->disconnected_f_op) { 787a9edfc60SKarsten Wiese fops_put(mfile->disconnected_f_op); 788a9edfc60SKarsten Wiese spin_lock(&shutdown_lock); 789a9edfc60SKarsten Wiese list_del(&mfile->shutdown_list); 790a9edfc60SKarsten Wiese spin_unlock(&shutdown_lock); 791a9edfc60SKarsten Wiese } 7921da177e4SLinus Torvalds if (card->files == NULL) 793c461482cSTakashi Iwai last_close = 1; 794c461482cSTakashi Iwai spin_unlock(&card->files_lock); 795c461482cSTakashi Iwai if (last_close) { 7961da177e4SLinus Torvalds wake_up(&card->shutdown_sleep); 797c461482cSTakashi Iwai if (card->free_on_last_close) 798c461482cSTakashi Iwai snd_card_do_free(card); 799c461482cSTakashi Iwai } 8001da177e4SLinus Torvalds if (!mfile) { 8011da177e4SLinus Torvalds snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file); 8021da177e4SLinus Torvalds return -ENOENT; 8031da177e4SLinus Torvalds } 8041da177e4SLinus Torvalds kfree(mfile); 8051da177e4SLinus Torvalds return 0; 8061da177e4SLinus Torvalds } 8071da177e4SLinus Torvalds 808c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_file_remove); 809c0d3fb39STakashi Iwai 8101da177e4SLinus Torvalds #ifdef CONFIG_PM 8111da177e4SLinus Torvalds /** 8121da177e4SLinus Torvalds * snd_power_wait - wait until the power-state is changed. 8131da177e4SLinus Torvalds * @card: soundcard structure 8141da177e4SLinus Torvalds * @power_state: expected power state 8151da177e4SLinus Torvalds * 8161da177e4SLinus Torvalds * Waits until the power-state is changed. 8171da177e4SLinus Torvalds * 8181da177e4SLinus Torvalds * Note: the power lock must be active before call. 8191da177e4SLinus Torvalds */ 820cbac4b0cSTakashi Iwai int snd_power_wait(struct snd_card *card, unsigned int power_state) 8211da177e4SLinus Torvalds { 8221da177e4SLinus Torvalds wait_queue_t wait; 8231da177e4SLinus Torvalds int result = 0; 8241da177e4SLinus Torvalds 8251da177e4SLinus Torvalds /* fastpath */ 8261da177e4SLinus Torvalds if (snd_power_get_state(card) == power_state) 8271da177e4SLinus Torvalds return 0; 8281da177e4SLinus Torvalds init_waitqueue_entry(&wait, current); 8291da177e4SLinus Torvalds add_wait_queue(&card->power_sleep, &wait); 8301da177e4SLinus Torvalds while (1) { 8311da177e4SLinus Torvalds if (card->shutdown) { 8321da177e4SLinus Torvalds result = -ENODEV; 8331da177e4SLinus Torvalds break; 8341da177e4SLinus Torvalds } 8351da177e4SLinus Torvalds if (snd_power_get_state(card) == power_state) 8361da177e4SLinus Torvalds break; 8371da177e4SLinus Torvalds set_current_state(TASK_UNINTERRUPTIBLE); 8381da177e4SLinus Torvalds snd_power_unlock(card); 8391da177e4SLinus Torvalds schedule_timeout(30 * HZ); 8401da177e4SLinus Torvalds snd_power_lock(card); 8411da177e4SLinus Torvalds } 8421da177e4SLinus Torvalds remove_wait_queue(&card->power_sleep, &wait); 8431da177e4SLinus Torvalds return result; 8441da177e4SLinus Torvalds } 8451da177e4SLinus Torvalds 846c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_power_wait); 8471da177e4SLinus Torvalds #endif /* CONFIG_PM */ 848