sound.c 11.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/*
 *  Advanced Linux Sound Architecture
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
 *
 *
 *   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/slab.h>
#include <linux/time.h>
T
Takashi Iwai 已提交
25
#include <linux/device.h>
26
#include <linux/module.h>
L
Linus Torvalds 已提交
27 28 29 30 31 32 33
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
#include <sound/version.h>
#include <sound/control.h>
#include <sound/initval.h>
#include <linux/kmod.h>
34
#include <linux/mutex.h>
L
Linus Torvalds 已提交
35 36 37

static int major = CONFIG_SND_MAJOR;
int snd_major;
38 39
EXPORT_SYMBOL(snd_major);

L
Linus Torvalds 已提交
40 41
static int cards_limit = 1;

42
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
L
Linus Torvalds 已提交
43 44 45 46 47 48 49 50 51 52 53 54 55
MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards.");
MODULE_LICENSE("GPL");
module_param(major, int, 0444);
MODULE_PARM_DESC(major, "Major # for sound driver.");
module_param(cards_limit, int, 0444);
MODULE_PARM_DESC(cards_limit, "Count of auto-loadable soundcards.");
MODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR);

/* this one holds the actual max. card number currently available.
 * as default, it's identical with cards_limit option.  when more
 * modules are loaded manually, this limit number increases, too.
 */
int snd_ecards_limit;
56
EXPORT_SYMBOL(snd_ecards_limit);
L
Linus Torvalds 已提交
57

58
static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
59
static DEFINE_MUTEX(sound_mutex);
L
Linus Torvalds 已提交
60

61
#ifdef CONFIG_MODULES
L
Linus Torvalds 已提交
62 63 64 65 66 67

/**
 * snd_request_card - try to load the card module
 * @card: the card number
 *
 * Tries to load the module "snd-card-X" for the given card number
68
 * via request_module.  Returns immediately if already loaded.
L
Linus Torvalds 已提交
69 70 71
 */
void snd_request_card(int card)
{
72
	if (snd_card_locked(card))
L
Linus Torvalds 已提交
73 74 75 76 77 78
		return;
	if (card < 0 || card >= cards_limit)
		return;
	request_module("snd-card-%i", card);
}

79 80
EXPORT_SYMBOL(snd_request_card);

L
Linus Torvalds 已提交
81 82 83 84 85 86 87 88 89 90 91 92
static void snd_request_other(int minor)
{
	char *str;

	switch (minor) {
	case SNDRV_MINOR_SEQUENCER:	str = "snd-seq";	break;
	case SNDRV_MINOR_TIMER:		str = "snd-timer";	break;
	default:			return;
	}
	request_module(str);
}

93
#endif	/* modular kernel */
L
Linus Torvalds 已提交
94

95 96 97 98 99 100 101 102 103 104 105 106 107
/**
 * snd_lookup_minor_data - get user data of a registered device
 * @minor: the minor number
 * @type: device type (SNDRV_DEVICE_TYPE_XXX)
 *
 * Checks that a minor device with the specified type is registered, and returns
 * its user data pointer.
 */
void *snd_lookup_minor_data(unsigned int minor, int type)
{
	struct snd_minor *mreg;
	void *private_data;

108
	if (minor >= ARRAY_SIZE(snd_minors))
109
		return NULL;
110
	mutex_lock(&sound_mutex);
111 112 113 114 115
	mreg = snd_minors[minor];
	if (mreg && mreg->type == type)
		private_data = mreg->private_data;
	else
		private_data = NULL;
116
	mutex_unlock(&sound_mutex);
117 118 119
	return private_data;
}

120 121
EXPORT_SYMBOL(snd_lookup_minor_data);

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
#ifdef CONFIG_MODULES
static struct snd_minor *autoload_device(unsigned int minor)
{
	int dev;
	mutex_unlock(&sound_mutex); /* release lock temporarily */
	dev = SNDRV_MINOR_DEVICE(minor);
	if (dev == SNDRV_MINOR_CONTROL) {
		/* /dev/aloadC? */
		int card = SNDRV_MINOR_CARD(minor);
		if (snd_cards[card] == NULL)
			snd_request_card(card);
	} else if (dev == SNDRV_MINOR_GLOBAL) {
		/* /dev/aloadSEQ */
		snd_request_other(minor);
	}
	mutex_lock(&sound_mutex); /* reacuire lock */
	return snd_minors[minor];
}
#else /* !CONFIG_MODULES */
#define autoload_device(minor)	NULL
#endif /* CONFIG_MODULES */

