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) 121a0830dbdSTakashi Iwai atomic_inc(&mreg->card_ptr->refcount); 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; 156*e84f9e57SAl 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 } 170*e84f9e57SAl Viro new_fops = fops_get(mptr->f_ops); 1714cf19b84STakashi Iwai mutex_unlock(&sound_mutex); 172*e84f9e57SAl Viro if (!new_fops) 173*e84f9e57SAl Viro return -ENODEV; 174*e84f9e57SAl Viro replace_fops(file, new_fops); 1754cf19b84STakashi Iwai 176*e84f9e57SAl 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 /** 24512b131c4SJohannes Berg * snd_register_device_for_dev - 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() 2511da177e4SLinus Torvalds * @name: the device file name 25212b131c4SJohannes Berg * @device: the &struct device to link this new device to 2531da177e4SLinus Torvalds * 2541da177e4SLinus Torvalds * Registers an ALSA device file for the given card. 2551da177e4SLinus Torvalds * The operators have to be set in reg parameter. 2561da177e4SLinus Torvalds * 257eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 2581da177e4SLinus Torvalds */ 25912b131c4SJohannes Berg int snd_register_device_for_dev(int type, struct snd_card *card, int dev, 26012b131c4SJohannes Berg const struct file_operations *f_ops, 26112b131c4SJohannes Berg void *private_data, 26212b131c4SJohannes Berg const char *name, struct device *device) 2631da177e4SLinus Torvalds { 264332682b1SClemens Ladisch int minor; 265512bbd6aSTakashi Iwai struct snd_minor *preg; 2661da177e4SLinus Torvalds 2677eaa943cSTakashi Iwai if (snd_BUG_ON(!name)) 2687eaa943cSTakashi Iwai return -EINVAL; 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) { 2871a60d4c5SIngo Molnar mutex_unlock(&sound_mutex); 2881da177e4SLinus Torvalds kfree(preg); 289332682b1SClemens Ladisch return minor; 2901da177e4SLinus Torvalds } 2916983b724SClemens Ladisch snd_minors[minor] = preg; 292abe9ab8fSGreg Kroah-Hartman preg->dev = device_create(sound_class, device, MKDEV(major, minor), 2935d99a8b8SGreg Kroah-Hartman private_data, "%s", name); 2942469049eSMariusz Kozlowski if (IS_ERR(preg->dev)) { 2952469049eSMariusz Kozlowski snd_minors[minor] = NULL; 2962469049eSMariusz Kozlowski mutex_unlock(&sound_mutex); 2972469049eSMariusz Kozlowski minor = PTR_ERR(preg->dev); 2982469049eSMariusz Kozlowski kfree(preg); 2992469049eSMariusz Kozlowski return minor; 3002469049eSMariusz Kozlowski } 3012469049eSMariusz Kozlowski 3021a60d4c5SIngo Molnar mutex_unlock(&sound_mutex); 3031da177e4SLinus Torvalds return 0; 3041da177e4SLinus Torvalds } 3051da177e4SLinus Torvalds 30612b131c4SJohannes Berg EXPORT_SYMBOL(snd_register_device_for_dev); 307c0d3fb39STakashi Iwai 3089d19f48cSTakashi Iwai /* find the matching minor record 3099d19f48cSTakashi Iwai * return the index of snd_minor, or -1 if not found 3109d19f48cSTakashi Iwai */ 3119d19f48cSTakashi Iwai static int find_snd_minor(int type, struct snd_card *card, int dev) 3129d19f48cSTakashi Iwai { 3139d19f48cSTakashi Iwai int cardnum, minor; 3149d19f48cSTakashi Iwai struct snd_minor *mptr; 3159d19f48cSTakashi Iwai 3169d19f48cSTakashi Iwai cardnum = card ? card->number : -1; 3179d19f48cSTakashi Iwai for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) 3189d19f48cSTakashi Iwai if ((mptr = snd_minors[minor]) != NULL && 3199d19f48cSTakashi Iwai mptr->type == type && 3209d19f48cSTakashi Iwai mptr->card == cardnum && 3219d19f48cSTakashi Iwai mptr->device == dev) 3229d19f48cSTakashi Iwai return minor; 3239d19f48cSTakashi Iwai return -1; 3249d19f48cSTakashi Iwai } 3259d19f48cSTakashi Iwai 3261da177e4SLinus Torvalds /** 3271da177e4SLinus Torvalds * snd_unregister_device - unregister the device on the given card 3281da177e4SLinus Torvalds * @type: the device type, SNDRV_DEVICE_TYPE_XXX 3291da177e4SLinus Torvalds * @card: the card instance 3301da177e4SLinus Torvalds * @dev: the device index 3311da177e4SLinus Torvalds * 3321da177e4SLinus Torvalds * Unregisters the device file already registered via 3331da177e4SLinus Torvalds * snd_register_device(). 3341da177e4SLinus Torvalds * 335eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 3361da177e4SLinus Torvalds */ 337512bbd6aSTakashi Iwai int snd_unregister_device(int type, struct snd_card *card, int dev) 3381da177e4SLinus Torvalds { 3399d19f48cSTakashi Iwai int minor; 3401da177e4SLinus Torvalds 3411a60d4c5SIngo Molnar mutex_lock(&sound_mutex); 3429d19f48cSTakashi Iwai minor = find_snd_minor(type, card, dev); 3439d19f48cSTakashi Iwai if (minor < 0) { 3441a60d4c5SIngo Molnar mutex_unlock(&sound_mutex); 3451da177e4SLinus Torvalds return -EINVAL; 3461da177e4SLinus Torvalds } 3471da177e4SLinus Torvalds 348d80f19faSGreg Kroah-Hartman device_destroy(sound_class, MKDEV(major, minor)); 3491da177e4SLinus Torvalds 3509d19f48cSTakashi Iwai kfree(snd_minors[minor]); 3516983b724SClemens Ladisch snd_minors[minor] = NULL; 3521a60d4c5SIngo Molnar mutex_unlock(&sound_mutex); 3531da177e4SLinus Torvalds return 0; 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds 356c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_unregister_device); 357c0d3fb39STakashi Iwai 3589d19f48cSTakashi Iwai int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev, 359d80f19faSGreg Kroah-Hartman struct device_attribute *attr) 3609d19f48cSTakashi Iwai { 3619d19f48cSTakashi Iwai int minor, ret = -EINVAL; 362d80f19faSGreg Kroah-Hartman struct device *d; 3639d19f48cSTakashi Iwai 3649d19f48cSTakashi Iwai mutex_lock(&sound_mutex); 3659d19f48cSTakashi Iwai minor = find_snd_minor(type, card, dev); 366d80f19faSGreg Kroah-Hartman if (minor >= 0 && (d = snd_minors[minor]->dev) != NULL) 367d80f19faSGreg Kroah-Hartman ret = device_create_file(d, attr); 3689d19f48cSTakashi Iwai mutex_unlock(&sound_mutex); 3699d19f48cSTakashi Iwai return ret; 3709d19f48cSTakashi Iwai 3719d19f48cSTakashi Iwai } 3729d19f48cSTakashi Iwai 3739d19f48cSTakashi Iwai EXPORT_SYMBOL(snd_add_device_sysfs_file); 3749d19f48cSTakashi Iwai 375e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS 3761da177e4SLinus Torvalds /* 3771da177e4SLinus Torvalds * INFO PART 3781da177e4SLinus Torvalds */ 3791da177e4SLinus Torvalds 3806581f4e7STakashi Iwai static struct snd_info_entry *snd_minor_info_entry; 3811da177e4SLinus Torvalds 3822af677fcSClemens Ladisch static const char *snd_device_type_name(int type) 3832af677fcSClemens Ladisch { 3842af677fcSClemens Ladisch switch (type) { 3852af677fcSClemens Ladisch case SNDRV_DEVICE_TYPE_CONTROL: 3862af677fcSClemens Ladisch return "control"; 3872af677fcSClemens Ladisch case SNDRV_DEVICE_TYPE_HWDEP: 3882af677fcSClemens Ladisch return "hardware dependent"; 3892af677fcSClemens Ladisch case SNDRV_DEVICE_TYPE_RAWMIDI: 3902af677fcSClemens Ladisch return "raw midi"; 3912af677fcSClemens Ladisch case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: 3922af677fcSClemens Ladisch return "digital audio playback"; 3932af677fcSClemens Ladisch case SNDRV_DEVICE_TYPE_PCM_CAPTURE: 3942af677fcSClemens Ladisch return "digital audio capture"; 3952af677fcSClemens Ladisch case SNDRV_DEVICE_TYPE_SEQUENCER: 3962af677fcSClemens Ladisch return "sequencer"; 3972af677fcSClemens Ladisch case SNDRV_DEVICE_TYPE_TIMER: 3982af677fcSClemens Ladisch return "timer"; 3992af677fcSClemens Ladisch default: 4002af677fcSClemens Ladisch return "?"; 4012af677fcSClemens Ladisch } 4022af677fcSClemens Ladisch } 4032af677fcSClemens Ladisch 404512bbd6aSTakashi Iwai static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) 4051da177e4SLinus Torvalds { 4066983b724SClemens Ladisch int minor; 407512bbd6aSTakashi Iwai struct snd_minor *mptr; 4081da177e4SLinus Torvalds 4091a60d4c5SIngo Molnar mutex_lock(&sound_mutex); 4106983b724SClemens Ladisch for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) { 4116983b724SClemens Ladisch if (!(mptr = snd_minors[minor])) 4126983b724SClemens Ladisch continue; 4136983b724SClemens Ladisch if (mptr->card >= 0) { 4146983b724SClemens Ladisch if (mptr->device >= 0) 415d001544dSClemens Ladisch snd_iprintf(buffer, "%3i: [%2i-%2i]: %s\n", 4166983b724SClemens Ladisch minor, mptr->card, mptr->device, 4176983b724SClemens Ladisch snd_device_type_name(mptr->type)); 4181da177e4SLinus Torvalds else 419d001544dSClemens Ladisch snd_iprintf(buffer, "%3i: [%2i] : %s\n", 4206983b724SClemens Ladisch minor, mptr->card, 4216983b724SClemens Ladisch snd_device_type_name(mptr->type)); 4226983b724SClemens Ladisch } else 4236983b724SClemens Ladisch snd_iprintf(buffer, "%3i: : %s\n", minor, 4246983b724SClemens Ladisch snd_device_type_name(mptr->type)); 4251da177e4SLinus Torvalds } 4261a60d4c5SIngo Molnar mutex_unlock(&sound_mutex); 4271da177e4SLinus Torvalds } 4281da177e4SLinus Torvalds 4291da177e4SLinus Torvalds int __init snd_minor_info_init(void) 4301da177e4SLinus Torvalds { 431512bbd6aSTakashi Iwai struct snd_info_entry *entry; 4321da177e4SLinus Torvalds 4331da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL); 4341da177e4SLinus Torvalds if (entry) { 4351da177e4SLinus Torvalds entry->c.text.read = snd_minor_info_read; 4361da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 4371da177e4SLinus Torvalds snd_info_free_entry(entry); 4381da177e4SLinus Torvalds entry = NULL; 4391da177e4SLinus Torvalds } 4401da177e4SLinus Torvalds } 4411da177e4SLinus Torvalds snd_minor_info_entry = entry; 4421da177e4SLinus Torvalds return 0; 4431da177e4SLinus Torvalds } 4441da177e4SLinus Torvalds 4451da177e4SLinus Torvalds int __exit snd_minor_info_done(void) 4461da177e4SLinus Torvalds { 447746d4a02STakashi Iwai snd_info_free_entry(snd_minor_info_entry); 4481da177e4SLinus Torvalds return 0; 4491da177e4SLinus Torvalds } 450e28563ccSTakashi Iwai #endif /* CONFIG_PROC_FS */ 4511da177e4SLinus Torvalds 4521da177e4SLinus Torvalds /* 4531da177e4SLinus Torvalds * INIT PART 4541da177e4SLinus Torvalds */ 4551da177e4SLinus Torvalds 4561da177e4SLinus Torvalds static int __init alsa_sound_init(void) 4571da177e4SLinus Torvalds { 4581da177e4SLinus Torvalds snd_major = major; 4591da177e4SLinus Torvalds snd_ecards_limit = cards_limit; 4601da177e4SLinus Torvalds if (register_chrdev(major, "alsa", &snd_fops)) { 4611da177e4SLinus Torvalds snd_printk(KERN_ERR "unable to register native major device number %d\n", major); 4621da177e4SLinus Torvalds return -EIO; 4631da177e4SLinus Torvalds } 4641da177e4SLinus Torvalds if (snd_info_init() < 0) { 4651da177e4SLinus Torvalds unregister_chrdev(major, "alsa"); 4661da177e4SLinus Torvalds return -ENOMEM; 4671da177e4SLinus Torvalds } 4681da177e4SLinus Torvalds snd_info_minor_register(); 4691da177e4SLinus Torvalds #ifndef MODULE 47042662748SJaroslav Kysela printk(KERN_INFO "Advanced Linux Sound Architecture Driver Initialized.\n"); 4711da177e4SLinus Torvalds #endif 4721da177e4SLinus Torvalds return 0; 4731da177e4SLinus Torvalds } 4741da177e4SLinus Torvalds 4751da177e4SLinus Torvalds static void __exit alsa_sound_exit(void) 4761da177e4SLinus Torvalds { 4771da177e4SLinus Torvalds snd_info_minor_unregister(); 4781da177e4SLinus Torvalds snd_info_done(); 47968fc4fabSAkinobu Mita unregister_chrdev(major, "alsa"); 4801da177e4SLinus Torvalds } 4811da177e4SLinus Torvalds 482c181a13aSThadeu Lima de Souza Cascardo subsys_initcall(alsa_sound_init); 483c181a13aSThadeu Lima de Souza Cascardo module_exit(alsa_sound_exit); 484