FreeBSD/Linux Kernel Cross Reference
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: releng/5.1/sys/i386/isa/pcaudio.c 111815 2003-03-03 12:15:54Z phk $
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
148 static void pcaintr(struct clockframe *frame);
149
150 static d_open_t pcaopen;
151 static d_close_t pcaclose;
152 static d_write_t pcawrite;
153 static d_ioctl_t pcaioctl;
154 static d_poll_t pcapoll;
155
156 #define CDEV_MAJOR 24
157 static struct cdevsw pca_cdevsw = {
158 .d_open = pcaopen,
159 .d_close = pcaclose,
160 .d_write = pcawrite,
161 .d_ioctl = pcaioctl,
162 .d_poll = pcapoll,
163 .d_name = "pca",
164 .d_maj = CDEV_MAJOR,
165 };
166
167 static void pca_continue(void);
168 static void pca_init(void);
169 static void pca_pause(void);
170
171 static void
172 conv(const unsigned char *table, unsigned char *buff, unsigned n)
173 {
174 unsigned i;
175
176 for (i = 0; i < n; i++)
177 buff[i] = table[buff[i]];
178 }
179
180
181 static void
182 pca_volume(int volume)
183 {
184 int i, j;
185
186 for (i=0; i<256; i++) {
187 j = ((i-128)*volume)/25;
188 /* XXX
189 j = ((i-128)*volume)/100;
190 */
191 if (j<-128)
192 j = -128;
193 if (j>127)
194 j = 127;
195 volume_table[i] = (((255-(j + 128))/4)+1);
196 }
197 }
198
199
200 static void
201 pca_init(void)
202 {
203 pca_status.open = 0;
204 pca_status.queries = 0;
205 pca_status.timer_on = 0;
206 pca_status.buf[0] = (unsigned char *)&buffer1[0];
207 pca_status.buf[1] = (unsigned char *)&buffer2[0];
208 pca_status.buf[2] = (unsigned char *)&buffer3[0];
209 pca_status.buffer = pca_status.buf[0];
210 pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
211 pca_status.current = 0;
212 pca_status.sample_rate = SAMPLE_RATE;
213 pca_status.scale = (pca_status.sample_rate << 8) / INTERRUPT_RATE;
214 pca_status.encoding = AUDIO_ENCODING_ULAW;
215 pca_status.volume = 100;
216
217 pca_volume(pca_status.volume);
218 }
219
220
221 static int
222 pca_start(void)
223 {
224 int x = splhigh();
225 int rv = 0;
226
227 /* use the first buffer */
228 pca_status.current = 0;
229 pca_status.index = 0;
230 pca_status.counter = 0;
231 pca_status.buffer = pca_status.buf[pca_status.current];
232 pca_status.oldval = inb(IO_PPI) | 0x03;
233 /* acquire the timers */
234 if (acquire_timer2(TIMER_LSB|TIMER_ONESHOT))
235 rv = -1;
236 else if (acquire_timer0(INTERRUPT_RATE, pcaintr)) {
237 release_timer2();
238 rv = -1;
239 } else
240 pca_status.timer_on = 1;
241
242 splx(x);
243 return rv;
244 }
245
246
247 static void
248 pca_stop(void)
249 {
250 int x = splhigh();
251
252 /* release the timers */
253 release_timer0();
254 release_timer2();
255 /* reset the buffer */
256 pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
257 pca_status.index = 0;
258 pca_status.counter = 0;
259 pca_status.current = 0;
260 pca_status.buffer = pca_status.buf[pca_status.current];
261 pca_status.timer_on = 0;
262 splx(x);
263 }
264
265
266 static void
267 pca_pause(void)
268 {
269 int x = splhigh();
270
271 release_timer0();
272 release_timer2();
273 pca_status.timer_on = 0;
274 splx(x);
275 }
276
277
278 static void
279 pca_continue(void)
280 {
281 int x = splhigh();
282
283 pca_status.oldval = inb(IO_PPI) | 0x03;
284 acquire_timer2(TIMER_LSB|TIMER_ONESHOT);
285 acquire_timer0(INTERRUPT_RATE, pcaintr);
286 pca_status.timer_on = 1;
287 splx(x);
288 }
289
290
291 static int
292 pca_wait(void)
293 {
294 int error, x;
295
296 if (!pca_status.timer_on)
297 return 0;
298
299 while (pca_status.in_use[0] || pca_status.in_use[1] ||
300 pca_status.in_use[2]) {
301 x = spltty();
302 pca_sleep = 1;
303 error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_drain", 0);
304 pca_sleep = 0;
305 splx(x);
306 if (error != 0 && error != ERESTART) {
307 pca_stop();
308 return error;
309 }
310 }
311 return 0;
312 }
313
314
315 static struct isa_pnp_id pca_ids[] = {
316 {0x0008d041, "AT-style speaker sound"}, /* PNP0800 */
317 {0}
318 };
319
320 static int
321 pcaprobe(device_t dev)
322 {
323 /* Check isapnp ids */
324 return ISA_PNP_PROBE(device_get_parent(dev), dev, pca_ids);
325 }
326
327
328 static int
329 pcaattach(device_t dev)
330 {
331 pca_init();
332 make_dev(&pca_cdevsw, 0, 0, 0, 0600, "pcaudio");
333 make_dev(&pca_cdevsw, 128, 0, 0, 0600, "pcaudioctl");
334 return 0;
335 }
336
337 static device_method_t pca_methods[] = {
338 DEVMETHOD(device_probe, pcaprobe),
339 DEVMETHOD(device_attach, pcaattach),
340 { 0, 0 }
341 };
342
343 static driver_t pca_driver = {
344 "pca",
345 pca_methods,
346 1
347 };
348
349 static devclass_t pca_devclass;
350
351 DRIVER_MODULE(pca, isa, pca_driver, pca_devclass, 0, 0);
352
353
354 static int
355 pcaopen(dev_t dev, int flags, int fmt, struct thread *td)
356 {
357 /* audioctl device can always be opened */
358 if (minor(dev) == 128)
359 return 0;
360 if (minor(dev) > 0)
361 return ENXIO;
362
363 /* audio device can only be open by one process */
364 if (pca_status.open) {
365 pca_status.queries = 1;
366 return EBUSY;
367 }
368 pca_status.buffer = pca_status.buf[0];
369 pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
370 pca_status.timer_on = 0;
371 pca_status.open = 1;
372 pca_status.processed = 0;
373 return 0;
374 }
375
376
377 static int
378 pcaclose(dev_t dev, int flags, int fmt, struct thread *td)
379 {
380 /* audioctl device can always be closed */
381 if (minor(dev) == 128)
382 return 0;
383 if (minor(dev) > 0)
384 return ENXIO;
385 /* audio device close drains all output and restores timers */
386 pca_wait();
387 pca_stop();
388 pca_status.open = 0;
389 return 0;
390 }
391
392
393 static int
394 pcawrite(dev_t dev, struct uio *uio, int flag)
395 {
396 int count, error, which, x;
397
398 /* only audio device can be written */
399 if (minor(dev) > 0)
400 return ENXIO;
401
402 while ((count = min(BUF_SIZE, uio->uio_resid)) > 0) {
403 if (pca_status.in_use[0] && pca_status.in_use[1] &&
404 pca_status.in_use[2]) {
405 if (flag & IO_NDELAY)
406 return EWOULDBLOCK;
407 x = spltty();
408 pca_sleep = 1;
409 error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_wait", 0);
410 pca_sleep = 0;
411 splx(x);
412 if (error != 0 && error != ERESTART) {
413 pca_stop();
414 return error;
415 }
416 }
417 if (!pca_status.in_use[0])
418 which = 0;
419 else if (!pca_status.in_use[1])
420 which = 1;
421 else
422 which = 2;
423 if (count && !pca_status.in_use[which]) {
424 uiomove(pca_status.buf[which], count, uio);
425 pca_status.processed += count;
426 switch (pca_status.encoding) {
427 case AUDIO_ENCODING_ULAW:
428 conv(ulaw_dsp, pca_status.buf[which], count);
429 break;
430
431 case AUDIO_ENCODING_ALAW:
432 conv(alaw_linear, pca_status.buf[which], count);
433 break;
434
435 case AUDIO_ENCODING_RAW:
436 break;
437 }
438 pca_status.in_use[which] = count;
439 if (!pca_status.timer_on)
440 if (pca_start())
441 return EBUSY;
442 }
443 }
444 return 0;
445 }
446
447
448 static int
449 pcaioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
450 {
451 audio_info_t *auptr;
452
453 switch(cmd) {
454
455 case AUDIO_GETINFO:
456 auptr = (audio_info_t *)data;
457 auptr->play.sample_rate = pca_status.sample_rate;
458 auptr->play.channels = 1;
459 auptr->play.precision = 8;
460 auptr->play.encoding = pca_status.encoding;
461
462 auptr->play.gain = pca_status.volume;
463 auptr->play.port = 0;
464
465 auptr->play.samples = pca_status.processed;
466 auptr->play.eof = 0;
467 auptr->play.pause = !pca_status.timer_on;
468 auptr->play.error = 0;
469 auptr->play.waiting = pca_status.queries;
470
471 auptr->play.open = pca_status.open;
472 auptr->play.active = pca_status.timer_on;
473 return 0;
474
475 case AUDIO_SETINFO:
476 auptr = (audio_info_t *)data;
477 if (auptr->play.sample_rate != (unsigned int)~0) {
478 pca_status.sample_rate = auptr->play.sample_rate;
479 pca_status.scale =
480 (pca_status.sample_rate << 8) / INTERRUPT_RATE;
481 }
482 if (auptr->play.encoding != (unsigned int)~0) {
483 pca_status.encoding = auptr->play.encoding;
484 }
485 if (auptr->play.gain != (unsigned int)~0) {
486 pca_status.volume = auptr->play.gain;
487 pca_volume(pca_status.volume);
488 }
489 if (auptr->play.pause != (unsigned char)~0) {
490 if (auptr->play.pause)
491 pca_pause();
492 else
493 pca_continue();
494 }
495
496 return 0;
497
498 case AUDIO_DRAIN:
499 case AUDIO_COMPAT_DRAIN:
500 return pca_wait();
501
502 case AUDIO_FLUSH:
503 case AUDIO_COMPAT_FLUSH:
504 pca_stop();
505 return 0;
506 case FIONBIO:
507 return 0;
508 }
509 return ENXIO;
510 }
511
512
513 static void
514 pcaintr(struct clockframe *frame)
515 {
516 if (pca_status.index < pca_status.in_use[pca_status.current]) {
517 disable_intr();
518 __asm__("outb %0,$0x61\n"
519 "andb $0xFE,%0\n"
520 "outb %0,$0x61"
521 : : "a" ((char)pca_status.oldval) );
522 __asm__("xlatb\n"
523 "outb %0,$0x42"
524 : : "a" ((char)pca_status.buffer[pca_status.index]),
525 "b" (volume_table) );
526 enable_intr();
527 pca_status.counter += pca_status.scale;
528 pca_status.index = (pca_status.counter >> 8);
529 }
530 if (pca_status.index >= pca_status.in_use[pca_status.current]) {
531 pca_status.index = pca_status.counter = 0;
532 pca_status.in_use[pca_status.current] = 0;
533 pca_status.current++;
534 if (pca_status.current > 2)
535 pca_status.current = 0;
536 pca_status.buffer = pca_status.buf[pca_status.current];
537 if (pca_sleep)
538 wakeup(&pca_sleep);
539 if (SEL_WAITING(&pca_status.wsel))
540 selwakeup(&pca_status.wsel);
541 }
542 }
543
544
545 static int
546 pcapoll(dev_t dev, int events, struct thread *td)
547 {
548 int s;
549 int revents = 0;
550
551 s = spltty();
552
553 if (events & (POLLOUT | POLLWRNORM)) {
554 if (!pca_status.in_use[0] || !pca_status.in_use[1] ||
555 !pca_status.in_use[2])
556 revents |= events & (POLLOUT | POLLWRNORM);
557 else
558 selrecord(td, &pca_status.wsel);
559 }
560 splx(s);
561 return (revents);
562 }
Cache object: d004c2cfc95e806924e9119100cf4c33
|