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 *
 103 * This function increments the reference counter of the card instance
 104 * if an associated instance with the given minor number and type is found.
 105 * The caller must call snd_card_unref() appropriately later.
 106 */
 107void *snd_lookup_minor_data(unsigned int minor, int type)
 108{
 109        struct snd_minor *mreg;
 110        void *private_data;
 111
 112        if (minor >= ARRAY_SIZE(snd_minors))
 113                return NULL;
 114        mutex_lock(&sound_mutex);
 115        mreg = snd_minors[minor];
 116        if (mreg && mreg->type == type) {
 117                private_data = mreg->private_data;
 118                if (private_data && mreg->card_ptr)
 119                        atomic_inc(&mreg->card_ptr->refcount);
 120        } else
 121                private_data = NULL;
 122        mutex_unlock(&sound_mutex);
 123        return private_data;
 124}
 125
 126EXPORT_SYMBOL(snd_lookup_minor_data);
 127
 128#ifdef CONFIG_MODULES
 129static struct snd_minor *autoload_device(unsigned int minor)
 130{
 131        int dev;
 132        mutex_unlock(&sound_mutex); /* release lock temporarily */
 133        dev = SNDRV_MINOR_DEVICE(minor);
 134        if (dev == SNDRV_MINOR_CONTROL) {
 135                /* /dev/aloadC? */
 136                int card = SNDRV_MINOR_CARD(minor);
 137                if (snd_cards[card] == NULL)
 138                        snd_request_card(card);
 139        } else if (dev == SNDRV_MINOR_GLOBAL) {
 140                /* /dev/aloadSEQ */
 141                snd_request_other(minor);
 142        }
 143        mutex_lock(&sound_mutex); /* reacuire lock */
 144        return snd_minors[minor];
 145}
 146#else /* !CONFIG_MODULES */
 147#define autoload_device(minor)  NULL
 148#endif /* CONFIG_MODULES */
 149
 150static int snd_open(struct inode *inode, struct file *file)
 151{
 152        unsigned int minor = iminor(inode);
 153        struct snd_minor *mptr = NULL;
 154        const struct file_operations *old_fops;
 155        int err = 0;
 156
 157        if (minor >= ARRAY_SIZE(snd_minors))
 158                return -ENODEV;
 159        mutex_lock(&sound_mutex);
 160        mptr = snd_minors[minor];
 161        if (mptr == NULL) {
 162                mptr = autoload_device(minor);
 163                if (!mptr) {
 164                        mutex_unlock(&sound_mutex);
 165                        return -ENODEV;
 166                }
 167        }
 168        old_fops = file->f_op;
 169        file->f_op = fops_get(mptr->f_ops);
 170        if (file->f_op == NULL) {
 171                file->f_op = old_fops;
 172                err = -ENODEV;
 173        }
 174        mutex_unlock(&sound_mutex);
 175        if (err < 0)
 176                return err;
 177
 178        if (file->f_op->open) {
 179                err = file->f_op->open(inode, file);
 180                if (err) {
 181                        fops_put(file->f_op);
 182                        file->f_op = fops_get(old_fops);
 183                }
 184        }
 185        fops_put(old_fops);
 186        return err;
 187}
 188
 189static const struct file_operations snd_fops =
 190{
 191        .owner =        THIS_MODULE,
 192        .open =         snd_open,
 193        .llseek =       noop_llseek,
 194};
 195
 196#ifdef CONFIG_SND_DYNAMIC_MINORS
 197static int snd_find_free_minor(int type)
 198{
 199        int minor;
 200
 201        /* static minors for module auto loading */
 202        if (type == SNDRV_DEVICE_TYPE_SEQUENCER)
 203                return SNDRV_MINOR_SEQUENCER;
 204        if (type == SNDRV_DEVICE_TYPE_TIMER)
 205                return SNDRV_MINOR_TIMER;
 206
 207        for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) {
 208                /* skip static minors still used for module auto loading */
 209                if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL)
 210                        continue;
 211                if (minor == SNDRV_MINOR_SEQUENCER ||
 212                    minor == SNDRV_MINOR_TIMER)
 213                        continue;
 214                if (!snd_minors[minor])
 215                        return minor;
 216        }
 217        return -EBUSY;
 218}
 219#else
 220static int snd_kernel_minor(int type, struct snd_card *card, int dev)
 221{
 222        int minor;
 223
 224        switch (type) {
 225        case SNDRV_DEVICE_TYPE_SEQUENCER:
 226        case SNDRV_DEVICE_TYPE_TIMER:
 227                minor = type;
 228                break;
 229        case SNDRV_DEVICE_TYPE_CONTROL:
 230                if (snd_BUG_ON(!card))
 231                        return -EINVAL;
 232                minor = SNDRV_MINOR(card->number, type);
 233                break;
 234        case SNDRV_DEVICE_TYPE_HWDEP:
 235        case SNDRV_DEVICE_TYPE_RAWMIDI:
 236        case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
 237        case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
 238        case SNDRV_DEVICE_TYPE_COMPRESS:
 239                if (snd_BUG_ON(!card))
 240                        return -EINVAL;
 241                minor = SNDRV_MINOR(card->number, type + dev);
 242                break;
 243        default:
 244                return -EINVAL;
 245        }
 246        if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS))
 247                return -EINVAL;
 248        return minor;
 249}
 250#endif
 251
 252/**
 253 * snd_register_device_for_dev - Register the ALSA device file for the card
 254 * @type: the device type, SNDRV_DEVICE_TYPE_XXX
 255 * @card: the card instance
 256 * @dev: the device index
 257 * @f_ops: the file operations
 258 * @private_data: user pointer for f_ops->open()
 259 * @name: the device file name
 260 * @device: the &struct device to link this new device to
 261 *
 262 * Registers an ALSA device file for the given card.
 263 * The operators have to be set in reg parameter.
 264 *
 265 * Returns zero if successful, or a negative error code on failure.
 266 */
 267int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
 268                                const struct file_operations *f_ops,
 269                                void *private_data,
 270                                const char *name, struct device *device)
 271{
 272        int minor;
 273        struct snd_minor *preg;
 274
 275        if (snd_BUG_ON(!name))
 276                return -EINVAL;
 277        preg = kmalloc(sizeof *preg, GFP_KERNEL);
 278        if (preg == NULL)
 279                return -ENOMEM;
 280        preg->type = type;
 281        preg->card = card ? card->number : -1;
 282        preg->device = dev;
 283        preg->f_ops = f_ops;
 284        preg->private_data = private_data;
 285        preg->card_ptr = card;
 286        mutex_lock(&sound_mutex);
 287#ifdef CONFIG_SND_DYNAMIC_MINORS
 288        minor = snd_find_free_minor(type);
 289#else
 290        minor = snd_kernel_minor(type, card, dev);
 291        if (minor >= 0 && snd_minors[minor])
 292                minor = -EBUSY;
 293#endif
 294        if (minor < 0) {
 295                mutex_unlock(&sound_mutex);
 296                kfree(preg);
 297                return minor;
 298        }
 299        snd_minors[minor] = preg;
 300        preg->dev = device_create(sound_class, device, MKDEV(major, minor),
 301                                  private_data, "%s", name);
 302        if (IS_ERR(preg->dev)) {
 303                snd_minors[minor] = NULL;
 304                mutex_unlock(&sound_mutex);
 305                minor = PTR_ERR(preg->dev);
 306                kfree(preg);
 307                return minor;
 308        }
 309
 310        mutex_unlock(&sound_mutex);
 311        return 0;
 312}
 313
 314EXPORT_SYMBOL(snd_register_device_for_dev);
 315
 316/* find the matching minor record
 317 * return the index of snd_minor, or -1 if not found
 318 */
 319static int find_snd_minor(int type, struct snd_card *card, int dev)
 320{
 321        int cardnum, minor;
 322        struct snd_minor *mptr;
 323
 324        cardnum = card ? card->number : -1;
 325        for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor)
 326                if ((mptr = snd_minors[minor]) != NULL &&
 327                    mptr->type == type &&
 328                    mptr->card == cardnum &&
 329                    mptr->device == dev)
 330                        return minor;
 331        return -1;
 332}
 333
 334/**
 335 * snd_unregister_device - unregister the device on the given card
 336 * @type: the device type, SNDRV_DEVICE_TYPE_XXX
 337 * @card: the card instance
 338 * @dev: the device index
 339 *
 340 * Unregisters the device file already registered via
 341 * snd_register_device().
 342 *
 343 * Returns zero if sucecessful, or a negative error code on failure
 344 */
 345int snd_unregister_device(int type, struct snd_card *card, int dev)
 346{
 347        int minor;
 348
 349        mutex_lock(&sound_mutex);
 350        minor = find_snd_minor(type, card, dev);
 351        if (minor < 0) {
 352                mutex_unlock(&sound_mutex);
 353                return -EINVAL;
 354        }
 355
 356        device_destroy(sound_class, MKDEV(major, minor));
 357
 358        kfree(snd_minors[minor]);
 359        snd_minors[minor] = NULL;
 360        mutex_unlock(&sound_mutex);
 361        return 0;
 362}
 363
 364EXPORT_SYMBOL(snd_unregister_device);
 365
 366int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev,
 367                              struct device_attribute *attr)
 368{
 369        int minor, ret = -EINVAL;
 370        struct device *d;
 371
 372        mutex_lock(&sound_mutex);
 373        minor = find_snd_minor(type, card, dev);
 374        if (minor >= 0 && (d = snd_minors[minor]->dev) != NULL)
 375                ret = device_create_file(d, attr);
 376        mutex_unlock(&sound_mutex);
 377        return ret;
 378
 379}
 380
 381EXPORT_SYMBOL(snd_add_device_sysfs_file);
 382
 383#ifdef CONFIG_PROC_FS
 384/*
 385 *  INFO PART
 386 */
 387
 388static struct snd_info_entry *snd_minor_info_entry;
 389
 390static const char *snd_device_type_name(int type)
 391{
 392        switch (type) {
 393        case SNDRV_DEVICE_TYPE_CONTROL:
 394                return "control";
 395        case SNDRV_DEVICE_TYPE_HWDEP:
 396                return "hardware dependent";
 397        case SNDRV_DEVICE_TYPE_RAWMIDI:
 398                return "raw midi";
 399        case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
 400                return "digital audio playback";
 401        case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
 402                return "digital audio capture";
 403        case SNDRV_DEVICE_TYPE_SEQUENCER:
 404                return "sequencer";
 405        case SNDRV_DEVICE_TYPE_TIMER:
 406                return "timer";
 407        default:
 408                return "?";
 409        }
 410}
 411
 412static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
 413{
 414        int minor;
 415        struct snd_minor *mptr;
 416
 417        mutex_lock(&sound_mutex);
 418        for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) {
 419                if (!(mptr = snd_minors[minor]))
 420                        continue;
 421                if (mptr->card >= 0) {
 422                        if (mptr->device >= 0)
 423                                snd_iprintf(buffer, "%3i: [%2i-%2i]: %s\n",
 424                                            minor, mptr->card, mptr->device,
 425                                            snd_device_type_name(mptr->type));
 426                        else
 427                                snd_iprintf(buffer, "%3i: [%2i]   : %s\n",
 428                                            minor, mptr->card,
 429                                            snd_device_type_name(mptr->type));
 430                } else
 431                        snd_iprintf(buffer, "%3i:        : %s\n", minor,
 432                                    snd_device_type_name(mptr->type));
 433        }
 434        mutex_unlock(&sound_mutex);
 435}
 436
 437int __init snd_minor_info_init(void)
 438{
 439        struct snd_info_entry *entry;
 440
 441        entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL);
 442        if (entry) {
 443                entry->c.text.read = snd_minor_info_read;
 444                if (snd_info_register(entry) < 0) {
 445                        snd_info_free_entry(entry);
 446                        entry = NULL;
 447                }
 448        }
 449        snd_minor_info_entry = entry;
 450        return 0;
 451}
 452
 453int __exit snd_minor_info_done(void)
 454{
 455        snd_info_free_entry(snd_minor_info_entry);
 456        return 0;
 457}
 458#endif /* CONFIG_PROC_FS */
 459
 460/*
 461 *  INIT PART
 462 */
 463
 464static int __init alsa_sound_init(void)
 465{
 466        snd_major = major;
 467        snd_ecards_limit = cards_limit;
 468        if (register_chrdev(major, "alsa", &snd_fops)) {
 469                snd_printk(KERN_ERR "unable to register native major device number %d\n", major);
 470                return -EIO;
 471        }
 472        if (snd_info_init() < 0) {
 473                unregister_chrdev(major, "alsa");
 474                return -ENOMEM;
 475        }
 476        snd_info_minor_register();
 477#ifndef MODULE
 478        printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n");
 479#endif
 480        return 0;
 481}
 482
 483static void __exit alsa_sound_exit(void)
 484{
 485        snd_info_minor_unregister();
 486        snd_info_done();
 487        unregister_chrdev(major, "alsa");
 488}
 489
 490subsys_initcall(alsa_sound_init);
 491module_exit(alsa_sound_exit);
 492
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.