init.c 23.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/*
 *  Initialization routines
3
 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
L
Linus Torvalds 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 *
 *   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
 *
 */

#include <linux/init.h>
#include <linux/sched.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/ctype.h>
#include <linux/pm.h>
29

L
Linus Torvalds 已提交
30 31 32 33
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>

34 35 36 37 38 39 40 41
/* 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 */
};

42 43 44
static DEFINE_SPINLOCK(shutdown_lock);
static LIST_HEAD(shutdown_files);

45
static const struct file_operations snd_shutdown_f_ops;
L
Linus Torvalds 已提交
46

47 48
static unsigned int snd_cards_lock;	/* locked for registering/using */
struct snd_card *snd_cards[SNDRV_CARDS];
49 50
EXPORT_SYMBOL(snd_cards);

51
static DEFINE_MUTEX(snd_card_mutex);
L
Linus Torvalds 已提交
52

53 54 55 56
static char *slots[SNDRV_CARDS];
module_param_array(slots, charp, NULL, 0444);
MODULE_PARM_DESC(slots, "Module names assigned to the slots.");

57
/* return non-zero if the given index is reserved for the given
58 59
 * module via slots option
 */
60
static int module_slot_match(struct module *module, int idx)
61
{
62
	int match = 1;
63
#ifdef MODULE
64 65
	const char *s1, *s2;

66 67
	if (!module || !module->name || !slots[idx])
		return 0;
68 69 70 71 72 73 74

	s1 = module->name;
	s2 = slots[idx];
	if (*s2 == '!') {
		match = 0; /* negative match */
		s2++;
	}
75 76 77 78 79 80 81 82 83 84 85
	/* 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)
86
			return !match;
87 88 89
		if (!c1)
			break;
	}
90 91
#endif /* MODULE */
	return match;
92 93
}

L
Linus Torvalds 已提交
94
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
95
int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);
96
EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
L
Linus Torvalds 已提交
97 98
#endif

99
#ifdef CONFIG_PROC_FS
100 101
static void snd_card_id_read(struct snd_info_entry *entry,
			     struct snd_info_buffer *buffer)
L
Linus Torvalds 已提交
102 103 104 105
{
	snd_iprintf(buffer, "%s\n", entry->card->id);
}

106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
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\n");
		return err;
	}
	if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) {
		snd_printd("unable to create card entry\n");
		return err;
	}
	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

L
Linus Torvalds 已提交
131
/**
T
Takashi Iwai 已提交
132
 *  snd_card_create - create and initialize a soundcard structure
L
Linus Torvalds 已提交
133 134 135 136
 *  @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
T
Takashi Iwai 已提交
137
 *  @card_ret: the pointer to store the created card instance
L
Linus Torvalds 已提交
138 139 140
 *
 *  Creates and initializes a soundcard structure.
 *
T
Takashi Iwai 已提交
141 142 143 144 145
 *  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.
L
Linus Torvalds 已提交
146
 */
T
Takashi Iwai 已提交
147 148 149
int snd_card_create(int idx, const char *xid,
		    struct module *module, int extra_size,
		    struct snd_card **card_ret)
L
Linus Torvalds 已提交
150
{
151
	struct snd_card *card;
152
	int err, idx2;
L
Linus Torvalds 已提交
153

T
Takashi Iwai 已提交
154 155 156 157
	if (snd_BUG_ON(!card_ret))
		return -EINVAL;
	*card_ret = NULL;

L
Linus Torvalds 已提交
158 159
	if (extra_size < 0)
		extra_size = 0;
160
	card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
T
Takashi Iwai 已提交
161 162
	if (!card)
		return -ENOMEM;
163
	if (xid)
164
		strlcpy(card->id, xid, sizeof(card->id));
L
Linus Torvalds 已提交
165
	err = 0;
166
	mutex_lock(&snd_card_mutex);
L
Linus Torvalds 已提交
167 168
	if (idx < 0) {
		for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
169
			/* idx == -1 == 0xffff means: take any free slot */
L
Linus Torvalds 已提交
170
			if (~snd_cards_lock & idx & 1<<idx2) {
171 172 173 174 175 176 177 178 179 180 181 182 183 184
				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;
				}
L
Linus Torvalds 已提交
185
			}
186
	}
187 188 189 190 191 192 193 194
	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) {
195
		mutex_unlock(&snd_card_mutex);
196 197
		snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i), error: %d\n",
			 idx, snd_ecards_limit - 1, err);
L
Linus Torvalds 已提交
198 199 200
		goto __error;
	}
	snd_cards_lock |= 1 << idx;		/* lock it */
