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
xin(int r)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
xout(int r,int v)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
speaker(int on)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*
getbuf(AQueue * q)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
putbuf(AQueue * q,Buf * b)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
achanreset(AChan * ac)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
startoutput(void)310 startoutput(void)
311 {
312 ilock(&csdev);
313 if(audio.out.current == 0)
314 continoutput();
315 iunlock(&csdev);
316 }
317
318 static void
continoutput(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
startinput(void)350 startinput(void)
351 {
352 ilock(&csdev);
353 if(audio.in.current == 0)
354 contininput();
355 iunlock(&csdev);
356 }
357
358 static void
contininput(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
cswait(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
csspeed(int freq)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
csformat(int r,int flag,int form,int * vec)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
cs4231intr(Ureg *,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
anybuf(void * p)493 anybuf(void *p)
494 {
495 return ((AChan*)p)->empty.first != 0;
496 }
497
498 static int
anyinput(void * p)499 anyinput(void *p)
500 {
501 return ((AChan*)p)->full.first != 0;
502 }
503
504 static int
outcomplete(void * p)505 outcomplete(void *p)
506 {
507 return ((AChan*)p)->full.first == 0 && ((AChan*)p)->current==0;
508 }
509
510 static int
incomplete(void * p)511 incomplete(void *p)
512 {
513 return ((AChan*)p)->current == 0;
514 }
515
516 static void
acbufinit(AChan * ac)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
setempty(void)531 setempty(void)
532 {
533 ilock(&csdev);
534 achanreset(&audio.in);
535 achanreset(&audio.out);
536 iunlock(&csdev);
537 }
538
539 void
cs4231reset(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
dacload(int r,int v)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
monoload(int r,int v)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
auxload(int r,int v)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
adcload(int r,int v)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
mxvolume(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
flushinput(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
waitoutput(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
resetlevel(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
cs4231init(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*
cs4231attach(char * param)736 cs4231attach(char *param)
737 {
738 return devattach('A', param);
739 }
740
741 Chan*
cs4231clone(Chan * c,Chan * nc)742 cs4231clone(Chan *c, Chan *nc)
743 {
744 return devclone(c, nc);
745 }
746
747 int
cs4231walk(Chan * c,char * name)748 cs4231walk(Chan *c, char *name)
749 {
750 return devwalk(c, name, audiodir, NPORT, devgen);
751 }
752
753 void
cs4231stat(Chan * c,char * db)754 cs4231stat(Chan *c, char *db)
755 {
756 devstat(c, db, audiodir, NPORT, devgen);
757 }
758
759 Chan*
cs4231open(Chan * c,int omode)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
cs4231create(Chan * c,char * name,int omode,ulong perm)797 cs4231create(Chan *c, char *name, int omode, ulong perm)
798 {
799 USED(c, name, omode, perm);
800 error(Eperm);
801 }
802
803 void
cs4231close(Chan * c)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
cs4231read(Chan * c,char * a,long n,vlong offset)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*
cs4231bread(Chan * c,long n,ulong offset)944 cs4231bread(Chan *c, long n, ulong offset)
945 {
946 return devbread(c, n, offset);
947 }
948
949 long
cs4231write(Chan * c,char * a,long n,vlong offset)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
cs4231bwrite(Chan * c,Block * bp,ulong offset)1156 cs4231bwrite(Chan *c, Block *bp, ulong offset)
1157 {
1158 return devbwrite(c, bp, offset);
1159 }
1160
1161 void
cs4231remove(Chan * c)1162 cs4231remove(Chan *c)
1163 {
1164 USED(c);
1165 error(Eperm);
1166 }
1167
1168 void
cs4231wstat(Chan * c,char * dp)1169 cs4231wstat(Chan *c, char *dp)
1170 {
1171 USED(c, dp);
1172 error(Eperm);
1173 }
1174
1175 static char *
encname(int v)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