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/maui.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/maui.c')
-rw-r--r-- | sound/oss/maui.c | 478 |
1 files changed, 478 insertions, 0 deletions
diff --git a/sound/oss/maui.c b/sound/oss/maui.c new file mode 100644 index 000000000000..05cf194eda6b --- /dev/null +++ b/sound/oss/maui.c | |||
@@ -0,0 +1,478 @@ | |||
1 | /* | ||
2 | * sound/maui.c | ||
3 | * | ||
4 | * The low level driver for Turtle Beach Maui and Tropez. | ||
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 | * Changes: | ||
14 | * Alan Cox General clean up, use kernel IRQ | ||
15 | * system | ||
16 | * Christoph Hellwig Adapted to module_init/module_exit | ||
17 | * Bartlomiej Zolnierkiewicz | ||
18 | * Added __init to download_code() | ||
19 | * | ||
20 | * Status: | ||
21 | * Andrew J. Kroll Tested 06/01/1999 with: | ||
22 | * * OSWF.MOT File Version: 1.15 | ||
23 | * * OSWF.MOT File Dated: 09/12/94 | ||
24 | * * Older versions will cause problems. | ||
25 | */ | ||
26 | |||
27 | #include <linux/interrupt.h> | ||
28 | #include <linux/config.h> | ||
29 | #include <linux/module.h> | ||
30 | #include <linux/init.h> | ||
31 | |||
32 | #define USE_SEQ_MACROS | ||
33 | #define USE_SIMPLE_MACROS | ||
34 | |||
35 | #include "sound_config.h" | ||
36 | #include "sound_firmware.h" | ||
37 | |||
38 | #include "mpu401.h" | ||
39 | |||
40 | static int maui_base = 0x330; | ||
41 | |||
42 | static volatile int irq_ok; | ||
43 | static int *maui_osp; | ||
44 | |||
45 | #define HOST_DATA_PORT (maui_base + 2) | ||
46 | #define HOST_STAT_PORT (maui_base + 3) | ||
47 | #define HOST_CTRL_PORT (maui_base + 3) | ||
48 | |||
49 | #define STAT_TX_INTR 0x40 | ||
50 | #define STAT_TX_AVAIL 0x20 | ||
51 | #define STAT_TX_IENA 0x10 | ||
52 | #define STAT_RX_INTR 0x04 | ||
53 | #define STAT_RX_AVAIL 0x02 | ||
54 | #define STAT_RX_IENA 0x01 | ||
55 | |||
56 | static int (*orig_load_patch)(int dev, int format, const char __user *addr, | ||
57 | int offs, int count, int pmgr_flag) = NULL; | ||
58 | |||
59 | #include "maui_boot.h" | ||
60 | |||
61 | static int maui_wait(int mask) | ||
62 | { | ||
63 | int i; | ||
64 | |||
65 | /* | ||
66 | * Perform a short initial wait without sleeping | ||
67 | */ | ||
68 | |||
69 | for (i = 0; i < 100; i++) | ||
70 | if (inb(HOST_STAT_PORT) & mask) | ||
71 | return 1; | ||
72 | |||
73 | /* | ||
74 | * Wait up to 15 seconds with sleeping | ||
75 | */ | ||
76 | |||
77 | for (i = 0; i < 150; i++) { | ||
78 | if (inb(HOST_STAT_PORT) & mask) | ||
79 | return 1; | ||
80 | current->state = TASK_INTERRUPTIBLE; | ||
81 | schedule_timeout(HZ / 10); | ||
82 | if (signal_pending(current)) | ||
83 | return 0; | ||
84 | } | ||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static int maui_read(void) | ||
89 | { | ||
90 | if (maui_wait(STAT_RX_AVAIL)) | ||
91 | return inb(HOST_DATA_PORT); | ||
92 | return -1; | ||
93 | } | ||
94 | |||
95 | static int maui_write(unsigned char data) | ||
96 | { | ||
97 | if (maui_wait(STAT_TX_AVAIL)) { | ||
98 | outb((data), HOST_DATA_PORT); | ||
99 | return 1; | ||
100 | } | ||
101 | printk(KERN_WARNING "Maui: Write timeout\n"); | ||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | static irqreturn_t mauiintr(int irq, void *dev_id, struct pt_regs *dummy) | ||
106 | { | ||
107 | irq_ok = 1; | ||
108 | return IRQ_HANDLED; | ||
109 | } | ||
110 | |||
111 | static int __init download_code(void) | ||
112 | { | ||
113 | int i, lines = 0; | ||
114 | int eol_seen = 0, done = 0; | ||
115 | int skip = 1; | ||
116 | |||
117 | printk(KERN_INFO "Code download (%d bytes): ", maui_osLen); | ||
118 | |||
119 | for (i = 0; i < maui_osLen; i++) { | ||
120 | if (maui_os[i] != '\r') { | ||
121 | if (!skip || (maui_os[i] == 'S' && (i == 0 || maui_os[i - 1] == '\n'))) { | ||
122 | skip = 0; | ||
123 | |||
124 | if (maui_os[i] == '\n') | ||
125 | eol_seen = skip = 1; | ||
126 | else if (maui_os[i] == 'S') { | ||
127 | if (maui_os[i + 1] == '8') | ||
128 | done = 1; | ||
129 | if (!maui_write(0xF1)) | ||
130 | goto failure; | ||
131 | if (!maui_write('S')) | ||
132 | goto failure; | ||
133 | } else { | ||
134 | if (!maui_write(maui_os[i])) | ||
135 | goto failure; | ||
136 | } | ||
137 | |||
138 | if (eol_seen) { | ||
139 | int c = 0; | ||
140 | int n; | ||
141 | |||
142 | eol_seen = 0; | ||
143 | |||
144 | for (n = 0; n < 2; n++) { | ||
145 | if (maui_wait(STAT_RX_AVAIL)) { | ||
146 | c = inb(HOST_DATA_PORT); | ||
147 | break; | ||
148 | } | ||
149 | } | ||
150 | if (c != 0x80) { | ||
151 | printk("Download not acknowledged\n"); | ||
152 | return 0; | ||
153 | } | ||
154 | else if (!(lines++ % 10)) | ||
155 | printk("."); | ||
156 | |||
157 | if (done) { | ||
158 | printk("\n"); | ||
159 | printk(KERN_INFO "Download complete\n"); | ||
160 | return 1; | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | |||
167 | failure: | ||
168 | printk("\n"); | ||
169 | printk(KERN_ERR "Download failed!!!\n"); | ||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | static int __init maui_init(int irq) | ||
174 | { | ||
175 | unsigned char bits; | ||
176 | |||
177 | switch (irq) { | ||
178 | case 9: | ||
179 | bits = 0x00; | ||
180 | break; | ||
181 | case 5: | ||
182 | bits = 0x08; | ||
183 | break; | ||
184 | case 12: | ||
185 | bits = 0x10; | ||
186 | break; | ||
187 | case 15: | ||
188 | bits = 0x18; | ||
189 | break; | ||
190 | |||
191 | default: | ||
192 | printk(KERN_ERR "Maui: Invalid IRQ %d\n", irq); | ||
193 | return 0; | ||
194 | } | ||
195 | outb((0x00), HOST_CTRL_PORT); /* Reset */ | ||
196 | outb((bits), HOST_DATA_PORT); /* Set the IRQ bits */ | ||
197 | outb((bits | 0x80), HOST_DATA_PORT); /* Set the IRQ bits again? */ | ||
198 | outb((0x80), HOST_CTRL_PORT); /* Leave reset */ | ||
199 | outb((0x80), HOST_CTRL_PORT); /* Leave reset */ | ||
200 | outb((0xD0), HOST_CTRL_PORT); /* Cause interrupt */ | ||
201 | |||
202 | #ifdef CONFIG_SMP | ||
203 | { | ||
204 | int i; | ||
205 | for (i = 0; i < 1000000 && !irq_ok; i++) | ||
206 | ; | ||
207 | if (!irq_ok) | ||
208 | return 0; | ||
209 | } | ||
210 | #endif | ||
211 | outb((0x80), HOST_CTRL_PORT); /* Leave reset */ | ||
212 | |||
213 | printk(KERN_INFO "Turtle Beach Maui initialization\n"); | ||
214 | |||
215 | if (!download_code()) | ||
216 | return 0; | ||
217 | |||
218 | outb((0xE0), HOST_CTRL_PORT); /* Normal operation */ | ||
219 | |||
220 | /* Select mpu401 mode */ | ||
221 | |||
222 | maui_write(0xf0); | ||
223 | maui_write(1); | ||
224 | if (maui_read() != 0x80) { | ||
225 | maui_write(0xf0); | ||
226 | maui_write(1); | ||
227 | if (maui_read() != 0x80) | ||
228 | printk(KERN_ERR "Maui didn't acknowledge set HW mode command\n"); | ||
229 | } | ||
230 | printk(KERN_INFO "Maui initialized OK\n"); | ||
231 | return 1; | ||
232 | } | ||
233 | |||
234 | static int maui_short_wait(int mask) { | ||
235 | int i; | ||
236 | |||
237 | for (i = 0; i < 1000; i++) { | ||
238 | if (inb(HOST_STAT_PORT) & mask) { | ||
239 | return 1; | ||
240 | } | ||
241 | } | ||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | static int maui_load_patch(int dev, int format, const char __user *addr, | ||
246 | int offs, int count, int pmgr_flag) | ||
247 | { | ||
248 | |||
249 | struct sysex_info header; | ||
250 | unsigned long left, src_offs; | ||
251 | int hdr_size = (unsigned long) &header.data[0] - (unsigned long) &header; | ||
252 | int i; | ||
253 | |||
254 | if (format == SYSEX_PATCH) /* Handled by midi_synth.c */ | ||
255 | return orig_load_patch(dev, format, addr, offs, count, pmgr_flag); | ||
256 | |||
257 | if (format != MAUI_PATCH) | ||
258 | { | ||
259 | printk(KERN_WARNING "Maui: Unknown patch format\n"); | ||
260 | } | ||
261 | if (count < hdr_size) { | ||
262 | /* printk("Maui error: Patch header too short\n");*/ | ||
263 | return -EINVAL; | ||
264 | } | ||
265 | count -= hdr_size; | ||
266 | |||
267 | /* | ||
268 | * Copy the header from user space but ignore the first bytes which have | ||
269 | * been transferred already. | ||
270 | */ | ||
271 | |||
272 | if(copy_from_user(&((char *) &header)[offs], &(addr)[offs], hdr_size - offs)) | ||
273 | return -EFAULT; | ||
274 | |||
275 | if (count < header.len) { | ||
276 | printk(KERN_ERR "Maui warning: Host command record too short (%d<%d)\n", count, (int) header.len); | ||
277 | header.len = count; | ||
278 | } | ||
279 | left = header.len; | ||
280 | src_offs = 0; | ||
281 | |||
282 | for (i = 0; i < left; i++) { | ||
283 | unsigned char data; | ||
284 | |||
285 | if(get_user(*(unsigned char *) &data, (unsigned char __user *) &((addr)[hdr_size + i]))) | ||
286 | return -EFAULT; | ||
287 | if (i == 0 && !(data & 0x80)) | ||
288 | return -EINVAL; | ||
289 | |||
290 | if (maui_write(data) == -1) | ||
291 | return -EIO; | ||
292 | } | ||
293 | |||
294 | if ((i = maui_read()) != 0x80) { | ||
295 | if (i != -1) | ||
296 | printk("Maui: Error status %02x\n", i); | ||
297 | return -EIO; | ||
298 | } | ||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | static int __init probe_maui(struct address_info *hw_config) | ||
303 | { | ||
304 | struct resource *ports; | ||
305 | int this_dev; | ||
306 | int i; | ||
307 | int tmp1, tmp2, ret; | ||
308 | |||
309 | ports = request_region(hw_config->io_base, 2, "mpu401"); | ||
310 | if (!ports) | ||
311 | return 0; | ||
312 | |||
313 | if (!request_region(hw_config->io_base + 2, 6, "Maui")) | ||
314 | goto out; | ||
315 | |||
316 | maui_base = hw_config->io_base; | ||
317 | maui_osp = hw_config->osp; | ||
318 | |||
319 | if (request_irq(hw_config->irq, mauiintr, 0, "Maui", NULL) < 0) | ||
320 | goto out2; | ||
321 | |||
322 | /* | ||
323 | * Initialize the processor if necessary | ||
324 | */ | ||
325 | |||
326 | if (maui_osLen > 0) { | ||
327 | if (!(inb(HOST_STAT_PORT) & STAT_TX_AVAIL) || | ||
328 | !maui_write(0x9F) || /* Report firmware version */ | ||
329 | !maui_short_wait(STAT_RX_AVAIL) || | ||
330 | maui_read() == -1 || maui_read() == -1) | ||
331 | if (!maui_init(hw_config->irq)) | ||
332 | goto out3; | ||
333 | } | ||
334 | if (!maui_write(0xCF)) /* Report hardware version */ { | ||
335 | printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); | ||
336 | goto out3; | ||
337 | } | ||
338 | if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) { | ||
339 | printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); | ||
340 | goto out3; | ||
341 | } | ||
342 | if (tmp1 == 0xff || tmp2 == 0xff) | ||
343 | goto out3; | ||
344 | printk(KERN_DEBUG "WaveFront hardware version %d.%d\n", tmp1, tmp2); | ||
345 | |||
346 | if (!maui_write(0x9F)) /* Report firmware version */ | ||
347 | goto out3; | ||
348 | if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) | ||
349 | goto out3; | ||
350 | |||
351 | printk(KERN_DEBUG "WaveFront firmware version %d.%d\n", tmp1, tmp2); | ||
352 | |||
353 | if (!maui_write(0x85)) /* Report free DRAM */ | ||
354 | goto out3; | ||
355 | tmp1 = 0; | ||
356 | for (i = 0; i < 4; i++) { | ||
357 | tmp1 |= maui_read() << (7 * i); | ||
358 | } | ||
359 | printk(KERN_DEBUG "Available DRAM %dk\n", tmp1 / 1024); | ||
360 | |||
361 | for (i = 0; i < 1000; i++) | ||
362 | if (probe_mpu401(hw_config, ports)) | ||
363 | break; | ||
364 | |||
365 | ret = probe_mpu401(hw_config, ports); | ||
366 | if (!ret) | ||
367 | goto out3; | ||
368 | |||
369 | conf_printf("Maui", hw_config); | ||
370 | |||
371 | hw_config->irq *= -1; | ||
372 | hw_config->name = "Maui"; | ||
373 | attach_mpu401(hw_config, THIS_MODULE); | ||
374 | |||
375 | if (hw_config->slots[1] != -1) /* The MPU401 driver installed itself */ { | ||
376 | struct synth_operations *synth; | ||
377 | |||
378 | this_dev = hw_config->slots[1]; | ||
379 | |||
380 | /* | ||
381 | * Intercept patch loading calls so that they can be handled | ||
382 | * by the Maui driver. | ||
383 | */ | ||
384 | |||
385 | synth = midi_devs[this_dev]->converter; | ||
386 | if (synth != NULL) { | ||
387 | synth->id = "MAUI"; | ||
388 | orig_load_patch = synth->load_patch; | ||
389 | synth->load_patch = &maui_load_patch; | ||
390 | } else | ||
391 | printk(KERN_ERR "Maui: Can't install patch loader\n"); | ||
392 | } | ||
393 | return 1; | ||
394 | |||
395 | out3: | ||
396 | free_irq(hw_config->irq, NULL); | ||
397 | out2: | ||
398 | release_region(hw_config->io_base + 2, 6); | ||
399 | out: | ||
400 | release_region(hw_config->io_base, 2); | ||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static void __exit unload_maui(struct address_info *hw_config) | ||
405 | { | ||
406 | int irq = hw_config->irq; | ||
407 | release_region(hw_config->io_base + 2, 6); | ||
408 | unload_mpu401(hw_config); | ||
409 | |||
410 | if (irq < 0) | ||
411 | irq = -irq; | ||
412 | if (irq > 0) | ||
413 | free_irq(irq, NULL); | ||
414 | } | ||
415 | |||
416 | static int fw_load; | ||
417 | |||
418 | static struct address_info cfg; | ||
419 | |||
420 | static int __initdata io = -1; | ||
421 | static int __initdata irq = -1; | ||
422 | |||
423 | module_param(io, int, 0); | ||
424 | module_param(irq, int, 0); | ||
425 | |||
426 | /* | ||
427 | * Install a Maui card. Needs mpu401 loaded already. | ||
428 | */ | ||
429 | |||
430 | static int __init init_maui(void) | ||
431 | { | ||
432 | printk(KERN_INFO "Turtle beach Maui and Tropez driver, Copyright (C) by Hannu Savolainen 1993-1996\n"); | ||
433 | |||
434 | cfg.io_base = io; | ||
435 | cfg.irq = irq; | ||
436 | |||
437 | if (cfg.io_base == -1 || cfg.irq == -1) { | ||
438 | printk(KERN_INFO "maui: irq and io must be set.\n"); | ||
439 | return -EINVAL; | ||
440 | } | ||
441 | |||
442 | if (maui_os == NULL) { | ||
443 | fw_load = 1; | ||
444 | maui_osLen = mod_firmware_load("/etc/sound/oswf.mot", (char **) &maui_os); | ||
445 | } | ||
446 | if (probe_maui(&cfg) == 0) | ||
447 | return -ENODEV; | ||
448 | |||
449 | return 0; | ||
450 | } | ||
451 | |||
452 | static void __exit cleanup_maui(void) | ||
453 | { | ||
454 | if (fw_load && maui_os) | ||
455 | vfree(maui_os); | ||
456 | unload_maui(&cfg); | ||
457 | } | ||
458 | |||
459 | module_init(init_maui); | ||
460 | module_exit(cleanup_maui); | ||
461 | |||
462 | #ifndef MODULE | ||
463 | static int __init setup_maui(char *str) | ||
464 | { | ||
465 | /* io, irq */ | ||
466 | int ints[3]; | ||
467 | |||
468 | str = get_options(str, ARRAY_SIZE(ints), ints); | ||
469 | |||
470 | io = ints[1]; | ||
471 | irq = ints[2]; | ||
472 | |||
473 | return 1; | ||
474 | } | ||
475 | |||
476 | __setup("maui=", setup_maui); | ||
477 | #endif | ||
478 | MODULE_LICENSE("GPL"); | ||