linux/sound/core/seq_device.c
<<
>>
Prefs
   1/*
   2 *  ALSA sequencer device management
   3 *  Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
   4 *
   5 *   This program is free software; you can redistribute it and/or modify
   6 *   it under the terms of the GNU General Public License as published by
   7 *   the Free Software Foundation; either version 2 of the License, or
   8 *   (at your option) any later version.
   9 *
  10 *   This program is distributed in the hope that it will be useful,
  11 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 *   GNU General Public License for more details.
  14 *
  15 *   You should have received a copy of the GNU General Public License
  16 *   along with this program; if not, write to the Free Software
  17 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18 *
  19 *
  20 *----------------------------------------------------------------
  21 *
  22 * This device handler separates the card driver module from sequencer
  23 * stuff (sequencer core, synth drivers, etc), so that user can avoid
  24 * to spend unnecessary resources e.g. if he needs only listening to
  25 * MP3s.
  26 *
  27 * The card (or lowlevel) driver creates a sequencer device entry
  28 * via snd_seq_device_new().  This is an entry pointer to communicate
  29 * with the sequencer device "driver", which is involved with the
  30 * actual part to communicate with the sequencer core.
  31 * Each sequencer device entry has an id string and the corresponding
  32 * driver with the same id is loaded when required.  For example,
  33 * lowlevel codes to access emu8000 chip on sbawe card are included in
  34 * emu8000-synth module.  To activate this module, the hardware
  35 * resources like i/o port are passed via snd_seq_device argument.
  36 *
  37 */
  38
  39#include <linux/device.h>
  40#include <linux/init.h>
  41#include <linux/module.h>
  42#include <sound/core.h>
  43#include <sound/info.h>
  44#include <sound/seq_device.h>
  45#include <sound/seq_kernel.h>
  46#include <sound/initval.h>
  47#include <linux/kmod.h>
  48#include <linux/slab.h>
  49#include <linux/mutex.h>
  50
  51MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
  52MODULE_DESCRIPTION("ALSA sequencer device management");
  53MODULE_LICENSE("GPL");
  54
  55/*
  56 * bus definition
  57 */
  58static int snd_seq_bus_match(struct device *dev, struct device_driver *drv)
  59{
  60        struct snd_seq_device *sdev = to_seq_dev(dev);
  61        struct snd_seq_driver *sdrv = to_seq_drv(drv);
  62
  63        return strcmp(sdrv->id, sdev->id) == 0 &&
  64                sdrv->argsize == sdev->argsize;
  65}
  66
  67static struct bus_type snd_seq_bus_type = {
  68        .name = "snd_seq",
  69        .match = snd_seq_bus_match,
  70};
  71
  72/*
  73 * proc interface -- just for compatibility
  74 */
  75#ifdef CONFIG_SND_PROC_FS
  76static struct snd_info_entry *info_entry;
  77
  78static int print_dev_info(struct device *dev, void *data)
  79{
  80        struct snd_seq_device *sdev = to_seq_dev(dev);
  81        struct snd_info_buffer *buffer = data;
  82
  83        snd_iprintf(buffer, "snd-%s,%s,%d\n", sdev->id,
  84                    dev->driver ? "loaded" : "empty",
  85                    dev->driver ? 1 : 0);
  86        return 0;
  87}
  88
  89static void snd_seq_device_info(struct snd_info_entry *entry,
  90                                struct snd_info_buffer *buffer)
  91{
  92        bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info);
  93}
  94#endif
  95
  96/*
  97 * load all registered drivers (called from seq_clientmgr.c)
  98 */
  99
 100#ifdef CONFIG_MODULES
 101/* flag to block auto-loading */
 102static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */
 103
 104static int request_seq_drv(struct device *dev, void *data)
 105{
 106        struct snd_seq_device *sdev = to_seq_dev(dev);
 107
 108        if (!dev->driver)
 109                request_module("snd-%s", sdev->id);
 110        return 0;
 111}
 112
 113static void autoload_drivers(struct work_struct *work)
 114{
 115        /* avoid reentrance */
 116        if (atomic_inc_return(&snd_seq_in_init) == 1)
 117                bus_for_each_dev(&snd_seq_bus_type, NULL, NULL,
 118                                 request_seq_drv);
 119        atomic_dec(&snd_seq_in_init);
 120}
 121
 122static DECLARE_WORK(autoload_work, autoload_drivers);
 123
 124static void queue_autoload_drivers(void)
 125{
 126        schedule_work(&autoload_work);
 127}
 128
 129void snd_seq_autoload_init(void)
 130{
 131        atomic_dec(&snd_seq_in_init);
 132#ifdef CONFIG_SND_SEQUENCER_MODULE
 133        /* initial autoload only when snd-seq is a module */
 134        queue_autoload_drivers();
 135#endif
 136}
 137EXPORT_SYMBOL(snd_seq_autoload_init);
 138
 139void snd_seq_autoload_exit(void)
 140{
 141        atomic_inc(&snd_seq_in_init);
 142}
 143EXPORT_SYMBOL(snd_seq_autoload_exit);
 144
 145void snd_seq_device_load_drivers(void)
 146{
 147        queue_autoload_drivers();
 148        flush_work(&autoload_work);
 149}
 150EXPORT_SYMBOL(snd_seq_device_load_drivers);
 151#define cancel_autoload_drivers()       cancel_work_sync(&autoload_work)
 152#else
 153#define queue_autoload_drivers() /* NOP */
 154#define cancel_autoload_drivers() /* NOP */
 155#endif
 156
 157/*
 158 * device management
 159 */
 160static int snd_seq_device_dev_free(struct snd_device *device)
 161{
 162        struct snd_seq_device *dev = device->device_data;
 163
 164        cancel_autoload_drivers();
 165        put_device(&dev->dev);
 166        return 0;
 167}
 168
 169static int snd_seq_device_dev_register(struct snd_device *device)
 170{
 171        struct snd_seq_device *dev = device->device_data;
 172        int err;
 173
 174        err = device_add(&dev->dev);
 175        if (err < 0)
 176                return err;
 177        if (!dev->dev.driver)
 178                queue_autoload_drivers();
 179        return 0;
 180}
 181
 182static int snd_seq_device_dev_disconnect(struct snd_device *device)
 183{
 184        struct snd_seq_device *dev = device->device_data;
 185
 186        device_del(&dev->dev);
 187        return 0;
 188}
 189
 190static void snd_seq_dev_release(struct device *dev)
 191{
 192        struct snd_seq_device *sdev = to_seq_dev(dev);
 193
 194        if (sdev->private_free)
 195                sdev->private_free(sdev);
 196        kfree(sdev);
 197}
 198
 199/*
 200 * register a sequencer device
 201 * card = card info
 202 * device = device number (if any)
 203 * id = id of driver
 204 * result = return pointer (NULL allowed if unnecessary)
 205 */
 206int snd_seq_device_new(struct snd_card *card, int device, const char *id,
 207                       int argsize, struct snd_seq_device **result)
 208{
 209        struct snd_seq_device *dev;
 210        int err;
 211        static struct snd_device_ops dops = {
 212                .dev_free = snd_seq_device_dev_free,
 213                .dev_register = snd_seq_device_dev_register,
 214                .dev_disconnect = snd_seq_device_dev_disconnect,
 215        };
 216
 217        if (result)
 218                *result = NULL;
 219
 220        if (snd_BUG_ON(!id))
 221                return -EINVAL;
 222
 223        dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL);
 224        if (!dev)
 225                return -ENOMEM;
 226
 227        /* set up device info */
 228        dev->card = card;
 229        dev->device = device;
 230        dev->id = id;
 231        dev->argsize = argsize;
 232
 233        device_initialize(&dev->dev);
 234        dev->dev.parent = &card->card_dev;
 235        dev->dev.bus = &snd_seq_bus_type;
 236        dev->dev.release = snd_seq_dev_release;
 237        dev_set_name(&dev->dev, "%s-%d-%d", dev->id, card->number, device);
 238
 239        /* add this device to the list */
 240        err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops);
 241        if (err < 0) {
 242                put_device(&dev->dev);
 243                return err;
 244        }
 245        
 246        if (result)
 247                *result = dev;
 248
 249        return 0;
 250}
 251EXPORT_SYMBOL(snd_seq_device_new);
 252
 253/*
 254 * driver registration
 255 */
 256int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod)
 257{
 258        if (WARN_ON(!drv->driver.name || !drv->id))
 259                return -EINVAL;
 260        drv->driver.bus = &snd_seq_bus_type;
 261        drv->driver.owner = mod;
 262        return driver_register(&drv->driver);
 263}
 264EXPORT_SYMBOL_GPL(__snd_seq_driver_register);
 265
 266void snd_seq_driver_unregister(struct snd_seq_driver *drv)
 267{
 268        driver_unregister(&drv->driver);
 269}
 270EXPORT_SYMBOL_GPL(snd_seq_driver_unregister);
 271
 272/*
 273 * module part
 274 */
 275
 276static int __init seq_dev_proc_init(void)
 277{
 278#ifdef CONFIG_SND_PROC_FS
 279        info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
 280                                                  snd_seq_root);
 281        if (info_entry == NULL)
 282                return -ENOMEM;
 283        info_entry->content = SNDRV_INFO_CONTENT_TEXT;
 284        info_entry->c.text.read = snd_seq_device_info;
 285        if (snd_info_register(info_entry) < 0) {
 286                snd_info_free_entry(info_entry);
 287                return -ENOMEM;
 288        }
 289#endif
 290        return 0;
 291}
 292
 293static int __init alsa_seq_device_init(void)
 294{
 295        int err;
 296
 297        err = bus_register(&snd_seq_bus_type);
 298        if (err < 0)
 299                return err;
 300        err = seq_dev_proc_init();
 301        if (err < 0)
 302                bus_unregister(&snd_seq_bus_type);
 303        return err;
 304}
 305
 306static void __exit alsa_seq_device_exit(void)
 307{
 308#ifdef CONFIG_MODULES
 309        cancel_work_sync(&autoload_work);
 310#endif
 311#ifdef CONFIG_SND_PROC_FS
 312        snd_info_free_entry(info_entry);
 313#endif
 314        bus_unregister(&snd_seq_bus_type);
 315}
 316
 317subsys_initcall(alsa_seq_device_init)
 318module_exit(alsa_seq_device_exit)
 319