11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Advanced Linux Sound Architecture 3c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.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 <linux/init.h> 231da177e4SLinus Torvalds #include <linux/slab.h> 241da177e4SLinus Torvalds #include <linux/time.h> 259a1a2a1dSTakashi Iwai #include <linux/device.h> 2665a77217SPaul Gortmaker #include <linux/module.h> 271da177e4SLinus Torvalds #include <sound/core.h> 281da177e4SLinus Torvalds #include <sound/minors.h> 291da177e4SLinus Torvalds #include <sound/info.h> 301da177e4SLinus Torvalds #include <sound/control.h> 311da177e4SLinus Torvalds #include <sound/initval.h> 321da177e4SLinus Torvalds #include <linux/kmod.h> 331a60d4c5SIngo Molnar #include <linux/mutex.h> 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds static int major = CONFIG_SND_MAJOR; 361da177e4SLinus Torvalds int snd_major; 37c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_major); 38c0d3fb39STakashi Iwai 391da177e4SLinus Torvalds static int cards_limit = 1; 401da177e4SLinus Torvalds 41c1017a4cSJaroslav Kysela MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 421da177e4SLinus Torvalds MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards."); 431da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 441da177e4SLinus Torvalds module_param(major, int, 0444); 451da177e4SLinus Torvalds MODULE_PARM_DESC(major, "Major # for sound driver."); 461da177e4SLinus Torvalds module_param(cards_limit, int, 0444); 471da177e4SLinus Torvalds MODULE_PARM_DESC(cards_limit, "Count of auto-loadable soundcards."); 481da177e4SLinus Torvalds MODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR); 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds /* this one holds the actual max. card number currently available. 511da177e4SLinus Torvalds * as default, it's identical with cards_limit option. when more 521da177e4SLinus Torvalds * modules are loaded manually, this limit number increases, too. 531da177e4SLinus Torvalds */ 541da177e4SLinus Torvalds int snd_ecards_limit; 55c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_ecards_limit); 561da177e4SLinus Torvalds 576983b724SClemens Ladisch static struct snd_minor *snd_minors[SNDRV_OS_MINORS]; 581a60d4c5SIngo Molnar static DEFINE_MUTEX(sound_mutex); 591da177e4SLinus Torvalds 60ee2da997SJohannes Berg #ifdef CONFIG_MODULES 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds /** 631da177e4SLinus Torvalds * snd_request_card - try to load the card module 641da177e4SLinus Torvalds * @card: the card number 651da177e4SLinus Torvalds * 661da177e4SLinus Torvalds * Tries to load the module "snd-card-X" for the given card number 67ee2da997SJohannes Berg * via request_module. Returns immediately if already loaded. 681da177e4SLinus Torvalds */ 691da177e4SLinus Torvalds void snd_request_card(int card) 701da177e4SLinus Torvalds { 71746df948STakashi Iwai if (snd_card_locked(card)) 721da177e4SLinus Torvalds return; 731da177e4SLinus Torvalds if (card < 0 || card >= cards_limit) 741da177e4SLinus Torvalds return; 751da177e4SLinus Torvalds request_module("snd-card-%i", card); 761da177e4SLinus Torvalds } 771da177e4SLinus Torvalds 78c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_request_card); 79c0d3fb39STakashi Iwai 801da177e4SLinus Torvalds static void snd_request_other(int minor) 811da177e4SLinus Torvalds { 821da177e4SLinus Torvalds char *str; 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds switch (minor) { 851da177e4SLinus Torvalds case SNDRV_MINOR_SEQUENCER: str = "snd-seq"; break; 861da177e4SLinus Torvalds case SNDRV_MINOR_TIMER: str = "snd-timer"; break; 871da177e4SLinus Torvalds default: return; 881da177e4SLinus Torvalds } 891da177e4SLinus Torvalds request_module(str); 901da177e4SLinus Torvalds } 911da177e4SLinus Torvalds 92ee2da997SJohannes Berg #endif /* modular kernel */ 931da177e4SLinus Torvalds 94f87135f5SClemens Ladisch /** 95f87135f5SClemens Ladisch * snd_lookup_minor_data - get user data of a registered device 96f87135f5SClemens Ladisch * @minor: the minor number 97f87135f5SClemens Ladisch * @type: device type (SNDRV_DEVICE_TYPE_XXX) 98f87135f5SClemens Ladisch * 99f87135f5SClemens Ladisch * Checks that a minor device with the specified type is registered, and returns 100f87135f5SClemens Ladisch * its user data pointer. 101a0830dbdSTakashi Iwai * 102a0830dbdSTakashi Iwai * This function increments the reference counter of the card instance 103a0830dbdSTakashi Iwai * if an associated instance with the given minor number and type is found. 104a0830dbdSTakashi Iwai * The caller must call snd_card_unref() appropriately later. 105eb7c06e8SYacine Belkadi * 106eb7c06e8SYacine Belkadi * Return: The user data pointer if the specified device is found. %NULL 107eb7c06e8SYacine Belkadi * otherwise. 108f87135f5SClemens Ladisch */ 109f87135f5SClemens Ladisch void *snd_lookup_minor_data(unsigned int minor, int type) 110f87135f5SClemens Ladisch { 111f87135f5SClemens Ladisch struct snd_minor *mreg; 112f87135f5SClemens Ladisch void *private_data; 113f87135f5SClemens Ladisch 1143a63e444SAdrian Bunk if (minor >= ARRAY_SIZE(snd_minors)) 115f87135f5SClemens Ladisch return NULL; 1161a60d4c5SIngo Molnar mutex_lock(&sound_mutex); 117f87135f5SClemens Ladisch mreg = snd_minors[minor]; 118a0830dbdSTakashi Iwai if (mreg && mreg->type == type) { 119f87135f5SClemens Ladisch private_data = mreg->private_data; 1208bb4d9ceSTakashi Iwai if (private_data && mreg->card_ptr) 121f2464064STakashi Iwai get_device(&mreg->card_ptr->card_dev); 122a0830dbdSTakashi Iwai } else 123f87135f5SClemens Ladisch private_data = NULL; 1241a60d4c5SIngo Molnar mutex_unlock(&sound_mutex); 125f87135f5SClemens Ladisch return private_data; 126f87135f5SClemens Ladisch } 127f87135f5SClemens Ladisch 128c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_lookup_minor_data); 129c0d3fb39STakashi Iwai 130ee2da997SJohannes Berg #ifdef CONFIG_MODULES 1314cf19b84STakashi Iwai static struct snd_minor *autoload_device(unsigned int minor) 1324cf19b84STakashi Iwai { 1334cf19b84STakashi Iwai int dev; 1344cf19b84STakashi Iwai mutex_unlock(&sound_mutex); /* release lock temporarily */ 1354cf19b84STakashi Iwai dev = SNDRV_MINOR_DEVICE(minor); 136332682b1SClemens Ladisch if (dev == SNDRV_MINOR_CONTROL) { 137332682b1SClemens Ladisch /* /dev/aloadC? */ 138332682b1SClemens Ladisch int card = SNDRV_MINOR_CARD(minor); 1391da177e4SLinus Torvalds if (snd_cards[card] == NULL) 140332682b1SClemens Ladisch snd_request_card(card); 141332682b1SClemens Ladisch } else if (dev == SNDRV_MINOR_GLOBAL) { 142332682b1SClemens Ladisch /* /dev/aloadSEQ */ 1431da177e4SLinus Torvalds snd_request_other(minor); 1441da177e4SLinus Torvalds } 1454cf19b84STakashi Iwai mutex_lock(&sound_mutex); /* reacuire lock */ 1464cf19b84STakashi Iwai return snd_minors[minor]; 1474cf19b84STakashi Iwai } 1484cf19b84STakashi Iwai #else /* !CONFIG_MODULES */ 1494cf19b84STakashi Iwai #define autoload_device(minor) NULL 1504cf19b84STakashi Iwai #endif /* CONFIG_MODULES */ 1514cf19b84STakashi Iwai 1524cf19b84STakashi Iwai static int snd_open(struct inode *inode, struct file *file) 1534cf19b84STakashi Iwai { 1544cf19b84STakashi Iwai unsigned int minor = iminor(inode); 1554cf19b84STakashi Iwai struct snd_minor *mptr = NULL; 156e84f9e57SAl Viro const struct file_operations *new_fops; 1574cf19b84STakashi Iwai int err = 0; 1584cf19b84STakashi Iwai 1594cf19b84STakashi Iwai if (minor >= ARRAY_SIZE(snd_minors)) 1601da177e4SLinus Torvalds return -ENODEV; 1614cf19b84STakashi Iwai mutex_lock(&sound_mutex); 1624cf19b84STakashi Iwai mptr = snd_minors[minor]; 1634cf19b84STakashi Iwai if (mptr == NULL) { 1644cf19b84STakashi Iwai mptr = autoload_device(minor); 1654cf19b84STakashi Iwai if (!mptr) { 1664cf19b84STakashi Iwai mutex_unlock(&sound_mutex); 1674cf19b84STakashi Iwai return -ENODEV; 1684cf19b84STakashi Iwai } 169332682b1SClemens Ladisch } 170e84f9e57SAl Viro new_fops = fops_get(mptr->f_ops); 1714cf19b84STakashi Iwai mutex_unlock(&sound_mutex); 172e84f9e57SAl Viro if (!new_fops) 173e84f9e57SAl Viro return -ENODEV; 174e84f9e57SAl Viro replace_fops(file, new_fops); 1754cf19b84STakashi Iwai 176e84f9e57SAl Viro if (file->f_op->open) 1771da177e4SLinus Torvalds err = file->f_op->open(inode, file); 1781da177e4SLinus Torvalds return err; 1791da177e4SLinus Torvalds } 1801da177e4SLinus Torvalds 1819c2e08c5SArjan van de Ven static const struct file_operations snd_fops = 1821da177e4SLinus Torvalds { 1831da177e4SLinus Torvalds .owner = THIS_MODULE, 1846038f373SArnd Bergmann .open = snd_open, 1856038f373SArnd Bergmann .llseek = noop_llseek, 1861da177e4SLinus Torvalds }; 1871da177e4SLinus Torvalds 188332682b1SClemens Ladisch #ifdef CONFIG_SND_DYNAMIC_MINORS 18903cfe6f5SKay Sievers static int snd_find_free_minor(int type) 190332682b1SClemens Ladisch { 191332682b1SClemens Ladisch int minor; 192332682b1SClemens Ladisch 19303cfe6f5SKay Sievers /* static minors for module auto loading */ 19403cfe6f5SKay Sievers if (type == SNDRV_DEVICE_TYPE_SEQUENCER) 19503cfe6f5SKay Sievers return SNDRV_MINOR_SEQUENCER; 19603cfe6f5SKay Sievers if (type == SNDRV_DEVICE_TYPE_TIMER) 19703cfe6f5SKay Sievers return SNDRV_MINOR_TIMER; 19803cfe6f5SKay Sievers 199332682b1SClemens Ladisch for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) { 20003cfe6f5SKay Sievers /* skip static minors still used for module auto loading */ 20103cfe6f5SKay Sievers if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL) 20203cfe6f5SKay Sievers continue; 20303cfe6f5SKay Sievers if (minor == SNDRV_MINOR_SEQUENCER || 20403cfe6f5SKay Sievers minor == SNDRV_MINOR_TIMER) 205332682b1SClemens Ladisch continue; 206332682b1SClemens Ladisch if (!snd_minors[minor]) 207332682b1SClemens Ladisch return minor; 208332682b1SClemens Ladisch } 209332682b1SClemens Ladisch return -EBUSY; 210332682b1SClemens Ladisch } 211332682b1SClemens Ladisch #else 212512bbd6aSTakashi Iwai static int snd_kernel_minor(int type, struct snd_card *card, int dev) 2131da177e4SLinus Torvalds { 2141da177e4SLinus Torvalds int minor; 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds switch (type) { 2171da177e4SLinus Torvalds case SNDRV_DEVICE_TYPE_SEQUENCER: 2181da177e4SLinus Torvalds case SNDRV_DEVICE_TYPE_TIMER: 2191da177e4SLinus Torvalds minor = type; 2201da177e4SLinus Torvalds break; 2211da177e4SLinus Torvalds case SNDRV_DEVICE_TYPE_CONTROL: 2227eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 2237eaa943cSTakashi Iwai return -EINVAL; 2241da177e4SLinus Torvalds minor = SNDRV_MINOR(card->number, type); 2251da177e4SLinus Torvalds break; 2261da177e4SLinus Torvalds case SNDRV_DEVICE_TYPE_HWDEP: 2271da177e4SLinus Torvalds case SNDRV_DEVICE_TYPE_RAWMIDI: 2281da177e4SLinus Torvalds case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: 2291da177e4SLinus Torvalds case SNDRV_DEVICE_TYPE_PCM_CAPTURE: 2303eafc959SOmair Mohammed Abdullah case SNDRV_DEVICE_TYPE_COMPRESS: 2317eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 2327eaa943cSTakashi Iwai return -EINVAL; 2331da177e4SLinus Torvalds minor = SNDRV_MINOR(card->number, type + dev); 2341da177e4SLinus Torvalds break; 2351da177e4SLinus Torvalds default: 2361da177e4SLinus Torvalds return -EINVAL; 2371da177e4SLinus Torvalds } 2387eaa943cSTakashi Iwai if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS)) 2397eaa943cSTakashi Iwai return -EINVAL; 2401da177e4SLinus Torvalds return minor; 2411da177e4SLinus Torvalds } 242332682b1SClemens Ladisch #endif 2431da177e4SLinus Torvalds 2441da177e4SLinus Torvalds /** 245*40a4b263STakashi Iwai * snd_register_device - Register the ALSA device file for the card 2461da177e4SLinus Torvalds * @type: the device type, SNDRV_DEVICE_TYPE_XXX 2471da177e4SLinus Torvalds * @card: the card instance 2481da177e4SLinus Torvalds * @dev: the device index 2492af677fcSClemens Ladisch * @f_ops: the file operations 250f87135f5SClemens Ladisch * @private_data: user pointer for f_ops->open() 251*40a4b263STakashi Iwai * @device: the device to register 2521da177e4SLinus Torvalds * 2531da177e4SLinus Torvalds * Registers an ALSA device file for the given card. 2541da177e4SLinus Torvalds * The operators have to be set in reg parameter. 2551da177e4SLinus Torvalds * 256eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 2571da177e4SLinus Torvalds */ 258*40a4b263STakashi Iwai int snd_register_device(int type, struct snd_card *card, int dev, 25912b131c4SJohannes Berg const struct file_operations *f_ops, 260*40a4b263STakashi Iwai void *private_data, struct device *device) 2611da177e4SLinus Torvalds { 262332682b1SClemens Ladisch int minor; 26392b7952dSTakashi Iwai int err = 0; 264512bbd6aSTakashi Iwai struct snd_minor *preg; 2651da177e4SLinus Torvalds 266*40a4b263STakashi Iwai if (snd_BUG_ON(!device)) 267*40a4b263STakashi Iwai return -EINVAL; 268*40a4b263STakashi Iwai 269562b590dSClemens Ladisch preg = kmalloc(sizeof *preg, GFP_KERNEL); 2701da177e4SLinus Torvalds if (preg == NULL) 2711da177e4SLinus Torvalds return -ENOMEM; 2722af677fcSClemens Ladisch preg->type = type; 2736983b724SClemens Ladisch preg->card = card ? card->number : -1; 2741da177e4SLinus Torvalds preg->device = dev; 2752af677fcSClemens Ladisch preg->f_ops = f_ops; 276f87135f5SClemens Ladisch preg->private_data = private_data; 277a0830dbdSTakashi Iwai preg->card_ptr = card; 2781a60d4c5SIngo Molnar mutex_lock(&sound_mutex); 279332682b1SClemens Ladisch #ifdef CONFIG_SND_DYNAMIC_MINORS 28003cfe6f5SKay Sievers minor = snd_find_free_minor(type); 281332682b1SClemens Ladisch #else 282332682b1SClemens Ladisch minor = snd_kernel_minor(type, card, dev); 283332682b1SClemens Ladisch if (minor >= 0 && snd_minors[minor]) 284332682b1SClemens Ladisch minor = -EBUSY; 285332682b1SClemens Ladisch #endif 286332682b1SClemens Ladisch if (minor < 0) { 28792b7952dSTakashi Iwai err = minor; 28892b7952dSTakashi Iwai goto error; 2892469049eSMariusz Kozlowski } 2902469049eSMariusz Kozlowski 29192b7952dSTakashi Iwai preg->dev = device; 29292b7952dSTakashi Iwai device->devt = MKDEV(major, minor); 29392b7952dSTakashi Iwai err = device_add(device); 29492b7952dSTakashi Iwai if (err < 0) 29592b7952dSTakashi Iwai goto error; 29692b7952dSTakashi Iwai 29792b7952dSTakashi Iwai snd_minors[minor] = preg; 29892b7952dSTakashi Iwai error: 2991a60d4c5SIngo Molnar mutex_unlock(&sound_mutex); 30092b7952dSTakashi Iwai if (err < 0) 30192b7952dSTakashi Iwai kfree(preg); 30292b7952dSTakashi Iwai return err; 3031da177e4SLinus Torvalds } 304*40a4b263STakashi Iwai EXPORT_SYMBOL(snd_register_device); 305c0d3fb39STakashi Iwai 3069d19f48cSTakashi Iwai /* find the matching minor record 3079d19f48cSTakashi Iwai * return the index of snd_minor, or -1 if not found 3089d19f48cSTakashi Iwai */ 3099d19f48cSTakashi Iwai static int find_snd_minor(int type, struct snd_card *card, int dev) 3109d19f48cSTakashi Iwai { 3119d19f48cSTakashi Iwai int cardnum, minor; 3129d19f48cSTakashi Iwai struct snd_minor *mptr; 3139d19f48cSTakashi Iwai 3149d19f48cSTakashi Iwai cardnum = card ? card->number : -1; 3159d19f48cSTakashi Iwai for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) 3169d19f48cSTakashi Iwai if ((mptr = snd_minors[minor]) != NULL && 3179d19f48cSTakashi Iwai mptr->type == type && 3189d19f48cSTakashi Iwai mptr->card == cardnum && 3199d19f48cSTakashi Iwai mptr->device == dev) 3209d19f48cSTakashi Iwai return minor; 3219d19f48cSTakashi Iwai return -1; 3229d19f48cSTakashi Iwai } 3239d19f48cSTakashi Iwai 3241da177e4SLinus Torvalds /** 3251da177e4SLinus Torvalds * snd_unregister_device - unregister the device on the given card 326*40a4b263STakashi Iwai * @dev: the device instance 3271da177e4SLinus Torvalds * 3281da177e4SLinus Torvalds * Unregisters the device file already registered via 3291da177e4SLinus Torvalds * snd_register_device(). 3301da177e4SLinus Torvalds * 331eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 3321da177e4SLinus Torvalds */ 333*40a4b263STakashi Iwai int snd_unregister_device(struct device *dev) 3341da177e4SLinus Torvalds { 3359d19f48cSTakashi Iwai int minor; 33692b7952dSTakashi Iwai struct snd_minor *preg; 3371da177e4SLinus Torvalds 3381a60d4c5SIngo Molnar mutex_lock(&sound_mutex); 339*40a4b263STakashi Iwai for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) { 34092b7952dSTakashi Iwai preg = snd_minors[minor]; 341*40a4b263STakashi Iwai if (preg && preg->dev == dev) { 3426983b724SClemens Ladisch snd_minors[minor] = NULL; 343*40a4b263STakashi Iwai device_del(dev); 344*40a4b263STakashi Iwai kfree(preg); 345*40a4b263STakashi Iwai break; 346*40a4b263STakashi Iwai } 347*40a4b263STakashi Iwai } 3481a60d4c5SIngo Molnar mutex_unlock(&sound_mutex); 349*40a4b263STakashi Iwai if (minor >= ARRAY_SIZE(snd_minors)) 350*40a4b263STakashi Iwai return -ENOENT; 3511da177e4SLinus Torvalds return 0; 3521da177e4SLinus Torvalds } 353c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_unregister_device); 354c0d3fb39STakashi Iwai 355eb9c38d5STakashi Iwai /** 356eb9c38d5STakashi Iwai * snd_get_device - get the assigned device to the given type and device number 357eb9c38d5STakashi Iwai * @type: the device type, SNDRV_DEVICE_TYPE_XXX 358eb9c38d5STakashi Iwai * @card:the card instance 359eb9c38d5STakashi Iwai * @dev: the device index 360eb9c38d5STakashi Iwai * 361eb9c38d5STakashi Iwai * The caller needs to release it via put_device() after using it. 362caa751baSTakashi Iwai */ 363caa751baSTakashi Iwai struct device *snd_get_device(int type, struct snd_card *card, int dev) 3649d19f48cSTakashi Iwai { 365caa751baSTakashi Iwai int minor; 366caa751baSTakashi Iwai struct device *d = NULL; 3679d19f48cSTakashi Iwai 3689d19f48cSTakashi Iwai mutex_lock(&sound_mutex); 3699d19f48cSTakashi Iwai minor = find_snd_minor(type, card, dev); 370caa751baSTakashi Iwai if (minor >= 0) { 371caa751baSTakashi Iwai d = snd_minors[minor]->dev; 372caa751baSTakashi Iwai if (d) 373caa751baSTakashi Iwai get_device(d); 3749d19f48cSTakashi Iwai } 375caa751baSTakashi Iwai mutex_unlock(&sound_mutex); 376caa751baSTakashi Iwai return d; 377caa751baSTakashi Iwai } 378caa751baSTakashi Iwai EXPORT_SYMBOL(snd_get_device); 3799d19f48cSTakashi Iwai 380e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS 3811da177e4SLinus Torvalds /* 3821da177e4SLinus Torvalds * INFO PART 3831da177e4SLinus Torvalds */ 3841da177e4SLinus Torvalds 3856581f4e7STakashi Iwai static struct snd_info_entry *snd_minor_info_entry; 3861da177e4SLinus Torvalds 3872af677fcSClemens Ladisch static const char *snd_device_type_name(int type) 3882af677fcSClemens Ladisch { 3892af677fcSClemens Ladisch switch (type) { 3902af677fcSClemens Ladisch case SNDRV_DEVICE_TYPE_CONTROL: 3912af677fcSClemens Ladisch return "control"; 3922af677fcSClemens Ladisch case SNDRV_DEVICE_TYPE_HWDEP: 3932af677fcSClemens Ladisch return "hardware dependent"; 3942af677fcSClemens Ladisch case SNDRV_DEVICE_TYPE_RAWMIDI: 3952af677fcSClemens Ladisch return "raw midi"; 3962af677fcSClemens Ladisch case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: 3972af677fcSClemens Ladisch return "digital audio playback"; 3982af677fcSClemens Ladisch case SNDRV_DEVICE_TYPE_PCM_CAPTURE: 3992af677fcSClemens Ladisch return "digital audio capture"; 4002af677fcSClemens Ladisch case SNDRV_DEVICE_TYPE_SEQUENCER: 4012af677fcSClemens Ladisch return "sequencer"; 4022af677fcSClemens Ladisch case SNDRV_DEVICE_TYPE_TIMER: 4032af677fcSClemens Ladisch return "timer"; 4042af677fcSClemens Ladisch default: 4052af677fcSClemens Ladisch return "?"; 4062af677fcSClemens Ladisch } 4072af677fcSClemens Ladisch } 4082af677fcSClemens Ladisch 409512bbd6aSTakashi Iwai static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) 4101da177e4SLinus Torvalds { 4116983b724SClemens Ladisch int minor; 412512bbd6aSTakashi Iwai struct snd_minor *mptr; 4131da177e4SLinus Torvalds 4141a60d4c5SIngo Molnar mutex_lock(&sound_mutex); 4156983b724SClemens Ladisch for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) { 4166983b724SClemens Ladisch if (!(mptr = snd_minors[minor])) 4176983b724SClemens Ladisch continue; 4186983b724SClemens Ladisch if (mptr->card >= 0) { 4196983b724SClemens Ladisch if (mptr->device >= 0) 420d001544dSClemens Ladisch snd_iprintf(buffer, "%3i: [%2i-%2i]: %s\n", 4216983b724SClemens Ladisch minor, mptr->card, mptr->device, 4226983b724SClemens Ladisch snd_device_type_name(mptr->type)); 4231da177e4SLinus Torvalds else 424d001544dSClemens Ladisch snd_iprintf(buffer, "%3i: [%2i] : %s\n", 4256983b724SClemens Ladisch minor, mptr->card, 4266983b724SClemens Ladisch snd_device_type_name(mptr->type)); 4276983b724SClemens Ladisch } else 4286983b724SClemens Ladisch snd_iprintf(buffer, "%3i: : %s\n", minor, 4296983b724SClemens Ladisch snd_device_type_name(mptr->type)); 4301da177e4SLinus Torvalds } 4311a60d4c5SIngo Molnar mutex_unlock(&sound_mutex); 4321da177e4SLinus Torvalds } 4331da177e4SLinus Torvalds 4341da177e4SLinus Torvalds int __init snd_minor_info_init(void) 4351da177e4SLinus Torvalds { 436512bbd6aSTakashi Iwai struct snd_info_entry *entry; 4371da177e4SLinus Torvalds 4381da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL); 4391da177e4SLinus Torvalds if (entry) { 4401da177e4SLinus Torvalds entry->c.text.read = snd_minor_info_read; 4411da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 4421da177e4SLinus Torvalds snd_info_free_entry(entry); 4431da177e4SLinus Torvalds entry = NULL; 4441da177e4SLinus Torvalds } 4451da177e4SLinus Torvalds } 4461da177e4SLinus Torvalds snd_minor_info_entry = entry; 4471da177e4SLinus Torvalds return 0; 4481da177e4SLinus Torvalds } 4491da177e4SLinus Torvalds 4501da177e4SLinus Torvalds int __exit snd_minor_info_done(void) 4511da177e4SLinus Torvalds { 452746d4a02STakashi Iwai snd_info_free_entry(snd_minor_info_entry); 4531da177e4SLinus Torvalds return 0; 4541da177e4SLinus Torvalds } 455e28563ccSTakashi Iwai #endif /* CONFIG_PROC_FS */ 4561da177e4SLinus Torvalds 4571da177e4SLinus Torvalds /* 4581da177e4SLinus Torvalds * INIT PART 4591da177e4SLinus Torvalds */ 4601da177e4SLinus Torvalds 4611da177e4SLinus Torvalds static int __init alsa_sound_init(void) 4621da177e4SLinus Torvalds { 4631da177e4SLinus Torvalds snd_major = major; 4641da177e4SLinus Torvalds snd_ecards_limit = cards_limit; 4651da177e4SLinus Torvalds if (register_chrdev(major, "alsa", &snd_fops)) { 466f2f9307aSTakashi Iwai pr_err("ALSA core: unable to register native major device number %d\n", major); 4671da177e4SLinus Torvalds return -EIO; 4681da177e4SLinus Torvalds } 4691da177e4SLinus Torvalds if (snd_info_init() < 0) { 4701da177e4SLinus Torvalds unregister_chrdev(major, "alsa"); 4711da177e4SLinus Torvalds return -ENOMEM; 4721da177e4SLinus Torvalds } 4731da177e4SLinus Torvalds snd_info_minor_register(); 4741da177e4SLinus Torvalds #ifndef MODULE 475f2f9307aSTakashi Iwai pr_info("Advanced Linux Sound Architecture Driver Initialized.\n"); 4761da177e4SLinus Torvalds #endif 4771da177e4SLinus Torvalds return 0; 4781da177e4SLinus Torvalds } 4791da177e4SLinus Torvalds 4801da177e4SLinus Torvalds static void __exit alsa_sound_exit(void) 4811da177e4SLinus Torvalds { 4821da177e4SLinus Torvalds snd_info_minor_unregister(); 4831da177e4SLinus Torvalds snd_info_done(); 48468fc4fabSAkinobu Mita unregister_chrdev(major, "alsa"); 4851da177e4SLinus Torvalds } 4861da177e4SLinus Torvalds 487c181a13aSThadeu Lima de Souza Cascardo subsys_initcall(alsa_sound_init); 488c181a13aSThadeu Lima de Souza Cascardo module_exit(alsa_sound_exit); 489