diff --git a/sys/src/9/pc/audiosb16.c b/sys/src/9/pc/audiosb16.c index eed65aa42..ac80cbda0 100644 --- a/sys/src/9/pc/audiosb16.c +++ b/sys/src/9/pc/audiosb16.c @@ -9,8 +9,9 @@ #include "io.h" #include "../port/error.h" -typedef struct AQueue AQueue; -typedef struct Buf Buf; +typedef struct Ring Ring; +typedef struct Blaster Blaster; +typedef struct Ctlr Ctlr; enum { @@ -31,82 +32,21 @@ enum Speed = 44100, Ncmd = 50, /* max volume command words */ + + Blocksize = 4096, + Blocks = 65536/Blocksize, }; -enum +struct Ring { - Bufsize = 1024, /* 5.8 ms each, must be power of two */ - Nbuf = 128, /* .74 seconds total */ - SBswab = 0, + uchar *buf; + ulong nbuf; + + ulong ri; + ulong wi; }; -#define CACHELINESZ 8 -#define UNCACHED(type, v) (type*)((ulong)(v)) - -struct Buf -{ - uchar* virt; - ulong phys; - Buf* next; -}; - -struct AQueue -{ - Lock; - Buf* first; - Buf* last; -}; - -static struct -{ - QLock; - Rendez vous; - int buffered; /* number of bytes en route */ - int curcount; /* how much data in current buffer */ - int active; /* boolean dma running */ - int intr; /* boolean an interrupt has happened */ - int rivol[Nvol]; /* right/left input/output volumes */ - int livol[Nvol]; - int rovol[Nvol]; - int lovol[Nvol]; - int major; /* SB16 major version number (sb 4) */ - int minor; /* SB16 minor version number */ - ulong totcount; /* how many bytes processed since open */ - vlong tottime; /* time at which totcount bytes were processed */ - - Buf buf[Nbuf]; /* buffers and queues */ - AQueue empty; - AQueue full; - Buf* current; - Buf* filling; - - int probed; - int ctlrno; -} audio; - -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 -}; - -static struct +struct Blaster { Lock; int reset; /* io ports to the sound blaster */ @@ -121,161 +61,267 @@ static struct int clri401; int dma; - void (*startdma)(void); - void (*intr)(void); -} blaster; + void (*startdma)(Ctlr*); + void (*intr)(Ctlr*); +}; -static void swab(uchar*); +struct Ctlr +{ + QLock; + Rendez vous; + int active; /* boolean dma running */ + int rivol[Nvol]; /* right/left input/output volumes */ + int livol[Nvol]; + int rovol[Nvol]; + int lovol[Nvol]; + int major; /* SB16 major version number (sb 4) */ + int minor; /* SB16 minor version number */ + ulong totcount; /* how many bytes processed since open */ + vlong tottime; /* time at which totcount bytes were processed */ + Ring ring; /* dma ring buffer */ + Blaster blaster; + Audio *adev; +}; + +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 +}; static char Emajor[] = "soundblaster not responding/wrong version"; static char Emode[] = "illegal open mode"; static char Evolume[] = "illegal volume specifier"; + +static long +buffered(Ring *r) +{ + ulong ri, wi; + + ri = r->ri; + wi = r->wi; + if(wi >= ri) + return wi - ri; + else + return r->nbuf - (ri - wi); +} + +static long +available(Ring *r) +{ + long m; + + m = (r->nbuf - 1) - buffered(r); + if(m < 0) + m = 0; + return m; +} + +static long +readring(Ring *r, uchar *p, long n) +{ + long n0, m; + + n0 = n; + while(n > 0){ + if((m = buffered(r)) <= 0) + break; + if(m > n) + m = n; + if(p){ + if(r->ri + m > r->nbuf) + m = r->nbuf - r->ri; + memmove(p, r->buf + r->ri, m); + p += m; + } + r->ri = (r->ri + m) % r->nbuf; + n -= m; + } + return n0 - n; +} + +static long +writering(Ring *r, uchar *p, long n) +{ + long n0, m; + + n0 = n; + while(n > 0){ + if((m = available(r)) <= 0) + break; + if(m > n) + m = n; + if(p){ + if(r->wi + m > r->nbuf) + m = r->nbuf - r->wi; + memmove(r->buf + r->wi, p, m); + p += m; + } + r->wi = (r->wi + m) % r->nbuf; + n -= m; + } + return n0 - n; +} + static int -sbcmd(int val) +sbcmd(Blaster *blaster, int val) { int i, s; for(i=1<<16; i!=0; i--) { - s = inb(blaster.wstatus); + s = inb(blaster->wstatus); if((s & 0x80) == 0) { - outb(blaster.write, val); + outb(blaster->write, val); return 0; } } - print("#A%d: sbcmd (%#.2x) timeout\n", audio.ctlrno, val); /**/ return 1; } static int -sbread(void) +sbread(Blaster *blaster) { int i, s; for(i=1<<16; i!=0; i--) { - s = inb(blaster.rstatus); + s = inb(blaster->rstatus); if((s & 0x80) != 0) { - return inb(blaster.read); + return inb(blaster->read); } } - print("#A%d: sbread did not respond\n", audio.ctlrno); /**/ return -1; } static int -ess1688w(int reg, int val) +ess1688w(Blaster *blaster, int reg, int val) { - if(sbcmd(reg) || sbcmd(val)) + if(sbcmd(blaster, reg) || sbcmd(blaster, val)) return 1; - return 0; } static int -ess1688r(int reg) +ess1688r(Blaster *blaster, int reg) { - if(sbcmd(0xC0) || sbcmd(reg)) + if(sbcmd(blaster, 0xC0) || sbcmd(blaster, reg)) return -1; - - return sbread(); + return sbread(blaster); } static int -mxcmd(int addr, int val) +mxcmd(Blaster *blaster, int addr, int val) { - - outb(blaster.mixaddr, addr); - outb(blaster.mixdata, val); + outb(blaster->mixaddr, addr); + outb(blaster->mixdata, val); return 1; } static int -mxread(int addr) +mxread(Blaster *blaster, int addr) { int s; - outb(blaster.mixaddr, addr); - s = inb(blaster.mixdata); + outb(blaster->mixaddr, addr); + s = inb(blaster->mixdata); return s; } static void -mxcmds(int s, int v) +mxcmds(Blaster *blaster, int s, int v) { if(v > 100) v = 100; if(v < 0) v = 0; - mxcmd(s, (v*255)/100); + mxcmd(blaster, s, (v*255)/100); } static void -mxcmdt(int s, int v) +mxcmdt(Blaster *blaster, int s, int v) { if(v > 100) v = 100; if(v <= 0) - mxcmd(s, 0); + mxcmd(blaster, s, 0); else - mxcmd(s, 255-100+v); + mxcmd(blaster, s, 255-100+v); } static void -mxcmdu(int s, int v) +mxcmdu(Blaster *blaster, int s, int v) { if(v > 100) v = 100; if(v <= 0) v = 0; - mxcmd(s, 128-50+v); + mxcmd(blaster, s, 128-50+v); } static void -mxvolume(void) +mxvolume(Ctlr *ctlr) { + Blaster *blaster; int *left, *right; int source; if(0){ - left = audio.livol; - right = audio.rivol; + left = ctlr->livol; + right = ctlr->rivol; }else{ - left = audio.lovol; - right = audio.rovol; + left = ctlr->lovol; + right = ctlr->rovol; } - ilock(&blaster); + blaster = &ctlr->blaster; - mxcmd(0x30, 255); /* left master */ - mxcmd(0x31, 255); /* right master */ - mxcmd(0x3f, 0); /* left igain */ - mxcmd(0x40, 0); /* right igain */ - mxcmd(0x41, 0); /* left ogain */ - mxcmd(0x42, 0); /* right ogain */ + ilock(blaster); - mxcmds(0x32, left[Vaudio]); - mxcmds(0x33, right[Vaudio]); + mxcmd(blaster, 0x30, 255); /* left master */ + mxcmd(blaster, 0x31, 255); /* right master */ + mxcmd(blaster, 0x3f, 0); /* left igain */ + mxcmd(blaster, 0x40, 0); /* right igain */ + mxcmd(blaster, 0x41, 0); /* left ogain */ + mxcmd(blaster, 0x42, 0); /* right ogain */ - mxcmds(0x34, left[Vsynth]); - mxcmds(0x35, right[Vsynth]); + mxcmds(blaster, 0x32, left[Vaudio]); + mxcmds(blaster, 0x33, right[Vaudio]); - mxcmds(0x36, left[Vcd]); - mxcmds(0x37, right[Vcd]); + mxcmds(blaster, 0x34, left[Vsynth]); + mxcmds(blaster, 0x35, right[Vsynth]); - mxcmds(0x38, left[Vline]); - mxcmds(0x39, right[Vline]); + mxcmds(blaster, 0x36, left[Vcd]); + mxcmds(blaster, 0x37, right[Vcd]); - mxcmds(0x3a, left[Vmic]); - mxcmds(0x3b, left[Vspeaker]); + mxcmds(blaster, 0x38, left[Vline]); + mxcmds(blaster, 0x39, right[Vline]); - mxcmdu(0x44, left[Vtreb]); - mxcmdu(0x45, right[Vtreb]); + mxcmds(blaster, 0x3a, left[Vmic]); + mxcmds(blaster, 0x3b, left[Vspeaker]); - mxcmdu(0x46, left[Vbass]); - mxcmdu(0x47, right[Vbass]); + mxcmdu(blaster, 0x44, left[Vtreb]); + mxcmdu(blaster, 0x45, right[Vtreb]); + + mxcmdu(blaster, 0x46, left[Vbass]); + mxcmdu(blaster, 0x47, right[Vbass]); source = 0; if(left[Vsynth]) @@ -293,85 +339,34 @@ mxvolume(void) if(left[Vmic]) source |= 1<<0; if(0) - mxcmd(0x3c, 0); /* output switch */ + mxcmd(blaster, 0x3c, 0); /* output switch */ else - mxcmd(0x3c, source); - mxcmd(0x3d, source); /* input left switch */ - mxcmd(0x3e, source); /* input right switch */ - iunlock(&blaster); -} - -static Buf* -getbuf(AQueue *q) -{ - Buf *b; - - ilock(q); - b = q->first; - if(b) - q->first = b->next; - iunlock(q); - - return b; + mxcmd(blaster, 0x3c, source); + mxcmd(blaster, 0x3d, source); /* input left switch */ + mxcmd(blaster, 0x3e, source); /* input right switch */ + iunlock(blaster); } static void -putbuf(AQueue *q, Buf *b) +contindma(Ctlr *ctlr) { + Blaster *blaster; + Ring *ring; - ilock(q); - b->next = 0; - if(q->first) - q->last->next = b; - else - q->first = b; - q->last = b; - iunlock(q); -} + blaster = &ctlr->blaster; + ring = &ctlr->ring; + if(buffered(ring) >= Blocksize){ + ring->ri = ring->nbuf - dmacount(blaster->dma); -/* - * move the dma to the next buffer - */ -static void -contindma(void) -{ - Buf *b; - - if(!audio.active) - goto shutdown; - - b = audio.current; - if(b){ - audio.totcount += Bufsize; - audio.tottime = todget(nil); + ctlr->totcount += Blocksize; + ctlr->tottime = todget(nil); + }else{ + dmaend(blaster->dma); + sbcmd(blaster, 0xd9); /* exit at end of count */ + sbcmd(blaster, 0xd5); /* pause */ + ctlr->active = 0; } - if(0) { - if(b){ - putbuf(&audio.full, b); - audio.buffered += Bufsize; - } - b = getbuf(&audio.empty); - } else { - if(b){ - putbuf(&audio.empty, b); - audio.buffered -= Bufsize; - } - b = getbuf(&audio.full); - } - audio.current = b; - if(b == 0) - goto shutdown; - if(dmasetup(blaster.dma, b->virt, Bufsize, 0) >= 0) - return; - print("#A%d: dmasetup fail\n", audio.ctlrno); - putbuf(&audio.empty, b); - -shutdown: - dmaend(blaster.dma); - sbcmd(0xd9); /* exit at end of count */ - sbcmd(0xd5); /* pause */ - audio.curcount = 0; - audio.active = 0; + wakeup(&ctlr->vous); } /* @@ -379,60 +374,64 @@ shutdown: * start first dma */ static void -sb16startdma(void) +sb16startdma(Ctlr *ctlr) { - ulong count; + Blaster *blaster; + Ring *ring; + long count; int speed; - ilock(&blaster); - dmaend(blaster.dma); + blaster = &ctlr->blaster; + ring = &ctlr->ring; + ilock(blaster); + dmaend(blaster->dma); if(0) { - sbcmd(0x42); /* input sampling rate */ - speed = audio.livol[Vspeed]; + sbcmd(blaster, 0x42); /* input sampling rate */ + speed = ctlr->livol[Vspeed]; } else { - sbcmd(0x41); /* output sampling rate */ - speed = audio.lovol[Vspeed]; + sbcmd(blaster, 0x41); /* output sampling rate */ + speed = ctlr->lovol[Vspeed]; } - sbcmd(speed>>8); - sbcmd(speed); + sbcmd(blaster, speed>>8); + sbcmd(blaster, speed); if(0) - sbcmd(0xbe); /* A/D, autoinit */ + sbcmd(blaster, 0xbe); /* A/D, autoinit */ else - sbcmd(0xb6); /* D/A, autoinit */ - sbcmd(0x30); /* stereo, signed 16 bit */ + sbcmd(blaster, 0xb6); /* D/A, autoinit */ - /* - * not really sure if this is correct, but - * it works in bochs - */ - count = (Bufsize >> 2) - 1; - sbcmd(count); - sbcmd(count>>8); + sbcmd(blaster, 0x30); /* stereo, signed 16 bit */ - audio.active = 1; - contindma(); - iunlock(&blaster); + count = (Blocksize>>1) - 1; + sbcmd(blaster, count); + sbcmd(blaster, count>>8); + + ctlr->active = 1; + if(dmasetup(blaster->dma, ring->buf, ring->nbuf, DMAWRITE|DMALOOP) < 0){ + ctlr->active = 0; + print("#A%d: dmasetup fail\n", ctlr->adev->ctlrno); + } + iunlock(blaster); } static int -ess1688reset(void) +ess1688reset(Blaster *blaster, int ctlrno) { int i; - outb(blaster.reset, 3); + outb(blaster->reset, 3); delay(1); /* >3 υs */ - outb(blaster.reset, 0); + outb(blaster->reset, 0); delay(1); - i = sbread(); + i = sbread(blaster); if(i != 0xAA) { - print("#A%d: no response %#.2x\n", audio.ctlrno, i); + print("#A%d: no response %#.2x\n", ctlrno, i); return 1; } - if(sbcmd(0xC6)){ /* extended mode */ - print("#A%d: barf 3\n", audio.ctlrno); + if(sbcmd(blaster, 0xC6)){ /* extended mode */ + print("#A%d: barf 3\n", ctlrno); return 1; } @@ -440,23 +439,28 @@ ess1688reset(void) } static void -ess1688startdma(void) +ess1688startdma(Ctlr *ctlr) { + Blaster *blaster; + Ring *ring; ulong count; int speed, x; - ilock(&blaster); - dmaend(blaster.dma); + blaster = &ctlr->blaster; + ring = &ctlr->ring; - ess1688reset(); + ilock(blaster); + dmaend(blaster->dma); + + ess1688reset(blaster, ctlr->adev->ctlrno); /* * Set the speed. */ if(0) - speed = audio.livol[Vspeed]; + speed = ctlr->livol[Vspeed]; else - speed = audio.lovol[Vspeed]; + speed = ctlr->lovol[Vspeed]; if(speed < 4000) speed = 4000; else if(speed > 48000) @@ -466,323 +470,226 @@ ess1688startdma(void) x = 0x80|(256-(795500+speed/2)/speed); else x = 128-(397700+speed/2)/speed; - ess1688w(0xA1, x & 0xFF); + ess1688w(blaster, 0xA1, x & 0xFF); speed = (speed * 9) / 20; x = 256 - 7160000 / (speed * 82); - ess1688w(0xA2, x & 0xFF); + ess1688w(blaster, 0xA2, x & 0xFF); if(0) - ess1688w(0xB8, 0x0E); /* A/D, autoinit */ + ess1688w(blaster, 0xB8, 0x0E); /* A/D, autoinit */ else - ess1688w(0xB8, 0x04); /* D/A, autoinit */ - x = ess1688r(0xA8) & ~0x03; - ess1688w(0xA8, x|0x01); /* 2 channels */ - ess1688w(0xB9, 2); /* demand mode, 4 bytes per request */ + ess1688w(blaster, 0xB8, 0x04); /* D/A, autoinit */ + x = ess1688r(blaster, 0xA8) & ~0x03; + ess1688w(blaster, 0xA8, x|0x01); /* 2 channels */ + ess1688w(blaster, 0xB9, 2); /* demand mode, 4 bytes per request */ if(1) - ess1688w(0xB6, 0); /* for output */ + ess1688w(blaster, 0xB6, 0); /* for output */ - ess1688w(0xB7, 0x71); - ess1688w(0xB7, 0xBC); + ess1688w(blaster, 0xB7, 0x71); + ess1688w(blaster, 0xB7, 0xBC); - x = ess1688r(0xB1) & 0x0F; - ess1688w(0xB1, x|0x50); - x = ess1688r(0xB2) & 0x0F; - ess1688w(0xB2, x|0x50); + x = ess1688r(blaster, 0xB1) & 0x0F; + ess1688w(blaster, 0xB1, x|0x50); + x = ess1688r(blaster, 0xB2) & 0x0F; + ess1688w(blaster, 0xB2, x|0x50); if(1) - sbcmd(0xD1); /* speaker on */ + sbcmd(blaster, 0xD1); /* speaker on */ - count = -Bufsize; - ess1688w(0xA4, count & 0xFF); - ess1688w(0xA5, (count>>8) & 0xFF); - x = ess1688r(0xB8); - ess1688w(0xB8, x|0x05); + count = -Blocksize; + ess1688w(blaster, 0xA4, count & 0xFF); + ess1688w(blaster, 0xA5, (count>>8) & 0xFF); + x = ess1688r(blaster, 0xB8); + ess1688w(blaster, 0xB8, x|0x05); - audio.active = 1; - contindma(); - iunlock(&blaster); -} - -/* - * if audio is stopped, - * start it up again. - */ -static void -pokeaudio(void) -{ - if(!audio.active) - blaster.startdma(); + ctlr->active = 1; + if(dmasetup(blaster->dma, ring->buf, ring->nbuf, DMAWRITE|DMALOOP) < 0){ + ctlr->active = 0; + print("#A%d: dmasetup fail\n", ctlr->adev->ctlrno); + } + iunlock(blaster); } static void -sb16intr(void) +sb16intr(Ctlr *ctlr) { + Blaster *blaster; int stat; - stat = mxread(0x82); /* get irq status */ - if(stat & 7) { - ilock(&blaster); + blaster = &ctlr->blaster; + ilock(blaster); + stat = mxread(blaster, 0x82); /* get irq status */ + if(stat & 3){ + contindma(ctlr); if(stat & 2) - inb(blaster.clri16); + inb(blaster->clri16); else if(stat & 1) - inb(blaster.clri8); - if(stat & 3){ - contindma(); - iunlock(&blaster); - - audio.intr = 1; - wakeup(&audio.vous); - } - if(stat & 4) - inb(blaster.clri401); - } + inb(blaster->clri8); + } else if(stat & 4) + inb(blaster->clri401); + iunlock(blaster); } static void -ess1688intr(void) +ess1688intr(Ctlr *ctlr) { - if(audio.active){ - ilock(&blaster); - contindma(); - inb(blaster.clri8); - iunlock(&blaster); - audio.intr = 1; - wakeup(&audio.vous); + Blaster *blaster; + + blaster = &ctlr->blaster; + ilock(blaster); + contindma(ctlr); + inb(blaster->clri8); + iunlock(blaster); +} + +static void +audiointr(Ureg *, void *arg) +{ + Audio *adev; + Ctlr *ctlr; + + adev = arg; + ctlr = adev->ctlr; + if(!ctlr->active){ + iprint("#A%d: unexpected %s interrupt\n", + ctlr->adev->ctlrno, ctlr->adev->name); return; } - print("#A%d: unexpected ess1688 interrupt\n", audio.ctlrno); -} - -void -audiosbintr(void) -{ - /* - * Carrera interrupt interface. - */ - blaster.intr(); + ctlr->blaster.intr(ctlr); } static void -pcaudiosbintr(Ureg*, void*) +setempty(Ctlr *ctlr) { - /* - * x86 interrupt interface. - */ - blaster.intr(); -} - -static int -anybuf(void*) -{ - return audio.intr; -} - -/* - * wait for some output to get - * empty buffers back. - */ -static int -waitaudio(void) -{ - - audio.intr = 0; - pokeaudio(); - tsleep(&audio.vous, anybuf, 0, 10000); - if(audio.intr == 0) { - audio.active = 0; - print("#A%d: audio timeout\n", audio.ctlrno); /**/ - return -1; - } - return 0; -} - -static void -swab(uchar *a) -{ - ulong *p, *ep, b; - - if(!SBswab){ - USED(a); - return; - } - p = (ulong*)a; - ep = p + (Bufsize>>2); - while(p < ep) { - b = *p; - b = (b>>24) | (b<<24) | - ((b&0xff0000) >> 8) | - ((b&0x00ff00) << 8); - *p++ = b; - } + ilock(&ctlr->blaster); + ctlr->ring.ri = 0; + ctlr->ring.wi = 0; + ctlr->totcount = 0; + ctlr->tottime = 0LL; + iunlock(&ctlr->blaster); } static void -sbbufinit(void) -{ - int i; - uchar *p; - - p = (uchar*)(((ulong)xalloc((Nbuf+1) * Bufsize) + Bufsize-1) & - ~(Bufsize-1)); - if (p == nil) - panic("sbbufinit: no memory"); - for(i=0; ilovol[i] = volumes[i].ilval; + ctlr->rovol[i] = volumes[i].irval; + ctlr->livol[i] = volumes[i].ilval; + ctlr->rivol[i] = volumes[i].irval; } } static long -audiobuffered(Audio *) +audiobuffered(Audio *adev) { - return audio.buffered; + return buffered(&((Ctlr*)adev->ctlr)->ring); } static long -audiostatus(Audio *, void *a, long n, vlong off) +audiostatus(Audio *adev, void *a, long n, vlong off) { char buf[300]; + Ctlr *ctlr; - snprint(buf, sizeof(buf), "bufsize %6d buffered %6d offset %10lud time %19lld\n", - Bufsize, audio.buffered, audio.totcount, audio.tottime); + ctlr = adev->ctlr; + snprint(buf, sizeof(buf), + "buffered %.4lx/%.4lx offset %10lud time %19lld\n", + buffered(&ctlr->ring), available(&ctlr->ring), + ctlr->totcount, ctlr->tottime); return readstr(off, a, n, buf); } -static long -audiowrite(Audio *, void *vp, long n, vlong) +static int +inactive(void *arg) { - long m, n0; - Buf *b; - char *a; + Ctlr *ctlr = arg; - a = vp; - n0 = n; - qlock(&audio); - if(waserror()){ - qunlock(&audio); - nexterror(); - } - - while(n > 0) { - b = audio.filling; - if(b == 0) { - b = getbuf(&audio.empty); - if(b == 0) { - if(waitaudio()) - pokeaudio(); - continue; - } - audio.filling = b; - audio.curcount = 0; - } - - m = Bufsize-audio.curcount; - if(m > n) - m = n; - memmove(b->virt+audio.curcount, a, m); - - audio.curcount += m; - n -= m; - a += m; - audio.buffered += m; - if(audio.curcount >= Bufsize) { - audio.filling = 0; - swab(b->virt); - putbuf(&audio.full, b); - pokeaudio(); - } - } - poperror(); - qunlock(&audio); - - return n0 - n; -} - -static void -audioclose(Audio *) -{ - qlock(&audio); - if(waserror()){ - qunlock(&audio); - nexterror(); - } - if(1) { - Buf *b; - - /* flush out last partial buffer */ - b = audio.filling; - if(b) { - audio.filling = 0; - memset(b->virt+audio.curcount, 0, Bufsize-audio.curcount); - audio.buffered += Bufsize-audio.curcount; - swab(b->virt); - putbuf(&audio.full, b); - } - if(!audio.active && audio.full.first) - pokeaudio(); - } - while(audio.active && waitaudio() == 0) - ; - setempty(); - audio.curcount = 0; - poperror(); - qunlock(&audio); + return !ctlr->active; } static int -ess1688(ISAConf* sbconf) +anybuf(void *arg) +{ + Ctlr *ctlr = arg; + + return available(&ctlr->ring) || inactive(ctlr); +} + +static long +audiowrite(Audio *adev, void *vp, long n, vlong) +{ + uchar *p, *e; + Ctlr *ctlr; + Ring *ring; + long m; + + p = vp; + e = p + n; + ctlr = adev->ctlr; + qlock(ctlr); + if(waserror()){ + qunlock(ctlr); + nexterror(); + } + ring = &ctlr->ring; + while(p < e) { + if((m = writering(ring, p, e - p)) <= 0){ + if(!ctlr->active && ring->ri == 0) + ctlr->blaster.startdma(ctlr); + if(!ctlr->active){ + setempty(ctlr); + continue; + } + sleep(&ctlr->vous, anybuf, ctlr); + continue; + } + p += m; + } + poperror(); + qunlock(ctlr); + + return p - (uchar*)vp; +} + +static void +audioclose(Audio *adev) +{ + Ctlr *ctlr; + + ctlr = adev->ctlr; + qlock(ctlr); + if(waserror()){ + qunlock(ctlr); + nexterror(); + } + sleep(&ctlr->vous, inactive, ctlr); + setempty(ctlr); + poperror(); + qunlock(ctlr); +} + +static int +ess1688(ISAConf* sbconf, Blaster *blaster, int ctlrno) { int i, major, minor; /* * Try for ESS1688. */ - sbcmd(0xE7); /* get version */ - major = sbread(); - minor = sbread(); + sbcmd(blaster, 0xE7); /* get version */ + major = sbread(blaster); + minor = sbread(blaster); if(major != 0x68 || minor != 0x8B){ - print("#A%d: model %#.2x %#.2x; not ESS1688 compatible\n", audio.ctlrno, major, minor); - return 1; + print("#A%d: model %#.2x %#.2x; not ESS1688 compatible\n", ctlrno, major, minor); + return -1; } - ess1688reset(); + ess1688reset(blaster, ctlrno); switch(sbconf->irq){ case 2: @@ -799,10 +706,10 @@ ess1688(ISAConf* sbconf) i = 0x50|(3<<2); break; default: - print("#A%d: bad ESS1688 irq %d\n", audio.ctlrno, sbconf->irq); + print("#A%d: bad ESS1688 irq %d\n", ctlrno, sbconf->irq); return 1; } - ess1688w(0xB1, i); + ess1688w(blaster, 0xB1, i); switch(sbconf->dma){ case 0: @@ -815,15 +722,15 @@ ess1688(ISAConf* sbconf) i = 0x50|(3<<2); break; default: - print("#A%d: bad ESS1688 dma %lud\n", audio.ctlrno, sbconf->dma); + print("#A%d: bad ESS1688 dma %lud\n", ctlrno, sbconf->dma); return 1; } - ess1688w(0xB2, i); + ess1688w(blaster, 0xB2, i); - ess1688reset(); + ess1688reset(blaster, ctlrno); - blaster.startdma = ess1688startdma; - blaster.intr = ess1688intr; + blaster->startdma = ess1688startdma; + blaster->intr = ess1688intr; return 0; } @@ -831,26 +738,19 @@ ess1688(ISAConf* sbconf) static int audioprobe(Audio *adev) { + static int irq[] = {9,5,7,10}; + + Ctlr *ctlr; + Blaster *blaster; ISAConf sbconf; int i, x; - static int irq[] = {2,5,7,10}; - static int dma16[] = {0,5,6,7}; - - if(audio.probed) - return -1; sbconf.port = 0x220; - sbconf.dma = 1; sbconf.irq = 5; + sbconf.dma = 0; if(isaconfig("audio", adev->ctlrno, &sbconf) == 0) return -1; - audio.probed = 1; - audio.ctlrno = adev->ctlrno; - if(sbconf.type == nil || - (cistrcmp(sbconf.type, "sb16") != 0 && - cistrcmp(sbconf.type, "ess1688") != 0)) - return -1; switch(sbconf.port){ case 0x220: case 0x240: @@ -858,124 +758,126 @@ audioprobe(Audio *adev) case 0x280: break; default: - print("#A%d: bad port %#lux\n", audio.ctlrno, sbconf.port); + print("#A%d: bad port %#lux\n", adev->ctlrno, sbconf.port); return -1; } if(ioalloc(sbconf.port, 0x10, 0, "audio") < 0){ - print("#A%d: cannot ioalloc range %lux+0x10\n", audio.ctlrno, sbconf.port); + print("#A%d: cannot ioalloc range %lux+0x10\n", adev->ctlrno, sbconf.port); return -1; } if(ioalloc(sbconf.port+0x100, 1, 0, "audio.mpu401") < 0){ iofree(sbconf.port); - print("#A%d: cannot ioalloc range %lux+0x01\n", audio.ctlrno, sbconf.port+0x100); + print("#A%d: cannot ioalloc range %lux+0x01\n", adev->ctlrno, sbconf.port+0x100); return -1; } - blaster.reset = sbconf.port + 0x6; - blaster.read = sbconf.port + 0xa; - blaster.write = sbconf.port + 0xc; - blaster.wstatus = sbconf.port + 0xc; - blaster.rstatus = sbconf.port + 0xe; - blaster.mixaddr = sbconf.port + 0x4; - blaster.mixdata = sbconf.port + 0x5; - blaster.clri8 = sbconf.port + 0xe; - blaster.clri16 = sbconf.port + 0xf; - blaster.clri401 = sbconf.port + 0x100; - blaster.dma = sbconf.dma; + ctlr = malloc(sizeof(Ctlr)); + ctlr->adev = adev; + adev->ctlr = ctlr; - blaster.startdma = sb16startdma; - blaster.intr = sb16intr; + blaster = &ctlr->blaster; + blaster->reset = sbconf.port + 0x6; + blaster->read = sbconf.port + 0xa; + blaster->write = sbconf.port + 0xc; + blaster->wstatus = sbconf.port + 0xc; + blaster->rstatus = sbconf.port + 0xe; + blaster->mixaddr = sbconf.port + 0x4; + blaster->mixdata = sbconf.port + 0x5; + blaster->clri8 = sbconf.port + 0xe; + blaster->clri16 = sbconf.port + 0xf; + blaster->clri401 = sbconf.port + 0x100; + blaster->dma = sbconf.dma; - resetlevel(); + blaster->startdma = sb16startdma; + blaster->intr = sb16intr; - outb(blaster.reset, 1); - delay(3); /* >3 υs */ - outb(blaster.reset, 0); + resetlevel(ctlr); + + outb(blaster->reset, 1); + delay(1); /* >3 υs */ + outb(blaster->reset, 0); delay(1); - i = sbread(); + i = sbread(blaster); if(i != 0xaa) { - print("#A%d: no response #%.2x\n", audio.ctlrno, i); + print("#A%d: no response #%.2x\n", adev->ctlrno, i); iofree(sbconf.port); iofree(sbconf.port+0x100); + free(ctlr); return -1; } - sbcmd(0xe1); /* get version */ - audio.major = sbread(); - audio.minor = sbread(); + sbcmd(blaster, 0xe1); /* get version */ + ctlr->major = sbread(blaster); + ctlr->minor = sbread(blaster); - if(audio.major != 4) { - if(audio.major != 3 || audio.minor != 1 || ess1688(&sbconf)){ + if(ctlr->major != 4) { + if(ctlr->major != 3 || ctlr->minor != 1 || ess1688(&sbconf, blaster, adev->ctlrno)){ print("#A%d: model %#.2x %#.2x; not SB 16 compatible\n", - audio.ctlrno, audio.major, audio.minor); + adev->ctlrno, ctlr->major, ctlr->minor); iofree(sbconf.port); iofree(sbconf.port+0x100); return -1; } - audio.major = 4; + ctlr->major = 4; } /* * initialize the mixer */ - mxcmd(0x00, 0); /* Reset mixer */ - mxvolume(); + mxcmd(blaster, 0x00, 0); /* Reset mixer */ + mxvolume(ctlr); - /* - * Attempt to set IRQ/DMA channels. - * On old ISA boards, these registers are writable. - * On Plug-n-Play boards, these are read-only. - * - * To accomodate both, we write to the registers, - * but then use the contents in case the write is - * disallowed. - */ + /* set irq */ for(i=0; idma>=5 && blaster->dma<=7){ + x = mxread(blaster, 0x81); + mxcmd(blaster, 0x81, (1<dma) & 0xF0 | (x & 0x0F)); + } + x = mxread(blaster, 0x81); + for(i=5; i<=7; i++){ + if(x & (1<dma = i; break; } } - } - x = mxread(0x81); - for(i=0; i<4; i++) - if(x & (1<dma<5){ + blaster->dma = 7; + continue; } + break; + } - print("#A%d: %s port 0x%04lux irq %d dma %d\n", audio.ctlrno, sbconf.type, - sbconf.port, sbconf.irq, blaster.dma); + print("#A%d: %s port 0x%04lux irq %d dma %d\n", adev->ctlrno, sbconf.type, + sbconf.port, sbconf.irq, blaster->dma); - if(dmainit(blaster.dma, Bufsize)) + ctlr->ring.nbuf = Blocks*Blocksize; + if(dmainit(blaster->dma, ctlr->ring.nbuf)){ + free(ctlr); return -1; - intrenable(sbconf.irq, pcaudiosbintr, 0, BUSUNKNOWN, "sb16"); + } + ctlr->ring.buf = dmabva(blaster->dma); - sbbufinit(); - setempty(); - mxvolume(); + intrenable(sbconf.irq, audiointr, adev, BUSUNKNOWN, sbconf.type); + + setempty(ctlr); + mxvolume(ctlr); adev->write = audiowrite; adev->close = audioclose; @@ -989,4 +891,5 @@ void audiosb16link(void) { addaudiocard("sb16", audioprobe); + addaudiocard("ess1688", audioprobe); } diff --git a/sys/src/9/pc/devfloppy.c b/sys/src/9/pc/devfloppy.c index 4a85fa63d..00323073e 100644 --- a/sys/src/9/pc/devfloppy.c +++ b/sys/src/9/pc/devfloppy.c @@ -857,7 +857,7 @@ floppyxfer(FDrive *dp, int cmd, void *a, long off, long n) dmaend(DMAchan); nexterror(); } - dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread); + dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread ? DMAREAD : DMAWRITE); if(dp->len < 0) error(Eio); @@ -999,7 +999,7 @@ floppyformat(FDrive *dp, Cmdbuf *cb) dmaend(DMAchan); nexterror(); } - if(dmasetup(DMAchan, buf, bp-buf, 0) < 0) + if(dmasetup(DMAchan, buf, bp-buf, DMAWRITE) < 0) error(Eio); /* diff --git a/sys/src/9/pc/dma.c b/sys/src/9/pc/dma.c index 7ecbc5f4a..4a81f516a 100644 --- a/sys/src/9/pc/dma.c +++ b/sys/src/9/pc/dma.c @@ -18,7 +18,7 @@ struct DMAxfer int blen; /* bounce buffer length */ void* va; /* virtual address destination/src */ long len; /* bytes to be transferred */ - int isread; + int flags; }; /* @@ -81,7 +81,7 @@ _i8237alloc(void) if(i8237dma > 2) i8237dma = 2; - bva = xspanalloc(64*1024*i8237dma, BY2PG, 64*1024); + bva = xspanalloc(64*1024*i8237dma, 0, 64*1024); if(bva == nil || PADDR(bva)+64*1024*i8237dma > 16*MB){ /* * This will panic with the current @@ -137,11 +137,38 @@ dmainit(int chan, int maxtransfer) xp->bpa = PADDR(xp->bva); xp->blen = maxtransfer; xp->len = 0; - xp->isread = 0; + xp->flags = 0; return 0; } +void* +dmabva(int chan) +{ + DMA *dp; + DMAxfer *xp; + + dp = &dma[(chan>>2)&1]; + chan = chan & 3; + xp = &dp->x[chan]; + return xp->bva; +} + +int +dmacount(int chan) +{ + int retval; + DMA *dp; + + dp = &dma[(chan>>2)&1]; + chan = chan & 3; + ilock(dp); + retval = inb(dp->count[chan]); + retval |= inb(dp->count[chan]) << 8; + iunlock(dp); + return ((retval<shift)+1) & 0xFFFF; +} + /* * setup a dma transfer. if the destination is not in kernel * memory, allocate a page for the transfer. @@ -153,7 +180,7 @@ dmainit(int chan, int maxtransfer) * boundaries) */ long -dmasetup(int chan, void *va, long len, int isread) +dmasetup(int chan, void *va, long len, int flags) { DMA *dp; ulong pa; @@ -175,11 +202,11 @@ dmasetup(int chan, void *va, long len, int isread) return -1; if(len > xp->blen) len = xp->blen; - if(!isread) + if(!(flags & DMAREAD)) memmove(xp->bva, va, len); xp->va = va; xp->len = len; - xp->isread = isread; + xp->flags = flags; pa = xp->bpa; } else @@ -189,7 +216,7 @@ dmasetup(int chan, void *va, long len, int isread) * this setup must be atomic */ ilock(dp); - mode = (isread ? 0x44 : 0x48) | chan; + mode = ((flags & DMAREAD) ? 0x44 : 0x48) | ((flags & DMALOOP) ? 0x10 : 0) | chan; outb(dp->mode, mode); /* single mode dma (give CPU a chance at mem) */ outb(dp->page[chan], pa>>16); outb(dp->cbp, 0); /* set count & address to their first byte */ @@ -238,7 +265,7 @@ dmaend(int chan) iunlock(dp); xp = &dp->x[chan]; - if(xp->len == 0 || !xp->isread) + if(xp->len == 0 || !(xp->flags & DMAREAD)) return; /* @@ -248,17 +275,3 @@ dmaend(int chan) xp->len = 0; } -/* -int -dmacount(int chan) -{ - int retval; - DMA *dp; - - dp = &dma[(chan>>2)&1]; - outb(dp->cbp, 0); - retval = inb(dp->count[chan]); - retval |= inb(dp->count[chan]) << 8; - return((retval<shift)+1); -} - */ diff --git a/sys/src/9/pc/fns.h b/sys/src/9/pc/fns.h index 4af1813e8..e376e7280 100644 --- a/sys/src/9/pc/fns.h +++ b/sys/src/9/pc/fns.h @@ -18,10 +18,14 @@ int cpuidentify(void); void cpuidprint(void); void (*cycles)(uvlong*); void delay(int); +void* dmabva(int); int dmacount(int); int dmadone(int); void dmaend(int); int dmainit(int, int); +#define DMAWRITE 0 +#define DMAREAD 1 +#define DMALOOP 2 long dmasetup(int, void*, long, int); #define evenaddr(x) /* x86 doesn't care */ void fpclear(void); @@ -181,5 +185,3 @@ int xchgw(ushort*, int); #define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) #define KADDR(a) kaddr(a) #define PADDR(a) paddr((void*)(a)) - -#define dcflush(a, b) diff --git a/sys/src/9/port/devaudio.c b/sys/src/9/port/devaudio.c index 89d756f56..9234e15a9 100644 --- a/sys/src/9/port/devaudio.c +++ b/sys/src/9/port/devaudio.c @@ -18,6 +18,7 @@ enum { Qaudioctl, Qaudiostatus, Qvolume, + Maxaudioprobes = 8, }; @@ -48,19 +49,26 @@ audioreset(void) int i, ctlrno = 0; Audio **pp; Audioprobe *probe; + pp = &audiodevs; *pp = malloc(sizeof(Audio)); - (*pp)->ctlrno = ctlrno++; - for(i = 0; i < naudioprobes; i++){ + + for(i=0; iname = probe->name; - while(!probe->probe(*pp)){ + + for(;;){ + memset(*pp, 0, sizeof(Audio)); + (*pp)->ctlrno = ctlrno; + (*pp)->name = probe->name; + if(probe->probe(*pp)) + break; + + ctlrno++; pp = &(*pp)->next; *pp = malloc(sizeof(Audio)); - (*pp)->ctlrno = ctlrno++; - (*pp)->name = probe->name; } } + free(*pp); *pp = nil; }