xref: /plan9/sys/src/9/port/devaudio.c (revision 567483c891f7c54442ce1d593e764767ee5fcaf7)
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
sbcmd(int val)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 (%#.2x) timeout\n", val);	/**/
153 	return 1;
154 }
155 
156 static	int
sbread(void)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
ess1688w(int reg,int val)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
ess1688r(int reg)181 ess1688r(int reg)
182 {
183 	if(sbcmd(0xC0) || sbcmd(reg))
184 		return -1;
185 
186 	return sbread();
187 }
188 
189 static	int
mxcmd(int addr,int val)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
mxread(int addr)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
mxcmds(int s,int v)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
mxcmdt(int s,int v)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
mxcmdu(int s,int v)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
mxvolume(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*
getbuf(AQueue * q)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
putbuf(AQueue * q,Buf * b)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
contindma(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
sb16startdma(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
ess1688reset(void)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 %#.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
ess1688startdma(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
pokeaudio(void)516 pokeaudio(void)
517 {
518 	if(!audio.active)
519 		blaster.startdma();
520 }
521 
522 static void
sb16intr(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
ess1688intr(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
audiosbintr(void)567 audiosbintr(void)
568 {
569 	/*
570 	 * Carrera interrupt interface.
571 	 */
572 	blaster.intr();
573 }
574 
575 static void
pcaudiosbintr(Ureg *,void *)576 pcaudiosbintr(Ureg*, void*)
577 {
578 	/*
579 	 * x86 interrupt interface.
580 	 */
581 	blaster.intr();
582 }
583 
584 void
audiodmaintr(void)585 audiodmaintr(void)
586 {
587 /*	print("#A: dma interrupt\n");	/**/
588 }
589 
590 static int
anybuf(void *)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
waitaudio(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
sbbufinit(void)615 sbbufinit(void)
616 {
617 	int i;
618 	uchar *p;
619 
620 	p = (uchar*)(((ulong)xalloc((Nbuf+1) * Bufsize) + Bufsize-1) &
621 		~(Bufsize-1));
622 	if (p == nil)
623 		panic("sbbufinit: no memory");
624 	for(i=0; i<Nbuf; i++) {
625 		dcflush(p, Bufsize);
626 		audio.buf[i].virt = UNCACHED(uchar, p);
627 		audio.buf[i].phys = (ulong)PADDR(p);
628 		p += Bufsize;
629 	}
630 }
631 
632 static	void
setempty(void)633 setempty(void)
634 {
635 	int i;
636 
637 	ilock(&blaster);
638 	audio.empty.first = 0;
639 	audio.empty.last = 0;
640 	audio.full.first = 0;
641 	audio.full.last = 0;
642 	audio.current = 0;
643 	audio.filling = 0;
644 	audio.buffered = 0;
645 	for(i=0; i<Nbuf; i++)
646 		putbuf(&audio.empty, &audio.buf[i]);
647 	audio.totcount = 0;
648 	audio.tottime = 0LL;
649 	iunlock(&blaster);
650 }
651 
652 static	void
resetlevel(void)653 resetlevel(void)
654 {
655 	int i;
656 
657 	for(i=0; volumes[i].name; i++) {
658 		audio.lovol[i] = volumes[i].ilval;
659 		audio.rovol[i] = volumes[i].irval;
660 		audio.livol[i] = volumes[i].ilval;
661 		audio.rivol[i] = volumes[i].irval;
662 	}
663 }
664 
665 static int
ess1688(ISAConf * sbconf)666 ess1688(ISAConf* sbconf)
667 {
668 	int i, major, minor;
669 
670 	/*
671 	 * Try for ESS1688.
672 	 */
673 	sbcmd(0xE7);			/* get version */
674 	major = sbread();
675 	minor = sbread();
676 	if(major != 0x68 || minor != 0x8B){
677 		print("#A: model %#.2x %#.2x; not ESS1688 compatible\n", major, minor);
678 		return 1;
679 	}
680 
681 	ess1688reset();
682 
683 	switch(sbconf->irq){
684 	case 2:
685 	case 9:
686 		i = 0x50|(0<<2);
687 		break;
688 	case 5:
689 		i = 0x50|(1<<2);
690 		break;
691 	case 7:
692 		i = 0x50|(2<<2);
693 		break;
694 	case 10:
695 		i = 0x50|(3<<2);
696 		break;
697 	default:
698 		print("#A: bad ESS1688 irq %d\n", sbconf->irq);
699 		return 1;
700 	}
701 	ess1688w(0xB1, i);
702 
703 	switch(sbconf->dma){
704 	case 0:
705 		i = 0x50|(1<<2);
706 		break;
707 	case 1:
708 		i = 0xF0|(2<<2);
709 		break;
710 	case 3:
711 		i = 0x50|(3<<2);
712 		break;
713 	default:
714 		print("#A: bad ESS1688 dma %lud\n", sbconf->dma);
715 		return 1;
716 	}
717 	ess1688w(0xB2, i);
718 
719 	ess1688reset();
720 
721 	blaster.startdma = ess1688startdma;
722 	blaster.intr = ess1688intr;
723 
724 	return 0;
725 }
726 
727 static void
audioinit(void)728 audioinit(void)
729 {
730 	ISAConf sbconf;
731 	int i, x;
732 	static int irq[] = {2,5,7,10};
733 
734 	sbconf.port = 0x220;
735 	sbconf.dma = Dma;
736 	sbconf.irq = IrqAUDIO;
737 	if(isaconfig("audio", 0, &sbconf) == 0)
738 		return;
739 	if(sbconf.type == nil ||
740 		(cistrcmp(sbconf.type, "sb16") != 0 &&
741 		 cistrcmp(sbconf.type, "ess1688") != 0))
742 		return;
743 	switch(sbconf.port){
744 	case 0x220:
745 	case 0x240:
746 	case 0x260:
747 	case 0x280:
748 		break;
749 	default:
750 		print("#A: bad port %#lux\n", sbconf.port);
751 		return;
752 	}
753 
754 	if(ioalloc(sbconf.port, 0x10, 0, "audio") < 0){
755 		print("#A: cannot ioalloc range %lux+0x10\n", sbconf.port);
756 		return;
757 	}
758 	if(ioalloc(sbconf.port+0x100, 1, 0, "audio.mpu401") < 0){
759 		iofree(sbconf.port);
760 		print("#A: cannot ioalloc range %lux+0x01\n", sbconf.port+0x100);
761 		return;
762 	}
763 
764 	switch(sbconf.irq){
765 	case 2:
766 	case 5:
767 	case 7:
768 	case 9:
769 	case 10:
770 		break;
771 	default:
772 		print("#A: bad irq %d\n", sbconf.irq);
773 		iofree(sbconf.port);
774 		iofree(sbconf.port+0x100);
775 		return;
776 	}
777 
778 	blaster.reset = sbconf.port + 0x6;
779 	blaster.read = sbconf.port + 0xa;
780 	blaster.write = sbconf.port + 0xc;
781 	blaster.wstatus = sbconf.port + 0xc;
782 	blaster.rstatus = sbconf.port + 0xe;
783 	blaster.mixaddr = sbconf.port + 0x4;
784 	blaster.mixdata = sbconf.port + 0x5;
785 	blaster.clri8 = sbconf.port + 0xe;
786 	blaster.clri16 = sbconf.port + 0xf;
787 	blaster.clri401 = sbconf.port + 0x100;
788 	blaster.dma = sbconf.dma;
789 
790 	blaster.startdma = sb16startdma;
791 	blaster.intr = sb16intr;
792 
793 	audio.amode = Aclosed;
794 	resetlevel();
795 
796 	outb(blaster.reset, 1);
797 	delay(1);			/* >3 υs */
798 	outb(blaster.reset, 0);
799 	delay(1);
800 
801 	i = sbread();
802 	if(i != 0xaa) {
803 		print("#A: no response #%.2x\n", i);
804 		iofree(sbconf.port);
805 		iofree(sbconf.port+0x100);
806 		return;
807 	}
808 
809 	sbcmd(0xe1);			/* get version */
810 	audio.major = sbread();
811 	audio.minor = sbread();
812 
813 	if(audio.major != 4) {
814 		if(audio.major != 3 || audio.minor != 1 || ess1688(&sbconf)){
815 			print("#A: model %#.2x %#.2x; not SB 16 compatible\n",
816 				audio.major, audio.minor);
817 			iofree(sbconf.port);
818 			iofree(sbconf.port+0x100);
819 			return;
820 		}
821 		audio.major = 4;
822 	}
823 
824 	/*
825 	 * initialize the mixer
826 	 */
827 	mxcmd(0x00, 0);			/* Reset mixer */
828 	mxvolume();
829 
830 	/*
831 	 * Attempt to set IRQ/DMA channels.
832 	 * On old ISA boards, these registers are writable.
833 	 * On Plug-n-Play boards, these are read-only.
834 	 *
835 	 * To accomodate both, we write to the registers,
836 	 * but then use the contents in case the write is
837 	 * disallowed.
838 	 */
839 	mxcmd(0x80,			/* irq */
840 		(sbconf.irq==2)? 1:
841 		(sbconf.irq==5)? 2:
842 		(sbconf.irq==7)? 4:
843 		(sbconf.irq==9)? 1:
844 		(sbconf.irq==10)? 8:
845 		0);
846 
847 	mxcmd(0x81, 1<<blaster.dma);	/* dma */
848 
849 	x = mxread(0x81);
850 	for(i=5; i<=7; i++)
851 		if(x & (1<<i)){
852 			blaster.dma = i;
853 			break;
854 		}
855 
856 	x = mxread(0x80);
857 	for(i=0; i<=3; i++)
858 		if(x & (1<<i)){
859 			sbconf.irq = irq[i];
860 			break;
861 		}
862 
863 	seteisadma(blaster.dma, audiodmaintr);
864 	setvec(Int0vec+sbconf.irq, pcaudiosbintr, 0);
865 }
866 
867 static Chan*
audioattach(char * param)868 audioattach(char *param)
869 {
870 	return devattach('A', param);
871 }
872 
873 static Walkqid*
audiowalk(Chan * c,Chan * nc,char ** name,int nname)874 audiowalk(Chan *c, Chan *nc, char **name, int nname)
875 {
876 	return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
877 }
878 
879 static int
audiostat(Chan * c,uchar * db,int n)880 audiostat(Chan *c, uchar *db, int n)
881 {
882 	audiodir[Qaudio].length = audio.buffered;
883 	return devstat(c, db, n, audiodir, nelem(audiodir), devgen);
884 }
885 
886 static Chan*
audioopen(Chan * c,int omode)887 audioopen(Chan *c, int omode)
888 {
889 	int amode;
890 
891 	if(audio.major != 4)
892 		error(Emajor);
893 
894 	switch((ulong)c->qid.path) {
895 	default:
896 		error(Eperm);
897 		break;
898 
899 	case Qstatus:
900 		if((omode&7) != OREAD)
901 			error(Eperm);
902 	case Qvolume:
903 	case Qdir:
904 		break;
905 
906 	case Qaudio:
907 		amode = Awrite;
908 		if((omode&7) == OREAD)
909 			amode = Aread;
910 		qlock(&audio);
911 		if(audio.amode != Aclosed){
912 			qunlock(&audio);
913 			error(Einuse);
914 		}
915 		if(audio.bufinit == 0) {
916 			audio.bufinit = 1;
917 			sbbufinit();
918 		}
919 		audio.amode = amode;
920 		setempty();
921 		audio.curcount = 0;
922 		qunlock(&audio);
923 		mxvolume();
924 		break;
925 	}
926 	c = devopen(c, omode, audiodir, nelem(audiodir), devgen);
927 	c->mode = openmode(omode);
928 	c->flag |= COPEN;
929 	c->offset = 0;
930 
931 	return c;
932 }
933 
934 static void
audioclose(Chan * c)935 audioclose(Chan *c)
936 {
937 	Buf *b;
938 
939 	switch((ulong)c->qid.path) {
940 	default:
941 		error(Eperm);
942 		break;
943 
944 	case Qdir:
945 	case Qvolume:
946 	case Qstatus:
947 		break;
948 
949 	case Qaudio:
950 		if(c->flag & COPEN) {
951 			qlock(&audio);
952 			if(audio.amode == Awrite) {
953 				/* flush out last partial buffer */
954 				b = audio.filling;
955 				if(b) {
956 					audio.filling = 0;
957 					memset(b->virt+audio.curcount, 0, Bufsize-audio.curcount);
958 					audio.buffered += Bufsize-audio.curcount;
959 					swab(b->virt);
960 					putbuf(&audio.full, b);
961 				}
962 				if(!audio.active && audio.full.first)
963 					pokeaudio();
964 			}
965 			audio.amode = Aclosed;
966 			if(waserror()){
967 				qunlock(&audio);
968 				nexterror();
969 			}
970 			while(audio.active)
971 				waitaudio();
972 			setempty();
973 			poperror();
974 			qunlock(&audio);
975 		}
976 		break;
977 	}
978 }
979 
980 static long
audioread(Chan * c,void * v,long n,vlong off)981 audioread(Chan *c, void *v, long n, vlong off)
982 {
983 	int liv, riv, lov, rov;
984 	long m, n0;
985 	char buf[300];
986 	Buf *b;
987 	int j;
988 	ulong offset = off;
989 	char *a;
990 
991 	n0 = n;
992 	a = v;
993 	switch((ulong)c->qid.path) {
994 	default:
995 		error(Eperm);
996 		break;
997 
998 	case Qdir:
999 		return devdirread(c, a, n, audiodir, nelem(audiodir), devgen);
1000 
1001 	case Qaudio:
1002 		if(audio.amode != Aread)
1003 			error(Emode);
1004 		qlock(&audio);
1005 		if(waserror()){
1006 			qunlock(&audio);
1007 			nexterror();
1008 		}
1009 		while(n > 0) {
1010 			b = audio.filling;
1011 			if(b == 0) {
1012 				b = getbuf(&audio.full);
1013 				if(b == 0) {
1014 					waitaudio();
1015 					continue;
1016 				}
1017 				audio.filling = b;
1018 				swab(b->virt);
1019 				audio.curcount = 0;
1020 			}
1021 			m = Bufsize-audio.curcount;
1022 			if(m > n)
1023 				m = n;
1024 			memmove(a, b->virt+audio.curcount, m);
1025 
1026 			audio.curcount += m;
1027 			n -= m;
1028 			a += m;
1029 			audio.buffered -= m;
1030 			if(audio.curcount >= Bufsize) {
1031 				audio.filling = 0;
1032 				putbuf(&audio.empty, b);
1033 			}
1034 		}
1035 		poperror();
1036 		qunlock(&audio);
1037 		break;
1038 
1039 	case Qstatus:
1040 		buf[0] = 0;
1041 		snprint(buf, sizeof(buf), "bufsize %6d buffered %6d offset  %10lud time %19lld\n",
1042 			Bufsize, audio.buffered, audio.totcount, audio.tottime);
1043 		return readstr(offset, a, n, buf);
1044 
1045 	case Qvolume:
1046 		j = 0;
1047 		buf[0] = 0;
1048 		for(m=0; volumes[m].name; m++){
1049 			liv = audio.livol[m];
1050 			riv = audio.rivol[m];
1051 			lov = audio.lovol[m];
1052 			rov = audio.rovol[m];
1053 			j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
1054 			if((volumes[m].flag & Fmono) || liv==riv && lov==rov){
1055 				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
1056 					j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
1057 				else{
1058 					if(volumes[m].flag & Fin)
1059 						j += snprint(buf+j, sizeof(buf)-j,
1060 							" in %d", liv);
1061 					if(volumes[m].flag & Fout)
1062 						j += snprint(buf+j, sizeof(buf)-j,
1063 							" out %d", lov);
1064 				}
1065 			}else{
1066 				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) &&
1067 				    liv==lov && riv==rov)
1068 					j += snprint(buf+j, sizeof(buf)-j,
1069 						" left %d right %d",
1070 						liv, riv);
1071 				else{
1072 					if(volumes[m].flag & Fin)
1073 						j += snprint(buf+j, sizeof(buf)-j,
1074 							" in left %d right %d",
1075 							liv, riv);
1076 					if(volumes[m].flag & Fout)
1077 						j += snprint(buf+j, sizeof(buf)-j,
1078 							" out left %d right %d",
1079 							lov, rov);
1080 				}
1081 			}
1082 			j += snprint(buf+j, sizeof(buf)-j, "\n");
1083 		}
1084 		return readstr(offset, a, n, buf);
1085 	}
1086 	return n0-n;
1087 }
1088 
1089 static long
audiowrite(Chan * c,void * vp,long n,vlong)1090 audiowrite(Chan *c, void *vp, long n, vlong)
1091 {
1092 	long m, n0;
1093 	int i, v, left, right, in, out;
1094 	Cmdbuf *cb;
1095 	Buf *b;
1096 	char *a;
1097 
1098 	a = vp;
1099 	n0 = n;
1100 	switch((ulong)c->qid.path) {
1101 	default:
1102 		error(Eperm);
1103 		break;
1104 
1105 	case Qvolume:
1106 		v = Vaudio;
1107 		left = 1;
1108 		right = 1;
1109 		in = 1;
1110 		out = 1;
1111 		cb = parsecmd(vp, n);
1112 		if(waserror()){
1113 			free(cb);
1114 			nexterror();
1115 		}
1116 
1117 		for(i = 0; i < cb->nf; i++){
1118 			/*
1119 			 * a number is volume
1120 			 */
1121 			if(cb->f[i][0] >= '0' && cb->f[i][0] <= '9') {
1122 				m = strtoul(cb->f[i], 0, 10);
1123 				if(left && out)
1124 					audio.lovol[v] = m;
1125 				if(left && in)
1126 					audio.livol[v] = m;
1127 				if(right && out)
1128 					audio.rovol[v] = m;
1129 				if(right && in)
1130 					audio.rivol[v] = m;
1131 				mxvolume();
1132 				goto cont0;
1133 			}
1134 
1135 			for(m=0; volumes[m].name; m++) {
1136 				if(strcmp(cb->f[i], volumes[m].name) == 0) {
1137 					v = m;
1138 					in = 1;
1139 					out = 1;
1140 					left = 1;
1141 					right = 1;
1142 					goto cont0;
1143 				}
1144 			}
1145 
1146 			if(strcmp(cb->f[i], "reset") == 0) {
1147 				resetlevel();
1148 				mxvolume();
1149 				goto cont0;
1150 			}
1151 			if(strcmp(cb->f[i], "in") == 0) {
1152 				in = 1;
1153 				out = 0;
1154 				goto cont0;
1155 			}
1156 			if(strcmp(cb->f[i], "out") == 0) {
1157 				in = 0;
1158 				out = 1;
1159 				goto cont0;
1160 			}
1161 			if(strcmp(cb->f[i], "left") == 0) {
1162 				left = 1;
1163 				right = 0;
1164 				goto cont0;
1165 			}
1166 			if(strcmp(cb->f[i], "right") == 0) {
1167 				left = 0;
1168 				right = 1;
1169 				goto cont0;
1170 			}
1171 			error(Evolume);
1172 			break;
1173 		cont0:;
1174 		}
1175 		free(cb);
1176 		poperror();
1177 		break;
1178 
1179 	case Qaudio:
1180 		if(audio.amode != Awrite)
1181 			error(Emode);
1182 		qlock(&audio);
1183 		if(waserror()){
1184 			qunlock(&audio);
1185 			nexterror();
1186 		}
1187 		while(n > 0) {
1188 			b = audio.filling;
1189 			if(b == 0) {
1190 				b = getbuf(&audio.empty);
1191 				if(b == 0) {
1192 					waitaudio();
1193 					continue;
1194 				}
1195 				audio.filling = b;
1196 				audio.curcount = 0;
1197 			}
1198 
1199 			m = Bufsize-audio.curcount;
1200 			if(m > n)
1201 				m = n;
1202 			memmove(b->virt+audio.curcount, a, m);
1203 
1204 			audio.curcount += m;
1205 			n -= m;
1206 			a += m;
1207 			audio.buffered += m;
1208 			if(audio.curcount >= Bufsize) {
1209 				audio.filling = 0;
1210 				swab(b->virt);
1211 				putbuf(&audio.full, b);
1212 				pokeaudio();
1213 			}
1214 		}
1215 		poperror();
1216 		qunlock(&audio);
1217 		break;
1218 	}
1219 	return n0 - n;
1220 }
1221 
1222 static	void
swab(uchar * a)1223 swab(uchar *a)
1224 {
1225 	ulong *p, *ep, b;
1226 
1227 	if(!SBswab){
1228 		USED(a);
1229 		return;
1230 	}
1231 	p = (ulong*)a;
1232 	ep = p + (Bufsize>>2);
1233 	while(p < ep) {
1234 		b = *p;
1235 		b = (b>>24) | (b<<24) |
1236 			((b&0xff0000) >> 8) |
1237 			((b&0x00ff00) << 8);
1238 		*p++ = b;
1239 	}
1240 }
1241 
1242 Dev audiodevtab = {
1243 	'A',
1244 	"audio",
1245 
1246 	devreset,
1247 	audioinit,
1248 	devshutdown,
1249 	audioattach,
1250 	audiowalk,
1251 	audiostat,
1252 	audioopen,
1253 	devcreate,
1254 	audioclose,
1255 	audioread,
1256 	devbread,
1257 	audiowrite,
1258 	devbwrite,
1259 	devremove,
1260 	devwstat,
1261 };
1262