2 * Copyright (c) 1994-1998 Søren Schmidt
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
31 #include <sys/param.h>
32 #include <sys/systm.h>
35 #include <sys/kernel.h>
37 #include <sys/filio.h>
39 #include <sys/vnode.h>
41 #include <machine/clock.h>
42 #include <machine/pcaudioio.h>
44 #include <isa/isareg.h>
45 #include <isa/isavar.h>
46 #include <i386/isa/timerreg.h>
49 #define SAMPLE_RATE 8000
50 #define INTERRUPT_RATE 16000
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 */
71 static char buffer1[BUF_SIZE];
72 static char buffer2[BUF_SIZE];
73 static char buffer3[BUF_SIZE];
74 static char volume_table[256];
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,
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,
146 static int pca_sleep = 0;
147 static int pca_initialized = 0;
149 static void pcaintr(struct clockframe *frame);
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;
157 #define CDEV_MAJOR 24
158 static struct cdevsw pca_cdevsw = {
160 /* close */ pcaclose,
162 /* write */ pcawrite,
163 /* ioctl */ pcaioctl,
166 /* strategy */ nostrategy,
168 /* maj */ CDEV_MAJOR,
174 static void pca_continue __P((void));
175 static void pca_init __P((void));
176 static void pca_pause __P((void));
179 conv(const unsigned char *table, unsigned char *buff, unsigned n)
183 for (i = 0; i < n; i++)
184 buff[i] = table[buff[i]];
189 pca_volume(int volume)
193 for (i=0; i<256; i++) {
194 j = ((i-128)*volume)/25;
196 j = ((i-128)*volume)/100;
202 volume_table[i] = (((255-(j + 128))/4)+1);
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;
224 pca_volume(pca_status.volume);
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))
243 else if (acquire_timer0(INTERRUPT_RATE, pcaintr)) {
247 pca_status.timer_on = 1;
259 /* release the timers */
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;
280 pca_status.timer_on = 0;
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;
303 if (!pca_status.timer_on)
306 while (pca_status.in_use[0] || pca_status.in_use[1] ||
307 pca_status.in_use[2]) {
310 error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_drain", 0);
313 if (error != 0 && error != ERESTART) {
322 static struct isa_pnp_id pca_ids[] = {
323 {0x0008d041, "AT-style speaker sound"}, /* PNP0800 */
328 pcaprobe(device_t dev)
332 /* Check isapnp ids */
333 error = ISA_PNP_PROBE(device_get_parent(dev), dev, pca_ids);
341 pcaattach(device_t dev)
344 make_dev(&pca_cdevsw, 0, 0, 0, 0600, "pcaudio");
345 make_dev(&pca_cdevsw, 128, 0, 0, 0600, "pcaudioctl");
349 static device_method_t pca_methods[] = {
350 DEVMETHOD(device_probe, pcaprobe),
351 DEVMETHOD(device_attach, pcaattach),
355 static driver_t pca_driver = {
361 static devclass_t pca_devclass;
363 DRIVER_MODULE(pca, isa, pca_driver, pca_devclass, 0, 0);
367 pcaopen(dev_t dev, int flags, int fmt, struct proc *p)
369 /* audioctl device can always be opened */
370 if (minor(dev) == 128)
375 if (!pca_initialized) {
380 /* audio device can only be open by one process */
381 if (pca_status.open) {
382 pca_status.queries = 1;
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;
389 pca_status.processed = 0;
395 pcaclose(dev_t dev, int flags, int fmt, struct proc *p)
397 /* audioctl device can always be closed */
398 if (minor(dev) == 128)
402 /* audio device close drains all output and restores timers */
411 pcawrite(dev_t dev, struct uio *uio, int flag)
413 int count, error, which, x;
415 /* only audio device can be written */
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)
426 error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_wait", 0);
429 if (error != 0 && error != ERESTART) {
434 if (!pca_status.in_use[0])
436 else if (!pca_status.in_use[1])
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);
448 case AUDIO_ENCODING_ALAW:
449 conv(alaw_linear, pca_status.buf[which], count);
452 case AUDIO_ENCODING_RAW:
455 pca_status.in_use[which] = count;
456 if (!pca_status.timer_on)
466 pcaioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
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;
479 auptr->play.gain = pca_status.volume;
480 auptr->play.port = 0;
482 auptr->play.samples = pca_status.processed;
484 auptr->play.pause = !pca_status.timer_on;
485 auptr->play.error = 0;
486 auptr->play.waiting = pca_status.queries;
488 auptr->play.open = pca_status.open;
489 auptr->play.active = pca_status.timer_on;
493 auptr = (audio_info_t *)data;
494 if (auptr->play.sample_rate != (unsigned int)~0) {
495 pca_status.sample_rate = auptr->play.sample_rate;
497 (pca_status.sample_rate << 8) / INTERRUPT_RATE;
499 if (auptr->play.encoding != (unsigned int)~0) {
500 pca_status.encoding = auptr->play.encoding;
502 if (auptr->play.gain != (unsigned int)~0) {
503 pca_status.volume = auptr->play.gain;
504 pca_volume(pca_status.volume);
506 if (auptr->play.pause != (unsigned char)~0) {
507 if (auptr->play.pause)
516 case AUDIO_COMPAT_DRAIN:
520 case AUDIO_COMPAT_FLUSH:
531 pcaintr(struct clockframe *frame)
533 if (pca_status.index < pca_status.in_use[pca_status.current]) {
535 __asm__("outb %0,$0x61\n"
538 : : "a" ((char)pca_status.oldval) );
541 : : "a" ((char)pca_status.buffer[pca_status.index]),
542 "b" (volume_table) );
544 pca_status.counter += pca_status.scale;
545 pca_status.index = (pca_status.counter >> 8);
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];
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;
566 pcapoll(dev_t dev, int events, struct proc *p)
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);
578 selrecord(p, &pca_status.wsel);