diff options
Diffstat (limited to 'os/ipaq1110/devaudio.c')
| -rw-r--r-- | os/ipaq1110/devaudio.c | 1056 |
1 files changed, 1056 insertions, 0 deletions
diff --git a/os/ipaq1110/devaudio.c b/os/ipaq1110/devaudio.c new file mode 100644 index 00000000..66fb5cdc --- /dev/null +++ b/os/ipaq1110/devaudio.c @@ -0,0 +1,1056 @@ +/* + * 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, +}; |