static int snd_open(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
145
{
146
	unsigned int minor = iminor(inode);
147
	struct snd_minor *mptr = NULL;
148
	const struct file_operations *old_fops;
L
Linus Torvalds 已提交
149 150
	int err = 0;

151
	if (minor >= ARRAY_SIZE(snd_minors))
152
		return -ENODEV;
153
	mutex_lock(&sound_mutex);
154 155
	mptr = snd_minors[minor];
	if (mptr == NULL) {
156 157 158
		mptr = autoload_device(minor);
		if (!mptr) {
			mutex_unlock(&sound_mutex);
159
			return -ENODEV;
160
		}
L
Linus Torvalds 已提交
161 162 163
	}
	old_fops = file->f_op;
	file->f_op = fops_get(mptr->f_ops);
L
Laurent Pinchart 已提交
164 165
	if (file->f_op == NULL) {
		file->f_op = old_fops;
166
		err = -ENODEV;
L
Laurent Pinchart 已提交
167
	}
168 169 170 171 172
	mutex_unlock(&sound_mutex);
	if (err < 0)
		return err;

	if (file->f_op->open) {
L
Linus Torvalds 已提交
173
		err = file->f_op->open(inode, file);
174 175 176 177
		if (err) {
			fops_put(file->f_op);
			file->f_op = fops_get(old_fops);
		}
L
Linus Torvalds 已提交
178 179 180 181 182
	}
	fops_put(old_fops);
	return err;
}

183
static const struct file_operations snd_fops =
L
Linus Torvalds 已提交
184 185
{
	.owner =	THIS_MODULE,
186 187
	.open =		snd_open,
	.llseek =	noop_llseek,
L
Linus Torvalds 已提交
188 189
};

190
#ifdef CONFIG_SND_DYNAMIC_MINORS
191
static int snd_find_free_minor(int type)
192 193 194
{
	int minor;

195 196 197 198 199 200
	/* static minors for module auto loading */
	if (type == SNDRV_DEVICE_TYPE_SEQUENCER)
		return SNDRV_MINOR_SEQUENCER;
	if (type == SNDRV_DEVICE_TYPE_TIMER)
		return SNDRV_MINOR_TIMER;

201
	for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) {
202 203 204 205 206
		/* skip static minors still used for module auto loading */
		if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL)
			continue;
		if (minor == SNDRV_MINOR_SEQUENCER ||
		    minor == SNDRV_MINOR_TIMER)
207 208 209 210 211 212 213
			continue;
		if (!snd_minors[minor])
			return minor;
	}
	return -EBUSY;
}
#else
214
static int snd_kernel_minor(int type, struct snd_card *card, int dev)
L
Linus Torvalds 已提交
215 216 217 218 219 220 221 222 223
{
	int minor;

	switch (type) {
	case SNDRV_DEVICE_TYPE_SEQUENCER:
	case SNDRV_DEVICE_TYPE_TIMER:
		minor = type;
		break;
	case SNDRV_DEVICE_TYPE_CONTROL:
224 225
		if (snd_BUG_ON(!card))
			return -EINVAL;
L
Linus Torvalds 已提交
226 227 228 229 230 231
		minor = SNDRV_MINOR(card->number, type);
		break;
	case SNDRV_DEVICE_TYPE_HWDEP:
	case SNDRV_DEVICE_TYPE_RAWMIDI:
	case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
	case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
232
	case SNDRV_DEVICE_TYPE_COMPRESS:
233 234
		if (snd_BUG_ON(!card))
			return -EINVAL;
L
Linus Torvalds 已提交
235 236 237 238 239
		minor = SNDRV_MINOR(card->number, type + dev);
		break;
	default:
		return -EINVAL;
	}
240 241
	if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS))
		return -EINVAL;
L
Linus Torvalds 已提交
242 243
	return minor;
}
244
#endif
L
Linus Torvalds 已提交
245 246