201 202
	if (idx >= snd_ecards_limit)
		snd_ecards_limit = idx + 1; /* increase the limit */
203
	mutex_unlock(&snd_card_mutex);
L
Linus Torvalds 已提交
204 205 206 207 208 209 210 211
	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);
212
	INIT_LIST_HEAD(&card->files_list);
L
Linus Torvalds 已提交
213 214
	init_waitqueue_head(&card->shutdown_sleep);
#ifdef CONFIG_PM
215
	mutex_init(&card->power_lock);
L
Linus Torvalds 已提交
216 217 218 219
	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 */
T
Takashi Iwai 已提交
220 221 222
	err = snd_ctl_create(card);
	if (err < 0) {
		snd_printk(KERN_ERR "unable to register control minors\n");
L
Linus Torvalds 已提交
223 224
		goto __error;
	}
T
Takashi Iwai 已提交
225 226 227
	err = snd_info_card_create(card);
	if (err < 0) {
		snd_printk(KERN_ERR "unable to create card info\n");
L
Linus Torvalds 已提交
228 229 230
		goto __error_ctl;
	}
	if (extra_size > 0)
231
		card->private_data = (char *)card + sizeof(struct snd_card);
T
Takashi Iwai 已提交
232 233
	*card_ret = card;
	return 0;
L
Linus Torvalds 已提交
234 235 236 237 238

      __error_ctl:
	snd_device_free_all(card, SNDRV_DEV_CMD_PRE);
      __error:
	kfree(card);
T
Takashi Iwai 已提交
239
  	return err;
L
Linus Torvalds 已提交
240
}
T
Takashi Iwai 已提交
241
EXPORT_SYMBOL(snd_card_create);
242

243 244 245 246 247 248 249 250 251 252 253
/* 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;
}

254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
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;
}

271 272 273 274 275 276 277 278
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;
279
			list_del_init(&df->shutdown_list);
280 281 282 283 284
			break;
		}
	}
	spin_unlock(&shutdown_lock);

A
Al Viro 已提交
285 286 287
	if (likely(df)) {
		if ((file->f_flags & FASYNC) && df->disconnected_f_op->fasync)
			df->disconnected_f_op->fasync(-1, file, 0);
288
		return df->disconnected_f_op->release(inode, file);
A
Al Viro 已提交
289
	}
290

291
	panic("%s(%p, %p) failed!", __func__, inode, file);
292 293
}

L
Linus Torvalds 已提交
294 295 296 297 298
static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait)
{
	return POLLERR | POLLNVAL;
}

299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
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;
}

315
static const struct file_operations snd_shutdown_f_ops =
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
{
	.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
};

L
Linus Torvalds 已提交
331 332 333 334 335 336 337 338 339 340 341
/**
 *  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).
 */
342
int snd_card_disconnect(struct snd_card *card)
L
Linus Torvalds 已提交
343 344 345 346 347
{
	struct snd_monitor_file *mfile;
	struct file *file;
	int err;

T
Takashi Iwai 已提交
348 349 350
	if (!card)
		return -EINVAL;

L
Linus Torvalds 已提交
351 352 353 354 355 356 357 358 359
	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 */
360
	mutex_lock(&snd_card_mutex);
L
Linus Torvalds 已提交
361
	snd_cards[card->number] = NULL;
T
Takashi Iwai 已提交
362
	snd_cards_lock &= ~(1 << card->number);
363
	mutex_unlock(&snd_card_mutex);
L
Linus Torvalds 已提交
364 365 366 367
	
	/* phase 2: replace file->f_op with special dummy operations */
	
	spin_lock(&card->files_lock);
368
	list_for_each_entry(mfile, &card->files_list, list) {
L
Linus Torvalds 已提交
369 370 371 372
		file = mfile->file;

		/* it's critical part, use endless loop */
		/* we have no room to fail */
373
		mfile->disconnected_f_op = mfile->file->f_op;
L
Linus Torvalds 已提交
374

375 376 377
		spin_lock(&shutdown_lock);
		list_add(&mfile->shutdown_list, &shutdown_files);
		spin_unlock(&shutdown_lock);
L
Linus Torvalds 已提交
378

379
		mfile->file->f_op = &snd_shutdown_f_ops;
380
		fops_get(mfile->file->f_op);
L
Linus Torvalds 已提交
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
	}
	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\n", card->number);

397
	snd_info_card_disconnect(card);
398 399 400 401 402
#ifndef CONFIG_SYSFS_DEPRECATED
	if (card->card_dev) {
		device_unregister(card->card_dev);
		card->card_dev = NULL;
	}
T
Takashi Iwai 已提交
403 404 405
#endif
#ifdef CONFIG_PM
	wake_up(&card->power_sleep);
406
#endif
L
Linus Torvalds 已提交
407 408 409
	return 0;	
}

