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