xref: /inferno-os/os/js/devcs4231.c (revision 6e425a9de8c003b5a733621a6b6730ec3cc902b8)
1 /*
2  * preliminary Crystal CS4231 audio driver,
3  * initially based on SB16 driver, and therefore needs work.
4  * for instance, i suspect the linked-list buffering is excessive:
5  * a rolling buffering scheme or double buffering should be fine,
6  * and possibly simpler.
7  *
8  * To do:
9  *	stop/start?
10  *	is the linux mix_cvt ideal?
11  *	ad1845 differences
12  *	adpcm freezing
13  */
14 
15 #include	"u.h"
16 #include	"../port/lib.h"
17 #include	"mem.h"
18 #include	"dat.h"
19 #include	"fns.h"
20 #include	"../port/error.h"
21 #include	"devtab.h"
22 #include	"io.h"
23 #include	"audio.h"
24 
25 #define	DPRINT	if(chatty)print
26 
27 typedef struct	AChan	AChan;
28 typedef struct	AQueue	AQueue;
29 typedef struct	Buf	Buf;
30 typedef struct	Vol	Vol;
31 
32 enum
33 {
34 	Qdir		= 0,
35 	Qaudio,
36 	Qaudioctl,
37 
38 	Fmono		= 1,
39 	Fin		= 2,
40 	Fout		= 4,
41 
42 	Vaudio		= 0,
43 	Vaux1,
44 	Vaux2,
45 	Vline,
46 	Vmic,
47 	Vmono,
48 	Vspeed,
49 	Vchans,
50 	Vbits,
51 	Nvol,
52 
53 	Speed		= 22050,
54 	Ncmd		= 50,		/* max volume command words */
55 };
56 
57 enum {
58 	Paddr=	0,
59 		TRD=	1<<5,
60 		MCE=	1<<6,
61 	Pdata=	1,
62 	Pstatus=	2,
63 	Pio=		3,
64 
65 	LeftADC=	0,
66 		MGE=	1<<5,
67 		ISline=	0<<6,
68 		ISaux1=	1<<6,
69 		ISmic=	2<<6,
70 		ISloop=	3<<6,
71 		ISmask=	3<<6,
72 	RightADC= 1,
73 	LeftAux1= 2,
74 		Mute=	1<<7,
75 	RightAux1= 3,
76 	LeftAux2= 4,
77 	RightAux2= 5,
78 	LeftDAC= 6,
79 	RightDAC= 7,
80 	OutFormat=	8,
81 		Stereo=	1<<4,
82 		Linear8=	0<<5,
83 		uLaw=	1<<5,
84 		Linear16=	2<<5,
85 		aLaw=	3<<5,
86 		ADPCM=	5<<5,
87 		Fmask=	7<<5,
88 	Config=	9,
89 		PEN=	1<<0,
90 		CEN=	1<<1,
91 		Nocal=	0<<3,
92 		Convcal=	1<<3,
93 		DACcal=	2<<3,
94 		Fullcal=	3<<3,
95 	PinControl=	10,
96 		IEN=		1<<1,
97 		DEN=	1<<3,
98 		Xctl0=	1<<6,
99 		Xctl1=	1<<7,
100 	Status=	11,
101 		ACI=		1<<5,
102 	Mode=	12,
103 		Mode2=	1<<6,
104 	Loopback=	13,
105 		LBE=		1<<0,
106 	PlayCount1=	14,
107 	PlayCount0=	15,
108 	Feature1=	16,
109 		PMCE=	1<<4,
110 		CMCE=	1<<5,
111 	Feature2=	17,
112 	LeftLine=	18,
113 	RightLine=	19,
114 	Timer0=	20,
115 	Timer1=	21,
116 	Feature3=	23,
117 	FeatureStatus=	24,
118 		PI=	1<<4,	/* playback interrupt */
119 		CI=	1<<5,	/* capture interrupt */
120 		TI=	1<<6,	/* timer interrupt */
121 	ChipID= 25,
122 	MonoCtl=	26,
123 		MBY=	1<<5,	/* mono bypass */
124 		MOM=	1<<6,
125 	InFormat=	28,
126 	RecCount1=	30,
127 	RecCount0=	31,
128 };
129 
130 #define	csdelay()	microdelay(1)
131 
132 static Dirtab audiodir[] =
133 {
134 	"audio",	{Qaudio},		0,	0666,
135 	"audioctl",	{Qaudioctl},		0,	0666,
136 };
137 #define	NPORT		(sizeof audiodir/sizeof(Dirtab))
138 
139 struct Buf
140 {
141 	uchar*	virt;
142 	int	count;
143 	Buf*	next;
144 };
145 struct AQueue
146 {
147 	Lock;
148 	Buf*	first;
149 	Buf*	last;
150 };
151 struct AChan
152 {
153 	QLock;
154 	Rendez	r;
155 	Buf	buf[Nbuf];	/* buffers and queues */
156 	AQueue	empty;
157 	AQueue	full;
158 	Buf*	current;
159 	Buf*	filling;
160 	int	flushing;
161 };
162 static struct
163 {
164 	QLock;
165 	int	opened;
166 	int	bufinit;	/* boolean if buffers allocated */
167 	int	rivol[Nvol];		/* right/left input/output volumes */
168 	int	livol[Nvol];
169 	int	rovol[Nvol];
170 	int	lovol[Nvol];
171 	int	loopback;
172 
173 	AChan	in;
174 	AChan	out;
175 } audio;
176 
177 static	char*	encname(int);
178 
179 static	int	dacload(int, int);
180 static	int	auxload(int, int);
181 static	int	adcload(int, int);
182 static	int	monoload(int, int);
183 
184 struct Vol
185 {
186 	char*	name;
187 	int	flag;
188 	int	ilval;		/* initial values */
189 	int	irval;
190 	int	reg;
191 	int	(*load)(int, int);
192 };
193 
194 static	Vol	volumes[] = {
195 [Vaudio]	{"audio",	    Fout, 	50,	50,	LeftDAC, dacload},
196 [Vaux1]		{"aux1",		Fin,	0,	0,	LeftAux1, auxload},
197 [Vaux2]		{"aux2",		Fin,	0,	0,	LeftAux2, auxload},
198 [Vline]		{"line",		Fin,	0,	0,	LeftLine, auxload},
199 [Vmono]		{"mono",		Fin|Fout|Fmono,	0,	0,	MonoCtl, monoload},
200 [Vmic]		{"mic",		Fin,	0,	0,	LeftADC, adcload},
201 
202 [Vspeed]	{"rate",	Fin|Fout|Fmono,	Speed,	Speed,},
203 [Vchans]	{"chans",	Fin|Fout|Fmono,	2,	2,},
204 [Vbits]	{"bits", Fin|Fout|Fmono, 8, 8,},
205 	{0},
206 };
207 
208 static struct
209 {
210 	Lock;
211 	int	port;
212 	int	irq;
213 	uchar	sticky;
214 	uchar	regs[32];
215 } csdev;
216 
217 static	void	contininput(void);
218 static	void	continoutput(void);
219 
220 static	char	Evolume[]	= "illegal audioctl specifier";
221 
222 static	int	chatty;
223 
224 #include "cs4231.h"
225 
226 static int
227 xin(int r)
228 {
229 	int i;
230 
231 	for(i=100; --i >= 0 && IN(Paddr) & 0x80;)
232 		csdelay();
233 	OUT(Paddr, r|csdev.sticky);
234 	csdelay();
235 	return IN(Pdata);
236 }
237 
238 static void
239 xout(int r, int v)
240 {
241 	int i;
242 
243 	for(i=100; --i >= 0 && IN(Paddr) & 0x80;)
244 		csdelay();
245 	OUT(Paddr, r|csdev.sticky);
246 	csdelay();
247 	OUT(Pdata, v);
248 	//csdelay();
249 }
250 
251 static void
252 speaker(int on)
253 {
254 	int s;
255 
256 	s = xin(PinControl);
257 	if(on)
258 		s |= Xctl0;
259 	else
260 		s &= ~Xctl0;
261 	xout(PinControl, s);
262 }
263 
264 static Buf*
265 getbuf(AQueue *q)
266 {
267 	Buf *b;
268 
269 	ilock(q);
270 	b = q->first;
271 	if(b)
272 		q->first = b->next;
273 	iunlock(q);
274 
275 	return b;
276 }
277 
278 static void
279 putbuf(AQueue *q, Buf *b)
280 {
281 	ilock(q);
282 	b->next = 0;
283 	if(q->first)
284 		q->last->next = b;
285 	else
286 		q->first = b;
287 	q->last = b;
288 	iunlock(q);
289 }
290 
291 static void
292 achanreset(AChan *ac)
293 {
294 	int i;
295 
296 	ac->filling = 0;
297 	ac->flushing = 0;
298 	ac->current = 0;
299 	ac->empty.first = 0;
300 	ac->empty.last = 0;
301 	ac->full.first = 0;
302 	ac->full.last = 0;
303 	for(i=0; i<Nbuf; i++){
304 		ac->buf[i].count = 0;
305 		putbuf(&ac->empty, &ac->buf[i]);
306 	}
307 }
308 
309 static void
310 startoutput(void)
311 {
312 	ilock(&csdev);
313 	if(audio.out.current == 0)
314 		continoutput();
315 	iunlock(&csdev);
316 }
317 
318 static void
319 continoutput(void)
320 {
321 	Buf *b;
322 	int f;
323 	ulong n;
324 
325 	b = getbuf(&audio.out.full);
326 	audio.out.current = b;
327 	//xout(Config, xin(Config)&~PEN);
328 	if(b){
329 		n = b->count;
330 		dmasetup(Wdma, b->virt, n, 0);
331 		f = xin(OutFormat);
332 		if((f & Fmask) == ADPCM)
333 			n >>= 2;
334 		else{
335 			if((f & Fmask) == Linear16)
336 				n >>= 1;
337 			if(f & Stereo)
338 				n >>= 1;
339 		}
340 		n--;
341 		xout(PlayCount0, n);
342 		xout(PlayCount1, n>>8);
343 		xout(Config, xin(Config)|PEN);
344 		DPRINT("cs: out %d\n", n);
345 	} else
346 		xout(Config, xin(Config)&~PEN);
347 }
348 
349 static void
350 startinput(void)
351 {
352 	ilock(&csdev);
353 	if(audio.in.current == 0)
354 		contininput();
355 	iunlock(&csdev);
356 }
357 
358 static void
359 contininput(void)
360 {
361 	Buf *b;
362 	int f;
363 	ulong n;
364 
365 	xout(Config, xin(Config)&~CEN);
366 	if(!audio.opened || audio.in.flushing){
367 		return;
368 	}
369 	b = getbuf(&audio.in.empty);
370 	audio.in.current = b;
371 	if(b){
372 		n = Bufsize;
373 		dmasetup(Rdma, b->virt, Bufsize, 1);
374 		f = xin(InFormat);
375 		if((f & Fmask) == ADPCM)
376 			n >>= 2;
377 		else{
378 			if((f & Fmask) == Linear16)
379 				n >>= 1;
380 			if(f & Stereo)
381 				n >>= 1;
382 		}
383 		n--;
384 		xout(RecCount0, n);
385 		xout(RecCount1, n>>8);
386 		xout(Config, xin(Config)|CEN);
387 		DPRINT("cs: in %d\n", n);
388 	}
389 }
390 
391 static void
392 cswait(void)
393 {
394 	int i;
395 
396 	for(i=50; --i >= 0 && IN(Paddr) & 0x80;)
397 		microdelay(2000);
398 	if(i < 0)
399 		print("cswait1\n");
400 	for(i=1000; --i >= 0 && (xin(Status) & ACI) == 0;)
401 		csdelay();
402 	for(i=1000; --i >= 0 && xin(Status) & ACI;)
403 		microdelay(2000);
404 	/* could give error(Eio) if i < 0 */
405 	if(i < 0)
406 		print("cswait2\n");
407 }
408 
409 static int
410 csspeed(int freq)
411 {
412 	int i;
413 	static int freqtab[] = {	/* p. 33 CFS2-CFS0 */
414 		/* xtal1 xtal2 */
415 		8000, 5510,
416 		16000, 11025,
417 		27420, 18900,
418 		32000, 22050,
419 		0, 37800,
420 		0, 44100,
421 		48000, 33075,
422 		9600, 6620,
423 	};
424 	for(i=0; i<16; i++)
425 		if(freqtab[i] == freq){
426 			xout(OutFormat, (xin(OutFormat)&~0xF) | i);
427 			return 1;
428 		}
429 	return 0;
430 }
431 
432 static void
433 csformat(int r, int flag, int form, int *vec)
434 {
435 	int v;
436 
437 	if(form == Linear8){
438 		if(vec[Vbits] == 16)
439 			form = Linear16;
440 		else if(vec[Vbits] == 4)
441 			form = ADPCM;
442 	}
443 	if(vec[Vchans] == 2)
444 		form |= Stereo;
445 	DPRINT("csformat(%x,%x,%x)\n", r, flag, form);
446 	if((xin(r)&0xF0) != form){
447 		v = xin(Feature1);
448 		xout(Feature1, v|flag);
449 		xout(r, (xin(r)&~0xF0)|form);
450 		xout(Feature1, v);
451 	}
452 	csdev.regs[r] = form;
453 }
454 
455 static void
456 cs4231intr(Ureg*, void*)
457 {
458 	int ir, s;
459 	Buf *b;
460 
461 	lock(&csdev);
462 	csdev.sticky |= TRD;
463 	ir = IN(Pstatus);
464 	s = xin(FeatureStatus);
465 	if(s & PI){
466 		b = audio.out.current;
467 		audio.out.current = 0;
468 		dmaend(Wdma);
469 		continoutput();
470 		if(b)
471 			putbuf(&audio.out.empty, b);
472 		wakeup(&audio.out.r);
473 	}
474 	if(s & CI){
475 		b = audio.in.current;
476 		audio.in.current = 0;
477 		dmaend(Rdma);
478 		contininput();
479 		if(b){
480 			b->count = Bufsize;
481 			putbuf(&audio.in.full, b);
482 		}
483 		wakeup(&audio.in.r);
484 	}
485 	OUT(Pstatus, 0);
486 	csdev.sticky &= ~TRD;
487 	unlock(&csdev);
488 	if(s & 0xF)
489 		DPRINT("audiointr: #%x\n", s);
490 }
491 
492 static int
493 anybuf(void *p)
494 {
495 	return ((AChan*)p)->empty.first != 0;
496 }
497 
498 static int
499 anyinput(void *p)
500 {
501 	return ((AChan*)p)->full.first != 0;
502 }
503 
504 static int
505 outcomplete(void *p)
506 {
507 	return ((AChan*)p)->full.first == 0 && ((AChan*)p)->current==0;
508 }
509 
510 static int
511 incomplete(void *p)
512 {
513 	return ((AChan*)p)->current == 0;
514 }
515 
516 static void
517 acbufinit(AChan *ac)
518 {
519 	int i;
520 	void *p;
521 
522 	for(i=0; i<Nbuf; i++) {
523 		//p = xspanalloc(Bufsize, CACHELINESZ, 64*1024);
524 		//dcflush(p, Bufsize);
525 		p = xalloc(Bufsize);
526 		ac->buf[i].virt = UNCACHED(uchar, p);
527 	}
528 }
529 
530 static void
531 setempty(void)
532 {
533 	ilock(&csdev);
534 	achanreset(&audio.in);
535 	achanreset(&audio.out);
536 	iunlock(&csdev);
537 }
538 
539 void
540 cs4231reset(void)
541 {
542 }
543 
544 static char mix_cvt[101] = {
545 	0, 0,3,7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42,
546 	43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65,
547 	65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79,
548 	80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90,
549 	91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99,
550 	100
551 };
552 
553 static int
554 dacload(int r, int v)
555 {
556 	USED(r);
557 	DPRINT("dacload(%x,%d)\n", r, v);
558 	if(v == 0)
559 		return Mute;
560 	return 63-((v*63)/100);
561 }
562 
563 static int
564 monoload(int r, int v)
565 {
566 	DPRINT("monoload(%x,%d)\n", r, v);
567 	if(v == 0)
568 		return r|Mute;
569 	return (r&~(Mute|MBY))|(15-((v*15)/100));
570 }
571 
572 static int
573 auxload(int r, int v)
574 {
575 	DPRINT("auxload(%x,%d)\n", r, v);
576 	USED(r);
577 	if(v == 0)
578 		return Mute;
579 	return 31-(v*31)/100;
580 }
581 
582 static int
583 adcload(int r, int v)
584 {
585 	DPRINT("adcload(%x,%d)\n", r, v);
586 	return (r&~0xF)|((v*15)/100)|MGE;
587 }
588 
589 static void
590 mxvolume(void)
591 {
592 	Vol *v;
593 	int i, l, r;
594 
595 	ilock(&csdev);
596 	speaker(0);
597 	for(i =0; volumes[i].name; i++){
598 		v = &volumes[i];
599 		if(v->load == 0)
600 			continue;
601 		if(v->flag & Fin){
602 			l = audio.livol[i];
603 			r = audio.rivol[i];
604 		} else {
605 			l = audio.lovol[i];
606 			r = audio.rovol[i];
607 		}
608 		if(l < 0)
609 			l = 0;
610 		if(r < 0)
611 			r = 0;
612 		if(l > 100)
613 			l = 100;
614 		if(r > 100)
615 			r = 100;
616 		l = mix_cvt[l];
617 		r = mix_cvt[r];
618 		if((v->flag & Fmono) == 0){
619 			xout(v->reg, (*v->load)(xin(v->reg), l));
620 			xout(v->reg+1, (*v->load)(xin(v->reg+1), r));
621 		} else
622 			xout(v->reg, (*v->load)(xin(v->reg), l));
623 	}
624 	xout(LeftADC, (xin(LeftADC)&~ISmask)|csdev.regs[LeftADC]);
625 	xout(RightADC, (xin(RightADC)&~ISmask)|csdev.regs[RightADC]);
626 	if(audio.loopback)
627 		xout(Loopback, xin(Loopback)|LBE);
628 	else
629 		xout(Loopback, xin(Loopback)&~LBE);
630 	csformat(InFormat, CMCE, csdev.regs[InFormat], audio.livol);
631 	csformat(OutFormat, PMCE, csdev.regs[OutFormat], audio.lovol);
632 	if(audio.lovol[Vaudio] || audio.rovol[Vaudio])
633 		speaker(1);
634 	iunlock(&csdev);
635 }
636 
637 static void
638 flushinput(void)
639 {
640 	Buf *b;
641 
642 	ilock(&csdev);
643 	audio.in.flushing = 1;
644 	iunlock(&csdev);
645 	qlock(&audio.in);
646 	if(waserror()){
647 		qunlock(&audio.in);
648 		nexterror();
649 	}
650 	sleep(&audio.in.r, incomplete, &audio.in);
651 	qunlock(&audio.in);
652 	poperror();
653 	ilock(&csdev);
654 	audio.in.flushing = 0;
655 	iunlock(&csdev);
656 	if((b = audio.in.filling) != 0){
657 		audio.in.filling = 0;
658 		putbuf(&audio.in.empty, b);
659 	}
660 	while((b = getbuf(&audio.in.full)) != 0)
661 		putbuf(&audio.in.empty, b);
662 }
663 
664 static void
665 waitoutput(void)
666 {
667 	qlock(&audio.out);
668 	if(waserror()){
669 		qunlock(&audio.out);
670 		nexterror();
671 	}
672 	startoutput();
673 	while(!outcomplete(&audio.out))
674 		sleep(&audio.out.r, outcomplete, &audio.out);
675 	qunlock(&audio.out);
676 	poperror();
677 }
678 
679 static	void
680 resetlevel(void)
681 {
682 	int i;
683 
684 	for(i=0; volumes[i].name; i++) {
685 		audio.lovol[i] = volumes[i].ilval;
686 		audio.rovol[i] = volumes[i].irval;
687 		audio.livol[i] = volumes[i].ilval;
688 		audio.rivol[i] = volumes[i].irval;
689 	}
690 }
691 
692 void
693 cs4231init(void)
694 {
695 	cs4231install();
696 
697 	csdev.regs[LeftADC] = ISmic;
698 	csdev.regs[RightADC] = ISmic;
699 	dmasize(Wdma, 8);
700 	dmasize(Rdma, 8);
701 	csdev.sticky = 0;
702 	OUT(Paddr, Mode);
703 	csdelay();
704 	if((IN(Pdata) & 0x8F) != 0x8a){
705 		DPRINT("port %x not cs4231a: %x\n", IN(Pdata));
706 		return;
707 	}
708 	print("audio0: cs4231a: port %x irq %d wdma %d rdma %d\n", csdev.port, csdev.irq, Wdma, Rdma);
709 
710 	resetlevel();
711 
712 	cswait();
713 	OUT(Paddr, Mode);
714 	csdelay();
715 	OUT(Pdata, Mode2|IN(Pdata));	/* mode2 for all the trimmings */
716 	csdelay();
717 	cswait();
718 
719 	csdev.sticky = MCE;
720 	xout(Config, Fullcal);
721 	csspeed(volumes[Vspeed].ilval);
722 	csformat(InFormat, CMCE, Linear8, audio.livol);
723 	csformat(OutFormat, PMCE, Linear8, audio.lovol);
724 	csdev.sticky &= ~MCE;
725 	OUT(Paddr, csdev.sticky);
726 	microdelay(10000);
727 	cswait();	/* recalibration takes ages */
728 
729 	xout(FeatureStatus, 0);
730 	OUT(Pstatus, 0);
731 	setvec(csdev.irq, cs4231intr, 0);
732 	xout(PinControl, xin(PinControl)|IEN);
733 }
734 
735 Chan*
736 cs4231attach(char *param)
737 {
738 	return devattach('A', param);
739 }
740 
741 Chan*
742 cs4231clone(Chan *c, Chan *nc)
743 {
744 	return devclone(c, nc);
745 }
746 
747 int
748 cs4231walk(Chan *c, char *name)
749 {
750 	return devwalk(c, name, audiodir, NPORT, devgen);
751 }
752 
753 void
754 cs4231stat(Chan *c, char *db)
755 {
756 	devstat(c, db, audiodir, NPORT, devgen);
757 }
758 
759 Chan*
760 cs4231open(Chan *c, int omode)
761 {
762 	switch(c->qid.path & ~CHDIR) {
763 	default:
764 		error(Eperm);
765 		break;
766 
767 	case Qaudioctl:
768 	case Qdir:
769 		break;
770 
771 	case Qaudio:
772 		qlock(&audio);
773 		if(audio.opened){
774 			qunlock(&audio);
775 			error(Einuse);
776 		}
777 		if(audio.bufinit == 0) {
778 			audio.bufinit = 1;
779 			acbufinit(&audio.in);
780 			acbufinit(&audio.out);
781 		}
782 		audio.opened = 1;
783 		setempty();
784 		qunlock(&audio);
785 		mxvolume();
786 		break;
787 	}
788 	c = devopen(c, omode, audiodir, NPORT, devgen);
789 	c->mode = openmode(omode);
790 	c->flag |= COPEN;
791 	c->offset = 0;
792 
793 	return c;
794 }
795 
796 void
797 cs4231create(Chan *c, char *name, int omode, ulong perm)
798 {
799 	USED(c, name, omode, perm);
800 	error(Eperm);
801 }
802 
803 void
804 cs4231close(Chan *c)
805 {
806 	Buf *b;
807 
808 	switch(c->qid.path & ~CHDIR) {
809 	default:
810 		error(Eperm);
811 		break;
812 
813 	case Qdir:
814 	case Qaudioctl:
815 		break;
816 
817 	case Qaudio:
818 		if(c->flag & COPEN) {
819 			qlock(&audio);
820 			audio.opened = 0;
821 			if(waserror()){
822 				qunlock(&audio);
823 				nexterror();
824 			}
825 			b = audio.out.filling;
826 			if(b){
827 				audio.out.filling = 0;
828 				putbuf(&audio.out.full, b);
829 			}
830 			waitoutput();
831 			flushinput();
832 			//tsleep(&up->sleep, return0, 0, 500);
833 			//speaker(0);
834 			qunlock(&audio);
835 			poperror();
836 		}
837 		break;
838 	}
839 }
840 
841 long
842 cs4231read(Chan *c, char *a, long n, vlong offset)
843 {
844 	int liv, riv, lov, rov, ifmt, ofmt;
845 	long m, n0;
846 	char buf[350];
847 	Buf *b;
848 	int j;
849 
850 	n0 = n;
851 	switch(c->qid.path & ~CHDIR) {
852 	default:
853 		error(Eperm);
854 		break;
855 
856 	case Qdir:
857 		return devdirread(c, a, n, audiodir, NPORT, devgen);
858 
859 	case Qaudio:
860 		qlock(&audio.in);
861 		if(waserror()){
862 			qunlock(&audio.in);
863 			nexterror();
864 		}
865 		while(n > 0) {
866 			b = audio.in.filling;
867 			if(b == 0) {
868 				b = getbuf(&audio.in.full);
869 				if(b == 0) {
870 					startinput();
871 					sleep(&audio.in.r, anyinput, &audio.in);
872 					continue;
873 				}
874 				audio.in.filling = b;
875 				b->count = 0;
876 			}
877 			m = Bufsize-b->count;
878 			if(m > n)
879 				m = n;
880 			memmove(a, b->virt+b->count, m);
881 
882 			b->count += m;
883 			n -= m;
884 			a += m;
885 			if(b->count >= Bufsize) {
886 				audio.in.filling = 0;
887 				putbuf(&audio.in.empty, b);
888 			}
889 		}
890 		qunlock(&audio.in);
891 		poperror();
892 		break;
893 
894 	case Qaudioctl:
895 		j = 0;
896 		buf[0] = 0;
897 		for(m=0; volumes[m].name; m++){
898 			liv = audio.livol[m];
899 			riv = audio.rivol[m];
900 			lov = audio.lovol[m];
901 			rov = audio.rovol[m];
902 			j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
903 			if((volumes[m].flag & Fmono) || liv==riv && lov==rov){
904 				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
905 					j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
906 				else{
907 					if(volumes[m].flag & Fin)
908 						j += snprint(buf+j, sizeof(buf)-j, " in %d", liv);
909 					if(volumes[m].flag & Fout)
910 						j += snprint(buf+j, sizeof(buf)-j, " out %d", lov);
911 				}
912 			}else{
913 				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov && riv==rov)
914 					j += snprint(buf+j, sizeof(buf)-j, " left %d right %d",
915 						liv, riv);
916 				else{
917 					if(volumes[m].flag & Fin)
918 						j += snprint(buf+j, sizeof(buf)-j, " in left %d right %d",
919 							liv, riv);
920 					if(volumes[m].flag & Fout)
921 						j += snprint(buf+j, sizeof(buf)-j, " out left %d right %d",
922 							lov, rov);
923 				}
924 			}
925 			j += snprint(buf+j, sizeof(buf)-j, "\n");
926 		}
927 		ifmt = xin(InFormat);
928 		ofmt = xin(OutFormat);
929 		if(ifmt != ofmt){
930 			j += snprint(buf+j, sizeof(buf)-j, "in enc %s\n", encname(ifmt));
931 			j += snprint(buf+j, sizeof(buf)-j, "out enc %s\n", encname(ofmt));
932 		} else
933 			j += snprint(buf+j, sizeof(buf)-j, "enc %s\n", encname(ifmt));
934 		j += snprint(buf+j, sizeof(buf)-j, "loop %d\n", audio.loopback);
935 		{int i; for(i=0; i<32; i++){j += snprint(buf+j, sizeof(buf)-j, " %d:%x", i, xin(i)); }j += snprint(buf+j,sizeof(buf)-j,"\n");}
936 		USED(j);
937 
938 		return readstr(offset, a, n, buf);
939 	}
940 	return n0-n;
941 }
942 
943 Block*
944 cs4231bread(Chan *c, long n, ulong offset)
945 {
946 	return devbread(c, n, offset);
947 }
948 
949 long
950 cs4231write(Chan *c, char *a, long n, vlong offset)
951 {
952 	long m, n0;
953 	int i, nf, v, left, right, in, out, fmt, doload;
954 	char buf[255], *field[Ncmd];
955 	Buf *b;
956 
957 	USED(offset);
958 
959 	n0 = n;
960 	switch(c->qid.path & ~CHDIR) {
961 	default:
962 		error(Eperm);
963 		break;
964 
965 	case Qaudioctl:
966 		waitoutput();
967 		flushinput();
968 		qlock(&audio);
969 		if(waserror()){
970 			qunlock(&audio);
971 			nexterror();
972 		}
973 		v = Vaudio;
974 		doload = 0;
975 		left = 1;
976 		right = 1;
977 		in = 1;
978 		out = 1;
979 		if(n > sizeof(buf)-1)
980 			n = sizeof(buf)-1;
981 		memmove(buf, a, n);
982 		buf[n] = '\0';
983 
984 		nf = getfields(buf, field, Ncmd, 1, " \t\n,");
985 		for(i = 0; i < nf; i++){
986 			/*
987 			 * a number is volume
988 			 */
989 			if(field[i][0] >= '0' && field[i][0] <= '9') {
990 				m = strtoul(field[i], 0, 10);
991 				if(left && out)
992 					audio.lovol[v] = m;
993 				if(left && in)
994 					audio.livol[v] = m;
995 				if(right && out)
996 					audio.rovol[v] = m;
997 				if(right && in)
998 					audio.rivol[v] = m;
999 				if(v == Vspeed){
1000 					ilock(&csdev);
1001 					csdev.sticky = MCE;
1002 					csspeed(m);
1003 					csdev.sticky &= ~MCE;
1004 					OUT(Paddr, csdev.sticky);
1005 					microdelay(10000);
1006 					cswait();
1007 					iunlock(&csdev);
1008 				} else
1009 					doload = 1;
1010 				continue;
1011 			}
1012 
1013 			for(m=0; volumes[m].name; m++) {
1014 				if(strcmp(field[i], volumes[m].name) == 0) {
1015 					v = m;
1016 					in = 1;
1017 					out = 1;
1018 					left = 1;
1019 					right = 1;
1020 					break;
1021 				}
1022 			}
1023 			if(volumes[m].name)
1024 				continue;
1025 
1026 			if(strcmp(field[i], "chat") == 0){
1027 				chatty = !chatty;
1028 				continue;
1029 			}
1030 
1031 			if(strcmp(field[i], "reset") == 0) {
1032 				resetlevel();
1033 				doload = 1;
1034 				continue;
1035 			}
1036 			if(strcmp(field[i], "loop") == 0) {
1037 				if(++i >= nf)
1038 					error(Evolume);
1039 				audio.loopback = strtoul(field[i], 0, 10);
1040 				doload = 1;
1041 				continue;
1042 			}
1043 			if(strcmp(field[i], "enc") == 0) {
1044 				if(++i >= nf)
1045 					error(Evolume);
1046 				fmt = -1;
1047 				if(strcmp(field[i], "ulaw") == 0)
1048 					fmt = uLaw;
1049 				else if(strcmp(field[i], "alaw") == 0)
1050 					fmt = aLaw;
1051 				else if(strcmp(field[i], "pcm") == 0)
1052 					fmt = Linear8;
1053 				else if(strcmp(field[i], "adpcm") == 0)
1054 					fmt = ADPCM;
1055 				else
1056 					error(Evolume);
1057 				if(in)
1058 					csdev.regs[InFormat] = fmt;
1059 				if(out)
1060 					csdev.regs[OutFormat] = fmt;
1061 				doload = 1;
1062 				continue;
1063 			}
1064 			if(strcmp(field[i], "dev") == 0) {
1065 				if(++i >= nf)
1066 					error(Evolume);
1067 				if(in){
1068 					fmt = -1;
1069 					if(strcmp(field[i], "mic") == 0)
1070 						fmt = ISmic;
1071 					else if(strcmp(field[i], "line") == 0)
1072 						fmt = ISline;
1073 					else if(strcmp(field[i], "aux1") == 0)
1074 						fmt = ISaux1;
1075 					else if(strcmp(field[i], "loop") == 0)
1076 						fmt = ISloop;
1077 					else
1078 						error(Evolume);
1079 					if(left)
1080 						csdev.regs[LeftADC] = fmt;
1081 					if(right)
1082 						csdev.regs[RightADC] = fmt;
1083 					doload = 1;
1084 				}
1085 				continue;
1086 			}
1087 			if(strcmp(field[i], "in") == 0) {
1088 				in = 1;
1089 				out = 0;
1090 				continue;
1091 			}
1092 			if(strcmp(field[i], "out") == 0) {
1093 				in = 0;
1094 				out = 1;
1095 				continue;
1096 			}
1097 			if(strcmp(field[i], "left") == 0) {
1098 				left = 1;
1099 				right = 0;
1100 				continue;
1101 			}
1102 			if(strcmp(field[i], "right") == 0) {
1103 				left = 0;
1104 				right = 1;
1105 				continue;
1106 			}
1107 			error(Evolume);
1108 		}
1109 		if(doload)
1110 			mxvolume();
1111 		qunlock(&audio);
1112 		poperror();
1113 		n=0;
1114 		break;
1115 
1116 	case Qaudio:
1117 		qlock(&audio.out);
1118 		if(waserror()){
1119 			qunlock(&audio.out);
1120 			nexterror();
1121 		}
1122 		while(n > 0) {
1123 			b = audio.out.filling;
1124 			if(b == 0) {
1125 				b = getbuf(&audio.out.empty);
1126 				if(b == 0) {
1127 					startoutput();
1128 					sleep(&audio.out.r, anybuf, &audio.out);
1129 					continue;
1130 				}
1131 				b->count = 0;
1132 				audio.out.filling = b;
1133 			}
1134 
1135 			m = Bufsize-b->count;
1136 			if(m > n)
1137 				m = n;
1138 			memmove(b->virt+b->count, a, m);
1139 
1140 			b->count += m;
1141 			n -= m;
1142 			a += m;
1143 			if(b->count >= Bufsize) {
1144 				audio.out.filling = 0;
1145 				putbuf(&audio.out.full, b);
1146 			}
1147 		}
1148 		qunlock(&audio.out);
1149 		poperror();
1150 		break;
1151 	}
1152 	return n0 - n;
1153 }
1154 
1155 long
1156 cs4231bwrite(Chan *c, Block *bp, ulong offset)
1157 {
1158 	return devbwrite(c, bp, offset);
1159 }
1160 
1161 void
1162 cs4231remove(Chan *c)
1163 {
1164 	USED(c);
1165 	error(Eperm);
1166 }
1167 
1168 void
1169 cs4231wstat(Chan *c, char *dp)
1170 {
1171 	USED(c, dp);
1172 	error(Eperm);
1173 }
1174 
1175 static char *
1176 encname(int v)
1177 {
1178 	switch(v & ~(0xF|Stereo)){
1179 	case uLaw:	return "ulaw";
1180 	case aLaw:	return "alaw";
1181 	case Linear8:	return "pcm";
1182 	case Linear16:	return "pcm16";
1183 	case ADPCM:	return "adpcm";
1184 	default:	return "?";
1185 	}
1186 }
1187