cregit-Linux how code gets into the kernel

Release 4.13 sound/core/sound.c

Directory: sound/core
/*
 *  Advanced Linux Sound Architecture
 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
 *
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */

#include <linux/init.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/device.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
#include <sound/control.h>
#include <sound/initval.h>
#include <linux/kmod.h>
#include <linux/mutex.h>


static int major = CONFIG_SND_MAJOR;

int snd_major;

EXPORT_SYMBOL(snd_major);


static int cards_limit = 1;

MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards.");
MODULE_LICENSE("GPL");
module_param(major, int, 0444);
MODULE_PARM_DESC(major, "Major # for sound driver.");
module_param(cards_limit, int, 0444);
MODULE_PARM_DESC(cards_limit, "Count of auto-loadable soundcards.");

MODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR);

/* this one holds the actual max. card number currently available.
 * as default, it's identical with cards_limit option.  when more
 * modules are loaded manually, this limit number increases, too.
 */

int snd_ecards_limit;

EXPORT_SYMBOL(snd_ecards_limit);


static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
static DEFINE_MUTEX(sound_mutex);

#ifdef CONFIG_MODULES

/**
 * snd_request_card - try to load the card module
 * @card: the card number
 *
 * Tries to load the module "snd-card-X" for the given card number
 * via request_module.  Returns immediately if already loaded.
 */

void snd_request_card(int card) { if (snd_card_locked(card)) return; if (card < 0 || card >= cards_limit) return; request_module("snd-card-%i", card); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela3294.12%466.67%
Takashi Iwai12.94%116.67%
Linus Torvalds12.94%116.67%
Total34100.00%6100.00%

EXPORT_SYMBOL(snd_request_card);
static void snd_request_other(int minor) { char *str; switch (minor) { case SNDRV_MINOR_SEQUENCER: str = "snd-seq"; break; case SNDRV_MINOR_TIMER: str = "snd-timer"; break; default: return; } request_module(str); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela42100.00%1100.00%
Total42100.00%1100.00%

#endif /* modular kernel */ /** * snd_lookup_minor_data - get user data of a registered device * @minor: the minor number * @type: device type (SNDRV_DEVICE_TYPE_XXX) * * Checks that a minor device with the specified type is registered, and returns * its user data pointer. * * This function increments the reference counter of the card instance * if an associated instance with the given minor number and type is found. * The caller must call snd_card_unref() appropriately later. * * Return: The user data pointer if the specified device is found. %NULL * otherwise. */
void *snd_lookup_minor_data(unsigned int minor, int type) { struct snd_minor *mreg; void *private_data; if (minor >= ARRAY_SIZE(snd_minors)) return NULL; mutex_lock(&sound_mutex); mreg = snd_minors[minor]; if (mreg && mreg->type == type) { private_data = mreg->private_data; if (private_data && mreg->card_ptr) get_device(&mreg->card_ptr->card_dev); } else private_data = NULL; mutex_unlock(&sound_mutex); return private_data; }

Contributors

PersonTokensPropCommitsCommitProp
Clemens Ladisch7476.29%116.67%
Takashi Iwai2020.62%350.00%
Ingo Molnar22.06%116.67%
Adrian Bunk11.03%116.67%
Total97100.00%6100.00%

EXPORT_SYMBOL(snd_lookup_minor_data); #ifdef CONFIG_MODULES
static struct snd_minor *autoload_device(unsigned int minor) { int dev; mutex_unlock(&sound_mutex); /* release lock temporarily */ dev = SNDRV_MINOR_DEVICE(minor); if (dev == SNDRV_MINOR_CONTROL) { /* /dev/aloadC? */ int card = SNDRV_MINOR_CARD(minor); if (snd_cards[card] == NULL) snd_request_card(card); } else if (dev == SNDRV_MINOR_GLOBAL) { /* /dev/aloadSEQ */ snd_request_other(minor); } mutex_lock(&sound_mutex); /* reacuire lock */ return snd_minors[minor]; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai88100.00%1100.00%
Total88100.00%1100.00%

#else /* !CONFIG_MODULES */ #define autoload_device(minor) NULL #endif /* CONFIG_MODULES */
static int snd_open(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); struct snd_minor *mptr = NULL; const struct file_operations *new_fops; int err = 0; if (minor >= ARRAY_SIZE(snd_minors)) return -ENODEV; mutex_lock(&sound_mutex); mptr = snd_minors[minor]; if (mptr == NULL) { mptr = autoload_device(minor); if (!mptr) { mutex_unlock(&sound_mutex); return -ENODEV; } } new_fops = fops_get(mptr->f_ops); mutex_unlock(&sound_mutex); if (!new_fops) return -ENODEV; replace_fops(file, new_fops); if (file->f_op->open) err = file->f_op->open(inode, file); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela8353.21%111.11%
Takashi Iwai2918.59%222.22%
Clemens Ladisch2314.74%111.11%
Al Viro148.97%222.22%
Laurent Pinchart53.21%111.11%
Adrian Bunk10.64%111.11%
Arjan van de Ven10.64%111.11%
Total156100.00%9100.00%

static const struct file_operations snd_fops = { .owner = THIS_MODULE, .open = snd_open, .llseek = noop_llseek, }; #ifdef CONFIG_SND_DYNAMIC_MINORS
static int snd_find_free_minor(int type, struct snd_card *card, int dev) { int minor; /* static minors for module auto loading */ if (type == SNDRV_DEVICE_TYPE_SEQUENCER) return SNDRV_MINOR_SEQUENCER; if (type == SNDRV_DEVICE_TYPE_TIMER) return SNDRV_MINOR_TIMER; for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) { /* skip static minors still used for module auto loading */ if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL) continue; if (minor == SNDRV_MINOR_SEQUENCER || minor == SNDRV_MINOR_TIMER) continue; if (!snd_minors[minor]) return minor; } return -EBUSY; }

Contributors

PersonTokensPropCommitsCommitProp
Clemens Ladisch5659.57%133.33%
Kay Sievers3031.91%133.33%
Takashi Iwai88.51%133.33%
Total94100.00%3100.00%

#else
static int snd_find_free_minor(int type, struct snd_card *card, int dev) { int minor; switch (type) { case SNDRV_DEVICE_TYPE_SEQUENCER: case SNDRV_DEVICE_TYPE_TIMER: minor = type; break; case SNDRV_DEVICE_TYPE_CONTROL: if (snd_BUG_ON(!card)) return -EINVAL; minor = SNDRV_MINOR(card->number, type); break; case SNDRV_DEVICE_TYPE_HWDEP: case SNDRV_DEVICE_TYPE_RAWMIDI: case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: case SNDRV_DEVICE_TYPE_PCM_CAPTURE: case SNDRV_DEVICE_TYPE_COMPRESS: if (snd_BUG_ON(!card)) return -EINVAL; minor = SNDRV_MINOR(card->number, type + dev); break; default: return -EINVAL; } if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS)) return -EINVAL; if (snd_minors[minor]) return -EBUSY; return minor; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela9063.83%120.00%
Takashi Iwai4834.04%360.00%
Omair Mohammed Abdullah32.13%120.00%
Total141100.00%5100.00%