/**
247
 * snd_register_device_for_dev - Register the ALSA device file for the card
L
Linus Torvalds 已提交
248 249 250
 * @type: the device type, SNDRV_DEVICE_TYPE_XXX
 * @card: the card instance
 * @dev: the device index
251
 * @f_ops: the file operations
252
 * @private_data: user pointer for f_ops->open()
L
Linus Torvalds 已提交
253
 * @name: the device file name
254
 * @device: the &struct device to link this new device to
L
Linus Torvalds 已提交
255 256 257 258
 *
 * Registers an ALSA device file for the given card.
 * The operators have to be set in reg parameter.
 *
259
 * Returns zero if successful, or a negative error code on failure.
L
Linus Torvalds 已提交
260
 */
261 262 263 264
int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
				const struct file_operations *f_ops,
				void *private_data,
				const char *name, struct device *device)
L
Linus Torvalds 已提交
265
{
266
	int minor;
267
	struct snd_minor *preg;
L
Linus Torvalds 已提交
268

269 270
	if (snd_BUG_ON(!name))
		return -EINVAL;
271
	preg = kmalloc(sizeof *preg, GFP_KERNEL);
L
Linus Torvalds 已提交
272 273
	if (preg == NULL)
		return -ENOMEM;
274
	preg->type = type;
275
	preg->card = card ? card->number : -1;
L
Linus Torvalds 已提交
276
	preg->device = dev;
277
	preg->f_ops = f_ops;
278
	preg->private_data = private_data;
279
	mutex_lock(&sound_mutex);
280
#ifdef CONFIG_SND_DYNAMIC_MINORS
281
	minor = snd_find_free_minor(type);
282 283 284 285 286 287
#else
	minor = snd_kernel_minor(type, card, dev);
	if (minor >= 0 && snd_minors[minor])
		minor = -EBUSY;
#endif
	if (minor < 0) {
288
		mutex_unlock(&sound_mutex);
L
Linus Torvalds 已提交
289
		kfree(preg);
290
		return minor;
L
Linus Torvalds 已提交
291
	}
292
	snd_minors[minor] = preg;
293 294
	preg->dev = device_create(sound_class, device, MKDEV(major, minor),
				  private_data, "%s", name);
295 296 297 298 299 300 301 302
	if (IS_ERR(preg->dev)) {
		snd_minors[minor] = NULL;
		mutex_unlock(&sound_mutex);
		minor = PTR_ERR(preg->dev);
		kfree(preg);
		return minor;
	}

303
	mutex_unlock(&sound_mutex);
L
Linus Torvalds 已提交
304 305 306
	return 0;
}

307
EXPORT_SYMBOL(snd_register_device_for_dev);
308

309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
/* find the matching minor record
 * return the index of snd_minor, or -1 if not found
 */
static int find_snd_minor(int type, struct snd_card *card, int dev)
{
	int cardnum, minor;
	struct snd_minor *mptr;

	cardnum = card ? card->number : -1;
	for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor)
		if ((mptr = snd_minors[minor]) != NULL &&
		    mptr->type == type &&
		    mptr->card == cardnum &&
		    mptr->device == dev)
			return minor;
	return -1;
}

L
Linus Torvalds 已提交
327 328 329 330 331 332 333 334 335 336 337
/**
 * snd_unregister_device - unregister the device on the given card
 * @type: the device type, SNDRV_DEVICE_TYPE_XXX
 * @card: the card instance
 * @dev: the device index
 *
 * Unregisters the device file already registered via
 * snd_register_device().
 *
 * Returns zero if sucecessful, or a negative error code on failure
 */
338
int snd_unregister_device(int type, struct snd_card *card, int dev)
L
Linus Torvalds 已提交
339
{
340
	int minor;
L
Linus Torvalds 已提交
341

342
	mutex_lock(&sound_mutex);
343 344
	minor = find_snd_minor(type, card, dev);
	if (minor < 0) {
345
		mutex_unlock(&sound_mutex);
L
Linus Torvalds 已提交
346 347 348
		return -EINVAL;
	}

349
	device_destroy(sound_class, MKDEV(major, minor));
L
Linus Torvalds 已提交
350

351
	kfree(snd_minors[minor]);
352
	snd_minors[minor] = NULL;
353
	mutex_unlock(&sound_mutex);
L
Linus Torvalds 已提交
354 355 356
	return 0;
}

