18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Advanced Linux Sound Architecture 48c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/init.h> 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/time.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <sound/core.h> 138c2ecf20Sopenharmony_ci#include <sound/minors.h> 148c2ecf20Sopenharmony_ci#include <sound/info.h> 158c2ecf20Sopenharmony_ci#include <sound/control.h> 168c2ecf20Sopenharmony_ci#include <sound/initval.h> 178c2ecf20Sopenharmony_ci#include <linux/kmod.h> 188c2ecf20Sopenharmony_ci#include <linux/mutex.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic int major = CONFIG_SND_MAJOR; 218c2ecf20Sopenharmony_ciint snd_major; 228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_major); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int cards_limit = 1; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards."); 288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 298c2ecf20Sopenharmony_cimodule_param(major, int, 0444); 308c2ecf20Sopenharmony_ciMODULE_PARM_DESC(major, "Major # for sound driver."); 318c2ecf20Sopenharmony_cimodule_param(cards_limit, int, 0444); 328c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cards_limit, "Count of auto-loadable soundcards."); 338c2ecf20Sopenharmony_ciMODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* this one holds the actual max. card number currently available. 368c2ecf20Sopenharmony_ci * as default, it's identical with cards_limit option. when more 378c2ecf20Sopenharmony_ci * modules are loaded manually, this limit number increases, too. 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ciint snd_ecards_limit; 408c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ecards_limit); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic struct snd_minor *snd_minors[SNDRV_OS_MINORS]; 438c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(sound_mutex); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#ifdef CONFIG_MODULES 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/** 488c2ecf20Sopenharmony_ci * snd_request_card - try to load the card module 498c2ecf20Sopenharmony_ci * @card: the card number 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * Tries to load the module "snd-card-X" for the given card number 528c2ecf20Sopenharmony_ci * via request_module. Returns immediately if already loaded. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_civoid snd_request_card(int card) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci if (snd_card_locked(card)) 578c2ecf20Sopenharmony_ci return; 588c2ecf20Sopenharmony_ci if (card < 0 || card >= cards_limit) 598c2ecf20Sopenharmony_ci return; 608c2ecf20Sopenharmony_ci request_module("snd-card-%i", card); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_request_card); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void snd_request_other(int minor) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci char *str; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci switch (minor) { 698c2ecf20Sopenharmony_ci case SNDRV_MINOR_SEQUENCER: str = "snd-seq"; break; 708c2ecf20Sopenharmony_ci case SNDRV_MINOR_TIMER: str = "snd-timer"; break; 718c2ecf20Sopenharmony_ci default: return; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci request_module(str); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#endif /* modular kernel */ 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/** 798c2ecf20Sopenharmony_ci * snd_lookup_minor_data - get user data of a registered device 808c2ecf20Sopenharmony_ci * @minor: the minor number 818c2ecf20Sopenharmony_ci * @type: device type (SNDRV_DEVICE_TYPE_XXX) 828c2ecf20Sopenharmony_ci * 838c2ecf20Sopenharmony_ci * Checks that a minor device with the specified type is registered, and returns 848c2ecf20Sopenharmony_ci * its user data pointer. 858c2ecf20Sopenharmony_ci * 868c2ecf20Sopenharmony_ci * This function increments the reference counter of the card instance 878c2ecf20Sopenharmony_ci * if an associated instance with the given minor number and type is found. 888c2ecf20Sopenharmony_ci * The caller must call snd_card_unref() appropriately later. 898c2ecf20Sopenharmony_ci * 908c2ecf20Sopenharmony_ci * Return: The user data pointer if the specified device is found. %NULL 918c2ecf20Sopenharmony_ci * otherwise. 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_civoid *snd_lookup_minor_data(unsigned int minor, int type) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct snd_minor *mreg; 968c2ecf20Sopenharmony_ci void *private_data; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (minor >= ARRAY_SIZE(snd_minors)) 998c2ecf20Sopenharmony_ci return NULL; 1008c2ecf20Sopenharmony_ci mutex_lock(&sound_mutex); 1018c2ecf20Sopenharmony_ci mreg = snd_minors[minor]; 1028c2ecf20Sopenharmony_ci if (mreg && mreg->type == type) { 1038c2ecf20Sopenharmony_ci private_data = mreg->private_data; 1048c2ecf20Sopenharmony_ci if (private_data && mreg->card_ptr) 1058c2ecf20Sopenharmony_ci get_device(&mreg->card_ptr->card_dev); 1068c2ecf20Sopenharmony_ci } else 1078c2ecf20Sopenharmony_ci private_data = NULL; 1088c2ecf20Sopenharmony_ci mutex_unlock(&sound_mutex); 1098c2ecf20Sopenharmony_ci return private_data; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_lookup_minor_data); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#ifdef CONFIG_MODULES 1148c2ecf20Sopenharmony_cistatic struct snd_minor *autoload_device(unsigned int minor) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci int dev; 1178c2ecf20Sopenharmony_ci mutex_unlock(&sound_mutex); /* release lock temporarily */ 1188c2ecf20Sopenharmony_ci dev = SNDRV_MINOR_DEVICE(minor); 1198c2ecf20Sopenharmony_ci if (dev == SNDRV_MINOR_CONTROL) { 1208c2ecf20Sopenharmony_ci /* /dev/aloadC? */ 1218c2ecf20Sopenharmony_ci int card = SNDRV_MINOR_CARD(minor); 1228c2ecf20Sopenharmony_ci struct snd_card *ref = snd_card_ref(card); 1238c2ecf20Sopenharmony_ci if (!ref) 1248c2ecf20Sopenharmony_ci snd_request_card(card); 1258c2ecf20Sopenharmony_ci else 1268c2ecf20Sopenharmony_ci snd_card_unref(ref); 1278c2ecf20Sopenharmony_ci } else if (dev == SNDRV_MINOR_GLOBAL) { 1288c2ecf20Sopenharmony_ci /* /dev/aloadSEQ */ 1298c2ecf20Sopenharmony_ci snd_request_other(minor); 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci mutex_lock(&sound_mutex); /* reacuire lock */ 1328c2ecf20Sopenharmony_ci return snd_minors[minor]; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci#else /* !CONFIG_MODULES */ 1358c2ecf20Sopenharmony_ci#define autoload_device(minor) NULL 1368c2ecf20Sopenharmony_ci#endif /* CONFIG_MODULES */ 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int snd_open(struct inode *inode, struct file *file) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci unsigned int minor = iminor(inode); 1418c2ecf20Sopenharmony_ci struct snd_minor *mptr = NULL; 1428c2ecf20Sopenharmony_ci const struct file_operations *new_fops; 1438c2ecf20Sopenharmony_ci int err = 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (minor >= ARRAY_SIZE(snd_minors)) 1468c2ecf20Sopenharmony_ci return -ENODEV; 1478c2ecf20Sopenharmony_ci mutex_lock(&sound_mutex); 1488c2ecf20Sopenharmony_ci mptr = snd_minors[minor]; 1498c2ecf20Sopenharmony_ci if (mptr == NULL) { 1508c2ecf20Sopenharmony_ci mptr = autoload_device(minor); 1518c2ecf20Sopenharmony_ci if (!mptr) { 1528c2ecf20Sopenharmony_ci mutex_unlock(&sound_mutex); 1538c2ecf20Sopenharmony_ci return -ENODEV; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci new_fops = fops_get(mptr->f_ops); 1578c2ecf20Sopenharmony_ci mutex_unlock(&sound_mutex); 1588c2ecf20Sopenharmony_ci if (!new_fops) 1598c2ecf20Sopenharmony_ci return -ENODEV; 1608c2ecf20Sopenharmony_ci replace_fops(file, new_fops); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (file->f_op->open) 1638c2ecf20Sopenharmony_ci err = file->f_op->open(inode, file); 1648c2ecf20Sopenharmony_ci return err; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic const struct file_operations snd_fops = 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1708c2ecf20Sopenharmony_ci .open = snd_open, 1718c2ecf20Sopenharmony_ci .llseek = noop_llseek, 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DYNAMIC_MINORS 1758c2ecf20Sopenharmony_cistatic int snd_find_free_minor(int type, struct snd_card *card, int dev) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci int minor; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* static minors for module auto loading */ 1808c2ecf20Sopenharmony_ci if (type == SNDRV_DEVICE_TYPE_SEQUENCER) 1818c2ecf20Sopenharmony_ci return SNDRV_MINOR_SEQUENCER; 1828c2ecf20Sopenharmony_ci if (type == SNDRV_DEVICE_TYPE_TIMER) 1838c2ecf20Sopenharmony_ci return SNDRV_MINOR_TIMER; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) { 1868c2ecf20Sopenharmony_ci /* skip static minors still used for module auto loading */ 1878c2ecf20Sopenharmony_ci if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL) 1888c2ecf20Sopenharmony_ci continue; 1898c2ecf20Sopenharmony_ci if (minor == SNDRV_MINOR_SEQUENCER || 1908c2ecf20Sopenharmony_ci minor == SNDRV_MINOR_TIMER) 1918c2ecf20Sopenharmony_ci continue; 1928c2ecf20Sopenharmony_ci if (!snd_minors[minor]) 1938c2ecf20Sopenharmony_ci return minor; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci return -EBUSY; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci#else 1988c2ecf20Sopenharmony_cistatic int snd_find_free_minor(int type, struct snd_card *card, int dev) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci int minor; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci switch (type) { 2038c2ecf20Sopenharmony_ci case SNDRV_DEVICE_TYPE_SEQUENCER: 2048c2ecf20Sopenharmony_ci case SNDRV_DEVICE_TYPE_TIMER: 2058c2ecf20Sopenharmony_ci minor = type; 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci case SNDRV_DEVICE_TYPE_CONTROL: 2088c2ecf20Sopenharmony_ci if (snd_BUG_ON(!card)) 2098c2ecf20Sopenharmony_ci return -EINVAL; 2108c2ecf20Sopenharmony_ci minor = SNDRV_MINOR(card->number, type); 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci case SNDRV_DEVICE_TYPE_HWDEP: 2138c2ecf20Sopenharmony_ci case SNDRV_DEVICE_TYPE_RAWMIDI: 2148c2ecf20Sopenharmony_ci case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: 2158c2ecf20Sopenharmony_ci case SNDRV_DEVICE_TYPE_PCM_CAPTURE: 2168c2ecf20Sopenharmony_ci case SNDRV_DEVICE_TYPE_COMPRESS: 2178c2ecf20Sopenharmony_ci if (snd_BUG_ON(!card)) 2188c2ecf20Sopenharmony_ci return -EINVAL; 2198c2ecf20Sopenharmony_ci minor = SNDRV_MINOR(card->number, type + dev); 2208c2ecf20Sopenharmony_ci break; 2218c2ecf20Sopenharmony_ci default: 2228c2ecf20Sopenharmony_ci return -EINVAL; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS)) 2258c2ecf20Sopenharmony_ci return -EINVAL; 2268c2ecf20Sopenharmony_ci if (snd_minors[minor]) 2278c2ecf20Sopenharmony_ci return -EBUSY; 2288c2ecf20Sopenharmony_ci return minor; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci#endif 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/** 2338c2ecf20Sopenharmony_ci * snd_register_device - Register the ALSA device file for the card 2348c2ecf20Sopenharmony_ci * @type: the device type, SNDRV_DEVICE_TYPE_XXX 2358c2ecf20Sopenharmony_ci * @card: the card instance 2368c2ecf20Sopenharmony_ci * @dev: the device index 2378c2ecf20Sopenharmony_ci * @f_ops: the file operations 2388c2ecf20Sopenharmony_ci * @private_data: user pointer for f_ops->open() 2398c2ecf20Sopenharmony_ci * @device: the device to register 2408c2ecf20Sopenharmony_ci * 2418c2ecf20Sopenharmony_ci * Registers an ALSA device file for the given card. 2428c2ecf20Sopenharmony_ci * The operators have to be set in reg parameter. 2438c2ecf20Sopenharmony_ci * 2448c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_ciint snd_register_device(int type, struct snd_card *card, int dev, 2478c2ecf20Sopenharmony_ci const struct file_operations *f_ops, 2488c2ecf20Sopenharmony_ci void *private_data, struct device *device) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci int minor; 2518c2ecf20Sopenharmony_ci int err = 0; 2528c2ecf20Sopenharmony_ci struct snd_minor *preg; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (snd_BUG_ON(!device)) 2558c2ecf20Sopenharmony_ci return -EINVAL; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci preg = kmalloc(sizeof *preg, GFP_KERNEL); 2588c2ecf20Sopenharmony_ci if (preg == NULL) 2598c2ecf20Sopenharmony_ci return -ENOMEM; 2608c2ecf20Sopenharmony_ci preg->type = type; 2618c2ecf20Sopenharmony_ci preg->card = card ? card->number : -1; 2628c2ecf20Sopenharmony_ci preg->device = dev; 2638c2ecf20Sopenharmony_ci preg->f_ops = f_ops; 2648c2ecf20Sopenharmony_ci preg->private_data = private_data; 2658c2ecf20Sopenharmony_ci preg->card_ptr = card; 2668c2ecf20Sopenharmony_ci mutex_lock(&sound_mutex); 2678c2ecf20Sopenharmony_ci minor = snd_find_free_minor(type, card, dev); 2688c2ecf20Sopenharmony_ci if (minor < 0) { 2698c2ecf20Sopenharmony_ci err = minor; 2708c2ecf20Sopenharmony_ci goto error; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci preg->dev = device; 2748c2ecf20Sopenharmony_ci device->devt = MKDEV(major, minor); 2758c2ecf20Sopenharmony_ci err = device_add(device); 2768c2ecf20Sopenharmony_ci if (err < 0) 2778c2ecf20Sopenharmony_ci goto error; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci snd_minors[minor] = preg; 2808c2ecf20Sopenharmony_ci error: 2818c2ecf20Sopenharmony_ci mutex_unlock(&sound_mutex); 2828c2ecf20Sopenharmony_ci if (err < 0) 2838c2ecf20Sopenharmony_ci kfree(preg); 2848c2ecf20Sopenharmony_ci return err; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_register_device); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/** 2898c2ecf20Sopenharmony_ci * snd_unregister_device - unregister the device on the given card 2908c2ecf20Sopenharmony_ci * @dev: the device instance 2918c2ecf20Sopenharmony_ci * 2928c2ecf20Sopenharmony_ci * Unregisters the device file already registered via 2938c2ecf20Sopenharmony_ci * snd_register_device(). 2948c2ecf20Sopenharmony_ci * 2958c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_ciint snd_unregister_device(struct device *dev) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci int minor; 3008c2ecf20Sopenharmony_ci struct snd_minor *preg; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci mutex_lock(&sound_mutex); 3038c2ecf20Sopenharmony_ci for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) { 3048c2ecf20Sopenharmony_ci preg = snd_minors[minor]; 3058c2ecf20Sopenharmony_ci if (preg && preg->dev == dev) { 3068c2ecf20Sopenharmony_ci snd_minors[minor] = NULL; 3078c2ecf20Sopenharmony_ci device_del(dev); 3088c2ecf20Sopenharmony_ci kfree(preg); 3098c2ecf20Sopenharmony_ci break; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci mutex_unlock(&sound_mutex); 3138c2ecf20Sopenharmony_ci if (minor >= ARRAY_SIZE(snd_minors)) 3148c2ecf20Sopenharmony_ci return -ENOENT; 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_unregister_device); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_PROC_FS 3208c2ecf20Sopenharmony_ci/* 3218c2ecf20Sopenharmony_ci * INFO PART 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_cistatic const char *snd_device_type_name(int type) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci switch (type) { 3268c2ecf20Sopenharmony_ci case SNDRV_DEVICE_TYPE_CONTROL: 3278c2ecf20Sopenharmony_ci return "control"; 3288c2ecf20Sopenharmony_ci case SNDRV_DEVICE_TYPE_HWDEP: 3298c2ecf20Sopenharmony_ci return "hardware dependent"; 3308c2ecf20Sopenharmony_ci case SNDRV_DEVICE_TYPE_RAWMIDI: 3318c2ecf20Sopenharmony_ci return "raw midi"; 3328c2ecf20Sopenharmony_ci case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: 3338c2ecf20Sopenharmony_ci return "digital audio playback"; 3348c2ecf20Sopenharmony_ci case SNDRV_DEVICE_TYPE_PCM_CAPTURE: 3358c2ecf20Sopenharmony_ci return "digital audio capture"; 3368c2ecf20Sopenharmony_ci case SNDRV_DEVICE_TYPE_SEQUENCER: 3378c2ecf20Sopenharmony_ci return "sequencer"; 3388c2ecf20Sopenharmony_ci case SNDRV_DEVICE_TYPE_TIMER: 3398c2ecf20Sopenharmony_ci return "timer"; 3408c2ecf20Sopenharmony_ci default: 3418c2ecf20Sopenharmony_ci return "?"; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci int minor; 3488c2ecf20Sopenharmony_ci struct snd_minor *mptr; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci mutex_lock(&sound_mutex); 3518c2ecf20Sopenharmony_ci for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) { 3528c2ecf20Sopenharmony_ci if (!(mptr = snd_minors[minor])) 3538c2ecf20Sopenharmony_ci continue; 3548c2ecf20Sopenharmony_ci if (mptr->card >= 0) { 3558c2ecf20Sopenharmony_ci if (mptr->device >= 0) 3568c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%3i: [%2i-%2i]: %s\n", 3578c2ecf20Sopenharmony_ci minor, mptr->card, mptr->device, 3588c2ecf20Sopenharmony_ci snd_device_type_name(mptr->type)); 3598c2ecf20Sopenharmony_ci else 3608c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%3i: [%2i] : %s\n", 3618c2ecf20Sopenharmony_ci minor, mptr->card, 3628c2ecf20Sopenharmony_ci snd_device_type_name(mptr->type)); 3638c2ecf20Sopenharmony_ci } else 3648c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%3i: : %s\n", minor, 3658c2ecf20Sopenharmony_ci snd_device_type_name(mptr->type)); 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci mutex_unlock(&sound_mutex); 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ciint __init snd_minor_info_init(void) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct snd_info_entry *entry; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL); 3758c2ecf20Sopenharmony_ci if (!entry) 3768c2ecf20Sopenharmony_ci return -ENOMEM; 3778c2ecf20Sopenharmony_ci entry->c.text.read = snd_minor_info_read; 3788c2ecf20Sopenharmony_ci return snd_info_register(entry); /* freed in error path */ 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci#endif /* CONFIG_SND_PROC_FS */ 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci/* 3838c2ecf20Sopenharmony_ci * INIT PART 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic int __init alsa_sound_init(void) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci snd_major = major; 3898c2ecf20Sopenharmony_ci snd_ecards_limit = cards_limit; 3908c2ecf20Sopenharmony_ci if (register_chrdev(major, "alsa", &snd_fops)) { 3918c2ecf20Sopenharmony_ci pr_err("ALSA core: unable to register native major device number %d\n", major); 3928c2ecf20Sopenharmony_ci return -EIO; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci if (snd_info_init() < 0) { 3958c2ecf20Sopenharmony_ci unregister_chrdev(major, "alsa"); 3968c2ecf20Sopenharmony_ci return -ENOMEM; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci#ifndef MODULE 3998c2ecf20Sopenharmony_ci pr_info("Advanced Linux Sound Architecture Driver Initialized.\n"); 4008c2ecf20Sopenharmony_ci#endif 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic void __exit alsa_sound_exit(void) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci snd_info_done(); 4078c2ecf20Sopenharmony_ci unregister_chrdev(major, "alsa"); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cisubsys_initcall(alsa_sound_init); 4118c2ecf20Sopenharmony_cimodule_exit(alsa_sound_exit); 412