#endif /** * snd_register_device - Register the ALSA device file for the card * @type: the device type, SNDRV_DEVICE_TYPE_XXX * @card: the card instance * @dev: the device index * @f_ops: the file operations * @private_data: user pointer for f_ops->open() * @device: the device to register * * Registers an ALSA device file for the given card. * The operators have to be set in reg parameter. * * Return: Zero if successful, or a negative error code on failure. */
int snd_register_device(int type, struct snd_card *card, int dev, const struct file_operations *f_ops, void *private_data, struct device *device) { int minor; int err = 0; struct snd_minor *preg; if (snd_BUG_ON(!device)) return -EINVAL; preg = kmalloc(sizeof *preg, GFP_KERNEL); if (preg == NULL) return -ENOMEM; preg->type = type; preg->card = card ? card->number : -1; preg->device = dev; preg->f_ops = f_ops; preg->private_data = private_data; preg->card_ptr = card; mutex_lock(&sound_mutex); minor = snd_find_free_minor(type, card, dev); if (minor < 0) { err = minor; goto error; } preg->dev = device; device->devt = MKDEV(major, minor); err = device_add(device); if (err < 0) goto error; snd_minors[minor] = preg; error: mutex_unlock(&sound_mutex); if (err < 0) kfree(preg); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai7735.98%428.57%
Jaroslav Kysela6932.24%17.14%
Clemens Ladisch4621.50%535.71%
Mariusz Kozlowski188.41%17.14%
Kay Sievers20.93%17.14%
Arjan van de Ven10.47%17.14%
Ingo Molnar10.47%17.14%
Total214100.00%14100.00%

EXPORT_SYMBOL(snd_register_device); /** * snd_unregister_device - unregister the device on the given card * @dev: the device instance * * Unregisters the device file already registered via * snd_register_device(). * * Return: Zero if successful, or a negative error code on failure. */
int snd_unregister_device(struct device *dev) { int minor; struct snd_minor *preg; mutex_lock(&sound_mutex); for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) { preg = snd_minors[minor]; if (preg && preg->dev == dev) { snd_minors[minor] = NULL; device_del(dev); kfree(preg); break; } } mutex_unlock(&sound_mutex); if (minor >= ARRAY_SIZE(snd_minors)) return -ENOENT; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai8786.14%350.00%
Jaroslav Kysela1110.89%116.67%
Clemens Ladisch21.98%116.67%
Ingo Molnar10.99%116.67%
Total101100.00%6100.00%

EXPORT_SYMBOL(snd_unregister_device); #ifdef CONFIG_SND_PROC_FS /* * INFO PART */
static const char *snd_device_type_name(int type) { switch (type) { case SNDRV_DEVICE_TYPE_CONTROL: return "control"; case SNDRV_DEVICE_TYPE_HWDEP: return "hardware dependent"; case SNDRV_DEVICE_TYPE_RAWMIDI: return "raw midi"; case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: return "digital audio playback"; case SNDRV_DEVICE_TYPE_PCM_CAPTURE: return "digital audio capture"; case SNDRV_DEVICE_TYPE_SEQUENCER: return "sequencer"; case SNDRV_DEVICE_TYPE_TIMER: return "timer"; default: return "?"; } }

Contributors

PersonTokensPropCommitsCommitProp
Clemens Ladisch63100.00%1100.00%
Total63100.00%1100.00%


static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { int minor; struct snd_minor *mptr; mutex_lock(&sound_mutex); for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) { if (!(mptr = snd_minors[minor])) continue; if (mptr->card >= 0) { if (mptr->device >= 0) snd_iprintf(buffer, "%3i: [%2i-%2i]: %s\n", minor, mptr->card, mptr->device, snd_device_type_name(mptr->type)); else snd_iprintf(buffer, "%3i: [%2i] : %s\n", minor, mptr->card, snd_device_type_name(mptr->type)); } else snd_iprintf(buffer, "%3i: : %s\n", minor, snd_device_type_name(mptr->type)); } mutex_unlock(&sound_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela9565.97%116.67%
Clemens Ladisch4128.47%350.00%
Takashi Iwai64.17%116.67%
Ingo Molnar21.39%116.67%
Total144100.00%6100.00%


int __init snd_minor_info_init(void) { struct snd_info_entry *entry; entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL); if (!entry) return -ENOMEM; entry->c.text.read = snd_minor_info_read; return snd_info_register(entry); /* freed in error path */ }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela4182.00%133.33%
Takashi Iwai918.00%266.67%
Total50100.00%3100.00%

#endif /* CONFIG_SND_PROC_FS */ /* * INIT PART */
static int __init alsa_sound_init(void) { snd_major = major; snd_ecards_limit = cards_limit; if (register_chrdev(major, "alsa", &snd_fops)) { pr_err("ALSA core: unable to register native major device number %d\n", major); return -EIO; } if (snd_info_init() < 0) { unregister_chrdev(major, "alsa"); return -ENOMEM; } #ifndef MODULE pr_info("Advanced Linux Sound Architecture Driver Initialized.\n"); #endif return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela7296.00%583.33%
Takashi Iwai34.00%116.67%
Total75100.00%6100.00%


static void __exit alsa_sound_exit(void) { snd_info_done(); unregister_chrdev(major, "alsa"); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela19100.00%2100.00%
Total19100.00%2100.00%

subsys_initcall(alsa_sound_init); module_exit(alsa_sound_exit);

Overall Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela69444.92%1629.09%
Takashi Iwai43127.90%1629.09%
Clemens Ladisch31620.45%610.91%
Kay Sievers322.07%11.82%
Mariusz Kozlowski181.17%11.82%
Al Viro140.91%23.64%
Ingo Molnar80.52%11.82%
Arnd Bergmann60.39%11.82%
Thadeu Lima de Souza Cascardo50.32%11.82%
Laurent Pinchart50.32%11.82%
Arjan van de Ven30.19%23.64%
Johannes Berg30.19%11.82%
Omair Mohammed Abdullah30.19%11.82%
Adrian Bunk20.13%11.82%
Jie Yang20.13%11.82%
Linus Torvalds10.06%11.82%
Yacine Belkadi10.06%11.82%
Paul Gortmaker10.06%11.82%
Total1545100.00%55100.00%
Directory: sound/core
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.