Linux-2.6.12-rc2
[linux-2.6] / sound / oss / msnd.c
1 /*********************************************************************
2  *
3  * msnd.c - Driver Base
4  *
5  * Turtle Beach MultiSound Sound Card Driver for Linux
6  *
7  * Copyright (C) 1998 Andrew Veliath
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * $Id: msnd.c,v 1.17 1999/03/21 16:50:09 andrewtv Exp $
24  *
25  ********************************************************************/
26
27 #include <linux/version.h>
28 #include <linux/module.h>
29 #include <linux/kernel.h>
30 #include <linux/slab.h>
31 #include <linux/vmalloc.h>
32 #include <linux/types.h>
33 #include <linux/delay.h>
34 #include <linux/mm.h>
35 #include <linux/init.h>
36 #include <linux/interrupt.h>
37
38 #include <asm/io.h>
39 #include <asm/uaccess.h>
40 #include <linux/spinlock.h>
41 #include <asm/irq.h>
42 #include "msnd.h"
43
44 #define LOGNAME                 "msnd"
45
46 #define MSND_MAX_DEVS           4
47
48 static multisound_dev_t         *devs[MSND_MAX_DEVS];
49 static int                      num_devs;
50
51 int __init msnd_register(multisound_dev_t *dev)
52 {
53         int i;
54
55         for (i = 0; i < MSND_MAX_DEVS; ++i)
56                 if (devs[i] == NULL)
57                         break;
58
59         if (i == MSND_MAX_DEVS)
60                 return -ENOMEM;
61
62         devs[i] = dev;
63         ++num_devs;
64         return 0;
65 }
66
67 void msnd_unregister(multisound_dev_t *dev)
68 {
69         int i;
70
71         for (i = 0; i < MSND_MAX_DEVS; ++i)
72                 if (devs[i] == dev)
73                         break;
74
75         if (i == MSND_MAX_DEVS) {
76                 printk(KERN_WARNING LOGNAME ": Unregistering unknown device\n");
77                 return;
78         }
79
80         devs[i] = NULL;
81         --num_devs;
82 }
83
84 void msnd_init_queue(void __iomem *base, int start, int size)
85 {
86         writew(PCTODSP_BASED(start), base + JQS_wStart);
87         writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize);
88         writew(0, base + JQS_wHead);
89         writew(0, base + JQS_wTail);
90 }
91
92 void msnd_fifo_init(msnd_fifo *f)
93 {
94         f->data = NULL;
95 }
96
97 void msnd_fifo_free(msnd_fifo *f)
98 {
99         if (f->data) {
100                 vfree(f->data);
101                 f->data = NULL;
102         }
103 }
104
105 int msnd_fifo_alloc(msnd_fifo *f, size_t n)
106 {
107         msnd_fifo_free(f);
108         f->data = (char *)vmalloc(n);
109         f->n = n;
110         f->tail = 0;
111         f->head = 0;
112         f->len = 0;
113
114         if (!f->data)
115                 return -ENOMEM;
116
117         return 0;
118 }
119
120 void msnd_fifo_make_empty(msnd_fifo *f)
121 {
122         f->len = f->tail = f->head = 0;
123 }
124
125 int msnd_fifo_write_io(msnd_fifo *f, char __iomem *buf, size_t len)
126 {
127         int count = 0;
128
129         while ((count < len) && (f->len != f->n)) {
130
131                 int nwritten;
132
133                 if (f->head <= f->tail) {
134                         nwritten = len - count;
135                         if (nwritten > f->n - f->tail)
136                                 nwritten = f->n - f->tail;
137                 }
138                 else {
139                         nwritten = f->head - f->tail;
140                         if (nwritten > len - count)
141                                 nwritten = len - count;
142                 }
143
144                 memcpy_fromio(f->data + f->tail, buf, nwritten);
145
146                 count += nwritten;
147                 buf += nwritten;
148                 f->len += nwritten;
149                 f->tail += nwritten;
150                 f->tail %= f->n;
151         }
152
153         return count;
154 }
155
156 int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len)
157 {
158         int count = 0;
159
160         while ((count < len) && (f->len != f->n)) {
161
162                 int nwritten;
163
164                 if (f->head <= f->tail) {
165                         nwritten = len - count;
166                         if (nwritten > f->n - f->tail)
167                                 nwritten = f->n - f->tail;
168                 }
169                 else {
170                         nwritten = f->head - f->tail;
171                         if (nwritten > len - count)
172                                 nwritten = len - count;
173                 }
174
175                 memcpy(f->data + f->tail, buf, nwritten);
176
177                 count += nwritten;
178                 buf += nwritten;
179                 f->len += nwritten;
180                 f->tail += nwritten;
181                 f->tail %= f->n;
182         }
183
184         return count;
185 }
186
187 int msnd_fifo_read_io(msnd_fifo *f, char __iomem *buf, size_t len)
188 {
189         int count = 0;
190
191         while ((count < len) && (f->len > 0)) {
192
193                 int nread;
194
195                 if (f->tail <= f->head) {
196                         nread = len - count;
197                         if (nread > f->n - f->head)
198                                 nread = f->n - f->head;
199                 }
200                 else {
201                         nread = f->tail - f->head;
202                         if (nread > len - count)
203                                 nread = len - count;
204                 }
205
206                 memcpy_toio(buf, f->data + f->head, nread);
207
208                 count += nread;
209                 buf += nread;
210                 f->len -= nread;
211                 f->head += nread;
212                 f->head %= f->n;
213         }
214
215         return count;
216 }
217
218 int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len)
219 {
220         int count = 0;
221
222         while ((count < len) && (f->len > 0)) {
223
224                 int nread;
225
226                 if (f->tail <= f->head) {
227                         nread = len - count;
228                         if (nread > f->n - f->head)
229                                 nread = f->n - f->head;
230                 }
231                 else {
232                         nread = f->tail - f->head;
233                         if (nread > len - count)
234                                 nread = len - count;
235                 }
236
237                 memcpy(buf, f->data + f->head, nread);
238
239                 count += nread;
240                 buf += nread;
241                 f->len -= nread;
242                 f->head += nread;
243                 f->head %= f->n;
244         }
245
246         return count;
247 }
248
249 static int msnd_wait_TXDE(multisound_dev_t *dev)
250 {
251         register unsigned int io = dev->io;
252         register int timeout = 1000;
253     
254         while(timeout-- > 0)
255                 if (msnd_inb(io + HP_ISR) & HPISR_TXDE)
256                         return 0;
257
258         return -EIO;
259 }
260
261 static int msnd_wait_HC0(multisound_dev_t *dev)
262 {
263         register unsigned int io = dev->io;
264         register int timeout = 1000;
265
266         while(timeout-- > 0)
267                 if (!(msnd_inb(io + HP_CVR) & HPCVR_HC))
268                         return 0;
269
270         return -EIO;
271 }
272
273 int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd)
274 {
275         unsigned long flags;
276
277         spin_lock_irqsave(&dev->lock, flags);
278         if (msnd_wait_HC0(dev) == 0) {
279                 msnd_outb(cmd, dev->io + HP_CVR);
280                 spin_unlock_irqrestore(&dev->lock, flags);
281                 return 0;
282         }
283         spin_unlock_irqrestore(&dev->lock, flags);
284
285         printk(KERN_DEBUG LOGNAME ": Send DSP command timeout\n");
286
287         return -EIO;
288 }
289
290 int msnd_send_word(multisound_dev_t *dev, unsigned char high,
291                    unsigned char mid, unsigned char low)
292 {
293         register unsigned int io = dev->io;
294
295         if (msnd_wait_TXDE(dev) == 0) {
296                 msnd_outb(high, io + HP_TXH);
297                 msnd_outb(mid, io + HP_TXM);
298                 msnd_outb(low, io + HP_TXL);
299                 return 0;
300         }
301
302         printk(KERN_DEBUG LOGNAME ": Send host word timeout\n");
303
304         return -EIO;
305 }
306
307 int msnd_upload_host(multisound_dev_t *dev, char *bin, int len)
308 {
309         int i;
310
311         if (len % 3 != 0) {
312                 printk(KERN_WARNING LOGNAME ": Upload host data not multiple of 3!\n");         
313                 return -EINVAL;
314         }
315
316         for (i = 0; i < len; i += 3)
317                 if (msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]) != 0)
318                         return -EIO;
319
320         msnd_inb(dev->io + HP_RXL);
321         msnd_inb(dev->io + HP_CVR);
322
323         return 0;
324 }
325
326 int msnd_enable_irq(multisound_dev_t *dev)
327 {
328         unsigned long flags;
329
330         if (dev->irq_ref++)
331                 return 0;
332
333         printk(KERN_DEBUG LOGNAME ": Enabling IRQ\n");
334
335         spin_lock_irqsave(&dev->lock, flags);
336         if (msnd_wait_TXDE(dev) == 0) {
337                 msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR);
338                 if (dev->type == msndClassic)
339                         msnd_outb(dev->irqid, dev->io + HP_IRQM);
340                 msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR);
341                 msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR);
342                 enable_irq(dev->irq);
343                 msnd_init_queue(dev->DSPQ, dev->dspq_data_buff, dev->dspq_buff_size);
344                 spin_unlock_irqrestore(&dev->lock, flags);
345                 return 0;
346         }
347         spin_unlock_irqrestore(&dev->lock, flags);
348
349         printk(KERN_DEBUG LOGNAME ": Enable IRQ failed\n");
350
351         return -EIO;
352 }
353
354 int msnd_disable_irq(multisound_dev_t *dev)
355 {
356         unsigned long flags;
357
358         if (--dev->irq_ref > 0)
359                 return 0;
360
361         if (dev->irq_ref < 0)
362                 printk(KERN_DEBUG LOGNAME ": IRQ ref count is %d\n", dev->irq_ref);
363
364         printk(KERN_DEBUG LOGNAME ": Disabling IRQ\n");
365
366         spin_lock_irqsave(&dev->lock, flags);
367         if (msnd_wait_TXDE(dev) == 0) {
368                 msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR);
369                 if (dev->type == msndClassic)
370                         msnd_outb(HPIRQ_NONE, dev->io + HP_IRQM);
371                 disable_irq(dev->irq);
372                 spin_unlock_irqrestore(&dev->lock, flags);
373                 return 0;
374         }
375         spin_unlock_irqrestore(&dev->lock, flags);
376
377         printk(KERN_DEBUG LOGNAME ": Disable IRQ failed\n");
378
379         return -EIO;
380 }
381
382 #ifndef LINUX20
383 EXPORT_SYMBOL(msnd_register);
384 EXPORT_SYMBOL(msnd_unregister);
385
386 EXPORT_SYMBOL(msnd_init_queue);
387
388 EXPORT_SYMBOL(msnd_fifo_init);
389 EXPORT_SYMBOL(msnd_fifo_free);
390 EXPORT_SYMBOL(msnd_fifo_alloc);
391 EXPORT_SYMBOL(msnd_fifo_make_empty);
392 EXPORT_SYMBOL(msnd_fifo_write_io);
393 EXPORT_SYMBOL(msnd_fifo_read_io);
394 EXPORT_SYMBOL(msnd_fifo_write);
395 EXPORT_SYMBOL(msnd_fifo_read);
396
397 EXPORT_SYMBOL(msnd_send_dsp_cmd);
398 EXPORT_SYMBOL(msnd_send_word);
399 EXPORT_SYMBOL(msnd_upload_host);
400
401 EXPORT_SYMBOL(msnd_enable_irq);
402 EXPORT_SYMBOL(msnd_disable_irq);
403 #endif
404
405 #ifdef MODULE
406 MODULE_AUTHOR                           ("Andrew Veliath <andrewtv@usa.net>");
407 MODULE_DESCRIPTION                      ("Turtle Beach MultiSound Driver Base");
408 MODULE_LICENSE("GPL");
409
410
411 int init_module(void)
412 {
413         return 0;
414 }
415
416 void cleanup_module(void)
417 {
418 }
419 #endif