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#else
 152#define queue_autoload_drivers() /* NOP */
 153#endif
 154
 155/*
 156 * device management
 157 */
 158static int snd_seq_device_dev_free(struct snd_device *device)
 159{
 160        struct snd_seq_device *dev = device->device_data;
 161
 162        put_device(&dev->dev);
 163        return 0;
 164}
 165
 166static int snd_seq_device_dev_register(struct snd_device *device)
 167{
 168        struct snd_seq_device *dev = device->device_data;
 169        int err;
 170
 171        err = device_add(&dev->dev);
 172        if (err < 0)
 173                return err;
 174        if (!dev->dev.driver)
 175                queue_autoload_drivers();
 176        return 0;
 177}
 178
 179static int snd_seq_device_dev_disconnect(struct snd_device *device)
 180{
 181        struct snd_seq_device *dev = device->device_data;
 182
 183        device_del(&dev->dev);
 184        return 0;
 185}
 186
 187static void snd_seq_dev_release(struct device *dev)
 188{
 189        struct snd_seq_device *sdev = to_seq_dev(dev);
 190
 191        if (sdev->private_free)
 192                sdev->private_free(sdev);
 193        kfree(sdev);
 194}
 195
 196/*
 197 * register a sequencer device
 198 * card = card info
 199 * device = device number (if any)
 200 * id = id of driver
 201 * result = return pointer (NULL allowed if unnecessary)
 202 */
 203int snd_seq_device_new(struct snd_card *card, int device, const char *id,
 204                       int argsize, struct snd_seq_device **result)
 205{
 206        struct snd_seq_device *dev;
 207        int err;
 208        static struct snd_device_ops dops = {
 209                .dev_free = snd_seq_device_dev_free,
 210                .dev_register = snd_seq_device_dev_register,
 211                .dev_disconnect = snd_seq_device_dev_disconnect,
 212        };
 213
 214        if (result)
 215                *result = NULL;
 216
 217        if (snd_BUG_ON(!id))
 218                return -EINVAL;
 219
 220        dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL);
 221        if (!dev)
 222                return -ENOMEM;
 223
 224        /* set up device info */
 225        dev->card = card;
 226        dev->device = device;
 227        dev->id = id;
 228        dev->argsize = argsize;
 229
 230        device_initialize(&dev->dev);
 231        dev->dev.parent = &card->card_dev;
 232        dev->dev.bus = &snd_seq_bus_type;
 233        dev->dev.release = snd_seq_dev_release;
 234        dev_set_name(&dev->dev, "%s-%d-%d", dev->id, card->number, device);
 235
 236        /* add this device to the list */
 237        err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops);
 238        if (err < 0) {
 239                put_device(&dev->dev);
 240                return err;
 241        }
 242        
 243        if (result)
 244                *result = dev;
 245
 246        return 0;
 247}
 248EXPORT_SYMBOL(snd_seq_device_new);
 249
 250/*
 251 * driver registration
 252 */
 253int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod)
 254{
 255        if (WARN_ON(!drv->driver.name || !drv->id))
 256                return -EINVAL;
 257        drv->driver.bus = &snd_seq_bus_type;
 258        drv->driver.owner = mod;
 259        return driver_register(&drv->driver);
 260}
 261EXPORT_SYMBOL_GPL(__snd_seq_driver_register);
 262
 263void snd_seq_driver_unregister(struct snd_seq_driver *drv)
 264{
 265        driver_unregister(&drv->driver);
 266}
 267EXPORT_SYMBOL_GPL(snd_seq_driver_unregister);
 268
 269/*
 270 * module part
 271 */
 272
 273static int __init seq_dev_proc_init(void)
 274{
 275#ifdef CONFIG_SND_PROC_FS
 276        info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
 277                                                  snd_seq_root);
 278        if (info_entry == NULL)
 279                return -ENOMEM;
 280        info_entry->content = SNDRV_INFO_CONTENT_TEXT;
 281        info_entry->c.text.read = snd_seq_device_info;
 282        if (snd_info_register(info_entry) < 0) {
 283                snd_info_free_entry(info_entry);
 284                return -ENOMEM;
 285        }
 286#endif
 287        return 0;
 288}
 289
 290static int __init alsa_seq_device_init(void)
 291{
 292        int err;
 293
 294        err = bus_register(&snd_seq_bus_type);
 295        if (err < 0)
 296                return err;
 297        err = seq_dev_proc_init();
 298        if (err < 0)
 299                bus_unregister(&snd_seq_bus_type);
 300        return err;
 301}
 302
 303static void __exit alsa_seq_device_exit(void)
 304{
 305#ifdef CONFIG_MODULES
 306        cancel_work_sync(&autoload_work);
 307#endif
 308#ifdef CONFIG_SND_PROC_FS
 309        snd_info_free_entry(info_entry);
 310#endif
 311        bus_unregister(&snd_seq_bus_type);
 312}
 313
 314subsys_initcall(alsa_seq_device_init)
 315module_exit(alsa_seq_device_exit)
 316