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