410 411
EXPORT_SYMBOL(snd_card_disconnect);

L
Linus Torvalds 已提交
412 413 414 415 416 417 418 419 420 421 422
/**
 *  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.
 */
423
static int snd_card_do_free(struct snd_card *card)
L
Linus Torvalds 已提交
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
{
#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)\n");
		/* 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)\n");
		/* 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)\n");
		/* Fatal, but this situation should never occur */
	}
	if (card->private_free)
		card->private_free(card);
443
	snd_info_free_entry(card->proc_id);
L
Linus Torvalds 已提交
444 445 446 447
	if (snd_info_card_free(card) < 0) {
		snd_printk(KERN_WARNING "unable to free card info\n");
		/* Not fatal error */
	}
448 449 450 451 452 453 454
	kfree(card);
	return 0;
}

int snd_card_free_when_closed(struct snd_card *card)
{
	int free_now = 0;
T
Takashi Iwai 已提交
455
	int ret = snd_card_disconnect(card);
456 457 458 459
	if (ret)
		return ret;

	spin_lock(&card->files_lock);
460
	if (list_empty(&card->files_list))
461 462 463 464 465 466 467 468 469 470 471 472 473 474
		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)
{
T
Takashi Iwai 已提交
475
	int ret = snd_card_disconnect(card);
476 477 478 479
	if (ret)
		return ret;

	/* wait, until all devices are ready for the free operation */
480
	wait_event(card->shutdown_sleep, list_empty(&card->files_list));
481
	snd_card_do_free(card);
L
Linus Torvalds 已提交
482 483 484
	return 0;
}

485 486
EXPORT_SYMBOL(snd_card_free);

487
static void snd_card_set_id_no_lock(struct snd_card *card, const char *nid)
L
Linus Torvalds 已提交
488
{
489
	int i, len, idx_flag = 0, loops = SNDRV_CARDS;
490 491
	const char *spos, *src;
	char *id;
L
Linus Torvalds 已提交
492
	
493 494 495 496 497 498 499 500 501 502
	if (nid == NULL) {
		id = card->shortname;
		spos = src = id;
		while (*id != '\0') {
			if (*id == ' ')
				spos = id + 1;
			id++;
		}
	} else {
		spos = src = nid;
L
Linus Torvalds 已提交
503 504 505 506 507
	}
	id = card->id;
	while (*spos != '\0' && !isalnum(*spos))
		spos++;
	if (isdigit(*spos))
508
		*id++ = isalpha(src[0]) ? src[0] : 'D';
L
Linus Torvalds 已提交
509 510 511 512 513 514 515 516 517 518 519 520 521 522
	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')
		strcpy(id, "default");

	while (1) {
	      	if (loops-- == 0) {
523
			snd_printk(KERN_ERR "unable to set card id (%s)\n", id);
L
Linus Torvalds 已提交
524 525 526 527 528 529
      			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++) {
530
			if (snd_cards[i] && !strcmp(snd_cards[i]->id, id))
L
Linus Torvalds 已提交
531 532 533 534 535 536
				goto __change;
		}
		break;

	      __change:
		len = strlen(id);
537 538 539 540 541 542
		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) {
L
Linus Torvalds 已提交
543 544 545 546 547 548
			strcat(id, "_1");
			idx_flag++;
		} else {
			spos = id + len - 2;
			if ((size_t)len <= sizeof(card->id) - 2)
				spos++;
549 550 551
			*(char *)spos++ = '_';
			*(char *)spos++ = '1';
			*(char *)spos++ = '\0';
L
Linus Torvalds 已提交
552 553 554 555 556
			idx_flag++;
		}
	}
}

557 558 559 560 561 562 563 564 565 566
/**
 *  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)
{
567 568 569 570 571 572
	/* 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);
573
}
574 575
EXPORT_SYMBOL(snd_card_set_id);

576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
#ifndef CONFIG_SYSFS_DEPRECATED
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\n", 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++) {
610 611 612 613 614 615
		if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1)) {
			if (card == snd_cards[idx])
				goto __ok;
			else
				goto __exist;
		}
616 617
	}
	strcpy(card->id, buf1);
618
	snd_info_card_id_change(card);
619
__ok:
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
	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\n", card ? card->number : -1);
}

static struct device_attribute card_number_attrs =
	__ATTR(number, S_IRUGO, card_number_show_attr, NULL);
#endif /* CONFIG_SYSFS_DEPRECATED */

