| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | *  Initialization routines | 
|  | 3 | *  Copyright (c) by Jaroslav Kysela <perex@suse.cz> | 
|  | 4 | * | 
|  | 5 | * | 
|  | 6 | *   This program is free software; you can redistribute it and/or modify | 
|  | 7 | *   it under the terms of the GNU General Public License as published by | 
|  | 8 | *   the Free Software Foundation; either version 2 of the License, or | 
|  | 9 | *   (at your option) any later version. | 
|  | 10 | * | 
|  | 11 | *   This program is distributed in the hope that it will be useful, | 
|  | 12 | *   but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 13 | *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 14 | *   GNU General Public License for more details. | 
|  | 15 | * | 
|  | 16 | *   You should have received a copy of the GNU General Public License | 
|  | 17 | *   along with this program; if not, write to the Free Software | 
|  | 18 | *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA | 
|  | 19 | * | 
|  | 20 | */ | 
|  | 21 |  | 
|  | 22 | #include <sound/driver.h> | 
|  | 23 | #include <linux/init.h> | 
|  | 24 | #include <linux/sched.h> | 
|  | 25 | #include <linux/file.h> | 
|  | 26 | #include <linux/slab.h> | 
|  | 27 | #include <linux/time.h> | 
|  | 28 | #include <linux/ctype.h> | 
|  | 29 | #include <linux/pci.h> | 
|  | 30 | #include <linux/pm.h> | 
| Russell King | d052d1b | 2005-10-29 19:07:23 +0100 | [diff] [blame] | 31 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 32 | #include <sound/core.h> | 
|  | 33 | #include <sound/control.h> | 
|  | 34 | #include <sound/info.h> | 
|  | 35 |  | 
|  | 36 | struct snd_shutdown_f_ops { | 
|  | 37 | struct file_operations f_ops; | 
|  | 38 | struct snd_shutdown_f_ops *next; | 
|  | 39 | }; | 
|  | 40 |  | 
|  | 41 | unsigned int snd_cards_lock = 0;	/* locked for registering/using */ | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 42 | struct snd_card *snd_cards[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = NULL}; | 
| Takashi Iwai | c0d3fb3 | 2006-04-28 15:13:39 +0200 | [diff] [blame^] | 43 | EXPORT_SYMBOL(snd_cards); | 
|  | 44 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 45 | DEFINE_RWLOCK(snd_card_rwlock); | 
|  | 46 |  | 
|  | 47 | #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 48 | int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag); | 
| Takashi Iwai | c0d3fb3 | 2006-04-28 15:13:39 +0200 | [diff] [blame^] | 49 | EXPORT_SYMBOL(snd_mixer_oss_notify_callback); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 50 | #endif | 
|  | 51 |  | 
| Takashi Iwai | e28563c | 2005-12-01 10:42:42 +0100 | [diff] [blame] | 52 | #ifdef CONFIG_PROC_FS | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 53 | static void snd_card_id_read(struct snd_info_entry *entry, | 
|  | 54 | struct snd_info_buffer *buffer) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 55 | { | 
|  | 56 | snd_iprintf(buffer, "%s\n", entry->card->id); | 
|  | 57 | } | 
|  | 58 |  | 
| Takashi Iwai | e28563c | 2005-12-01 10:42:42 +0100 | [diff] [blame] | 59 | static inline int init_info_for_card(struct snd_card *card) | 
|  | 60 | { | 
|  | 61 | int err; | 
|  | 62 | struct snd_info_entry *entry; | 
|  | 63 |  | 
|  | 64 | if ((err = snd_info_card_register(card)) < 0) { | 
|  | 65 | snd_printd("unable to create card info\n"); | 
|  | 66 | return err; | 
|  | 67 | } | 
|  | 68 | if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) { | 
|  | 69 | snd_printd("unable to create card entry\n"); | 
|  | 70 | return err; | 
|  | 71 | } | 
|  | 72 | entry->c.text.read_size = PAGE_SIZE; | 
|  | 73 | entry->c.text.read = snd_card_id_read; | 
|  | 74 | if (snd_info_register(entry) < 0) { | 
|  | 75 | snd_info_free_entry(entry); | 
|  | 76 | entry = NULL; | 
|  | 77 | } | 
|  | 78 | card->proc_id = entry; | 
|  | 79 | return 0; | 
|  | 80 | } | 
|  | 81 | #else /* !CONFIG_PROC_FS */ | 
|  | 82 | #define init_info_for_card(card) | 
|  | 83 | #endif | 
|  | 84 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 85 | static void snd_card_free_thread(void * __card); | 
|  | 86 |  | 
|  | 87 | /** | 
|  | 88 | *  snd_card_new - create and initialize a soundcard structure | 
|  | 89 | *  @idx: card index (address) [0 ... (SNDRV_CARDS-1)] | 
|  | 90 | *  @xid: card identification (ASCII string) | 
|  | 91 | *  @module: top level module for locking | 
|  | 92 | *  @extra_size: allocate this extra size after the main soundcard structure | 
|  | 93 | * | 
|  | 94 | *  Creates and initializes a soundcard structure. | 
|  | 95 | * | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 96 | *  Returns kmallocated snd_card structure. Creates the ALSA control interface | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 97 | *  (which is blocked until snd_card_register function is called). | 
|  | 98 | */ | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 99 | struct snd_card *snd_card_new(int idx, const char *xid, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 100 | struct module *module, int extra_size) | 
|  | 101 | { | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 102 | struct snd_card *card; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 103 | int err; | 
|  | 104 |  | 
|  | 105 | if (extra_size < 0) | 
|  | 106 | extra_size = 0; | 
| Takashi Iwai | ca2c096 | 2005-09-09 14:20:23 +0200 | [diff] [blame] | 107 | card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 108 | if (card == NULL) | 
|  | 109 | return NULL; | 
|  | 110 | if (xid) { | 
|  | 111 | if (!snd_info_check_reserved_words(xid)) | 
|  | 112 | goto __error; | 
|  | 113 | strlcpy(card->id, xid, sizeof(card->id)); | 
|  | 114 | } | 
|  | 115 | err = 0; | 
|  | 116 | write_lock(&snd_card_rwlock); | 
|  | 117 | if (idx < 0) { | 
|  | 118 | int idx2; | 
|  | 119 | for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) | 
|  | 120 | if (~snd_cards_lock & idx & 1<<idx2) { | 
|  | 121 | idx = idx2; | 
|  | 122 | if (idx >= snd_ecards_limit) | 
|  | 123 | snd_ecards_limit = idx + 1; | 
|  | 124 | break; | 
|  | 125 | } | 
|  | 126 | } else if (idx < snd_ecards_limit) { | 
|  | 127 | if (snd_cards_lock & (1 << idx)) | 
|  | 128 | err = -ENODEV;	/* invalid */ | 
|  | 129 | } else if (idx < SNDRV_CARDS) | 
|  | 130 | snd_ecards_limit = idx + 1; /* increase the limit */ | 
|  | 131 | else | 
|  | 132 | err = -ENODEV; | 
|  | 133 | if (idx < 0 || err < 0) { | 
|  | 134 | write_unlock(&snd_card_rwlock); | 
|  | 135 | snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i)\n", idx, snd_ecards_limit - 1); | 
|  | 136 | goto __error; | 
|  | 137 | } | 
|  | 138 | snd_cards_lock |= 1 << idx;		/* lock it */ | 
|  | 139 | write_unlock(&snd_card_rwlock); | 
|  | 140 | card->number = idx; | 
|  | 141 | card->module = module; | 
|  | 142 | INIT_LIST_HEAD(&card->devices); | 
|  | 143 | init_rwsem(&card->controls_rwsem); | 
|  | 144 | rwlock_init(&card->ctl_files_rwlock); | 
|  | 145 | INIT_LIST_HEAD(&card->controls); | 
|  | 146 | INIT_LIST_HEAD(&card->ctl_files); | 
|  | 147 | spin_lock_init(&card->files_lock); | 
|  | 148 | init_waitqueue_head(&card->shutdown_sleep); | 
|  | 149 | INIT_WORK(&card->free_workq, snd_card_free_thread, card); | 
|  | 150 | #ifdef CONFIG_PM | 
| Ingo Molnar | 1a60d4c | 2006-01-16 16:29:08 +0100 | [diff] [blame] | 151 | mutex_init(&card->power_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 152 | init_waitqueue_head(&card->power_sleep); | 
|  | 153 | #endif | 
|  | 154 | /* the control interface cannot be accessed from the user space until */ | 
|  | 155 | /* snd_cards_bitmask and snd_cards are set with snd_card_register */ | 
|  | 156 | if ((err = snd_ctl_create(card)) < 0) { | 
|  | 157 | snd_printd("unable to register control minors\n"); | 
|  | 158 | goto __error; | 
|  | 159 | } | 
|  | 160 | if ((err = snd_info_card_create(card)) < 0) { | 
|  | 161 | snd_printd("unable to create card info\n"); | 
|  | 162 | goto __error_ctl; | 
|  | 163 | } | 
|  | 164 | if (extra_size > 0) | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 165 | card->private_data = (char *)card + sizeof(struct snd_card); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 166 | return card; | 
|  | 167 |  | 
|  | 168 | __error_ctl: | 
|  | 169 | snd_device_free_all(card, SNDRV_DEV_CMD_PRE); | 
|  | 170 | __error: | 
|  | 171 | kfree(card); | 
|  | 172 | return NULL; | 
|  | 173 | } | 
|  | 174 |  | 
| Takashi Iwai | c0d3fb3 | 2006-04-28 15:13:39 +0200 | [diff] [blame^] | 175 | EXPORT_SYMBOL(snd_card_new); | 
|  | 176 |  | 
| Clemens Ladisch | b3b0abe | 2006-03-03 14:08:43 +0100 | [diff] [blame] | 177 | static loff_t snd_disconnect_llseek(struct file *file, loff_t offset, int orig) | 
|  | 178 | { | 
|  | 179 | return -ENODEV; | 
|  | 180 | } | 
|  | 181 |  | 
|  | 182 | static ssize_t snd_disconnect_read(struct file *file, char __user *buf, | 
|  | 183 | size_t count, loff_t *offset) | 
|  | 184 | { | 
|  | 185 | return -ENODEV; | 
|  | 186 | } | 
|  | 187 |  | 
|  | 188 | static ssize_t snd_disconnect_write(struct file *file, const char __user *buf, | 
|  | 189 | size_t count, loff_t *offset) | 
|  | 190 | { | 
|  | 191 | return -ENODEV; | 
|  | 192 | } | 
|  | 193 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 194 | static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait) | 
|  | 195 | { | 
|  | 196 | return POLLERR | POLLNVAL; | 
|  | 197 | } | 
|  | 198 |  | 
| Clemens Ladisch | b3b0abe | 2006-03-03 14:08:43 +0100 | [diff] [blame] | 199 | static long snd_disconnect_ioctl(struct file *file, | 
|  | 200 | unsigned int cmd, unsigned long arg) | 
|  | 201 | { | 
|  | 202 | return -ENODEV; | 
|  | 203 | } | 
|  | 204 |  | 
|  | 205 | static int snd_disconnect_mmap(struct file *file, struct vm_area_struct *vma) | 
|  | 206 | { | 
|  | 207 | return -ENODEV; | 
|  | 208 | } | 
|  | 209 |  | 
|  | 210 | static int snd_disconnect_fasync(int fd, struct file *file, int on) | 
|  | 211 | { | 
|  | 212 | return -ENODEV; | 
|  | 213 | } | 
|  | 214 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 215 | /** | 
|  | 216 | *  snd_card_disconnect - disconnect all APIs from the file-operations (user space) | 
|  | 217 | *  @card: soundcard structure | 
|  | 218 | * | 
|  | 219 | *  Disconnects all APIs from the file-operations (user space). | 
|  | 220 | * | 
|  | 221 | *  Returns zero, otherwise a negative error code. | 
|  | 222 | * | 
|  | 223 | *  Note: The current implementation replaces all active file->f_op with special | 
|  | 224 | *        dummy file operations (they do nothing except release). | 
|  | 225 | */ | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 226 | int snd_card_disconnect(struct snd_card *card) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 227 | { | 
|  | 228 | struct snd_monitor_file *mfile; | 
|  | 229 | struct file *file; | 
|  | 230 | struct snd_shutdown_f_ops *s_f_ops; | 
| Arjan van de Ven | 99ac48f | 2006-03-28 01:56:41 -0800 | [diff] [blame] | 231 | struct file_operations *f_ops; | 
|  | 232 | const struct file_operations *old_f_ops; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 233 | int err; | 
|  | 234 |  | 
|  | 235 | spin_lock(&card->files_lock); | 
|  | 236 | if (card->shutdown) { | 
|  | 237 | spin_unlock(&card->files_lock); | 
|  | 238 | return 0; | 
|  | 239 | } | 
|  | 240 | card->shutdown = 1; | 
|  | 241 | spin_unlock(&card->files_lock); | 
|  | 242 |  | 
|  | 243 | /* phase 1: disable fops (user space) operations for ALSA API */ | 
|  | 244 | write_lock(&snd_card_rwlock); | 
|  | 245 | snd_cards[card->number] = NULL; | 
|  | 246 | write_unlock(&snd_card_rwlock); | 
|  | 247 |  | 
|  | 248 | /* phase 2: replace file->f_op with special dummy operations */ | 
|  | 249 |  | 
|  | 250 | spin_lock(&card->files_lock); | 
|  | 251 | mfile = card->files; | 
|  | 252 | while (mfile) { | 
|  | 253 | file = mfile->file; | 
|  | 254 |  | 
|  | 255 | /* it's critical part, use endless loop */ | 
|  | 256 | /* we have no room to fail */ | 
|  | 257 | s_f_ops = kmalloc(sizeof(struct snd_shutdown_f_ops), GFP_ATOMIC); | 
|  | 258 | if (s_f_ops == NULL) | 
|  | 259 | panic("Atomic allocation failed for snd_shutdown_f_ops!"); | 
|  | 260 |  | 
|  | 261 | f_ops = &s_f_ops->f_ops; | 
|  | 262 |  | 
|  | 263 | memset(f_ops, 0, sizeof(*f_ops)); | 
|  | 264 | f_ops->owner = file->f_op->owner; | 
|  | 265 | f_ops->release = file->f_op->release; | 
| Clemens Ladisch | b3b0abe | 2006-03-03 14:08:43 +0100 | [diff] [blame] | 266 | f_ops->llseek = snd_disconnect_llseek; | 
|  | 267 | f_ops->read = snd_disconnect_read; | 
|  | 268 | f_ops->write = snd_disconnect_write; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 269 | f_ops->poll = snd_disconnect_poll; | 
| Clemens Ladisch | b3b0abe | 2006-03-03 14:08:43 +0100 | [diff] [blame] | 270 | f_ops->unlocked_ioctl = snd_disconnect_ioctl; | 
|  | 271 | #ifdef CONFIG_COMPAT | 
|  | 272 | f_ops->compat_ioctl = snd_disconnect_ioctl; | 
|  | 273 | #endif | 
|  | 274 | f_ops->mmap = snd_disconnect_mmap; | 
|  | 275 | f_ops->fasync = snd_disconnect_fasync; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 276 |  | 
|  | 277 | s_f_ops->next = card->s_f_ops; | 
|  | 278 | card->s_f_ops = s_f_ops; | 
|  | 279 |  | 
|  | 280 | f_ops = fops_get(f_ops); | 
|  | 281 |  | 
|  | 282 | old_f_ops = file->f_op; | 
|  | 283 | file->f_op = f_ops;	/* must be atomic */ | 
|  | 284 | fops_put(old_f_ops); | 
|  | 285 |  | 
|  | 286 | mfile = mfile->next; | 
|  | 287 | } | 
|  | 288 | spin_unlock(&card->files_lock); | 
|  | 289 |  | 
|  | 290 | /* phase 3: notify all connected devices about disconnection */ | 
|  | 291 | /* at this point, they cannot respond to any calls except release() */ | 
|  | 292 |  | 
|  | 293 | #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) | 
|  | 294 | if (snd_mixer_oss_notify_callback) | 
|  | 295 | snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT); | 
|  | 296 | #endif | 
|  | 297 |  | 
|  | 298 | /* notify all devices that we are disconnected */ | 
|  | 299 | err = snd_device_disconnect_all(card); | 
|  | 300 | if (err < 0) | 
|  | 301 | snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number); | 
|  | 302 |  | 
|  | 303 | return 0; | 
|  | 304 | } | 
|  | 305 |  | 
| Takashi Iwai | c0d3fb3 | 2006-04-28 15:13:39 +0200 | [diff] [blame^] | 306 | EXPORT_SYMBOL(snd_card_disconnect); | 
|  | 307 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 308 | /** | 
|  | 309 | *  snd_card_free - frees given soundcard structure | 
|  | 310 | *  @card: soundcard structure | 
|  | 311 | * | 
|  | 312 | *  This function releases the soundcard structure and the all assigned | 
|  | 313 | *  devices automatically.  That is, you don't have to release the devices | 
|  | 314 | *  by yourself. | 
|  | 315 | * | 
|  | 316 | *  Returns zero. Frees all associated devices and frees the control | 
|  | 317 | *  interface associated to given soundcard. | 
|  | 318 | */ | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 319 | int snd_card_free(struct snd_card *card) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 320 | { | 
|  | 321 | struct snd_shutdown_f_ops *s_f_ops; | 
|  | 322 |  | 
|  | 323 | if (card == NULL) | 
|  | 324 | return -EINVAL; | 
|  | 325 | write_lock(&snd_card_rwlock); | 
|  | 326 | snd_cards[card->number] = NULL; | 
|  | 327 | write_unlock(&snd_card_rwlock); | 
|  | 328 |  | 
|  | 329 | #ifdef CONFIG_PM | 
|  | 330 | wake_up(&card->power_sleep); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 331 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 332 | /* wait, until all devices are ready for the free operation */ | 
|  | 333 | wait_event(card->shutdown_sleep, card->files == NULL); | 
|  | 334 |  | 
|  | 335 | #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) | 
|  | 336 | if (snd_mixer_oss_notify_callback) | 
|  | 337 | snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); | 
|  | 338 | #endif | 
|  | 339 | if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) { | 
|  | 340 | snd_printk(KERN_ERR "unable to free all devices (pre)\n"); | 
|  | 341 | /* Fatal, but this situation should never occur */ | 
|  | 342 | } | 
|  | 343 | if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) { | 
|  | 344 | snd_printk(KERN_ERR "unable to free all devices (normal)\n"); | 
|  | 345 | /* Fatal, but this situation should never occur */ | 
|  | 346 | } | 
|  | 347 | if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) { | 
|  | 348 | snd_printk(KERN_ERR "unable to free all devices (post)\n"); | 
|  | 349 | /* Fatal, but this situation should never occur */ | 
|  | 350 | } | 
|  | 351 | if (card->private_free) | 
|  | 352 | card->private_free(card); | 
| Takashi Iwai | e28563c | 2005-12-01 10:42:42 +0100 | [diff] [blame] | 353 | snd_info_unregister(card->proc_id); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 354 | if (snd_info_card_free(card) < 0) { | 
|  | 355 | snd_printk(KERN_WARNING "unable to free card info\n"); | 
|  | 356 | /* Not fatal error */ | 
|  | 357 | } | 
|  | 358 | while (card->s_f_ops) { | 
|  | 359 | s_f_ops = card->s_f_ops; | 
|  | 360 | card->s_f_ops = s_f_ops->next; | 
|  | 361 | kfree(s_f_ops); | 
|  | 362 | } | 
|  | 363 | write_lock(&snd_card_rwlock); | 
|  | 364 | snd_cards_lock &= ~(1 << card->number); | 
|  | 365 | write_unlock(&snd_card_rwlock); | 
|  | 366 | kfree(card); | 
|  | 367 | return 0; | 
|  | 368 | } | 
|  | 369 |  | 
| Takashi Iwai | c0d3fb3 | 2006-04-28 15:13:39 +0200 | [diff] [blame^] | 370 | EXPORT_SYMBOL(snd_card_free); | 
|  | 371 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 372 | static void snd_card_free_thread(void * __card) | 
|  | 373 | { | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 374 | struct snd_card *card = __card; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 375 | struct module * module = card->module; | 
|  | 376 |  | 
|  | 377 | if (!try_module_get(module)) { | 
|  | 378 | snd_printk(KERN_ERR "unable to lock toplevel module for card %i in free thread\n", card->number); | 
|  | 379 | module = NULL; | 
|  | 380 | } | 
|  | 381 |  | 
|  | 382 | snd_card_free(card); | 
|  | 383 |  | 
|  | 384 | module_put(module); | 
|  | 385 | } | 
|  | 386 |  | 
|  | 387 | /** | 
|  | 388 | *  snd_card_free_in_thread - call snd_card_free() in thread | 
|  | 389 | *  @card: soundcard structure | 
|  | 390 | * | 
|  | 391 | *  This function schedules the call of snd_card_free() function in a | 
|  | 392 | *  work queue.  When all devices are released (non-busy), the work | 
|  | 393 | *  is woken up and calls snd_card_free(). | 
|  | 394 | * | 
|  | 395 | *  When a card can be disconnected at any time by hotplug service, | 
|  | 396 | *  this function should be used in disconnect (or detach) callback | 
|  | 397 | *  instead of calling snd_card_free() directly. | 
|  | 398 | * | 
|  | 399 | *  Returns - zero otherwise a negative error code if the start of thread failed. | 
|  | 400 | */ | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 401 | int snd_card_free_in_thread(struct snd_card *card) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 402 | { | 
|  | 403 | if (card->files == NULL) { | 
|  | 404 | snd_card_free(card); | 
|  | 405 | return 0; | 
|  | 406 | } | 
|  | 407 |  | 
|  | 408 | if (schedule_work(&card->free_workq)) | 
|  | 409 | return 0; | 
|  | 410 |  | 
|  | 411 | snd_printk(KERN_ERR "schedule_work() failed in snd_card_free_in_thread for card %i\n", card->number); | 
|  | 412 | /* try to free the structure immediately */ | 
|  | 413 | snd_card_free(card); | 
|  | 414 | return -EFAULT; | 
|  | 415 | } | 
|  | 416 |  | 
| Takashi Iwai | c0d3fb3 | 2006-04-28 15:13:39 +0200 | [diff] [blame^] | 417 | EXPORT_SYMBOL(snd_card_free_in_thread); | 
|  | 418 |  | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 419 | static void choose_default_id(struct snd_card *card) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 420 | { | 
| Clemens Ladisch | d001544 | 2005-11-20 14:09:05 +0100 | [diff] [blame] | 421 | int i, len, idx_flag = 0, loops = SNDRV_CARDS; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 422 | char *id, *spos; | 
|  | 423 |  | 
|  | 424 | id = spos = card->shortname; | 
|  | 425 | while (*id != '\0') { | 
|  | 426 | if (*id == ' ') | 
|  | 427 | spos = id + 1; | 
|  | 428 | id++; | 
|  | 429 | } | 
|  | 430 | id = card->id; | 
|  | 431 | while (*spos != '\0' && !isalnum(*spos)) | 
|  | 432 | spos++; | 
|  | 433 | if (isdigit(*spos)) | 
|  | 434 | *id++ = isalpha(card->shortname[0]) ? card->shortname[0] : 'D'; | 
|  | 435 | while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) { | 
|  | 436 | if (isalnum(*spos)) | 
|  | 437 | *id++ = *spos; | 
|  | 438 | spos++; | 
|  | 439 | } | 
|  | 440 | *id = '\0'; | 
|  | 441 |  | 
|  | 442 | id = card->id; | 
|  | 443 |  | 
|  | 444 | if (*id == '\0') | 
|  | 445 | strcpy(id, "default"); | 
|  | 446 |  | 
|  | 447 | while (1) { | 
|  | 448 | if (loops-- == 0) { | 
|  | 449 | snd_printk(KERN_ERR "unable to choose default card id (%s)\n", id); | 
|  | 450 | strcpy(card->id, card->proc_root->name); | 
|  | 451 | return; | 
|  | 452 | } | 
|  | 453 | if (!snd_info_check_reserved_words(id)) | 
|  | 454 | goto __change; | 
|  | 455 | for (i = 0; i < snd_ecards_limit; i++) { | 
|  | 456 | if (snd_cards[i] && !strcmp(snd_cards[i]->id, id)) | 
|  | 457 | goto __change; | 
|  | 458 | } | 
|  | 459 | break; | 
|  | 460 |  | 
|  | 461 | __change: | 
|  | 462 | len = strlen(id); | 
| Clemens Ladisch | d001544 | 2005-11-20 14:09:05 +0100 | [diff] [blame] | 463 | if (idx_flag) { | 
|  | 464 | if (id[len-1] != '9') | 
|  | 465 | id[len-1]++; | 
|  | 466 | else | 
|  | 467 | id[len-1] = 'A'; | 
|  | 468 | } else if ((size_t)len <= sizeof(card->id) - 3) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 469 | strcat(id, "_1"); | 
|  | 470 | idx_flag++; | 
|  | 471 | } else { | 
|  | 472 | spos = id + len - 2; | 
|  | 473 | if ((size_t)len <= sizeof(card->id) - 2) | 
|  | 474 | spos++; | 
|  | 475 | *spos++ = '_'; | 
|  | 476 | *spos++ = '1'; | 
|  | 477 | *spos++ = '\0'; | 
|  | 478 | idx_flag++; | 
|  | 479 | } | 
|  | 480 | } | 
|  | 481 | } | 
|  | 482 |  | 
|  | 483 | /** | 
|  | 484 | *  snd_card_register - register the soundcard | 
|  | 485 | *  @card: soundcard structure | 
|  | 486 | * | 
|  | 487 | *  This function registers all the devices assigned to the soundcard. | 
|  | 488 | *  Until calling this, the ALSA control interface is blocked from the | 
|  | 489 | *  external accesses.  Thus, you should call this function at the end | 
|  | 490 | *  of the initialization of the card. | 
|  | 491 | * | 
|  | 492 | *  Returns zero otherwise a negative error code if the registrain failed. | 
|  | 493 | */ | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 494 | int snd_card_register(struct snd_card *card) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 495 | { | 
|  | 496 | int err; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 497 |  | 
| Takashi Iwai | 7c22f1a | 2005-10-10 11:46:31 +0200 | [diff] [blame] | 498 | snd_assert(card != NULL, return -EINVAL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 499 | if ((err = snd_device_register_all(card)) < 0) | 
|  | 500 | return err; | 
|  | 501 | write_lock(&snd_card_rwlock); | 
|  | 502 | if (snd_cards[card->number]) { | 
|  | 503 | /* already registered */ | 
|  | 504 | write_unlock(&snd_card_rwlock); | 
|  | 505 | return 0; | 
|  | 506 | } | 
|  | 507 | if (card->id[0] == '\0') | 
|  | 508 | choose_default_id(card); | 
|  | 509 | snd_cards[card->number] = card; | 
|  | 510 | write_unlock(&snd_card_rwlock); | 
| Takashi Iwai | e28563c | 2005-12-01 10:42:42 +0100 | [diff] [blame] | 511 | init_info_for_card(card); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 512 | #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) | 
|  | 513 | if (snd_mixer_oss_notify_callback) | 
|  | 514 | snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER); | 
|  | 515 | #endif | 
|  | 516 | return 0; | 
|  | 517 | } | 
|  | 518 |  | 
| Takashi Iwai | c0d3fb3 | 2006-04-28 15:13:39 +0200 | [diff] [blame^] | 519 | EXPORT_SYMBOL(snd_card_register); | 
|  | 520 |  | 
| Takashi Iwai | e28563c | 2005-12-01 10:42:42 +0100 | [diff] [blame] | 521 | #ifdef CONFIG_PROC_FS | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 522 | static struct snd_info_entry *snd_card_info_entry = NULL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 523 |  | 
| Takashi Iwai | a381a7a | 2005-11-17 15:55:49 +0100 | [diff] [blame] | 524 | static void snd_card_info_read(struct snd_info_entry *entry, | 
|  | 525 | struct snd_info_buffer *buffer) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 526 | { | 
|  | 527 | int idx, count; | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 528 | struct snd_card *card; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 529 |  | 
|  | 530 | for (idx = count = 0; idx < SNDRV_CARDS; idx++) { | 
|  | 531 | read_lock(&snd_card_rwlock); | 
|  | 532 | if ((card = snd_cards[idx]) != NULL) { | 
|  | 533 | count++; | 
| Clemens Ladisch | d001544 | 2005-11-20 14:09:05 +0100 | [diff] [blame] | 534 | snd_iprintf(buffer, "%2i [%-15s]: %s - %s\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 535 | idx, | 
|  | 536 | card->id, | 
|  | 537 | card->driver, | 
|  | 538 | card->shortname); | 
| Clemens Ladisch | d001544 | 2005-11-20 14:09:05 +0100 | [diff] [blame] | 539 | snd_iprintf(buffer, "                      %s\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 540 | card->longname); | 
|  | 541 | } | 
|  | 542 | read_unlock(&snd_card_rwlock); | 
|  | 543 | } | 
|  | 544 | if (!count) | 
|  | 545 | snd_iprintf(buffer, "--- no soundcards ---\n"); | 
|  | 546 | } | 
|  | 547 |  | 
| Takashi Iwai | e28563c | 2005-12-01 10:42:42 +0100 | [diff] [blame] | 548 | #ifdef CONFIG_SND_OSSEMUL | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 549 |  | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 550 | void snd_card_info_read_oss(struct snd_info_buffer *buffer) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 551 | { | 
|  | 552 | int idx, count; | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 553 | struct snd_card *card; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 554 |  | 
|  | 555 | for (idx = count = 0; idx < SNDRV_CARDS; idx++) { | 
|  | 556 | read_lock(&snd_card_rwlock); | 
|  | 557 | if ((card = snd_cards[idx]) != NULL) { | 
|  | 558 | count++; | 
|  | 559 | snd_iprintf(buffer, "%s\n", card->longname); | 
|  | 560 | } | 
|  | 561 | read_unlock(&snd_card_rwlock); | 
|  | 562 | } | 
|  | 563 | if (!count) { | 
|  | 564 | snd_iprintf(buffer, "--- no soundcards ---\n"); | 
|  | 565 | } | 
|  | 566 | } | 
|  | 567 |  | 
|  | 568 | #endif | 
|  | 569 |  | 
|  | 570 | #ifdef MODULE | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 571 | static struct snd_info_entry *snd_card_module_info_entry; | 
|  | 572 | static void snd_card_module_info_read(struct snd_info_entry *entry, | 
|  | 573 | struct snd_info_buffer *buffer) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 574 | { | 
|  | 575 | int idx; | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 576 | struct snd_card *card; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 577 |  | 
|  | 578 | for (idx = 0; idx < SNDRV_CARDS; idx++) { | 
|  | 579 | read_lock(&snd_card_rwlock); | 
|  | 580 | if ((card = snd_cards[idx]) != NULL) | 
| Clemens Ladisch | d001544 | 2005-11-20 14:09:05 +0100 | [diff] [blame] | 581 | snd_iprintf(buffer, "%2i %s\n", | 
|  | 582 | idx, card->module->name); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 583 | read_unlock(&snd_card_rwlock); | 
|  | 584 | } | 
|  | 585 | } | 
|  | 586 | #endif | 
|  | 587 |  | 
|  | 588 | int __init snd_card_info_init(void) | 
|  | 589 | { | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 590 | struct snd_info_entry *entry; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 591 |  | 
|  | 592 | entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL); | 
| Takashi Iwai | 7c22f1a | 2005-10-10 11:46:31 +0200 | [diff] [blame] | 593 | if (! entry) | 
|  | 594 | return -ENOMEM; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 595 | entry->c.text.read_size = PAGE_SIZE; | 
|  | 596 | entry->c.text.read = snd_card_info_read; | 
|  | 597 | if (snd_info_register(entry) < 0) { | 
|  | 598 | snd_info_free_entry(entry); | 
|  | 599 | return -ENOMEM; | 
|  | 600 | } | 
|  | 601 | snd_card_info_entry = entry; | 
|  | 602 |  | 
|  | 603 | #ifdef MODULE | 
|  | 604 | entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL); | 
|  | 605 | if (entry) { | 
|  | 606 | entry->c.text.read_size = PAGE_SIZE; | 
|  | 607 | entry->c.text.read = snd_card_module_info_read; | 
|  | 608 | if (snd_info_register(entry) < 0) | 
|  | 609 | snd_info_free_entry(entry); | 
|  | 610 | else | 
|  | 611 | snd_card_module_info_entry = entry; | 
|  | 612 | } | 
|  | 613 | #endif | 
|  | 614 |  | 
|  | 615 | return 0; | 
|  | 616 | } | 
|  | 617 |  | 
|  | 618 | int __exit snd_card_info_done(void) | 
|  | 619 | { | 
| Takashi Iwai | e28563c | 2005-12-01 10:42:42 +0100 | [diff] [blame] | 620 | snd_info_unregister(snd_card_info_entry); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 621 | #ifdef MODULE | 
| Takashi Iwai | e28563c | 2005-12-01 10:42:42 +0100 | [diff] [blame] | 622 | snd_info_unregister(snd_card_module_info_entry); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 623 | #endif | 
|  | 624 | return 0; | 
|  | 625 | } | 
|  | 626 |  | 
| Takashi Iwai | e28563c | 2005-12-01 10:42:42 +0100 | [diff] [blame] | 627 | #endif /* CONFIG_PROC_FS */ | 
|  | 628 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 629 | /** | 
|  | 630 | *  snd_component_add - add a component string | 
|  | 631 | *  @card: soundcard structure | 
|  | 632 | *  @component: the component id string | 
|  | 633 | * | 
|  | 634 | *  This function adds the component id string to the supported list. | 
|  | 635 | *  The component can be referred from the alsa-lib. | 
|  | 636 | * | 
|  | 637 | *  Returns zero otherwise a negative error code. | 
|  | 638 | */ | 
|  | 639 |  | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 640 | int snd_component_add(struct snd_card *card, const char *component) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 641 | { | 
|  | 642 | char *ptr; | 
|  | 643 | int len = strlen(component); | 
|  | 644 |  | 
|  | 645 | ptr = strstr(card->components, component); | 
|  | 646 | if (ptr != NULL) { | 
|  | 647 | if (ptr[len] == '\0' || ptr[len] == ' ')	/* already there */ | 
|  | 648 | return 1; | 
|  | 649 | } | 
|  | 650 | if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) { | 
|  | 651 | snd_BUG(); | 
|  | 652 | return -ENOMEM; | 
|  | 653 | } | 
|  | 654 | if (card->components[0] != '\0') | 
|  | 655 | strcat(card->components, " "); | 
|  | 656 | strcat(card->components, component); | 
|  | 657 | return 0; | 
|  | 658 | } | 
|  | 659 |  | 
| Takashi Iwai | c0d3fb3 | 2006-04-28 15:13:39 +0200 | [diff] [blame^] | 660 | EXPORT_SYMBOL(snd_component_add); | 
|  | 661 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 662 | /** | 
|  | 663 | *  snd_card_file_add - add the file to the file list of the card | 
|  | 664 | *  @card: soundcard structure | 
|  | 665 | *  @file: file pointer | 
|  | 666 | * | 
|  | 667 | *  This function adds the file to the file linked-list of the card. | 
|  | 668 | *  This linked-list is used to keep tracking the connection state, | 
|  | 669 | *  and to avoid the release of busy resources by hotplug. | 
|  | 670 | * | 
|  | 671 | *  Returns zero or a negative error code. | 
|  | 672 | */ | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 673 | int snd_card_file_add(struct snd_card *card, struct file *file) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 674 | { | 
|  | 675 | struct snd_monitor_file *mfile; | 
|  | 676 |  | 
|  | 677 | mfile = kmalloc(sizeof(*mfile), GFP_KERNEL); | 
|  | 678 | if (mfile == NULL) | 
|  | 679 | return -ENOMEM; | 
|  | 680 | mfile->file = file; | 
|  | 681 | mfile->next = NULL; | 
|  | 682 | spin_lock(&card->files_lock); | 
|  | 683 | if (card->shutdown) { | 
|  | 684 | spin_unlock(&card->files_lock); | 
|  | 685 | kfree(mfile); | 
|  | 686 | return -ENODEV; | 
|  | 687 | } | 
|  | 688 | mfile->next = card->files; | 
|  | 689 | card->files = mfile; | 
|  | 690 | spin_unlock(&card->files_lock); | 
|  | 691 | return 0; | 
|  | 692 | } | 
|  | 693 |  | 
| Takashi Iwai | c0d3fb3 | 2006-04-28 15:13:39 +0200 | [diff] [blame^] | 694 | EXPORT_SYMBOL(snd_card_file_add); | 
|  | 695 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 696 | /** | 
|  | 697 | *  snd_card_file_remove - remove the file from the file list | 
|  | 698 | *  @card: soundcard structure | 
|  | 699 | *  @file: file pointer | 
|  | 700 | * | 
|  | 701 | *  This function removes the file formerly added to the card via | 
|  | 702 | *  snd_card_file_add() function. | 
|  | 703 | *  If all files are removed and the release of the card is | 
|  | 704 | *  scheduled, it will wake up the the thread to call snd_card_free() | 
|  | 705 | *  (see snd_card_free_in_thread() function). | 
|  | 706 | * | 
|  | 707 | *  Returns zero or a negative error code. | 
|  | 708 | */ | 
| Takashi Iwai | 512bbd6 | 2005-11-17 13:51:18 +0100 | [diff] [blame] | 709 | int snd_card_file_remove(struct snd_card *card, struct file *file) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 710 | { | 
|  | 711 | struct snd_monitor_file *mfile, *pfile = NULL; | 
|  | 712 |  | 
|  | 713 | spin_lock(&card->files_lock); | 
|  | 714 | mfile = card->files; | 
|  | 715 | while (mfile) { | 
|  | 716 | if (mfile->file == file) { | 
|  | 717 | if (pfile) | 
|  | 718 | pfile->next = mfile->next; | 
|  | 719 | else | 
|  | 720 | card->files = mfile->next; | 
|  | 721 | break; | 
|  | 722 | } | 
|  | 723 | pfile = mfile; | 
|  | 724 | mfile = mfile->next; | 
|  | 725 | } | 
|  | 726 | spin_unlock(&card->files_lock); | 
|  | 727 | if (card->files == NULL) | 
|  | 728 | wake_up(&card->shutdown_sleep); | 
|  | 729 | if (!mfile) { | 
|  | 730 | snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file); | 
|  | 731 | return -ENOENT; | 
|  | 732 | } | 
|  | 733 | kfree(mfile); | 
|  | 734 | return 0; | 
|  | 735 | } | 
|  | 736 |  | 
| Takashi Iwai | c0d3fb3 | 2006-04-28 15:13:39 +0200 | [diff] [blame^] | 737 | EXPORT_SYMBOL(snd_card_file_remove); | 
|  | 738 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 739 | #ifdef CONFIG_PM | 
|  | 740 | /** | 
|  | 741 | *  snd_power_wait - wait until the power-state is changed. | 
|  | 742 | *  @card: soundcard structure | 
|  | 743 | *  @power_state: expected power state | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 744 | * | 
|  | 745 | *  Waits until the power-state is changed. | 
|  | 746 | * | 
|  | 747 | *  Note: the power lock must be active before call. | 
|  | 748 | */ | 
| Takashi Iwai | cbac4b0 | 2006-03-27 12:38:07 +0200 | [diff] [blame] | 749 | int snd_power_wait(struct snd_card *card, unsigned int power_state) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 750 | { | 
|  | 751 | wait_queue_t wait; | 
|  | 752 | int result = 0; | 
|  | 753 |  | 
|  | 754 | /* fastpath */ | 
|  | 755 | if (snd_power_get_state(card) == power_state) | 
|  | 756 | return 0; | 
|  | 757 | init_waitqueue_entry(&wait, current); | 
|  | 758 | add_wait_queue(&card->power_sleep, &wait); | 
|  | 759 | while (1) { | 
|  | 760 | if (card->shutdown) { | 
|  | 761 | result = -ENODEV; | 
|  | 762 | break; | 
|  | 763 | } | 
|  | 764 | if (snd_power_get_state(card) == power_state) | 
|  | 765 | break; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 766 | set_current_state(TASK_UNINTERRUPTIBLE); | 
|  | 767 | snd_power_unlock(card); | 
|  | 768 | schedule_timeout(30 * HZ); | 
|  | 769 | snd_power_lock(card); | 
|  | 770 | } | 
|  | 771 | remove_wait_queue(&card->power_sleep, &wait); | 
|  | 772 | return result; | 
|  | 773 | } | 
|  | 774 |  | 
| Takashi Iwai | c0d3fb3 | 2006-04-28 15:13:39 +0200 | [diff] [blame^] | 775 | EXPORT_SYMBOL(snd_power_wait); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 776 | #endif /* CONFIG_PM */ |