Blame view

sound/core/init.c 23.7 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
  /*
   *  Initialization routines
c1017a4cd   Jaroslav Kysela   [ALSA] Changed Ja...
3
   *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
   *
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   *   This program is distributed in the hope that it will be useful,
   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   *   along with this program; if not, write to the Free Software
   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
  #include <linux/init.h>
  #include <linux/sched.h>
da155d5b4   Paul Gortmaker   sound: Add module...
23
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
27
  #include <linux/file.h>
  #include <linux/slab.h>
  #include <linux/time.h>
  #include <linux/ctype.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
  #include <linux/pm.h>
d052d1bef   Russell King   Create platform_d...
29

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
  #include <sound/core.h>
  #include <sound/control.h>
  #include <sound/info.h>
82a783f4b   Takashi Iwai   ALSA: Remove stru...
33
34
35
36
37
38
39
  /* monitor files for graceful shutdown (hotplug) */
  struct snd_monitor_file {
  	struct file *file;
  	const struct file_operations *disconnected_f_op;
  	struct list_head shutdown_list;	/* still need to shutdown */
  	struct list_head list;	/* link of monitor files */
  };
a9edfc602   Karsten Wiese   [ALSA] Handle fil...
40
41
  static DEFINE_SPINLOCK(shutdown_lock);
  static LIST_HEAD(shutdown_files);
9c2e08c59   Arjan van de Ven   [PATCH] mark stru...
42
  static const struct file_operations snd_shutdown_f_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43

6581f4e74   Takashi Iwai   [ALSA] Remove zer...
44
45
  static unsigned int snd_cards_lock;	/* locked for registering/using */
  struct snd_card *snd_cards[SNDRV_CARDS];
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
46
  EXPORT_SYMBOL(snd_cards);
