linux/sound/core/sound.c
<<
>>
Prefs
   1/*
   2 *  Advanced Linux Sound Architecture
   3 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
   4 *
   5 *
   6 *   This program is free software; you can redistribute it and/or modify
   7 *   it under the terms of the GNU General Public License as published by
   8 *   the Free Software Foundation; either version 2 of the License, or
   9 *   (at your option) any later version.
  10 *
  11 *   This program is distributed in the hope that it will be useful,
  12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *   GNU General Public License for more details.
  15 *
  16 *   You should have received a copy of the GNU General Public License
  17 *   along with this program; if not, write to the Free Software
  18 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  19 *
  20 */
  21
  22#include <linux/init.h>
  23#include <linux/slab.h>
  24#include <linux/time.h>
  25#include <linux/device.h>
  26#include <linux/moduleparam.h>
  27#include <sound/core.h>
  28#include <sound/minors.h>
  29#include <sound/info.h>
  30#include <sound/version.h>
  31#include <sound/control.h>
  32#include <sound/initval.h>
  33#include <linux/kmod.h>
  34#include <linux/mutex.h>
  35
  36static int major = CONFIG_SND_MAJOR;
  37int snd_major;
  38EXPORT_SYMBOL(snd_major);
  39
  40static int cards_limit = 1;
  41
  42MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
  43MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards.");
  44MODULE_LICENSE("GPL");
  45module_param(major, int, 0444);
  46MODULE_PARM_DESC(major, "Major # for sound driver.");
  47module_param(cards_limit, int, 0444);
  48MODULE_PARM_DESC(cards_limit, "Count of auto-loadable soundcards.");
  49MODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR);
  50
  51/* this one holds the actual max. card number currently available.
  52 * as default, it's identical with cards_limit option.  when more
  53 * modules are loaded manually, this limit number increases, too.
  54 */
  55int snd_ecards_limit;
  56EXPORT_SYMBOL(snd_ecards_limit);
  57
  58static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
  59static DEFINE_MUTEX(sound_mutex);
  60
  61#ifdef CONFIG_MODULES
  62
  63/**
  64 * snd_request_card - try to load the card module
  65 * @card: the card number
  66 *
  67 * Tries to load the module "snd-card-X" for the given card number
  68 * via request_module.  Returns immediately if already loaded.
  69 */
  70void snd_request_card(int card)
  71{
  72        if (snd_card_locked(card))
  73                return;
  74        if (card < 0 || card >= cards_limit)
  75                return;
  76        request_module("snd-card-%i", card);
  77}
  78
  79EXPORT_SYMBOL(snd_request_card);
  80
  81static void snd_request_other(int minor)
  82{
  83        char *str;
  84
  85        switch (minor) {
  86        case SNDRV_MINOR_SEQUENCER:     str = "snd-seq";        break;
  87        case SNDRV_MINOR_TIMER:         str = "snd-timer";      break;
  88        default:                        return;
  89        }
  90        request_module(str);
  91}
  92
  93#endif  /* modular kernel */
  94
  95/**
  96 * snd_lookup_minor_data - get user data of a registered device
  97 * @minor: the minor number
  98 * @type: device type (SNDRV_DEVICE_TYPE_XXX)
  99 *
 100 * Checks that a minor device with the specified type is registered, and returns
 101 * its user data pointer.
 102 */
 103void *snd_lookup_minor_data(unsigned int minor, int type)
 104{
 105        struct snd_minor *mreg;
 106        void *private_data;
 107
 108        if (minor >= ARRAY_SIZE(snd_minors))
 109                return NULL;
 110        mutex_lock(&sound_mutex);
 111        mreg = snd_minors[minor];
 112        if (mreg && mreg->type == type)
 113                private_data = mreg->private_data;
 114        else
 115                private_data = NULL;
 116        mutex_unlock(&sound_mutex);
 117        return private_data;
 118}
 119
 120EXPORT_SYMBOL(snd_lookup_minor_data);
 121
 122#ifdef CONFIG_MODULES
 123static struct snd_minor *autoload_device(unsigned int minor)
 124{
 125        int dev;
 126        mutex_unlock(&sound_mutex); /* release lock temporarily */
 127        dev = SNDRV_MINOR_DEVICE(minor);
 128        if (dev == SNDRV_MINOR_CONTROL) {
 129                /* /dev/aloadC? */
 130                int card = SNDRV_MINOR_CARD(minor);
 131                if (snd_cards[card] == NULL)
 132                        snd_request_card(card);
 133        } else if (dev == SNDRV_MINOR_GLOBAL) {
 134                /* /dev/aloadSEQ */
 135                snd_request_other(minor);
 136        }
 137        mutex_lock(&sound_mutex); /* reacuire lock */
 138        return snd_minors[minor];
 139}
 140#else /* !CONFIG_MODULES */
 141#define autoload_device(minor)  NULL
 142#endif /* CONFIG_MODULES */
 143
 144static int snd_open(struct inode *inode, struct file *file)
 145{
 146        unsigned int minor = iminor(inode);
 147        struct snd_minor *mptr = NULL;
 148        const struct file_operations *old_fops;
 149        int err = 0;
 150
 151        if (minor >= ARRAY_SIZE(snd_minors))
 152                return -ENODEV;
 153        mutex_lock(&sound_mutex);
 154        mptr = snd_minors[minor];
 155        if (mptr == NULL) {
 156                mptr = autoload_device(minor);
 157                if (!mptr) {
 158                        mutex_unlock(&sound_mutex);
 159                        return -ENODEV;
 160                }
 161        }
 162        old_fops = file->f_op;
 163        file->f_op = fops_get(mptr->f_ops);
 164        if (file->f_op == NULL) {
 165                file->f_op = old_fops;
 166                err = -ENODEV;
 167        }
 168        mutex_unlock(&sound_mutex);
 169        if (err < 0)
 170                return err;
 171
 172        if (file->f_op->open) {
 173                err = file->f_op->open(inode, file);
 174                if (err) {
 175                        fops_put(file->f_op);
 176                        file->f_op = fops_get(old_fops);
 177                }
 178        }
 179        fops_put(old_fops);
 180        return err;
 181}
 182
 183static const struct file_operations snd_fops =
 184{
 185        .owner =        THIS_MODULE,
 186        .open =         snd_open,
 187        .llseek =       noop_llseek,
 188};
 189
 190#ifdef CONFIG_SND_DYNAMIC_MINORS
 191static int snd_find_free_minor(int type)
 192{
 193        int minor;
 194
 195        /* static minors for module auto loading */
 196        if (type == SNDRV_DEVICE_TYPE_SEQUENCER)
 197                return SNDRV_MINOR_SEQUENCER;
 198        if (type == SNDRV_DEVICE_TYPE_TIMER)
 199                return SNDRV_MINOR_TIMER;
 200
 201        for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) {
 202                /* skip static minors still used for module auto loading */
 203                if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL)
 204                        continue;
 205                if (minor == SNDRV_MINOR_SEQUENCER ||
 206                    minor == SNDRV_MINOR_TIMER)
 207                        continue;
 208                if (!snd_minors[minor])
 209                        return minor;
 210        }
 211        return -EBUSY;
 212}
 213#else
 214static int snd_kernel_minor(int type, struct snd_card *card, int dev)
 215{
 216        int minor;
 217
 218        switch (type) {
 219        case SNDRV_DEVICE_TYPE_SEQUENCER:
 220        case SNDRV_DEVICE_TYPE_TIMER:
 221                minor = type;
 222                break;
 223        case SNDRV_DEVICE_TYPE_CONTROL:
 224                if (snd_BUG_ON(!card))
 225                        return -EINVAL;
 226                minor = SNDRV_MINOR(card->number, type);
 227                break;
 228        case SNDRV_DEVICE_TYPE_HWDEP:
 229        case SNDRV_DEVICE_TYPE_RAWMIDI:
 230        case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
 231        case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
 232                if (snd_BUG_ON(!card))
 233                        return -EINVAL;
 234                minor = SNDRV_MINOR(card->number, type + dev);
 235                break;
 236        default:
 237                return -EINVAL;
 238        }
 239        if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS))
 240                return -EINVAL;
 241        return minor;
 242}
 243#endif
 244
 245/**
 246 * snd_register_device_for_dev - Register the ALSA device file for the card
 247 * @type: the device type, SNDRV_DEVICE_TYPE_XXX
 248 * @card: the card instance
 249 * @dev: the device index
 250 * @f_ops: the file operations
 251 * @private_data: user pointer for f_ops->open()
 252 * @name: the device file name
 253 * @device: the &struct device to link this new device to
 254 *
 255 * Registers an ALSA device file for the given card.
 256 * The operators have to be set in reg parameter.
 257 *
 258 * Returns zero if successful, or a negative error code on failure.
 259 */
 260int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
 261                                const struct file_operations *f_ops,
 262                                void *private_data,
 263                                const char *name, struct device *device)
 264{
 265        int minor;
 266        struct snd_minor *preg;
 267
 268        if (snd_BUG_ON(!name))
 269                return -EINVAL;
 270        preg = kmalloc(sizeof *preg, GFP_KERNEL);
 271        if (preg == NULL)
 272                return -ENOMEM;
 273        preg->type = type;
 274        preg->card = card ? card->number : -1;
 275        preg->device = dev;
 276        preg->f_ops = f_ops;
 277        preg->private_data = private_data;
 278        mutex_lock(&sound_mutex);
 279#ifdef CONFIG_SND_DYNAMIC_MINORS
 280        minor = snd_find_free_minor(type);
 281#else
 282        minor = snd_kernel_minor(type, card, dev);
 283        if (minor >= 0 && snd_minors[minor])
 284                minor = -EBUSY;
 285#endif
 286        if (minor < 0) {
 287                mutex_unlock(&sound_mutex);
 288                kfree(preg);
 289                return minor;
 290        }
 291        snd_minors[minor] = preg;
 292        preg->dev = device_create(sound_class, device, MKDEV(major, minor),
 293                                  private_data, "%s", name);
 294        if (IS_ERR(preg->dev)) {
 295                snd_minors[minor] = NULL;
 296                mutex_unlock(&sound_mutex);
 297                minor = PTR_ERR(preg->dev);
 298                kfree(preg);
 299                return minor;
 300        }
 301
 302        mutex_unlock(&sound_mutex);
 303        return 0;
 304}
 305
 306EXPORT_SYMBOL(snd_register_device_for_dev);
 307
 308/* find the matching minor record
 309 * return the index of snd_minor, or -1 if not found
 310 */
 311static int find_snd_minor(int type, struct snd_card *card, int dev)
 312{
 313        int cardnum, minor;
 314        struct snd_minor *mptr;
 315
 316        cardnum = card ? card->number : -1;
 317        for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor)
 318                if ((mptr = snd_minors[minor]) != NULL &&
 319                    mptr->type == type &&
 320                    mptr->card == cardnum &&
 321                    mptr->device == dev)
 322                        return minor;
 323        return -1;
 324}
 325
 326/**
 327 * snd_unregister_device - unregister the device on the given card
 328 * @type: the device type, SNDRV_DEVICE_TYPE_XXX
 329 * @card: the card instance
 330 * @dev: the device index
 331 *
 332 * Unregisters the device file already registered via
 333 * snd_register_device().
 334 *
 335 * Returns zero if sucecessful, or a negative error code on failure
 336 */
 337int snd_unregister_device(int type, struct snd_card *card, int dev)
 338{
 339        int minor;
 340
 341        mutex_lock(&sound_mutex);
 342        minor = find_snd_minor(type, card, dev);
 343        if (minor < 0) {
 344                mutex_unlock(&sound_mutex);
 345                return -EINVAL;
 346        }
 347
 348        device_destroy(sound_class, MKDEV(major, minor));
 349
 350        kfree(snd_minors[minor]);
 351        snd_minors[minor] = NULL;
 352        mutex_unlock(&sound_mutex);
 353        return 0;
 354}
 355
 356EXPORT_SYMBOL(snd_unregister_device);
 357
 358int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev,
 359                              struct device_attribute *attr)
 360{
 361        int minor, ret = -EINVAL;
 362        struct device *d;
 363
 364        mutex_lock(&sound_mutex);
 365        minor = find_snd_minor(type, card, dev);
 366        if (minor >= 0 && (d = snd_minors[minor]->dev) != NULL)
 367                ret = device_create_file(d, attr);
 368        mutex_unlock(&sound_mutex);
 369        return ret;
 370
 371}
 372
 373EXPORT_SYMBOL(snd_add_device_sysfs_file);
 374
 375#ifdef CONFIG_PROC_FS
 376/*
 377 *  INFO PART
 378 */
 379
 380static struct snd_info_entry *snd_minor_info_entry;
 381
 382static const char *snd_device_type_name(int type)
 383{
 384        switch (type) {
 385        case SNDRV_DEVICE_TYPE_CONTROL:
 386                return "control";
 387        case SNDRV_DEVICE_TYPE_HWDEP:
 388                return "hardware dependent";
 389        case SNDRV_DEVICE_TYPE_RAWMIDI:
 390                return "raw midi";
 391        case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
 392                return "digital audio playback";
 393        case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
 394                return "digital audio capture";
 395        case SNDRV_DEVICE_TYPE_SEQUENCER:
 396                return "sequencer";
 397        case SNDRV_DEVICE_TYPE_TIMER:
 398                return "timer";
 399        default:
 400                return "?";
 401        }
 402}
 403
 404static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
 405{
 406        int minor;
 407        struct snd_minor *mptr;
 408
 409        mutex_lock(&sound_mutex);
 410        for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) {
 411                if (!(mptr = snd_minors[minor]))
 412                        continue;
 413                if (mptr->card >= 0) {
 414                        if (mptr->device >= 0)
 415                                snd_iprintf(buffer, "%3i: [%2i-%2i]: %s\n",
 416                                            minor, mptr->card, mptr->device,
 417                                            snd_device_type_name(mptr->type));
 418                        else
 419                                snd_iprintf(buffer, "%3i: [%2i]   : %s\n",
 420                                            minor, mptr->card,
 421                                            snd_device_type_name(mptr->type));
 422                } else
 423                        snd_iprintf(buffer, "%3i:        : %s\n", minor,
 424                                    snd_device_type_name(mptr->type));
 425        }
 426        mutex_unlock(&sound_mutex);
 427}
 428
 429int __init snd_minor_info_init(void)
 430{
 431        struct snd_info_entry *entry;
 432
 433        entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL);
 434        if (entry) {
 435                entry->c.text.read = snd_minor_info_read;
 436                if (snd_info_register(entry) < 0) {
 437                        snd_info_free_entry(entry);
 438                        entry = NULL;
 439                }
 440        }
 441        snd_minor_info_entry = entry;
 442        return 0;
 443}
 444
 445int __exit snd_minor_info_done(void)
 446{
 447        snd_info_free_entry(snd_minor_info_entry);
 448        return 0;
 449}
 450#endif /* CONFIG_PROC_FS */
 451
 452/*
 453 *  INIT PART
 454 */
 455
 456static int __init alsa_sound_init(void)
 457{
 458        snd_major = major;
 459        snd_ecards_limit = cards_limit;
 460        if (register_chrdev(major, "alsa", &snd_fops)) {
 461                snd_printk(KERN_ERR "unable to register native major device number %d\n", major);
 462                return -EIO;
 463        }
 464        if (snd_info_init() < 0) {
 465                unregister_chrdev(major, "alsa");
 466                return -ENOMEM;
 467        }
 468        snd_info_minor_register();
 469#ifndef MODULE
 470        printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n");
 471#endif
 472        return 0;
 473}
 474
 475static void __exit alsa_sound_exit(void)
 476{
 477        snd_info_minor_unregister();
 478        snd_info_done();
 479        unregister_chrdev(major, "alsa");
 480}
 481
 482subsys_initcall(alsa_sound_init);
 483module_exit(alsa_sound_exit);
 484