357 358
EXPORT_SYMBOL(snd_unregister_device);

359
int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev,
360
			      struct device_attribute *attr)
361 362
{
	int minor, ret = -EINVAL;
363
	struct device *d;
364 365 366

	mutex_lock(&sound_mutex);
	minor = find_snd_minor(type, card, dev);
367 368
	if (minor >= 0 && (d = snd_minors[minor]->dev) != NULL)
		ret = device_create_file(d, attr);
369 370 371 372 373 374 375
	mutex_unlock(&sound_mutex);
	return ret;

}

EXPORT_SYMBOL(snd_add_device_sysfs_file);

376
#ifdef CONFIG_PROC_FS
L
Linus Torvalds 已提交
377 378 379 380
/*
 *  INFO PART
 */

381
static struct snd_info_entry *snd_minor_info_entry;
L
Linus Torvalds 已提交
382

383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
static const char *snd_device_type_name(int type)
{
	switch (type) {
	case SNDRV_DEVICE_TYPE_CONTROL:
		return "control";
	case SNDRV_DEVICE_TYPE_HWDEP:
		return "hardware dependent";
	case SNDRV_DEVICE_TYPE_RAWMIDI:
		return "raw midi";
	case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
		return "digital audio playback";
	case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
		return "digital audio capture";
	case SNDRV_DEVICE_TYPE_SEQUENCER:
		return "sequencer";
	case SNDRV_DEVICE_TYPE_TIMER:
		return "timer";
	default:
		return "?";
	}
}

405
static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
L
Linus Torvalds 已提交
406
{
407
	int minor;
408
	struct snd_minor *mptr;
L
Linus Torvalds 已提交
409

410
	mutex_lock(&sound_mutex);
411 412 413 414 415
	for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) {
		if (!(mptr = snd_minors[minor]))
			continue;
		if (mptr->card >= 0) {
			if (mptr->device >= 0)
416
				snd_iprintf(buffer, "%3i: [%2i-%2i]: %s\n",
417 418 419
					    minor, mptr->card, mptr->device,
					    snd_device_type_name(mptr->type));
			else
420
				snd_iprintf(buffer, "%3i: [%2i]   : %s\n",
421 422 423
					    minor, mptr->card,
					    snd_device_type_name(mptr->type));
		} else
424
			snd_iprintf(buffer, "%3i:        : %s\n", minor,
425
				    snd_device_type_name(mptr->type));
L
Linus Torvalds 已提交
426
	}
427
	mutex_unlock(&sound_mutex);
L
Linus Torvalds 已提交
428 429 430 431
}

int __init snd_minor_info_init(void)
{
432
	struct snd_info_entry *entry;
L
Linus Torvalds 已提交
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447

	entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL);
	if (entry) {
		entry->c.text.read = snd_minor_info_read;
		if (snd_info_register(entry) < 0) {
			snd_info_free_entry(entry);
			entry = NULL;
		}
	}
	snd_minor_info_entry = entry;
	return 0;
}

int __exit snd_minor_info_done(void)
{
448
	snd_info_free_entry(snd_minor_info_entry);
L
Linus Torvalds 已提交
449 450
	return 0;
}
451
#endif /* CONFIG_PROC_FS */
L
Linus Torvalds 已提交
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479

/*
 *  INIT PART
 */

static int __init alsa_sound_init(void)
{
	snd_major = major;
	snd_ecards_limit = cards_limit;
	if (register_chrdev(major, "alsa", &snd_fops)) {
		snd_printk(KERN_ERR "unable to register native major device number %d\n", major);
		return -EIO;
	}
	if (snd_info_init() < 0) {
		unregister_chrdev(major, "alsa");
		return -ENOMEM;
	}
	snd_info_minor_register();
#ifndef MODULE
	printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n");
#endif
	return 0;
}

static void __exit alsa_sound_exit(void)
{
	snd_info_minor_unregister();
	snd_info_done();
480
	unregister_chrdev(major, "alsa");
L
Linus Torvalds 已提交
481 482
}

483 484
subsys_initcall(alsa_sound_init);
module_exit(alsa_sound_exit);