746df9489   Takashi Iwai   [ALSA] Fix rwlock...
47
  static DEFINE_MUTEX(snd_card_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48

304cd07f9   Takashi Iwai   [ALSA] Introduce ...
49
50
51
  static char *slots[SNDRV_CARDS];
  module_param_array(slots, charp, NULL, 0444);
  MODULE_PARM_DESC(slots, "Module names assigned to the slots.");
a93bbaa77   Takashi Iwai   [ALSA] Improve th...
52
  /* return non-zero if the given index is reserved for the given
304cd07f9   Takashi Iwai   [ALSA] Introduce ...
53
54
   * module via slots option
   */
a93bbaa77   Takashi Iwai   [ALSA] Improve th...
55
  static int module_slot_match(struct module *module, int idx)
304cd07f9   Takashi Iwai   [ALSA] Introduce ...
56
  {
a93bbaa77   Takashi Iwai   [ALSA] Improve th...
57
  	int match = 1;
304cd07f9   Takashi Iwai   [ALSA] Introduce ...
58
  #ifdef MODULE
a93bbaa77   Takashi Iwai   [ALSA] Improve th...
59
  	const char *s1, *s2;
304cd07f9   Takashi Iwai   [ALSA] Introduce ...
60
61
  	if (!module || !module->name || !slots[idx])
  		return 0;
a93bbaa77   Takashi Iwai   [ALSA] Improve th...
62
63
64
65
66
67
68
  
  	s1 = module->name;
  	s2 = slots[idx];
  	if (*s2 == '!') {
  		match = 0; /* negative match */
  		s2++;
  	}
304cd07f9   Takashi Iwai   [ALSA] Introduce ...
69
70
71
72
73
74
75
76
77
78
79
  	/* compare module name strings
  	 * hyphens are handled as equivalent with underscore
  	 */
  	for (;;) {
  		char c1 = *s1++;
  		char c2 = *s2++;
  		if (c1 == '-')
  			c1 = '_';
  		if (c2 == '-')
  			c2 = '_';
  		if (c1 != c2)
a93bbaa77   Takashi Iwai   [ALSA] Improve th...
80
  			return !match;
304cd07f9   Takashi Iwai   [ALSA] Introduce ...
81
82
83
  		if (!c1)
  			break;
  	}
a93bbaa77   Takashi Iwai   [ALSA] Improve th...
84
85
  #endif /* MODULE */
  	return match;
304cd07f9   Takashi Iwai   [ALSA] Introduce ...
86
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
512bbd6a8   Takashi Iwai   [ALSA] Remove xxx...
88
  int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
89
  EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
  #endif
e28563cce   Takashi Iwai   [ALSA] Optimize f...
91
  #ifdef CONFIG_PROC_FS
512bbd6a8   Takashi Iwai   [ALSA] Remove xxx...
92
93
  static void snd_card_id_read(struct snd_info_entry *entry,
  			     struct snd_info_buffer *buffer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
96
97
  {
  	snd_iprintf(buffer, "%s
  ", entry->card->id);
  }
e28563cce   Takashi Iwai   [ALSA] Optimize f...
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
  static inline int init_info_for_card(struct snd_card *card)
  {
  	int err;
  	struct snd_info_entry *entry;
  
  	if ((err = snd_info_card_register(card)) < 0) {
  		snd_printd("unable to create card info
  ");
  		return err;
  	}
  	if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) {
  		snd_printd("unable to create card entry
  ");
  		return err;
  	}
e28563cce   Takashi Iwai   [ALSA] Optimize f...
113
114
115
116
117
118
119
120
121
122
123
  	entry->c.text.read = snd_card_id_read;
  	if (snd_info_register(entry) < 0) {
  		snd_info_free_entry(entry);
  		entry = NULL;
  	}
  	card->proc_id = entry;
  	return 0;
  }
  #else /* !CONFIG_PROC_FS */
  #define init_info_for_card(card)
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
  /**
53fb1e635   Takashi Iwai   ALSA: Introduce s...
125
   *  snd_card_create - create and initialize a soundcard structure
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
127
128
129
   *  @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
   *  @xid: card identification (ASCII string)
   *  @module: top level module for locking
   *  @extra_size: allocate this extra size after the main soundcard structure
53fb1e635   Takashi Iwai   ALSA: Introduce s...
130
   *  @card_ret: the pointer to store the created card instance
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
132
133
   *
   *  Creates and initializes a soundcard structure.
   *
53fb1e635   Takashi Iwai   ALSA: Introduce s...
134
135
136
137
138
   *  The function allocates snd_card instance via kzalloc with the given
   *  space for the driver to use freely.  The allocated struct is stored
   *  in the given card_ret pointer.
   *
   *  Returns zero if successful or a negative error code.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
   */
53fb1e635   Takashi Iwai   ALSA: Introduce s...
140
141
142
  int snd_card_create(int idx, const char *xid,
  		    struct module *module, int extra_size,
  		    struct snd_card **card_ret)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
  {
512bbd6a8   Takashi Iwai   [ALSA] Remove xxx...
144
  	struct snd_card *card;
a93bbaa77   Takashi Iwai   [ALSA] Improve th...
145
  	int err, idx2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146

53fb1e635   Takashi Iwai   ALSA: Introduce s...
147
148
149
  	if (snd_BUG_ON(!card_ret))
  		return -EINVAL;
  	*card_ret = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
151
  	if (extra_size < 0)
  		extra_size = 0;
ca2c09665   Takashi Iwai   [ALSA] Replace wi...
152
  	card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
53fb1e635   Takashi Iwai   ALSA: Introduce s...
153
154
  	if (!card)
  		return -ENOMEM;
10a8ebbb0   Jaroslav Kysela   ALSA: Core - add ...
155
  	if (xid)
5fdc18d93   Jaroslav Kysela   ALSA: Core - clea...
156
  		strlcpy(card->id, xid, sizeof(card->id));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
  	err = 0;
746df9489   Takashi Iwai   [ALSA] Fix rwlock...
158
  	mutex_lock(&snd_card_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
  	if (idx < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
  		for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
5c33dd70b   Oliver Neukum   [ALSA] cleanup an...
161
  			/* idx == -1 == 0xffff means: take any free slot */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
  			if (~snd_cards_lock & idx & 1<<idx2) {
a93bbaa77   Takashi Iwai   [ALSA] Improve th...
163
164
165
166
167
168
169
170
171
172
173
174
175
176
  				if (module_slot_match(module, idx2)) {
  					idx = idx2;
  					break;
  				}
  			}
  	}
  	if (idx < 0) {
  		for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
  			/* idx == -1 == 0xffff means: take any free slot */
  			if (~snd_cards_lock & idx & 1<<idx2) {
  				if (!slots[idx2] || !*slots[idx2]) {
  					idx = idx2;
  					break;
  				}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
  			}
5c33dd70b   Oliver Neukum   [ALSA] cleanup an...
178
  	}
a93bbaa77   Takashi Iwai   [ALSA] Improve th...
179
180
181
182
183
184
185
186
  	if (idx < 0)
  		err = -ENODEV;
  	else if (idx < snd_ecards_limit) {
  		if (snd_cards_lock & (1 << idx))
  			err = -EBUSY;	/* invalid */
  	} else if (idx >= SNDRV_CARDS)
  		err = -ENODEV;
  	if (err < 0) {
746df9489   Takashi Iwai   [ALSA] Fix rwlock...
187
  		mutex_unlock(&snd_card_mutex);
5c33dd70b   Oliver Neukum   [ALSA] cleanup an...
188
189
190
  		snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i), error: %d
  ",
  			 idx, snd_ecards_limit - 1, err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
193
  		goto __error;
  	}
  	snd_cards_lock |= 1 << idx;		/* lock it */
a93bbaa77   Takashi Iwai   [ALSA] Improve th...
194
195
  	if (idx >= snd_ecards_limit)
  		snd_ecards_limit = idx + 1; /* increase the limit */
746df9489   Takashi Iwai   [ALSA] Fix rwlock...
196
  	mutex_unlock(&snd_card_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
199
200
201
202
203
204
  	card->number = idx;
  	card->module = module;
  	INIT_LIST_HEAD(&card->devices);
  	init_rwsem(&card->controls_rwsem);
  	rwlock_init(&card->ctl_files_rwlock);
  	INIT_LIST_HEAD(&card->controls);
  	INIT_LIST_HEAD(&card->ctl_files);
  	spin_lock_init(&card->files_lock);
118dd6bfe   Takashi Iwai   ALSA: Clean up sn...
205
  	INIT_LIST_HEAD(&card->files_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
  	init_waitqueue_head(&card->shutdown_sleep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
  #ifdef CONFIG_PM
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
208
  	mutex_init(&card->power_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
211
212
  	init_waitqueue_head(&card->power_sleep);
  #endif
  	/* the control interface cannot be accessed from the user space until */
  	/* snd_cards_bitmask and snd_cards are set with snd_card_register */
53fb1e635   Takashi Iwai   ALSA: Introduce s...
213
214
215
216
  	err = snd_ctl_create(card);
  	if (err < 0) {
  		snd_printk(KERN_ERR "unable to register control minors
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
218
  		goto __error;
  	}
53fb1e635   Takashi Iwai   ALSA: Introduce s...
219
220
221
222
  	err = snd_info_card_create(card);
  	if (err < 0) {
  		snd_printk(KERN_ERR "unable to create card info
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
224
225
  		goto __error_ctl;
  	}
  	if (extra_size > 0)
512bbd6a8   Takashi Iwai   [ALSA] Remove xxx...
226
  		card->private_data = (char *)card + sizeof(struct snd_card);
53fb1e635   Takashi Iwai   ALSA: Introduce s...
227
228
  	*card_ret = card;
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
232
233
  
        __error_ctl:
  	snd_device_free_all(card, SNDRV_DEV_CMD_PRE);
        __error:
  	kfree(card);
53fb1e635   Takashi Iwai   ALSA: Introduce s...
234
    	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
  }
53fb1e635   Takashi Iwai   ALSA: Introduce s...
236
  EXPORT_SYMBOL(snd_card_create);
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
237

746df9489   Takashi Iwai   [ALSA] Fix rwlock...
238
239
240
241
242
243
244
245
246
247
  /* return non-zero if a card is already locked */
  int snd_card_locked(int card)
  {
  	int locked;
  
  	mutex_lock(&snd_card_mutex);
  	locked = snd_cards_lock & (1 << card);
  	mutex_unlock(&snd_card_mutex);
  	return locked;
  }
b3b0abe11   Clemens Ladisch   [ALSA] return ENO...
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
  static loff_t snd_disconnect_llseek(struct file *file, loff_t offset, int orig)
  {
  	return -ENODEV;
  }
  
  static ssize_t snd_disconnect_read(struct file *file, char __user *buf,
  				   size_t count, loff_t *offset)
  {
  	return -ENODEV;
  }
  
  static ssize_t snd_disconnect_write(struct file *file, const char __user *buf,
  				    size_t count, loff_t *offset)
  {
  	return -ENODEV;
  }
a9edfc602   Karsten Wiese   [ALSA] Handle fil...
264
265
266
267
268
269
270
271
  static int snd_disconnect_release(struct inode *inode, struct file *file)
  {
  	struct snd_monitor_file *df = NULL, *_df;
  
  	spin_lock(&shutdown_lock);
  	list_for_each_entry(_df, &shutdown_files, shutdown_list) {
  		if (_df->file == file) {
  			df = _df;
118dd6bfe   Takashi Iwai   ALSA: Clean up sn...
272
  			list_del_init(&df->shutdown_list);
a9edfc602   Karsten Wiese   [ALSA] Handle fil...
273
274
275
276
  			break;
  		}
  	}
  	spin_unlock(&shutdown_lock);
233e70f42   Al Viro   saner FASYNC hand...
277
278
279
  	if (likely(df)) {
  		if ((file->f_flags & FASYNC) && df->disconnected_f_op->fasync)
  			df->disconnected_f_op->fasync(-1, file, 0);
a9edfc602   Karsten Wiese   [ALSA] Handle fil...
280
  		return df->disconnected_f_op->release(inode, file);
233e70f42   Al Viro   saner FASYNC hand...
281
  	}
a9edfc602   Karsten Wiese   [ALSA] Handle fil...
282

9bf8e7dde   Harvey Harrison   [ALSA] sound: rep...
283
  	panic("%s(%p, %p) failed!", __func__, inode, file);
a9edfc602   Karsten Wiese   [ALSA] Handle fil...
284
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
286
287
288
  static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait)
  {
  	return POLLERR | POLLNVAL;
  }
b3b0abe11   Clemens Ladisch   [ALSA] return ENO...
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
  static long snd_disconnect_ioctl(struct file *file,
  				 unsigned int cmd, unsigned long arg)
  {
  	return -ENODEV;
  }
  
  static int snd_disconnect_mmap(struct file *file, struct vm_area_struct *vma)
  {
  	return -ENODEV;
  }
  
  static int snd_disconnect_fasync(int fd, struct file *file, int on)
  {
  	return -ENODEV;
  }
9c2e08c59   Arjan van de Ven   [PATCH] mark stru...
304
  static const struct file_operations snd_shutdown_f_ops =
a9edfc602   Karsten Wiese   [ALSA] Handle fil...
305
306
307
308
309
310
311
312
313
314
315
316
317
318
  {
  	.owner = 	THIS_MODULE,
  	.llseek =	snd_disconnect_llseek,
  	.read = 	snd_disconnect_read,
  	.write =	snd_disconnect_write,
  	.release =	snd_disconnect_release,
  	.poll =		snd_disconnect_poll,
  	.unlocked_ioctl = snd_disconnect_ioctl,
  #ifdef CONFIG_COMPAT
  	.compat_ioctl = snd_disconnect_ioctl,
  #endif
  	.mmap =		snd_disconnect_mmap,
  	.fasync =	snd_disconnect_fasync
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
320
321
322
323
324
325
326
327
328
329
  /**
   *  snd_card_disconnect - disconnect all APIs from the file-operations (user space)
   *  @card: soundcard structure
   *
   *  Disconnects all APIs from the file-operations (user space).
   *
   *  Returns zero, otherwise a negative error code.
   *
   *  Note: The current implementation replaces all active file->f_op with special
   *        dummy file operations (they do nothing except release).
   */
512bbd6a8   Takashi Iwai   [ALSA] Remove xxx...
330
  int snd_card_disconnect(struct snd_card *card)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
332
  {
  	struct snd_monitor_file *mfile;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
  	int err;
f18638dcf   Takashi Iwai   [ALSA] Clean up s...
334
335
  	if (!card)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
338
339
340
341
342
343
344
  	spin_lock(&card->files_lock);
  	if (card->shutdown) {
  		spin_unlock(&card->files_lock);
  		return 0;
  	}
  	card->shutdown = 1;
  	spin_unlock(&card->files_lock);
  
  	/* phase 1: disable fops (user space) operations for ALSA API */
746df9489   Takashi Iwai   [ALSA] Fix rwlock...
345
  	mutex_lock(&snd_card_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
  	snd_cards[card->number] = NULL;
f18638dcf   Takashi Iwai   [ALSA] Clean up s...
347
  	snd_cards_lock &= ~(1 << card->number);
746df9489   Takashi Iwai   [ALSA] Fix rwlock...
348
  	mutex_unlock(&snd_card_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
350
351
352
  	
  	/* phase 2: replace file->f_op with special dummy operations */
  	
  	spin_lock(&card->files_lock);
118dd6bfe   Takashi Iwai   ALSA: Clean up sn...
353
  	list_for_each_entry(mfile, &card->files_list, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
  		/* it's critical part, use endless loop */
  		/* we have no room to fail */
a9edfc602   Karsten Wiese   [ALSA] Handle fil...
356
  		mfile->disconnected_f_op = mfile->file->f_op;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357

a9edfc602   Karsten Wiese   [ALSA] Handle fil...
358
359
360
  		spin_lock(&shutdown_lock);
  		list_add(&mfile->shutdown_list, &shutdown_files);
  		spin_unlock(&shutdown_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361

a9edfc602   Karsten Wiese   [ALSA] Handle fil...
362
  		mfile->file->f_op = &snd_shutdown_f_ops;
bc9abce0d   Miguel Boton   [ALSA] fix compil...
363
  		fops_get(mfile->file->f_op);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
  	}
  	spin_unlock(&card->files_lock);	
  
  	/* phase 3: notify all connected devices about disconnection */
  	/* at this point, they cannot respond to any calls except release() */
  
  #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
  	if (snd_mixer_oss_notify_callback)
  		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT);
  #endif
  
  	/* notify all devices that we are disconnected */
  	err = snd_device_disconnect_all(card);
  	if (err < 0)
  		snd_printk(KERN_ERR "not all devices for card %i can be disconnected
  ", card->number);
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
380
  	snd_info_card_disconnect(card);
73d38b13f   Takashi Iwai   [ALSA] Fix the ra...
381
382
383
384
  	if (card->card_dev) {
  		device_unregister(card->card_dev);
  		card->card_dev = NULL;
  	}
f18638dcf   Takashi Iwai   [ALSA] Clean up s...
385
386
387
  #ifdef CONFIG_PM
  	wake_up(&card->power_sleep);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
389
  	return 0;	
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
390
  EXPORT_SYMBOL(snd_card_disconnect);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
392
393
394
395
396
397
398
399
400
401
  /**
   *  snd_card_free - frees given soundcard structure
   *  @card: soundcard structure
   *
   *  This function releases the soundcard structure and the all assigned
   *  devices automatically.  That is, you don't have to release the devices
   *  by yourself.
   *
   *  Returns zero. Frees all associated devices and frees the control
   *  interface associated to given soundcard.
   */
c461482c8   Takashi Iwai   [ALSA] Unregister...
402
  static int snd_card_do_free(struct snd_card *card)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
  #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
  	if (snd_mixer_oss_notify_callback)
  		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
  #endif
  	if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) {
  		snd_printk(KERN_ERR "unable to free all devices (pre)
  ");
  		/* Fatal, but this situation should never occur */
  	}
  	if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) {
  		snd_printk(KERN_ERR "unable to free all devices (normal)
  ");
  		/* Fatal, but this situation should never occur */
  	}
  	if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) {
  		snd_printk(KERN_ERR "unable to free all devices (post)
  ");
  		/* Fatal, but this situation should never occur */
  	}
  	if (card->private_free)
  		card->private_free(card);
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
425
  	snd_info_free_entry(card->proc_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
426
427
428
429
430
  	if (snd_info_card_free(card) < 0) {
  		snd_printk(KERN_WARNING "unable to free card info
  ");
  		/* Not fatal error */
  	}
c461482c8   Takashi Iwai   [ALSA] Unregister...
431
432
433
  	kfree(card);
  	return 0;
  }
c461482c8   Takashi Iwai   [ALSA] Unregister...
434
435
436
  int snd_card_free_when_closed(struct snd_card *card)
  {
  	int free_now = 0;
f18638dcf   Takashi Iwai   [ALSA] Clean up s...
437
  	int ret = snd_card_disconnect(card);
c461482c8   Takashi Iwai   [ALSA] Unregister...
438
439
440
441
  	if (ret)
  		return ret;
  
  	spin_lock(&card->files_lock);
118dd6bfe   Takashi Iwai   ALSA: Clean up sn...
442
  	if (list_empty(&card->files_list))
c461482c8   Takashi Iwai   [ALSA] Unregister...
443
444
445
446
447
448
449
450
451
452
453
454
455
456
  		free_now = 1;
  	else
  		card->free_on_last_close = 1;
  	spin_unlock(&card->files_lock);
  
  	if (free_now)
  		snd_card_do_free(card);
  	return 0;
  }
  
  EXPORT_SYMBOL(snd_card_free_when_closed);
  
  int snd_card_free(struct snd_card *card)
  {
f18638dcf   Takashi Iwai   [ALSA] Clean up s...
457
  	int ret = snd_card_disconnect(card);
c461482c8   Takashi Iwai   [ALSA] Unregister...
458
459
460
461
  	if (ret)
  		return ret;
  
  	/* wait, until all devices are ready for the free operation */
118dd6bfe   Takashi Iwai   ALSA: Clean up sn...
462
  	wait_event(card->shutdown_sleep, list_empty(&card->files_list));
c461482c8   Takashi Iwai   [ALSA] Unregister...
463
  	snd_card_do_free(card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
465
  	return 0;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
466
  EXPORT_SYMBOL(snd_card_free);
5fdc18d93   Jaroslav Kysela   ALSA: Core - clea...
467
  static void snd_card_set_id_no_lock(struct snd_card *card, const char *nid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
  {
d001544de   Clemens Ladisch   [ALSA] dynamic mi...
469
  	int i, len, idx_flag = 0, loops = SNDRV_CARDS;
10a8ebbb0   Jaroslav Kysela   ALSA: Core - add ...
470
471
  	const char *spos, *src;
  	char *id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
  	
10a8ebbb0   Jaroslav Kysela   ALSA: Core - add ...
473
474
475
476
477
478
479
480
481
482
  	if (nid == NULL) {
  		id = card->shortname;
  		spos = src = id;
  		while (*id != '\0') {
  			if (*id == ' ')
  				spos = id + 1;
  			id++;
  		}
  	} else {
  		spos = src = nid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
484
485
486
487
  	}
  	id = card->id;
  	while (*spos != '\0' && !isalnum(*spos))
  		spos++;
  	if (isdigit(*spos))
10a8ebbb0   Jaroslav Kysela   ALSA: Core - add ...
488
  		*id++ = isalpha(src[0]) ? src[0] : 'D';
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
491
492
493
494
495
496
497
498
  	while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) {
  		if (isalnum(*spos))
  			*id++ = *spos;
  		spos++;
  	}
  	*id = '\0';
  
  	id = card->id;
  	
  	if (*id == '\0')
d8e4f9aed   Takashi Iwai   ALSA: core - Don'...
499
  		strcpy(id, "Default");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
501
502
  
  	while (1) {
  	      	if (loops-- == 0) {
10a8ebbb0   Jaroslav Kysela   ALSA: Core - add ...
503
504
  			snd_printk(KERN_ERR "unable to set card id (%s)
  ", id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
506
507
508
509
510
        			strcpy(card->id, card->proc_root->name);
        			return;
        		}
  	      	if (!snd_info_check_reserved_words(id))
        			goto __change;
  		for (i = 0; i < snd_ecards_limit; i++) {
5fdc18d93   Jaroslav Kysela   ALSA: Core - clea...
511
  			if (snd_cards[i] && !strcmp(snd_cards[i]->id, id))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
513
514
515
516
517
  				goto __change;
  		}
  		break;
  
  	      __change:
  		len = strlen(id);
d001544de   Clemens Ladisch   [ALSA] dynamic mi...
518
519
520
521
522
523
  		if (idx_flag) {
  			if (id[len-1] != '9')
  				id[len-1]++;
  			else
  				id[len-1] = 'A';
  		} else if ((size_t)len <= sizeof(card->id) - 3) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
525
526
527
528
529
  			strcat(id, "_1");
  			idx_flag++;
  		} else {
  			spos = id + len - 2;
  			if ((size_t)len <= sizeof(card->id) - 2)
  				spos++;
10a8ebbb0   Jaroslav Kysela   ALSA: Core - add ...
530
531
532
  			*(char *)spos++ = '_';
  			*(char *)spos++ = '1';
  			*(char *)spos++ = '\0';
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
534
535
536
  			idx_flag++;
  		}
  	}
  }
872c78202   Mark Brown   ALSA: Fix double ...
537
538
539
540
541
542
543
544
545
546
  /**
   *  snd_card_set_id - set card identification name
   *  @card: soundcard structure
   *  @nid: new identification string
   *
   *  This function sets the card identification and checks for name
   *  collisions.
   */
  void snd_card_set_id(struct snd_card *card, const char *nid)
  {
5fdc18d93   Jaroslav Kysela   ALSA: Core - clea...
547
548
549
550
551
552
  	/* check if user specified own card->id */
  	if (card->id[0] != '\0')
  		return;
  	mutex_lock(&snd_card_mutex);
  	snd_card_set_id_no_lock(card, nid);
  	mutex_unlock(&snd_card_mutex);
872c78202   Mark Brown   ALSA: Fix double ...
553
  }
10a8ebbb0   Jaroslav Kysela   ALSA: Core - add ...
554
  EXPORT_SYMBOL(snd_card_set_id);
9fb6198e8   Jaroslav Kysela   ALSA: add /sys/cl...
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
  static ssize_t
  card_id_show_attr(struct device *dev,
  		  struct device_attribute *attr, char *buf)
  {
  	struct snd_card *card = dev_get_drvdata(dev);
  	return snprintf(buf, PAGE_SIZE, "%s
  ", card ? card->id : "(null)");
  }
  
  static ssize_t
  card_id_store_attr(struct device *dev, struct device_attribute *attr,
  		   const char *buf, size_t count)
  {
  	struct snd_card *card = dev_get_drvdata(dev);
  	char buf1[sizeof(card->id)];
  	size_t copy = count > sizeof(card->id) - 1 ?
  					sizeof(card->id) - 1 : count;
  	size_t idx;
  	int c;
  
  	for (idx = 0; idx < copy; idx++) {
  		c = buf[idx];
  		if (!isalnum(c) && c != '_' && c != '-')
  			return -EINVAL;
  	}
  	memcpy(buf1, buf, copy);
  	buf1[copy] = '\0';
  	mutex_lock(&snd_card_mutex);
  	if (!snd_info_check_reserved_words(buf1)) {
  	     __exist:
  		mutex_unlock(&snd_card_mutex);
  		return -EEXIST;
  	}
  	for (idx = 0; idx < snd_ecards_limit; idx++) {
4437ecdc7   Peter Ujfalusi   ALSA: core: Allow...
589
590
591
592
593
594
  		if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1)) {
  			if (card == snd_cards[idx])
  				goto __ok;
  			else
  				goto __exist;
  		}
9fb6198e8   Jaroslav Kysela   ALSA: add /sys/cl...
595
596
  	}
  	strcpy(card->id, buf1);
c2eb9c4ea   Jaroslav Kysela   ALSA: when card i...
597
  	snd_info_card_id_change(card);
4437ecdc7   Peter Ujfalusi   ALSA: core: Allow...
598
  __ok:
9fb6198e8   Jaroslav Kysela   ALSA: add /sys/cl...
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
  	mutex_unlock(&snd_card_mutex);
  
  	return count;
  }
  
  static struct device_attribute card_id_attrs =
  	__ATTR(id, S_IRUGO | S_IWUSR, card_id_show_attr, card_id_store_attr);
  
  static ssize_t
  card_number_show_attr(struct device *dev,
  		     struct device_attribute *attr, char *buf)
  {
  	struct snd_card *card = dev_get_drvdata(dev);
  	return snprintf(buf, PAGE_SIZE, "%i
  ", card ? card->number : -1);
  }
  
  static struct device_attribute card_number_attrs =
  	__ATTR(number, S_IRUGO, card_number_show_attr, NULL);
9fb6198e8   Jaroslav Kysela   ALSA: add /sys/cl...
618

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
620
621
622
623
624
625
626
627
  /**
   *  snd_card_register - register the soundcard
   *  @card: soundcard structure
   *
   *  This function registers all the devices assigned to the soundcard.
   *  Until calling this, the ALSA control interface is blocked from the
   *  external accesses.  Thus, you should call this function at the end
   *  of the initialization of the card.
   *
b595076a1   Uwe Kleine-König   tree-wide: fix co...
628
   *  Returns zero otherwise a negative error code if the registration failed.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
629
   */
512bbd6a8   Takashi Iwai   [ALSA] Remove xxx...
630
  int snd_card_register(struct snd_card *card)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631
632
  {
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
633

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
634
635
  	if (snd_BUG_ON(!card))
  		return -EINVAL;
39aba963d   Kay Sievers   driver core: remo...
636

7d2aae1e8   Takashi Iwai   [PATCH] ALSA: Fix...
637
  	if (!card->card_dev) {
abe9ab8f6   Greg Kroah-Hartman   device create: so...
638
  		card->card_dev = device_create(sound_class, card->dev,
9fb6198e8   Jaroslav Kysela   ALSA: add /sys/cl...
639
  					       MKDEV(0, 0), card,
abe9ab8f6   Greg Kroah-Hartman   device create: so...
640
  					       "card%i", card->number);
7d2aae1e8   Takashi Iwai   [PATCH] ALSA: Fix...
641
642
  		if (IS_ERR(card->card_dev))
  			card->card_dev = NULL;
d80f19fab   Greg Kroah-Hartman   Driver core: conv...
643
  	}
39aba963d   Kay Sievers   driver core: remo...
644

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
645
646
  	if ((err = snd_device_register_all(card)) < 0)
  		return err;
746df9489   Takashi Iwai   [ALSA] Fix rwlock...
647
  	mutex_lock(&snd_card_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648
649
  	if (snd_cards[card->number]) {
  		/* already registered */
746df9489   Takashi Iwai   [ALSA] Fix rwlock...
650
  		mutex_unlock(&snd_card_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651
652
  		return 0;
  	}
5fdc18d93   Jaroslav Kysela   ALSA: Core - clea...
653
  	snd_card_set_id_no_lock(card, card->id[0] == '\0' ? NULL : card->id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
654
  	snd_cards[card->number] = card;
746df9489   Takashi Iwai   [ALSA] Fix rwlock...
655
  	mutex_unlock(&snd_card_mutex);
e28563cce   Takashi Iwai   [ALSA] Optimize f...
656
  	init_info_for_card(card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
657
658
659
660
  #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
  	if (snd_mixer_oss_notify_callback)
  		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
  #endif
9fb6198e8   Jaroslav Kysela   ALSA: add /sys/cl...
661
  	if (card->card_dev) {
2af752936   Hannes Eder   sound: Fix warnin...
662
663
664
665
666
667
  		err = device_create_file(card->card_dev, &card_id_attrs);
  		if (err < 0)
  			return err;
  		err = device_create_file(card->card_dev, &card_number_attrs);
  		if (err < 0)
  			return err;
9fb6198e8   Jaroslav Kysela   ALSA: add /sys/cl...
668
  	}
39aba963d   Kay Sievers   driver core: remo...
669

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
670
671
  	return 0;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
672
  EXPORT_SYMBOL(snd_card_register);
e28563cce   Takashi Iwai   [ALSA] Optimize f...
673
  #ifdef CONFIG_PROC_FS
6581f4e74   Takashi Iwai   [ALSA] Remove zer...
674
  static struct snd_info_entry *snd_card_info_entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
675

a381a7a66   Takashi Iwai   [ALSA] Decentrali...
676
677
  static void snd_card_info_read(struct snd_info_entry *entry,
  			       struct snd_info_buffer *buffer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
678
679
  {
  	int idx, count;
512bbd6a8   Takashi Iwai   [ALSA] Remove xxx...
680
  	struct snd_card *card;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
681
682
  
  	for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
746df9489   Takashi Iwai   [ALSA] Fix rwlock...
683
  		mutex_lock(&snd_card_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
684
685
  		if ((card = snd_cards[idx]) != NULL) {
  			count++;
d001544de   Clemens Ladisch   [ALSA] dynamic mi...
686
687
  			snd_iprintf(buffer, "%2i [%-15s]: %s - %s
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
689
690
691
  					idx,
  					card->id,
  					card->driver,
  					card->shortname);
d001544de   Clemens Ladisch   [ALSA] dynamic mi...
692
693
  			snd_iprintf(buffer, "                      %s
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
694
695
  					card->longname);
  		}
746df9489   Takashi Iwai   [ALSA] Fix rwlock...
696
  		mutex_unlock(&snd_card_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
697
698
699
700
701
  	}
  	if (!count)
  		snd_iprintf(buffer, "--- no soundcards ---
  ");
  }
e28563cce   Takashi Iwai   [ALSA] Optimize f...
702
  #ifdef CONFIG_SND_OSSEMUL
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
703

512bbd6a8   Takashi Iwai   [ALSA] Remove xxx...
704
  void snd_card_info_read_oss(struct snd_info_buffer *buffer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
705
706
  {
  	int idx, count;
512bbd6a8   Takashi Iwai   [ALSA] Remove xxx...
707
  	struct snd_card *card;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
708
709
  
  	for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
746df9489   Takashi Iwai   [ALSA] Fix rwlock...
710
  		mutex_lock(&snd_card_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
712
713
714
715
  		if ((card = snd_cards[idx]) != NULL) {
  			count++;
  			snd_iprintf(buffer, "%s
  ", card->longname);
  		}
746df9489   Takashi Iwai   [ALSA] Fix rwlock...
716
  		mutex_unlock(&snd_card_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
718
719
720
721
722
723
724
725
726
  	}
  	if (!count) {
  		snd_iprintf(buffer, "--- no soundcards ---
  ");
  	}
  }
  
  #endif
  
  #ifdef MODULE
512bbd6a8   Takashi Iwai   [ALSA] Remove xxx...
727
728
729
  static struct snd_info_entry *snd_card_module_info_entry;
  static void snd_card_module_info_read(struct snd_info_entry *entry,
  				      struct snd_info_buffer *buffer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
731
  {
  	int idx;
512bbd6a8   Takashi Iwai   [ALSA] Remove xxx...
732
  	struct snd_card *card;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
733
734
  
  	for (idx = 0; idx < SNDRV_CARDS; idx++) {
746df9489   Takashi Iwai   [ALSA] Fix rwlock...
735
  		mutex_lock(&snd_card_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
  		if ((card = snd_cards[idx]) != NULL)
d001544de   Clemens Ladisch   [ALSA] dynamic mi...
737
738
739
  			snd_iprintf(buffer, "%2i %s
  ",
  				    idx, card->module->name);
746df9489   Takashi Iwai   [ALSA] Fix rwlock...
740
  		mutex_unlock(&snd_card_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
741
742
743
744
745
746
  	}
  }
  #endif
  
  int __init snd_card_info_init(void)
  {
512bbd6a8   Takashi Iwai   [ALSA] Remove xxx...
747
  	struct snd_info_entry *entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
748
749
  
  	entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL);
7c22f1aaa   Takashi Iwai   [ALSA] Remove snd...
750
751
  	if (! entry)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752
753
754
755
756
757
758
759
760
761
  	entry->c.text.read = snd_card_info_read;
  	if (snd_info_register(entry) < 0) {
  		snd_info_free_entry(entry);
  		return -ENOMEM;
  	}
  	snd_card_info_entry = entry;
  
  #ifdef MODULE
  	entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL);
  	if (entry) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
762
763
764
765
766
767
768
769
770
771
772
773
774
  		entry->c.text.read = snd_card_module_info_read;
  		if (snd_info_register(entry) < 0)
  			snd_info_free_entry(entry);
  		else
  			snd_card_module_info_entry = entry;
  	}
  #endif
  
  	return 0;
  }
  
  int __exit snd_card_info_done(void)
  {
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
775
  	snd_info_free_entry(snd_card_info_entry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
776
  #ifdef MODULE
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
777
  	snd_info_free_entry(snd_card_module_info_entry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
778
779
780
  #endif
  	return 0;
  }
e28563cce   Takashi Iwai   [ALSA] Optimize f...
781
  #endif /* CONFIG_PROC_FS */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
782
783
784
785
786
787
788
789
790
791
792
  /**
   *  snd_component_add - add a component string
   *  @card: soundcard structure
   *  @component: the component id string
   *
   *  This function adds the component id string to the supported list.
   *  The component can be referred from the alsa-lib.
   *
   *  Returns zero otherwise a negative error code.
   */
    
512bbd6a8   Takashi Iwai   [ALSA] Remove xxx...
793
  int snd_component_add(struct snd_card *card, const char *component)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
  {
  	char *ptr;
  	int len = strlen(component);
  
  	ptr = strstr(card->components, component);
  	if (ptr != NULL) {
  		if (ptr[len] == '\0' || ptr[len] == ' ')	/* already there */
  			return 1;
  	}
  	if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) {
  		snd_BUG();
  		return -ENOMEM;
  	}
  	if (card->components[0] != '\0')
  		strcat(card->components, " ");
  	strcat(card->components, component);
  	return 0;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
812
  EXPORT_SYMBOL(snd_component_add);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
813
814
815
816
817
818
819
820
821
822
823
  /**
   *  snd_card_file_add - add the file to the file list of the card
   *  @card: soundcard structure
   *  @file: file pointer
   *
   *  This function adds the file to the file linked-list of the card.
   *  This linked-list is used to keep tracking the connection state,
   *  and to avoid the release of busy resources by hotplug.
   *
   *  Returns zero or a negative error code.
   */
512bbd6a8   Takashi Iwai   [ALSA] Remove xxx...
824
  int snd_card_file_add(struct snd_card *card, struct file *file)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
825
826
827
828
829
830
831
  {
  	struct snd_monitor_file *mfile;
  
  	mfile = kmalloc(sizeof(*mfile), GFP_KERNEL);
  	if (mfile == NULL)
  		return -ENOMEM;
  	mfile->file = file;
a9edfc602   Karsten Wiese   [ALSA] Handle fil...
832
  	mfile->disconnected_f_op = NULL;
a45e3d6b1   Takashi Iwai   ALSA: Fix yet ano...
833
  	INIT_LIST_HEAD(&mfile->shutdown_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
834
835
836
837
838
839
  	spin_lock(&card->files_lock);
  	if (card->shutdown) {
  		spin_unlock(&card->files_lock);
  		kfree(mfile);
  		return -ENODEV;
  	}
118dd6bfe   Takashi Iwai   ALSA: Clean up sn...
840
  	list_add(&mfile->list, &card->files_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
841
842
843
  	spin_unlock(&card->files_lock);
  	return 0;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
844
  EXPORT_SYMBOL(snd_card_file_add);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
845
846
847
848
849
850
851
  /**
   *  snd_card_file_remove - remove the file from the file list
   *  @card: soundcard structure
   *  @file: file pointer
   *
   *  This function removes the file formerly added to the card via
   *  snd_card_file_add() function.
2b29b13c5   Takashi Iwai   [ALSA] Deprecate ...
852
853
854
   *  If all files are removed and snd_card_free_when_closed() was
   *  called beforehand, it processes the pending release of
   *  resources.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
855
856
857
   *
   *  Returns zero or a negative error code.
   */
512bbd6a8   Takashi Iwai   [ALSA] Remove xxx...
858
  int snd_card_file_remove(struct snd_card *card, struct file *file)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
859
  {
118dd6bfe   Takashi Iwai   ALSA: Clean up sn...
860
  	struct snd_monitor_file *mfile, *found = NULL;
c461482c8   Takashi Iwai   [ALSA] Unregister...
861
  	int last_close = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
863
  
  	spin_lock(&card->files_lock);
118dd6bfe   Takashi Iwai   ALSA: Clean up sn...
864
  	list_for_each_entry(mfile, &card->files_list, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
865
  		if (mfile->file == file) {
118dd6bfe   Takashi Iwai   ALSA: Clean up sn...
866
  			list_del(&mfile->list);
a45e3d6b1   Takashi Iwai   ALSA: Fix yet ano...
867
868
869
  			spin_lock(&shutdown_lock);
  			list_del(&mfile->shutdown_list);
  			spin_unlock(&shutdown_lock);
118dd6bfe   Takashi Iwai   ALSA: Clean up sn...
870
871
872
  			if (mfile->disconnected_f_op)
  				fops_put(mfile->disconnected_f_op);
  			found = mfile;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873
874
  			break;
  		}
a9edfc602   Karsten Wiese   [ALSA] Handle fil...
875
  	}
118dd6bfe   Takashi Iwai   ALSA: Clean up sn...
876
  	if (list_empty(&card->files_list))
c461482c8   Takashi Iwai   [ALSA] Unregister...
877
878
879
  		last_close = 1;
  	spin_unlock(&card->files_lock);
  	if (last_close) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
880
  		wake_up(&card->shutdown_sleep);
c461482c8   Takashi Iwai   [ALSA] Unregister...
881
882
883
  		if (card->free_on_last_close)
  			snd_card_do_free(card);
  	}
118dd6bfe   Takashi Iwai   ALSA: Clean up sn...
884
  	if (!found) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
885
886
887
888
  		snd_printk(KERN_ERR "ALSA card file remove problem (%p)
  ", file);
  		return -ENOENT;
  	}
118dd6bfe   Takashi Iwai   ALSA: Clean up sn...
889
  	kfree(found);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
890
891
  	return 0;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
892
  EXPORT_SYMBOL(snd_card_file_remove);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
893
894
895
896
897
  #ifdef CONFIG_PM
  /**
   *  snd_power_wait - wait until the power-state is changed.
   *  @card: soundcard structure
   *  @power_state: expected power state
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
898
899
900
901
902
   *
   *  Waits until the power-state is changed.
   *
   *  Note: the power lock must be active before call.
   */
cbac4b0cb   Takashi Iwai   [ALSA] Cleanup un...
903
  int snd_power_wait(struct snd_card *card, unsigned int power_state)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
  {
  	wait_queue_t wait;
  	int result = 0;
  
  	/* fastpath */
  	if (snd_power_get_state(card) == power_state)
  		return 0;
  	init_waitqueue_entry(&wait, current);
  	add_wait_queue(&card->power_sleep, &wait);
  	while (1) {
  		if (card->shutdown) {
  			result = -ENODEV;
  			break;
  		}
  		if (snd_power_get_state(card) == power_state)
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
920
921
922
923
924
925
926
927
  		set_current_state(TASK_UNINTERRUPTIBLE);
  		snd_power_unlock(card);
  		schedule_timeout(30 * HZ);
  		snd_power_lock(card);
  	}
  	remove_wait_queue(&card->power_sleep, &wait);
  	return result;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
928
  EXPORT_SYMBOL(snd_power_wait);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
929
  #endif /* CONFIG_PM */