L
Linus Torvalds 已提交
640 641 642 643 644 645 646 647 648 649 650
/**
 *  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.
 *
 *  Returns zero otherwise a negative error code if the registrain failed.
 */
651
int snd_card_register(struct snd_card *card)
L
Linus Torvalds 已提交
652 653 654
{
	int err;

655 656
	if (snd_BUG_ON(!card))
		return -EINVAL;
T
Takashi Iwai 已提交
657 658
#ifndef CONFIG_SYSFS_DEPRECATED
	if (!card->card_dev) {
659
		card->card_dev = device_create(sound_class, card->dev,
660
					       MKDEV(0, 0), card,
661
					       "card%i", card->number);
T
Takashi Iwai 已提交
662 663
		if (IS_ERR(card->card_dev))
			card->card_dev = NULL;
664
	}
T
Takashi Iwai 已提交
665
#endif
L
Linus Torvalds 已提交
666 667
	if ((err = snd_device_register_all(card)) < 0)
		return err;
668
	mutex_lock(&snd_card_mutex);
L
Linus Torvalds 已提交
669 670
	if (snd_cards[card->number]) {
		/* already registered */
671
		mutex_unlock(&snd_card_mutex);
L
Linus Torvalds 已提交
672 673
		return 0;
	}
674
	snd_card_set_id_no_lock(card, card->id[0] == '\0' ? NULL : card->id);
L
Linus Torvalds 已提交
675
	snd_cards[card->number] = card;
676
	mutex_unlock(&snd_card_mutex);
677
	init_info_for_card(card);
L
Linus Torvalds 已提交
678 679 680
#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);
681 682 683
#endif
#ifndef CONFIG_SYSFS_DEPRECATED
	if (card->card_dev) {
684 685 686 687 688 689
		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;
690
	}
L
Linus Torvalds 已提交
691 692 693 694
#endif
	return 0;
}

695 696
EXPORT_SYMBOL(snd_card_register);

697
#ifdef CONFIG_PROC_FS
698
static struct snd_info_entry *snd_card_info_entry;
L
Linus Torvalds 已提交
699

T
Takashi Iwai 已提交
700 701
static void snd_card_info_read(struct snd_info_entry *entry,
			       struct snd_info_buffer *buffer)
L
Linus Torvalds 已提交
702 703
{
	int idx, count;
704
	struct snd_card *card;
L
Linus Torvalds 已提交
705 706

	for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
707
		mutex_lock(&snd_card_mutex);
L
Linus Torvalds 已提交
708 709
		if ((card = snd_cards[idx]) != NULL) {
			count++;
710
			snd_iprintf(buffer, "%2i [%-15s]: %s - %s\n",
L
Linus Torvalds 已提交
711 712 713 714
					idx,
					card->id,
					card->driver,
					card->shortname);
715
			snd_iprintf(buffer, "                      %s\n",
L
Linus Torvalds 已提交
716 717
					card->longname);
		}
718
		mutex_unlock(&snd_card_mutex);
L
Linus Torvalds 已提交
719 720 721 722 723
	}
	if (!count)
		snd_iprintf(buffer, "--- no soundcards ---\n");
}

724
#ifdef CONFIG_SND_OSSEMUL
L
Linus Torvalds 已提交
725

726
void snd_card_info_read_oss(struct snd_info_buffer *buffer)
L
Linus Torvalds 已提交
727 728
{
	int idx, count;
729
	struct snd_card *card;
L
Linus Torvalds 已提交
730 731

	for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
732
		mutex_lock(&snd_card_mutex);
L
Linus Torvalds 已提交
733 734 735 736
		if ((card = snd_cards[idx]) != NULL) {
			count++;
			snd_iprintf(buffer, "%s\n", card->longname);
		}
737
		mutex_unlock(&snd_card_mutex);
L
Linus Torvalds 已提交
738 739 740 741 742 743 744 745 746
	}
	if (!count) {
		snd_iprintf(buffer, "--- no soundcards ---\n");
	}
}

#endif

#ifdef MODULE
747 748 749
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)
L
Linus Torvalds 已提交
750 751
{
	int idx;
752
	struct snd_card *card;
L
Linus Torvalds 已提交
753 754

	for (idx = 0; idx < SNDRV_CARDS; idx++) {
755
		mutex_lock(&snd_card_mutex);
L
Linus Torvalds 已提交
756
		if ((card = snd_cards[idx]) != NULL)
757 758
			snd_iprintf(buffer, "%2i %s\n",
				    idx, card->module->name);
759
		mutex_unlock(&snd_card_mutex);
L
Linus Torvalds 已提交
760 761 762 763 764 765
	}
}
#endif

