| plan 9 kernel history: overview | file list | diff list |
2000/1116/bitsy/devuda1341.c (diff list | history)
| bitsy/devuda1341.c on 2000/1103 | ||
| 2000/1103 | /* * SAC/UDA 1341 Audio driver for the Bitsy * * The Philips UDA 1341 sound chip is accessed through the Serial Audio * Controller (SAC) of the StrongARM SA-1110. This is much more a SAC * controller than a UDA controller, but we have a devsac.c already. * * 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" | |
| 2000/1110 | #include "sa1110dma.h" | |
| 2000/1103 | ||
| 2000/1113 | static int debug = 1; | |
| 2000/1103 | /* * GPIO based L3 bus support. * * This provides control of Philips L3 type devices. * GPIO lines are used for clock, data and mode pins. * * Note: The L3 pins are shared with I2C devices. This should not present * any problems as long as an I2C start sequence is not generated. This is * defined as a 1->0 transition on the data lines when the clock is high. * It is critical this code only allow data transitions when the clock * is low. This is always legal in L3. * * The IIC interface requires the clock and data pin to be LOW when idle. We * must make sure we leave them in this state. * * It appears the read data is generated on the falling edge of the clock * and should be held stable during the clock high time. */ /* * L3 setup and hold times (expressed in us) */ #define L3_DataSetupTime 1 /* 190 ns */ #define L3_DataHoldTime 1 /* 30 ns */ #define L3_ModeSetupTime 1 /* 190 ns */ #define L3_ModeHoldTime 1 /* 190 ns */ #define L3_ClockHighTime 100 /* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */ #define L3_ClockLowTime 100 /* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */ #define L3_HaltTime 1 /* 190 ns */ /* 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 */ #define UDA1341_DATA0 0 #define UDA1341_DATA1 1 #define UDA1341_STATUS 2 | |
| 2000/1111 | #define UDA1341_L3Addr 5 | |
| 2000/1103 | typedef struct AQueue AQueue; typedef struct Buf Buf; | |
| 2000/1109 | typedef struct IOstate IOstate; | |
| 2000/1103 | enum { Qdir = 0, Qaudio, Qvolume, Qstatus, Fmono = 1, Fin = 2, Fout = 4, Aclosed = 0, Aread, Awrite, Vaudio = 0, Vsynth, Vcd, Vline, Vmic, Vspeaker, Vtreb, Vbass, Vspeed, Nvol, | |
| 2000/1109 | Bufsize = 8*1024, /* 46 ms each */ Nbuf = 32, /* 1.5 seconds total */ | |
| 2000/1103 | Speed = 44100, Ncmd = 50, /* max volume command words */ }; Dirtab audiodir[] = { "audio", {Qaudio}, 0, 0666, "volume", {Qvolume}, 0, 0666, "audiostat",{Qstatus}, 0, 0444, }; struct Buf { uchar* virt; | |
| 2000/1109 | uint nbytes; | |
| 2000/1103 | }; | |
| 2000/1104 | ||
| 2000/1109 | struct IOstate | |
| 2000/1103 | { QLock; | |
| 2000/1115 | Lock ilock; | |
| 2000/1110 | Rendez vous; Chan *chan; /* chan of open */ int 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 */ | |
| 2000/1109 | }; | |
| 2000/1103 | ||
| 2000/1104 | static struct { QLock; | |
| 2000/1109 | 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]; ulong totcount; /* how many bytes processed since open */ vlong tottime; /* time at which totcount bytes were processed */ IOstate i; IOstate o; | |
| 2000/1103 | } audio; | |
| 2000/1116 | static struct { ulong bytes; ulong totaldma; ulong idledma; ulong faildma; } iostats; | |
| 2000/1103 | static struct { char* name; int flag; int ilval; /* initial values */ int irval; } volumes[] = { [Vaudio] "audio", Fout, 50, 50, [Vsynth] "synth", Fin|Fout, 0, 0, [Vcd] "cd", Fin|Fout, 0, 0, [Vline] "line", Fin|Fout, 0, 0, [Vmic] "mic", Fin|Fout|Fmono, 0, 0, [Vspeaker] "speaker", Fout|Fmono, 0, 0, [Vtreb] "treb", Fout, 50, 50, [Vbass] "bass", Fout, 50, 50, [Vspeed] "speed", Fin|Fout|Fmono, Speed, Speed, 0 }; /* * Grab control of the IIC/L3 shared pins */ | |
| 2000/1110 | static void L3_acquirepins(void) | |
| 2000/1103 | { | |
| 2000/1110 | gpioregs->set = (GPIO_L3_SCLK_o | GPIO_L3_SDA_io); gpioregs->direction |= (GPIO_L3_SCLK_o | GPIO_L3_SDA_io); | |
| 2000/1103 | } /* * Release control of the IIC/L3 shared pins */ | |
| 2000/1110 | static void L3_releasepins(void) | |
| 2000/1103 | { | |
| 2000/1110 | gpioregs->direction &= ~(GPIO_L3_SCLK_o | GPIO_L3_SDA_io); gpioregs->clear = (GPIO_L3_SCLK_o | GPIO_L3_SDA_io); | |
| 2000/1103 | } /* * Initialize the interface */ | |
| 2000/1110 | static void L3_init(void) | |
| 2000/1103 | { | |
| 2000/1110 | gpioregs->altfunc &= ~(GPIO_L3_SDA_io | GPIO_L3_SCLK_o | GPIO_L3_MODE_o); gpioregs->set = GPIO_L3_MODE_o; gpioregs->direction |= GPIO_L3_MODE_o; | |
| 2000/1103 | L3_releasepins(); } /* * Get a bit. The clock is high on entry and on exit. Data is read after * the clock low time has expired. */ | |
| 2000/1110 | static int L3_getbit(void) | |
| 2000/1103 | { int data; | |
| 2000/1110 | gpioregs->clear = GPIO_L3_SCLK_o; µdelay(L3_ClockLowTime); | |
| 2000/1103 | ||
| 2000/1110 | data = (gpioregs->level & GPIO_L3_SDA_io) ? 1 : 0; | |
| 2000/1103 | ||
| 2000/1110 | gpioregs->set = GPIO_L3_SCLK_o; µdelay(L3_ClockHighTime); | |
| 2000/1103 | return data; } /* * Send a bit. The clock is high on entry and on exit. Data is sent only * when the clock is low (I2C compatibility). */ | |
| 2000/1110 | static void L3_sendbit(int bit) | |
| 2000/1103 | { | |
| 2000/1110 | gpioregs->clear = GPIO_L3_SCLK_o; | |
| 2000/1103 | if (bit & 1) | |
| 2000/1110 | gpioregs->set = GPIO_L3_SDA_io; | |
| 2000/1103 | else | |
| 2000/1110 | gpioregs->clear = GPIO_L3_SDA_io; | |
| 2000/1103 | /* Assumes L3_DataSetupTime < L3_ClockLowTime */ | |
| 2000/1110 | µdelay(L3_ClockLowTime); | |
| 2000/1103 | ||
| 2000/1110 | gpioregs->set = GPIO_L3_SCLK_o; µdelay(L3_ClockHighTime); | |
| 2000/1103 | } /* * Send a byte. The mode line is set or pulsed based on the mode sequence * count. The mode line is high on entry and exit. The mod line is pulsed * before the second data byte and before ech byte thereafter. */ | |
| 2000/1110 | static void L3_sendbyte(char data, int mode) | |
| 2000/1103 | { int i; L3_acquirepins(); switch(mode) { | |
| 2000/1110 | case 0: /* Address mode */ gpioregs->clear = GPIO_L3_MODE_o; | |
| 2000/1103 | break; | |
| 2000/1110 | case 1: /* First data byte */ | |
| 2000/1103 | break; | |
| 2000/1110 | default: /* Subsequent bytes */ gpioregs->clear = GPIO_L3_MODE_o; µdelay(L3_HaltTime); gpioregs->set = GPIO_L3_MODE_o; | |
| 2000/1103 | break; } | |
| 2000/1110 | µdelay(L3_ModeSetupTime); | |
| 2000/1103 | for (i = 0; i < 8; i++) L3_sendbit(data >> i); if (mode == 0) /* Address mode */ | |
| 2000/1110 | gpioregs->set = GPIO_L3_MODE_o; | |
| 2000/1103 | ||
| 2000/1110 | µdelay(L3_ModeHoldTime); | |
| 2000/1103 | L3_releasepins(); } /* * Get a byte. The mode line is set or pulsed based on the mode sequence * count. The mode line is high on entry and exit. The mod line is pulsed * before the second data byte and before each byte thereafter. This * function is never valid with mode == 0 (address cycle) as the address * is always sent on the bus, not read. */ | |
| 2000/1110 | static char L3_getbyte(int mode) | |
| 2000/1103 | { char data = 0; int i; L3_acquirepins(); | |
| 2000/1110 | gpioregs->direction &= ~(GPIO_L3_SDA_io); | |
| 2000/1103 | switch(mode) { | |
| 2000/1110 | case 0: /* Address mode - never valid */ | |
| 2000/1103 | break; | |
| 2000/1110 | case 1: /* First data byte */ | |
| 2000/1103 | break; | |
| 2000/1110 | default: /* Subsequent bytes */ gpioregs->clear = GPIO_L3_MODE_o; µdelay(L3_HaltTime); gpioregs->set = GPIO_L3_MODE_o; | |
| 2000/1103 | break; } | |
| 2000/1110 | µdelay(L3_ModeSetupTime); | |
| 2000/1103 | for (i = 0; i < 8; i++) data |= (L3_getbit() << i); | |
| 2000/1110 | µdelay(L3_ModeHoldTime); | |
| 2000/1103 | L3_releasepins(); return data; } /* * Write data to a device on the L3 bus. The address is passed as well as * the data and length. The length written is returned. The register space * is encoded in the address (low two bits are set and device address is * in the upper 6 bits). */ | |
| 2000/1110 | static int | |
| 2000/1111 | L3_write(uchar addr, uchar *data, int len) | |
| 2000/1103 | { int mode = 0; int bytes = len; L3_sendbyte(addr, mode++); while(len--) L3_sendbyte(*data++, mode++); return bytes; } /* * Read data from a device on the L3 bus. The address is passed as well as * the data and length. The length read is returned. The register space * is encoded in the address (low two bits are set and device address is * in the upper 6 bits). */ | |
| 2000/1110 | static int | |
| 2000/1111 | L3_read(uchar addr, uchar *data, int len) | |
| 2000/1103 | { int mode = 0; int bytes = len; L3_sendbyte(addr, mode++); while(len--) *data++ = L3_getbyte(mode++); return bytes; } static char Emode[] = "illegal open mode"; static char Evolume[] = "illegal volume specifier"; | |
| 2000/1109 | static void bufinit(IOstate *b) | |
| 2000/1103 | { | |
| 2000/1109 | int i; | |
| 2000/1103 | ||
| 2000/1113 | if (debug) print("#A: bufinit\n"); | |
| 2000/1109 | for (i = 0; i < Nbuf; i++) b->buf[i].virt = xalloc(Bufsize); b->bufinit = 1; }; | |
| 2000/1103 | ||
| 2000/1109 | static void setempty(IOstate *b) | |
| 2000/1103 | { | |
| 2000/1109 | int i; | |
| 2000/1103 | ||
| 2000/1113 | if (debug) print("#A: setempty\n"); | |
| 2000/1109 | for (i = 0; i < Nbuf; i++) { b->buf[i].nbytes = 0; } b->filling = b->buf; b->current = b->buf; | |
| 2000/1110 | b->next = b->buf; | |
| 2000/1103 | } | |
| 2000/1110 | static int | |
| 2000/1111 | audioqnotfull(void *x) | |
| 2000/1110 | { | |
| 2000/1111 | IOstate *s = x; | |
| 2000/1110 | /* called with lock */ | |
| 2000/1116 | return dmaidle(s->dma) || s->filling != s->current; | |
| 2000/1110 | } | |
| 2000/1103 | static void audioinit(void) { | |
| 2000/1110 | /* do nothing */ } static void audioenable(void) { | |
| 2000/1111 | uchar data[2]; | |
| 2000/1103 | L3_init(); | |
| 2000/1110 | /* Setup the uarts */ | |
| 2000/1116 | ppcregs->assignment &= ~(1<<18); | |
| 2000/1103 | ||
| 2000/1111 | gpioregs->altfunc &= ~(GPIO_SSP_TXD_o | GPIO_SSP_RXD_i | GPIO_SSP_SCLK_o | GPIO_SSP_SFRM_o); gpioregs->altfunc |= (GPIO_SSP_CLK_i); gpioregs->direction &= ~(GPIO_SSP_CLK_i); | |
| 2000/1103 | ||
| 2000/1116 | sspregs->control0 = 0; sspregs->control0 = 0x031f; /* 16 bits, TI frames, serial clock rate 3 */ sspregs->control1 = 0x0020; /* ext clock */ sspregs->control0 = 0x039f; /* enable */ | |
| 2000/1110 | /* Enable the audio power */ | |
| 2000/1111 | audiopower(1); amplifierpower(1); audiomute(0); | |
| 2000/1110 | ||
| 2000/1116 | /* external clock configured for 44100 samples/sec */ | |
| 2000/1111 | gpioregs->direction |= (GPIO_CLK_SET0_o|GPIO_CLK_SET1_o); | |
| 2000/1116 | /* This is purportedly the wrong way round: 0 should be set, 1 cleared */ gpioregs->set = GPIO_CLK_SET0_o|GPIO_CLK_SET1_o; // gpioregs->clear = GPIO_CLK_SET1_o; | |
| 2000/1110 | /* Wait for the UDA1341 to wake up */ | |
| 2000/1111 | delay(100); | |
| 2000/1110 | ||
| 2000/1111 | /* Reset the chip */ | |
| 2000/1116 | data[0] = 2<<UdaStatusSC | 1<<UdaStatusIF | 1<<UdaStatusRST; | |
| 2000/1111 | L3_write(UDA1341_L3Addr<<2 | UDA1341_STATUS, data, 1 ); | |
| 2000/1116 | gpioregs->set = EGPIO_codec_reset; gpioregs->clear = EGPIO_codec_reset; /* write uda 1341 status[0] */ data[0] &= ~(1<<UdaStatusRST); /* clear reset */ L3_write(UDA1341_L3Addr<<2 | UDA1341_STATUS, data, 1 ); | |
| 2000/1110 | ||
| 2000/1111 | /* write uda 1341 status[1] */ data[0] = 0x80 | 0x3<<UdaStatusPC | 1<<UdaStatusIGS | 1<<UdaStatusOGS; L3_write(UDA1341_L3Addr<<2 | UDA1341_STATUS, data, 1); /* write uda 1341 data0[0] (volume) */ | |
| 2000/1116 | data[0] = 15 & 0x3f; /* 6 bits, others must be 0 */ | |
| 2000/1111 | L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 1); /* write uda 1341 data0[2] (mode switch) */ data[0] = 0x80 | 3; /* mode = 3 */ L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 1); /* set mixer mode and level */ data[0] = 0xc0 | 2 /* ext address */; data[1] = 0xe0 | 2 | 4 << 2; /* mixer mode and mic level */ L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 2); /* set agc control and input amplifier gain */ data[0] = 0xc0 | 4 /* ext address */; data[1] = 0xe0 | 1<<4 | 3; /* AGC control and input ampl gain */ L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 2 ); /* set agc time constant and output level */ data[0] = 0xc0 | 6 /* ext address */; data[1] = 0xe0 | 0<<2 | 3; /* agc time constant and output level */ L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 2 ); | |
| 2000/1116 | if (debug) { print("#A: audio enabled\n"); print("\tsspregs->control0 = 0x%lux\n", sspregs->control0); print("\tsspregs->control1 = 0x%lux\n", sspregs->control1); } | |
| 2000/1103 | } | |
| 2000/1107 | static void | |
| 2000/1111 | sendaudio(IOstate *b) { | |
| 2000/1115 | /* interrupt routine calls this too */ | |
| 2000/1116 | int n; | |
| 2000/1115 | if (debug > 1) print("#A: sendaudio\n"); ilock(&b->ilock); | |
| 2000/1110 | while (b->next != b->filling) { assert(b->next->nbytes); | |
| 2000/1116 | if ((n = dmastart(b->dma, b->next->virt, b->next->nbytes)) == 0) { iostats.faildma++; | |
| 2000/1115 | break; } | |
| 2000/1116 | iostats.totaldma++; if (n == 1) iostats.idledma++; | |
| 2000/1115 | if (debug > 1) print("#A: dmastart @%p\n", b->next); b->next->nbytes = 0; | |
| 2000/1110 | b->next++; if (b->next == &b->buf[Nbuf]) | |
| 2000/1111 | b->next = &b->buf[0]; | |
| 2000/1110 | } | |
| 2000/1115 | iunlock(&b->ilock); | |
| 2000/1110 | } static void | |
| 2000/1116 | audiointr(void *x, ulong ndma) { | |
| 2000/1110 | IOstate *s = x; if (s == &audio.o) { /* Only interrupt routine touches s->current */ | |
| 2000/1115 | if (debug > 1) iprint("#A: audio interrupt @%p\n", s->current); | |
| 2000/1110 | s->current++; | |
| 2000/1111 | if (s->current == &s->buf[Nbuf]) s->current = &s->buf[0]; | |
| 2000/1116 | if (ndma > 0) sendaudio(s); | |
| 2000/1109 | } | |
| 2000/1111 | wakeup(&s->vous); | |
| 2000/1109 | } | |
| 2000/1107 | static Chan* audioattach(char *param) { return devattach('A', param); } static int audiowalk(Chan *c, char *name) { return devwalk(c, name, audiodir, nelem(audiodir), devgen); } static void audiostat(Chan *c, char *db) { devstat(c, db, audiodir, nelem(audiodir), devgen); } static Chan* audioopen(Chan *c, int omode) { switch(c->qid.path & ~CHDIR) { default: error(Eperm); break; case Qstatus: if((omode&7) != OREAD) error(Eperm); case Qvolume: case Qdir: break; case Qaudio: | |
| 2000/1109 | omode = (omode & 0x7) + 1; | |
| 2000/1110 | // if (omode & ~(Aread | Awrite)) if (omode & ~(Awrite)) | |
| 2000/1108 | error(Ebadarg); | |
| 2000/1107 | qlock(&audio); | |
| 2000/1108 | if(audio.amode & omode){ | |
| 2000/1107 | qunlock(&audio); error(Einuse); } | |
| 2000/1111 | audioenable(); | |
| 2000/1116 | memset(&iostats, 0, sizeof(iostats)); | |
| 2000/1109 | if (omode & Aread) { | |
| 2000/1108 | /* read */ | |
| 2000/1109 | audio.amode |= Aread; if(audio.i.bufinit == 0) bufinit(&audio.i); setempty(&audio.i); | |
| 2000/1114 | audio.i.chan = c; audio.i.dma = dmaalloc(0, 0, 8, 2, SSPRecvDMA, Port4SSP, audiointr, (void*)&audio.o); | |
| 2000/1108 | } if (omode & 0x2) { /* write */ | |
| 2000/1111 | // amplifierpower(1); // audiomute(0); | |
| 2000/1109 | audio.amode |= Awrite; if(audio.o.bufinit == 0) bufinit(&audio.o); setempty(&audio.o); | |
| 2000/1110 | audio.o.chan = c; | |
| 2000/1114 | audio.o.dma = dmaalloc(0, 0, 8, 2, SSPXmitDMA, Port4SSP, audiointr, (void*)&audio.o); | |
| 2000/1107 | } | |
| 2000/1109 | // mxvolume(); | |
| 2000/1115 | qunlock(&audio); | |
| 2000/1113 | if (debug) print("#A: open done\n"); | |
| 2000/1107 | break; } c = devopen(c, omode, audiodir, nelem(audiodir), devgen); c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; return c; } static void audioclose(Chan *c) { switch(c->qid.path & ~CHDIR) { default: error(Eperm); break; case Qdir: case Qvolume: case Qstatus: break; case Qaudio: | |
| 2000/1115 | if (debug > 1) print("#A: close\n"); | |
| 2000/1107 | if(c->flag & COPEN) { qlock(&audio); | |
| 2000/1110 | qlock(&audio.o); | |
| 2000/1107 | if(waserror()){ | |
| 2000/1110 | qunlock(&audio.o); | |
| 2000/1107 | qunlock(&audio); nexterror(); } | |
| 2000/1110 | if (audio.o.chan == c) { /* closing the write end */ audio.amode &= ~Awrite; | |
| 2000/1115 | if (audio.o.filling->nbytes) { | |
| 2000/1110 | audio.o.filling++; if (audio.o.filling == &audio.o.buf[Nbuf]) audio.o.filling = &audio.o.buf[0]; sendaudio(&audio.o); } | |
| 2000/1116 | delay(2000); // dmawait(audio.o.dma); if (!dmaidle(audio.o.dma)) print("dma still busy\n"); | |
| 2000/1111 | amplifierpower(0); | |
| 2000/1110 | setempty(&audio.o); | |
| 2000/1116 | dmafree(audio.o.dma); | |
| 2000/1110 | } if (audio.i.chan == c) { /* closing the read end */ audio.amode &= ~Aread; setempty(&audio.i); } if (audio.amode == 0) { /* turn audio off */ audiopower(0); } | |
| 2000/1107 | poperror(); | |
| 2000/1110 | qunlock(&audio.o); | |
| 2000/1107 | qunlock(&audio); | |
| 2000/1116 | print("total dmas: %lud\n", iostats.totaldma); print("dmas while idle: %lud\n", iostats.idledma); print("dmas while busy: %lud\n", iostats.faildma); | |
| 2000/1107 | } 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 *a; n0 = n; a = v; switch(c->qid.path & ~CHDIR) { default: error(Eperm); break; case Qdir: return devdirread(c, a, n, audiodir, nelem(audiodir), devgen); case Qaudio: break; case Qstatus: buf[0] = 0; snprint(buf, sizeof(buf), "bytes %lud\ntime %lld\n", audio.totcount, audio.tottime); return readstr(offset, a, n, buf); case Qvolume: 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, a, 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]; | |
| 2000/1109 | char *p; IOstate *a; | |
| 2000/1107 | ||
| 2000/1109 | p = vp; | |
| 2000/1107 | n0 = n; switch(c->qid.path & ~CHDIR) { default: error(Eperm); break; case Qvolume: v = Vaudio; left = 1; right = 1; in = 1; out = 1; if(n > sizeof(buf)-1) n = sizeof(buf)-1; | |
| 2000/1109 | memmove(buf, p, n); | |
| 2000/1107 | buf[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(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; } 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], "reset") == 0) { | |
| 2000/1111 | // resetlevel(); // mxvolume(); | |
| 2000/1107 | 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; } error(Evolume); break; cont0:; } break; case Qaudio: | |
| 2000/1115 | if (debug > 1) print("#A: write %ld\n", n); | |
| 2000/1109 | if((audio.amode & Awrite) == 0) | |
| 2000/1107 | error(Emode); | |
| 2000/1109 | a = &audio.o; qlock(a); | |
| 2000/1107 | if(waserror()){ | |
| 2000/1109 | qunlock(a); | |
| 2000/1107 | nexterror(); } while(n > 0) { | |
| 2000/1109 | /* wait if dma in progress */ | |
| 2000/1116 | while (!dmaidle(a->dma) && a->filling == a->current) { | |
| 2000/1115 | if (debug > 1) print("#A: sleep\n"); | |
| 2000/1110 | sleep(&a->vous, audioqnotfull, a); | |
| 2000/1115 | } | |
| 2000/1107 | ||
| 2000/1109 | m = Bufsize - a->filling->nbytes; | |
| 2000/1107 | if(m > n) m = n; | |
| 2000/1111 | memmove(a->filling->virt + a->filling->nbytes, p, m); | |
| 2000/1107 | ||
| 2000/1109 | a->filling->nbytes += m; | |
| 2000/1107 | n -= m; | |
| 2000/1109 | p += m; if(a->filling->nbytes >= Bufsize) { | |
| 2000/1115 | if (debug > 1) print("#A: filled @%p\n", a->filling); | |
| 2000/1109 | a->filling++; if (a->filling == &a->buf[Nbuf]) a->filling = a->buf; | |
| 2000/1110 | sendaudio(a); | |
| 2000/1107 | } } poperror(); | |
| 2000/1109 | qunlock(a); | |
| 2000/1107 | break; } return n0 - n; } | |
| 2000/1111 | Dev uda1341devtab = { | |
| 2000/1107 | 'A', "audio", devreset, audioinit, audioattach, devclone, audiowalk, audiostat, audioopen, devcreate, audioclose, audioread, devbread, audiowrite, devbwrite, devremove, devwstat, }; | |