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 "pca.h"
32 #if NPCA > 0
33 #include "opt_devfs.h"
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/conf.h>
38 #include <sys/proc.h>
39 #include <sys/kernel.h>
40 #include <sys/filio.h>
41 #include <sys/poll.h>
42 #include <sys/vnode.h>
43
44 #include <machine/clock.h>
45 #include <machine/pcaudioio.h>
46
47 #include <i386/isa/isa.h>
48 #include <i386/isa/isa_device.h>
49 #include <i386/isa/timerreg.h>
50
51 #ifdef DEVFS
52 #include <sys/devfsext.h>
53 #endif /* DEVFS */
54
55 #define BUF_SIZE 8192
56 #define SAMPLE_RATE 8000
57 #define INTERRUPT_RATE 16000
58
59 static struct pca_status {
60 char open; /* device open */
61 char queries; /* did others try opening */
62 unsigned char *buf[3]; /* triple buffering */
63 unsigned char *buffer; /* current buffer ptr */
64 unsigned in_use[3]; /* buffers fill */
65 unsigned index; /* index in current buffer */
66 unsigned counter; /* sample counter */
67 unsigned scale; /* sample counter scale */
68 unsigned sample_rate; /* sample rate */
69 unsigned processed; /* samples processed */
70 unsigned volume; /* volume for pc-speaker */
71 char encoding; /* Ulaw, Alaw or linear */
72 char current; /* current buffer */
73 unsigned char oldval; /* old timer port value */
74 char timer_on; /* is playback running */
75 struct selinfo wsel; /* select/poll status */
76 } pca_status;
77
78 static char buffer1[BUF_SIZE];
79 static char buffer2[BUF_SIZE];
80 static char buffer3[BUF_SIZE];
81 static char volume_table[256];
82
83 #define DSP_ULAW_NOT_WANTED
84 #include <i386/isa/snd/ulaw.h>
85
86 static unsigned char alaw_linear[] = {
87 45, 214, 122, 133, 0, 255, 107, 149,
88 86, 171, 126, 129, 0, 255, 117, 138,
89 13, 246, 120, 135, 0, 255, 99, 157,
90 70, 187, 124, 131, 0, 255, 113, 142,
91 61, 198, 123, 132, 0, 255, 111, 145,
92 94, 163, 127, 128, 0, 255, 119, 136,
93 29, 230, 121, 134, 0, 255, 103, 153,
94 78, 179, 125, 130, 0, 255, 115, 140,
95 37, 222, 122, 133, 0, 255, 105, 151,
96 82, 175, 126, 129, 0, 255, 116, 139,
97 5, 254, 120, 135, 0, 255, 97, 159,
98 66, 191, 124, 131, 0, 255, 112, 143,
99 53, 206, 123, 132, 0, 255, 109, 147,
100 90, 167, 127, 128, 0, 255, 118, 137,
101 21, 238, 121, 134, 0, 255, 101, 155,
102 74, 183, 125, 130, 0, 255, 114, 141,
103 49, 210, 123, 133, 0, 255, 108, 148,
104 88, 169, 127, 129, 0, 255, 118, 138,
105 17, 242, 121, 135, 0, 255, 100, 156,
106 72, 185, 125, 131, 0, 255, 114, 142,
107 64, 194, 124, 132, 0, 255, 112, 144,
108 96, 161, 128, 128, 1, 255, 120, 136,
109 33, 226, 122, 134, 0, 255, 104, 152,
110 80, 177, 126, 130, 0, 255, 116, 140,
111 41, 218, 122, 133, 0, 255, 106, 150,
112 84, 173, 126, 129, 0, 255, 117, 139,
113 9, 250, 120, 135, 0, 255, 98, 158,
114 68, 189, 124, 131, 0, 255, 113, 143,
115 57, 202, 123, 132, 0, 255, 110, 146,
116 92, 165, 127, 128, 0, 255, 119, 137,
117 25, 234, 121, 134, 0, 255, 102, 154,
118 76, 181, 125, 130, 0, 255, 115, 141,
119 };
120
121 #ifdef DEVFS
122 static void *pca_devfs_token;
123 static void *pcac_devfs_token;
124 #endif
125
126 static int pca_sleep = 0;
127 static int pca_initialized = 0;
128
129 static void pcaintr(struct clockframe *frame);
130 static int pcaprobe(struct isa_device *dvp);
131 static int pcaattach(struct isa_device *dvp);
132
133 struct isa_driver pcadriver = {
134 pcaprobe, pcaattach, "pca",
135 };
136
137 static d_open_t pcaopen;
138 static d_close_t pcaclose;
139 static d_write_t pcawrite;
140 static d_ioctl_t pcaioctl;
141 static d_poll_t pcapoll;
142
143 #define CDEV_MAJOR 24
144 static struct cdevsw pca_cdevsw =
145 { pcaopen, pcaclose, noread, pcawrite, /*24*/
146 pcaioctl, nostop, nullreset, nodevtotty,/* pcaudio */
147 pcapoll, nommap, NULL, "pca", NULL, -1 };
148
149 static void pca_continue __P((void));
150 static void pca_init __P((void));
151 static void pca_pause __P((void));
152
153 static __inline void
154 conv(const void *table, void *buff, unsigned int n)
155 {
156 __asm__("1:\tmovb (%2), %3\n"
157 "\txlatb\n"
158 "\tmovb %3, (%2)\n"
159 "\tinc %2\n"
160 "\tdec %1\n"
161 "\tjnz 1b\n"
162 :
163 :"b" (table), "c" (n), "D" (buff), "a" ((char)n)
164 :"bx","cx","di","ax");
165 }
166
167
168 static void
169 pca_volume(int volume)
170 {
171 int i, j;
172
173 for (i=0; i<256; i++) {
174 j = ((i-128)*volume)/25;
175 /* XXX
176 j = ((i-128)*volume)/100;
177 */
178 if (j<-128)
179 j = -128;
180 if (j>127)
181 j = 127;
182 volume_table[i] = (((255-(j + 128))/4)+1);
183 }
184 }
185
186
187 static void
188 pca_init(void)
189 {
190 pca_status.open = 0;
191 pca_status.queries = 0;
192 pca_status.timer_on = 0;
193 pca_status.buf[0] = (unsigned char *)&buffer1[0];
194 pca_status.buf[1] = (unsigned char *)&buffer2[0];
195 pca_status.buf[2] = (unsigned char *)&buffer3[0];
196 pca_status.buffer = pca_status.buf[0];
197 pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
198 pca_status.current = 0;
199 pca_status.sample_rate = SAMPLE_RATE;
200 pca_status.scale = (pca_status.sample_rate << 8) / INTERRUPT_RATE;
201 pca_status.encoding = AUDIO_ENCODING_ULAW;
202 pca_status.volume = 100;
203
204 pca_volume(pca_status.volume);
205 }
206
207
208 static int
209 pca_start(void)
210 {
211 int x = splhigh();
212 int rv = 0;
213
214 /* use the first buffer */
215 pca_status.current = 0;
216 pca_status.index = 0;
217 pca_status.counter = 0;
218 pca_status.buffer = pca_status.buf[pca_status.current];
219 pca_status.oldval = inb(IO_PPI) | 0x03;
220 /* acquire the timers */
221 if (acquire_timer2(TIMER_LSB|TIMER_ONESHOT))
222 rv = -1;
223 else if (acquire_timer0(INTERRUPT_RATE, pcaintr)) {
224 release_timer2();
225 rv = -1;
226 } else
227 pca_status.timer_on = 1;
228
229 splx(x);
230 return rv;
231 }
232
233
234 static void
235 pca_stop(void)
236 {
237 int x = splhigh();
238
239 /* release the timers */
240 release_timer0();
241 release_timer2();
242 /* reset the buffer */
243 pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
244 pca_status.index = 0;
245 pca_status.counter = 0;
246 pca_status.current = 0;
247 pca_status.buffer = pca_status.buf[pca_status.current];
248 pca_status.timer_on = 0;
249 splx(x);
250 }
251
252
253 static void
254 pca_pause(void)
255 {
256 int x = splhigh();
257
258 release_timer0();
259 release_timer2();
260 pca_status.timer_on = 0;
261 splx(x);
262 }
263
264
265 static void
266 pca_continue(void)
267 {
268 int x = splhigh();
269
270 pca_status.oldval = inb(IO_PPI) | 0x03;
271 acquire_timer2(TIMER_LSB|TIMER_ONESHOT);
272 acquire_timer0(INTERRUPT_RATE, pcaintr);
273 pca_status.timer_on = 1;
274 splx(x);
275 }
276
277
278 static int
279 pca_wait(void)
280 {
281 int error, x;
282
283 if (!pca_status.timer_on)
284 return 0;
285
286 while (pca_status.in_use[0] || pca_status.in_use[1] ||
287 pca_status.in_use[2]) {
288 x = spltty();
289 pca_sleep = 1;
290 error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_drain", 0);
291 pca_sleep = 0;
292 splx(x);
293 if (error != 0 && error != ERESTART) {
294 pca_stop();
295 return error;
296 }
297 }
298 return 0;
299 }
300
301
302 static int
303 pcaprobe(struct isa_device *dvp)
304 {
305 return(-1);
306 }
307
308
309 static int
310 pcaattach(struct isa_device *dvp)
311 {
312 printf("pca%d: PC speaker audio driver\n", dvp->id_unit);
313 pca_init();
314 #ifdef DEVFS
315 pca_devfs_token =
316 devfs_add_devswf(&pca_cdevsw, 0, DV_CHR, 0, 0, 0600, "pcaudio");
317 pcac_devfs_token =
318 devfs_add_devswf(&pca_cdevsw, 128, DV_CHR, 0, 0, 0600,
319 "pcaudioctl");
320 #endif /*DEVFS*/
321
322 return 1;
323 }
324
325
326 static int
327 pcaopen(dev_t dev, int flags, int fmt, struct proc *p)
328 {
329 /* audioctl device can always be opened */
330 if (minor(dev) == 128)
331 return 0;
332 if (minor(dev) > 0)
333 return ENXIO;
334
335 if (!pca_initialized) {
336 pca_init();
337 pca_initialized = 1;
338 }
339
340 /* audio device can only be open by one process */
341 if (pca_status.open) {
342 pca_status.queries = 1;
343 return EBUSY;
344 }
345 pca_status.buffer = pca_status.buf[0];
346 pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
347 pca_status.timer_on = 0;
348 pca_status.open = 1;
349 pca_status.processed = 0;
350 return 0;
351 }
352
353
354 static int
355 pcaclose(dev_t dev, int flags, int fmt, struct proc *p)
356 {
357 /* audioctl device can always be closed */
358 if (minor(dev) == 128)
359 return 0;
360 if (minor(dev) > 0)
361 return ENXIO;
362 /* audio device close drains all output and restores timers */
363 pca_wait();
364 pca_stop();
365 pca_status.open = 0;
366 return 0;
367 }
368
369
370 static int
371 pcawrite(dev_t dev, struct uio *uio, int flag)
372 {
373 int count, error, which, x;
374
375 /* only audio device can be written */
376 if (minor(dev) > 0)
377 return ENXIO;
378
379 while ((count = min(BUF_SIZE, uio->uio_resid)) > 0) {
380 if (pca_status.in_use[0] && pca_status.in_use[1] &&
381 pca_status.in_use[2]) {
382 if (flag & IO_NDELAY)
383 return EWOULDBLOCK;
384 x = spltty();
385 pca_sleep = 1;
386 error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_wait", 0);
387 pca_sleep = 0;
388 splx(x);
389 if (error != 0 && error != ERESTART) {
390 pca_stop();
391 return error;
392 }
393 }
394 if (!pca_status.in_use[0])
395 which = 0;
396 else if (!pca_status.in_use[1])
397 which = 1;
398 else
399 which = 2;
400 if (count && !pca_status.in_use[which]) {
401 uiomove(pca_status.buf[which], count, uio);
402 pca_status.processed += count;
403 switch (pca_status.encoding) {
404 case AUDIO_ENCODING_ULAW:
405 conv(ulaw_dsp, pca_status.buf[which], count);
406 break;
407
408 case AUDIO_ENCODING_ALAW:
409 conv(alaw_linear, pca_status.buf[which], count);
410 break;
411
412 case AUDIO_ENCODING_RAW:
413 break;
414 }
415 pca_status.in_use[which] = count;
416 if (!pca_status.timer_on)
417 if (pca_start())
418 return EBUSY;
419 }
420 }
421 return 0;
422 }
423
424
425 static int
426 pcaioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
427 {
428 audio_info_t *auptr;
429
430 switch(cmd) {
431
432 case AUDIO_GETINFO:
433 auptr = (audio_info_t *)data;
434 auptr->play.sample_rate = pca_status.sample_rate;
435 auptr->play.channels = 1;
436 auptr->play.precision = 8;
437 auptr->play.encoding = pca_status.encoding;
438
439 auptr->play.gain = pca_status.volume;
440 auptr->play.port = 0;
441
442 auptr->play.samples = pca_status.processed;
443 auptr->play.eof = 0;
444 auptr->play.pause = !pca_status.timer_on;
445 auptr->play.error = 0;
446 auptr->play.waiting = pca_status.queries;
447
448 auptr->play.open = pca_status.open;
449 auptr->play.active = pca_status.timer_on;
450 return 0;
451
452 case AUDIO_SETINFO:
453 auptr = (audio_info_t *)data;
454 if (auptr->play.sample_rate != (unsigned int)~0) {
455 pca_status.sample_rate = auptr->play.sample_rate;
456 pca_status.scale =
457 (pca_status.sample_rate << 8) / INTERRUPT_RATE;
458 }
459 if (auptr->play.encoding != (unsigned int)~0) {
460 pca_status.encoding = auptr->play.encoding;
461 }
462 if (auptr->play.gain != (unsigned int)~0) {
463 pca_status.volume = auptr->play.gain;
464 pca_volume(pca_status.volume);
465 }
466 if (auptr->play.pause != (unsigned char)~0) {
467 if (auptr->play.pause)
468 pca_pause();
469 else
470 pca_continue();
471 }
472
473 return 0;
474
475 case AUDIO_DRAIN:
476 case AUDIO_COMPAT_DRAIN:
477 return pca_wait();
478
479 case AUDIO_FLUSH:
480 case AUDIO_COMPAT_FLUSH:
481 pca_stop();
482 return 0;
483 case FIONBIO:
484 return 0;
485 }
486 return ENXIO;
487 }
488
489
490 static void
491 pcaintr(struct clockframe *frame)
492 {
493 if (pca_status.index < pca_status.in_use[pca_status.current]) {
494 disable_intr();
495 __asm__("outb %0,$0x61\n"
496 "andb $0xFE,%0\n"
497 "outb %0,$0x61"
498 : : "a" ((char)pca_status.oldval) );
499 __asm__("xlatb\n"
500 "outb %0,$0x42"
501 : : "a" ((char)pca_status.buffer[pca_status.index]),
502 "b" (volume_table) );
503 enable_intr();
504 pca_status.counter += pca_status.scale;
505 pca_status.index = (pca_status.counter >> 8);
506 }
507 if (pca_status.index >= pca_status.in_use[pca_status.current]) {
508 pca_status.index = pca_status.counter = 0;
509 pca_status.in_use[pca_status.current] = 0;
510 pca_status.current++;
511 if (pca_status.current > 2)
512 pca_status.current = 0;
513 pca_status.buffer = pca_status.buf[pca_status.current];
514 if (pca_sleep)
515 wakeup(&pca_sleep);
516 if (pca_status.wsel.si_pid) {
517 selwakeup((struct selinfo *)&pca_status.wsel.si_pid);
518 pca_status.wsel.si_pid = 0;
519 pca_status.wsel.si_flags = 0;
520 }
521 }
522 }
523
524
525 static int
526 pcapoll(dev_t dev, int events, struct proc *p)
527 {
528 int s;
529 struct proc *p1;
530 int revents = 0;
531
532 s = spltty();
533
534 if (events & (POLLOUT | POLLWRNORM))
535 if (!pca_status.in_use[0] || !pca_status.in_use[1] ||
536 !pca_status.in_use[2])
537 revents |= events & (POLLOUT | POLLWRNORM);
538 else {
539 if (pca_status.wsel.si_pid &&
540 (p1=pfind(pca_status.wsel.si_pid))
541 && p1->p_wchan == (caddr_t)&selwait)
542 pca_status.wsel.si_flags = SI_COLL;
543 else
544 pca_status.wsel.si_pid = p->p_pid;
545 }
546
547 splx(s);
548 return (revents);
549 }
550
551 static pca_devsw_installed = 0;
552
553 static void pca_drvinit(void *unused)
554 {
555 dev_t dev;
556
557 if( ! pca_devsw_installed ) {
558 dev = makedev(CDEV_MAJOR, 0);
559 cdevsw_add(&dev,&pca_cdevsw, NULL);
560 pca_devsw_installed = 1;
561 }
562 }
563
564 SYSINIT(pcadev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,pca_drvinit,NULL)
565
566 #endif
Cache object: 7c5da39705e0e4b9b3dd52653be9e933
|