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 1608bfb181cSTakashi Iwai static int snd_card_do_free(struct snd_card *card); 16134356dbdSTakashi Iwai static const struct attribute_group *card_dev_attr_groups[]; 1628bfb181cSTakashi Iwai 1638bfb181cSTakashi Iwai static void release_card_device(struct device *dev) 1648bfb181cSTakashi Iwai { 1658bfb181cSTakashi Iwai snd_card_do_free(dev_to_snd_card(dev)); 1668bfb181cSTakashi Iwai } 1678bfb181cSTakashi Iwai 1681da177e4SLinus Torvalds /** 169393aa9c1STakashi Iwai * snd_card_new - create and initialize a soundcard structure 170393aa9c1STakashi Iwai * @parent: the parent device object 1711da177e4SLinus Torvalds * @idx: card index (address) [0 ... (SNDRV_CARDS-1)] 1721da177e4SLinus Torvalds * @xid: card identification (ASCII string) 1731da177e4SLinus Torvalds * @module: top level module for locking 1741da177e4SLinus Torvalds * @extra_size: allocate this extra size after the main soundcard structure 17553fb1e63STakashi Iwai * @card_ret: the pointer to store the created card instance 1761da177e4SLinus Torvalds * 1771da177e4SLinus Torvalds * Creates and initializes a soundcard structure. 1781da177e4SLinus Torvalds * 17953fb1e63STakashi Iwai * The function allocates snd_card instance via kzalloc with the given 18053fb1e63STakashi Iwai * space for the driver to use freely. The allocated struct is stored 18153fb1e63STakashi Iwai * in the given card_ret pointer. 18253fb1e63STakashi Iwai * 183eb7c06e8SYacine Belkadi * Return: Zero if successful or a negative error code. 1841da177e4SLinus Torvalds */ 185393aa9c1STakashi Iwai int snd_card_new(struct device *parent, int idx, const char *xid, 18653fb1e63STakashi Iwai struct module *module, int extra_size, 18753fb1e63STakashi Iwai struct snd_card **card_ret) 1881da177e4SLinus Torvalds { 189512bbd6aSTakashi Iwai struct snd_card *card; 190deb6596fSTakashi Iwai int err; 1911da177e4SLinus Torvalds 19253fb1e63STakashi Iwai if (snd_BUG_ON(!card_ret)) 19353fb1e63STakashi Iwai return -EINVAL; 19453fb1e63STakashi Iwai *card_ret = NULL; 19553fb1e63STakashi Iwai 1961da177e4SLinus Torvalds if (extra_size < 0) 1971da177e4SLinus Torvalds extra_size = 0; 198ca2c0966STakashi Iwai card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL); 19953fb1e63STakashi Iwai if (!card) 20053fb1e63STakashi Iwai return -ENOMEM; 2018bfb181cSTakashi Iwai if (extra_size > 0) 2028bfb181cSTakashi Iwai card->private_data = (char *)card + sizeof(struct snd_card); 20310a8ebbbSJaroslav Kysela if (xid) 2045fdc18d9SJaroslav Kysela strlcpy(card->id, xid, sizeof(card->id)); 2051da177e4SLinus Torvalds err = 0; 206746df948STakashi Iwai mutex_lock(&snd_card_mutex); 207deb6596fSTakashi Iwai if (idx < 0) /* first check the matching module-name slot */ 208deb6596fSTakashi Iwai idx = get_slot_from_bitmask(idx, module_slot_match, module); 209deb6596fSTakashi Iwai if (idx < 0) /* if not matched, assign an empty slot */ 210deb6596fSTakashi Iwai idx = get_slot_from_bitmask(idx, check_empty_slot, module); 211a93bbaa7STakashi Iwai if (idx < 0) 212a93bbaa7STakashi Iwai err = -ENODEV; 213a93bbaa7STakashi Iwai else if (idx < snd_ecards_limit) { 2147bb2491bSTakashi Iwai if (test_bit(idx, snd_cards_lock)) 2155c33dd70SOliver Neukum err = -EBUSY; /* invalid */ 216a93bbaa7STakashi Iwai } else if (idx >= SNDRV_CARDS) 2171da177e4SLinus Torvalds err = -ENODEV; 218a93bbaa7STakashi Iwai if (err < 0) { 219746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 220f2f9307aSTakashi Iwai dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n", 2215c33dd70SOliver Neukum idx, snd_ecards_limit - 1, err); 2228bfb181cSTakashi Iwai kfree(card); 2238bfb181cSTakashi Iwai return err; 2241da177e4SLinus Torvalds } 2257bb2491bSTakashi Iwai set_bit(idx, snd_cards_lock); /* lock it */ 226a93bbaa7STakashi Iwai if (idx >= snd_ecards_limit) 227a93bbaa7STakashi Iwai snd_ecards_limit = idx + 1; /* increase the limit */ 228746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 229393aa9c1STakashi Iwai card->dev = parent; 2301da177e4SLinus Torvalds card->number = idx; 2311da177e4SLinus Torvalds card->module = module; 2321da177e4SLinus Torvalds INIT_LIST_HEAD(&card->devices); 2331da177e4SLinus Torvalds init_rwsem(&card->controls_rwsem); 2341da177e4SLinus Torvalds rwlock_init(&card->ctl_files_rwlock); 2351da177e4SLinus Torvalds INIT_LIST_HEAD(&card->controls); 2361da177e4SLinus Torvalds INIT_LIST_HEAD(&card->ctl_files); 2371da177e4SLinus Torvalds spin_lock_init(&card->files_lock); 238118dd6bfSTakashi Iwai INIT_LIST_HEAD(&card->files_list); 2391da177e4SLinus Torvalds #ifdef CONFIG_PM 2401a60d4c5SIngo Molnar mutex_init(&card->power_lock); 2411da177e4SLinus Torvalds init_waitqueue_head(&card->power_sleep); 2421da177e4SLinus Torvalds #endif 2438bfb181cSTakashi Iwai 2448bfb181cSTakashi Iwai device_initialize(&card->card_dev); 2458bfb181cSTakashi Iwai card->card_dev.parent = parent; 2468bfb181cSTakashi Iwai card->card_dev.class = sound_class; 2478bfb181cSTakashi Iwai card->card_dev.release = release_card_device; 24834356dbdSTakashi Iwai card->card_dev.groups = card_dev_attr_groups; 2498bfb181cSTakashi Iwai err = kobject_set_name(&card->card_dev.kobj, "card%d", idx); 2508bfb181cSTakashi Iwai if (err < 0) 2518bfb181cSTakashi Iwai goto __error; 2528bfb181cSTakashi Iwai 2531da177e4SLinus Torvalds /* the control interface cannot be accessed from the user space until */ 2541da177e4SLinus Torvalds /* snd_cards_bitmask and snd_cards are set with snd_card_register */ 25553fb1e63STakashi Iwai err = snd_ctl_create(card); 25653fb1e63STakashi Iwai if (err < 0) { 257f2f9307aSTakashi Iwai dev_err(parent, "unable to register control minors\n"); 2581da177e4SLinus Torvalds goto __error; 2591da177e4SLinus Torvalds } 26053fb1e63STakashi Iwai err = snd_info_card_create(card); 26153fb1e63STakashi Iwai if (err < 0) { 262f2f9307aSTakashi Iwai dev_err(parent, "unable to create card info\n"); 2631da177e4SLinus Torvalds goto __error_ctl; 2641da177e4SLinus Torvalds } 26553fb1e63STakashi Iwai *card_ret = card; 26653fb1e63STakashi Iwai return 0; 2671da177e4SLinus Torvalds 2681da177e4SLinus Torvalds __error_ctl: 2691da177e4SLinus Torvalds snd_device_free_all(card, SNDRV_DEV_CMD_PRE); 2701da177e4SLinus Torvalds __error: 2718bfb181cSTakashi Iwai put_device(&card->card_dev); 27253fb1e63STakashi Iwai return err; 2731da177e4SLinus Torvalds } 274393aa9c1STakashi Iwai EXPORT_SYMBOL(snd_card_new); 275c0d3fb39STakashi Iwai 276746df948STakashi Iwai /* return non-zero if a card is already locked */ 277746df948STakashi Iwai int snd_card_locked(int card) 278746df948STakashi Iwai { 279746df948STakashi Iwai int locked; 280746df948STakashi Iwai 281746df948STakashi Iwai mutex_lock(&snd_card_mutex); 2827bb2491bSTakashi Iwai locked = test_bit(card, snd_cards_lock); 283746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 284746df948STakashi Iwai return locked; 285746df948STakashi Iwai } 286746df948STakashi Iwai 287b3b0abe1SClemens Ladisch static loff_t snd_disconnect_llseek(struct file *file, loff_t offset, int orig) 288b3b0abe1SClemens Ladisch { 289b3b0abe1SClemens Ladisch return -ENODEV; 290b3b0abe1SClemens Ladisch } 291b3b0abe1SClemens Ladisch 292b3b0abe1SClemens Ladisch static ssize_t snd_disconnect_read(struct file *file, char __user *buf, 293b3b0abe1SClemens Ladisch size_t count, loff_t *offset) 294b3b0abe1SClemens Ladisch { 295b3b0abe1SClemens Ladisch return -ENODEV; 296b3b0abe1SClemens Ladisch } 297b3b0abe1SClemens Ladisch 298b3b0abe1SClemens Ladisch static ssize_t snd_disconnect_write(struct file *file, const char __user *buf, 299b3b0abe1SClemens Ladisch size_t count, loff_t *offset) 300b3b0abe1SClemens Ladisch { 301b3b0abe1SClemens Ladisch return -ENODEV; 302b3b0abe1SClemens Ladisch } 303b3b0abe1SClemens Ladisch 304a9edfc60SKarsten Wiese static int snd_disconnect_release(struct inode *inode, struct file *file) 305a9edfc60SKarsten Wiese { 306a9edfc60SKarsten Wiese struct snd_monitor_file *df = NULL, *_df; 307a9edfc60SKarsten Wiese 308a9edfc60SKarsten Wiese spin_lock(&shutdown_lock); 309a9edfc60SKarsten Wiese list_for_each_entry(_df, &shutdown_files, shutdown_list) { 310a9edfc60SKarsten Wiese if (_df->file == file) { 311a9edfc60SKarsten Wiese df = _df; 312118dd6bfSTakashi Iwai list_del_init(&df->shutdown_list); 313a9edfc60SKarsten Wiese break; 314a9edfc60SKarsten Wiese } 315a9edfc60SKarsten Wiese } 316a9edfc60SKarsten Wiese spin_unlock(&shutdown_lock); 317a9edfc60SKarsten Wiese 318233e70f4SAl Viro if (likely(df)) { 319233e70f4SAl Viro if ((file->f_flags & FASYNC) && df->disconnected_f_op->fasync) 320233e70f4SAl Viro df->disconnected_f_op->fasync(-1, file, 0); 321a9edfc60SKarsten Wiese return df->disconnected_f_op->release(inode, file); 322233e70f4SAl Viro } 323a9edfc60SKarsten Wiese 3249bf8e7ddSHarvey Harrison panic("%s(%p, %p) failed!", __func__, inode, file); 325a9edfc60SKarsten Wiese } 326a9edfc60SKarsten Wiese 3271da177e4SLinus Torvalds static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait) 3281da177e4SLinus Torvalds { 3291da177e4SLinus Torvalds return POLLERR | POLLNVAL; 3301da177e4SLinus Torvalds } 3311da177e4SLinus Torvalds 332b3b0abe1SClemens Ladisch static long snd_disconnect_ioctl(struct file *file, 333b3b0abe1SClemens Ladisch unsigned int cmd, unsigned long arg) 334b3b0abe1SClemens Ladisch { 335b3b0abe1SClemens Ladisch return -ENODEV; 336b3b0abe1SClemens Ladisch } 337b3b0abe1SClemens Ladisch 338b3b0abe1SClemens Ladisch static int snd_disconnect_mmap(struct file *file, struct vm_area_struct *vma) 339b3b0abe1SClemens Ladisch { 340b3b0abe1SClemens Ladisch return -ENODEV; 341b3b0abe1SClemens Ladisch } 342b3b0abe1SClemens Ladisch 343b3b0abe1SClemens Ladisch static int snd_disconnect_fasync(int fd, struct file *file, int on) 344b3b0abe1SClemens Ladisch { 345b3b0abe1SClemens Ladisch return -ENODEV; 346b3b0abe1SClemens Ladisch } 347b3b0abe1SClemens Ladisch 3489c2e08c5SArjan van de Ven static const struct file_operations snd_shutdown_f_ops = 349a9edfc60SKarsten Wiese { 350a9edfc60SKarsten Wiese .owner = THIS_MODULE, 351a9edfc60SKarsten Wiese .llseek = snd_disconnect_llseek, 352a9edfc60SKarsten Wiese .read = snd_disconnect_read, 353a9edfc60SKarsten Wiese .write = snd_disconnect_write, 354a9edfc60SKarsten Wiese .release = snd_disconnect_release, 355a9edfc60SKarsten Wiese .poll = snd_disconnect_poll, 356a9edfc60SKarsten Wiese .unlocked_ioctl = snd_disconnect_ioctl, 357a9edfc60SKarsten Wiese #ifdef CONFIG_COMPAT 358a9edfc60SKarsten Wiese .compat_ioctl = snd_disconnect_ioctl, 359a9edfc60SKarsten Wiese #endif 360a9edfc60SKarsten Wiese .mmap = snd_disconnect_mmap, 361a9edfc60SKarsten Wiese .fasync = snd_disconnect_fasync 362a9edfc60SKarsten Wiese }; 363a9edfc60SKarsten Wiese 3641da177e4SLinus Torvalds /** 3651da177e4SLinus Torvalds * snd_card_disconnect - disconnect all APIs from the file-operations (user space) 3661da177e4SLinus Torvalds * @card: soundcard structure 3671da177e4SLinus Torvalds * 3681da177e4SLinus Torvalds * Disconnects all APIs from the file-operations (user space). 3691da177e4SLinus Torvalds * 370eb7c06e8SYacine Belkadi * Return: Zero, otherwise a negative error code. 3711da177e4SLinus Torvalds * 3721da177e4SLinus Torvalds * Note: The current implementation replaces all active file->f_op with special 3731da177e4SLinus Torvalds * dummy file operations (they do nothing except release). 3741da177e4SLinus Torvalds */ 375512bbd6aSTakashi Iwai int snd_card_disconnect(struct snd_card *card) 3761da177e4SLinus Torvalds { 3771da177e4SLinus Torvalds struct snd_monitor_file *mfile; 3781da177e4SLinus Torvalds int err; 3791da177e4SLinus Torvalds 380f18638dcSTakashi Iwai if (!card) 381f18638dcSTakashi Iwai return -EINVAL; 382f18638dcSTakashi Iwai 3831da177e4SLinus Torvalds spin_lock(&card->files_lock); 3841da177e4SLinus Torvalds if (card->shutdown) { 3851da177e4SLinus Torvalds spin_unlock(&card->files_lock); 3861da177e4SLinus Torvalds return 0; 3871da177e4SLinus Torvalds } 3881da177e4SLinus Torvalds card->shutdown = 1; 3891da177e4SLinus Torvalds spin_unlock(&card->files_lock); 3901da177e4SLinus Torvalds 3911da177e4SLinus Torvalds /* phase 1: disable fops (user space) operations for ALSA API */ 392746df948STakashi Iwai mutex_lock(&snd_card_mutex); 3931da177e4SLinus Torvalds snd_cards[card->number] = NULL; 3947bb2491bSTakashi Iwai clear_bit(card->number, snd_cards_lock); 395746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 3961da177e4SLinus Torvalds 3971da177e4SLinus Torvalds /* phase 2: replace file->f_op with special dummy operations */ 3981da177e4SLinus Torvalds 3991da177e4SLinus Torvalds spin_lock(&card->files_lock); 400118dd6bfSTakashi Iwai list_for_each_entry(mfile, &card->files_list, list) { 4011da177e4SLinus Torvalds /* it's critical part, use endless loop */ 4021da177e4SLinus Torvalds /* we have no room to fail */ 403a9edfc60SKarsten Wiese mfile->disconnected_f_op = mfile->file->f_op; 4041da177e4SLinus Torvalds 405a9edfc60SKarsten Wiese spin_lock(&shutdown_lock); 406a9edfc60SKarsten Wiese list_add(&mfile->shutdown_list, &shutdown_files); 407a9edfc60SKarsten Wiese spin_unlock(&shutdown_lock); 4081da177e4SLinus Torvalds 409a9edfc60SKarsten Wiese mfile->file->f_op = &snd_shutdown_f_ops; 410bc9abce0SMiguel Boton fops_get(mfile->file->f_op); 4111da177e4SLinus Torvalds } 4121da177e4SLinus Torvalds spin_unlock(&card->files_lock); 4131da177e4SLinus Torvalds 4141da177e4SLinus Torvalds /* phase 3: notify all connected devices about disconnection */ 4151da177e4SLinus Torvalds /* at this point, they cannot respond to any calls except release() */ 4161da177e4SLinus Torvalds 4178eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_MIXER_OSS) 4181da177e4SLinus Torvalds if (snd_mixer_oss_notify_callback) 4191da177e4SLinus Torvalds snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT); 4201da177e4SLinus Torvalds #endif 4211da177e4SLinus Torvalds 4221da177e4SLinus Torvalds /* notify all devices that we are disconnected */ 4231da177e4SLinus Torvalds err = snd_device_disconnect_all(card); 4241da177e4SLinus Torvalds if (err < 0) 425f2f9307aSTakashi Iwai dev_err(card->dev, "not all devices for card %i can be disconnected\n", card->number); 4261da177e4SLinus Torvalds 427746d4a02STakashi Iwai snd_info_card_disconnect(card); 4288bfb181cSTakashi Iwai if (card->registered) { 4298bfb181cSTakashi Iwai device_del(&card->card_dev); 4308bfb181cSTakashi Iwai card->registered = false; 43173d38b13STakashi Iwai } 432f18638dcSTakashi Iwai #ifdef CONFIG_PM 433f18638dcSTakashi Iwai wake_up(&card->power_sleep); 434f18638dcSTakashi Iwai #endif 4351da177e4SLinus Torvalds return 0; 4361da177e4SLinus Torvalds } 4371da177e4SLinus Torvalds 438c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_disconnect); 439c0d3fb39STakashi Iwai 4401da177e4SLinus Torvalds /** 4411da177e4SLinus Torvalds * snd_card_free - frees given soundcard structure 4421da177e4SLinus Torvalds * @card: soundcard structure 4431da177e4SLinus Torvalds * 4441da177e4SLinus Torvalds * This function releases the soundcard structure and the all assigned 4451da177e4SLinus Torvalds * devices automatically. That is, you don't have to release the devices 4461da177e4SLinus Torvalds * by yourself. 4471da177e4SLinus Torvalds * 448eb7c06e8SYacine Belkadi * Return: Zero. Frees all associated devices and frees the control 4491da177e4SLinus Torvalds * interface associated to given soundcard. 4501da177e4SLinus Torvalds */ 451c461482cSTakashi Iwai static int snd_card_do_free(struct snd_card *card) 4521da177e4SLinus Torvalds { 4538eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_MIXER_OSS) 4541da177e4SLinus Torvalds if (snd_mixer_oss_notify_callback) 4551da177e4SLinus Torvalds snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); 4561da177e4SLinus Torvalds #endif 4571da177e4SLinus Torvalds if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) { 458f2f9307aSTakashi Iwai dev_err(card->dev, "unable to free all devices (pre)\n"); 4591da177e4SLinus Torvalds /* Fatal, but this situation should never occur */ 4601da177e4SLinus Torvalds } 4611da177e4SLinus Torvalds if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) { 462f2f9307aSTakashi Iwai dev_err(card->dev, "unable to free all devices (normal)\n"); 4631da177e4SLinus Torvalds /* Fatal, but this situation should never occur */ 4641da177e4SLinus Torvalds } 4651da177e4SLinus Torvalds if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) { 466f2f9307aSTakashi Iwai dev_err(card->dev, "unable to free all devices (post)\n"); 4671da177e4SLinus Torvalds /* Fatal, but this situation should never occur */ 4681da177e4SLinus Torvalds } 4691da177e4SLinus Torvalds if (card->private_free) 4701da177e4SLinus Torvalds card->private_free(card); 471746d4a02STakashi Iwai snd_info_free_entry(card->proc_id); 4721da177e4SLinus Torvalds if (snd_info_card_free(card) < 0) { 473f2f9307aSTakashi Iwai dev_warn(card->dev, "unable to free card info\n"); 4741da177e4SLinus Torvalds /* Not fatal error */ 4751da177e4SLinus Torvalds } 476f2464064STakashi Iwai if (card->release_completion) 477f2464064STakashi Iwai complete(card->release_completion); 478c461482cSTakashi Iwai kfree(card); 479c461482cSTakashi Iwai return 0; 480c461482cSTakashi Iwai } 481c461482cSTakashi Iwai 482c461482cSTakashi Iwai int snd_card_free_when_closed(struct snd_card *card) 483c461482cSTakashi Iwai { 484f18638dcSTakashi Iwai int ret = snd_card_disconnect(card); 485c461482cSTakashi Iwai if (ret) 486c461482cSTakashi Iwai return ret; 4878bfb181cSTakashi Iwai put_device(&card->card_dev); 4881da177e4SLinus Torvalds return 0; 4891da177e4SLinus Torvalds } 490f2464064STakashi Iwai EXPORT_SYMBOL(snd_card_free_when_closed); 4911da177e4SLinus Torvalds 492f2464064STakashi Iwai int snd_card_free(struct snd_card *card) 493f2464064STakashi Iwai { 494f2464064STakashi Iwai struct completion released; 495f2464064STakashi Iwai int ret; 496f2464064STakashi Iwai 497f2464064STakashi Iwai init_completion(&released); 498f2464064STakashi Iwai card->release_completion = &released; 499f2464064STakashi Iwai ret = snd_card_free_when_closed(card); 500f2464064STakashi Iwai if (ret) 501f2464064STakashi Iwai return ret; 502f2464064STakashi Iwai /* wait, until all devices are ready for the free operation */ 503f2464064STakashi Iwai wait_for_completion(&released); 504f2464064STakashi Iwai return 0; 505f2464064STakashi Iwai } 506c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_free); 507c0d3fb39STakashi Iwai 508e7df2a3aSTakashi Iwai /* retrieve the last word of shortname or longname */ 509e7df2a3aSTakashi Iwai static const char *retrieve_id_from_card_name(const char *name) 5101da177e4SLinus Torvalds { 511e7df2a3aSTakashi Iwai const char *spos = name; 512e7df2a3aSTakashi Iwai 513e7df2a3aSTakashi Iwai while (*name) { 514e7df2a3aSTakashi Iwai if (isspace(*name) && isalnum(name[1])) 515e7df2a3aSTakashi Iwai spos = name + 1; 516e7df2a3aSTakashi Iwai name++; 517e7df2a3aSTakashi Iwai } 518e7df2a3aSTakashi Iwai return spos; 519e7df2a3aSTakashi Iwai } 520e7df2a3aSTakashi Iwai 521e7df2a3aSTakashi Iwai /* return true if the given id string doesn't conflict any other card ids */ 522e7df2a3aSTakashi Iwai static bool card_id_ok(struct snd_card *card, const char *id) 523e7df2a3aSTakashi Iwai { 524e7df2a3aSTakashi Iwai int i; 525e7df2a3aSTakashi Iwai if (!snd_info_check_reserved_words(id)) 526e7df2a3aSTakashi Iwai return false; 527e7df2a3aSTakashi Iwai for (i = 0; i < snd_ecards_limit; i++) { 528e7df2a3aSTakashi Iwai if (snd_cards[i] && snd_cards[i] != card && 529e7df2a3aSTakashi Iwai !strcmp(snd_cards[i]->id, id)) 530e7df2a3aSTakashi Iwai return false; 531e7df2a3aSTakashi Iwai } 532e7df2a3aSTakashi Iwai return true; 533e7df2a3aSTakashi Iwai } 534e7df2a3aSTakashi Iwai 535e7df2a3aSTakashi Iwai /* copy to card->id only with valid letters from nid */ 536e7df2a3aSTakashi Iwai static void copy_valid_id_string(struct snd_card *card, const char *src, 537e7df2a3aSTakashi Iwai const char *nid) 538e7df2a3aSTakashi Iwai { 539e7df2a3aSTakashi Iwai char *id = card->id; 540e7df2a3aSTakashi Iwai 541e7df2a3aSTakashi Iwai while (*nid && !isalnum(*nid)) 542e7df2a3aSTakashi Iwai nid++; 543e7df2a3aSTakashi Iwai if (isdigit(*nid)) 544e7df2a3aSTakashi Iwai *id++ = isalpha(*src) ? *src : 'D'; 545e7df2a3aSTakashi Iwai while (*nid && (size_t)(id - card->id) < sizeof(card->id) - 1) { 546e7df2a3aSTakashi Iwai if (isalnum(*nid)) 547e7df2a3aSTakashi Iwai *id++ = *nid; 548e7df2a3aSTakashi Iwai nid++; 549e7df2a3aSTakashi Iwai } 550e7df2a3aSTakashi Iwai *id = 0; 551e7df2a3aSTakashi Iwai } 552e7df2a3aSTakashi Iwai 553e7df2a3aSTakashi Iwai /* Set card->id from the given string 554e7df2a3aSTakashi Iwai * If the string conflicts with other ids, add a suffix to make it unique. 555e7df2a3aSTakashi Iwai */ 556e7df2a3aSTakashi Iwai static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, 557e7df2a3aSTakashi Iwai const char *nid) 558e7df2a3aSTakashi Iwai { 559e7df2a3aSTakashi Iwai int len, loops; 560e7df2a3aSTakashi Iwai bool is_default = false; 56110a8ebbbSJaroslav Kysela char *id; 5621da177e4SLinus Torvalds 563e7df2a3aSTakashi Iwai copy_valid_id_string(card, src, nid); 5641da177e4SLinus Torvalds id = card->id; 5651da177e4SLinus Torvalds 566e7df2a3aSTakashi Iwai again: 567e7df2a3aSTakashi Iwai /* use "Default" for obviously invalid strings 568e7df2a3aSTakashi Iwai * ("card" conflicts with proc directories) 569e7df2a3aSTakashi Iwai */ 570e7df2a3aSTakashi Iwai if (!*id || !strncmp(id, "card", 4)) { 571d8e4f9aeSTakashi Iwai strcpy(id, "Default"); 572e7df2a3aSTakashi Iwai is_default = true; 5731da177e4SLinus Torvalds } 5741da177e4SLinus Torvalds 5758edbb198STakashi Iwai len = strlen(id); 576e7df2a3aSTakashi Iwai for (loops = 0; loops < SNDRV_CARDS; loops++) { 5778edbb198STakashi Iwai char *spos; 5788edbb198STakashi Iwai char sfxstr[5]; /* "_012" */ 5798edbb198STakashi Iwai int sfxlen; 5808edbb198STakashi Iwai 581e7df2a3aSTakashi Iwai if (card_id_ok(card, id)) 582e7df2a3aSTakashi Iwai return; /* OK */ 583e7df2a3aSTakashi Iwai 5848edbb198STakashi Iwai /* Add _XYZ suffix */ 5858edbb198STakashi Iwai sprintf(sfxstr, "_%X", loops + 1); 5868edbb198STakashi Iwai sfxlen = strlen(sfxstr); 5878edbb198STakashi Iwai if (len + sfxlen >= sizeof(card->id)) 5888edbb198STakashi Iwai spos = id + sizeof(card->id) - sfxlen - 1; 589d001544dSClemens Ladisch else 5908edbb198STakashi Iwai spos = id + len; 5918edbb198STakashi Iwai strcpy(spos, sfxstr); 5921da177e4SLinus Torvalds } 593e7df2a3aSTakashi Iwai /* fallback to the default id */ 594e7df2a3aSTakashi Iwai if (!is_default) { 595e7df2a3aSTakashi Iwai *id = 0; 596e7df2a3aSTakashi Iwai goto again; 597e7df2a3aSTakashi Iwai } 598e7df2a3aSTakashi Iwai /* last resort... */ 599f2f9307aSTakashi Iwai dev_err(card->dev, "unable to set card id (%s)\n", id); 600e7df2a3aSTakashi Iwai if (card->proc_root->name) 60197f44f56STakashi Iwai strlcpy(card->id, card->proc_root->name, sizeof(card->id)); 6021da177e4SLinus Torvalds } 6031da177e4SLinus Torvalds 604872c7820SMark Brown /** 605872c7820SMark Brown * snd_card_set_id - set card identification name 606872c7820SMark Brown * @card: soundcard structure 607872c7820SMark Brown * @nid: new identification string 608872c7820SMark Brown * 609872c7820SMark Brown * This function sets the card identification and checks for name 610872c7820SMark Brown * collisions. 611872c7820SMark Brown */ 612872c7820SMark Brown void snd_card_set_id(struct snd_card *card, const char *nid) 613872c7820SMark Brown { 6145fdc18d9SJaroslav Kysela /* check if user specified own card->id */ 6155fdc18d9SJaroslav Kysela if (card->id[0] != '\0') 6165fdc18d9SJaroslav Kysela return; 6175fdc18d9SJaroslav Kysela mutex_lock(&snd_card_mutex); 618e7df2a3aSTakashi Iwai snd_card_set_id_no_lock(card, nid, nid); 6195fdc18d9SJaroslav Kysela mutex_unlock(&snd_card_mutex); 620872c7820SMark Brown } 62110a8ebbbSJaroslav Kysela EXPORT_SYMBOL(snd_card_set_id); 62210a8ebbbSJaroslav Kysela 6239fb6198eSJaroslav Kysela static ssize_t 6249fb6198eSJaroslav Kysela card_id_show_attr(struct device *dev, 6259fb6198eSJaroslav Kysela struct device_attribute *attr, char *buf) 6269fb6198eSJaroslav Kysela { 6279fb6198eSJaroslav Kysela struct snd_card *card = dev_get_drvdata(dev); 6289fb6198eSJaroslav Kysela return snprintf(buf, PAGE_SIZE, "%s\n", card ? card->id : "(null)"); 6299fb6198eSJaroslav Kysela } 6309fb6198eSJaroslav Kysela 6319fb6198eSJaroslav Kysela static ssize_t 6329fb6198eSJaroslav Kysela card_id_store_attr(struct device *dev, struct device_attribute *attr, 6339fb6198eSJaroslav Kysela const char *buf, size_t count) 6349fb6198eSJaroslav Kysela { 6359fb6198eSJaroslav Kysela struct snd_card *card = dev_get_drvdata(dev); 6369fb6198eSJaroslav Kysela char buf1[sizeof(card->id)]; 6379fb6198eSJaroslav Kysela size_t copy = count > sizeof(card->id) - 1 ? 6389fb6198eSJaroslav Kysela sizeof(card->id) - 1 : count; 6399fb6198eSJaroslav Kysela size_t idx; 6409fb6198eSJaroslav Kysela int c; 6419fb6198eSJaroslav Kysela 6429fb6198eSJaroslav Kysela for (idx = 0; idx < copy; idx++) { 6439fb6198eSJaroslav Kysela c = buf[idx]; 6449fb6198eSJaroslav Kysela if (!isalnum(c) && c != '_' && c != '-') 6459fb6198eSJaroslav Kysela return -EINVAL; 6469fb6198eSJaroslav Kysela } 6479fb6198eSJaroslav Kysela memcpy(buf1, buf, copy); 6489fb6198eSJaroslav Kysela buf1[copy] = '\0'; 6499fb6198eSJaroslav Kysela mutex_lock(&snd_card_mutex); 650e7df2a3aSTakashi Iwai if (!card_id_ok(NULL, buf1)) { 6519fb6198eSJaroslav Kysela mutex_unlock(&snd_card_mutex); 6529fb6198eSJaroslav Kysela return -EEXIST; 6539fb6198eSJaroslav Kysela } 6549fb6198eSJaroslav Kysela strcpy(card->id, buf1); 655c2eb9c4eSJaroslav Kysela snd_info_card_id_change(card); 6569fb6198eSJaroslav Kysela mutex_unlock(&snd_card_mutex); 6579fb6198eSJaroslav Kysela 6589fb6198eSJaroslav Kysela return count; 6599fb6198eSJaroslav Kysela } 6609fb6198eSJaroslav Kysela 66134356dbdSTakashi Iwai static DEVICE_ATTR(id, S_IRUGO | S_IWUSR, card_id_show_attr, card_id_store_attr); 6629fb6198eSJaroslav Kysela 6639fb6198eSJaroslav Kysela static ssize_t 6649fb6198eSJaroslav Kysela card_number_show_attr(struct device *dev, 6659fb6198eSJaroslav Kysela struct device_attribute *attr, char *buf) 6669fb6198eSJaroslav Kysela { 6679fb6198eSJaroslav Kysela struct snd_card *card = dev_get_drvdata(dev); 6689fb6198eSJaroslav Kysela return snprintf(buf, PAGE_SIZE, "%i\n", card ? card->number : -1); 6699fb6198eSJaroslav Kysela } 6709fb6198eSJaroslav Kysela 67134356dbdSTakashi Iwai static DEVICE_ATTR(number, S_IRUGO, card_number_show_attr, NULL); 67234356dbdSTakashi Iwai 67334356dbdSTakashi Iwai static struct attribute *card_dev_attrs[] = { 67434356dbdSTakashi Iwai &dev_attr_id.attr, 67534356dbdSTakashi Iwai &dev_attr_number.attr, 67634356dbdSTakashi Iwai NULL 67734356dbdSTakashi Iwai }; 67834356dbdSTakashi Iwai 67934356dbdSTakashi Iwai static struct attribute_group card_dev_attr_group = { 68034356dbdSTakashi Iwai .attrs = card_dev_attrs, 68134356dbdSTakashi Iwai }; 68234356dbdSTakashi Iwai 68334356dbdSTakashi Iwai static const struct attribute_group *card_dev_attr_groups[] = { 68434356dbdSTakashi Iwai &card_dev_attr_group, 68534356dbdSTakashi Iwai NULL 68634356dbdSTakashi Iwai }; 6879fb6198eSJaroslav Kysela 6881da177e4SLinus Torvalds /** 6891da177e4SLinus Torvalds * snd_card_register - register the soundcard 6901da177e4SLinus Torvalds * @card: soundcard structure 6911da177e4SLinus Torvalds * 6921da177e4SLinus Torvalds * This function registers all the devices assigned to the soundcard. 6931da177e4SLinus Torvalds * Until calling this, the ALSA control interface is blocked from the 6941da177e4SLinus Torvalds * external accesses. Thus, you should call this function at the end 6951da177e4SLinus Torvalds * of the initialization of the card. 6961da177e4SLinus Torvalds * 697eb7c06e8SYacine Belkadi * Return: Zero otherwise a negative error code if the registration failed. 6981da177e4SLinus Torvalds */ 699512bbd6aSTakashi Iwai int snd_card_register(struct snd_card *card) 7001da177e4SLinus Torvalds { 7011da177e4SLinus Torvalds int err; 7021da177e4SLinus Torvalds 7037eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 7047eaa943cSTakashi Iwai return -EINVAL; 70539aba963SKay Sievers 7068bfb181cSTakashi Iwai if (!card->registered) { 7078bfb181cSTakashi Iwai err = device_add(&card->card_dev); 7088bfb181cSTakashi Iwai if (err < 0) 7098bfb181cSTakashi Iwai return err; 7108bfb181cSTakashi Iwai card->registered = true; 711d80f19faSGreg Kroah-Hartman } 71239aba963SKay Sievers 7131da177e4SLinus Torvalds if ((err = snd_device_register_all(card)) < 0) 7141da177e4SLinus Torvalds return err; 715746df948STakashi Iwai mutex_lock(&snd_card_mutex); 7161da177e4SLinus Torvalds if (snd_cards[card->number]) { 7171da177e4SLinus Torvalds /* already registered */ 718746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 7191da177e4SLinus Torvalds return 0; 7201da177e4SLinus Torvalds } 721e7df2a3aSTakashi Iwai if (*card->id) { 722e7df2a3aSTakashi Iwai /* make a unique id name from the given string */ 723e7df2a3aSTakashi Iwai char tmpid[sizeof(card->id)]; 724e7df2a3aSTakashi Iwai memcpy(tmpid, card->id, sizeof(card->id)); 725e7df2a3aSTakashi Iwai snd_card_set_id_no_lock(card, tmpid, tmpid); 726e7df2a3aSTakashi Iwai } else { 727e7df2a3aSTakashi Iwai /* create an id from either shortname or longname */ 728e7df2a3aSTakashi Iwai const char *src; 729e7df2a3aSTakashi Iwai src = *card->shortname ? card->shortname : card->longname; 730e7df2a3aSTakashi Iwai snd_card_set_id_no_lock(card, src, 731e7df2a3aSTakashi Iwai retrieve_id_from_card_name(src)); 732e7df2a3aSTakashi Iwai } 7331da177e4SLinus Torvalds snd_cards[card->number] = card; 734746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 735e28563ccSTakashi Iwai init_info_for_card(card); 7368eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_MIXER_OSS) 7371da177e4SLinus Torvalds if (snd_mixer_oss_notify_callback) 7381da177e4SLinus Torvalds snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER); 7391da177e4SLinus Torvalds #endif 7401da177e4SLinus Torvalds return 0; 7411da177e4SLinus Torvalds } 7421da177e4SLinus Torvalds 743c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_register); 744c0d3fb39STakashi Iwai 745e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS 7466581f4e7STakashi Iwai static struct snd_info_entry *snd_card_info_entry; 7471da177e4SLinus Torvalds 748a381a7a6STakashi Iwai static void snd_card_info_read(struct snd_info_entry *entry, 749a381a7a6STakashi Iwai struct snd_info_buffer *buffer) 7501da177e4SLinus Torvalds { 7511da177e4SLinus Torvalds int idx, count; 752512bbd6aSTakashi Iwai struct snd_card *card; 7531da177e4SLinus Torvalds 7541da177e4SLinus Torvalds for (idx = count = 0; idx < SNDRV_CARDS; idx++) { 755746df948STakashi Iwai mutex_lock(&snd_card_mutex); 7561da177e4SLinus Torvalds if ((card = snd_cards[idx]) != NULL) { 7571da177e4SLinus Torvalds count++; 758d001544dSClemens Ladisch snd_iprintf(buffer, "%2i [%-15s]: %s - %s\n", 7591da177e4SLinus Torvalds idx, 7601da177e4SLinus Torvalds card->id, 7611da177e4SLinus Torvalds card->driver, 7621da177e4SLinus Torvalds card->shortname); 7631da177e4SLinus Torvalds snd_iprintf(buffer, " %s\n", 7641da177e4SLinus Torvalds card->longname); 7651da177e4SLinus Torvalds } 766746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 7671da177e4SLinus Torvalds } 7681da177e4SLinus Torvalds if (!count) 7691da177e4SLinus Torvalds snd_iprintf(buffer, "--- no soundcards ---\n"); 7701da177e4SLinus Torvalds } 7711da177e4SLinus Torvalds 772e28563ccSTakashi Iwai #ifdef CONFIG_SND_OSSEMUL 7731da177e4SLinus Torvalds 774512bbd6aSTakashi Iwai void snd_card_info_read_oss(struct snd_info_buffer *buffer) 7751da177e4SLinus Torvalds { 7761da177e4SLinus Torvalds int idx, count; 777512bbd6aSTakashi Iwai struct snd_card *card; 7781da177e4SLinus Torvalds 7791da177e4SLinus Torvalds for (idx = count = 0; idx < SNDRV_CARDS; idx++) { 780746df948STakashi Iwai mutex_lock(&snd_card_mutex); 7811da177e4SLinus Torvalds if ((card = snd_cards[idx]) != NULL) { 7821da177e4SLinus Torvalds count++; 7831da177e4SLinus Torvalds snd_iprintf(buffer, "%s\n", card->longname); 7841da177e4SLinus Torvalds } 785746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 7861da177e4SLinus Torvalds } 7871da177e4SLinus Torvalds if (!count) { 7881da177e4SLinus Torvalds snd_iprintf(buffer, "--- no soundcards ---\n"); 7891da177e4SLinus Torvalds } 7901da177e4SLinus Torvalds } 7911da177e4SLinus Torvalds 7921da177e4SLinus Torvalds #endif 7931da177e4SLinus Torvalds 7941da177e4SLinus Torvalds #ifdef MODULE 795512bbd6aSTakashi Iwai static struct snd_info_entry *snd_card_module_info_entry; 796512bbd6aSTakashi Iwai static void snd_card_module_info_read(struct snd_info_entry *entry, 797512bbd6aSTakashi Iwai struct snd_info_buffer *buffer) 7981da177e4SLinus Torvalds { 7991da177e4SLinus Torvalds int idx; 800512bbd6aSTakashi Iwai struct snd_card *card; 8011da177e4SLinus Torvalds 8021da177e4SLinus Torvalds for (idx = 0; idx < SNDRV_CARDS; idx++) { 803746df948STakashi Iwai mutex_lock(&snd_card_mutex); 8041da177e4SLinus Torvalds if ((card = snd_cards[idx]) != NULL) 805d001544dSClemens Ladisch snd_iprintf(buffer, "%2i %s\n", 806d001544dSClemens Ladisch idx, card->module->name); 807746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 8081da177e4SLinus Torvalds } 8091da177e4SLinus Torvalds } 8101da177e4SLinus Torvalds #endif 8111da177e4SLinus Torvalds 8121da177e4SLinus Torvalds int __init snd_card_info_init(void) 8131da177e4SLinus Torvalds { 814512bbd6aSTakashi Iwai struct snd_info_entry *entry; 8151da177e4SLinus Torvalds 8161da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL); 8177c22f1aaSTakashi Iwai if (! entry) 8187c22f1aaSTakashi Iwai return -ENOMEM; 8191da177e4SLinus Torvalds entry->c.text.read = snd_card_info_read; 8201da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 8211da177e4SLinus Torvalds snd_info_free_entry(entry); 8221da177e4SLinus Torvalds return -ENOMEM; 8231da177e4SLinus Torvalds } 8241da177e4SLinus Torvalds snd_card_info_entry = entry; 8251da177e4SLinus Torvalds 8261da177e4SLinus Torvalds #ifdef MODULE 8271da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL); 8281da177e4SLinus Torvalds if (entry) { 8291da177e4SLinus Torvalds entry->c.text.read = snd_card_module_info_read; 8301da177e4SLinus Torvalds if (snd_info_register(entry) < 0) 8311da177e4SLinus Torvalds snd_info_free_entry(entry); 8321da177e4SLinus Torvalds else 8331da177e4SLinus Torvalds snd_card_module_info_entry = entry; 8341da177e4SLinus Torvalds } 8351da177e4SLinus Torvalds #endif 8361da177e4SLinus Torvalds 8371da177e4SLinus Torvalds return 0; 8381da177e4SLinus Torvalds } 8391da177e4SLinus Torvalds 8401da177e4SLinus Torvalds int __exit snd_card_info_done(void) 8411da177e4SLinus Torvalds { 842746d4a02STakashi Iwai snd_info_free_entry(snd_card_info_entry); 8431da177e4SLinus Torvalds #ifdef MODULE 844746d4a02STakashi Iwai snd_info_free_entry(snd_card_module_info_entry); 8451da177e4SLinus Torvalds #endif 8461da177e4SLinus Torvalds return 0; 8471da177e4SLinus Torvalds } 8481da177e4SLinus Torvalds 849e28563ccSTakashi Iwai #endif /* CONFIG_PROC_FS */ 850e28563ccSTakashi Iwai 8511da177e4SLinus Torvalds /** 8521da177e4SLinus Torvalds * snd_component_add - add a component string 8531da177e4SLinus Torvalds * @card: soundcard structure 8541da177e4SLinus Torvalds * @component: the component id string 8551da177e4SLinus Torvalds * 8561da177e4SLinus Torvalds * This function adds the component id string to the supported list. 8571da177e4SLinus Torvalds * The component can be referred from the alsa-lib. 8581da177e4SLinus Torvalds * 859eb7c06e8SYacine Belkadi * Return: Zero otherwise a negative error code. 8601da177e4SLinus Torvalds */ 8611da177e4SLinus Torvalds 862512bbd6aSTakashi Iwai int snd_component_add(struct snd_card *card, const char *component) 8631da177e4SLinus Torvalds { 8641da177e4SLinus Torvalds char *ptr; 8651da177e4SLinus Torvalds int len = strlen(component); 8661da177e4SLinus Torvalds 8671da177e4SLinus Torvalds ptr = strstr(card->components, component); 8681da177e4SLinus Torvalds if (ptr != NULL) { 8691da177e4SLinus Torvalds if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */ 8701da177e4SLinus Torvalds return 1; 8711da177e4SLinus Torvalds } 8721da177e4SLinus Torvalds if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) { 8731da177e4SLinus Torvalds snd_BUG(); 8741da177e4SLinus Torvalds return -ENOMEM; 8751da177e4SLinus Torvalds } 8761da177e4SLinus Torvalds if (card->components[0] != '\0') 8771da177e4SLinus Torvalds strcat(card->components, " "); 8781da177e4SLinus Torvalds strcat(card->components, component); 8791da177e4SLinus Torvalds return 0; 8801da177e4SLinus Torvalds } 8811da177e4SLinus Torvalds 882c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_component_add); 883c0d3fb39STakashi Iwai 8841da177e4SLinus Torvalds /** 8851da177e4SLinus Torvalds * snd_card_file_add - add the file to the file list of the card 8861da177e4SLinus Torvalds * @card: soundcard structure 8871da177e4SLinus Torvalds * @file: file pointer 8881da177e4SLinus Torvalds * 8891da177e4SLinus Torvalds * This function adds the file to the file linked-list of the card. 8901da177e4SLinus Torvalds * This linked-list is used to keep tracking the connection state, 8911da177e4SLinus Torvalds * and to avoid the release of busy resources by hotplug. 8921da177e4SLinus Torvalds * 893eb7c06e8SYacine Belkadi * Return: zero or a negative error code. 8941da177e4SLinus Torvalds */ 895512bbd6aSTakashi Iwai int snd_card_file_add(struct snd_card *card, struct file *file) 8961da177e4SLinus Torvalds { 8971da177e4SLinus Torvalds struct snd_monitor_file *mfile; 8981da177e4SLinus Torvalds 8991da177e4SLinus Torvalds mfile = kmalloc(sizeof(*mfile), GFP_KERNEL); 9001da177e4SLinus Torvalds if (mfile == NULL) 9011da177e4SLinus Torvalds return -ENOMEM; 9021da177e4SLinus Torvalds mfile->file = file; 903a9edfc60SKarsten Wiese mfile->disconnected_f_op = NULL; 904a45e3d6bSTakashi Iwai INIT_LIST_HEAD(&mfile->shutdown_list); 9051da177e4SLinus Torvalds spin_lock(&card->files_lock); 9061da177e4SLinus Torvalds if (card->shutdown) { 9071da177e4SLinus Torvalds spin_unlock(&card->files_lock); 9081da177e4SLinus Torvalds kfree(mfile); 9091da177e4SLinus Torvalds return -ENODEV; 9101da177e4SLinus Torvalds } 911118dd6bfSTakashi Iwai list_add(&mfile->list, &card->files_list); 912f2464064STakashi Iwai get_device(&card->card_dev); 9131da177e4SLinus Torvalds spin_unlock(&card->files_lock); 9141da177e4SLinus Torvalds return 0; 9151da177e4SLinus Torvalds } 9161da177e4SLinus Torvalds 917c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_file_add); 918c0d3fb39STakashi Iwai 9191da177e4SLinus Torvalds /** 9201da177e4SLinus Torvalds * snd_card_file_remove - remove the file from the file list 9211da177e4SLinus Torvalds * @card: soundcard structure 9221da177e4SLinus Torvalds * @file: file pointer 9231da177e4SLinus Torvalds * 9241da177e4SLinus Torvalds * This function removes the file formerly added to the card via 9251da177e4SLinus Torvalds * snd_card_file_add() function. 9262b29b13cSTakashi Iwai * If all files are removed and snd_card_free_when_closed() was 9272b29b13cSTakashi Iwai * called beforehand, it processes the pending release of 9282b29b13cSTakashi Iwai * resources. 9291da177e4SLinus Torvalds * 930eb7c06e8SYacine Belkadi * Return: Zero or a negative error code. 9311da177e4SLinus Torvalds */ 932512bbd6aSTakashi Iwai int snd_card_file_remove(struct snd_card *card, struct file *file) 9331da177e4SLinus Torvalds { 934118dd6bfSTakashi Iwai struct snd_monitor_file *mfile, *found = NULL; 9351da177e4SLinus Torvalds 9361da177e4SLinus Torvalds spin_lock(&card->files_lock); 937118dd6bfSTakashi Iwai list_for_each_entry(mfile, &card->files_list, list) { 9381da177e4SLinus Torvalds if (mfile->file == file) { 939118dd6bfSTakashi Iwai list_del(&mfile->list); 940a45e3d6bSTakashi Iwai spin_lock(&shutdown_lock); 941a45e3d6bSTakashi Iwai list_del(&mfile->shutdown_list); 942a45e3d6bSTakashi Iwai spin_unlock(&shutdown_lock); 943118dd6bfSTakashi Iwai if (mfile->disconnected_f_op) 944118dd6bfSTakashi Iwai fops_put(mfile->disconnected_f_op); 945118dd6bfSTakashi Iwai found = mfile; 9461da177e4SLinus Torvalds break; 9471da177e4SLinus Torvalds } 9481da177e4SLinus Torvalds } 949c461482cSTakashi Iwai spin_unlock(&card->files_lock); 950118dd6bfSTakashi Iwai if (!found) { 951f2f9307aSTakashi Iwai dev_err(card->dev, "card file remove problem (%p)\n", file); 9521da177e4SLinus Torvalds return -ENOENT; 9531da177e4SLinus Torvalds } 954118dd6bfSTakashi Iwai kfree(found); 955f2464064STakashi Iwai put_device(&card->card_dev); 9561da177e4SLinus Torvalds return 0; 9571da177e4SLinus Torvalds } 9581da177e4SLinus Torvalds 959c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_file_remove); 960c0d3fb39STakashi Iwai 9611da177e4SLinus Torvalds #ifdef CONFIG_PM 9621da177e4SLinus Torvalds /** 9631da177e4SLinus Torvalds * snd_power_wait - wait until the power-state is changed. 9641da177e4SLinus Torvalds * @card: soundcard structure 9651da177e4SLinus Torvalds * @power_state: expected power state 9661da177e4SLinus Torvalds * 9671da177e4SLinus Torvalds * Waits until the power-state is changed. 9681da177e4SLinus Torvalds * 969eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code. 970eb7c06e8SYacine Belkadi * 9711da177e4SLinus Torvalds * Note: the power lock must be active before call. 9721da177e4SLinus Torvalds */ 973cbac4b0cSTakashi Iwai int snd_power_wait(struct snd_card *card, unsigned int power_state) 9741da177e4SLinus Torvalds { 9751da177e4SLinus Torvalds wait_queue_t wait; 9761da177e4SLinus Torvalds int result = 0; 9771da177e4SLinus Torvalds 9781da177e4SLinus Torvalds /* fastpath */ 9791da177e4SLinus Torvalds if (snd_power_get_state(card) == power_state) 9801da177e4SLinus Torvalds return 0; 9811da177e4SLinus Torvalds init_waitqueue_entry(&wait, current); 9821da177e4SLinus Torvalds add_wait_queue(&card->power_sleep, &wait); 9831da177e4SLinus Torvalds while (1) { 9841da177e4SLinus Torvalds if (card->shutdown) { 9851da177e4SLinus Torvalds result = -ENODEV; 9861da177e4SLinus Torvalds break; 9871da177e4SLinus Torvalds } 9881da177e4SLinus Torvalds if (snd_power_get_state(card) == power_state) 9891da177e4SLinus Torvalds break; 9901da177e4SLinus Torvalds set_current_state(TASK_UNINTERRUPTIBLE); 9911da177e4SLinus Torvalds snd_power_unlock(card); 9921da177e4SLinus Torvalds schedule_timeout(30 * HZ); 9931da177e4SLinus Torvalds snd_power_lock(card); 9941da177e4SLinus Torvalds } 9951da177e4SLinus Torvalds remove_wait_queue(&card->power_sleep, &wait); 9961da177e4SLinus Torvalds return result; 9971da177e4SLinus Torvalds } 9981da177e4SLinus Torvalds 999c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_power_wait); 10001da177e4SLinus Torvalds #endif /* CONFIG_PM */ 1001