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/module.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        case SNDRV_DEVICE_TYPE_COMPRESS:
 233                if (snd_BUG_ON(!card))
 234                        return -EINVAL;
 235                minor = SNDRV_MINOR(card->number, type + dev);
 236                break;
 237        default:
 238                return -EINVAL;
 239        }
 240        if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS))
 241                return -EINVAL;
 242        return minor;
 243}
 244#endif
 245
 246/**
 247 * snd_register_device_for_dev - Register the ALSA device file for the card
 248 * @type: the device type, SNDRV_DEVICE_TYPE_XXX
 249 * @card: the card instance
 250 * @dev: the device index
 251 * @f_ops: the file operations
 252 * @private_data: user pointer for f_ops->open()
 253 * @name: the device file name
 254 * @device: the &struct device to link this new device to
 255 *
 256 * Registers an ALSA device file for the given card.
 257 * The operators have to be set in reg parameter.
 258 *
 259 * Returns zero if successful, or a negative error code on failure.
 260 */
 261int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
 262                                const struct file_operations *f_ops,
 263                                void *private_data,
 264                                const char *name, struct device *device)
 265{
 266        int minor;
 267        struct snd_minor *preg;
 268
 269        if (snd_BUG_ON(!name))
 270                return -EINVAL;
 271        preg = kmalloc(sizeof *preg, GFP_KERNEL);
 272        if (preg == NULL)
 273                return -ENOMEM;
 274        preg->type = type;
 275        preg->card = card ? card->number : -1;
 276        preg->device = dev;
 277        preg->f_ops = f_ops;
 278        preg->private_data = private_data;
 279        mutex_lock(&sound_mutex);
 280#ifdef CONFIG_SND_DYNAMIC_MINORS
 281        minor = snd_find_free_minor(type);
 282#else
 283        minor = snd_kernel_minor(type, card, dev);
 284        if (minor >= 0 && snd_minors[minor])
 285                minor = -EBUSY;
 286#endif
 287        if (minor < 0) {
 288                mutex_unlock(&sound_mutex);
 289                kfree(preg);
 290                return minor;
 291        }
 292        snd_minors[minor] = preg;
 293        preg->dev = device_create(sound_class, device, MKDEV(major, minor),
 294                                  private_data, "%s", name);
 295        if (IS_ERR(preg->dev)) {
 296                snd_minors[minor] = NULL;
 297                mutex_unlock(&sound_mutex);
 298                minor = PTR_ERR(preg->dev);
 299                kfree(preg);
 300                return minor;
 301        }
 302
 303        mutex_unlock(&sound_mutex);
 304        return 0;
 305}
 306
 307EXPORT_SYMBOL(snd_register_device_for_dev);
 308
 309/* find the matching minor record
 310 * return the index of snd_minor, or -1 if not found
 311 */
 312static int find_snd_minor(int type, struct snd_card *card, int dev)
 313{
 314        int cardnum, minor;
 315        struct snd_minor *mptr;
 316
 317        cardnum = card ? card->number : -1;
 318        for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor)
 319                if ((mptr = snd_minors[minor]) != NULL &&
 320                    mptr->type == type &&
 321                    mptr->card == cardnum &&
 322                    mptr->device == dev)
 323                        return minor;
 324        return -1;
 325}
 326
 327/**
 328 * snd_unregister_device - unregister the device on the given card
 329 * @type: the device type, SNDRV_DEVICE_TYPE_XXX
 330 * @card: the card instance
 331 * @dev: the device index
 332 *
 333 * Unregisters the device file already registered via
 334 * snd_register_device().
 335 *
 336 * Returns zero if sucecessful, or a negative error code on failure
 337 */
 338int snd_unregister_device(int type, struct snd_card *card, int dev)
 339{
 340        int minor;
 341
 342        mutex_lock(&sound_mutex);
 343        minor = find_snd_minor(type, card, dev);
 344        if (minor < 0) {
 345                mutex_unlock(&sound_mutex);
 346                return -EINVAL;
 347        }
 348
 349        device_destroy(sound_class, MKDEV(major, minor));
 350
 351        kfree(snd_minors[minor]);
 352        snd_minors[minor] = NULL;
 353        mutex_unlock(&sound_mutex);
 354        return 0;
 355}
 356
 357EXPORT_SYMBOL(snd_unregister_device);
 358
 359int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev,
 360                              struct device_attribute *attr)
 361{
 362        int minor, ret = -EINVAL;
 363        struct device *d;
 364
 365        mutex_lock(&sound_mutex);
 366        minor = find_snd_minor(type, card, dev);
 367        if (minor >= 0 && (d = snd_minors[minor]->dev) != NULL)
 368                ret = device_create_file(d, attr);
 369        mutex_unlock(&sound_mutex);
 370        return ret;
 371
 372}
 373
 374EXPORT_SYMBOL(snd_add_device_sysfs_file);
 375
 376#ifdef CONFIG_PROC_FS
 377/*
 378 *  INFO PART
 379 */
 380
 381static struct snd_info_entry *snd_minor_info_entry;
 382
 383static const char *snd_device_type_name(int type)
 384{
 385        switch (type) {
 386        case SNDRV_DEVICE_TYPE_CONTROL:
 387                return "control";
 388        case SNDRV_DEVICE_TYPE_HWDEP:
 389                return "hardware dependent";
 390        case SNDRV_DEVICE_TYPE_RAWMIDI:
 391                return "raw midi";
 392        case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
 393                return "digital audio playback";
 394        case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
 395                return "digital audio capture";
 396        case SNDRV_DEVICE_TYPE_SEQUENCER:
 397                return "sequencer";
 398        case SNDRV_DEVICE_TYPE_TIMER:
 399                return "timer";
 400        default:
 401                return "?";
 402        }
 403}
 404
 405static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
 406{
 407        int minor;
 408        struct snd_minor *mptr;
 409
 410        mutex_lock(&sound_mutex);
 411        for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) {
 412                if (!(mptr = snd_minors[minor]))
 413                        continue;
 414                if (mptr->card >= 0) {
 415                        if (mptr->device >= 0)
 416                                snd_iprintf(buffer, "%3i: [%2i-%2i]: %s\n",
 417                                            minor, mptr->card, mptr->device,
 418                                            snd_device_type_name(mptr->type));
 419                        else
 420                                snd_iprintf(buffer, "%3i: [%2i]   : %s\n",
 421                                            minor, mptr->card,
 422                                            snd_device_type_name(mptr->type));
 423                } else
 424                        snd_iprintf(buffer, "%3i:        : %s\n", minor,
 425                                    snd_device_type_name(mptr->type));
 426        }
 427        mutex_unlock(&sound_mutex);
 428}
 429
 430int __init snd_minor_info_init(void)
 431{
 432        struct snd_info_entry *entry;
 433
 434        entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL);
 435        if (entry) {
 436                entry->c.text.read = snd_minor_info_read;
 437                if (snd_info_register(entry) < 0) {
 438                        snd_info_free_entry(entry);
 439                        entry = NULL;
 440                }
 441        }
 442        snd_minor_info_entry = entry;
 443        return 0;
 444}
 445
 446int __exit snd_minor_info_done(void)
 447{
 448        snd_info_free_entry(snd_minor_info_entry);
 449        return 0;
 450}
 451#endif /* CONFIG_PROC_FS */
 452
 453/*
 454 *  INIT PART
 455 */
 456
 457static int __init alsa_sound_init(void)
 458{
 459        snd_major = major;
 460        snd_ecards_limit = cards_limit;
 461        if (register_chrdev(major, "alsa", &snd_fops)) {
 462                snd_printk(KERN_ERR "unable to register native major device number %d\n", major);
 463                return -EIO;
 464        }
 465        if (snd_info_init() < 0) {
 466                unregister_chrdev(major, "alsa");
 467                return -ENOMEM;
 468        }
 469        snd_info_minor_register();
 470#ifndef MODULE
 471        printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n");
 472#endif
 473        return 0;
 474}
 475
 476static void __exit alsa_sound_exit(void)
 477{
 478        snd_info_minor_unregister();
 479        snd_info_done();
 480        unregister_chrdev(major, "alsa");
 481}
 482
 483subsys_initcall(alsa_sound_init);
 484module_exit(alsa_sound_exit);
 485
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.