at v4.17 7.9 kB view raw
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)