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