162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Advanced Linux Sound Architecture 462306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/time.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/debugfs.h> 1362306a36Sopenharmony_ci#include <sound/core.h> 1462306a36Sopenharmony_ci#include <sound/minors.h> 1562306a36Sopenharmony_ci#include <sound/info.h> 1662306a36Sopenharmony_ci#include <sound/control.h> 1762306a36Sopenharmony_ci#include <sound/initval.h> 1862306a36Sopenharmony_ci#include <linux/kmod.h> 1962306a36Sopenharmony_ci#include <linux/mutex.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic int major = CONFIG_SND_MAJOR; 2262306a36Sopenharmony_ciint snd_major; 2362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_major); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int cards_limit = 1; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 2862306a36Sopenharmony_ciMODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards."); 2962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3062306a36Sopenharmony_cimodule_param(major, int, 0444); 3162306a36Sopenharmony_ciMODULE_PARM_DESC(major, "Major # for sound driver."); 3262306a36Sopenharmony_cimodule_param(cards_limit, int, 0444); 3362306a36Sopenharmony_ciMODULE_PARM_DESC(cards_limit, "Count of auto-loadable soundcards."); 3462306a36Sopenharmony_ciMODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* this one holds the actual max. card number currently available. 3762306a36Sopenharmony_ci * as default, it's identical with cards_limit option. when more 3862306a36Sopenharmony_ci * modules are loaded manually, this limit number increases, too. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ciint snd_ecards_limit; 4162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ecards_limit); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG 4462306a36Sopenharmony_cistruct dentry *sound_debugfs_root; 4562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sound_debugfs_root); 4662306a36Sopenharmony_ci#endif 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic struct snd_minor *snd_minors[SNDRV_OS_MINORS]; 4962306a36Sopenharmony_cistatic DEFINE_MUTEX(sound_mutex); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#ifdef CONFIG_MODULES 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/** 5462306a36Sopenharmony_ci * snd_request_card - try to load the card module 5562306a36Sopenharmony_ci * @card: the card number 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * Tries to load the module "snd-card-X" for the given card number 5862306a36Sopenharmony_ci * via request_module. Returns immediately if already loaded. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_civoid snd_request_card(int card) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci if (snd_card_locked(card)) 6362306a36Sopenharmony_ci return; 6462306a36Sopenharmony_ci if (card < 0 || card >= cards_limit) 6562306a36Sopenharmony_ci return; 6662306a36Sopenharmony_ci request_module("snd-card-%i", card); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_request_card); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic void snd_request_other(int minor) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci char *str; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci switch (minor) { 7562306a36Sopenharmony_ci case SNDRV_MINOR_SEQUENCER: str = "snd-seq"; break; 7662306a36Sopenharmony_ci case SNDRV_MINOR_TIMER: str = "snd-timer"; break; 7762306a36Sopenharmony_ci default: return; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci request_module(str); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#endif /* modular kernel */ 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/** 8562306a36Sopenharmony_ci * snd_lookup_minor_data - get user data of a registered device 8662306a36Sopenharmony_ci * @minor: the minor number 8762306a36Sopenharmony_ci * @type: device type (SNDRV_DEVICE_TYPE_XXX) 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * Checks that a minor device with the specified type is registered, and returns 9062306a36Sopenharmony_ci * its user data pointer. 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * This function increments the reference counter of the card instance 9362306a36Sopenharmony_ci * if an associated instance with the given minor number and type is found. 9462306a36Sopenharmony_ci * The caller must call snd_card_unref() appropriately later. 9562306a36Sopenharmony_ci * 9662306a36Sopenharmony_ci * Return: The user data pointer if the specified device is found. %NULL 9762306a36Sopenharmony_ci * otherwise. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_civoid *snd_lookup_minor_data(unsigned int minor, int type) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct snd_minor *mreg; 10262306a36Sopenharmony_ci void *private_data; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (minor >= ARRAY_SIZE(snd_minors)) 10562306a36Sopenharmony_ci return NULL; 10662306a36Sopenharmony_ci mutex_lock(&sound_mutex); 10762306a36Sopenharmony_ci mreg = snd_minors[minor]; 10862306a36Sopenharmony_ci if (mreg && mreg->type == type) { 10962306a36Sopenharmony_ci private_data = mreg->private_data; 11062306a36Sopenharmony_ci if (private_data && mreg->card_ptr) 11162306a36Sopenharmony_ci get_device(&mreg->card_ptr->card_dev); 11262306a36Sopenharmony_ci } else 11362306a36Sopenharmony_ci private_data = NULL; 11462306a36Sopenharmony_ci mutex_unlock(&sound_mutex); 11562306a36Sopenharmony_ci return private_data; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_lookup_minor_data); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#ifdef CONFIG_MODULES 12062306a36Sopenharmony_cistatic struct snd_minor *autoload_device(unsigned int minor) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci int dev; 12362306a36Sopenharmony_ci mutex_unlock(&sound_mutex); /* release lock temporarily */ 12462306a36Sopenharmony_ci dev = SNDRV_MINOR_DEVICE(minor); 12562306a36Sopenharmony_ci if (dev == SNDRV_MINOR_CONTROL) { 12662306a36Sopenharmony_ci /* /dev/aloadC? */ 12762306a36Sopenharmony_ci int card = SNDRV_MINOR_CARD(minor); 12862306a36Sopenharmony_ci struct snd_card *ref = snd_card_ref(card); 12962306a36Sopenharmony_ci if (!ref) 13062306a36Sopenharmony_ci snd_request_card(card); 13162306a36Sopenharmony_ci else 13262306a36Sopenharmony_ci snd_card_unref(ref); 13362306a36Sopenharmony_ci } else if (dev == SNDRV_MINOR_GLOBAL) { 13462306a36Sopenharmony_ci /* /dev/aloadSEQ */ 13562306a36Sopenharmony_ci snd_request_other(minor); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci mutex_lock(&sound_mutex); /* reacuire lock */ 13862306a36Sopenharmony_ci return snd_minors[minor]; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci#else /* !CONFIG_MODULES */ 14162306a36Sopenharmony_ci#define autoload_device(minor) NULL 14262306a36Sopenharmony_ci#endif /* CONFIG_MODULES */ 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int snd_open(struct inode *inode, struct file *file) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci unsigned int minor = iminor(inode); 14762306a36Sopenharmony_ci struct snd_minor *mptr = NULL; 14862306a36Sopenharmony_ci const struct file_operations *new_fops; 14962306a36Sopenharmony_ci int err = 0; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (minor >= ARRAY_SIZE(snd_minors)) 15262306a36Sopenharmony_ci return -ENODEV; 15362306a36Sopenharmony_ci mutex_lock(&sound_mutex); 15462306a36Sopenharmony_ci mptr = snd_minors[minor]; 15562306a36Sopenharmony_ci if (mptr == NULL) { 15662306a36Sopenharmony_ci mptr = autoload_device(minor); 15762306a36Sopenharmony_ci if (!mptr) { 15862306a36Sopenharmony_ci mutex_unlock(&sound_mutex); 15962306a36Sopenharmony_ci return -ENODEV; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci new_fops = fops_get(mptr->f_ops); 16362306a36Sopenharmony_ci mutex_unlock(&sound_mutex); 16462306a36Sopenharmony_ci if (!new_fops) 16562306a36Sopenharmony_ci return -ENODEV; 16662306a36Sopenharmony_ci replace_fops(file, new_fops); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (file->f_op->open) 16962306a36Sopenharmony_ci err = file->f_op->open(inode, file); 17062306a36Sopenharmony_ci return err; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic const struct file_operations snd_fops = 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci .owner = THIS_MODULE, 17662306a36Sopenharmony_ci .open = snd_open, 17762306a36Sopenharmony_ci .llseek = noop_llseek, 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci#ifdef CONFIG_SND_DYNAMIC_MINORS 18162306a36Sopenharmony_cistatic int snd_find_free_minor(int type, struct snd_card *card, int dev) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci int minor; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* static minors for module auto loading */ 18662306a36Sopenharmony_ci if (type == SNDRV_DEVICE_TYPE_SEQUENCER) 18762306a36Sopenharmony_ci return SNDRV_MINOR_SEQUENCER; 18862306a36Sopenharmony_ci if (type == SNDRV_DEVICE_TYPE_TIMER) 18962306a36Sopenharmony_ci return SNDRV_MINOR_TIMER; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) { 19262306a36Sopenharmony_ci /* skip static minors still used for module auto loading */ 19362306a36Sopenharmony_ci if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL) 19462306a36Sopenharmony_ci continue; 19562306a36Sopenharmony_ci if (minor == SNDRV_MINOR_SEQUENCER || 19662306a36Sopenharmony_ci minor == SNDRV_MINOR_TIMER) 19762306a36Sopenharmony_ci continue; 19862306a36Sopenharmony_ci if (!snd_minors[minor]) 19962306a36Sopenharmony_ci return minor; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci return -EBUSY; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci#else 20462306a36Sopenharmony_cistatic int snd_find_free_minor(int type, struct snd_card *card, int dev) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci int minor; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci switch (type) { 20962306a36Sopenharmony_ci case SNDRV_DEVICE_TYPE_SEQUENCER: 21062306a36Sopenharmony_ci case SNDRV_DEVICE_TYPE_TIMER: 21162306a36Sopenharmony_ci minor = type; 21262306a36Sopenharmony_ci break; 21362306a36Sopenharmony_ci case SNDRV_DEVICE_TYPE_CONTROL: 21462306a36Sopenharmony_ci if (snd_BUG_ON(!card)) 21562306a36Sopenharmony_ci return -EINVAL; 21662306a36Sopenharmony_ci minor = SNDRV_MINOR(card->number, type); 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci case SNDRV_DEVICE_TYPE_HWDEP: 21962306a36Sopenharmony_ci case SNDRV_DEVICE_TYPE_RAWMIDI: 22062306a36Sopenharmony_ci case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: 22162306a36Sopenharmony_ci case SNDRV_DEVICE_TYPE_PCM_CAPTURE: 22262306a36Sopenharmony_ci case SNDRV_DEVICE_TYPE_COMPRESS: 22362306a36Sopenharmony_ci if (snd_BUG_ON(!card)) 22462306a36Sopenharmony_ci return -EINVAL; 22562306a36Sopenharmony_ci minor = SNDRV_MINOR(card->number, type + dev); 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci default: 22862306a36Sopenharmony_ci return -EINVAL; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS)) 23162306a36Sopenharmony_ci return -EINVAL; 23262306a36Sopenharmony_ci if (snd_minors[minor]) 23362306a36Sopenharmony_ci return -EBUSY; 23462306a36Sopenharmony_ci return minor; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci#endif 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci/** 23962306a36Sopenharmony_ci * snd_register_device - Register the ALSA device file for the card 24062306a36Sopenharmony_ci * @type: the device type, SNDRV_DEVICE_TYPE_XXX 24162306a36Sopenharmony_ci * @card: the card instance 24262306a36Sopenharmony_ci * @dev: the device index 24362306a36Sopenharmony_ci * @f_ops: the file operations 24462306a36Sopenharmony_ci * @private_data: user pointer for f_ops->open() 24562306a36Sopenharmony_ci * @device: the device to register 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci * Registers an ALSA device file for the given card. 24862306a36Sopenharmony_ci * The operators have to be set in reg parameter. 24962306a36Sopenharmony_ci * 25062306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ciint snd_register_device(int type, struct snd_card *card, int dev, 25362306a36Sopenharmony_ci const struct file_operations *f_ops, 25462306a36Sopenharmony_ci void *private_data, struct device *device) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci int minor; 25762306a36Sopenharmony_ci int err = 0; 25862306a36Sopenharmony_ci struct snd_minor *preg; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (snd_BUG_ON(!device)) 26162306a36Sopenharmony_ci return -EINVAL; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci preg = kmalloc(sizeof *preg, GFP_KERNEL); 26462306a36Sopenharmony_ci if (preg == NULL) 26562306a36Sopenharmony_ci return -ENOMEM; 26662306a36Sopenharmony_ci preg->type = type; 26762306a36Sopenharmony_ci preg->card = card ? card->number : -1; 26862306a36Sopenharmony_ci preg->device = dev; 26962306a36Sopenharmony_ci preg->f_ops = f_ops; 27062306a36Sopenharmony_ci preg->private_data = private_data; 27162306a36Sopenharmony_ci preg->card_ptr = card; 27262306a36Sopenharmony_ci mutex_lock(&sound_mutex); 27362306a36Sopenharmony_ci minor = snd_find_free_minor(type, card, dev); 27462306a36Sopenharmony_ci if (minor < 0) { 27562306a36Sopenharmony_ci err = minor; 27662306a36Sopenharmony_ci goto error; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci preg->dev = device; 28062306a36Sopenharmony_ci device->devt = MKDEV(major, minor); 28162306a36Sopenharmony_ci err = device_add(device); 28262306a36Sopenharmony_ci if (err < 0) 28362306a36Sopenharmony_ci goto error; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci snd_minors[minor] = preg; 28662306a36Sopenharmony_ci error: 28762306a36Sopenharmony_ci mutex_unlock(&sound_mutex); 28862306a36Sopenharmony_ci if (err < 0) 28962306a36Sopenharmony_ci kfree(preg); 29062306a36Sopenharmony_ci return err; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ciEXPORT_SYMBOL(snd_register_device); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci/** 29562306a36Sopenharmony_ci * snd_unregister_device - unregister the device on the given card 29662306a36Sopenharmony_ci * @dev: the device instance 29762306a36Sopenharmony_ci * 29862306a36Sopenharmony_ci * Unregisters the device file already registered via 29962306a36Sopenharmony_ci * snd_register_device(). 30062306a36Sopenharmony_ci * 30162306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_ciint snd_unregister_device(struct device *dev) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci int minor; 30662306a36Sopenharmony_ci struct snd_minor *preg; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci mutex_lock(&sound_mutex); 30962306a36Sopenharmony_ci for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) { 31062306a36Sopenharmony_ci preg = snd_minors[minor]; 31162306a36Sopenharmony_ci if (preg && preg->dev == dev) { 31262306a36Sopenharmony_ci snd_minors[minor] = NULL; 31362306a36Sopenharmony_ci device_del(dev); 31462306a36Sopenharmony_ci kfree(preg); 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci mutex_unlock(&sound_mutex); 31962306a36Sopenharmony_ci if (minor >= ARRAY_SIZE(snd_minors)) 32062306a36Sopenharmony_ci return -ENOENT; 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_unregister_device); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci#ifdef CONFIG_SND_PROC_FS 32662306a36Sopenharmony_ci/* 32762306a36Sopenharmony_ci * INFO PART 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_cistatic const char *snd_device_type_name(int type) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci switch (type) { 33262306a36Sopenharmony_ci case SNDRV_DEVICE_TYPE_CONTROL: 33362306a36Sopenharmony_ci return "control"; 33462306a36Sopenharmony_ci case SNDRV_DEVICE_TYPE_HWDEP: 33562306a36Sopenharmony_ci return "hardware dependent"; 33662306a36Sopenharmony_ci case SNDRV_DEVICE_TYPE_RAWMIDI: 33762306a36Sopenharmony_ci return "raw midi"; 33862306a36Sopenharmony_ci case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: 33962306a36Sopenharmony_ci return "digital audio playback"; 34062306a36Sopenharmony_ci case SNDRV_DEVICE_TYPE_PCM_CAPTURE: 34162306a36Sopenharmony_ci return "digital audio capture"; 34262306a36Sopenharmony_ci case SNDRV_DEVICE_TYPE_SEQUENCER: 34362306a36Sopenharmony_ci return "sequencer"; 34462306a36Sopenharmony_ci case SNDRV_DEVICE_TYPE_TIMER: 34562306a36Sopenharmony_ci return "timer"; 34662306a36Sopenharmony_ci case SNDRV_DEVICE_TYPE_COMPRESS: 34762306a36Sopenharmony_ci return "compress"; 34862306a36Sopenharmony_ci default: 34962306a36Sopenharmony_ci return "?"; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci int minor; 35662306a36Sopenharmony_ci struct snd_minor *mptr; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci mutex_lock(&sound_mutex); 35962306a36Sopenharmony_ci for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) { 36062306a36Sopenharmony_ci mptr = snd_minors[minor]; 36162306a36Sopenharmony_ci if (!mptr) 36262306a36Sopenharmony_ci continue; 36362306a36Sopenharmony_ci if (mptr->card >= 0) { 36462306a36Sopenharmony_ci if (mptr->device >= 0) 36562306a36Sopenharmony_ci snd_iprintf(buffer, "%3i: [%2i-%2i]: %s\n", 36662306a36Sopenharmony_ci minor, mptr->card, mptr->device, 36762306a36Sopenharmony_ci snd_device_type_name(mptr->type)); 36862306a36Sopenharmony_ci else 36962306a36Sopenharmony_ci snd_iprintf(buffer, "%3i: [%2i] : %s\n", 37062306a36Sopenharmony_ci minor, mptr->card, 37162306a36Sopenharmony_ci snd_device_type_name(mptr->type)); 37262306a36Sopenharmony_ci } else 37362306a36Sopenharmony_ci snd_iprintf(buffer, "%3i: : %s\n", minor, 37462306a36Sopenharmony_ci snd_device_type_name(mptr->type)); 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci mutex_unlock(&sound_mutex); 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ciint __init snd_minor_info_init(void) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct snd_info_entry *entry; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL); 38462306a36Sopenharmony_ci if (!entry) 38562306a36Sopenharmony_ci return -ENOMEM; 38662306a36Sopenharmony_ci entry->c.text.read = snd_minor_info_read; 38762306a36Sopenharmony_ci return snd_info_register(entry); /* freed in error path */ 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci#endif /* CONFIG_SND_PROC_FS */ 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci/* 39262306a36Sopenharmony_ci * INIT PART 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic int __init alsa_sound_init(void) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci snd_major = major; 39862306a36Sopenharmony_ci snd_ecards_limit = cards_limit; 39962306a36Sopenharmony_ci if (register_chrdev(major, "alsa", &snd_fops)) { 40062306a36Sopenharmony_ci pr_err("ALSA core: unable to register native major device number %d\n", major); 40162306a36Sopenharmony_ci return -EIO; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci if (snd_info_init() < 0) { 40462306a36Sopenharmony_ci unregister_chrdev(major, "alsa"); 40562306a36Sopenharmony_ci return -ENOMEM; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG 40962306a36Sopenharmony_ci sound_debugfs_root = debugfs_create_dir("sound", NULL); 41062306a36Sopenharmony_ci#endif 41162306a36Sopenharmony_ci#ifndef MODULE 41262306a36Sopenharmony_ci pr_info("Advanced Linux Sound Architecture Driver Initialized.\n"); 41362306a36Sopenharmony_ci#endif 41462306a36Sopenharmony_ci return 0; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic void __exit alsa_sound_exit(void) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG 42062306a36Sopenharmony_ci debugfs_remove(sound_debugfs_root); 42162306a36Sopenharmony_ci#endif 42262306a36Sopenharmony_ci snd_info_done(); 42362306a36Sopenharmony_ci unregister_chrdev(major, "alsa"); 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cisubsys_initcall(alsa_sound_init); 42762306a36Sopenharmony_cimodule_exit(alsa_sound_exit); 428