123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056 |
- /*
- * SAC/UDA 1341 Audio driver for the Bitsy
- *
- * This code is covered by the Lucent Public Licence 1.02 (http://plan9.bell-labs.com/plan9dist/license.html);
- * see the file NOTICE in the current directory. Modifications for the Inferno environment by Vita Nuova.
- *
- * The Philips UDA 1341 sound chip is accessed through the Serial Audio
- * Controller (SAC) of the StrongARM SA-1110.
- *
- * The code morphs Nicolas Pitre's <nico@cam.org> Linux controller
- * and Ken's Soundblaster controller.
- *
- * The interface should be identical to that of devaudio.c
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "../port/error.h"
- #include "io.h"
- static int debug = 0;
- /* UDA 1341 Registers */
- enum {
- /* Status0 register */
- UdaStatusDC = 0, /* 1 bit */
- UdaStatusIF = 1, /* 3 bits */
- UdaStatusSC = 4, /* 2 bits */
- UdaStatusRST = 6, /* 1 bit */
- };
- enum {
- /* Status1 register */
- UdaStatusPC = 0, /* 2 bits */
- UdaStatusDS = 2, /* 1 bit */
- UdaStatusPDA = 3, /* 1 bit */
- UdaStatusPAD = 4, /* 1 bit */
- UdaStatusIGS = 5, /* 1 bit */
- UdaStatusOGS = 6, /* 1 bit */
- };
- /*
- * UDA1341 L3 address and command types
- */
- enum {
- UDA1341_DATA0 = 0,
- UDA1341_DATA1,
- UDA1341_STATUS,
- UDA1341_L3Addr = 0x14,
- };
- typedef struct AQueue AQueue;
- typedef struct Buf Buf;
- typedef struct IOstate IOstate;
- enum
- {
- Qdir = 0,
- Qaudio,
- Qvolume,
- Qstatus,
- Qaudioctl,
- Fmono = 1,
- Fin = 2,
- Fout = 4,
- Aclosed = 0,
- Aread,
- Awrite,
- Vaudio = 0,
- Vmic,
- Vtreb,
- Vbass,
- Vspeed,
- Vfilter,
- Vinvert,
- Nvol,
- Bufsize = 4*1024, /* 46 ms each */
- Nbuf = 32, /* 1.5 seconds total */
- Speed = 44100,
- Ncmd = 50, /* max volume command words */
- };
- Dirtab
- audiodir[] =
- {
- ".", {Qdir, 0, QTDIR}, 0, 0555,
- "audio", {Qaudio}, 0, 0666,
- "volume", {Qvolume}, 0, 0666,
- "audioctl", {Qaudioctl}, 0, 0666,
- "audiostat",{Qstatus}, 0, 0444,
- };
- struct Buf
- {
- uchar* virt;
- ulong phys;
- uint nbytes;
- };
- struct IOstate
- {
- QLock;
- Lock ilock;
- Rendez vous;
- Chan *chan; /* chan of open */
- Dma* dma; /* dma chan, alloc on open, free on close */
- int bufinit; /* boolean, if buffers allocated */
- Buf buf[Nbuf]; /* buffers and queues */
- volatile Buf *current; /* next dma to finish */
- volatile Buf *next; /* next candidate for dma */
- volatile Buf *filling; /* buffer being filled */
- /* just be be cute (and to have defines like linux, a real operating system) */
- #define emptying filling
- };
- static struct
- {
- QLock;
- int amode; /* Aclosed/Aread/Awrite for /audio */
- int intr; /* boolean an interrupt has happened */
- int rivol[Nvol]; /* right/left input/output volumes */
- int livol[Nvol];
- int rovol[Nvol];
- int lovol[Nvol];
- uvlong totcount; /* how many bytes processed since open */
- vlong tottime; /* time at which totcount bytes were processed */
- int clockout; /* need steady output to provide input clock */
- IOstate i;
- IOstate o;
- } audio;
- static struct
- {
- ulong bytes;
- ulong totaldma;
- ulong idledma;
- ulong faildma;
- ulong samedma;
- } iostats;
- static struct
- {
- char* name;
- int flag;
- int ilval; /* initial values */
- int irval;
- } volumes[] =
- {
- [Vaudio] {"audio", Fout|Fmono, 80, 80},
- [Vmic] {"mic", Fin|Fmono, 0, 0},
- [Vtreb] {"treb", Fout|Fmono, 50, 50},
- [Vbass] {"bass", Fout|Fmono, 50, 50},
- [Vspeed] {"speed", Fin|Fout|Fmono, Speed, Speed},
- [Vfilter] {"filter", Fout|Fmono, 0, 0},
- [Vinvert] {"invert", Fin|Fout|Fmono, 0, 0},
- [Nvol] {0}
- };
- static void setreg(char *name, int val, int n);
- static char Emode[] = "illegal open mode";
- static char Evolume[] = "illegal volume specifier";
- static void
- bufinit(IOstate *b)
- {
- int i;
- if (debug) print("bufinit\n");
- for (i = 0; i < Nbuf; i++) {
- b->buf[i].virt = xspanalloc(Bufsize, CACHELINESZ, 0);
- b->buf[i].phys = PADDR(b->buf[i].virt);
- }
- b->bufinit = 1;
- };
- static void
- setempty(IOstate *b)
- {
- int i;
- if (debug) print("setempty\n");
- for (i = 0; i < Nbuf; i++) {
- b->buf[i].nbytes = 0;
- }
- b->filling = b->buf;
- b->current = b->buf;
- b->next = b->buf;
- }
- static int
- audioqnotempty(void *x)
- {
- IOstate *s = x;
- return dmaidle(s->dma) || s->emptying != s->current;
- }
- static int
- audioqnotfull(void *x)
- {
- IOstate *s = x;
- return dmaidle(s->dma) || s->filling != s->current;
- }
- static void
- audioreset(void)
- {
- /* Turn MCP operations off */
- MCPREG->mccr = 0;
- }
- uchar status0[1] = {0x22};
- uchar status1[1] = {0x80};
- uchar data00[1] = {0x00}; /* volume control, bits 0 – 5 */
- uchar data01[1] = {0x40};
- uchar data02[1] = {0x80};
- uchar data0e0[2] = {0xc0, 0xe0};
- uchar data0e1[2] = {0xc1, 0xe0};
- uchar data0e2[2] = {0xc2, 0xf2};
- /* there is no data0e3 */
- uchar data0e4[2] = {0xc4, 0xe0};
- uchar data0e5[2] = {0xc5, 0xe0};
- uchar data0e6[2] = {0xc6, 0xe3};
- static void
- enable(void)
- {
- uchar data[1];
- int cs;
- L3init();
- PPCREG->ppar &= ~PPAR_SPR;
- /* external clock and ssp configured for current samples/sec */
- cs = archaudiospeed(audio.livol[Vspeed], 1);
- status0[0] = (status0[0] & ~(3<<4)) | (cs<<4);
- /* Enable the audio power */
- archaudiopower(1);
- // egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset, 1);
- /* Wait for the UDA1341 to wake up */
- delay(100);
- /* Reset the chip */
- data[0] = status0[0] | 1<<UdaStatusRST;
- L3write(UDA1341_L3Addr | UDA1341_STATUS, data, 1 );
- archcodecreset();
- /* write uda 1341 status[0] */
- L3write(UDA1341_L3Addr | UDA1341_STATUS, status0, 1 );
- L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
- L3write(UDA1341_L3Addr | UDA1341_DATA0, data02, 1);
- L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e2, 2);
- L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e6, 2 );
- if (debug) {
- print("enable: status0 = 0x%2.2ux\n", status0[0]);
- print("enable: status1 = 0x%2.2ux\n", status1[0]);
- print("enable: data02 = 0x%2.2ux\n", data02[0]);
- print("enable: data0e2 = 0x%4.4ux\n", data0e2[0] | data0e2[1]<<8);
- print("enable: data0e4 = 0x%4.4ux\n", data0e4[0] | data0e4[1]<<8);
- print("enable: data0e6 = 0x%4.4ux\n", data0e6[0] | data0e6[1]<<8);
- }
- }
- static void
- disable(void)
- {
- SSPREG->sscr0 = 0x031f; /* disable */
- }
- static void
- resetlevel(void)
- {
- int i;
- for(i=0; volumes[i].name; i++) {
- audio.lovol[i] = volumes[i].ilval;
- audio.rovol[i] = volumes[i].irval;
- audio.livol[i] = volumes[i].ilval;
- audio.rivol[i] = volumes[i].irval;
- }
- }
- static void
- mxvolume(void) {
- int *left, *right;
- int cs;
- cs = archaudiospeed(audio.livol[Vspeed], 1);
- status0[0] = (status0[0] & ~(3<<4)) | (cs<<4);
- L3write(UDA1341_L3Addr | UDA1341_STATUS, status0, 1);
- if(debug)
- print("mxvolume: status0 = %2.2ux\n", status0[0]);
- if(audio.amode & Aread){
- left = audio.livol;
- right = audio.rivol;
- if (left[Vmic]+right[Vmic] == 0) {
- /* Turn on automatic gain control (AGC) */
- data0e4[1] |= 0x10;
- L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e4, 2 );
- } else {
- int v;
- /* Turn on manual gain control */
- v = ((left[Vmic]+right[Vmic])*0x7f/200)&0x7f;
- data0e4[1] &= ~0x13;
- data0e5[1] &= ~0x1f;
- data0e4[1] |= v & 0x3;
- data0e5[0] |= (v & 0x7c)<<6;
- data0e5[1] |= (v & 0x7c)>>2;
- L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e4, 2 );
- L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e5, 2 );
- }
- if (left[Vinvert]+right[Vinvert] == 0)
- status1[0] &= ~0x10;
- else
- status1[0] |= 0x10;
- L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
- if (debug) {
- print("mxvolume: status1 = 0x%2.2ux\n", status1[0]);
- print("mxvolume: data0e4 = 0x%4.4ux\n", data0e4[0]|data0e4[0]<<8);
- print("mxvolume: data0e5 = 0x%4.4ux\n", data0e5[0]|data0e5[0]<<8);
- }
- }
- if(audio.amode & Awrite){
- left = audio.lovol;
- right = audio.rovol;
- data00[0] &= ~0x3f;
- data00[0] |= ((200-left[Vaudio]-right[Vaudio])*0x3f/200)&0x3f;
- if (left[Vtreb]+right[Vtreb] <= 100
- && left[Vbass]+right[Vbass] <= 100)
- /* settings neutral */
- data02[0] &= ~0x03;
- else {
- data02[0] |= 0x03;
- data01[0] &= ~0x3f;
- data01[0] |= ((left[Vtreb]+right[Vtreb]-100)*0x3/100)&0x03;
- data01[0] |= (((left[Vbass]+right[Vbass]-100)*0xf/100)&0xf)<<2;
- }
- if (left[Vfilter]+right[Vfilter] == 0)
- data02[0] &= ~0x10;
- else
- data02[0]|= 0x10;
- if (left[Vinvert]+right[Vinvert] == 0)
- status1[0] &= ~0x8;
- else
- status1[0] |= 0x8;
- L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
- L3write(UDA1341_L3Addr | UDA1341_DATA0, data00, 1);
- L3write(UDA1341_L3Addr | UDA1341_DATA0, data01, 1);
- L3write(UDA1341_L3Addr | UDA1341_DATA0, data02, 1);
- if (debug) {
- print("mxvolume: status1 = 0x%2.2ux\n", status1[0]);
- print("mxvolume: data00 = 0x%2.2ux\n", data00[0]);
- print("mxvolume: data01 = 0x%2.2ux\n", data01[0]);
- print("mxvolume: data02 = 0x%2.2ux\n", data02[0]);
- }
- }
- }
- static void
- setreg(char *name, int val, int n)
- {
- uchar x[2];
- int i;
- if(strcmp(name, "pause") == 0){
- for(i = 0; i < n; i++)
- microdelay(val);
- return;
- }
- x[0] = val;
- x[1] = val>>8;
- switch(n){
- case 1:
- case 2:
- break;
- default:
- error("setreg");
- }
- if(strcmp(name, "status") == 0){
- L3write(UDA1341_L3Addr | UDA1341_STATUS, x, n);
- } else if(strcmp(name, "data0") == 0){
- L3write(UDA1341_L3Addr | UDA1341_DATA0, x, n);
- } else if(strcmp(name, "data1") == 0){
- L3write(UDA1341_L3Addr | UDA1341_DATA1, x, n);
- } else
- error("setreg");
- }
- static void
- outenable(void) {
- /* turn on DAC, set output gain switch */
- archaudioamp(1);
- archaudiomute(0);
- status1[0] |= 0x41;
- L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
- /* set volume */
- data00[0] |= 0xf;
- L3write(UDA1341_L3Addr | UDA1341_DATA0, data00, 1);
- if (debug) {
- print("outenable: status1 = 0x%2.2ux\n", status1[0]);
- print("outenable: data00 = 0x%2.2ux\n", data00[0]);
- }
- }
- static void
- outdisable(void) {
- archaudiomute(1);
- dmastop(audio.o.dma);
- /* turn off DAC, clear output gain switch */
- archaudioamp(0);
- status1[0] &= ~0x41;
- L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
- if (debug) {
- print("outdisable: status1 = 0x%2.2ux\n", status1[0]);
- }
- // egpiobits(EGPIO_audio_power, 0);
- }
- static void
- inenable(void) {
- /* turn on ADC, set input gain switch */
- status1[0] |= 0x22;
- L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
- if (debug) {
- print("inenable: status1 = 0x%2.2ux\n", status1[0]);
- }
- }
- static void
- indisable(void) {
- dmastop(audio.i.dma);
- /* turn off ADC, clear input gain switch */
- status1[0] &= ~0x22;
- L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
- if (debug) {
- print("indisable: status1 = 0x%2.2ux\n", status1[0]);
- }
- }
- static void
- sendaudio(IOstate *s) {
- /* interrupt routine calls this too */
- int n;
- if (debug > 1) print("#A: sendaudio\n");
- ilock(&s->ilock);
- while (s->next != s->filling) {
- assert(s->next->nbytes);
- if ((n = dmastart(s->dma, (void*)s->next->phys, s->next->nbytes)) == 0) {
- iostats.faildma++;
- break;
- }
- iostats.totaldma++;
- switch (n) {
- case 1:
- iostats.idledma++;
- break;
- case 3:
- iostats.faildma++;
- break;
- }
- if (debug) {
- if (debug > 1)
- print("dmastart @%p\n", s->next);
- else
- iprint("+");
- }
- s->next->nbytes = 0;
- s->next++;
- if (s->next == &s->buf[Nbuf])
- s->next = &s->buf[0];
- }
- iunlock(&s->ilock);
- }
- static void
- recvaudio(IOstate *s) {
- /* interrupt routine calls this too */
- int n;
- if (debug > 1) print("#A: recvaudio\n");
- ilock(&s->ilock);
- while (s->next != s->emptying) {
- assert(s->next->nbytes == 0);
- if ((n = dmastart(s->dma, (void*)s->next->phys, Bufsize)) == 0) {
- iostats.faildma++;
- break;
- }
- iostats.totaldma++;
- switch (n) {
- case 1:
- iostats.idledma++;
- break;
- case 3:
- iostats.faildma++;
- break;
- }
- if (debug) {
- if (debug > 1)
- print("dmastart @%p\n", s->next);
- else
- iprint("+");
- }
- s->next++;
- if (s->next == &s->buf[Nbuf])
- s->next = &s->buf[0];
- }
- iunlock(&s->ilock);
- }
- static void
- audiopower(int flag) {
- IOstate *s;
- if (debug) {
- iprint("audiopower %d\n", flag);
- }
- if (flag) {
- /* power on only when necessary */
- if (audio.amode) {
- archaudiopower(1);
- enable();
- if (audio.amode & Aread) {
- inenable();
- s = &audio.i;
- dmastop(s->dma);
- recvaudio(s);
- }
- if (audio.amode & Awrite) {
- outenable();
- s = &audio.o;
- dmastop(s->dma);
- sendaudio(s);
- }
- mxvolume();
- }
- } else {
- /* power off */
- if (audio.amode & Aread)
- indisable();
- if (audio.amode & Awrite)
- outdisable();
- disable();
- archaudiopower(0);
- }
- }
- static void
- audiointr(void *x, ulong ndma) {
- IOstate *s = x;
- if (debug) {
- if (debug > 1)
- iprint("#A: audio interrupt @%p\n", s->current);
- else
- iprint("-");
- }
- /* Only interrupt routine touches s->current */
- s->current++;
- if (s->current == &s->buf[Nbuf])
- s->current = &s->buf[0];
- if (ndma > 0) {
- if (s == &audio.o)
- sendaudio(s);
- else if (s == &audio.i)
- recvaudio(s);
- }
- wakeup(&s->vous);
- }
- static void
- audioinit(void)
- {
- audio.amode = Aclosed;
- resetlevel();
- // powerenable(audiopower);
- }
- static Chan*
- audioattach(char *param)
- {
- return devattach('A', param);
- }
- static Walkqid*
- audiowalk(Chan *c, Chan *nc, char **name, int nname)
- {
- return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
- }
- static int
- audiostat(Chan *c, uchar *db, int n)
- {
- return devstat(c, db, n, audiodir, nelem(audiodir), devgen);
- }
- static Chan*
- audioopen(Chan *c, int mode)
- {
- IOstate *s;
- int omode = mode;
- switch((ulong)c->qid.path) {
- default:
- error(Eperm);
- break;
- case Qstatus:
- if((omode&7) != OREAD)
- error(Eperm);
- case Qvolume:
- case Qaudioctl:
- case Qdir:
- break;
- case Qaudio:
- omode = (omode & 0x7) + 1;
- if (omode & ~(Aread | Awrite))
- error(Ebadarg);
- qlock(&audio);
- if(audio.amode & omode){
- qunlock(&audio);
- error(Einuse);
- }
- enable();
- memset(&iostats, 0, sizeof(iostats));
- if (omode & Aread) {
- inenable();
- s = &audio.i;
- if(s->bufinit == 0)
- bufinit(s);
- setempty(s);
- s->emptying = &s->buf[Nbuf-1];
- s->chan = c;
- s->dma = dmasetup(DmaSSP, 1, 0, audiointr, (void*)s);
- audio.amode |= Aread;
- audio.clockout = 1;
- }
- if (omode & Awrite) {
- outenable();
- s = &audio.o;
- audio.amode |= Awrite;
- if(s->bufinit == 0)
- bufinit(s);
- setempty(s);
- s->chan = c;
- s->dma = dmasetup(DmaSSP, 0, 0, audiointr, (void*)s);
- audio.amode |= Awrite;
- }
- mxvolume();
- qunlock(&audio);
- if (debug) print("open done\n");
- break;
- }
- c = devopen(c, mode, audiodir, nelem(audiodir), devgen);
- c->mode = openmode(mode);
- c->flag |= COPEN;
- c->offset = 0;
- return c;
- }
- static void
- audioclose(Chan *c)
- {
- IOstate *s;
- switch((ulong)c->qid.path) {
- default:
- error(Eperm);
- break;
- case Qdir:
- case Qvolume:
- case Qaudioctl:
- case Qstatus:
- break;
- case Qaudio:
- if (debug > 1) print("#A: close\n");
- if(c->flag & COPEN) {
- qlock(&audio);
- if(waserror()){
- qunlock(&audio);
- nexterror();
- }
- if (audio.o.chan == c) {
- /* closing the write end */
- audio.amode &= ~Awrite;
- s = &audio.o;
- qlock(s);
- if(waserror()){
- qunlock(s);
- nexterror();
- }
- if (s->filling->nbytes) {
- /* send remaining partial buffer */
- s->filling++;
- if (s->filling == &s->buf[Nbuf])
- s->filling = &s->buf[0];
- sendaudio(s);
- }
- dmawait(s->dma);
- outdisable();
- setempty(s);
- dmafree(s->dma);
- qunlock(s);
- poperror();
- }
- if (audio.i.chan == c) {
- /* closing the read end */
- audio.amode &= ~Aread;
- s = &audio.i;
- qlock(s);
- if(waserror()){
- qunlock(s);
- nexterror();
- }
- indisable();
- setempty(s);
- dmafree(s->dma);
- qunlock(s);
- poperror();
- }
- if (audio.amode == 0) {
- /* turn audio off */
- archaudiopower(0);
- }
- qunlock(&audio);
- poperror();
- if (debug) {
- print("total dmas: %lud\n", iostats.totaldma);
- print("dmas while idle: %lud\n", iostats.idledma);
- print("dmas while busy: %lud\n", iostats.faildma);
- print("out of order dma: %lud\n", iostats.samedma);
- }
- }
- break;
- }
- }
- static long
- audioread(Chan *c, void *v, long n, vlong off)
- {
- int liv, riv, lov, rov;
- long m, n0;
- char buf[300];
- int j;
- ulong offset = off;
- char *p;
- IOstate *s;
- n0 = n;
- p = v;
- switch((ulong)c->qid.path) {
- default:
- error(Eperm);
- break;
- case Qdir:
- return devdirread(c, p, n, audiodir, nelem(audiodir), devgen);
- case Qaudio:
- if (debug > 1) print("#A: read %ld\n", n);
- if((audio.amode & Aread) == 0)
- error(Emode);
- s = &audio.i;
- qlock(s);
- if(waserror()){
- qunlock(s);
- nexterror();
- }
- while(n > 0) {
- if(s->emptying->nbytes == 0) {
- if (debug > 1) print("#A: emptied @%p\n", s->emptying);
- recvaudio(s);
- s->emptying++;
- if (s->emptying == &s->buf[Nbuf])
- s->emptying = s->buf;
- }
- /* wait if dma in progress */
- while (!dmaidle(s->dma) && s->emptying == s->current) {
- if (debug > 1) print("#A: sleep\n");
- sleep(&s->vous, audioqnotempty, s);
- }
- m = Bufsize - s->emptying->nbytes;
- if(m > n)
- m = n;
- memmove(p, s->emptying->virt + s->emptying->nbytes, m);
- s->emptying->nbytes -= m;
- n -= m;
- p += m;
- }
- poperror();
- qunlock(s);
- break;
- break;
- case Qstatus:
- buf[0] = 0;
- snprint(buf, sizeof(buf), "bytes %llud\ntime %lld\n",
- audio.totcount, audio.tottime);
- return readstr(offset, p, n, buf);
- case Qvolume:
- case Qaudioctl:
- j = 0;
- buf[0] = 0;
- for(m=0; volumes[m].name; m++){
- liv = audio.livol[m];
- riv = audio.rivol[m];
- lov = audio.lovol[m];
- rov = audio.rovol[m];
- j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
- if((volumes[m].flag & Fmono) || liv==riv && lov==rov){
- if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
- j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
- else{
- if(volumes[m].flag & Fin)
- j += snprint(buf+j, sizeof(buf)-j,
- " in %d", liv);
- if(volumes[m].flag & Fout)
- j += snprint(buf+j, sizeof(buf)-j,
- " out %d", lov);
- }
- }else{
- if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) &&
- liv==lov && riv==rov)
- j += snprint(buf+j, sizeof(buf)-j,
- " left %d right %d",
- liv, riv);
- else{
- if(volumes[m].flag & Fin)
- j += snprint(buf+j, sizeof(buf)-j,
- " in left %d right %d",
- liv, riv);
- if(volumes[m].flag & Fout)
- j += snprint(buf+j, sizeof(buf)-j,
- " out left %d right %d",
- lov, rov);
- }
- }
- j += snprint(buf+j, sizeof(buf)-j, "\n");
- }
- return readstr(offset, p, n, buf);
- }
- return n0-n;
- }
- static long
- audiowrite(Chan *c, void *vp, long n, vlong)
- {
- long m, n0;
- int i, nf, v, left, right, in, out;
- char buf[255], *field[Ncmd];
- char *p;
- IOstate *a;
- p = vp;
- n0 = n;
- switch((ulong)c->qid.path) {
- default:
- error(Eperm);
- break;
- case Qvolume:
- case Qaudioctl:
- v = Vaudio;
- left = 1;
- right = 1;
- in = 1;
- out = 1;
- if(n > sizeof(buf)-1)
- n = sizeof(buf)-1;
- memmove(buf, p, n);
- buf[n] = '\0';
- n = 0;
- nf = getfields(buf, field, Ncmd, 1, " \t\n");
- for(i = 0; i < nf; i++){
- /*
- * a number is volume
- */
- if(field[i][0] >= '0' && field[i][0] <= '9') {
- m = strtoul(field[i], 0, 10);
- if(v == Vspeed){
- if(archaudiospeed(m, 0) < 0)
- error(Evolume);
- }else
- if(m < 0 || m > 100)
- error(Evolume);
- if(left && out)
- audio.lovol[v] = m;
- if(left && in)
- audio.livol[v] = m;
- if(right && out)
- audio.rovol[v] = m;
- if(right && in)
- audio.rivol[v] = m;
- goto cont0;
- }
- if(strcmp(field[i], "rate") == 0)
- field[i] = "speed"; /* honestly ... */
- for(m=0; volumes[m].name; m++) {
- if(strcmp(field[i], volumes[m].name) == 0) {
- v = m;
- in = 1;
- out = 1;
- left = 1;
- right = 1;
- goto cont0;
- }
- }
- if(strcmp(field[i], "enc") == 0) {
- if(++i >= nf)
- error(Evolume);
- if(strcmp(field[i], "pcm") != 0)
- error(Evolume);
- goto cont0;
- }
- if(strcmp(field[i], "bits") == 0) {
- if(++i >= nf)
- error(Evolume);
- if(strtol(field[i], 0, 0) != 16)
- error(Evolume);
- goto cont0;
- }
- if(strcmp(field[i], "chans") == 0) {
- if(++i >= nf)
- error(Evolume);
- if(strtol(field[i], 0, 0) != 2)
- error(Evolume);
- goto cont0;
- }
- if(strcmp(field[i], "reset") == 0) {
- resetlevel();
- goto cont0;
- }
- if(strcmp(field[i], "debug") == 0) {
- debug = debug?0:1;
- goto cont0;
- }
- if(strcmp(field[i], "in") == 0) {
- in = 1;
- out = 0;
- goto cont0;
- }
- if(strcmp(field[i], "out") == 0) {
- in = 0;
- out = 1;
- goto cont0;
- }
- if(strcmp(field[i], "left") == 0) {
- left = 1;
- right = 0;
- goto cont0;
- }
- if(strcmp(field[i], "right") == 0) {
- left = 0;
- right = 1;
- goto cont0;
- }
- if(strcmp(field[i], "reg") == 0) {
- if(nf < 3)
- error(Evolume);
- setreg(field[1], atoi(field[2]), nf == 4 ? atoi(field[3]):1);
- return n0;
- }
- error(Evolume);
- break;
- cont0:;
- }
- mxvolume();
- break;
- case Qaudio:
- if (debug > 1) print("#A: write %ld\n", n);
- if((audio.amode & Awrite) == 0)
- error(Emode);
- a = &audio.o;
- qlock(a);
- if(waserror()){
- qunlock(a);
- nexterror();
- }
- while(n > 0) {
- /* wait if dma in progress */
- while (!dmaidle(a->dma) && a->filling == a->current) {
- if (debug > 1) print("#A: sleep\n");
- sleep(&a->vous, audioqnotfull, a);
- }
- m = Bufsize - a->filling->nbytes;
- if(m > n)
- m = n;
- memmove(a->filling->virt + a->filling->nbytes, p, m);
- a->filling->nbytes += m;
- n -= m;
- p += m;
- if(a->filling->nbytes >= Bufsize) {
- if (debug > 1) print("#A: filled @%p\n", a->filling);
- a->filling++;
- if (a->filling == &a->buf[Nbuf])
- a->filling = a->buf;
- sendaudio(a);
- }
- }
- poperror();
- qunlock(a);
- break;
- }
- return n0 - n;
- }
- Dev audiodevtab = {
- 'A',
- "audio",
- audioreset,
- audioinit,
- devshutdown,
- audioattach,
- audiowalk,
- audiostat,
- audioopen,
- devcreate,
- audioclose,
- audioread,
- devbread,
- audiowrite,
- devbwrite,
- devremove,
- devwstat,
- audiopower,
- };
|