xref: /plan9-contrib/sys/src/9/port/devaudio.c (revision 5e4924093ecb86f7174bf23023955abc83fb6962)
1 /*
2  *	SB 16 driver
3  */
4 #include	"u.h"
5 #include	"../port/lib.h"
6 #include	"mem.h"
7 #include	"dat.h"
8 #include	"fns.h"
9 #include	"../port/error.h"
10 #include	"io.h"
11 #include	"audio.h"
12 
13 typedef struct	AQueue	AQueue;
14 typedef struct	Buf	Buf;
15 
16 enum
17 {
18 	Qdir		= 0,
19 	Qaudio,
20 	Qvolume,
21 	Qstatus,
22 
23 	Fmono		= 1,
24 	Fin		= 2,
25 	Fout		= 4,
26 
27 	Aclosed		= 0,
28 	Aread,
29 	Awrite,
30 
31 	Vaudio		= 0,
32 	Vsynth,
33 	Vcd,
34 	Vline,
35 	Vmic,
36 	Vspeaker,
37 	Vtreb,
38 	Vbass,
39 	Vspeed,
40 	Nvol,
41 
42 	Speed		= 44100,
43 	Ncmd		= 50,		/* max volume command words */
44 };
45 
46 Dirtab
47 audiodir[] =
48 {
49 	".",	{Qdir, 0, QTDIR},		0,	DMDIR|0555,
50 	"audio",	{Qaudio},		0,	0666,
51 	"volume",	{Qvolume},		0,	0666,
52 	"audiostat",{Qstatus},		0,	0444,
53 };
54 
55 struct	Buf
56 {
57 	uchar*	virt;
58 	ulong	phys;
59 	Buf*	next;
60 };
61 struct	AQueue
62 {
63 	Lock;
64 	Buf*	first;
65 	Buf*	last;
66 };
67 static	struct
68 {
69 	QLock;
70 	Rendez	vous;
71 	int	buffered;		/* number of bytes en route */
72 	int	bufinit;		/* boolean if buffers allocated */
73 	int	curcount;		/* how much data in current buffer */
74 	int	active;		/* boolean dma running */
75 	int	intr;			/* boolean an interrupt has happened */
76 	int	amode;		/* Aclosed/Aread/Awrite for /audio */
77 	int	rivol[Nvol];	/* right/left input/output volumes */
78 	int	livol[Nvol];
79 	int	rovol[Nvol];
80 	int	lovol[Nvol];
81 	int	major;		/* SB16 major version number (sb 4) */
82 	int	minor;		/* SB16 minor version number */
83 	ulong	totcount;	/* how many bytes processed since open */
84 	vlong	tottime;	/* time at which totcount bytes were processed */
85 
86 	Buf	buf[Nbuf];		/* buffers and queues */
87 	AQueue	empty;
88 	AQueue	full;
89 	Buf*	current;
90 	Buf*	filling;
91 } audio;
92 
93 static	struct
94 {
95 	char*	name;
96 	int	flag;
97 	int	ilval;		/* initial values */
98 	int	irval;
99 } volumes[] =
100 {
101 [Vaudio]		"audio",	Fout, 		50,	50,
102 [Vsynth]		"synth",	Fin|Fout,	0,	0,
103 [Vcd]		"cd",		Fin|Fout,	0,	0,
104 [Vline]		"line",	Fin|Fout,	0,	0,
105 [Vmic]		"mic",	Fin|Fout|Fmono,	0,	0,
106 [Vspeaker]	"speaker",	Fout|Fmono,	0,	0,
107 
108 [Vtreb]		"treb",		Fout, 		50,	50,
109 [Vbass]		"bass",		Fout, 		50,	50,
110 
111 [Vspeed]	"speed",	Fin|Fout|Fmono,	Speed,	Speed,
112 		0
113 };
114 
115 static struct
116 {
117 	Lock;
118 	int	reset;		/* io ports to the sound blaster */
119 	int	read;
120 	int	write;
121 	int	wstatus;
122 	int	rstatus;
123 	int	mixaddr;
124 	int	mixdata;
125 	int	clri8;
126 	int	clri16;
127 	int	clri401;
128 	int	dma;
129 
130 	void	(*startdma)(void);
131 	void	(*intr)(void);
132 } blaster;
133 
134 static	void	swab(uchar*);
135 
136 static	char	Emajor[]	= "soundblaster not responding/wrong version";
137 static	char	Emode[]		= "illegal open mode";
138 static	char	Evolume[]	= "illegal volume specifier";
139 
140 static	int
141 sbcmd(int val)
142 {
143 	int i, s;
144 
145 	for(i=1<<16; i!=0; i--) {
146 		s = inb(blaster.wstatus);
147 		if((s & 0x80) == 0) {
148 			outb(blaster.write, val);
149 			return 0;
150 		}
151 	}
152 /*	print("#A: sbcmd (0x%.2x) timeout\n", val);	/**/
153 	return 1;
154 }
155 
156 static	int
157 sbread(void)
158 {
159 	int i, s;
160 
161 	for(i=1<<16; i!=0; i--) {
162 		s = inb(blaster.rstatus);
163 		if((s & 0x80) != 0) {
164 			return inb(blaster.read);
165 		}
166 	}
167 /*	print("#A: sbread did not respond\n");	/**/
168 	return -1;
169 }
170 
171 static int
172 ess1688w(int reg, int val)
173 {
174 	if(sbcmd(reg) || sbcmd(val))
175 		return 1;
176 
177 	return 0;
178 }
179 
180 static int
181 ess1688r(int reg)
182 {
183 	if(sbcmd(0xC0) || sbcmd(reg))
184 		return -1;
185 
186 	return sbread();
187 }
188 
189 static	int
190 mxcmd(int addr, int val)
191 {
192 
193 	outb(blaster.mixaddr, addr);
194 	outb(blaster.mixdata, val);
195 	return 1;
196 }
197 
198 static	int
199 mxread(int addr)
200 {
201 	int s;
202 
203 	outb(blaster.mixaddr, addr);
204 	s = inb(blaster.mixdata);
205 	return s;
206 }
207 
208 static	void
209 mxcmds(int s, int v)
210 {
211 
212 	if(v > 100)
213 		v = 100;
214 	if(v < 0)
215 		v = 0;
216 	mxcmd(s, (v*255)/100);
217 }
218 
219 static	void
220 mxcmdt(int s, int v)
221 {
222 
223 	if(v > 100)
224 		v = 100;
225 	if(v <= 0)
226 		mxcmd(s, 0);
227 	else
228 		mxcmd(s, 255-100+v);
229 }
230 
231 static	void
232 mxcmdu(int s, int v)
233 {
234 
235 	if(v > 100)
236 		v = 100;
237 	if(v <= 0)
238 		v = 0;
239 	mxcmd(s, 128-50+v);
240 }
241 
242 static	void
243 mxvolume(void)
244 {
245 	int *left, *right;
246 	int source;
247 
248 	if(audio.amode == Aread){
249 		left = audio.livol;
250 		right = audio.rivol;
251 	}else{
252 		left = audio.lovol;
253 		right = audio.rovol;
254 	}
255 
256 	ilock(&blaster);
257 
258 	mxcmd(0x30, 255);		/* left master */
259 	mxcmd(0x31, 255);		/* right master */
260 	mxcmd(0x3f, 0);		/* left igain */
261 	mxcmd(0x40, 0);		/* right igain */
262 	mxcmd(0x41, 0);		/* left ogain */
263 	mxcmd(0x42, 0);		/* right ogain */
264 
265 	mxcmds(0x32, left[Vaudio]);
266 	mxcmds(0x33, right[Vaudio]);
267 
268 	mxcmds(0x34, left[Vsynth]);
269 	mxcmds(0x35, right[Vsynth]);
270 
271 	mxcmds(0x36, left[Vcd]);
272 	mxcmds(0x37, right[Vcd]);
273 
274 	mxcmds(0x38, left[Vline]);
275 	mxcmds(0x39, right[Vline]);
276 
277 	mxcmds(0x3a, left[Vmic]);
278 	mxcmds(0x3b, left[Vspeaker]);
279 
280 	mxcmdu(0x44, left[Vtreb]);
281 	mxcmdu(0x45, right[Vtreb]);
282 
283 	mxcmdu(0x46, left[Vbass]);
284 	mxcmdu(0x47, right[Vbass]);
285 
286 	source = 0;
287 	if(left[Vsynth])
288 		source |= 1<<6;
289 	if(right[Vsynth])
290 		source |= 1<<5;
291 	if(left[Vaudio])
292 		source |= 1<<4;
293 	if(right[Vaudio])
294 		source |= 1<<3;
295 	if(left[Vcd])
296 		source |= 1<<2;
297 	if(right[Vcd])
298 		source |= 1<<1;
299 	if(left[Vmic])
300 		source |= 1<<0;
301 	if(audio.amode == Aread)
302 		mxcmd(0x3c, 0);		/* output switch */
303 	else
304 		mxcmd(0x3c, source);
305 	mxcmd(0x3d, source);		/* input left switch */
306 	mxcmd(0x3e, source);		/* input right switch */
307 	iunlock(&blaster);
308 }
309 
310 static	Buf*
311 getbuf(AQueue *q)
312 {
313 	Buf *b;
314 
315 	ilock(q);
316 	b = q->first;
317 	if(b)
318 		q->first = b->next;
319 	iunlock(q);
320 
321 	return b;
322 }
323 
324 static	void
325 putbuf(AQueue *q, Buf *b)
326 {
327 
328 	ilock(q);
329 	b->next = 0;
330 	if(q->first)
331 		q->last->next = b;
332 	else
333 		q->first = b;
334 	q->last = b;
335 	iunlock(q);
336 }
337 
338 /*
339  * move the dma to the next buffer
340  */
341 static	void
342 contindma(void)
343 {
344 	Buf *b;
345 
346 	if(!audio.active)
347 		goto shutdown;
348 
349 	b = audio.current;
350 	if(b){
351 		audio.totcount += Bufsize;
352 		audio.tottime = todget(nil);
353 	}
354 	if(audio.amode == Aread) {
355 		if(b){
356 			putbuf(&audio.full, b);
357 			audio.buffered += Bufsize;
358 		}
359 		b = getbuf(&audio.empty);
360 	} else {
361 		if(b){
362 			putbuf(&audio.empty, b);
363 			audio.buffered -= Bufsize;
364 		}
365 		b = getbuf(&audio.full);
366 	}
367 	audio.current = b;
368 	if(b == 0)
369 		goto shutdown;
370 
371 	if(dmasetup(blaster.dma, b->virt, Bufsize, audio.amode == Aread) >= 0)
372 		return;
373 	print("#A: dmasetup fail\n");
374 	putbuf(&audio.empty, b);
375 
376 shutdown:
377 	dmaend(blaster.dma);
378 	sbcmd(0xd9);				/* exit at end of count */
379 	sbcmd(0xd5);				/* pause */
380 	audio.curcount = 0;
381 	audio.active = 0;
382 }
383 
384 /*
385  * cause sb to get an interrupt per buffer.
386  * start first dma
387  */
388 static	void
389 sb16startdma(void)
390 {
391 	ulong count;
392 	int speed;
393 
394 	ilock(&blaster);
395 	dmaend(blaster.dma);
396 	if(audio.amode == Aread) {
397 		sbcmd(0x42);			/* input sampling rate */
398 		speed = audio.livol[Vspeed];
399 	} else {
400 		sbcmd(0x41);			/* output sampling rate */
401 		speed = audio.lovol[Vspeed];
402 	}
403 	sbcmd(speed>>8);
404 	sbcmd(speed);
405 
406 	count = (Bufsize >> 1) - 1;
407 	if(audio.amode == Aread)
408 		sbcmd(0xbe);			/* A/D, autoinit */
409 	else
410 		sbcmd(0xb6);			/* D/A, autoinit */
411 	sbcmd(0x30);				/* stereo, 16 bit */
412 	sbcmd(count);
413 	sbcmd(count>>8);
414 
415 	audio.active = 1;
416 	contindma();
417 	iunlock(&blaster);
418 }
419 
420 static int
421 ess1688reset(void)
422 {
423 	int i;
424 
425 	outb(blaster.reset, 3);
426 	delay(1);			/* >3 υs */
427 	outb(blaster.reset, 0);
428 	delay(1);
429 
430 	i = sbread();
431 	if(i != 0xAA) {
432 		print("#A: no response 0x%.2x\n", i);
433 		return 1;
434 	}
435 
436 	if(sbcmd(0xC6)){		/* extended mode */
437 		print("#A: barf 3\n");
438 		return 1;
439 	}
440 
441 	return 0;
442 }
443 
444 static	void
445 ess1688startdma(void)
446 {
447 	ulong count;
448 	int speed, x;
449 
450 	ilock(&blaster);
451 	dmaend(blaster.dma);
452 
453 	if(audio.amode == Awrite)
454 		ess1688reset();
455 	if(audio.amode == Aread)
456 		sbcmd(0xD3);			/* speaker off */
457 
458 	/*
459 	 * Set the speed.
460 	 */
461 	if(audio.amode == Aread)
462 		speed = audio.livol[Vspeed];
463 	else
464 		speed = audio.lovol[Vspeed];
465 	if(speed < 4000)
466 		speed = 4000;
467 	else if(speed > 48000)
468 		speed = 48000;
469 
470 	if(speed > 22000)
471 		  x = 0x80|(256-(795500+speed/2)/speed);
472 	else
473 		  x = 128-(397700+speed/2)/speed;
474 	ess1688w(0xA1, x & 0xFF);
475 
476 	speed = (speed * 9) / 20;
477 	x = 256 - 7160000 / (speed * 82);
478 	ess1688w(0xA2, x & 0xFF);
479 
480 	if(audio.amode == Aread)
481 		ess1688w(0xB8, 0x0E);		/* A/D, autoinit */
482 	else
483 		ess1688w(0xB8, 0x04);		/* D/A, autoinit */
484 	x = ess1688r(0xA8) & ~0x03;
485 	ess1688w(0xA8, x|0x01);			/* 2 channels */
486 	ess1688w(0xB9, 2);			/* demand mode, 4 bytes per request */
487 
488 	if(audio.amode == Awrite)
489 		ess1688w(0xB6, 0);
490 	ess1688w(0xB7, 0x71);
491 	ess1688w(0xB7, 0xBC);
492 
493 	x = ess1688r(0xB1) & 0x0F;
494 	ess1688w(0xB1, x|0x50);
495 	x = ess1688r(0xB2) & 0x0F;
496 	ess1688w(0xB2, x|0x50);
497 	if(audio.amode == Awrite)
498 		sbcmd(0xD1);			/* speaker on */
499 
500 	count = -Bufsize;
501 	ess1688w(0xA4, count & 0xFF);
502 	ess1688w(0xA5, (count>>8) & 0xFF);
503 	x = ess1688r(0xB8);
504 	ess1688w(0xB8, x|0x05);
505 
506 	audio.active = 1;
507 	contindma();
508 	iunlock(&blaster);
509 }
510 
511 /*
512  * if audio is stopped,
513  * start it up again.
514  */
515 static	void
516 pokeaudio(void)
517 {
518 	if(!audio.active)
519 		blaster.startdma();
520 }
521 
522 static void
523 sb16intr(void)
524 {
525 	int stat, dummy;
526 
527 	stat = mxread(0x82) & 7;		/* get irq status */
528 	if(stat) {
529 		dummy = 0;
530 		if(stat & 2) {
531 			ilock(&blaster);
532 			dummy = inb(blaster.clri16);
533 			contindma();
534 			iunlock(&blaster);
535 			audio.intr = 1;
536 			wakeup(&audio.vous);
537 		}
538 		if(stat & 1) {
539 			dummy = inb(blaster.clri8);
540 		}
541 		if(stat & 4) {
542 			dummy = inb(blaster.clri401);
543 		}
544 		USED(dummy);
545 	}
546 }
547 
548 static void
549 ess1688intr(void)
550 {
551 	int dummy;
552 
553 	if(audio.active){
554 		ilock(&blaster);
555 		contindma();
556 		dummy = inb(blaster.clri8);
557 		iunlock(&blaster);
558 		audio.intr = 1;
559 		wakeup(&audio.vous);
560 		USED(dummy);
561 	}
562 	else
563 		print("#A: unexpected ess1688 interrupt\n");
564 }
565 
566 void
567 audiosbintr(void)
568 {
569 	/*
570 	 * Carrera interrupt interface.
571 	 */
572 	blaster.intr();
573 }
574 
575 static void
576 pcaudiosbintr(Ureg*, void*)
577 {
578 	/*
579 	 * x86 interrupt interface.
580 	 */
581 	blaster.intr();
582 }
583 
584 void
585 audiodmaintr(void)
586 {
587 /*	print("#A: dma interrupt\n");	/**/
588 }
589 
590 static int
591 anybuf(void*)
592 {
593 	return audio.intr;
594 }
595 
596 /*
597  * wait for some output to get
598  * empty buffers back.
599  */
600 static void
601 waitaudio(void)
602 {
603 
604 	audio.intr = 0;
605 	pokeaudio();
606 	tsleep(&audio.vous, anybuf, 0, 10000);
607 	if(audio.intr == 0) {
608 /*		print("#A: audio timeout\n");	/**/
609 		audio.active = 0;
610 		pokeaudio();
611 	}
612 }
613 
614 static void
615 sbbufinit(void)
616 {
617 	int i;
618 	uchar *p;
619 
620 	p = (uchar*)(((ulong)xalloc((Nbuf+1) * Bufsize) + Bufsize-1)&~(Bufsize-1));
621 	for(i=0; i<Nbuf; i++) {
622 		dcflush(p, Bufsize);
623 		audio.buf[i].virt = UNCACHED(uchar, p);
624 		audio.buf[i].phys = (ulong)PADDR(p);
625 		p += Bufsize;
626 	}
627 }
628 
629 static	void
630 setempty(void)
631 {
632 	int i;
633 
634 	ilock(&blaster);
635 	audio.empty.first = 0;
636 	audio.empty.last = 0;
637 	audio.full.first = 0;
638 	audio.full.last = 0;
639 	audio.current = 0;
640 	audio.filling = 0;
641 	audio.buffered = 0;
642 	for(i=0; i<Nbuf; i++)
643 		putbuf(&audio.empty, &audio.buf[i]);
644 	audio.totcount = 0;
645 	audio.tottime = 0LL;
646 	iunlock(&blaster);
647 }
648 
649 static	void
650 resetlevel(void)
651 {
652 	int i;
653 
654 	for(i=0; volumes[i].name; i++) {
655 		audio.lovol[i] = volumes[i].ilval;
656 		audio.rovol[i] = volumes[i].irval;
657 		audio.livol[i] = volumes[i].ilval;
658 		audio.rivol[i] = volumes[i].irval;
659 	}
660 }
661 
662 static int
663 ess1688(ISAConf* sbconf)
664 {
665 	int i, major, minor;
666 
667 	/*
668 	 * Try for ESS1688.
669 	 */
670 	sbcmd(0xE7);			/* get version */
671 	major = sbread();
672 	minor = sbread();
673 	if(major != 0x68 || minor != 0x8B){
674 		print("#A: model 0x%.2x 0x%.2x; not ESS1688 compatible\n", major, minor);
675 		return 1;
676 	}
677 
678 	ess1688reset();
679 
680 	switch(sbconf->irq){
681 	case 2:
682 	case 9:
683 		i = 0x50|(0<<2);
684 		break;
685 	case 5:
686 		i = 0x50|(1<<2);
687 		break;
688 	case 7:
689 		i = 0x50|(2<<2);
690 		break;
691 	case 10:
692 		i = 0x50|(3<<2);
693 		break;
694 	default:
695 		print("#A: bad ESS1688 irq %d\n", sbconf->irq);
696 		return 1;
697 	}
698 	ess1688w(0xB1, i);
699 
700 	switch(sbconf->dma){
701 	case 0:
702 		i = 0x50|(1<<2);
703 		break;
704 	case 1:
705 		i = 0xF0|(2<<2);
706 		break;
707 	case 3:
708 		i = 0x50|(3<<2);
709 		break;
710 	default:
711 		print("#A: bad ESS1688 dma %lud\n", sbconf->dma);
712 		return 1;
713 	}
714 	ess1688w(0xB2, i);
715 
716 	ess1688reset();
717 
718 	blaster.startdma = ess1688startdma;
719 	blaster.intr = ess1688intr;
720 
721 	return 0;
722 }
723 
724 static void
725 audioinit(void)
726 {
727 	ISAConf sbconf;
728 	int i, x;
729 	static int irq[] = {2,5,7,10};
730 
731 	sbconf.port = 0x220;
732 	sbconf.dma = Dma;
733 	sbconf.irq = IrqAUDIO;
734 	if(isaconfig("audio", 0, &sbconf) == 0)
735 		return;
736 	if(sbconf.type == nil ||
737 		(cistrcmp(sbconf.type, "sb16") != 0 &&
738 		 cistrcmp(sbconf.type, "ess1688") != 0))
739 		return;
740 	switch(sbconf.port){
741 	case 0x220:
742 	case 0x240:
743 	case 0x260:
744 	case 0x280:
745 		break;
746 	default:
747 		print("#A: bad port 0x%lux\n", sbconf.port);
748 		return;
749 	}
750 
751 	if(ioalloc(sbconf.port, 0x10, 0, "audio") < 0){
752 		print("#A: cannot ioalloc range %lux+0x10\n", sbconf.port);
753 		return;
754 	}
755 	if(ioalloc(sbconf.port+0x100, 1, 0, "audio.mpu401") < 0){
756 		iofree(sbconf.port);
757 		print("#A: cannot ioalloc range %lux+0x01\n", sbconf.port+0x100);
758 		return;
759 	}
760 
761 	switch(sbconf.irq){
762 	case 2:
763 	case 5:
764 	case 7:
765 	case 9:
766 	case 10:
767 		break;
768 	default:
769 		print("#A: bad irq %d\n", sbconf.irq);
770 		iofree(sbconf.port);
771 		iofree(sbconf.port+0x100);
772 		return;
773 	}
774 
775 	blaster.reset = sbconf.port + 0x6;
776 	blaster.read = sbconf.port + 0xa;
777 	blaster.write = sbconf.port + 0xc;
778 	blaster.wstatus = sbconf.port + 0xc;
779 	blaster.rstatus = sbconf.port + 0xe;
780 	blaster.mixaddr = sbconf.port + 0x4;
781 	blaster.mixdata = sbconf.port + 0x5;
782 	blaster.clri8 = sbconf.port + 0xe;
783 	blaster.clri16 = sbconf.port + 0xf;
784 	blaster.clri401 = sbconf.port + 0x100;
785 	blaster.dma = sbconf.dma;
786 
787 	blaster.startdma = sb16startdma;
788 	blaster.intr = sb16intr;
789 
790 	audio.amode = Aclosed;
791 	resetlevel();
792 
793 	outb(blaster.reset, 1);
794 	delay(1);			/* >3 υs */
795 	outb(blaster.reset, 0);
796 	delay(1);
797 
798 	i = sbread();
799 	if(i != 0xaa) {
800 		print("#A: no response #%.2x\n", i);
801 		iofree(sbconf.port);
802 		iofree(sbconf.port+0x100);
803 		return;
804 	}
805 
806 	sbcmd(0xe1);			/* get version */
807 	audio.major = sbread();
808 	audio.minor = sbread();
809 
810 	if(audio.major != 4) {
811 		if(audio.major != 3 || audio.minor != 1 || ess1688(&sbconf)){
812 			print("#A: model 0x%.2x 0x%.2x; not SB 16 compatible\n",
813 				audio.major, audio.minor);
814 			iofree(sbconf.port);
815 			iofree(sbconf.port+0x100);
816 			return;
817 		}
818 		audio.major = 4;
819 	}
820 
821 	/*
822 	 * initialize the mixer
823 	 */
824 	mxcmd(0x00, 0);			/* Reset mixer */
825 	mxvolume();
826 
827 	/*
828 	 * Attempt to set IRQ/DMA channels.
829 	 * On old ISA boards, these registers are writable.
830 	 * On Plug-n-Play boards, these are read-only.
831 	 *
832 	 * To accomodate both, we write to the registers,
833 	 * but then use the contents in case the write is
834 	 * disallowed.
835 	 */
836 	mxcmd(0x80,			/* irq */
837 		(sbconf.irq==2)? 1:
838 		(sbconf.irq==5)? 2:
839 		(sbconf.irq==7)? 4:
840 		(sbconf.irq==9)? 1:
841 		(sbconf.irq==10)? 8:
842 		0);
843 
844 	mxcmd(0x81, 1<<blaster.dma);	/* dma */
845 
846 	x = mxread(0x81);
847 	for(i=5; i<=7; i++)
848 		if(x & (1<<i)){
849 			blaster.dma = i;
850 			break;
851 		}
852 
853 	x = mxread(0x80);
854 	for(i=0; i<=3; i++)
855 		if(x & (1<<i)){
856 			sbconf.irq = irq[i];
857 			break;
858 		}
859 
860 	seteisadma(blaster.dma, audiodmaintr);
861 	setvec(Int0vec+sbconf.irq, pcaudiosbintr, 0);
862 }
863 
864 static Chan*
865 audioattach(char *param)
866 {
867 	return devattach('A', param);
868 }
869 
870 static Walkqid*
871 audiowalk(Chan *c, Chan *nc, char **name, int nname)
872 {
873 	return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
874 }
875 
876 static int
877 audiostat(Chan *c, uchar *db, int n)
878 {
879 	audiodir[Qaudio].length = audio.buffered;
880 	return devstat(c, db, n, audiodir, nelem(audiodir), devgen);
881 }
882 
883 static Chan*
884 audioopen(Chan *c, int omode)
885 {
886 	int amode;
887 
888 	if(audio.major != 4)
889 		error(Emajor);
890 
891 	switch((ulong)c->qid.path) {
892 	default:
893 		error(Eperm);
894 		break;
895 
896 	case Qstatus:
897 		if((omode&7) != OREAD)
898 			error(Eperm);
899 	case Qvolume:
900 	case Qdir:
901 		break;
902 
903 	case Qaudio:
904 		amode = Awrite;
905 		if((omode&7) == OREAD)
906 			amode = Aread;
907 		qlock(&audio);
908 		if(audio.amode != Aclosed){
909 			qunlock(&audio);
910 			error(Einuse);
911 		}
912 		if(audio.bufinit == 0) {
913 			audio.bufinit = 1;
914 			sbbufinit();
915 		}
916 		audio.amode = amode;
917 		setempty();
918 		audio.curcount = 0;
919 		qunlock(&audio);
920 		mxvolume();
921 		break;
922 	}
923 	c = devopen(c, omode, audiodir, nelem(audiodir), devgen);
924 	c->mode = openmode(omode);
925 	c->flag |= COPEN;
926 	c->offset = 0;
927 
928 	return c;
929 }
930 
931 static void
932 audioclose(Chan *c)
933 {
934 	Buf *b;
935 
936 	switch((ulong)c->qid.path) {
937 	default:
938 		error(Eperm);
939 		break;
940 
941 	case Qdir:
942 	case Qvolume:
943 	case Qstatus:
944 		break;
945 
946 	case Qaudio:
947 		if(c->flag & COPEN) {
948 			qlock(&audio);
949 			if(audio.amode == Awrite) {
950 				/* flush out last partial buffer */
951 				b = audio.filling;
952 				if(b) {
953 					audio.filling = 0;
954 					memset(b->virt+audio.curcount, 0, Bufsize-audio.curcount);
955 					audio.buffered += Bufsize-audio.curcount;
956 					swab(b->virt);
957 					putbuf(&audio.full, b);
958 				}
959 				if(!audio.active && audio.full.first)
960 					pokeaudio();
961 			}
962 			audio.amode = Aclosed;
963 			if(waserror()){
964 				qunlock(&audio);
965 				nexterror();
966 			}
967 			while(audio.active)
968 				waitaudio();
969 			setempty();
970 			poperror();
971 			qunlock(&audio);
972 		}
973 		break;
974 	}
975 }
976 
977 static long
978 audioread(Chan *c, void *v, long n, vlong off)
979 {
980 	int liv, riv, lov, rov;
981 	long m, n0;
982 	char buf[300];
983 	Buf *b;
984 	int j;
985 	ulong offset = off;
986 	char *a;
987 
988 	n0 = n;
989 	a = v;
990 	switch((ulong)c->qid.path) {
991 	default:
992 		error(Eperm);
993 		break;
994 
995 	case Qdir:
996 		return devdirread(c, a, n, audiodir, nelem(audiodir), devgen);
997 
998 	case Qaudio:
999 		if(audio.amode != Aread)
1000 			error(Emode);
1001 		qlock(&audio);
1002 		if(waserror()){
1003 			qunlock(&audio);
1004 			nexterror();
1005 		}
1006 		while(n > 0) {
1007 			b = audio.filling;
1008 			if(b == 0) {
1009 				b = getbuf(&audio.full);
1010 				if(b == 0) {
1011 					waitaudio();
1012 					continue;
1013 				}
1014 				audio.filling = b;
1015 				swab(b->virt);
1016 				audio.curcount = 0;
1017 			}
1018 			m = Bufsize-audio.curcount;
1019 			if(m > n)
1020 				m = n;
1021 			memmove(a, b->virt+audio.curcount, m);
1022 
1023 			audio.curcount += m;
1024 			n -= m;
1025 			a += m;
1026 			audio.buffered -= m;
1027 			if(audio.curcount >= Bufsize) {
1028 				audio.filling = 0;
1029 				putbuf(&audio.empty, b);
1030 			}
1031 		}
1032 		poperror();
1033 		qunlock(&audio);
1034 		break;
1035 
1036 	case Qstatus:
1037 		buf[0] = 0;
1038 		snprint(buf, sizeof(buf), "bufsize %6d buffered %6d offset  %10lud time %19lld\n",
1039 			Bufsize, audio.buffered, audio.totcount, audio.tottime);
1040 		return readstr(offset, a, n, buf);
1041 
1042 	case Qvolume:
1043 		j = 0;
1044 		buf[0] = 0;
1045 		for(m=0; volumes[m].name; m++){
1046 			liv = audio.livol[m];
1047 			riv = audio.rivol[m];
1048 			lov = audio.lovol[m];
1049 			rov = audio.rovol[m];
1050 			j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
1051 			if((volumes[m].flag & Fmono) || liv==riv && lov==rov){
1052 				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
1053 					j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
1054 				else{
1055 					if(volumes[m].flag & Fin)
1056 						j += snprint(buf+j, sizeof(buf)-j,
1057 							" in %d", liv);
1058 					if(volumes[m].flag & Fout)
1059 						j += snprint(buf+j, sizeof(buf)-j,
1060 							" out %d", lov);
1061 				}
1062 			}else{
1063 				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) &&
1064 				    liv==lov && riv==rov)
1065 					j += snprint(buf+j, sizeof(buf)-j,
1066 						" left %d right %d",
1067 						liv, riv);
1068 				else{
1069 					if(volumes[m].flag & Fin)
1070 						j += snprint(buf+j, sizeof(buf)-j,
1071 							" in left %d right %d",
1072 							liv, riv);
1073 					if(volumes[m].flag & Fout)
1074 						j += snprint(buf+j, sizeof(buf)-j,
1075 							" out left %d right %d",
1076 							lov, rov);
1077 				}
1078 			}
1079 			j += snprint(buf+j, sizeof(buf)-j, "\n");
1080 		}
1081 		return readstr(offset, a, n, buf);
1082 	}
1083 	return n0-n;
1084 }
1085 
1086 static long
1087 audiowrite(Chan *c, void *vp, long n, vlong)
1088 {
1089 	long m, n0;
1090 	int i, v, left, right, in, out;
1091 	Cmdbuf *cb;
1092 	Buf *b;
1093 	char *a;
1094 
1095 	a = vp;
1096 	n0 = n;
1097 	switch((ulong)c->qid.path) {
1098 	default:
1099 		error(Eperm);
1100 		break;
1101 
1102 	case Qvolume:
1103 		v = Vaudio;
1104 		left = 1;
1105 		right = 1;
1106 		in = 1;
1107 		out = 1;
1108 		cb = parsecmd(vp, n);
1109 		if(waserror()){
1110 			free(cb);
1111 			nexterror();
1112 		}
1113 
1114 		for(i = 0; i < cb->nf; i++){
1115 			/*
1116 			 * a number is volume
1117 			 */
1118 			if(cb->f[i][0] >= '0' && cb->f[i][0] <= '9') {
1119 				m = strtoul(cb->f[i], 0, 10);
1120 				if(left && out)
1121 					audio.lovol[v] = m;
1122 				if(left && in)
1123 					audio.livol[v] = m;
1124 				if(right && out)
1125 					audio.rovol[v] = m;
1126 				if(right && in)
1127 					audio.rivol[v] = m;
1128 				mxvolume();
1129 				goto cont0;
1130 			}
1131 
1132 			for(m=0; volumes[m].name; m++) {
1133 				if(strcmp(cb->f[i], volumes[m].name) == 0) {
1134 					v = m;
1135 					in = 1;
1136 					out = 1;
1137 					left = 1;
1138 					right = 1;
1139 					goto cont0;
1140 				}
1141 			}
1142 
1143 			if(strcmp(cb->f[i], "reset") == 0) {
1144 				resetlevel();
1145 				mxvolume();
1146 				goto cont0;
1147 			}
1148 			if(strcmp(cb->f[i], "in") == 0) {
1149 				in = 1;
1150 				out = 0;
1151 				goto cont0;
1152 			}
1153 			if(strcmp(cb->f[i], "out") == 0) {
1154 				in = 0;
1155 				out = 1;
1156 				goto cont0;
1157 			}
1158 			if(strcmp(cb->f[i], "left") == 0) {
1159 				left = 1;
1160 				right = 0;
1161 				goto cont0;
1162 			}
1163 			if(strcmp(cb->f[i], "right") == 0) {
1164 				left = 0;
1165 				right = 1;
1166 				goto cont0;
1167 			}
1168 			error(Evolume);
1169 			break;
1170 		cont0:;
1171 		}
1172 		free(cb);
1173 		poperror();
1174 		break;
1175 
1176 	case Qaudio:
1177 		if(audio.amode != Awrite)
1178 			error(Emode);
1179 		qlock(&audio);
1180 		if(waserror()){
1181 			qunlock(&audio);
1182 			nexterror();
1183 		}
1184 		while(n > 0) {
1185 			b = audio.filling;
1186 			if(b == 0) {
1187 				b = getbuf(&audio.empty);
1188 				if(b == 0) {
1189 					waitaudio();
1190 					continue;
1191 				}
1192 				audio.filling = b;
1193 				audio.curcount = 0;
1194 			}
1195 
1196 			m = Bufsize-audio.curcount;
1197 			if(m > n)
1198 				m = n;
1199 			memmove(b->virt+audio.curcount, a, m);
1200 
1201 			audio.curcount += m;
1202 			n -= m;
1203 			a += m;
1204 			audio.buffered += m;
1205 			if(audio.curcount >= Bufsize) {
1206 				audio.filling = 0;
1207 				swab(b->virt);
1208 				putbuf(&audio.full, b);
1209 				pokeaudio();
1210 			}
1211 		}
1212 		poperror();
1213 		qunlock(&audio);
1214 		break;
1215 	}
1216 	return n0 - n;
1217 }
1218 
1219 static	void
1220 swab(uchar *a)
1221 {
1222 	ulong *p, *ep, b;
1223 
1224 	if(!SBswab){
1225 		USED(a);
1226 		return;
1227 	}
1228 	p = (ulong*)a;
1229 	ep = p + (Bufsize>>2);
1230 	while(p < ep) {
1231 		b = *p;
1232 		b = (b>>24) | (b<<24) |
1233 			((b&0xff0000) >> 8) |
1234 			((b&0x00ff00) << 8);
1235 		*p++ = b;
1236 	}
1237 }
1238 
1239 Dev audiodevtab = {
1240 	'A',
1241 	"audio",
1242 
1243 	devreset,
1244 	audioinit,
1245 	devshutdown,
1246 	audioattach,
1247 	audiowalk,
1248 	audiostat,
1249 	audioopen,
1250 	devcreate,
1251 	audioclose,
1252 	audioread,
1253 	devbread,
1254 	audiowrite,
1255 	devbwrite,
1256 	devremove,
1257 	devwstat,
1258 };
1259