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