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/oss/soundcard.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/oss/soundcard.c')
-rw-r--r-- | sound/oss/soundcard.c | 751 |
1 files changed, 751 insertions, 0 deletions
diff --git a/sound/oss/soundcard.c b/sound/oss/soundcard.c new file mode 100644 index 000000000000..de91c90a0112 --- /dev/null +++ b/sound/oss/soundcard.c | |||
@@ -0,0 +1,751 @@ | |||
1 | /* | ||
2 | * linux/drivers/sound/soundcard.c | ||
3 | * | ||
4 | * Sound card driver for Linux | ||
5 | * | ||
6 | * | ||
7 | * Copyright (C) by Hannu Savolainen 1993-1997 | ||
8 | * | ||
9 | * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) | ||
10 | * Version 2 (June 1991). See the "COPYING" file distributed with this software | ||
11 | * for more info. | ||
12 | * | ||
13 | * | ||
14 | * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) | ||
15 | * integrated sound_switch.c | ||
16 | * Stefan Reinauer : integrated /proc/sound (equals to /dev/sndstat, | ||
17 | * which should disappear in the near future) | ||
18 | * Eric Dumas : devfs support (22-Jan-98) <dumas@linux.eu.org> with | ||
19 | * fixups by C. Scott Ananian <cananian@alumni.princeton.edu> | ||
20 | * Richard Gooch : moved common (non OSS-specific) devices to sound_core.c | ||
21 | * Rob Riggs : Added persistent DMA buffers support (1998/10/17) | ||
22 | * Christoph Hellwig : Some cleanup work (2000/03/01) | ||
23 | */ | ||
24 | |||
25 | #include <linux/config.h> | ||
26 | |||
27 | #include "sound_config.h" | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/types.h> | ||
30 | #include <linux/errno.h> | ||
31 | #include <linux/signal.h> | ||
32 | #include <linux/fcntl.h> | ||
33 | #include <linux/ctype.h> | ||
34 | #include <linux/stddef.h> | ||
35 | #include <linux/kmod.h> | ||
36 | #include <asm/dma.h> | ||
37 | #include <asm/io.h> | ||
38 | #include <linux/wait.h> | ||
39 | #include <linux/slab.h> | ||
40 | #include <linux/ioport.h> | ||
41 | #include <linux/devfs_fs_kernel.h> | ||
42 | #include <linux/major.h> | ||
43 | #include <linux/delay.h> | ||
44 | #include <linux/proc_fs.h> | ||
45 | #include <linux/smp_lock.h> | ||
46 | #include <linux/module.h> | ||
47 | |||
48 | /* | ||
49 | * This ought to be moved into include/asm/dma.h | ||
50 | */ | ||
51 | #ifndef valid_dma | ||
52 | #define valid_dma(n) ((n) >= 0 && (n) < MAX_DMA_CHANNELS && (n) != 4) | ||
53 | #endif | ||
54 | |||
55 | /* | ||
56 | * Table for permanently allocated memory (used when unloading the module) | ||
57 | */ | ||
58 | void * sound_mem_blocks[1024]; | ||
59 | int sound_nblocks = 0; | ||
60 | |||
61 | /* Persistent DMA buffers */ | ||
62 | #ifdef CONFIG_SOUND_DMAP | ||
63 | int sound_dmap_flag = 1; | ||
64 | #else | ||
65 | int sound_dmap_flag = 0; | ||
66 | #endif | ||
67 | |||
68 | static char dma_alloc_map[MAX_DMA_CHANNELS]; | ||
69 | |||
70 | #define DMA_MAP_UNAVAIL 0 | ||
71 | #define DMA_MAP_FREE 1 | ||
72 | #define DMA_MAP_BUSY 2 | ||
73 | |||
74 | |||
75 | unsigned long seq_time = 0; /* Time for /dev/sequencer */ | ||
76 | extern struct class_simple *sound_class; | ||
77 | |||
78 | /* | ||
79 | * Table for configurable mixer volume handling | ||
80 | */ | ||
81 | static mixer_vol_table mixer_vols[MAX_MIXER_DEV]; | ||
82 | static int num_mixer_volumes; | ||
83 | |||
84 | int *load_mixer_volumes(char *name, int *levels, int present) | ||
85 | { | ||
86 | int i, n; | ||
87 | |||
88 | for (i = 0; i < num_mixer_volumes; i++) { | ||
89 | if (strcmp(name, mixer_vols[i].name) == 0) { | ||
90 | if (present) | ||
91 | mixer_vols[i].num = i; | ||
92 | return mixer_vols[i].levels; | ||
93 | } | ||
94 | } | ||
95 | if (num_mixer_volumes >= MAX_MIXER_DEV) { | ||
96 | printk(KERN_ERR "Sound: Too many mixers (%s)\n", name); | ||
97 | return levels; | ||
98 | } | ||
99 | n = num_mixer_volumes++; | ||
100 | |||
101 | strcpy(mixer_vols[n].name, name); | ||
102 | |||
103 | if (present) | ||
104 | mixer_vols[n].num = n; | ||
105 | else | ||
106 | mixer_vols[n].num = -1; | ||
107 | |||
108 | for (i = 0; i < 32; i++) | ||
109 | mixer_vols[n].levels[i] = levels[i]; | ||
110 | return mixer_vols[n].levels; | ||
111 | } | ||
112 | |||
113 | static int set_mixer_levels(void __user * arg) | ||
114 | { | ||
115 | /* mixer_vol_table is 174 bytes, so IMHO no reason to not allocate it on the stack */ | ||
116 | mixer_vol_table buf; | ||
117 | |||
118 | if (__copy_from_user(&buf, arg, sizeof(buf))) | ||
119 | return -EFAULT; | ||
120 | load_mixer_volumes(buf.name, buf.levels, 0); | ||
121 | if (__copy_to_user(arg, &buf, sizeof(buf))) | ||
122 | return -EFAULT; | ||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static int get_mixer_levels(void __user * arg) | ||
127 | { | ||
128 | int n; | ||
129 | |||
130 | if (__get_user(n, (int __user *)(&(((mixer_vol_table __user *)arg)->num)))) | ||
131 | return -EFAULT; | ||
132 | if (n < 0 || n >= num_mixer_volumes) | ||
133 | return -EINVAL; | ||
134 | if (__copy_to_user(arg, &mixer_vols[n], sizeof(mixer_vol_table))) | ||
135 | return -EFAULT; | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | /* 4K page size but our output routines use some slack for overruns */ | ||
140 | #define PROC_BLOCK_SIZE (3*1024) | ||
141 | |||
142 | static ssize_t sound_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | ||
143 | { | ||
144 | int dev = iminor(file->f_dentry->d_inode); | ||
145 | int ret = -EINVAL; | ||
146 | |||
147 | /* | ||
148 | * The OSS drivers aren't remotely happy without this locking, | ||
149 | * and unless someone fixes them when they are about to bite the | ||
150 | * big one anyway, we might as well bandage here.. | ||
151 | */ | ||
152 | |||
153 | lock_kernel(); | ||
154 | |||
155 | DEB(printk("sound_read(dev=%d, count=%d)\n", dev, count)); | ||
156 | switch (dev & 0x0f) { | ||
157 | case SND_DEV_DSP: | ||
158 | case SND_DEV_DSP16: | ||
159 | case SND_DEV_AUDIO: | ||
160 | ret = audio_read(dev, file, buf, count); | ||
161 | break; | ||
162 | |||
163 | case SND_DEV_SEQ: | ||
164 | case SND_DEV_SEQ2: | ||
165 | ret = sequencer_read(dev, file, buf, count); | ||
166 | break; | ||
167 | |||
168 | case SND_DEV_MIDIN: | ||
169 | ret = MIDIbuf_read(dev, file, buf, count); | ||
170 | } | ||
171 | unlock_kernel(); | ||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | static ssize_t sound_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) | ||
176 | { | ||
177 | int dev = iminor(file->f_dentry->d_inode); | ||
178 | int ret = -EINVAL; | ||
179 | |||
180 | lock_kernel(); | ||
181 | DEB(printk("sound_write(dev=%d, count=%d)\n", dev, count)); | ||
182 | switch (dev & 0x0f) { | ||
183 | case SND_DEV_SEQ: | ||
184 | case SND_DEV_SEQ2: | ||
185 | ret = sequencer_write(dev, file, buf, count); | ||
186 | break; | ||
187 | |||
188 | case SND_DEV_DSP: | ||
189 | case SND_DEV_DSP16: | ||
190 | case SND_DEV_AUDIO: | ||
191 | ret = audio_write(dev, file, buf, count); | ||
192 | break; | ||
193 | |||
194 | case SND_DEV_MIDIN: | ||
195 | ret = MIDIbuf_write(dev, file, buf, count); | ||
196 | break; | ||
197 | } | ||
198 | unlock_kernel(); | ||
199 | return ret; | ||
200 | } | ||
201 | |||
202 | static int sound_open(struct inode *inode, struct file *file) | ||
203 | { | ||
204 | int dev = iminor(inode); | ||
205 | int retval; | ||
206 | |||
207 | DEB(printk("sound_open(dev=%d)\n", dev)); | ||
208 | if ((dev >= SND_NDEVS) || (dev < 0)) { | ||
209 | printk(KERN_ERR "Invalid minor device %d\n", dev); | ||
210 | return -ENXIO; | ||
211 | } | ||
212 | switch (dev & 0x0f) { | ||
213 | case SND_DEV_CTL: | ||
214 | dev >>= 4; | ||
215 | if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) { | ||
216 | request_module("mixer%d", dev); | ||
217 | } | ||
218 | if (dev && (dev >= num_mixers || mixer_devs[dev] == NULL)) | ||
219 | return -ENXIO; | ||
220 | |||
221 | if (!try_module_get(mixer_devs[dev]->owner)) | ||
222 | return -ENXIO; | ||
223 | break; | ||
224 | |||
225 | case SND_DEV_SEQ: | ||
226 | case SND_DEV_SEQ2: | ||
227 | if ((retval = sequencer_open(dev, file)) < 0) | ||
228 | return retval; | ||
229 | break; | ||
230 | |||
231 | case SND_DEV_MIDIN: | ||
232 | if ((retval = MIDIbuf_open(dev, file)) < 0) | ||
233 | return retval; | ||
234 | break; | ||
235 | |||
236 | case SND_DEV_DSP: | ||
237 | case SND_DEV_DSP16: | ||
238 | case SND_DEV_AUDIO: | ||
239 | if ((retval = audio_open(dev, file)) < 0) | ||
240 | return retval; | ||
241 | break; | ||
242 | |||
243 | default: | ||
244 | printk(KERN_ERR "Invalid minor device %d\n", dev); | ||
245 | return -ENXIO; | ||
246 | } | ||
247 | |||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | static int sound_release(struct inode *inode, struct file *file) | ||
252 | { | ||
253 | int dev = iminor(inode); | ||
254 | |||
255 | lock_kernel(); | ||
256 | DEB(printk("sound_release(dev=%d)\n", dev)); | ||
257 | switch (dev & 0x0f) { | ||
258 | case SND_DEV_CTL: | ||
259 | module_put(mixer_devs[dev >> 4]->owner); | ||
260 | break; | ||
261 | |||
262 | case SND_DEV_SEQ: | ||
263 | case SND_DEV_SEQ2: | ||
264 | sequencer_release(dev, file); | ||
265 | break; | ||
266 | |||
267 | case SND_DEV_MIDIN: | ||
268 | MIDIbuf_release(dev, file); | ||
269 | break; | ||
270 | |||
271 | case SND_DEV_DSP: | ||
272 | case SND_DEV_DSP16: | ||
273 | case SND_DEV_AUDIO: | ||
274 | audio_release(dev, file); | ||
275 | break; | ||
276 | |||
277 | default: | ||
278 | printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev); | ||
279 | } | ||
280 | unlock_kernel(); | ||
281 | |||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | static int get_mixer_info(int dev, void __user *arg) | ||
286 | { | ||
287 | mixer_info info; | ||
288 | memset(&info, 0, sizeof(info)); | ||
289 | strlcpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); | ||
290 | strlcpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); | ||
291 | info.modify_counter = mixer_devs[dev]->modify_counter; | ||
292 | if (__copy_to_user(arg, &info, sizeof(info))) | ||
293 | return -EFAULT; | ||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | static int get_old_mixer_info(int dev, void __user *arg) | ||
298 | { | ||
299 | _old_mixer_info info; | ||
300 | memset(&info, 0, sizeof(info)); | ||
301 | strlcpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); | ||
302 | strlcpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); | ||
303 | if (copy_to_user(arg, &info, sizeof(info))) | ||
304 | return -EFAULT; | ||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | static int sound_mixer_ioctl(int mixdev, unsigned int cmd, void __user *arg) | ||
309 | { | ||
310 | if (mixdev < 0 || mixdev >= MAX_MIXER_DEV) | ||
311 | return -ENXIO; | ||
312 | /* Try to load the mixer... */ | ||
313 | if (mixer_devs[mixdev] == NULL) { | ||
314 | request_module("mixer%d", mixdev); | ||
315 | } | ||
316 | if (mixdev >= num_mixers || !mixer_devs[mixdev]) | ||
317 | return -ENXIO; | ||
318 | if (cmd == SOUND_MIXER_INFO) | ||
319 | return get_mixer_info(mixdev, arg); | ||
320 | if (cmd == SOUND_OLD_MIXER_INFO) | ||
321 | return get_old_mixer_info(mixdev, arg); | ||
322 | if (_SIOC_DIR(cmd) & _SIOC_WRITE) | ||
323 | mixer_devs[mixdev]->modify_counter++; | ||
324 | if (!mixer_devs[mixdev]->ioctl) | ||
325 | return -EINVAL; | ||
326 | return mixer_devs[mixdev]->ioctl(mixdev, cmd, arg); | ||
327 | } | ||
328 | |||
329 | static int sound_ioctl(struct inode *inode, struct file *file, | ||
330 | unsigned int cmd, unsigned long arg) | ||
331 | { | ||
332 | int len = 0, dtype; | ||
333 | int dev = iminor(inode); | ||
334 | void __user *p = (void __user *)arg; | ||
335 | |||
336 | if (_SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0) { | ||
337 | /* | ||
338 | * Have to validate the address given by the process. | ||
339 | */ | ||
340 | len = _SIOC_SIZE(cmd); | ||
341 | if (len < 1 || len > 65536 || !p) | ||
342 | return -EFAULT; | ||
343 | if (_SIOC_DIR(cmd) & _SIOC_WRITE) | ||
344 | if (!access_ok(VERIFY_READ, p, len)) | ||
345 | return -EFAULT; | ||
346 | if (_SIOC_DIR(cmd) & _SIOC_READ) | ||
347 | if (!access_ok(VERIFY_WRITE, p, len)) | ||
348 | return -EFAULT; | ||
349 | } | ||
350 | DEB(printk("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg)); | ||
351 | if (cmd == OSS_GETVERSION) | ||
352 | return __put_user(SOUND_VERSION, (int __user *)p); | ||
353 | |||
354 | if (_IOC_TYPE(cmd) == 'M' && num_mixers > 0 && /* Mixer ioctl */ | ||
355 | (dev & 0x0f) != SND_DEV_CTL) { | ||
356 | dtype = dev & 0x0f; | ||
357 | switch (dtype) { | ||
358 | case SND_DEV_DSP: | ||
359 | case SND_DEV_DSP16: | ||
360 | case SND_DEV_AUDIO: | ||
361 | return sound_mixer_ioctl(audio_devs[dev >> 4]->mixer_dev, | ||
362 | cmd, p); | ||
363 | |||
364 | default: | ||
365 | return sound_mixer_ioctl(dev >> 4, cmd, p); | ||
366 | } | ||
367 | } | ||
368 | switch (dev & 0x0f) { | ||
369 | case SND_DEV_CTL: | ||
370 | if (cmd == SOUND_MIXER_GETLEVELS) | ||
371 | return get_mixer_levels(p); | ||
372 | if (cmd == SOUND_MIXER_SETLEVELS) | ||
373 | return set_mixer_levels(p); | ||
374 | return sound_mixer_ioctl(dev >> 4, cmd, p); | ||
375 | |||
376 | case SND_DEV_SEQ: | ||
377 | case SND_DEV_SEQ2: | ||
378 | return sequencer_ioctl(dev, file, cmd, p); | ||
379 | |||
380 | case SND_DEV_DSP: | ||
381 | case SND_DEV_DSP16: | ||
382 | case SND_DEV_AUDIO: | ||
383 | return audio_ioctl(dev, file, cmd, p); | ||
384 | break; | ||
385 | |||
386 | case SND_DEV_MIDIN: | ||
387 | return MIDIbuf_ioctl(dev, file, cmd, p); | ||
388 | break; | ||
389 | |||
390 | } | ||
391 | return -EINVAL; | ||
392 | } | ||
393 | |||
394 | static unsigned int sound_poll(struct file *file, poll_table * wait) | ||
395 | { | ||
396 | struct inode *inode = file->f_dentry->d_inode; | ||
397 | int dev = iminor(inode); | ||
398 | |||
399 | DEB(printk("sound_poll(dev=%d)\n", dev)); | ||
400 | switch (dev & 0x0f) { | ||
401 | case SND_DEV_SEQ: | ||
402 | case SND_DEV_SEQ2: | ||
403 | return sequencer_poll(dev, file, wait); | ||
404 | |||
405 | case SND_DEV_MIDIN: | ||
406 | return MIDIbuf_poll(dev, file, wait); | ||
407 | |||
408 | case SND_DEV_DSP: | ||
409 | case SND_DEV_DSP16: | ||
410 | case SND_DEV_AUDIO: | ||
411 | return DMAbuf_poll(file, dev >> 4, wait); | ||
412 | } | ||
413 | return 0; | ||
414 | } | ||
415 | |||
416 | static int sound_mmap(struct file *file, struct vm_area_struct *vma) | ||
417 | { | ||
418 | int dev_class; | ||
419 | unsigned long size; | ||
420 | struct dma_buffparms *dmap = NULL; | ||
421 | int dev = iminor(file->f_dentry->d_inode); | ||
422 | |||
423 | dev_class = dev & 0x0f; | ||
424 | dev >>= 4; | ||
425 | |||
426 | if (dev_class != SND_DEV_DSP && dev_class != SND_DEV_DSP16 && dev_class != SND_DEV_AUDIO) { | ||
427 | printk(KERN_ERR "Sound: mmap() not supported for other than audio devices\n"); | ||
428 | return -EINVAL; | ||
429 | } | ||
430 | lock_kernel(); | ||
431 | if (vma->vm_flags & VM_WRITE) /* Map write and read/write to the output buf */ | ||
432 | dmap = audio_devs[dev]->dmap_out; | ||
433 | else if (vma->vm_flags & VM_READ) | ||
434 | dmap = audio_devs[dev]->dmap_in; | ||
435 | else { | ||
436 | printk(KERN_ERR "Sound: Undefined mmap() access\n"); | ||
437 | unlock_kernel(); | ||
438 | return -EINVAL; | ||
439 | } | ||
440 | |||
441 | if (dmap == NULL) { | ||
442 | printk(KERN_ERR "Sound: mmap() error. dmap == NULL\n"); | ||
443 | unlock_kernel(); | ||
444 | return -EIO; | ||
445 | } | ||
446 | if (dmap->raw_buf == NULL) { | ||
447 | printk(KERN_ERR "Sound: mmap() called when raw_buf == NULL\n"); | ||
448 | unlock_kernel(); | ||
449 | return -EIO; | ||
450 | } | ||
451 | if (dmap->mapping_flags) { | ||
452 | printk(KERN_ERR "Sound: mmap() called twice for the same DMA buffer\n"); | ||
453 | unlock_kernel(); | ||
454 | return -EIO; | ||
455 | } | ||
456 | if (vma->vm_pgoff != 0) { | ||
457 | printk(KERN_ERR "Sound: mmap() offset must be 0.\n"); | ||
458 | unlock_kernel(); | ||
459 | return -EINVAL; | ||
460 | } | ||
461 | size = vma->vm_end - vma->vm_start; | ||
462 | |||
463 | if (size != dmap->bytes_in_use) { | ||
464 | printk(KERN_WARNING "Sound: mmap() size = %ld. Should be %d\n", size, dmap->bytes_in_use); | ||
465 | } | ||
466 | if (remap_pfn_range(vma, vma->vm_start, | ||
467 | virt_to_phys(dmap->raw_buf) >> PAGE_SHIFT, | ||
468 | vma->vm_end - vma->vm_start, vma->vm_page_prot)) { | ||
469 | unlock_kernel(); | ||
470 | return -EAGAIN; | ||
471 | } | ||
472 | |||
473 | dmap->mapping_flags |= DMA_MAP_MAPPED; | ||
474 | |||
475 | if( audio_devs[dev]->d->mmap) | ||
476 | audio_devs[dev]->d->mmap(dev); | ||
477 | |||
478 | memset(dmap->raw_buf, | ||
479 | dmap->neutral_byte, | ||
480 | dmap->bytes_in_use); | ||
481 | unlock_kernel(); | ||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | struct file_operations oss_sound_fops = { | ||
486 | .owner = THIS_MODULE, | ||
487 | .llseek = no_llseek, | ||
488 | .read = sound_read, | ||
489 | .write = sound_write, | ||
490 | .poll = sound_poll, | ||
491 | .ioctl = sound_ioctl, | ||
492 | .mmap = sound_mmap, | ||
493 | .open = sound_open, | ||
494 | .release = sound_release, | ||
495 | }; | ||
496 | |||
497 | /* | ||
498 | * Create the required special subdevices | ||
499 | */ | ||
500 | |||
501 | static int create_special_devices(void) | ||
502 | { | ||
503 | int seq1,seq2; | ||
504 | seq1=register_sound_special(&oss_sound_fops, 1); | ||
505 | if(seq1==-1) | ||
506 | goto bad; | ||
507 | seq2=register_sound_special(&oss_sound_fops, 8); | ||
508 | if(seq2!=-1) | ||
509 | return 0; | ||
510 | unregister_sound_special(1); | ||
511 | bad: | ||
512 | return -1; | ||
513 | } | ||
514 | |||
515 | |||
516 | /* These device names follow the official Linux device list, | ||
517 | * Documentation/devices.txt. Let us know if there are other | ||
518 | * common names we should support for compatibility. | ||
519 | * Only those devices not created by the generic code in sound_core.c are | ||
520 | * registered here. | ||
521 | */ | ||
522 | static const struct { | ||
523 | unsigned short minor; | ||
524 | char *name; | ||
525 | umode_t mode; | ||
526 | int *num; | ||
527 | } dev_list[] = { /* list of minor devices */ | ||
528 | /* seems to be some confusion here -- this device is not in the device list */ | ||
529 | {SND_DEV_DSP16, "dspW", S_IWUGO | S_IRUSR | S_IRGRP, | ||
530 | &num_audiodevs}, | ||
531 | {SND_DEV_AUDIO, "audio", S_IWUGO | S_IRUSR | S_IRGRP, | ||
532 | &num_audiodevs}, | ||
533 | }; | ||
534 | |||
535 | static int dmabuf; | ||
536 | static int dmabug; | ||
537 | |||
538 | module_param(dmabuf, int, 0444); | ||
539 | module_param(dmabug, int, 0444); | ||
540 | |||
541 | static int __init oss_init(void) | ||
542 | { | ||
543 | int err; | ||
544 | int i, j; | ||
545 | |||
546 | /* drag in sound_syms.o */ | ||
547 | { | ||
548 | extern char sound_syms_symbol; | ||
549 | sound_syms_symbol = 0; | ||
550 | } | ||
551 | |||
552 | #ifdef CONFIG_PCI | ||
553 | if(dmabug) | ||
554 | isa_dma_bridge_buggy = dmabug; | ||
555 | #endif | ||
556 | |||
557 | err = create_special_devices(); | ||
558 | if (err) { | ||
559 | printk(KERN_ERR "sound: driver already loaded/included in kernel\n"); | ||
560 | return err; | ||
561 | } | ||
562 | |||
563 | /* Protecting the innocent */ | ||
564 | sound_dmap_flag = (dmabuf > 0 ? 1 : 0); | ||
565 | |||
566 | for (i = 0; i < sizeof (dev_list) / sizeof *dev_list; i++) { | ||
567 | devfs_mk_cdev(MKDEV(SOUND_MAJOR, dev_list[i].minor), | ||
568 | S_IFCHR | dev_list[i].mode, | ||
569 | "sound/%s", dev_list[i].name); | ||
570 | class_simple_device_add(sound_class, | ||
571 | MKDEV(SOUND_MAJOR, dev_list[i].minor), | ||
572 | NULL, "%s", dev_list[i].name); | ||
573 | |||
574 | if (!dev_list[i].num) | ||
575 | continue; | ||
576 | |||
577 | for (j = 1; j < *dev_list[i].num; j++) { | ||
578 | devfs_mk_cdev(MKDEV(SOUND_MAJOR, | ||
579 | dev_list[i].minor + (j*0x10)), | ||
580 | S_IFCHR | dev_list[i].mode, | ||
581 | "sound/%s%d", dev_list[i].name, j); | ||
582 | class_simple_device_add(sound_class, | ||
583 | MKDEV(SOUND_MAJOR, dev_list[i].minor + (j*0x10)), | ||
584 | NULL, | ||
585 | "%s%d", dev_list[i].name, j); | ||
586 | } | ||
587 | } | ||
588 | |||
589 | if (sound_nblocks >= 1024) | ||
590 | printk(KERN_ERR "Sound warning: Deallocation table was too small.\n"); | ||
591 | |||
592 | return 0; | ||
593 | } | ||
594 | |||
595 | static void __exit oss_cleanup(void) | ||
596 | { | ||
597 | int i, j; | ||
598 | |||
599 | for (i = 0; i < sizeof (dev_list) / sizeof *dev_list; i++) { | ||
600 | devfs_remove("sound/%s", dev_list[i].name); | ||
601 | class_simple_device_remove(MKDEV(SOUND_MAJOR, dev_list[i].minor)); | ||
602 | if (!dev_list[i].num) | ||
603 | continue; | ||
604 | for (j = 1; j < *dev_list[i].num; j++) { | ||
605 | devfs_remove("sound/%s%d", dev_list[i].name, j); | ||
606 | class_simple_device_remove(MKDEV(SOUND_MAJOR, dev_list[i].minor + (j*0x10))); | ||
607 | } | ||
608 | } | ||
609 | |||
610 | unregister_sound_special(1); | ||
611 | unregister_sound_special(8); | ||
612 | |||
613 | sound_stop_timer(); | ||
614 | |||
615 | sequencer_unload(); | ||
616 | |||
617 | for (i = 0; i < MAX_DMA_CHANNELS; i++) | ||
618 | if (dma_alloc_map[i] != DMA_MAP_UNAVAIL) { | ||
619 | printk(KERN_ERR "Sound: Hmm, DMA%d was left allocated - fixed\n", i); | ||
620 | sound_free_dma(i); | ||
621 | } | ||
622 | |||
623 | for (i = 0; i < sound_nblocks; i++) | ||
624 | vfree(sound_mem_blocks[i]); | ||
625 | |||
626 | } | ||
627 | |||
628 | module_init(oss_init); | ||
629 | module_exit(oss_cleanup); | ||
630 | MODULE_LICENSE("GPL"); | ||
631 | |||
632 | |||
633 | int sound_alloc_dma(int chn, char *deviceID) | ||
634 | { | ||
635 | int err; | ||
636 | |||
637 | if ((err = request_dma(chn, deviceID)) != 0) | ||
638 | return err; | ||
639 | |||
640 | dma_alloc_map[chn] = DMA_MAP_FREE; | ||
641 | |||
642 | return 0; | ||
643 | } | ||
644 | |||
645 | int sound_open_dma(int chn, char *deviceID) | ||
646 | { | ||
647 | if (!valid_dma(chn)) { | ||
648 | printk(KERN_ERR "sound_open_dma: Invalid DMA channel %d\n", chn); | ||
649 | return 1; | ||
650 | } | ||
651 | |||
652 | if (dma_alloc_map[chn] != DMA_MAP_FREE) { | ||
653 | printk("sound_open_dma: DMA channel %d busy or not allocated (%d)\n", chn, dma_alloc_map[chn]); | ||
654 | return 1; | ||
655 | } | ||
656 | dma_alloc_map[chn] = DMA_MAP_BUSY; | ||
657 | return 0; | ||
658 | } | ||
659 | |||
660 | void sound_free_dma(int chn) | ||
661 | { | ||
662 | if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL) { | ||
663 | /* printk( "sound_free_dma: Bad access to DMA channel %d\n", chn); */ | ||
664 | return; | ||
665 | } | ||
666 | free_dma(chn); | ||
667 | dma_alloc_map[chn] = DMA_MAP_UNAVAIL; | ||
668 | } | ||
669 | |||
670 | void sound_close_dma(int chn) | ||
671 | { | ||
672 | if (dma_alloc_map[chn] != DMA_MAP_BUSY) { | ||
673 | printk(KERN_ERR "sound_close_dma: Bad access to DMA channel %d\n", chn); | ||
674 | return; | ||
675 | } | ||
676 | dma_alloc_map[chn] = DMA_MAP_FREE; | ||
677 | } | ||
678 | |||
679 | static void do_sequencer_timer(unsigned long dummy) | ||
680 | { | ||
681 | sequencer_timer(0); | ||
682 | } | ||
683 | |||
684 | |||
685 | static struct timer_list seq_timer = | ||
686 | TIMER_INITIALIZER(do_sequencer_timer, 0, 0); | ||
687 | |||
688 | void request_sound_timer(int count) | ||
689 | { | ||
690 | extern unsigned long seq_time; | ||
691 | |||
692 | if (count < 0) { | ||
693 | seq_timer.expires = (-count) + jiffies; | ||
694 | add_timer(&seq_timer); | ||
695 | return; | ||
696 | } | ||
697 | count += seq_time; | ||
698 | |||
699 | count -= jiffies; | ||
700 | |||
701 | if (count < 1) | ||
702 | count = 1; | ||
703 | |||
704 | seq_timer.expires = (count) + jiffies; | ||
705 | add_timer(&seq_timer); | ||
706 | } | ||
707 | |||
708 | void sound_stop_timer(void) | ||
709 | { | ||
710 | del_timer(&seq_timer); | ||
711 | } | ||
712 | |||
713 | void conf_printf(char *name, struct address_info *hw_config) | ||
714 | { | ||
715 | #ifndef CONFIG_SOUND_TRACEINIT | ||
716 | return; | ||
717 | #else | ||
718 | printk("<%s> at 0x%03x", name, hw_config->io_base); | ||
719 | |||
720 | if (hw_config->irq) | ||
721 | printk(" irq %d", (hw_config->irq > 0) ? hw_config->irq : -hw_config->irq); | ||
722 | |||
723 | if (hw_config->dma != -1 || hw_config->dma2 != -1) | ||
724 | { | ||
725 | printk(" dma %d", hw_config->dma); | ||
726 | if (hw_config->dma2 != -1) | ||
727 | printk(",%d", hw_config->dma2); | ||
728 | } | ||
729 | printk("\n"); | ||
730 | #endif | ||
731 | } | ||
732 | |||
733 | void conf_printf2(char *name, int base, int irq, int dma, int dma2) | ||
734 | { | ||
735 | #ifndef CONFIG_SOUND_TRACEINIT | ||
736 | return; | ||
737 | #else | ||
738 | printk("<%s> at 0x%03x", name, base); | ||
739 | |||
740 | if (irq) | ||
741 | printk(" irq %d", (irq > 0) ? irq : -irq); | ||
742 | |||
743 | if (dma != -1 || dma2 != -1) | ||
744 | { | ||
745 | printk(" dma %d", dma); | ||
746 | if (dma2 != -1) | ||
747 | printk(",%d", dma2); | ||
748 | } | ||
749 | printk("\n"); | ||
750 | #endif | ||
751 | } | ||