42b925c9196de454421f45f77eed1cdc9cc0d49d
[dragonfly.git] / sys / dev / sound / isa / i386 / pca / pcaudio.c
1 /*-
2  * Copyright (c) 1994-1998 Søren Schmidt
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification, immediately at the beginning of the file.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * $FreeBSD: src/sys/i386/isa/pcaudio.c,v 1.58 2000/01/25 21:58:43 dfr Exp $
29  * $DragonFly: src/sys/dev/sound/isa/i386/pca/Attic/pcaudio.c,v 1.2 2003/06/17 04:28:37 dillon Exp $
30  */
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/conf.h>
35 #include <sys/proc.h>
36 #include <sys/kernel.h>
37 #include <sys/bus.h>
38 #include <sys/filio.h>
39 #include <sys/poll.h>
40 #include <sys/vnode.h>
41
42 #include <machine/clock.h>
43 #include <machine/pcaudioio.h>
44
45 #include <isa/isareg.h>
46 #include <isa/isavar.h>
47 #include <i386/isa/timerreg.h>
48
49 #define BUF_SIZE        8192
50 #define SAMPLE_RATE     8000
51 #define INTERRUPT_RATE  16000
52
53 static struct pca_status {
54         char            open;           /* device open */
55         char            queries;        /* did others try opening */
56         unsigned char   *buf[3];        /* triple buffering */
57         unsigned char   *buffer;        /* current buffer ptr */
58         unsigned        in_use[3];      /* buffers fill */
59         unsigned        index;          /* index in current buffer */
60         unsigned        counter;        /* sample counter */
61         unsigned        scale;          /* sample counter scale */
62         unsigned        sample_rate;    /* sample rate */
63         unsigned        processed;      /* samples processed */
64         unsigned        volume;         /* volume for pc-speaker */
65         char            encoding;       /* Ulaw, Alaw or linear */
66         u_char          current;        /* current buffer */
67         unsigned char   oldval;         /* old timer port value */
68         char            timer_on;       /* is playback running */
69         struct selinfo  wsel;           /* select/poll status */
70 } pca_status;
71
72 static char buffer1[BUF_SIZE];
73 static char buffer2[BUF_SIZE];
74 static char buffer3[BUF_SIZE];
75 static char volume_table[256];
76
77 static unsigned char ulaw_dsp[] = {
78      3,    7,   11,   15,   19,   23,   27,   31,
79     35,   39,   43,   47,   51,   55,   59,   63,
80     66,   68,   70,   72,   74,   76,   78,   80,
81     82,   84,   86,   88,   90,   92,   94,   96,
82     98,   99,  100,  101,  102,  103,  104,  105,
83    106,  107,  108,  109,  110,  111,  112,  113,
84    113,  114,  114,  115,  115,  116,  116,  117,
85    117,  118,  118,  119,  119,  120,  120,  121,
86    121,  121,  122,  122,  122,  122,  123,  123,
87    123,  123,  124,  124,  124,  124,  125,  125,
88    125,  125,  125,  125,  126,  126,  126,  126,
89    126,  126,  126,  126,  127,  127,  127,  127,
90    127,  127,  127,  127,  127,  127,  127,  127,
91    128,  128,  128,  128,  128,  128,  128,  128,
92    128,  128,  128,  128,  128,  128,  128,  128,
93    128,  128,  128,  128,  128,  128,  128,  128,
94    253,  249,  245,  241,  237,  233,  229,  225,
95    221,  217,  213,  209,  205,  201,  197,  193,
96    190,  188,  186,  184,  182,  180,  178,  176,
97    174,  172,  170,  168,  166,  164,  162,  160,
98    158,  157,  156,  155,  154,  153,  152,  151,
99    150,  149,  148,  147,  146,  145,  144,  143,
100    143,  142,  142,  141,  141,  140,  140,  139,
101    139,  138,  138,  137,  137,  136,  136,  135,
102    135,  135,  134,  134,  134,  134,  133,  133,
103    133,  133,  132,  132,  132,  132,  131,  131,
104    131,  131,  131,  131,  130,  130,  130,  130,
105    130,  130,  130,  130,  129,  129,  129,  129,
106    129,  129,  129,  129,  129,  129,  129,  129,
107    128,  128,  128,  128,  128,  128,  128,  128,
108    128,  128,  128,  128,  128,  128,  128,  128,
109    128,  128,  128,  128,  128,  128,  128,  128,
110 };
111
112 static unsigned char alaw_linear[] = {
113         45,     214,    122,    133,    0,              255,    107,    149, 
114         86,     171,    126,    129,    0,              255,    117,    138, 
115         13,     246,    120,    135,    0,              255,    99,     157, 
116         70,     187,    124,    131,    0,              255,    113,    142, 
117         61,     198,    123,    132,    0,              255,    111,    145, 
118         94,     163,    127,    128,    0,              255,    119,    136, 
119         29,     230,    121,    134,    0,              255,    103,    153, 
120         78,     179,    125,    130,    0,              255,    115,    140, 
121         37,     222,    122,    133,    0,              255,    105,    151, 
122         82,     175,    126,    129,    0,              255,    116,    139, 
123         5,      254,    120,    135,    0,              255,    97,     159, 
124         66,     191,    124,    131,    0,              255,    112,    143, 
125         53,     206,    123,    132,    0,              255,    109,    147, 
126         90,     167,    127,    128,    0,              255,    118,    137, 
127         21,     238,    121,    134,    0,              255,    101,    155, 
128         74,     183,    125,    130,    0,              255,    114,    141, 
129         49,     210,    123,    133,    0,              255,    108,    148, 
130         88,     169,    127,    129,    0,              255,    118,    138, 
131         17,     242,    121,    135,    0,              255,    100,    156, 
132         72,     185,    125,    131,    0,              255,    114,    142, 
133         64,     194,    124,    132,    0,              255,    112,    144, 
134         96,     161,    128,    128,    1,              255,    120,    136, 
135         33,     226,    122,    134,    0,              255,    104,    152, 
136         80,     177,    126,    130,    0,              255,    116,    140, 
137         41,     218,    122,    133,    0,              255,    106,    150, 
138         84,     173,    126,    129,    0,              255,    117,    139, 
139         9,      250,    120,    135,    0,              255,    98,     158, 
140         68,     189,    124,    131,    0,              255,    113,    143, 
141         57,     202,    123,    132,    0,              255,    110,    146, 
142         92,     165,    127,    128,    0,              255,    119,    137, 
143         25,     234,    121,    134,    0,              255,    102,    154, 
144         76,     181,    125,    130,    0,              255,    115,    141, 
145 };
146
147 static int pca_sleep = 0;
148 static int pca_initialized = 0;
149
150 static void pcaintr(struct clockframe *frame);
151
152 static  d_open_t        pcaopen;
153 static  d_close_t       pcaclose;
154 static  d_write_t       pcawrite;
155 static  d_ioctl_t       pcaioctl;
156 static  d_poll_t        pcapoll;
157
158 #define CDEV_MAJOR 24
159 static struct cdevsw pca_cdevsw = {
160         /* open */      pcaopen,
161         /* close */     pcaclose,
162         /* read */      noread,
163         /* write */     pcawrite,
164         /* ioctl */     pcaioctl,
165         /* poll */      pcapoll,
166         /* mmap */      nommap,
167         /* strategy */  nostrategy,
168         /* name */      "pca",
169         /* maj */       CDEV_MAJOR,
170         /* dump */      nodump,
171         /* psize */     nopsize,
172         /* flags */     0,
173         /* bmaj */      -1
174 };
175
176 static void pca_continue __P((void));
177 static void pca_init __P((void));
178 static void pca_pause __P((void));
179
180 static void
181 conv(const unsigned char *table, unsigned char *buff, unsigned n)
182 {
183         unsigned i;
184
185         for (i = 0; i < n; i++)
186                 buff[i] = table[buff[i]];
187 }
188
189
190 static void
191 pca_volume(int volume)
192 {
193         int i, j;
194
195         for (i=0; i<256; i++) {
196                 j = ((i-128)*volume)/25;
197 /* XXX
198                 j = ((i-128)*volume)/100;
199 */
200                 if (j<-128)
201                         j = -128;
202                 if (j>127)
203                         j = 127;
204                 volume_table[i] = (((255-(j + 128))/4)+1);
205         }
206 }
207
208
209 static void
210 pca_init(void)
211 {
212         pca_status.open = 0;
213         pca_status.queries = 0;
214         pca_status.timer_on = 0;
215         pca_status.buf[0] = (unsigned char *)&buffer1[0];
216         pca_status.buf[1] = (unsigned char *)&buffer2[0];
217         pca_status.buf[2] = (unsigned char *)&buffer3[0];
218         pca_status.buffer = pca_status.buf[0];
219         pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
220         pca_status.current = 0;
221         pca_status.sample_rate = SAMPLE_RATE;
222         pca_status.scale = (pca_status.sample_rate << 8) / INTERRUPT_RATE;
223         pca_status.encoding = AUDIO_ENCODING_ULAW;
224         pca_status.volume = 100;
225
226         pca_volume(pca_status.volume);
227 }
228
229
230 static int
231 pca_start(void)
232 {
233         int x = splhigh();
234         int rv = 0;
235
236         /* use the first buffer */
237         pca_status.current  = 0;
238         pca_status.index = 0;
239         pca_status.counter = 0;
240         pca_status.buffer  = pca_status.buf[pca_status.current];
241         pca_status.oldval = inb(IO_PPI) | 0x03;
242         /* acquire the timers */
243         if (acquire_timer2(TIMER_LSB|TIMER_ONESHOT))
244                 rv = -1;
245         else if (acquire_timer0(INTERRUPT_RATE, pcaintr)) {
246                 release_timer2();
247                 rv =  -1;
248         } else
249                 pca_status.timer_on = 1;
250
251         splx(x);
252         return rv;
253 }
254
255
256 static void
257 pca_stop(void)
258 {
259         int x = splhigh();
260
261         /* release the timers */
262         release_timer0();
263         release_timer2();
264         /* reset the buffer */
265         pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
266         pca_status.index = 0;
267         pca_status.counter = 0;
268         pca_status.current = 0;
269         pca_status.buffer = pca_status.buf[pca_status.current];
270         pca_status.timer_on = 0;
271         splx(x);
272 }
273
274
275 static void
276 pca_pause(void)
277 {
278         int x = splhigh();
279
280         release_timer0();
281         release_timer2();
282         pca_status.timer_on = 0;
283         splx(x);
284 }
285
286
287 static void
288 pca_continue(void)
289 {
290         int x = splhigh();
291
292         pca_status.oldval = inb(IO_PPI) | 0x03;
293         acquire_timer2(TIMER_LSB|TIMER_ONESHOT);
294         acquire_timer0(INTERRUPT_RATE, pcaintr);
295         pca_status.timer_on = 1;
296         splx(x);
297 }
298
299
300 static int
301 pca_wait(void)
302 {
303         int error, x;
304
305         if (!pca_status.timer_on)
306                 return 0;
307
308         while (pca_status.in_use[0] || pca_status.in_use[1] ||
309             pca_status.in_use[2]) {
310                 x = spltty();
311                 pca_sleep = 1;
312                 error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_drain", 0);
313                 pca_sleep = 0;
314                 splx(x);
315                 if (error != 0 && error != ERESTART) {
316                         pca_stop();
317                         return error;
318                 }
319         }
320         return 0;
321 }
322
323
324 static struct isa_pnp_id pca_ids[] = {
325         {0x0008d041, "AT-style speaker sound"}, /* PNP0800 */
326         {0}
327 };
328
329 static int
330 pcaprobe(device_t dev)
331 {
332         int error;
333
334         /* Check isapnp ids */
335         error = ISA_PNP_PROBE(device_get_parent(dev), dev, pca_ids);
336         if (error == ENXIO)
337                 return ENXIO;
338         return 0;
339 }
340
341
342 static int
343 pcaattach(device_t dev)
344 {
345         pca_init();
346         make_dev(&pca_cdevsw, 0, 0, 0, 0600, "pcaudio");
347         make_dev(&pca_cdevsw, 128, 0, 0, 0600, "pcaudioctl");
348         return 0;
349 }
350
351 static device_method_t pca_methods[] = {
352         DEVMETHOD(device_probe,         pcaprobe),
353         DEVMETHOD(device_attach,        pcaattach),
354         { 0, 0 }
355 };
356
357 static driver_t pca_driver = {
358         "pca",
359         pca_methods,
360         1
361 };
362
363 static devclass_t pca_devclass;
364
365 DRIVER_MODULE(pca, isa, pca_driver, pca_devclass, 0, 0);
366
367
368 static int
369 pcaopen(dev_t dev, int flags, int fmt, struct proc *p)
370 {
371         /* audioctl device can always be opened */
372         if (minor(dev) == 128)
373                 return 0;
374         if (minor(dev) > 0)
375                 return ENXIO;
376
377         if (!pca_initialized) {
378                 pca_init();
379                 pca_initialized = 1;
380         }
381
382         /* audio device can only be open by one process */
383         if (pca_status.open) {
384                 pca_status.queries = 1;
385                 return EBUSY;
386         }
387         pca_status.buffer = pca_status.buf[0];
388         pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
389         pca_status.timer_on = 0;
390         pca_status.open = 1;
391         pca_status.processed = 0;
392         return 0;
393 }
394
395
396 static int
397 pcaclose(dev_t dev, int flags, int fmt, struct proc *p)
398 {
399         /* audioctl device can always be closed */
400         if (minor(dev) == 128)
401                 return 0;
402         if (minor(dev) > 0)
403                 return ENXIO;
404         /* audio device close drains all output and restores timers */
405         pca_wait();
406         pca_stop();
407         pca_status.open = 0;
408         return 0;
409 }
410
411
412 static int
413 pcawrite(dev_t dev, struct uio *uio, int flag)
414 {
415         int count, error, which, x;
416
417         /* only audio device can be written */
418         if (minor(dev) > 0)
419                 return ENXIO;
420
421         while ((count = min(BUF_SIZE, uio->uio_resid)) > 0) {
422                 if (pca_status.in_use[0] && pca_status.in_use[1] &&
423                     pca_status.in_use[2]) {
424                         if (flag & IO_NDELAY)
425                                 return EWOULDBLOCK;
426                         x = spltty();
427                         pca_sleep = 1;
428                         error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_wait", 0);
429                         pca_sleep = 0;
430                         splx(x);
431                         if (error != 0 && error != ERESTART) {
432                                 pca_stop();
433                                 return error;
434                         }
435                 }
436                 if (!pca_status.in_use[0])
437                         which = 0;
438                 else if (!pca_status.in_use[1])
439                         which = 1;
440                 else
441                         which = 2;
442                 if (count && !pca_status.in_use[which]) {
443                         uiomove(pca_status.buf[which], count, uio);
444                         pca_status.processed += count;
445                         switch (pca_status.encoding) {
446                         case AUDIO_ENCODING_ULAW:
447                                 conv(ulaw_dsp, pca_status.buf[which], count);
448                                 break;
449
450                         case AUDIO_ENCODING_ALAW:
451                                 conv(alaw_linear, pca_status.buf[which], count);
452                                 break;
453
454                         case AUDIO_ENCODING_RAW:
455                                 break;
456                         }
457                         pca_status.in_use[which] = count;
458                         if (!pca_status.timer_on)
459                                 if (pca_start())
460                                         return EBUSY;
461                 }
462         }
463         return 0;
464 }
465
466
467 static int
468 pcaioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
469 {
470         audio_info_t *auptr;
471
472         switch(cmd) {
473
474         case AUDIO_GETINFO:
475                 auptr = (audio_info_t *)data;
476                 auptr->play.sample_rate = pca_status.sample_rate;
477                 auptr->play.channels = 1;
478                 auptr->play.precision = 8;
479                 auptr->play.encoding = pca_status.encoding;
480
481                 auptr->play.gain = pca_status.volume;
482                 auptr->play.port = 0;
483
484                 auptr->play.samples = pca_status.processed;
485                 auptr->play.eof = 0;
486                 auptr->play.pause = !pca_status.timer_on;
487                 auptr->play.error = 0;
488                 auptr->play.waiting = pca_status.queries;
489
490                 auptr->play.open = pca_status.open;
491                 auptr->play.active = pca_status.timer_on;
492                 return 0;
493
494         case AUDIO_SETINFO:
495                 auptr = (audio_info_t *)data;
496                 if (auptr->play.sample_rate != (unsigned int)~0) {
497                         pca_status.sample_rate = auptr->play.sample_rate;
498                         pca_status.scale =
499                                 (pca_status.sample_rate << 8) / INTERRUPT_RATE;
500                 }
501                 if (auptr->play.encoding != (unsigned int)~0) {
502                         pca_status.encoding = auptr->play.encoding;
503                 }
504                 if (auptr->play.gain != (unsigned int)~0) {
505                         pca_status.volume = auptr->play.gain;
506                         pca_volume(pca_status.volume);
507                 }
508                 if (auptr->play.pause != (unsigned char)~0) {
509                         if (auptr->play.pause)
510                                 pca_pause();
511                         else
512                                 pca_continue();
513                 }
514
515                 return 0;
516
517         case AUDIO_DRAIN:
518         case AUDIO_COMPAT_DRAIN:
519                 return pca_wait();
520
521         case AUDIO_FLUSH:
522         case AUDIO_COMPAT_FLUSH:
523                 pca_stop();
524                 return 0;
525         case FIONBIO:
526                 return 0;
527         }
528         return ENXIO;
529 }
530
531
532 static void
533 pcaintr(struct clockframe *frame)
534 {
535         if (pca_status.index < pca_status.in_use[pca_status.current]) {
536                 disable_intr();
537                 __asm__("outb %0,$0x61\n"
538                         "andb $0xFE,%0\n"
539                         "outb %0,$0x61"
540                         : : "a" ((char)pca_status.oldval) );
541                 __asm__("xlatb\n"
542                         "outb %0,$0x42"
543                         : : "a" ((char)pca_status.buffer[pca_status.index]),
544                             "b" (volume_table) );
545                 enable_intr();
546                 pca_status.counter += pca_status.scale;
547                 pca_status.index = (pca_status.counter >> 8);
548         }
549         if (pca_status.index >= pca_status.in_use[pca_status.current]) {
550                 pca_status.index = pca_status.counter = 0;
551                 pca_status.in_use[pca_status.current] = 0;
552                 pca_status.current++;
553                 if (pca_status.current > 2)
554                         pca_status.current = 0;
555                 pca_status.buffer = pca_status.buf[pca_status.current];
556                 if (pca_sleep)
557                         wakeup(&pca_sleep);
558                 if (pca_status.wsel.si_pid) {
559                         selwakeup((struct selinfo *)&pca_status.wsel.si_pid);
560                         pca_status.wsel.si_pid = 0;
561                         pca_status.wsel.si_flags = 0;
562                 }
563         }
564 }
565
566
567 static int
568 pcapoll(dev_t dev, int events, struct proc *p)
569 {
570         int s;
571         struct proc *p1;
572         int revents = 0;
573
574         s = spltty();
575
576         if (events & (POLLOUT | POLLWRNORM)) {
577                 if (!pca_status.in_use[0] || !pca_status.in_use[1] ||
578                     !pca_status.in_use[2])
579                         revents |= events & (POLLOUT | POLLWRNORM);
580                 else {
581                         if (pca_status.wsel.si_pid &&
582                             (p1=pfind(pca_status.wsel.si_pid))
583                             && p1->p_wchan == (caddr_t)&selwait)
584                                 pca_status.wsel.si_flags = SI_COLL;
585                         else
586                                 pca_status.wsel.si_pid = p->p_pid;
587                 }
588         }
589         splx(s);
590         return (revents);
591 }