int __init snd_card_info_init(void)
{
766
	struct snd_info_entry *entry;
L
Linus Torvalds 已提交
767 768

	entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL);
769 770
	if (! entry)
		return -ENOMEM;
L
Linus Torvalds 已提交
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793
	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) {
		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)
{
794
	snd_info_free_entry(snd_card_info_entry);
L
Linus Torvalds 已提交
795
#ifdef MODULE
796
	snd_info_free_entry(snd_card_module_info_entry);
L
Linus Torvalds 已提交
797 798 799 800
#endif
	return 0;
}

801 802
#endif /* CONFIG_PROC_FS */

L
Linus Torvalds 已提交
803 804 805 806 807 808 809 810 811 812 813
/**
 *  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.
 */
  
814
int snd_component_add(struct snd_card *card, const char *component)
L
Linus Torvalds 已提交
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833
{
	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;
}

834 835
EXPORT_SYMBOL(snd_component_add);

L
Linus Torvalds 已提交
836 837 838 839 840 841 842 843 844 845 846
/**
 *  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.
 */
847
int snd_card_file_add(struct snd_card *card, struct file *file)
L
Linus Torvalds 已提交
848 849 850 851 852 853 854
{
	struct snd_monitor_file *mfile;

	mfile = kmalloc(sizeof(*mfile), GFP_KERNEL);
	if (mfile == NULL)
		return -ENOMEM;
	mfile->file = file;
855
	mfile->disconnected_f_op = NULL;
L
Linus Torvalds 已提交
856 857 858 859 860 861
	spin_lock(&card->files_lock);
	if (card->shutdown) {
		spin_unlock(&card->files_lock);
		kfree(mfile);
		return -ENODEV;
	}
862
	list_add(&mfile->list, &card->files_list);
L
Linus Torvalds 已提交
863 864 865 866
	spin_unlock(&card->files_lock);
	return 0;
}

867 868
EXPORT_SYMBOL(snd_card_file_add);

L
Linus Torvalds 已提交
869 870 871 872 873 874 875
/**
 *  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.
876 877 878
 *  If all files are removed and snd_card_free_when_closed() was
 *  called beforehand, it processes the pending release of
 *  resources.
L
Linus Torvalds 已提交
879 880 881
 *
 *  Returns zero or a negative error code.
 */
882
int snd_card_file_remove(struct snd_card *card, struct file *file)
L
Linus Torvalds 已提交
883
{
884
	struct snd_monitor_file *mfile, *found = NULL;
885
	int last_close = 0;
L
Linus Torvalds 已提交
886 887

	spin_lock(&card->files_lock);
888
	list_for_each_entry(mfile, &card->files_list, list) {
L
Linus Torvalds 已提交
889
		if (mfile->file == file) {
890 891 892 893
			list_del(&mfile->list);
			if (mfile->disconnected_f_op)
				fops_put(mfile->disconnected_f_op);
			found = mfile;
L
Linus Torvalds 已提交
894 895
			break;
		}
896
	}
897
	if (list_empty(&card->files_list))
898 899 900
		last_close = 1;
	spin_unlock(&card->files_lock);
	if (last_close) {
L
Linus Torvalds 已提交
901
		wake_up(&card->shutdown_sleep);
902 903 904
		if (card->free_on_last_close)
			snd_card_do_free(card);
	}
905
	if (!found) {
L
Linus Torvalds 已提交
906 907 908
		snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);
		return -ENOENT;
	}
909
	kfree(found);
L
Linus Torvalds 已提交
910 911 912
	return 0;
}

913 914
EXPORT_SYMBOL(snd_card_file_remove);

L
Linus Torvalds 已提交
915 916 917 918 919 920 921 922 923 924
#ifdef CONFIG_PM
/**
 *  snd_power_wait - wait until the power-state is changed.
 *  @card: soundcard structure
 *  @power_state: expected power state
 *
 *  Waits until the power-state is changed.
 *
 *  Note: the power lock must be active before call.
 */
925
int snd_power_wait(struct snd_card *card, unsigned int power_state)
L
Linus Torvalds 已提交
926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950
{
	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;
		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;
}

951
EXPORT_SYMBOL(snd_power_wait);
L
Linus Torvalds 已提交
952
#endif /* CONFIG_PM */