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,
175 static void pca_continue __P((void));
176 static void pca_init __P((void));
177 static void pca_pause __P((void));
180 conv(const unsigned char *table, unsigned char *buff, unsigned n)
184 for (i = 0; i < n; i++)
185 buff[i] = table[buff[i]];
190 pca_volume(int volume)
194 for (i=0; i<256; i++) {
195 j = ((i-128)*volume)/25;
197 j = ((i-128)*volume)/100;
203 volume_table[i] = (((255-(j + 128))/4)+1);
212 pca_status.queries = 0;
213 pca_status.timer_on = 0;
214 pca_status.buf[0] = (unsigned char *)&buffer1[0];
215 pca_status.buf[1] = (unsigned char *)&buffer2[0];
216 pca_status.buf[2] = (unsigned char *)&buffer3[0];
217 pca_status.buffer = pca_status.buf[0];
218 pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
219 pca_status.current = 0;
220 pca_status.sample_rate = SAMPLE_RATE;
221 pca_status.scale = (pca_status.sample_rate << 8) / INTERRUPT_RATE;
222 pca_status.encoding = AUDIO_ENCODING_ULAW;
223 pca_status.volume = 100;
225 pca_volume(pca_status.volume);
235 /* use the first buffer */
236 pca_status.current = 0;
237 pca_status.index = 0;
238 pca_status.counter = 0;
239 pca_status.buffer = pca_status.buf[pca_status.current];
240 pca_status.oldval = inb(IO_PPI) | 0x03;
241 /* acquire the timers */
242 if (acquire_timer2(TIMER_LSB|TIMER_ONESHOT))
244 else if (acquire_timer0(INTERRUPT_RATE, pcaintr)) {
248 pca_status.timer_on = 1;
260 /* release the timers */
263 /* reset the buffer */
264 pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
265 pca_status.index = 0;
266 pca_status.counter = 0;
267 pca_status.current = 0;
268 pca_status.buffer = pca_status.buf[pca_status.current];
269 pca_status.timer_on = 0;
281 pca_status.timer_on = 0;
291 pca_status.oldval = inb(IO_PPI) | 0x03;
292 acquire_timer2(TIMER_LSB|TIMER_ONESHOT);
293 acquire_timer0(INTERRUPT_RATE, pcaintr);
294 pca_status.timer_on = 1;
304 if (!pca_status.timer_on)
307 while (pca_status.in_use[0] || pca_status.in_use[1] ||
308 pca_status.in_use[2]) {
311 error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_drain", 0);
314 if (error != 0 && error != ERESTART) {
323 static struct isa_pnp_id pca_ids[] = {
324 {0x0008d041, "AT-style speaker sound"}, /* PNP0800 */
329 pcaprobe(device_t dev)
333 /* Check isapnp ids */
334 error = ISA_PNP_PROBE(device_get_parent(dev), dev, pca_ids);
342 pcaattach(device_t dev)
345 make_dev(&pca_cdevsw, 0, 0, 0, 0600, "pcaudio");
346 make_dev(&pca_cdevsw, 128, 0, 0, 0600, "pcaudioctl");
350 static device_method_t pca_methods[] = {
351 DEVMETHOD(device_probe, pcaprobe),
352 DEVMETHOD(device_attach, pcaattach),
356 static driver_t pca_driver = {
362 static devclass_t pca_devclass;
364 DRIVER_MODULE(pca, isa, pca_driver, pca_devclass, 0, 0);
368 pcaopen(dev_t dev, int flags, int fmt, struct proc *p)
370 /* audioctl device can always be opened */
371 if (minor(dev) == 128)
376 if (!pca_initialized) {
381 /* audio device can only be open by one process */
382 if (pca_status.open) {
383 pca_status.queries = 1;
386 pca_status.buffer = pca_status.buf[0];
387 pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
388 pca_status.timer_on = 0;
390 pca_status.processed = 0;
396 pcaclose(dev_t dev, int flags, int fmt, struct proc *p)
398 /* audioctl device can always be closed */
399 if (minor(dev) == 128)
403 /* audio device close drains all output and restores timers */
412 pcawrite(dev_t dev, struct uio *uio, int flag)
414 int count, error, which, x;
416 /* only audio device can be written */
420 while ((count = min(BUF_SIZE, uio->uio_resid)) > 0) {
421 if (pca_status.in_use[0] && pca_status.in_use[1] &&
422 pca_status.in_use[2]) {
423 if (flag & IO_NDELAY)
427 error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_wait", 0);
430 if (error != 0 && error != ERESTART) {
435 if (!pca_status.in_use[0])
437 else if (!pca_status.in_use[1])
441 if (count && !pca_status.in_use[which]) {
442 uiomove(pca_status.buf[which], count, uio);
443 pca_status.processed += count;
444 switch (pca_status.encoding) {
445 case AUDIO_ENCODING_ULAW:
446 conv(ulaw_dsp, pca_status.buf[which], count);
449 case AUDIO_ENCODING_ALAW:
450 conv(alaw_linear, pca_status.buf[which], count);
453 case AUDIO_ENCODING_RAW:
456 pca_status.in_use[which] = count;
457 if (!pca_status.timer_on)
467 pcaioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
474 auptr = (audio_info_t *)data;
475 auptr->play.sample_rate = pca_status.sample_rate;
476 auptr->play.channels = 1;
477 auptr->play.precision = 8;
478 auptr->play.encoding = pca_status.encoding;
480 auptr->play.gain = pca_status.volume;
481 auptr->play.port = 0;
483 auptr->play.samples = pca_status.processed;
485 auptr->play.pause = !pca_status.timer_on;
486 auptr->play.error = 0;
487 auptr->play.waiting = pca_status.queries;
489 auptr->play.open = pca_status.open;
490 auptr->play.active = pca_status.timer_on;
494 auptr = (audio_info_t *)data;
495 if (auptr->play.sample_rate != (unsigned int)~0) {
496 pca_status.sample_rate = auptr->play.sample_rate;
498 (pca_status.sample_rate << 8) / INTERRUPT_RATE;
500 if (auptr->play.encoding != (unsigned int)~0) {
501 pca_status.encoding = auptr->play.encoding;
503 if (auptr->play.gain != (unsigned int)~0) {
504 pca_status.volume = auptr->play.gain;
505 pca_volume(pca_status.volume);
507 if (auptr->play.pause != (unsigned char)~0) {
508 if (auptr->play.pause)
517 case AUDIO_COMPAT_DRAIN:
521 case AUDIO_COMPAT_FLUSH:
532 pcaintr(struct clockframe *frame)
534 if (pca_status.index < pca_status.in_use[pca_status.current]) {
536 __asm__("outb %0,$0x61\n"
539 : : "a" ((char)pca_status.oldval) );
542 : : "a" ((char)pca_status.buffer[pca_status.index]),
543 "b" (volume_table) );
545 pca_status.counter += pca_status.scale;
546 pca_status.index = (pca_status.counter >> 8);
548 if (pca_status.index >= pca_status.in_use[pca_status.current]) {
549 pca_status.index = pca_status.counter = 0;
550 pca_status.in_use[pca_status.current] = 0;
551 pca_status.current++;
552 if (pca_status.current > 2)
553 pca_status.current = 0;
554 pca_status.buffer = pca_status.buf[pca_status.current];
557 if (pca_status.wsel.si_pid) {
558 selwakeup((struct selinfo *)&pca_status.wsel.si_pid);
559 pca_status.wsel.si_pid = 0;
560 pca_status.wsel.si_flags = 0;
567 pcapoll(dev_t dev, int events, struct proc *p)
574 if (events & (POLLOUT | POLLWRNORM)) {
575 if (!pca_status.in_use[0] || !pca_status.in_use[1] ||
576 !pca_status.in_use[2])
577 revents |= events & (POLLOUT | POLLWRNORM);
579 selrecord(p, &pca_status.wsel);