xref: /openbmc/linux/sound/core/sound.c (revision 12b131c4cf3eb1dc8a60082a434b7b100774c2e7)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  Advanced Linux Sound Architecture
31da177e4SLinus Torvalds  *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *   This program is free software; you can redistribute it and/or modify
71da177e4SLinus Torvalds  *   it under the terms of the GNU General Public License as published by
81da177e4SLinus Torvalds  *   the Free Software Foundation; either version 2 of the License, or
91da177e4SLinus Torvalds  *   (at your option) any later version.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  *   This program is distributed in the hope that it will be useful,
121da177e4SLinus Torvalds  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
131da177e4SLinus Torvalds  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
141da177e4SLinus Torvalds  *   GNU General Public License for more details.
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  *   You should have received a copy of the GNU General Public License
171da177e4SLinus Torvalds  *   along with this program; if not, write to the Free Software
181da177e4SLinus Torvalds  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
191da177e4SLinus Torvalds  *
201da177e4SLinus Torvalds  */
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds #include <sound/driver.h>
231da177e4SLinus Torvalds #include <linux/init.h>
241da177e4SLinus Torvalds #include <linux/slab.h>
251da177e4SLinus Torvalds #include <linux/time.h>
269a1a2a1dSTakashi Iwai #include <linux/device.h>
271da177e4SLinus Torvalds #include <linux/moduleparam.h>
281da177e4SLinus Torvalds #include <sound/core.h>
291da177e4SLinus Torvalds #include <sound/minors.h>
301da177e4SLinus Torvalds #include <sound/info.h>
311da177e4SLinus Torvalds #include <sound/version.h>
321da177e4SLinus Torvalds #include <sound/control.h>
331da177e4SLinus Torvalds #include <sound/initval.h>
341da177e4SLinus Torvalds #include <linux/kmod.h>
351a60d4c5SIngo Molnar #include <linux/mutex.h>
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds #define SNDRV_OS_MINORS 256
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds static int major = CONFIG_SND_MAJOR;
401da177e4SLinus Torvalds int snd_major;
41c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_major);
42c0d3fb39STakashi Iwai 
431da177e4SLinus Torvalds static int cards_limit = 1;
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
461da177e4SLinus Torvalds MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards.");
471da177e4SLinus Torvalds MODULE_LICENSE("GPL");
481da177e4SLinus Torvalds module_param(major, int, 0444);
491da177e4SLinus Torvalds MODULE_PARM_DESC(major, "Major # for sound driver.");
501da177e4SLinus Torvalds module_param(cards_limit, int, 0444);
511da177e4SLinus Torvalds MODULE_PARM_DESC(cards_limit, "Count of auto-loadable soundcards.");
521da177e4SLinus Torvalds MODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR);
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds /* this one holds the actual max. card number currently available.
551da177e4SLinus Torvalds  * as default, it's identical with cards_limit option.  when more
561da177e4SLinus Torvalds  * modules are loaded manually, this limit number increases, too.
571da177e4SLinus Torvalds  */
581da177e4SLinus Torvalds int snd_ecards_limit;
59c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_ecards_limit);
601da177e4SLinus Torvalds 
616983b724SClemens Ladisch static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
621a60d4c5SIngo Molnar static DEFINE_MUTEX(sound_mutex);
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds #ifdef CONFIG_KMOD
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds /**
671da177e4SLinus Torvalds  * snd_request_card - try to load the card module
681da177e4SLinus Torvalds  * @card: the card number
691da177e4SLinus Torvalds  *
701da177e4SLinus Torvalds  * Tries to load the module "snd-card-X" for the given card number
711da177e4SLinus Torvalds  * via KMOD.  Returns immediately if already loaded.
721da177e4SLinus Torvalds  */
731da177e4SLinus Torvalds void snd_request_card(int card)
741da177e4SLinus Torvalds {
751da177e4SLinus Torvalds 	if (! current->fs->root)
761da177e4SLinus Torvalds 		return;
77746df948STakashi Iwai 	if (snd_card_locked(card))
781da177e4SLinus Torvalds 		return;
791da177e4SLinus Torvalds 	if (card < 0 || card >= cards_limit)
801da177e4SLinus Torvalds 		return;
811da177e4SLinus Torvalds 	request_module("snd-card-%i", card);
821da177e4SLinus Torvalds }
831da177e4SLinus Torvalds 
84c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_request_card);
85c0d3fb39STakashi Iwai 
861da177e4SLinus Torvalds static void snd_request_other(int minor)
871da177e4SLinus Torvalds {
881da177e4SLinus Torvalds 	char *str;
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds 	if (! current->fs->root)
911da177e4SLinus Torvalds 		return;
921da177e4SLinus Torvalds 	switch (minor) {
931da177e4SLinus Torvalds 	case SNDRV_MINOR_SEQUENCER:	str = "snd-seq";	break;
941da177e4SLinus Torvalds 	case SNDRV_MINOR_TIMER:		str = "snd-timer";	break;
951da177e4SLinus Torvalds 	default:			return;
961da177e4SLinus Torvalds 	}
971da177e4SLinus Torvalds 	request_module(str);
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds #endif				/* request_module support */
1011da177e4SLinus Torvalds 
102f87135f5SClemens Ladisch /**
103f87135f5SClemens Ladisch  * snd_lookup_minor_data - get user data of a registered device
104f87135f5SClemens Ladisch  * @minor: the minor number
105f87135f5SClemens Ladisch  * @type: device type (SNDRV_DEVICE_TYPE_XXX)
106f87135f5SClemens Ladisch  *
107f87135f5SClemens Ladisch  * Checks that a minor device with the specified type is registered, and returns
108f87135f5SClemens Ladisch  * its user data pointer.
109f87135f5SClemens Ladisch  */
110f87135f5SClemens Ladisch void *snd_lookup_minor_data(unsigned int minor, int type)
111f87135f5SClemens Ladisch {
112f87135f5SClemens Ladisch 	struct snd_minor *mreg;
113f87135f5SClemens Ladisch 	void *private_data;
114f87135f5SClemens Ladisch 
1153a63e444SAdrian Bunk 	if (minor >= ARRAY_SIZE(snd_minors))
116f87135f5SClemens Ladisch 		return NULL;
1171a60d4c5SIngo Molnar 	mutex_lock(&sound_mutex);
118f87135f5SClemens Ladisch 	mreg = snd_minors[minor];
119f87135f5SClemens Ladisch 	if (mreg && mreg->type == type)
120f87135f5SClemens Ladisch 		private_data = mreg->private_data;
121f87135f5SClemens Ladisch 	else
122f87135f5SClemens Ladisch 		private_data = NULL;
1231a60d4c5SIngo Molnar 	mutex_unlock(&sound_mutex);
124f87135f5SClemens Ladisch 	return private_data;
125f87135f5SClemens Ladisch }
126f87135f5SClemens Ladisch 
127c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_lookup_minor_data);
128c0d3fb39STakashi Iwai 
1291da177e4SLinus Torvalds static int snd_open(struct inode *inode, struct file *file)
1301da177e4SLinus Torvalds {
131332682b1SClemens Ladisch 	unsigned int minor = iminor(inode);
132512bbd6aSTakashi Iwai 	struct snd_minor *mptr = NULL;
13399ac48f5SArjan van de Ven 	const struct file_operations *old_fops;
1341da177e4SLinus Torvalds 	int err = 0;
1351da177e4SLinus Torvalds 
1363a63e444SAdrian Bunk 	if (minor >= ARRAY_SIZE(snd_minors))
137332682b1SClemens Ladisch 		return -ENODEV;
138332682b1SClemens Ladisch 	mptr = snd_minors[minor];
139332682b1SClemens Ladisch 	if (mptr == NULL) {
1401da177e4SLinus Torvalds #ifdef CONFIG_KMOD
141332682b1SClemens Ladisch 		int dev = SNDRV_MINOR_DEVICE(minor);
142332682b1SClemens Ladisch 		if (dev == SNDRV_MINOR_CONTROL) {
143332682b1SClemens Ladisch 			/* /dev/aloadC? */
144332682b1SClemens Ladisch 			int card = SNDRV_MINOR_CARD(minor);
1451da177e4SLinus Torvalds 			if (snd_cards[card] == NULL)
146332682b1SClemens Ladisch 				snd_request_card(card);
147332682b1SClemens Ladisch 		} else if (dev == SNDRV_MINOR_GLOBAL) {
148332682b1SClemens Ladisch 			/* /dev/aloadSEQ */
1491da177e4SLinus Torvalds 			snd_request_other(minor);
1501da177e4SLinus Torvalds 		}
151332682b1SClemens Ladisch #ifndef CONFIG_SND_DYNAMIC_MINORS
152332682b1SClemens Ladisch 		/* /dev/snd/{controlC?,seq} */
153332682b1SClemens Ladisch 		mptr = snd_minors[minor];
154332682b1SClemens Ladisch 		if (mptr == NULL)
155332682b1SClemens Ladisch #endif
156332682b1SClemens Ladisch #endif
1571da177e4SLinus Torvalds 			return -ENODEV;
158332682b1SClemens Ladisch 	}
1591da177e4SLinus Torvalds 	old_fops = file->f_op;
1601da177e4SLinus Torvalds 	file->f_op = fops_get(mptr->f_ops);
1611da177e4SLinus Torvalds 	if (file->f_op->open)
1621da177e4SLinus Torvalds 		err = file->f_op->open(inode, file);
1631da177e4SLinus Torvalds 	if (err) {
1641da177e4SLinus Torvalds 		fops_put(file->f_op);
1651da177e4SLinus Torvalds 		file->f_op = fops_get(old_fops);
1661da177e4SLinus Torvalds 	}
1671da177e4SLinus Torvalds 	fops_put(old_fops);
1681da177e4SLinus Torvalds 	return err;
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds static struct file_operations snd_fops =
1721da177e4SLinus Torvalds {
1731da177e4SLinus Torvalds 	.owner =	THIS_MODULE,
1741da177e4SLinus Torvalds 	.open =		snd_open
1751da177e4SLinus Torvalds };
1761da177e4SLinus Torvalds 
177332682b1SClemens Ladisch #ifdef CONFIG_SND_DYNAMIC_MINORS
178332682b1SClemens Ladisch static int snd_find_free_minor(void)
179332682b1SClemens Ladisch {
180332682b1SClemens Ladisch 	int minor;
181332682b1SClemens Ladisch 
182332682b1SClemens Ladisch 	for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) {
183332682b1SClemens Ladisch 		/* skip minors still used statically for autoloading devices */
184332682b1SClemens Ladisch 		if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL ||
185332682b1SClemens Ladisch 		    minor == SNDRV_MINOR_SEQUENCER)
186332682b1SClemens Ladisch 			continue;
187332682b1SClemens Ladisch 		if (!snd_minors[minor])
188332682b1SClemens Ladisch 			return minor;
189332682b1SClemens Ladisch 	}
190332682b1SClemens Ladisch 	return -EBUSY;
191332682b1SClemens Ladisch }
192332682b1SClemens Ladisch #else
193512bbd6aSTakashi Iwai static int snd_kernel_minor(int type, struct snd_card *card, int dev)
1941da177e4SLinus Torvalds {
1951da177e4SLinus Torvalds 	int minor;
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds 	switch (type) {
1981da177e4SLinus Torvalds 	case SNDRV_DEVICE_TYPE_SEQUENCER:
1991da177e4SLinus Torvalds 	case SNDRV_DEVICE_TYPE_TIMER:
2001da177e4SLinus Torvalds 		minor = type;
2011da177e4SLinus Torvalds 		break;
2021da177e4SLinus Torvalds 	case SNDRV_DEVICE_TYPE_CONTROL:
2031da177e4SLinus Torvalds 		snd_assert(card != NULL, return -EINVAL);
2041da177e4SLinus Torvalds 		minor = SNDRV_MINOR(card->number, type);
2051da177e4SLinus Torvalds 		break;
2061da177e4SLinus Torvalds 	case SNDRV_DEVICE_TYPE_HWDEP:
2071da177e4SLinus Torvalds 	case SNDRV_DEVICE_TYPE_RAWMIDI:
2081da177e4SLinus Torvalds 	case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
2091da177e4SLinus Torvalds 	case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
2101da177e4SLinus Torvalds 		snd_assert(card != NULL, return -EINVAL);
2111da177e4SLinus Torvalds 		minor = SNDRV_MINOR(card->number, type + dev);
2121da177e4SLinus Torvalds 		break;
2131da177e4SLinus Torvalds 	default:
2141da177e4SLinus Torvalds 		return -EINVAL;
2151da177e4SLinus Torvalds 	}
2161da177e4SLinus Torvalds 	snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL);
2171da177e4SLinus Torvalds 	return minor;
2181da177e4SLinus Torvalds }
219332682b1SClemens Ladisch #endif
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds /**
222*12b131c4SJohannes Berg  * snd_register_device_for_dev - Register the ALSA device file for the card
2231da177e4SLinus Torvalds  * @type: the device type, SNDRV_DEVICE_TYPE_XXX
2241da177e4SLinus Torvalds  * @card: the card instance
2251da177e4SLinus Torvalds  * @dev: the device index
2262af677fcSClemens Ladisch  * @f_ops: the file operations
227f87135f5SClemens Ladisch  * @private_data: user pointer for f_ops->open()
2281da177e4SLinus Torvalds  * @name: the device file name
229*12b131c4SJohannes Berg  * @device: the &struct device to link this new device to
2301da177e4SLinus Torvalds  *
2311da177e4SLinus Torvalds  * Registers an ALSA device file for the given card.
2321da177e4SLinus Torvalds  * The operators have to be set in reg parameter.
2331da177e4SLinus Torvalds  *
234*12b131c4SJohannes Berg  * Returns zero if successful, or a negative error code on failure.
2351da177e4SLinus Torvalds  */
236*12b131c4SJohannes Berg int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
237*12b131c4SJohannes Berg 				const struct file_operations *f_ops,
238*12b131c4SJohannes Berg 				void *private_data,
239*12b131c4SJohannes Berg 				const char *name, struct device *device)
2401da177e4SLinus Torvalds {
241332682b1SClemens Ladisch 	int minor;
242512bbd6aSTakashi Iwai 	struct snd_minor *preg;
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds 	snd_assert(name, return -EINVAL);
245562b590dSClemens Ladisch 	preg = kmalloc(sizeof *preg, GFP_KERNEL);
2461da177e4SLinus Torvalds 	if (preg == NULL)
2471da177e4SLinus Torvalds 		return -ENOMEM;
2482af677fcSClemens Ladisch 	preg->type = type;
2496983b724SClemens Ladisch 	preg->card = card ? card->number : -1;
2501da177e4SLinus Torvalds 	preg->device = dev;
2512af677fcSClemens Ladisch 	preg->f_ops = f_ops;
252f87135f5SClemens Ladisch 	preg->private_data = private_data;
2531a60d4c5SIngo Molnar 	mutex_lock(&sound_mutex);
254332682b1SClemens Ladisch #ifdef CONFIG_SND_DYNAMIC_MINORS
255332682b1SClemens Ladisch 	minor = snd_find_free_minor();
256332682b1SClemens Ladisch #else
257332682b1SClemens Ladisch 	minor = snd_kernel_minor(type, card, dev);
258332682b1SClemens Ladisch 	if (minor >= 0 && snd_minors[minor])
259332682b1SClemens Ladisch 		minor = -EBUSY;
260332682b1SClemens Ladisch #endif
261332682b1SClemens Ladisch 	if (minor < 0) {
2621a60d4c5SIngo Molnar 		mutex_unlock(&sound_mutex);
2631da177e4SLinus Torvalds 		kfree(preg);
264332682b1SClemens Ladisch 		return minor;
2651da177e4SLinus Torvalds 	}
2666983b724SClemens Ladisch 	snd_minors[minor] = preg;
267d80f19faSGreg Kroah-Hartman 	preg->dev = device_create(sound_class, device, MKDEV(major, minor),
268d80f19faSGreg Kroah-Hartman 				  "%s", name);
269d80f19faSGreg Kroah-Hartman 	if (preg->dev)
270d80f19faSGreg Kroah-Hartman 		dev_set_drvdata(preg->dev, private_data);
2711da177e4SLinus Torvalds 
2721a60d4c5SIngo Molnar 	mutex_unlock(&sound_mutex);
2731da177e4SLinus Torvalds 	return 0;
2741da177e4SLinus Torvalds }
2751da177e4SLinus Torvalds 
276*12b131c4SJohannes Berg EXPORT_SYMBOL(snd_register_device_for_dev);
277c0d3fb39STakashi Iwai 
2789d19f48cSTakashi Iwai /* find the matching minor record
2799d19f48cSTakashi Iwai  * return the index of snd_minor, or -1 if not found
2809d19f48cSTakashi Iwai  */
2819d19f48cSTakashi Iwai static int find_snd_minor(int type, struct snd_card *card, int dev)
2829d19f48cSTakashi Iwai {
2839d19f48cSTakashi Iwai 	int cardnum, minor;
2849d19f48cSTakashi Iwai 	struct snd_minor *mptr;
2859d19f48cSTakashi Iwai 
2869d19f48cSTakashi Iwai 	cardnum = card ? card->number : -1;
2879d19f48cSTakashi Iwai 	for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor)
2889d19f48cSTakashi Iwai 		if ((mptr = snd_minors[minor]) != NULL &&
2899d19f48cSTakashi Iwai 		    mptr->type == type &&
2909d19f48cSTakashi Iwai 		    mptr->card == cardnum &&
2919d19f48cSTakashi Iwai 		    mptr->device == dev)
2929d19f48cSTakashi Iwai 			return minor;
2939d19f48cSTakashi Iwai 	return -1;
2949d19f48cSTakashi Iwai }
2959d19f48cSTakashi Iwai 
2961da177e4SLinus Torvalds /**
2971da177e4SLinus Torvalds  * snd_unregister_device - unregister the device on the given card
2981da177e4SLinus Torvalds  * @type: the device type, SNDRV_DEVICE_TYPE_XXX
2991da177e4SLinus Torvalds  * @card: the card instance
3001da177e4SLinus Torvalds  * @dev: the device index
3011da177e4SLinus Torvalds  *
3021da177e4SLinus Torvalds  * Unregisters the device file already registered via
3031da177e4SLinus Torvalds  * snd_register_device().
3041da177e4SLinus Torvalds  *
3051da177e4SLinus Torvalds  * Returns zero if sucecessful, or a negative error code on failure
3061da177e4SLinus Torvalds  */
307512bbd6aSTakashi Iwai int snd_unregister_device(int type, struct snd_card *card, int dev)
3081da177e4SLinus Torvalds {
3099d19f48cSTakashi Iwai 	int minor;
3101da177e4SLinus Torvalds 
3111a60d4c5SIngo Molnar 	mutex_lock(&sound_mutex);
3129d19f48cSTakashi Iwai 	minor = find_snd_minor(type, card, dev);
3139d19f48cSTakashi Iwai 	if (minor < 0) {
3141a60d4c5SIngo Molnar 		mutex_unlock(&sound_mutex);
3151da177e4SLinus Torvalds 		return -EINVAL;
3161da177e4SLinus Torvalds 	}
3171da177e4SLinus Torvalds 
318d80f19faSGreg Kroah-Hartman 	device_destroy(sound_class, MKDEV(major, minor));
3191da177e4SLinus Torvalds 
3209d19f48cSTakashi Iwai 	kfree(snd_minors[minor]);
3216983b724SClemens Ladisch 	snd_minors[minor] = NULL;
3221a60d4c5SIngo Molnar 	mutex_unlock(&sound_mutex);
3231da177e4SLinus Torvalds 	return 0;
3241da177e4SLinus Torvalds }
3251da177e4SLinus Torvalds 
326c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_unregister_device);
327c0d3fb39STakashi Iwai 
3289d19f48cSTakashi Iwai int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev,
329d80f19faSGreg Kroah-Hartman 			      struct device_attribute *attr)
3309d19f48cSTakashi Iwai {
3319d19f48cSTakashi Iwai 	int minor, ret = -EINVAL;
332d80f19faSGreg Kroah-Hartman 	struct device *d;
3339d19f48cSTakashi Iwai 
3349d19f48cSTakashi Iwai 	mutex_lock(&sound_mutex);
3359d19f48cSTakashi Iwai 	minor = find_snd_minor(type, card, dev);
336d80f19faSGreg Kroah-Hartman 	if (minor >= 0 && (d = snd_minors[minor]->dev) != NULL)
337d80f19faSGreg Kroah-Hartman 		ret = device_create_file(d, attr);
3389d19f48cSTakashi Iwai 	mutex_unlock(&sound_mutex);
3399d19f48cSTakashi Iwai 	return ret;
3409d19f48cSTakashi Iwai 
3419d19f48cSTakashi Iwai }
3429d19f48cSTakashi Iwai 
3439d19f48cSTakashi Iwai EXPORT_SYMBOL(snd_add_device_sysfs_file);
3449d19f48cSTakashi Iwai 
345e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS
3461da177e4SLinus Torvalds /*
3471da177e4SLinus Torvalds  *  INFO PART
3481da177e4SLinus Torvalds  */
3491da177e4SLinus Torvalds 
3506581f4e7STakashi Iwai static struct snd_info_entry *snd_minor_info_entry;
3511da177e4SLinus Torvalds 
3522af677fcSClemens Ladisch static const char *snd_device_type_name(int type)
3532af677fcSClemens Ladisch {
3542af677fcSClemens Ladisch 	switch (type) {
3552af677fcSClemens Ladisch 	case SNDRV_DEVICE_TYPE_CONTROL:
3562af677fcSClemens Ladisch 		return "control";
3572af677fcSClemens Ladisch 	case SNDRV_DEVICE_TYPE_HWDEP:
3582af677fcSClemens Ladisch 		return "hardware dependent";
3592af677fcSClemens Ladisch 	case SNDRV_DEVICE_TYPE_RAWMIDI:
3602af677fcSClemens Ladisch 		return "raw midi";
3612af677fcSClemens Ladisch 	case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
3622af677fcSClemens Ladisch 		return "digital audio playback";
3632af677fcSClemens Ladisch 	case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
3642af677fcSClemens Ladisch 		return "digital audio capture";
3652af677fcSClemens Ladisch 	case SNDRV_DEVICE_TYPE_SEQUENCER:
3662af677fcSClemens Ladisch 		return "sequencer";
3672af677fcSClemens Ladisch 	case SNDRV_DEVICE_TYPE_TIMER:
3682af677fcSClemens Ladisch 		return "timer";
3692af677fcSClemens Ladisch 	default:
3702af677fcSClemens Ladisch 		return "?";
3712af677fcSClemens Ladisch 	}
3722af677fcSClemens Ladisch }
3732af677fcSClemens Ladisch 
374512bbd6aSTakashi Iwai static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
3751da177e4SLinus Torvalds {
3766983b724SClemens Ladisch 	int minor;
377512bbd6aSTakashi Iwai 	struct snd_minor *mptr;
3781da177e4SLinus Torvalds 
3791a60d4c5SIngo Molnar 	mutex_lock(&sound_mutex);
3806983b724SClemens Ladisch 	for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) {
3816983b724SClemens Ladisch 		if (!(mptr = snd_minors[minor]))
3826983b724SClemens Ladisch 			continue;
3836983b724SClemens Ladisch 		if (mptr->card >= 0) {
3846983b724SClemens Ladisch 			if (mptr->device >= 0)
385d001544dSClemens Ladisch 				snd_iprintf(buffer, "%3i: [%2i-%2i]: %s\n",
3866983b724SClemens Ladisch 					    minor, mptr->card, mptr->device,
3876983b724SClemens Ladisch 					    snd_device_type_name(mptr->type));
3881da177e4SLinus Torvalds 			else
389d001544dSClemens Ladisch 				snd_iprintf(buffer, "%3i: [%2i]   : %s\n",
3906983b724SClemens Ladisch 					    minor, mptr->card,
3916983b724SClemens Ladisch 					    snd_device_type_name(mptr->type));
3926983b724SClemens Ladisch 		} else
3936983b724SClemens Ladisch 			snd_iprintf(buffer, "%3i:        : %s\n", minor,
3946983b724SClemens Ladisch 				    snd_device_type_name(mptr->type));
3951da177e4SLinus Torvalds 	}
3961a60d4c5SIngo Molnar 	mutex_unlock(&sound_mutex);
3971da177e4SLinus Torvalds }
3981da177e4SLinus Torvalds 
3991da177e4SLinus Torvalds int __init snd_minor_info_init(void)
4001da177e4SLinus Torvalds {
401512bbd6aSTakashi Iwai 	struct snd_info_entry *entry;
4021da177e4SLinus Torvalds 
4031da177e4SLinus Torvalds 	entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL);
4041da177e4SLinus Torvalds 	if (entry) {
4051da177e4SLinus Torvalds 		entry->c.text.read = snd_minor_info_read;
4061da177e4SLinus Torvalds 		if (snd_info_register(entry) < 0) {
4071da177e4SLinus Torvalds 			snd_info_free_entry(entry);
4081da177e4SLinus Torvalds 			entry = NULL;
4091da177e4SLinus Torvalds 		}
4101da177e4SLinus Torvalds 	}
4111da177e4SLinus Torvalds 	snd_minor_info_entry = entry;
4121da177e4SLinus Torvalds 	return 0;
4131da177e4SLinus Torvalds }
4141da177e4SLinus Torvalds 
4151da177e4SLinus Torvalds int __exit snd_minor_info_done(void)
4161da177e4SLinus Torvalds {
417746d4a02STakashi Iwai 	snd_info_free_entry(snd_minor_info_entry);
4181da177e4SLinus Torvalds 	return 0;
4191da177e4SLinus Torvalds }
420e28563ccSTakashi Iwai #endif /* CONFIG_PROC_FS */
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds /*
4231da177e4SLinus Torvalds  *  INIT PART
4241da177e4SLinus Torvalds  */
4251da177e4SLinus Torvalds 
4261da177e4SLinus Torvalds static int __init alsa_sound_init(void)
4271da177e4SLinus Torvalds {
4281da177e4SLinus Torvalds 	snd_major = major;
4291da177e4SLinus Torvalds 	snd_ecards_limit = cards_limit;
4301da177e4SLinus Torvalds 	if (register_chrdev(major, "alsa", &snd_fops)) {
4311da177e4SLinus Torvalds 		snd_printk(KERN_ERR "unable to register native major device number %d\n", major);
4321da177e4SLinus Torvalds 		return -EIO;
4331da177e4SLinus Torvalds 	}
4341da177e4SLinus Torvalds 	if (snd_info_init() < 0) {
4351da177e4SLinus Torvalds 		unregister_chrdev(major, "alsa");
4361da177e4SLinus Torvalds 		return -ENOMEM;
4371da177e4SLinus Torvalds 	}
4381da177e4SLinus Torvalds 	snd_info_minor_register();
4391da177e4SLinus Torvalds #ifndef MODULE
4401da177e4SLinus Torvalds 	printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n");
4411da177e4SLinus Torvalds #endif
4421da177e4SLinus Torvalds 	return 0;
4431da177e4SLinus Torvalds }
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds static void __exit alsa_sound_exit(void)
4461da177e4SLinus Torvalds {
4471da177e4SLinus Torvalds 	snd_info_minor_unregister();
4481da177e4SLinus Torvalds 	snd_info_done();
4491da177e4SLinus Torvalds 	if (unregister_chrdev(major, "alsa") != 0)
4501da177e4SLinus Torvalds 		snd_printk(KERN_ERR "unable to unregister major device number %d\n", major);
4511da177e4SLinus Torvalds }
4521da177e4SLinus Torvalds 
4531da177e4SLinus Torvalds module_init(alsa_sound_init)
4541da177e4SLinus Torvalds module_exit(alsa_sound_exit)
455