diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/sound_core.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/sound_core.c')
-rw-r--r-- | sound/sound_core.c | 583 |
1 files changed, 583 insertions, 0 deletions
diff --git a/sound/sound_core.c b/sound/sound_core.c new file mode 100644 index 000000000000..30f75c9288cb --- /dev/null +++ b/sound/sound_core.c | |||
@@ -0,0 +1,583 @@ | |||
1 | /* | ||
2 | * Sound core handling. Breaks out sound functions to submodules | ||
3 | * | ||
4 | * Author: Alan Cox <alan.cox@linux.org> | ||
5 | * | ||
6 | * Fixes: | ||
7 | * | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * as published by the Free Software Foundation; either version | ||
12 | * 2 of the License, or (at your option) any later version. | ||
13 | * | ||
14 | * -------------------- | ||
15 | * | ||
16 | * Top level handler for the sound subsystem. Various devices can | ||
17 | * plug into this. The fact they don't all go via OSS doesn't mean | ||
18 | * they don't have to implement the OSS API. There is a lot of logic | ||
19 | * to keeping much of the OSS weight out of the code in a compatibility | ||
20 | * module, but it's up to the driver to rember to load it... | ||
21 | * | ||
22 | * The code provides a set of functions for registration of devices | ||
23 | * by type. This is done rather than providing a single call so that | ||
24 | * we can hide any future changes in the internals (eg when we go to | ||
25 | * 32bit dev_t) from the modules and their interface. | ||
26 | * | ||
27 | * Secondly we need to allocate the dsp, dsp16 and audio devices as | ||
28 | * one. Thus we misuse the chains a bit to simplify this. | ||
29 | * | ||
30 | * Thirdly to make it more fun and for 2.3.x and above we do all | ||
31 | * of this using fine grained locking. | ||
32 | * | ||
33 | * FIXME: we have to resolve modules and fine grained load/unload | ||
34 | * locking at some point in 2.3.x. | ||
35 | */ | ||
36 | |||
37 | #include <linux/config.h> | ||
38 | #include <linux/module.h> | ||
39 | #include <linux/init.h> | ||
40 | #include <linux/slab.h> | ||
41 | #include <linux/types.h> | ||
42 | #include <linux/kernel.h> | ||
43 | #include <linux/fs.h> | ||
44 | #include <linux/sound.h> | ||
45 | #include <linux/major.h> | ||
46 | #include <linux/kmod.h> | ||
47 | #include <linux/devfs_fs_kernel.h> | ||
48 | #include <linux/device.h> | ||
49 | |||
50 | #define SOUND_STEP 16 | ||
51 | |||
52 | |||
53 | struct sound_unit | ||
54 | { | ||
55 | int unit_minor; | ||
56 | struct file_operations *unit_fops; | ||
57 | struct sound_unit *next; | ||
58 | char name[32]; | ||
59 | }; | ||
60 | |||
61 | #ifdef CONFIG_SOUND_MSNDCLAS | ||
62 | extern int msnd_classic_init(void); | ||
63 | #endif | ||
64 | #ifdef CONFIG_SOUND_MSNDPIN | ||
65 | extern int msnd_pinnacle_init(void); | ||
66 | #endif | ||
67 | |||
68 | struct class_simple *sound_class; | ||
69 | EXPORT_SYMBOL(sound_class); | ||
70 | |||
71 | /* | ||
72 | * Low level list operator. Scan the ordered list, find a hole and | ||
73 | * join into it. Called with the lock asserted | ||
74 | */ | ||
75 | |||
76 | static int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list, struct file_operations *fops, int index, int low, int top) | ||
77 | { | ||
78 | int n=low; | ||
79 | |||
80 | if (index < 0) { /* first free */ | ||
81 | |||
82 | while (*list && (*list)->unit_minor<n) | ||
83 | list=&((*list)->next); | ||
84 | |||
85 | while(n<top) | ||
86 | { | ||
87 | /* Found a hole ? */ | ||
88 | if(*list==NULL || (*list)->unit_minor>n) | ||
89 | break; | ||
90 | list=&((*list)->next); | ||
91 | n+=SOUND_STEP; | ||
92 | } | ||
93 | |||
94 | if(n>=top) | ||
95 | return -ENOENT; | ||
96 | } else { | ||
97 | n = low+(index*16); | ||
98 | while (*list) { | ||
99 | if ((*list)->unit_minor==n) | ||
100 | return -EBUSY; | ||
101 | if ((*list)->unit_minor>n) | ||
102 | break; | ||
103 | list=&((*list)->next); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | /* | ||
108 | * Fill it in | ||
109 | */ | ||
110 | |||
111 | s->unit_minor=n; | ||
112 | s->unit_fops=fops; | ||
113 | |||
114 | /* | ||
115 | * Link it | ||
116 | */ | ||
117 | |||
118 | s->next=*list; | ||
119 | *list=s; | ||
120 | |||
121 | |||
122 | return n; | ||
123 | } | ||
124 | |||
125 | /* | ||
126 | * Remove a node from the chain. Called with the lock asserted | ||
127 | */ | ||
128 | |||
129 | static struct sound_unit *__sound_remove_unit(struct sound_unit **list, int unit) | ||
130 | { | ||
131 | while(*list) | ||
132 | { | ||
133 | struct sound_unit *p=*list; | ||
134 | if(p->unit_minor==unit) | ||
135 | { | ||
136 | *list=p->next; | ||
137 | return p; | ||
138 | } | ||
139 | list=&(p->next); | ||
140 | } | ||
141 | printk(KERN_ERR "Sound device %d went missing!\n", unit); | ||
142 | return NULL; | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * This lock guards the sound loader list. | ||
147 | */ | ||
148 | |||
149 | static DEFINE_SPINLOCK(sound_loader_lock); | ||
150 | |||
151 | /* | ||
152 | * Allocate the controlling structure and add it to the sound driver | ||
153 | * list. Acquires locks as needed | ||
154 | */ | ||
155 | |||
156 | static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int index, int low, int top, const char *name, umode_t mode) | ||
157 | { | ||
158 | struct sound_unit *s = kmalloc(sizeof(*s), GFP_KERNEL); | ||
159 | int r; | ||
160 | |||
161 | if (!s) | ||
162 | return -ENOMEM; | ||
163 | |||
164 | spin_lock(&sound_loader_lock); | ||
165 | r = __sound_insert_unit(s, list, fops, index, low, top); | ||
166 | spin_unlock(&sound_loader_lock); | ||
167 | |||
168 | if (r < 0) | ||
169 | goto fail; | ||
170 | else if (r < SOUND_STEP) | ||
171 | sprintf(s->name, "sound/%s", name); | ||
172 | else | ||
173 | sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP); | ||
174 | |||
175 | devfs_mk_cdev(MKDEV(SOUND_MAJOR, s->unit_minor), | ||
176 | S_IFCHR | mode, s->name); | ||
177 | class_simple_device_add(sound_class, MKDEV(SOUND_MAJOR, s->unit_minor), | ||
178 | NULL, s->name+6); | ||
179 | return r; | ||
180 | |||
181 | fail: | ||
182 | kfree(s); | ||
183 | return r; | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * Remove a unit. Acquires locks as needed. The drivers MUST have | ||
188 | * completed the removal before their file operations become | ||
189 | * invalid. | ||
190 | */ | ||
191 | |||
192 | static void sound_remove_unit(struct sound_unit **list, int unit) | ||
193 | { | ||
194 | struct sound_unit *p; | ||
195 | |||
196 | spin_lock(&sound_loader_lock); | ||
197 | p = __sound_remove_unit(list, unit); | ||
198 | spin_unlock(&sound_loader_lock); | ||
199 | if (p) { | ||
200 | devfs_remove(p->name); | ||
201 | class_simple_device_remove(MKDEV(SOUND_MAJOR, p->unit_minor)); | ||
202 | kfree(p); | ||
203 | } | ||
204 | } | ||
205 | |||
206 | /* | ||
207 | * Allocations | ||
208 | * | ||
209 | * 0 *16 Mixers | ||
210 | * 1 *8 Sequencers | ||
211 | * 2 *16 Midi | ||
212 | * 3 *16 DSP | ||
213 | * 4 *16 SunDSP | ||
214 | * 5 *16 DSP16 | ||
215 | * 6 -- sndstat (obsolete) | ||
216 | * 7 *16 unused | ||
217 | * 8 -- alternate sequencer (see above) | ||
218 | * 9 *16 raw synthesizer access | ||
219 | * 10 *16 unused | ||
220 | * 11 *16 unused | ||
221 | * 12 *16 unused | ||
222 | * 13 *16 unused | ||
223 | * 14 *16 unused | ||
224 | * 15 *16 unused | ||
225 | */ | ||
226 | |||
227 | static struct sound_unit *chains[SOUND_STEP]; | ||
228 | |||
229 | /** | ||
230 | * register_sound_special - register a special sound node | ||
231 | * @fops: File operations for the driver | ||
232 | * @unit: Unit number to allocate | ||
233 | * | ||
234 | * Allocate a special sound device by minor number from the sound | ||
235 | * subsystem. The allocated number is returned on succes. On failure | ||
236 | * a negative error code is returned. | ||
237 | */ | ||
238 | |||
239 | int register_sound_special(struct file_operations *fops, int unit) | ||
240 | { | ||
241 | const int chain = unit % SOUND_STEP; | ||
242 | int max_unit = 128 + chain; | ||
243 | const char *name; | ||
244 | char _name[16]; | ||
245 | |||
246 | switch (chain) { | ||
247 | case 0: | ||
248 | name = "mixer"; | ||
249 | break; | ||
250 | case 1: | ||
251 | name = "sequencer"; | ||
252 | if (unit >= SOUND_STEP) | ||
253 | goto __unknown; | ||
254 | max_unit = unit + 1; | ||
255 | break; | ||
256 | case 2: | ||
257 | name = "midi"; | ||
258 | break; | ||
259 | case 3: | ||
260 | name = "dsp"; | ||
261 | break; | ||
262 | case 4: | ||
263 | name = "audio"; | ||
264 | break; | ||
265 | case 8: | ||
266 | name = "sequencer2"; | ||
267 | if (unit >= SOUND_STEP) | ||
268 | goto __unknown; | ||
269 | max_unit = unit + 1; | ||
270 | break; | ||
271 | case 9: | ||
272 | name = "dmmidi"; | ||
273 | break; | ||
274 | case 10: | ||
275 | name = "dmfm"; | ||
276 | break; | ||
277 | case 12: | ||
278 | name = "adsp"; | ||
279 | break; | ||
280 | case 13: | ||
281 | name = "amidi"; | ||
282 | break; | ||
283 | case 14: | ||
284 | name = "admmidi"; | ||
285 | break; | ||
286 | default: | ||
287 | { | ||
288 | __unknown: | ||
289 | sprintf(_name, "unknown%d", chain); | ||
290 | if (unit >= SOUND_STEP) | ||
291 | strcat(_name, "-"); | ||
292 | name = _name; | ||
293 | } | ||
294 | break; | ||
295 | } | ||
296 | return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit, | ||
297 | name, S_IRUSR | S_IWUSR); | ||
298 | } | ||
299 | |||
300 | EXPORT_SYMBOL(register_sound_special); | ||
301 | |||
302 | /** | ||
303 | * register_sound_mixer - register a mixer device | ||
304 | * @fops: File operations for the driver | ||
305 | * @dev: Unit number to allocate | ||
306 | * | ||
307 | * Allocate a mixer device. Unit is the number of the mixer requested. | ||
308 | * Pass -1 to request the next free mixer unit. On success the allocated | ||
309 | * number is returned, on failure a negative error code is returned. | ||
310 | */ | ||
311 | |||
312 | int register_sound_mixer(struct file_operations *fops, int dev) | ||
313 | { | ||
314 | return sound_insert_unit(&chains[0], fops, dev, 0, 128, | ||
315 | "mixer", S_IRUSR | S_IWUSR); | ||
316 | } | ||
317 | |||
318 | EXPORT_SYMBOL(register_sound_mixer); | ||
319 | |||
320 | /** | ||
321 | * register_sound_midi - register a midi device | ||
322 | * @fops: File operations for the driver | ||
323 | * @dev: Unit number to allocate | ||
324 | * | ||
325 | * Allocate a midi device. Unit is the number of the midi device requested. | ||
326 | * Pass -1 to request the next free midi unit. On success the allocated | ||
327 | * number is returned, on failure a negative error code is returned. | ||
328 | */ | ||
329 | |||
330 | int register_sound_midi(struct file_operations *fops, int dev) | ||
331 | { | ||
332 | return sound_insert_unit(&chains[2], fops, dev, 2, 130, | ||
333 | "midi", S_IRUSR | S_IWUSR); | ||
334 | } | ||
335 | |||
336 | EXPORT_SYMBOL(register_sound_midi); | ||
337 | |||
338 | /* | ||
339 | * DSP's are registered as a triple. Register only one and cheat | ||
340 | * in open - see below. | ||
341 | */ | ||
342 | |||
343 | /** | ||
344 | * register_sound_dsp - register a DSP device | ||
345 | * @fops: File operations for the driver | ||
346 | * @dev: Unit number to allocate | ||
347 | * | ||
348 | * Allocate a DSP device. Unit is the number of the DSP requested. | ||
349 | * Pass -1 to request the next free DSP unit. On success the allocated | ||
350 | * number is returned, on failure a negative error code is returned. | ||
351 | * | ||
352 | * This function allocates both the audio and dsp device entries together | ||
353 | * and will always allocate them as a matching pair - eg dsp3/audio3 | ||
354 | */ | ||
355 | |||
356 | int register_sound_dsp(struct file_operations *fops, int dev) | ||
357 | { | ||
358 | return sound_insert_unit(&chains[3], fops, dev, 3, 131, | ||
359 | "dsp", S_IWUSR | S_IRUSR); | ||
360 | } | ||
361 | |||
362 | EXPORT_SYMBOL(register_sound_dsp); | ||
363 | |||
364 | /** | ||
365 | * register_sound_synth - register a synth device | ||
366 | * @fops: File operations for the driver | ||
367 | * @dev: Unit number to allocate | ||
368 | * | ||
369 | * Allocate a synth device. Unit is the number of the synth device requested. | ||
370 | * Pass -1 to request the next free synth unit. On success the allocated | ||
371 | * number is returned, on failure a negative error code is returned. | ||
372 | */ | ||
373 | |||
374 | |||
375 | int register_sound_synth(struct file_operations *fops, int dev) | ||
376 | { | ||
377 | return sound_insert_unit(&chains[9], fops, dev, 9, 137, | ||
378 | "synth", S_IRUSR | S_IWUSR); | ||
379 | } | ||
380 | |||
381 | EXPORT_SYMBOL(register_sound_synth); | ||
382 | |||
383 | /** | ||
384 | * unregister_sound_special - unregister a special sound device | ||
385 | * @unit: unit number to allocate | ||
386 | * | ||
387 | * Release a sound device that was allocated with | ||
388 | * register_sound_special(). The unit passed is the return value from | ||
389 | * the register function. | ||
390 | */ | ||
391 | |||
392 | |||
393 | void unregister_sound_special(int unit) | ||
394 | { | ||
395 | sound_remove_unit(&chains[unit % SOUND_STEP], unit); | ||
396 | } | ||
397 | |||
398 | EXPORT_SYMBOL(unregister_sound_special); | ||
399 | |||
400 | /** | ||
401 | * unregister_sound_mixer - unregister a mixer | ||
402 | * @unit: unit number to allocate | ||
403 | * | ||
404 | * Release a sound device that was allocated with register_sound_mixer(). | ||
405 | * The unit passed is the return value from the register function. | ||
406 | */ | ||
407 | |||
408 | void unregister_sound_mixer(int unit) | ||
409 | { | ||
410 | sound_remove_unit(&chains[0], unit); | ||
411 | } | ||
412 | |||
413 | EXPORT_SYMBOL(unregister_sound_mixer); | ||
414 | |||
415 | /** | ||
416 | * unregister_sound_midi - unregister a midi device | ||
417 | * @unit: unit number to allocate | ||
418 | * | ||
419 | * Release a sound device that was allocated with register_sound_midi(). | ||
420 | * The unit passed is the return value from the register function. | ||
421 | */ | ||
422 | |||
423 | void unregister_sound_midi(int unit) | ||
424 | { | ||
425 | return sound_remove_unit(&chains[2], unit); | ||
426 | } | ||
427 | |||
428 | EXPORT_SYMBOL(unregister_sound_midi); | ||
429 | |||
430 | /** | ||
431 | * unregister_sound_dsp - unregister a DSP device | ||
432 | * @unit: unit number to allocate | ||
433 | * | ||
434 | * Release a sound device that was allocated with register_sound_dsp(). | ||
435 | * The unit passed is the return value from the register function. | ||
436 | * | ||
437 | * Both of the allocated units are released together automatically. | ||
438 | */ | ||
439 | |||
440 | void unregister_sound_dsp(int unit) | ||
441 | { | ||
442 | return sound_remove_unit(&chains[3], unit); | ||
443 | } | ||
444 | |||
445 | |||
446 | EXPORT_SYMBOL(unregister_sound_dsp); | ||
447 | |||
448 | /** | ||
449 | * unregister_sound_synth - unregister a synth device | ||
450 | * @unit: unit number to allocate | ||
451 | * | ||
452 | * Release a sound device that was allocated with register_sound_synth(). | ||
453 | * The unit passed is the return value from the register function. | ||
454 | */ | ||
455 | |||
456 | void unregister_sound_synth(int unit) | ||
457 | { | ||
458 | return sound_remove_unit(&chains[9], unit); | ||
459 | } | ||
460 | |||
461 | EXPORT_SYMBOL(unregister_sound_synth); | ||
462 | |||
463 | /* | ||
464 | * Now our file operations | ||
465 | */ | ||
466 | |||
467 | static int soundcore_open(struct inode *, struct file *); | ||
468 | |||
469 | static struct file_operations soundcore_fops= | ||
470 | { | ||
471 | /* We must have an owner or the module locking fails */ | ||
472 | .owner = THIS_MODULE, | ||
473 | .open = soundcore_open, | ||
474 | }; | ||
475 | |||
476 | static struct sound_unit *__look_for_unit(int chain, int unit) | ||
477 | { | ||
478 | struct sound_unit *s; | ||
479 | |||
480 | s=chains[chain]; | ||
481 | while(s && s->unit_minor <= unit) | ||
482 | { | ||
483 | if(s->unit_minor==unit) | ||
484 | return s; | ||
485 | s=s->next; | ||
486 | } | ||
487 | return NULL; | ||
488 | } | ||
489 | |||
490 | int soundcore_open(struct inode *inode, struct file *file) | ||
491 | { | ||
492 | int chain; | ||
493 | int unit = iminor(inode); | ||
494 | struct sound_unit *s; | ||
495 | struct file_operations *new_fops = NULL; | ||
496 | |||
497 | chain=unit&0x0F; | ||
498 | if(chain==4 || chain==5) /* dsp/audio/dsp16 */ | ||
499 | { | ||
500 | unit&=0xF0; | ||
501 | unit|=3; | ||
502 | chain=3; | ||
503 | } | ||
504 | |||
505 | spin_lock(&sound_loader_lock); | ||
506 | s = __look_for_unit(chain, unit); | ||
507 | if (s) | ||
508 | new_fops = fops_get(s->unit_fops); | ||
509 | if (!new_fops) { | ||
510 | spin_unlock(&sound_loader_lock); | ||
511 | /* | ||
512 | * Please, don't change this order or code. | ||
513 | * For ALSA slot means soundcard and OSS emulation code | ||
514 | * comes as add-on modules which aren't depend on | ||
515 | * ALSA toplevel modules for soundcards, thus we need | ||
516 | * load them at first. [Jaroslav Kysela <perex@jcu.cz>] | ||
517 | */ | ||
518 | request_module("sound-slot-%i", unit>>4); | ||
519 | request_module("sound-service-%i-%i", unit>>4, chain); | ||
520 | spin_lock(&sound_loader_lock); | ||
521 | s = __look_for_unit(chain, unit); | ||
522 | if (s) | ||
523 | new_fops = fops_get(s->unit_fops); | ||
524 | } | ||
525 | if (new_fops) { | ||
526 | /* | ||
527 | * We rely upon the fact that we can't be unloaded while the | ||
528 | * subdriver is there, so if ->open() is successful we can | ||
529 | * safely drop the reference counter and if it is not we can | ||
530 | * revert to old ->f_op. Ugly, indeed, but that's the cost of | ||
531 | * switching ->f_op in the first place. | ||
532 | */ | ||
533 | int err = 0; | ||
534 | struct file_operations *old_fops = file->f_op; | ||
535 | file->f_op = new_fops; | ||
536 | spin_unlock(&sound_loader_lock); | ||
537 | if(file->f_op->open) | ||
538 | err = file->f_op->open(inode,file); | ||
539 | if (err) { | ||
540 | fops_put(file->f_op); | ||
541 | file->f_op = fops_get(old_fops); | ||
542 | } | ||
543 | fops_put(old_fops); | ||
544 | return err; | ||
545 | } | ||
546 | spin_unlock(&sound_loader_lock); | ||
547 | return -ENODEV; | ||
548 | } | ||
549 | |||
550 | extern int mod_firmware_load(const char *, char **); | ||
551 | EXPORT_SYMBOL(mod_firmware_load); | ||
552 | |||
553 | |||
554 | MODULE_DESCRIPTION("Core sound module"); | ||
555 | MODULE_AUTHOR("Alan Cox"); | ||
556 | MODULE_LICENSE("GPL"); | ||
557 | MODULE_ALIAS_CHARDEV_MAJOR(SOUND_MAJOR); | ||
558 | |||
559 | static void __exit cleanup_soundcore(void) | ||
560 | { | ||
561 | /* We have nothing to really do here - we know the lists must be | ||
562 | empty */ | ||
563 | unregister_chrdev(SOUND_MAJOR, "sound"); | ||
564 | devfs_remove("sound"); | ||
565 | class_simple_destroy(sound_class); | ||
566 | } | ||
567 | |||
568 | static int __init init_soundcore(void) | ||
569 | { | ||
570 | if (register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1) { | ||
571 | printk(KERN_ERR "soundcore: sound device already in use.\n"); | ||
572 | return -EBUSY; | ||
573 | } | ||
574 | devfs_mk_dir ("sound"); | ||
575 | sound_class = class_simple_create(THIS_MODULE, "sound"); | ||
576 | if (IS_ERR(sound_class)) | ||
577 | return PTR_ERR(sound_class); | ||
578 | |||
579 | return 0; | ||
580 | } | ||
581 | |||
582 | module_init(init_soundcore); | ||
583 | module_exit(cleanup_soundcore); | ||