11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Initialization routines 3c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 71da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 81da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 91da177e4SLinus Torvalds * (at your option) any later version. 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 121da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 131da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 141da177e4SLinus Torvalds * GNU General Public License for more details. 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 171da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 181da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds */ 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #include <linux/init.h> 231da177e4SLinus Torvalds #include <linux/sched.h> 24da155d5bSPaul Gortmaker #include <linux/module.h> 2551990e82SPaul Gortmaker #include <linux/device.h> 261da177e4SLinus Torvalds #include <linux/file.h> 271da177e4SLinus Torvalds #include <linux/slab.h> 281da177e4SLinus Torvalds #include <linux/time.h> 291da177e4SLinus Torvalds #include <linux/ctype.h> 301da177e4SLinus Torvalds #include <linux/pm.h> 31f2464064STakashi Iwai #include <linux/completion.h> 32d052d1beSRussell King 331da177e4SLinus Torvalds #include <sound/core.h> 341da177e4SLinus Torvalds #include <sound/control.h> 351da177e4SLinus Torvalds #include <sound/info.h> 361da177e4SLinus Torvalds 3782a783f4STakashi Iwai /* monitor files for graceful shutdown (hotplug) */ 3882a783f4STakashi Iwai struct snd_monitor_file { 3982a783f4STakashi Iwai struct file *file; 4082a783f4STakashi Iwai const struct file_operations *disconnected_f_op; 4182a783f4STakashi Iwai struct list_head shutdown_list; /* still need to shutdown */ 4282a783f4STakashi Iwai struct list_head list; /* link of monitor files */ 4382a783f4STakashi Iwai }; 4482a783f4STakashi Iwai 45a9edfc60SKarsten Wiese static DEFINE_SPINLOCK(shutdown_lock); 46a9edfc60SKarsten Wiese static LIST_HEAD(shutdown_files); 47a9edfc60SKarsten Wiese 489c2e08c5SArjan van de Ven static const struct file_operations snd_shutdown_f_ops; 491da177e4SLinus Torvalds 507bb2491bSTakashi Iwai /* locked for registering/using */ 517bb2491bSTakashi Iwai static DECLARE_BITMAP(snd_cards_lock, SNDRV_CARDS); 526581f4e7STakashi Iwai struct snd_card *snd_cards[SNDRV_CARDS]; 53c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_cards); 54c0d3fb39STakashi Iwai 55746df948STakashi Iwai static DEFINE_MUTEX(snd_card_mutex); 561da177e4SLinus Torvalds 57304cd07fSTakashi Iwai static char *slots[SNDRV_CARDS]; 58304cd07fSTakashi Iwai module_param_array(slots, charp, NULL, 0444); 59304cd07fSTakashi Iwai MODULE_PARM_DESC(slots, "Module names assigned to the slots."); 60304cd07fSTakashi Iwai 61a93bbaa7STakashi Iwai /* return non-zero if the given index is reserved for the given 62304cd07fSTakashi Iwai * module via slots option 63304cd07fSTakashi Iwai */ 64a93bbaa7STakashi Iwai static int module_slot_match(struct module *module, int idx) 65304cd07fSTakashi Iwai { 66a93bbaa7STakashi Iwai int match = 1; 67304cd07fSTakashi Iwai #ifdef MODULE 68a93bbaa7STakashi Iwai const char *s1, *s2; 69a93bbaa7STakashi Iwai 7060f6fef8STakashi Iwai if (!module || !*module->name || !slots[idx]) 71304cd07fSTakashi Iwai return 0; 72a93bbaa7STakashi Iwai 73a93bbaa7STakashi Iwai s1 = module->name; 74a93bbaa7STakashi Iwai s2 = slots[idx]; 75a93bbaa7STakashi Iwai if (*s2 == '!') { 76a93bbaa7STakashi Iwai match = 0; /* negative match */ 77a93bbaa7STakashi Iwai s2++; 78a93bbaa7STakashi Iwai } 79304cd07fSTakashi Iwai /* compare module name strings 80304cd07fSTakashi Iwai * hyphens are handled as equivalent with underscore 81304cd07fSTakashi Iwai */ 82304cd07fSTakashi Iwai for (;;) { 83304cd07fSTakashi Iwai char c1 = *s1++; 84304cd07fSTakashi Iwai char c2 = *s2++; 85304cd07fSTakashi Iwai if (c1 == '-') 86304cd07fSTakashi Iwai c1 = '_'; 87304cd07fSTakashi Iwai if (c2 == '-') 88304cd07fSTakashi Iwai c2 = '_'; 89304cd07fSTakashi Iwai if (c1 != c2) 90a93bbaa7STakashi Iwai return !match; 91304cd07fSTakashi Iwai if (!c1) 92304cd07fSTakashi Iwai break; 93304cd07fSTakashi Iwai } 94a93bbaa7STakashi Iwai #endif /* MODULE */ 95a93bbaa7STakashi Iwai return match; 96304cd07fSTakashi Iwai } 97304cd07fSTakashi Iwai 988eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_MIXER_OSS) 99512bbd6aSTakashi Iwai int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag); 100c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_mixer_oss_notify_callback); 1011da177e4SLinus Torvalds #endif 1021da177e4SLinus Torvalds 103e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS 104512bbd6aSTakashi Iwai static void snd_card_id_read(struct snd_info_entry *entry, 105512bbd6aSTakashi Iwai struct snd_info_buffer *buffer) 1061da177e4SLinus Torvalds { 1071da177e4SLinus Torvalds snd_iprintf(buffer, "%s\n", entry->card->id); 1081da177e4SLinus Torvalds } 1091da177e4SLinus Torvalds 110e28563ccSTakashi Iwai static inline int init_info_for_card(struct snd_card *card) 111e28563ccSTakashi Iwai { 112e28563ccSTakashi Iwai int err; 113e28563ccSTakashi Iwai struct snd_info_entry *entry; 114e28563ccSTakashi Iwai 115e28563ccSTakashi Iwai if ((err = snd_info_card_register(card)) < 0) { 116f2f9307aSTakashi Iwai dev_dbg(card->dev, "unable to create card info\n"); 117e28563ccSTakashi Iwai return err; 118e28563ccSTakashi Iwai } 119e28563ccSTakashi Iwai if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) { 120f2f9307aSTakashi Iwai dev_dbg(card->dev, "unable to create card entry\n"); 121e28563ccSTakashi Iwai return err; 122e28563ccSTakashi Iwai } 123e28563ccSTakashi Iwai entry->c.text.read = snd_card_id_read; 124e28563ccSTakashi Iwai if (snd_info_register(entry) < 0) { 125e28563ccSTakashi Iwai snd_info_free_entry(entry); 126e28563ccSTakashi Iwai entry = NULL; 127e28563ccSTakashi Iwai } 128e28563ccSTakashi Iwai card->proc_id = entry; 129e28563ccSTakashi Iwai return 0; 130e28563ccSTakashi Iwai } 131e28563ccSTakashi Iwai #else /* !CONFIG_PROC_FS */ 132e28563ccSTakashi Iwai #define init_info_for_card(card) 133e28563ccSTakashi Iwai #endif 134e28563ccSTakashi Iwai 135deb6596fSTakashi Iwai static int check_empty_slot(struct module *module, int slot) 136deb6596fSTakashi Iwai { 137deb6596fSTakashi Iwai return !slots[slot] || !*slots[slot]; 138deb6596fSTakashi Iwai } 139deb6596fSTakashi Iwai 140deb6596fSTakashi Iwai /* return an empty slot number (>= 0) found in the given bitmask @mask. 141deb6596fSTakashi Iwai * @mask == -1 == 0xffffffff means: take any free slot up to 32 142deb6596fSTakashi Iwai * when no slot is available, return the original @mask as is. 143deb6596fSTakashi Iwai */ 144deb6596fSTakashi Iwai static int get_slot_from_bitmask(int mask, int (*check)(struct module *, int), 145deb6596fSTakashi Iwai struct module *module) 146deb6596fSTakashi Iwai { 147deb6596fSTakashi Iwai int slot; 148deb6596fSTakashi Iwai 149deb6596fSTakashi Iwai for (slot = 0; slot < SNDRV_CARDS; slot++) { 150deb6596fSTakashi Iwai if (slot < 32 && !(mask & (1U << slot))) 151deb6596fSTakashi Iwai continue; 152deb6596fSTakashi Iwai if (!test_bit(slot, snd_cards_lock)) { 153deb6596fSTakashi Iwai if (check(module, slot)) 154deb6596fSTakashi Iwai return slot; /* found */ 155deb6596fSTakashi Iwai } 156deb6596fSTakashi Iwai } 157deb6596fSTakashi Iwai return mask; /* unchanged */ 158deb6596fSTakashi Iwai } 159deb6596fSTakashi Iwai 1604b440be6STakashi Iwai /* the default release callback set in snd_device_initialize() below; 1614b440be6STakashi Iwai * this is just NOP for now, as almost all jobs are already done in 1624b440be6STakashi Iwai * dev_free callback of snd_device chain instead. 1634b440be6STakashi Iwai */ 1644b440be6STakashi Iwai static void default_release(struct device *dev) 1654b440be6STakashi Iwai { 1664b440be6STakashi Iwai } 1674b440be6STakashi Iwai 1684b440be6STakashi Iwai /** 1694b440be6STakashi Iwai * snd_device_initialize - Initialize struct device for sound devices 1704b440be6STakashi Iwai * @dev: device to initialize 1714b440be6STakashi Iwai * @card: card to assign, optional 1724b440be6STakashi Iwai */ 1734b440be6STakashi Iwai void snd_device_initialize(struct device *dev, struct snd_card *card) 1744b440be6STakashi Iwai { 1754b440be6STakashi Iwai device_initialize(dev); 1764b440be6STakashi Iwai if (card) 1774b440be6STakashi Iwai dev->parent = &card->card_dev; 1784b440be6STakashi Iwai dev->class = sound_class; 1794b440be6STakashi Iwai dev->release = default_release; 1804b440be6STakashi Iwai } 1814b440be6STakashi Iwai EXPORT_SYMBOL_GPL(snd_device_initialize); 1824b440be6STakashi Iwai 1838bfb181cSTakashi Iwai static int snd_card_do_free(struct snd_card *card); 1846bbc7fedSTakashi Iwai static const struct attribute_group card_dev_attr_group; 1858bfb181cSTakashi Iwai 1868bfb181cSTakashi Iwai static void release_card_device(struct device *dev) 1878bfb181cSTakashi Iwai { 1888bfb181cSTakashi Iwai snd_card_do_free(dev_to_snd_card(dev)); 1898bfb181cSTakashi Iwai } 1908bfb181cSTakashi Iwai 1911da177e4SLinus Torvalds /** 192393aa9c1STakashi Iwai * snd_card_new - create and initialize a soundcard structure 193393aa9c1STakashi Iwai * @parent: the parent device object 1941da177e4SLinus Torvalds * @idx: card index (address) [0 ... (SNDRV_CARDS-1)] 1951da177e4SLinus Torvalds * @xid: card identification (ASCII string) 1961da177e4SLinus Torvalds * @module: top level module for locking 1971da177e4SLinus Torvalds * @extra_size: allocate this extra size after the main soundcard structure 19853fb1e63STakashi Iwai * @card_ret: the pointer to store the created card instance 1991da177e4SLinus Torvalds * 2001da177e4SLinus Torvalds * Creates and initializes a soundcard structure. 2011da177e4SLinus Torvalds * 20253fb1e63STakashi Iwai * The function allocates snd_card instance via kzalloc with the given 20353fb1e63STakashi Iwai * space for the driver to use freely. The allocated struct is stored 20453fb1e63STakashi Iwai * in the given card_ret pointer. 20553fb1e63STakashi Iwai * 206eb7c06e8SYacine Belkadi * Return: Zero if successful or a negative error code. 2071da177e4SLinus Torvalds */ 208393aa9c1STakashi Iwai int snd_card_new(struct device *parent, int idx, const char *xid, 20953fb1e63STakashi Iwai struct module *module, int extra_size, 21053fb1e63STakashi Iwai struct snd_card **card_ret) 2111da177e4SLinus Torvalds { 212512bbd6aSTakashi Iwai struct snd_card *card; 213deb6596fSTakashi Iwai int err; 2141da177e4SLinus Torvalds 21553fb1e63STakashi Iwai if (snd_BUG_ON(!card_ret)) 21653fb1e63STakashi Iwai return -EINVAL; 21753fb1e63STakashi Iwai *card_ret = NULL; 21853fb1e63STakashi Iwai 2191da177e4SLinus Torvalds if (extra_size < 0) 2201da177e4SLinus Torvalds extra_size = 0; 221ca2c0966STakashi Iwai card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL); 22253fb1e63STakashi Iwai if (!card) 22353fb1e63STakashi Iwai return -ENOMEM; 2248bfb181cSTakashi Iwai if (extra_size > 0) 2258bfb181cSTakashi Iwai card->private_data = (char *)card + sizeof(struct snd_card); 22610a8ebbbSJaroslav Kysela if (xid) 2275fdc18d9SJaroslav Kysela strlcpy(card->id, xid, sizeof(card->id)); 2281da177e4SLinus Torvalds err = 0; 229746df948STakashi Iwai mutex_lock(&snd_card_mutex); 230deb6596fSTakashi Iwai if (idx < 0) /* first check the matching module-name slot */ 231deb6596fSTakashi Iwai idx = get_slot_from_bitmask(idx, module_slot_match, module); 232deb6596fSTakashi Iwai if (idx < 0) /* if not matched, assign an empty slot */ 233deb6596fSTakashi Iwai idx = get_slot_from_bitmask(idx, check_empty_slot, module); 234a93bbaa7STakashi Iwai if (idx < 0) 235a93bbaa7STakashi Iwai err = -ENODEV; 236a93bbaa7STakashi Iwai else if (idx < snd_ecards_limit) { 2377bb2491bSTakashi Iwai if (test_bit(idx, snd_cards_lock)) 2385c33dd70SOliver Neukum err = -EBUSY; /* invalid */ 239a93bbaa7STakashi Iwai } else if (idx >= SNDRV_CARDS) 2401da177e4SLinus Torvalds err = -ENODEV; 241a93bbaa7STakashi Iwai if (err < 0) { 242746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 243f2f9307aSTakashi Iwai dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n", 2445c33dd70SOliver Neukum idx, snd_ecards_limit - 1, err); 2458bfb181cSTakashi Iwai kfree(card); 2468bfb181cSTakashi Iwai return err; 2471da177e4SLinus Torvalds } 2487bb2491bSTakashi Iwai set_bit(idx, snd_cards_lock); /* lock it */ 249a93bbaa7STakashi Iwai if (idx >= snd_ecards_limit) 250a93bbaa7STakashi Iwai snd_ecards_limit = idx + 1; /* increase the limit */ 251746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 252393aa9c1STakashi Iwai card->dev = parent; 2531da177e4SLinus Torvalds card->number = idx; 2541da177e4SLinus Torvalds card->module = module; 2551da177e4SLinus Torvalds INIT_LIST_HEAD(&card->devices); 2561da177e4SLinus Torvalds init_rwsem(&card->controls_rwsem); 2571da177e4SLinus Torvalds rwlock_init(&card->ctl_files_rwlock); 25807f4d9d7SLars-Peter Clausen mutex_init(&card->user_ctl_lock); 2591da177e4SLinus Torvalds INIT_LIST_HEAD(&card->controls); 2601da177e4SLinus Torvalds INIT_LIST_HEAD(&card->ctl_files); 2611da177e4SLinus Torvalds spin_lock_init(&card->files_lock); 262118dd6bfSTakashi Iwai INIT_LIST_HEAD(&card->files_list); 2631da177e4SLinus Torvalds #ifdef CONFIG_PM 2641a60d4c5SIngo Molnar mutex_init(&card->power_lock); 2651da177e4SLinus Torvalds init_waitqueue_head(&card->power_sleep); 2661da177e4SLinus Torvalds #endif 2678bfb181cSTakashi Iwai 2688bfb181cSTakashi Iwai device_initialize(&card->card_dev); 2698bfb181cSTakashi Iwai card->card_dev.parent = parent; 2708bfb181cSTakashi Iwai card->card_dev.class = sound_class; 2718bfb181cSTakashi Iwai card->card_dev.release = release_card_device; 2726bbc7fedSTakashi Iwai card->card_dev.groups = card->dev_groups; 2736bbc7fedSTakashi Iwai card->dev_groups[0] = &card_dev_attr_group; 2748bfb181cSTakashi Iwai err = kobject_set_name(&card->card_dev.kobj, "card%d", idx); 2758bfb181cSTakashi Iwai if (err < 0) 2768bfb181cSTakashi Iwai goto __error; 2778bfb181cSTakashi Iwai 2781da177e4SLinus Torvalds /* the control interface cannot be accessed from the user space until */ 2791da177e4SLinus Torvalds /* snd_cards_bitmask and snd_cards are set with snd_card_register */ 28053fb1e63STakashi Iwai err = snd_ctl_create(card); 28153fb1e63STakashi Iwai if (err < 0) { 282f2f9307aSTakashi Iwai dev_err(parent, "unable to register control minors\n"); 2831da177e4SLinus Torvalds goto __error; 2841da177e4SLinus Torvalds } 28553fb1e63STakashi Iwai err = snd_info_card_create(card); 28653fb1e63STakashi Iwai if (err < 0) { 287f2f9307aSTakashi Iwai dev_err(parent, "unable to create card info\n"); 2881da177e4SLinus Torvalds goto __error_ctl; 2891da177e4SLinus Torvalds } 29053fb1e63STakashi Iwai *card_ret = card; 29153fb1e63STakashi Iwai return 0; 2921da177e4SLinus Torvalds 2931da177e4SLinus Torvalds __error_ctl: 294289ca025STakashi Iwai snd_device_free_all(card); 2951da177e4SLinus Torvalds __error: 2968bfb181cSTakashi Iwai put_device(&card->card_dev); 29753fb1e63STakashi Iwai return err; 2981da177e4SLinus Torvalds } 299393aa9c1STakashi Iwai EXPORT_SYMBOL(snd_card_new); 300c0d3fb39STakashi Iwai 301746df948STakashi Iwai /* return non-zero if a card is already locked */ 302746df948STakashi Iwai int snd_card_locked(int card) 303746df948STakashi Iwai { 304746df948STakashi Iwai int locked; 305746df948STakashi Iwai 306746df948STakashi Iwai mutex_lock(&snd_card_mutex); 3077bb2491bSTakashi Iwai locked = test_bit(card, snd_cards_lock); 308746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 309746df948STakashi Iwai return locked; 310746df948STakashi Iwai } 311746df948STakashi Iwai 312b3b0abe1SClemens Ladisch static loff_t snd_disconnect_llseek(struct file *file, loff_t offset, int orig) 313b3b0abe1SClemens Ladisch { 314b3b0abe1SClemens Ladisch return -ENODEV; 315b3b0abe1SClemens Ladisch } 316b3b0abe1SClemens Ladisch 317b3b0abe1SClemens Ladisch static ssize_t snd_disconnect_read(struct file *file, char __user *buf, 318b3b0abe1SClemens Ladisch size_t count, loff_t *offset) 319b3b0abe1SClemens Ladisch { 320b3b0abe1SClemens Ladisch return -ENODEV; 321b3b0abe1SClemens Ladisch } 322b3b0abe1SClemens Ladisch 323b3b0abe1SClemens Ladisch static ssize_t snd_disconnect_write(struct file *file, const char __user *buf, 324b3b0abe1SClemens Ladisch size_t count, loff_t *offset) 325b3b0abe1SClemens Ladisch { 326b3b0abe1SClemens Ladisch return -ENODEV; 327b3b0abe1SClemens Ladisch } 328b3b0abe1SClemens Ladisch 329a9edfc60SKarsten Wiese static int snd_disconnect_release(struct inode *inode, struct file *file) 330a9edfc60SKarsten Wiese { 331a9edfc60SKarsten Wiese struct snd_monitor_file *df = NULL, *_df; 332a9edfc60SKarsten Wiese 333a9edfc60SKarsten Wiese spin_lock(&shutdown_lock); 334a9edfc60SKarsten Wiese list_for_each_entry(_df, &shutdown_files, shutdown_list) { 335a9edfc60SKarsten Wiese if (_df->file == file) { 336a9edfc60SKarsten Wiese df = _df; 337118dd6bfSTakashi Iwai list_del_init(&df->shutdown_list); 338a9edfc60SKarsten Wiese break; 339a9edfc60SKarsten Wiese } 340a9edfc60SKarsten Wiese } 341a9edfc60SKarsten Wiese spin_unlock(&shutdown_lock); 342a9edfc60SKarsten Wiese 343233e70f4SAl Viro if (likely(df)) { 344233e70f4SAl Viro if ((file->f_flags & FASYNC) && df->disconnected_f_op->fasync) 345233e70f4SAl Viro df->disconnected_f_op->fasync(-1, file, 0); 346a9edfc60SKarsten Wiese return df->disconnected_f_op->release(inode, file); 347233e70f4SAl Viro } 348a9edfc60SKarsten Wiese 3499bf8e7ddSHarvey Harrison panic("%s(%p, %p) failed!", __func__, inode, file); 350a9edfc60SKarsten Wiese } 351a9edfc60SKarsten Wiese 3521da177e4SLinus Torvalds static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait) 3531da177e4SLinus Torvalds { 3541da177e4SLinus Torvalds return POLLERR | POLLNVAL; 3551da177e4SLinus Torvalds } 3561da177e4SLinus Torvalds 357b3b0abe1SClemens Ladisch static long snd_disconnect_ioctl(struct file *file, 358b3b0abe1SClemens Ladisch unsigned int cmd, unsigned long arg) 359b3b0abe1SClemens Ladisch { 360b3b0abe1SClemens Ladisch return -ENODEV; 361b3b0abe1SClemens Ladisch } 362b3b0abe1SClemens Ladisch 363b3b0abe1SClemens Ladisch static int snd_disconnect_mmap(struct file *file, struct vm_area_struct *vma) 364b3b0abe1SClemens Ladisch { 365b3b0abe1SClemens Ladisch return -ENODEV; 366b3b0abe1SClemens Ladisch } 367b3b0abe1SClemens Ladisch 368b3b0abe1SClemens Ladisch static int snd_disconnect_fasync(int fd, struct file *file, int on) 369b3b0abe1SClemens Ladisch { 370b3b0abe1SClemens Ladisch return -ENODEV; 371b3b0abe1SClemens Ladisch } 372b3b0abe1SClemens Ladisch 3739c2e08c5SArjan van de Ven static const struct file_operations snd_shutdown_f_ops = 374a9edfc60SKarsten Wiese { 375a9edfc60SKarsten Wiese .owner = THIS_MODULE, 376a9edfc60SKarsten Wiese .llseek = snd_disconnect_llseek, 377a9edfc60SKarsten Wiese .read = snd_disconnect_read, 378a9edfc60SKarsten Wiese .write = snd_disconnect_write, 379a9edfc60SKarsten Wiese .release = snd_disconnect_release, 380a9edfc60SKarsten Wiese .poll = snd_disconnect_poll, 381a9edfc60SKarsten Wiese .unlocked_ioctl = snd_disconnect_ioctl, 382a9edfc60SKarsten Wiese #ifdef CONFIG_COMPAT 383a9edfc60SKarsten Wiese .compat_ioctl = snd_disconnect_ioctl, 384a9edfc60SKarsten Wiese #endif 385a9edfc60SKarsten Wiese .mmap = snd_disconnect_mmap, 386a9edfc60SKarsten Wiese .fasync = snd_disconnect_fasync 387a9edfc60SKarsten Wiese }; 388a9edfc60SKarsten Wiese 3891da177e4SLinus Torvalds /** 3901da177e4SLinus Torvalds * snd_card_disconnect - disconnect all APIs from the file-operations (user space) 3911da177e4SLinus Torvalds * @card: soundcard structure 3921da177e4SLinus Torvalds * 3931da177e4SLinus Torvalds * Disconnects all APIs from the file-operations (user space). 3941da177e4SLinus Torvalds * 395eb7c06e8SYacine Belkadi * Return: Zero, otherwise a negative error code. 3961da177e4SLinus Torvalds * 3971da177e4SLinus Torvalds * Note: The current implementation replaces all active file->f_op with special 3981da177e4SLinus Torvalds * dummy file operations (they do nothing except release). 3991da177e4SLinus Torvalds */ 400512bbd6aSTakashi Iwai int snd_card_disconnect(struct snd_card *card) 4011da177e4SLinus Torvalds { 4021da177e4SLinus Torvalds struct snd_monitor_file *mfile; 4031da177e4SLinus Torvalds 404f18638dcSTakashi Iwai if (!card) 405f18638dcSTakashi Iwai return -EINVAL; 406f18638dcSTakashi Iwai 4071da177e4SLinus Torvalds spin_lock(&card->files_lock); 4081da177e4SLinus Torvalds if (card->shutdown) { 4091da177e4SLinus Torvalds spin_unlock(&card->files_lock); 4101da177e4SLinus Torvalds return 0; 4111da177e4SLinus Torvalds } 4121da177e4SLinus Torvalds card->shutdown = 1; 4131da177e4SLinus Torvalds spin_unlock(&card->files_lock); 4141da177e4SLinus Torvalds 4151da177e4SLinus Torvalds /* phase 1: disable fops (user space) operations for ALSA API */ 416746df948STakashi Iwai mutex_lock(&snd_card_mutex); 4171da177e4SLinus Torvalds snd_cards[card->number] = NULL; 4187bb2491bSTakashi Iwai clear_bit(card->number, snd_cards_lock); 419746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 4201da177e4SLinus Torvalds 4211da177e4SLinus Torvalds /* phase 2: replace file->f_op with special dummy operations */ 4221da177e4SLinus Torvalds 4231da177e4SLinus Torvalds spin_lock(&card->files_lock); 424118dd6bfSTakashi Iwai list_for_each_entry(mfile, &card->files_list, list) { 4251da177e4SLinus Torvalds /* it's critical part, use endless loop */ 4261da177e4SLinus Torvalds /* we have no room to fail */ 427a9edfc60SKarsten Wiese mfile->disconnected_f_op = mfile->file->f_op; 4281da177e4SLinus Torvalds 429a9edfc60SKarsten Wiese spin_lock(&shutdown_lock); 430a9edfc60SKarsten Wiese list_add(&mfile->shutdown_list, &shutdown_files); 431a9edfc60SKarsten Wiese spin_unlock(&shutdown_lock); 4321da177e4SLinus Torvalds 433a9edfc60SKarsten Wiese mfile->file->f_op = &snd_shutdown_f_ops; 434bc9abce0SMiguel Boton fops_get(mfile->file->f_op); 4351da177e4SLinus Torvalds } 4361da177e4SLinus Torvalds spin_unlock(&card->files_lock); 4371da177e4SLinus Torvalds 4381da177e4SLinus Torvalds /* phase 3: notify all connected devices about disconnection */ 4391da177e4SLinus Torvalds /* at this point, they cannot respond to any calls except release() */ 4401da177e4SLinus Torvalds 4418eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_MIXER_OSS) 4421da177e4SLinus Torvalds if (snd_mixer_oss_notify_callback) 4431da177e4SLinus Torvalds snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT); 4441da177e4SLinus Torvalds #endif 4451da177e4SLinus Torvalds 4461da177e4SLinus Torvalds /* notify all devices that we are disconnected */ 447e086e303STakashi Iwai snd_device_disconnect_all(card); 4481da177e4SLinus Torvalds 449746d4a02STakashi Iwai snd_info_card_disconnect(card); 4508bfb181cSTakashi Iwai if (card->registered) { 4518bfb181cSTakashi Iwai device_del(&card->card_dev); 4528bfb181cSTakashi Iwai card->registered = false; 45373d38b13STakashi Iwai } 454f18638dcSTakashi Iwai #ifdef CONFIG_PM 455f18638dcSTakashi Iwai wake_up(&card->power_sleep); 456f18638dcSTakashi Iwai #endif 4571da177e4SLinus Torvalds return 0; 4581da177e4SLinus Torvalds } 4591da177e4SLinus Torvalds 460c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_disconnect); 461c0d3fb39STakashi Iwai 462c461482cSTakashi Iwai static int snd_card_do_free(struct snd_card *card) 4631da177e4SLinus Torvalds { 4648eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_MIXER_OSS) 4651da177e4SLinus Torvalds if (snd_mixer_oss_notify_callback) 4661da177e4SLinus Torvalds snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); 4671da177e4SLinus Torvalds #endif 46872620d60STakashi Iwai snd_device_free_all(card); 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 482eb9c38d5STakashi Iwai /** 483eb9c38d5STakashi Iwai * snd_card_free_when_closed - Disconnect the card, free it later eventually 484eb9c38d5STakashi Iwai * @card: soundcard structure 485eb9c38d5STakashi Iwai * 486eb9c38d5STakashi Iwai * Unlike snd_card_free(), this function doesn't try to release the card 487eb9c38d5STakashi Iwai * resource immediately, but tries to disconnect at first. When the card 488eb9c38d5STakashi Iwai * is still in use, the function returns before freeing the resources. 489eb9c38d5STakashi Iwai * The card resources will be freed when the refcount gets to zero. 490eb9c38d5STakashi Iwai */ 491c461482cSTakashi Iwai int snd_card_free_when_closed(struct snd_card *card) 492c461482cSTakashi Iwai { 493f18638dcSTakashi Iwai int ret = snd_card_disconnect(card); 494c461482cSTakashi Iwai if (ret) 495c461482cSTakashi Iwai return ret; 4968bfb181cSTakashi Iwai put_device(&card->card_dev); 4971da177e4SLinus Torvalds return 0; 4981da177e4SLinus Torvalds } 499f2464064STakashi Iwai EXPORT_SYMBOL(snd_card_free_when_closed); 5001da177e4SLinus Torvalds 501eb9c38d5STakashi Iwai /** 502eb9c38d5STakashi Iwai * snd_card_free - frees given soundcard structure 503eb9c38d5STakashi Iwai * @card: soundcard structure 504eb9c38d5STakashi Iwai * 505eb9c38d5STakashi Iwai * This function releases the soundcard structure and the all assigned 506eb9c38d5STakashi Iwai * devices automatically. That is, you don't have to release the devices 507eb9c38d5STakashi Iwai * by yourself. 508eb9c38d5STakashi Iwai * 509eb9c38d5STakashi Iwai * This function waits until the all resources are properly released. 510eb9c38d5STakashi Iwai * 511eb9c38d5STakashi Iwai * Return: Zero. Frees all associated devices and frees the control 512eb9c38d5STakashi Iwai * interface associated to given soundcard. 513eb9c38d5STakashi Iwai */ 514f2464064STakashi Iwai int snd_card_free(struct snd_card *card) 515f2464064STakashi Iwai { 516f2464064STakashi Iwai struct completion released; 517f2464064STakashi Iwai int ret; 518f2464064STakashi Iwai 519f2464064STakashi Iwai init_completion(&released); 520f2464064STakashi Iwai card->release_completion = &released; 521f2464064STakashi Iwai ret = snd_card_free_when_closed(card); 522f2464064STakashi Iwai if (ret) 523f2464064STakashi Iwai return ret; 524f2464064STakashi Iwai /* wait, until all devices are ready for the free operation */ 525f2464064STakashi Iwai wait_for_completion(&released); 526f2464064STakashi Iwai return 0; 527f2464064STakashi Iwai } 528c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_free); 529c0d3fb39STakashi Iwai 530e7df2a3aSTakashi Iwai /* retrieve the last word of shortname or longname */ 531e7df2a3aSTakashi Iwai static const char *retrieve_id_from_card_name(const char *name) 5321da177e4SLinus Torvalds { 533e7df2a3aSTakashi Iwai const char *spos = name; 534e7df2a3aSTakashi Iwai 535e7df2a3aSTakashi Iwai while (*name) { 536e7df2a3aSTakashi Iwai if (isspace(*name) && isalnum(name[1])) 537e7df2a3aSTakashi Iwai spos = name + 1; 538e7df2a3aSTakashi Iwai name++; 539e7df2a3aSTakashi Iwai } 540e7df2a3aSTakashi Iwai return spos; 541e7df2a3aSTakashi Iwai } 542e7df2a3aSTakashi Iwai 543e7df2a3aSTakashi Iwai /* return true if the given id string doesn't conflict any other card ids */ 544e7df2a3aSTakashi Iwai static bool card_id_ok(struct snd_card *card, const char *id) 545e7df2a3aSTakashi Iwai { 546e7df2a3aSTakashi Iwai int i; 547e7df2a3aSTakashi Iwai if (!snd_info_check_reserved_words(id)) 548e7df2a3aSTakashi Iwai return false; 549e7df2a3aSTakashi Iwai for (i = 0; i < snd_ecards_limit; i++) { 550e7df2a3aSTakashi Iwai if (snd_cards[i] && snd_cards[i] != card && 551e7df2a3aSTakashi Iwai !strcmp(snd_cards[i]->id, id)) 552e7df2a3aSTakashi Iwai return false; 553e7df2a3aSTakashi Iwai } 554e7df2a3aSTakashi Iwai return true; 555e7df2a3aSTakashi Iwai } 556e7df2a3aSTakashi Iwai 557e7df2a3aSTakashi Iwai /* copy to card->id only with valid letters from nid */ 558e7df2a3aSTakashi Iwai static void copy_valid_id_string(struct snd_card *card, const char *src, 559e7df2a3aSTakashi Iwai const char *nid) 560e7df2a3aSTakashi Iwai { 561e7df2a3aSTakashi Iwai char *id = card->id; 562e7df2a3aSTakashi Iwai 563e7df2a3aSTakashi Iwai while (*nid && !isalnum(*nid)) 564e7df2a3aSTakashi Iwai nid++; 565e7df2a3aSTakashi Iwai if (isdigit(*nid)) 566e7df2a3aSTakashi Iwai *id++ = isalpha(*src) ? *src : 'D'; 567e7df2a3aSTakashi Iwai while (*nid && (size_t)(id - card->id) < sizeof(card->id) - 1) { 568e7df2a3aSTakashi Iwai if (isalnum(*nid)) 569e7df2a3aSTakashi Iwai *id++ = *nid; 570e7df2a3aSTakashi Iwai nid++; 571e7df2a3aSTakashi Iwai } 572e7df2a3aSTakashi Iwai *id = 0; 573e7df2a3aSTakashi Iwai } 574e7df2a3aSTakashi Iwai 575e7df2a3aSTakashi Iwai /* Set card->id from the given string 576e7df2a3aSTakashi Iwai * If the string conflicts with other ids, add a suffix to make it unique. 577e7df2a3aSTakashi Iwai */ 578e7df2a3aSTakashi Iwai static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, 579e7df2a3aSTakashi Iwai const char *nid) 580e7df2a3aSTakashi Iwai { 581e7df2a3aSTakashi Iwai int len, loops; 582e7df2a3aSTakashi Iwai bool is_default = false; 58310a8ebbbSJaroslav Kysela char *id; 5841da177e4SLinus Torvalds 585e7df2a3aSTakashi Iwai copy_valid_id_string(card, src, nid); 5861da177e4SLinus Torvalds id = card->id; 5871da177e4SLinus Torvalds 588e7df2a3aSTakashi Iwai again: 589e7df2a3aSTakashi Iwai /* use "Default" for obviously invalid strings 590e7df2a3aSTakashi Iwai * ("card" conflicts with proc directories) 591e7df2a3aSTakashi Iwai */ 592e7df2a3aSTakashi Iwai if (!*id || !strncmp(id, "card", 4)) { 593d8e4f9aeSTakashi Iwai strcpy(id, "Default"); 594e7df2a3aSTakashi Iwai is_default = true; 5951da177e4SLinus Torvalds } 5961da177e4SLinus Torvalds 5978edbb198STakashi Iwai len = strlen(id); 598e7df2a3aSTakashi Iwai for (loops = 0; loops < SNDRV_CARDS; loops++) { 5998edbb198STakashi Iwai char *spos; 6008edbb198STakashi Iwai char sfxstr[5]; /* "_012" */ 6018edbb198STakashi Iwai int sfxlen; 6028edbb198STakashi Iwai 603e7df2a3aSTakashi Iwai if (card_id_ok(card, id)) 604e7df2a3aSTakashi Iwai return; /* OK */ 605e7df2a3aSTakashi Iwai 6068edbb198STakashi Iwai /* Add _XYZ suffix */ 6078edbb198STakashi Iwai sprintf(sfxstr, "_%X", loops + 1); 6088edbb198STakashi Iwai sfxlen = strlen(sfxstr); 6098edbb198STakashi Iwai if (len + sfxlen >= sizeof(card->id)) 6108edbb198STakashi Iwai spos = id + sizeof(card->id) - sfxlen - 1; 611d001544dSClemens Ladisch else 6128edbb198STakashi Iwai spos = id + len; 6138edbb198STakashi Iwai strcpy(spos, sfxstr); 6141da177e4SLinus Torvalds } 615e7df2a3aSTakashi Iwai /* fallback to the default id */ 616e7df2a3aSTakashi Iwai if (!is_default) { 617e7df2a3aSTakashi Iwai *id = 0; 618e7df2a3aSTakashi Iwai goto again; 619e7df2a3aSTakashi Iwai } 620e7df2a3aSTakashi Iwai /* last resort... */ 621f2f9307aSTakashi Iwai dev_err(card->dev, "unable to set card id (%s)\n", id); 622e7df2a3aSTakashi Iwai if (card->proc_root->name) 62397f44f56STakashi Iwai strlcpy(card->id, card->proc_root->name, sizeof(card->id)); 6241da177e4SLinus Torvalds } 6251da177e4SLinus Torvalds 626872c7820SMark Brown /** 627872c7820SMark Brown * snd_card_set_id - set card identification name 628872c7820SMark Brown * @card: soundcard structure 629872c7820SMark Brown * @nid: new identification string 630872c7820SMark Brown * 631872c7820SMark Brown * This function sets the card identification and checks for name 632872c7820SMark Brown * collisions. 633872c7820SMark Brown */ 634872c7820SMark Brown void snd_card_set_id(struct snd_card *card, const char *nid) 635872c7820SMark Brown { 6365fdc18d9SJaroslav Kysela /* check if user specified own card->id */ 6375fdc18d9SJaroslav Kysela if (card->id[0] != '\0') 6385fdc18d9SJaroslav Kysela return; 6395fdc18d9SJaroslav Kysela mutex_lock(&snd_card_mutex); 640e7df2a3aSTakashi Iwai snd_card_set_id_no_lock(card, nid, nid); 6415fdc18d9SJaroslav Kysela mutex_unlock(&snd_card_mutex); 642872c7820SMark Brown } 64310a8ebbbSJaroslav Kysela EXPORT_SYMBOL(snd_card_set_id); 64410a8ebbbSJaroslav Kysela 6459fb6198eSJaroslav Kysela static ssize_t 6469fb6198eSJaroslav Kysela card_id_show_attr(struct device *dev, 6479fb6198eSJaroslav Kysela struct device_attribute *attr, char *buf) 6489fb6198eSJaroslav Kysela { 649b203dbabSTakashi Iwai struct snd_card *card = container_of(dev, struct snd_card, card_dev); 650b203dbabSTakashi Iwai return snprintf(buf, PAGE_SIZE, "%s\n", card->id); 6519fb6198eSJaroslav Kysela } 6529fb6198eSJaroslav Kysela 6539fb6198eSJaroslav Kysela static ssize_t 6549fb6198eSJaroslav Kysela card_id_store_attr(struct device *dev, struct device_attribute *attr, 6559fb6198eSJaroslav Kysela const char *buf, size_t count) 6569fb6198eSJaroslav Kysela { 657b203dbabSTakashi Iwai struct snd_card *card = container_of(dev, struct snd_card, card_dev); 6589fb6198eSJaroslav Kysela char buf1[sizeof(card->id)]; 6599fb6198eSJaroslav Kysela size_t copy = count > sizeof(card->id) - 1 ? 6609fb6198eSJaroslav Kysela sizeof(card->id) - 1 : count; 6619fb6198eSJaroslav Kysela size_t idx; 6629fb6198eSJaroslav Kysela int c; 6639fb6198eSJaroslav Kysela 6649fb6198eSJaroslav Kysela for (idx = 0; idx < copy; idx++) { 6659fb6198eSJaroslav Kysela c = buf[idx]; 6669fb6198eSJaroslav Kysela if (!isalnum(c) && c != '_' && c != '-') 6679fb6198eSJaroslav Kysela return -EINVAL; 6689fb6198eSJaroslav Kysela } 6699fb6198eSJaroslav Kysela memcpy(buf1, buf, copy); 6709fb6198eSJaroslav Kysela buf1[copy] = '\0'; 6719fb6198eSJaroslav Kysela mutex_lock(&snd_card_mutex); 672e7df2a3aSTakashi Iwai if (!card_id_ok(NULL, buf1)) { 6739fb6198eSJaroslav Kysela mutex_unlock(&snd_card_mutex); 6749fb6198eSJaroslav Kysela return -EEXIST; 6759fb6198eSJaroslav Kysela } 6769fb6198eSJaroslav Kysela strcpy(card->id, buf1); 677c2eb9c4eSJaroslav Kysela snd_info_card_id_change(card); 6789fb6198eSJaroslav Kysela mutex_unlock(&snd_card_mutex); 6799fb6198eSJaroslav Kysela 6809fb6198eSJaroslav Kysela return count; 6819fb6198eSJaroslav Kysela } 6829fb6198eSJaroslav Kysela 68334356dbdSTakashi Iwai static DEVICE_ATTR(id, S_IRUGO | S_IWUSR, card_id_show_attr, card_id_store_attr); 6849fb6198eSJaroslav Kysela 6859fb6198eSJaroslav Kysela static ssize_t 6869fb6198eSJaroslav Kysela card_number_show_attr(struct device *dev, 6879fb6198eSJaroslav Kysela struct device_attribute *attr, char *buf) 6889fb6198eSJaroslav Kysela { 689b203dbabSTakashi Iwai struct snd_card *card = container_of(dev, struct snd_card, card_dev); 690b203dbabSTakashi Iwai return snprintf(buf, PAGE_SIZE, "%i\n", card->number); 6919fb6198eSJaroslav Kysela } 6929fb6198eSJaroslav Kysela 69334356dbdSTakashi Iwai static DEVICE_ATTR(number, S_IRUGO, card_number_show_attr, NULL); 69434356dbdSTakashi Iwai 69534356dbdSTakashi Iwai static struct attribute *card_dev_attrs[] = { 69634356dbdSTakashi Iwai &dev_attr_id.attr, 69734356dbdSTakashi Iwai &dev_attr_number.attr, 69834356dbdSTakashi Iwai NULL 69934356dbdSTakashi Iwai }; 70034356dbdSTakashi Iwai 7016bbc7fedSTakashi Iwai static const struct attribute_group card_dev_attr_group = { 70234356dbdSTakashi Iwai .attrs = card_dev_attrs, 70334356dbdSTakashi Iwai }; 70434356dbdSTakashi Iwai 7056bbc7fedSTakashi Iwai /** 7066bbc7fedSTakashi Iwai * snd_card_add_dev_attr - Append a new sysfs attribute group to card 7076bbc7fedSTakashi Iwai * @card: card instance 7086bbc7fedSTakashi Iwai * @group: attribute group to append 7096bbc7fedSTakashi Iwai */ 7106bbc7fedSTakashi Iwai int snd_card_add_dev_attr(struct snd_card *card, 7116bbc7fedSTakashi Iwai const struct attribute_group *group) 7126bbc7fedSTakashi Iwai { 7136bbc7fedSTakashi Iwai int i; 7146bbc7fedSTakashi Iwai 7156bbc7fedSTakashi Iwai /* loop for (arraysize-1) here to keep NULL at the last entry */ 7166bbc7fedSTakashi Iwai for (i = 0; i < ARRAY_SIZE(card->dev_groups) - 1; i++) { 7176bbc7fedSTakashi Iwai if (!card->dev_groups[i]) { 7186bbc7fedSTakashi Iwai card->dev_groups[i] = group; 7196bbc7fedSTakashi Iwai return 0; 7206bbc7fedSTakashi Iwai } 7216bbc7fedSTakashi Iwai } 7226bbc7fedSTakashi Iwai 7236bbc7fedSTakashi Iwai dev_err(card->dev, "Too many groups assigned\n"); 7246bbc7fedSTakashi Iwai return -ENOSPC; 72534356dbdSTakashi Iwai }; 7266bbc7fedSTakashi Iwai EXPORT_SYMBOL_GPL(snd_card_add_dev_attr); 7279fb6198eSJaroslav Kysela 7281da177e4SLinus Torvalds /** 7291da177e4SLinus Torvalds * snd_card_register - register the soundcard 7301da177e4SLinus Torvalds * @card: soundcard structure 7311da177e4SLinus Torvalds * 7321da177e4SLinus Torvalds * This function registers all the devices assigned to the soundcard. 7331da177e4SLinus Torvalds * Until calling this, the ALSA control interface is blocked from the 7341da177e4SLinus Torvalds * external accesses. Thus, you should call this function at the end 7351da177e4SLinus Torvalds * of the initialization of the card. 7361da177e4SLinus Torvalds * 737eb7c06e8SYacine Belkadi * Return: Zero otherwise a negative error code if the registration failed. 7381da177e4SLinus Torvalds */ 739512bbd6aSTakashi Iwai int snd_card_register(struct snd_card *card) 7401da177e4SLinus Torvalds { 7411da177e4SLinus Torvalds int err; 7421da177e4SLinus Torvalds 7437eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 7447eaa943cSTakashi Iwai return -EINVAL; 74539aba963SKay Sievers 7468bfb181cSTakashi Iwai if (!card->registered) { 7478bfb181cSTakashi Iwai err = device_add(&card->card_dev); 7488bfb181cSTakashi Iwai if (err < 0) 7498bfb181cSTakashi Iwai return err; 7508bfb181cSTakashi Iwai card->registered = true; 751d80f19faSGreg Kroah-Hartman } 75239aba963SKay Sievers 7531da177e4SLinus Torvalds if ((err = snd_device_register_all(card)) < 0) 7541da177e4SLinus Torvalds return err; 755746df948STakashi Iwai mutex_lock(&snd_card_mutex); 7561da177e4SLinus Torvalds if (snd_cards[card->number]) { 7571da177e4SLinus Torvalds /* already registered */ 758746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 7591da177e4SLinus Torvalds return 0; 7601da177e4SLinus Torvalds } 761e7df2a3aSTakashi Iwai if (*card->id) { 762e7df2a3aSTakashi Iwai /* make a unique id name from the given string */ 763e7df2a3aSTakashi Iwai char tmpid[sizeof(card->id)]; 764e7df2a3aSTakashi Iwai memcpy(tmpid, card->id, sizeof(card->id)); 765e7df2a3aSTakashi Iwai snd_card_set_id_no_lock(card, tmpid, tmpid); 766e7df2a3aSTakashi Iwai } else { 767e7df2a3aSTakashi Iwai /* create an id from either shortname or longname */ 768e7df2a3aSTakashi Iwai const char *src; 769e7df2a3aSTakashi Iwai src = *card->shortname ? card->shortname : card->longname; 770e7df2a3aSTakashi Iwai snd_card_set_id_no_lock(card, src, 771e7df2a3aSTakashi Iwai retrieve_id_from_card_name(src)); 772e7df2a3aSTakashi Iwai } 7731da177e4SLinus Torvalds snd_cards[card->number] = card; 774746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 775e28563ccSTakashi Iwai init_info_for_card(card); 7768eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_MIXER_OSS) 7771da177e4SLinus Torvalds if (snd_mixer_oss_notify_callback) 7781da177e4SLinus Torvalds snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER); 7791da177e4SLinus Torvalds #endif 7801da177e4SLinus Torvalds return 0; 7811da177e4SLinus Torvalds } 7821da177e4SLinus Torvalds 783c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_register); 784c0d3fb39STakashi Iwai 785e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS 786a381a7a6STakashi Iwai static void snd_card_info_read(struct snd_info_entry *entry, 787a381a7a6STakashi Iwai struct snd_info_buffer *buffer) 7881da177e4SLinus Torvalds { 7891da177e4SLinus Torvalds int idx, count; 790512bbd6aSTakashi Iwai struct snd_card *card; 7911da177e4SLinus Torvalds 7921da177e4SLinus Torvalds for (idx = count = 0; idx < SNDRV_CARDS; idx++) { 793746df948STakashi Iwai mutex_lock(&snd_card_mutex); 7941da177e4SLinus Torvalds if ((card = snd_cards[idx]) != NULL) { 7951da177e4SLinus Torvalds count++; 796d001544dSClemens Ladisch snd_iprintf(buffer, "%2i [%-15s]: %s - %s\n", 7971da177e4SLinus Torvalds idx, 7981da177e4SLinus Torvalds card->id, 7991da177e4SLinus Torvalds card->driver, 8001da177e4SLinus Torvalds card->shortname); 8011da177e4SLinus Torvalds snd_iprintf(buffer, " %s\n", 8021da177e4SLinus Torvalds card->longname); 8031da177e4SLinus Torvalds } 804746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 8051da177e4SLinus Torvalds } 8061da177e4SLinus Torvalds if (!count) 8071da177e4SLinus Torvalds snd_iprintf(buffer, "--- no soundcards ---\n"); 8081da177e4SLinus Torvalds } 8091da177e4SLinus Torvalds 810e28563ccSTakashi Iwai #ifdef CONFIG_SND_OSSEMUL 811512bbd6aSTakashi Iwai void snd_card_info_read_oss(struct snd_info_buffer *buffer) 8121da177e4SLinus Torvalds { 8131da177e4SLinus Torvalds int idx, count; 814512bbd6aSTakashi Iwai struct snd_card *card; 8151da177e4SLinus Torvalds 8161da177e4SLinus Torvalds for (idx = count = 0; idx < SNDRV_CARDS; idx++) { 817746df948STakashi Iwai mutex_lock(&snd_card_mutex); 8181da177e4SLinus Torvalds if ((card = snd_cards[idx]) != NULL) { 8191da177e4SLinus Torvalds count++; 8201da177e4SLinus Torvalds snd_iprintf(buffer, "%s\n", card->longname); 8211da177e4SLinus Torvalds } 822746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 8231da177e4SLinus Torvalds } 8241da177e4SLinus Torvalds if (!count) { 8251da177e4SLinus Torvalds snd_iprintf(buffer, "--- no soundcards ---\n"); 8261da177e4SLinus Torvalds } 8271da177e4SLinus Torvalds } 8281da177e4SLinus Torvalds 8291da177e4SLinus Torvalds #endif 8301da177e4SLinus Torvalds 8311da177e4SLinus Torvalds #ifdef MODULE 832512bbd6aSTakashi Iwai static void snd_card_module_info_read(struct snd_info_entry *entry, 833512bbd6aSTakashi Iwai struct snd_info_buffer *buffer) 8341da177e4SLinus Torvalds { 8351da177e4SLinus Torvalds int idx; 836512bbd6aSTakashi Iwai struct snd_card *card; 8371da177e4SLinus Torvalds 8381da177e4SLinus Torvalds for (idx = 0; idx < SNDRV_CARDS; idx++) { 839746df948STakashi Iwai mutex_lock(&snd_card_mutex); 8401da177e4SLinus Torvalds if ((card = snd_cards[idx]) != NULL) 841d001544dSClemens Ladisch snd_iprintf(buffer, "%2i %s\n", 842d001544dSClemens Ladisch idx, card->module->name); 843746df948STakashi Iwai mutex_unlock(&snd_card_mutex); 8441da177e4SLinus Torvalds } 8451da177e4SLinus Torvalds } 8461da177e4SLinus Torvalds #endif 8471da177e4SLinus Torvalds 8481da177e4SLinus Torvalds int __init snd_card_info_init(void) 8491da177e4SLinus Torvalds { 850512bbd6aSTakashi Iwai struct snd_info_entry *entry; 8511da177e4SLinus Torvalds 8521da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL); 8537c22f1aaSTakashi Iwai if (! entry) 8547c22f1aaSTakashi Iwai return -ENOMEM; 8551da177e4SLinus Torvalds entry->c.text.read = snd_card_info_read; 856b591b6e9STakashi Iwai if (snd_info_register(entry) < 0) 857b591b6e9STakashi Iwai return -ENOMEM; /* freed in error path */ 8581da177e4SLinus Torvalds 8591da177e4SLinus Torvalds #ifdef MODULE 8601da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL); 861b591b6e9STakashi Iwai if (!entry) 862b591b6e9STakashi Iwai return -ENOMEM; 8631da177e4SLinus Torvalds entry->c.text.read = snd_card_module_info_read; 8641da177e4SLinus Torvalds if (snd_info_register(entry) < 0) 865b591b6e9STakashi Iwai return -ENOMEM; /* freed in error path */ 8661da177e4SLinus Torvalds #endif 8671da177e4SLinus Torvalds 8681da177e4SLinus Torvalds return 0; 8691da177e4SLinus Torvalds } 870e28563ccSTakashi Iwai #endif /* CONFIG_PROC_FS */ 871e28563ccSTakashi Iwai 8721da177e4SLinus Torvalds /** 8731da177e4SLinus Torvalds * snd_component_add - add a component string 8741da177e4SLinus Torvalds * @card: soundcard structure 8751da177e4SLinus Torvalds * @component: the component id string 8761da177e4SLinus Torvalds * 8771da177e4SLinus Torvalds * This function adds the component id string to the supported list. 8781da177e4SLinus Torvalds * The component can be referred from the alsa-lib. 8791da177e4SLinus Torvalds * 880eb7c06e8SYacine Belkadi * Return: Zero otherwise a negative error code. 8811da177e4SLinus Torvalds */ 8821da177e4SLinus Torvalds 883512bbd6aSTakashi Iwai int snd_component_add(struct snd_card *card, const char *component) 8841da177e4SLinus Torvalds { 8851da177e4SLinus Torvalds char *ptr; 8861da177e4SLinus Torvalds int len = strlen(component); 8871da177e4SLinus Torvalds 8881da177e4SLinus Torvalds ptr = strstr(card->components, component); 8891da177e4SLinus Torvalds if (ptr != NULL) { 8901da177e4SLinus Torvalds if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */ 8911da177e4SLinus Torvalds return 1; 8921da177e4SLinus Torvalds } 8931da177e4SLinus Torvalds if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) { 8941da177e4SLinus Torvalds snd_BUG(); 8951da177e4SLinus Torvalds return -ENOMEM; 8961da177e4SLinus Torvalds } 8971da177e4SLinus Torvalds if (card->components[0] != '\0') 8981da177e4SLinus Torvalds strcat(card->components, " "); 8991da177e4SLinus Torvalds strcat(card->components, component); 9001da177e4SLinus Torvalds return 0; 9011da177e4SLinus Torvalds } 9021da177e4SLinus Torvalds 903c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_component_add); 904c0d3fb39STakashi Iwai 9051da177e4SLinus Torvalds /** 9061da177e4SLinus Torvalds * snd_card_file_add - add the file to the file list of the card 9071da177e4SLinus Torvalds * @card: soundcard structure 9081da177e4SLinus Torvalds * @file: file pointer 9091da177e4SLinus Torvalds * 9101da177e4SLinus Torvalds * This function adds the file to the file linked-list of the card. 9111da177e4SLinus Torvalds * This linked-list is used to keep tracking the connection state, 9121da177e4SLinus Torvalds * and to avoid the release of busy resources by hotplug. 9131da177e4SLinus Torvalds * 914eb7c06e8SYacine Belkadi * Return: zero or a negative error code. 9151da177e4SLinus Torvalds */ 916512bbd6aSTakashi Iwai int snd_card_file_add(struct snd_card *card, struct file *file) 9171da177e4SLinus Torvalds { 9181da177e4SLinus Torvalds struct snd_monitor_file *mfile; 9191da177e4SLinus Torvalds 9201da177e4SLinus Torvalds mfile = kmalloc(sizeof(*mfile), GFP_KERNEL); 9211da177e4SLinus Torvalds if (mfile == NULL) 9221da177e4SLinus Torvalds return -ENOMEM; 9231da177e4SLinus Torvalds mfile->file = file; 924a9edfc60SKarsten Wiese mfile->disconnected_f_op = NULL; 925a45e3d6bSTakashi Iwai INIT_LIST_HEAD(&mfile->shutdown_list); 9261da177e4SLinus Torvalds spin_lock(&card->files_lock); 9271da177e4SLinus Torvalds if (card->shutdown) { 9281da177e4SLinus Torvalds spin_unlock(&card->files_lock); 9291da177e4SLinus Torvalds kfree(mfile); 9301da177e4SLinus Torvalds return -ENODEV; 9311da177e4SLinus Torvalds } 932118dd6bfSTakashi Iwai list_add(&mfile->list, &card->files_list); 933f2464064STakashi Iwai get_device(&card->card_dev); 9341da177e4SLinus Torvalds spin_unlock(&card->files_lock); 9351da177e4SLinus Torvalds return 0; 9361da177e4SLinus Torvalds } 9371da177e4SLinus Torvalds 938c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_file_add); 939c0d3fb39STakashi Iwai 9401da177e4SLinus Torvalds /** 9411da177e4SLinus Torvalds * snd_card_file_remove - remove the file from the file list 9421da177e4SLinus Torvalds * @card: soundcard structure 9431da177e4SLinus Torvalds * @file: file pointer 9441da177e4SLinus Torvalds * 9451da177e4SLinus Torvalds * This function removes the file formerly added to the card via 9461da177e4SLinus Torvalds * snd_card_file_add() function. 9472b29b13cSTakashi Iwai * If all files are removed and snd_card_free_when_closed() was 9482b29b13cSTakashi Iwai * called beforehand, it processes the pending release of 9492b29b13cSTakashi Iwai * resources. 9501da177e4SLinus Torvalds * 951eb7c06e8SYacine Belkadi * Return: Zero or a negative error code. 9521da177e4SLinus Torvalds */ 953512bbd6aSTakashi Iwai int snd_card_file_remove(struct snd_card *card, struct file *file) 9541da177e4SLinus Torvalds { 955118dd6bfSTakashi Iwai struct snd_monitor_file *mfile, *found = NULL; 9561da177e4SLinus Torvalds 9571da177e4SLinus Torvalds spin_lock(&card->files_lock); 958118dd6bfSTakashi Iwai list_for_each_entry(mfile, &card->files_list, list) { 9591da177e4SLinus Torvalds if (mfile->file == file) { 960118dd6bfSTakashi Iwai list_del(&mfile->list); 961a45e3d6bSTakashi Iwai spin_lock(&shutdown_lock); 962a45e3d6bSTakashi Iwai list_del(&mfile->shutdown_list); 963a45e3d6bSTakashi Iwai spin_unlock(&shutdown_lock); 964118dd6bfSTakashi Iwai if (mfile->disconnected_f_op) 965118dd6bfSTakashi Iwai fops_put(mfile->disconnected_f_op); 966118dd6bfSTakashi Iwai found = mfile; 9671da177e4SLinus Torvalds break; 9681da177e4SLinus Torvalds } 9691da177e4SLinus Torvalds } 970c461482cSTakashi Iwai spin_unlock(&card->files_lock); 971118dd6bfSTakashi Iwai if (!found) { 972f2f9307aSTakashi Iwai dev_err(card->dev, "card file remove problem (%p)\n", file); 9731da177e4SLinus Torvalds return -ENOENT; 9741da177e4SLinus Torvalds } 975118dd6bfSTakashi Iwai kfree(found); 976f2464064STakashi Iwai put_device(&card->card_dev); 9771da177e4SLinus Torvalds return 0; 9781da177e4SLinus Torvalds } 9791da177e4SLinus Torvalds 980c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_file_remove); 981c0d3fb39STakashi Iwai 9821da177e4SLinus Torvalds #ifdef CONFIG_PM 9831da177e4SLinus Torvalds /** 9841da177e4SLinus Torvalds * snd_power_wait - wait until the power-state is changed. 9851da177e4SLinus Torvalds * @card: soundcard structure 9861da177e4SLinus Torvalds * @power_state: expected power state 9871da177e4SLinus Torvalds * 9881da177e4SLinus Torvalds * Waits until the power-state is changed. 9891da177e4SLinus Torvalds * 990eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code. 991eb7c06e8SYacine Belkadi * 9921da177e4SLinus Torvalds * Note: the power lock must be active before call. 9931da177e4SLinus Torvalds */ 994cbac4b0cSTakashi Iwai int snd_power_wait(struct snd_card *card, unsigned int power_state) 9951da177e4SLinus Torvalds { 9961da177e4SLinus Torvalds wait_queue_t wait; 9971da177e4SLinus Torvalds int result = 0; 9981da177e4SLinus Torvalds 9991da177e4SLinus Torvalds /* fastpath */ 10001da177e4SLinus Torvalds if (snd_power_get_state(card) == power_state) 10011da177e4SLinus Torvalds return 0; 10021da177e4SLinus Torvalds init_waitqueue_entry(&wait, current); 10031da177e4SLinus Torvalds add_wait_queue(&card->power_sleep, &wait); 10041da177e4SLinus Torvalds while (1) { 10051da177e4SLinus Torvalds if (card->shutdown) { 10061da177e4SLinus Torvalds result = -ENODEV; 10071da177e4SLinus Torvalds break; 10081da177e4SLinus Torvalds } 10091da177e4SLinus Torvalds if (snd_power_get_state(card) == power_state) 10101da177e4SLinus Torvalds break; 10111da177e4SLinus Torvalds set_current_state(TASK_UNINTERRUPTIBLE); 10121da177e4SLinus Torvalds snd_power_unlock(card); 10131da177e4SLinus Torvalds schedule_timeout(30 * HZ); 10141da177e4SLinus Torvalds snd_power_lock(card); 10151da177e4SLinus Torvalds } 10161da177e4SLinus Torvalds remove_wait_queue(&card->power_sleep, &wait); 10171da177e4SLinus Torvalds return result; 10181da177e4SLinus Torvalds } 10191da177e4SLinus Torvalds 1020c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_power_wait); 10211da177e4SLinus Torvalds #endif /* CONFIG_PM */ 1022