at v2.6.12 575 lines 14 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 <sound/driver.h> 40#include <linux/init.h> 41#include <sound/core.h> 42#include <sound/info.h> 43#include <sound/seq_device.h> 44#include <sound/seq_kernel.h> 45#include <sound/initval.h> 46#include <linux/kmod.h> 47#include <linux/slab.h> 48 49MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); 50MODULE_DESCRIPTION("ALSA sequencer device management"); 51MODULE_LICENSE("GPL"); 52 53/* 54 * driver list 55 */ 56typedef struct ops_list ops_list_t; 57 58/* driver state */ 59#define DRIVER_EMPTY 0 60#define DRIVER_LOADED (1<<0) 61#define DRIVER_REQUESTED (1<<1) 62#define DRIVER_LOCKED (1<<2) 63 64struct ops_list { 65 char id[ID_LEN]; /* driver id */ 66 int driver; /* driver state */ 67 int used; /* reference counter */ 68 int argsize; /* argument size */ 69 70 /* operators */ 71 snd_seq_dev_ops_t ops; 72 73 /* registred devices */ 74 struct list_head dev_list; /* list of devices */ 75 int num_devices; /* number of associated devices */ 76 int num_init_devices; /* number of initialized devices */ 77 struct semaphore reg_mutex; 78 79 struct list_head list; /* next driver */ 80}; 81 82 83static LIST_HEAD(opslist); 84static int num_ops; 85static DECLARE_MUTEX(ops_mutex); 86static snd_info_entry_t *info_entry = NULL; 87 88/* 89 * prototypes 90 */ 91static int snd_seq_device_free(snd_seq_device_t *dev); 92static int snd_seq_device_dev_free(snd_device_t *device); 93static int snd_seq_device_dev_register(snd_device_t *device); 94static int snd_seq_device_dev_disconnect(snd_device_t *device); 95static int snd_seq_device_dev_unregister(snd_device_t *device); 96 97static int init_device(snd_seq_device_t *dev, ops_list_t *ops); 98static int free_device(snd_seq_device_t *dev, ops_list_t *ops); 99static ops_list_t *find_driver(char *id, int create_if_empty); 100static ops_list_t *create_driver(char *id); 101static void unlock_driver(ops_list_t *ops); 102static void remove_drivers(void); 103 104/* 105 * show all drivers and their status 106 */ 107 108static void snd_seq_device_info(snd_info_entry_t *entry, snd_info_buffer_t * buffer) 109{ 110 struct list_head *head; 111 112 down(&ops_mutex); 113 list_for_each(head, &opslist) { 114 ops_list_t *ops = list_entry(head, ops_list_t, list); 115 snd_iprintf(buffer, "snd-%s%s%s%s,%d\n", 116 ops->id, 117 ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""), 118 ops->driver & DRIVER_REQUESTED ? ",requested" : "", 119 ops->driver & DRIVER_LOCKED ? ",locked" : "", 120 ops->num_devices); 121 } 122 up(&ops_mutex); 123} 124 125/* 126 * load all registered drivers (called from seq_clientmgr.c) 127 */ 128 129#ifdef CONFIG_KMOD 130/* avoid auto-loading during module_init() */ 131static int snd_seq_in_init; 132void snd_seq_autoload_lock(void) 133{ 134 snd_seq_in_init++; 135} 136 137void snd_seq_autoload_unlock(void) 138{ 139 snd_seq_in_init--; 140} 141#endif 142 143void snd_seq_device_load_drivers(void) 144{ 145#ifdef CONFIG_KMOD 146 struct list_head *head; 147 148 /* Calling request_module during module_init() 149 * may cause blocking. 150 */ 151 if (snd_seq_in_init) 152 return; 153 154 if (! current->fs->root) 155 return; 156 157 down(&ops_mutex); 158 list_for_each(head, &opslist) { 159 ops_list_t *ops = list_entry(head, ops_list_t, list); 160 if (! (ops->driver & DRIVER_LOADED) && 161 ! (ops->driver & DRIVER_REQUESTED)) { 162 ops->used++; 163 up(&ops_mutex); 164 ops->driver |= DRIVER_REQUESTED; 165 request_module("snd-%s", ops->id); 166 down(&ops_mutex); 167 ops->used--; 168 } 169 } 170 up(&ops_mutex); 171#endif 172} 173 174/* 175 * register a sequencer device 176 * card = card info (NULL allowed) 177 * device = device number (if any) 178 * id = id of driver 179 * result = return pointer (NULL allowed if unnecessary) 180 */ 181int snd_seq_device_new(snd_card_t *card, int device, char *id, int argsize, 182 snd_seq_device_t **result) 183{ 184 snd_seq_device_t *dev; 185 ops_list_t *ops; 186 int err; 187 static snd_device_ops_t dops = { 188 .dev_free = snd_seq_device_dev_free, 189 .dev_register = snd_seq_device_dev_register, 190 .dev_disconnect = snd_seq_device_dev_disconnect, 191 .dev_unregister = snd_seq_device_dev_unregister 192 }; 193 194 if (result) 195 *result = NULL; 196 197 snd_assert(id != NULL, return -EINVAL); 198 199 ops = find_driver(id, 1); 200 if (ops == NULL) 201 return -ENOMEM; 202 203 dev = kcalloc(1, sizeof(*dev)*2 + argsize, GFP_KERNEL); 204 if (dev == NULL) { 205 unlock_driver(ops); 206 return -ENOMEM; 207 } 208 209 /* set up device info */ 210 dev->card = card; 211 dev->device = device; 212 strlcpy(dev->id, id, sizeof(dev->id)); 213 dev->argsize = argsize; 214 dev->status = SNDRV_SEQ_DEVICE_FREE; 215 216 /* add this device to the list */ 217 down(&ops->reg_mutex); 218 list_add_tail(&dev->list, &ops->dev_list); 219 ops->num_devices++; 220 up(&ops->reg_mutex); 221 222 unlock_driver(ops); 223 224 if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) { 225 snd_seq_device_free(dev); 226 return err; 227 } 228 229 if (result) 230 *result = dev; 231 232 return 0; 233} 234 235/* 236 * free the existing device 237 */ 238static int snd_seq_device_free(snd_seq_device_t *dev) 239{ 240 ops_list_t *ops; 241 242 snd_assert(dev != NULL, return -EINVAL); 243 244 ops = find_driver(dev->id, 0); 245 if (ops == NULL) 246 return -ENXIO; 247 248 /* remove the device from the list */ 249 down(&ops->reg_mutex); 250 list_del(&dev->list); 251 ops->num_devices--; 252 up(&ops->reg_mutex); 253 254 free_device(dev, ops); 255 if (dev->private_free) 256 dev->private_free(dev); 257 kfree(dev); 258 259 unlock_driver(ops); 260 261 return 0; 262} 263 264static int snd_seq_device_dev_free(snd_device_t *device) 265{ 266 snd_seq_device_t *dev = device->device_data; 267 return snd_seq_device_free(dev); 268} 269 270/* 271 * register the device 272 */ 273static int snd_seq_device_dev_register(snd_device_t *device) 274{ 275 snd_seq_device_t *dev = device->device_data; 276 ops_list_t *ops; 277 278 ops = find_driver(dev->id, 0); 279 if (ops == NULL) 280 return -ENOENT; 281 282 /* initialize this device if the corresponding driver was 283 * already loaded 284 */ 285 if (ops->driver & DRIVER_LOADED) 286 init_device(dev, ops); 287 288 unlock_driver(ops); 289 return 0; 290} 291 292/* 293 * disconnect the device 294 */ 295static int snd_seq_device_dev_disconnect(snd_device_t *device) 296{ 297 snd_seq_device_t *dev = device->device_data; 298 ops_list_t *ops; 299 300 ops = find_driver(dev->id, 0); 301 if (ops == NULL) 302 return -ENOENT; 303 304 free_device(dev, ops); 305 306 unlock_driver(ops); 307 return 0; 308} 309 310/* 311 * unregister the existing device 312 */ 313static int snd_seq_device_dev_unregister(snd_device_t *device) 314{ 315 snd_seq_device_t *dev = device->device_data; 316 return snd_seq_device_free(dev); 317} 318 319/* 320 * register device driver 321 * id = driver id 322 * entry = driver operators - duplicated to each instance 323 */ 324int snd_seq_device_register_driver(char *id, snd_seq_dev_ops_t *entry, int argsize) 325{ 326 struct list_head *head; 327 ops_list_t *ops; 328 329 if (id == NULL || entry == NULL || 330 entry->init_device == NULL || entry->free_device == NULL) 331 return -EINVAL; 332 333 snd_seq_autoload_lock(); 334 ops = find_driver(id, 1); 335 if (ops == NULL) { 336 snd_seq_autoload_unlock(); 337 return -ENOMEM; 338 } 339 if (ops->driver & DRIVER_LOADED) { 340 snd_printk(KERN_WARNING "driver_register: driver '%s' already exists\n", id); 341 unlock_driver(ops); 342 snd_seq_autoload_unlock(); 343 return -EBUSY; 344 } 345 346 down(&ops->reg_mutex); 347 /* copy driver operators */ 348 ops->ops = *entry; 349 ops->driver |= DRIVER_LOADED; 350 ops->argsize = argsize; 351 352 /* initialize existing devices if necessary */ 353 list_for_each(head, &ops->dev_list) { 354 snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list); 355 init_device(dev, ops); 356 } 357 up(&ops->reg_mutex); 358 359 unlock_driver(ops); 360 snd_seq_autoload_unlock(); 361 362 return 0; 363} 364 365 366/* 367 * create driver record 368 */ 369static ops_list_t * create_driver(char *id) 370{ 371 ops_list_t *ops; 372 373 ops = kmalloc(sizeof(*ops), GFP_KERNEL); 374 if (ops == NULL) 375 return ops; 376 memset(ops, 0, sizeof(*ops)); 377 378 /* set up driver entry */ 379 strlcpy(ops->id, id, sizeof(ops->id)); 380 init_MUTEX(&ops->reg_mutex); 381 ops->driver = DRIVER_EMPTY; 382 INIT_LIST_HEAD(&ops->dev_list); 383 /* lock this instance */ 384 ops->used = 1; 385 386 /* register driver entry */ 387 down(&ops_mutex); 388 list_add_tail(&ops->list, &opslist); 389 num_ops++; 390 up(&ops_mutex); 391 392 return ops; 393} 394 395 396/* 397 * unregister the specified driver 398 */ 399int snd_seq_device_unregister_driver(char *id) 400{ 401 struct list_head *head; 402 ops_list_t *ops; 403 404 ops = find_driver(id, 0); 405 if (ops == NULL) 406 return -ENXIO; 407 if (! (ops->driver & DRIVER_LOADED) || 408 (ops->driver & DRIVER_LOCKED)) { 409 snd_printk(KERN_ERR "driver_unregister: cannot unload driver '%s': status=%x\n", id, ops->driver); 410 unlock_driver(ops); 411 return -EBUSY; 412 } 413 414 /* close and release all devices associated with this driver */ 415 down(&ops->reg_mutex); 416 ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */ 417 list_for_each(head, &ops->dev_list) { 418 snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list); 419 free_device(dev, ops); 420 } 421 422 ops->driver = 0; 423 if (ops->num_init_devices > 0) 424 snd_printk(KERN_ERR "free_driver: init_devices > 0!! (%d)\n", ops->num_init_devices); 425 up(&ops->reg_mutex); 426 427 unlock_driver(ops); 428 429 /* remove empty driver entries */ 430 remove_drivers(); 431 432 return 0; 433} 434 435 436/* 437 * remove empty driver entries 438 */ 439static void remove_drivers(void) 440{ 441 struct list_head *head; 442 443 down(&ops_mutex); 444 head = opslist.next; 445 while (head != &opslist) { 446 ops_list_t *ops = list_entry(head, ops_list_t, list); 447 if (! (ops->driver & DRIVER_LOADED) && 448 ops->used == 0 && ops->num_devices == 0) { 449 head = head->next; 450 list_del(&ops->list); 451 kfree(ops); 452 num_ops--; 453 } else 454 head = head->next; 455 } 456 up(&ops_mutex); 457} 458 459/* 460 * initialize the device - call init_device operator 461 */ 462static int init_device(snd_seq_device_t *dev, ops_list_t *ops) 463{ 464 if (! (ops->driver & DRIVER_LOADED)) 465 return 0; /* driver is not loaded yet */ 466 if (dev->status != SNDRV_SEQ_DEVICE_FREE) 467 return 0; /* already initialized */ 468 if (ops->argsize != dev->argsize) { 469 snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize); 470 return -EINVAL; 471 } 472 if (ops->ops.init_device(dev) >= 0) { 473 dev->status = SNDRV_SEQ_DEVICE_REGISTERED; 474 ops->num_init_devices++; 475 } else { 476 snd_printk(KERN_ERR "init_device failed: %s: %s\n", dev->name, dev->id); 477 } 478 479 return 0; 480} 481 482/* 483 * release the device - call free_device operator 484 */ 485static int free_device(snd_seq_device_t *dev, ops_list_t *ops) 486{ 487 int result; 488 489 if (! (ops->driver & DRIVER_LOADED)) 490 return 0; /* driver is not loaded yet */ 491 if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED) 492 return 0; /* not registered */ 493 if (ops->argsize != dev->argsize) { 494 snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize); 495 return -EINVAL; 496 } 497 if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) { 498 dev->status = SNDRV_SEQ_DEVICE_FREE; 499 dev->driver_data = NULL; 500 ops->num_init_devices--; 501 } else { 502 snd_printk(KERN_ERR "free_device failed: %s: %s\n", dev->name, dev->id); 503 } 504 505 return 0; 506} 507 508/* 509 * find the matching driver with given id 510 */ 511static ops_list_t * find_driver(char *id, int create_if_empty) 512{ 513 struct list_head *head; 514 515 down(&ops_mutex); 516 list_for_each(head, &opslist) { 517 ops_list_t *ops = list_entry(head, ops_list_t, list); 518 if (strcmp(ops->id, id) == 0) { 519 ops->used++; 520 up(&ops_mutex); 521 return ops; 522 } 523 } 524 up(&ops_mutex); 525 if (create_if_empty) 526 return create_driver(id); 527 return NULL; 528} 529 530static void unlock_driver(ops_list_t *ops) 531{ 532 down(&ops_mutex); 533 ops->used--; 534 up(&ops_mutex); 535} 536 537 538/* 539 * module part 540 */ 541 542static int __init alsa_seq_device_init(void) 543{ 544 info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers", snd_seq_root); 545 if (info_entry == NULL) 546 return -ENOMEM; 547 info_entry->content = SNDRV_INFO_CONTENT_TEXT; 548 info_entry->c.text.read_size = 2048; 549 info_entry->c.text.read = snd_seq_device_info; 550 if (snd_info_register(info_entry) < 0) { 551 snd_info_free_entry(info_entry); 552 return -ENOMEM; 553 } 554 return 0; 555} 556 557static void __exit alsa_seq_device_exit(void) 558{ 559 remove_drivers(); 560 snd_info_unregister(info_entry); 561 if (num_ops) 562 snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops); 563} 564 565module_init(alsa_seq_device_init) 566module_exit(alsa_seq_device_exit) 567 568EXPORT_SYMBOL(snd_seq_device_load_drivers); 569EXPORT_SYMBOL(snd_seq_device_new); 570EXPORT_SYMBOL(snd_seq_device_register_driver); 571EXPORT_SYMBOL(snd_seq_device_unregister_driver); 572#ifdef CONFIG_KMOD 573EXPORT_SYMBOL(snd_seq_autoload_lock); 574EXPORT_SYMBOL(snd_seq_autoload_unlock); 575#endif