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