sound.c 11.7 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>
L
Linus Torvalds 已提交
26 27 28 29 30 31 32 33
#include <linux/moduleparam.h>
#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 233
		if (snd_BUG_ON(!card))
			return -EINVAL;
L
Linus Torvalds 已提交
234 235 236 237 238
		minor = SNDRV_MINOR(card->number, type + dev);
		break;
	default:
		return -EINVAL;
	}
239 240
	if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS))
		return -EINVAL;
L
Linus Torvalds 已提交
241 242
	return minor;
}
243
#endif
L
Linus Torvalds 已提交
244 245

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

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

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

306
EXPORT_SYMBOL(snd_register_device_for_dev);
307

308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
/* 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 已提交
326 327 328 329 330 331 332 333 334 335 336
/**
 * 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
 */
337
int snd_unregister_device(int type, struct snd_card *card, int dev)
L
Linus Torvalds 已提交
338
{
339
	int minor;
L
Linus Torvalds 已提交
340

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

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

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

356 357
EXPORT_SYMBOL(snd_unregister_device);

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

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

}

EXPORT_SYMBOL(snd_add_device_sysfs_file);

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

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

382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
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 "?";
	}
}

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

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

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

	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)
{
447
	snd_info_free_entry(snd_minor_info_entry);
L
Linus Torvalds 已提交
448 449
	return 0;
}
450
#endif /* CONFIG_PROC_FS */
L
Linus Torvalds 已提交
451 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

/*
 *  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();
479
	unregister_chrdev(major, "alsa");
L
Linus Torvalds 已提交
480 481
}

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