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$
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 /* bmaj */ -1
173 };
174
175 static void pca_continue __P((void));
176 static void pca_init __P((void));
177 static void pca_pause __P((void));
178
179 static void
180 conv(const unsigned char *table, unsigned char *buff, unsigned n)
181 {
182 unsigned i;
183
184 for (i = 0; i < n; i++)
185 buff[i] = table[buff[i]];
186 }
187
188
189 static void
190 pca_volume(int volume)
191 {
192 int i, j;
193
194 for (i=0; i<256; i++) {
195 j = ((i-128)*volume)/25;
196 /* XXX
197 j = ((i-128)*volume)/100;
198 */
199 if (j<-128)
200 j = -128;
201 if (j>127)
202 j = 127;
203 volume_table[i] = (((255-(j + 128))/4)+1);
204 }
205 }
206
207
208 static void
209 pca_init(void)
210 {
211 pca_status.open = 0;
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;
224
225 pca_volume(pca_status.volume);
226 }
227
228
229 static int
230 pca_start(void)
231 {
232 int x = splhigh();
233 int rv = 0;
234
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))
243 rv = -1;
244 else if (acquire_timer0(INTERRUPT_RATE, pcaintr)) {
245 release_timer2();
246 rv = -1;
247 } else
248 pca_status.timer_on = 1;
249
250 splx(x);
251 return rv;
252 }
253
254
255 static void
256 pca_stop(void)
257 {
258 int x = splhigh();
259
260 /* release the timers */
261 release_timer0();
262 release_timer2();
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;
270 splx(x);
271 }
272
273
274 static void
275 pca_pause(void)
276 {
277 int x = splhigh();
278
279 release_timer0();
280 release_timer2();
281 pca_status.timer_on = 0;
282 splx(x);
283 }
284
285
286 static void
287 pca_continue(void)
288 {
289 int x = splhigh();
290
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;
295 splx(x);
296 }
297
298
299 static int
300 pca_wait(void)
301 {
302 int error, x;
303
304 if (!pca_status.timer_on)
305 return 0;
306
307 while (pca_status.in_use[0] || pca_status.in_use[1] ||
308 pca_status.in_use[2]) {
309 x = spltty();
310 pca_sleep = 1;
311 error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_drain", 0);
312 pca_sleep = 0;
313 splx(x);
314 if (error != 0 && error != ERESTART) {
315 pca_stop();
316 return error;
317 }
318 }
319 return 0;
320 }
321
322
323 static struct isa_pnp_id pca_ids[] = {
324 {0x0008d041, "AT-style speaker sound"}, /* PNP0800 */
325 {0}
326 };
327
328 static int
329 pcaprobe(device_t dev)
330 {
331 int error;
332
333 /* Check isapnp ids */
334 error = ISA_PNP_PROBE(device_get_parent(dev), dev, pca_ids);
335 if (error == ENXIO)
336 return ENXIO;
337 return 0;
338 }
339
340
341 static int
342 pcaattach(device_t dev)
343 {
344 pca_init();
345 make_dev(&pca_cdevsw, 0, 0, 0, 0600, "pcaudio");
346 make_dev(&pca_cdevsw, 128, 0, 0, 0600, "pcaudioctl");
347 return 0;
348 }
349
350 static device_method_t pca_methods[] = {
351 DEVMETHOD(device_probe, pcaprobe),
352 DEVMETHOD(device_attach, pcaattach),
353 { 0, 0 }
354 };
355
356 static driver_t pca_driver = {
357 "pca",
358 pca_methods,
359 1
360 };
361
362 static devclass_t pca_devclass;
363
364 DRIVER_MODULE(pca, isa, pca_driver, pca_devclass, 0, 0);
365
366
367 static int
368 pcaopen(dev_t dev, int flags, int fmt, struct proc *p)
369 {
370 /* audioctl device can always be opened */
371 if (minor(dev) == 128)
372 return 0;
373 if (minor(dev) > 0)
374 return ENXIO;
375
376 if (!pca_initialized) {
377 pca_init();
378 pca_initialized = 1;
379 }
380
381 /* audio device can only be open by one process */
382 if (pca_status.open) {
383 pca_status.queries = 1;
384 return EBUSY;
385 }
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;
389 pca_status.open = 1;
390 pca_status.processed = 0;
391 return 0;
392 }
393
394
395 static int
396 pcaclose(dev_t dev, int flags, int fmt, struct proc *p)
397 {
398 /* audioctl device can always be closed */
399 if (minor(dev) == 128)
400 return 0;
401 if (minor(dev) > 0)
402 return ENXIO;
403 /* audio device close drains all output and restores timers */
404 pca_wait();
405 pca_stop();
406 pca_status.open = 0;
407 return 0;
408 }
409
410
411 static int
412 pcawrite(dev_t dev, struct uio *uio, int flag)
413 {
414 int count, error, which, x;
415
416 /* only audio device can be written */
417 if (minor(dev) > 0)
418 return ENXIO;
419
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)
424 return EWOULDBLOCK;
425 x = spltty();
426 pca_sleep = 1;
427 error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_wait", 0);
428 pca_sleep = 0;
429 splx(x);
430 if (error != 0 && error != ERESTART) {
431 pca_stop();
432 return error;
433 }
434 }
435 if (!pca_status.in_use[0])
436 which = 0;
437 else if (!pca_status.in_use[1])
438 which = 1;
439 else
440 which = 2;
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);
447 break;
448
449 case AUDIO_ENCODING_ALAW:
450 conv(alaw_linear, pca_status.buf[which], count);
451 break;
452
453 case AUDIO_ENCODING_RAW:
454 break;
455 }
456 pca_status.in_use[which] = count;
457 if (!pca_status.timer_on)
458 if (pca_start())
459 return EBUSY;
460 }
461 }
462 return 0;
463 }
464
465
466 static int
467 pcaioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
468 {
469 audio_info_t *auptr;
470
471 switch(cmd) {
472
473 case AUDIO_GETINFO:
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;
479
480 auptr->play.gain = pca_status.volume;
481 auptr->play.port = 0;
482
483 auptr->play.samples = pca_status.processed;
484 auptr->play.eof = 0;
485 auptr->play.pause = !pca_status.timer_on;
486 auptr->play.error = 0;
487 auptr->play.waiting = pca_status.queries;
488
489 auptr->play.open = pca_status.open;
490 auptr->play.active = pca_status.timer_on;
491 return 0;
492
493 case AUDIO_SETINFO:
494 auptr = (audio_info_t *)data;
495 if (auptr->play.sample_rate != (unsigned int)~0) {
496 pca_status.sample_rate = auptr->play.sample_rate;
497 pca_status.scale =
498 (pca_status.sample_rate << 8) / INTERRUPT_RATE;
499 }
500 if (auptr->play.encoding != (unsigned int)~0) {
501 pca_status.encoding = auptr->play.encoding;
502 }
503 if (auptr->play.gain != (unsigned int)~0) {
504 pca_status.volume = auptr->play.gain;
505 pca_volume(pca_status.volume);
506 }
507 if (auptr->play.pause != (unsigned char)~0) {
508 if (auptr->play.pause)
509 pca_pause();
510 else
511 pca_continue();
512 }
513
514 return 0;
515
516 case AUDIO_DRAIN:
517 case AUDIO_COMPAT_DRAIN:
518 return pca_wait();
519
520 case AUDIO_FLUSH:
521 case AUDIO_COMPAT_FLUSH:
522 pca_stop();
523 return 0;
524 case FIONBIO:
525 return 0;
526 }
527 return ENXIO;
528 }
529
530
531 static void
532 pcaintr(struct clockframe *frame)
533 {
534 if (pca_status.index < pca_status.in_use[pca_status.current]) {
535 disable_intr();
536 __asm__("outb %0,$0x61\n"
537 "andb $0xFE,%0\n"
538 "outb %0,$0x61"
539 : : "a" ((char)pca_status.oldval) );
540 __asm__("xlatb\n"
541 "outb %0,$0x42"
542 : : "a" ((char)pca_status.buffer[pca_status.index]),
543 "b" (volume_table) );
544 enable_intr();
545 pca_status.counter += pca_status.scale;
546 pca_status.index = (pca_status.counter >> 8);
547 }
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];
555 if (pca_sleep)
556 wakeup(&pca_sleep);
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;
561 }
562 }
563 }
564
565
566 static int
567 pcapoll(dev_t dev, int events, struct proc *p)
568 {
569 int s;
570 struct proc *p1;
571 int revents = 0;
572
573 s = spltty();
574
575 if (events & (POLLOUT | POLLWRNORM)) {
576 if (!pca_status.in_use[0] || !pca_status.in_use[1] ||
577 !pca_status.in_use[2])
578 revents |= events & (POLLOUT | POLLWRNORM);
579 else {
580 if (pca_status.wsel.si_pid &&
581 (p1=pfind(pca_status.wsel.si_pid))
582 && p1->p_wchan == (caddr_t)&selwait)
583 pca_status.wsel.si_flags = SI_COLL;
584 else
585 pca_status.wsel.si_pid = p->p_pid;
586 }
587 }
588 splx(s);
589 return (revents);
590 }
Cache object: 18726e1cc075c0b3f2b8ae555257706e
|