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