From e08058a90775e34f1de0f187d82946747c03fdbf Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Thu, 19 Jan 1995 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1995-01-19 --- carrera/audio.h | 34 ++ pc/audio.h | 35 ++ port/devaudio.c | 841 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 910 insertions(+) create mode 100644 carrera/audio.h create mode 100644 pc/audio.h create mode 100644 port/devaudio.c diff --git a/carrera/audio.h b/carrera/audio.h new file mode 100644 index 0000000000000000000000000000000000000000..b699c53f3345cf191175e57b2de0413dc4be1d67 --- /dev/null +++ b/carrera/audio.h @@ -0,0 +1,34 @@ +enum +{ + Bufsize = 16*1024, /* 92 ms each */ + Nbuf = 16, /* 1.5 seconds total */ + Dma = 6, + Irq = 7, + + DMA2_WRMASK = 0xd4, + DMA2_WRMODE = 0xd6, + DMA2_CLRBP = 0xd8, + + DMA6_ADDRESS = 0xc8, + DMA6_COUNT = 0xca, + DMA6_LPAGE = 0x89, + DMA6_HPAGE = 0x489, + + Dspport = 0x220, + + PORT_RESET = Dspport + 0x6, + PORT_READ = Dspport + 0xA, + PORT_WRITE = Dspport + 0xC, + PORT_WSTATUS = Dspport + 0xC, + PORT_RSTATUS = Dspport + 0xE, + + PORT_MIXER_ADDR = Dspport + 0x4, + PORT_MIXER_DATA = Dspport + 0x5, + + PORT_CLRI8 = Dspport + 0xE, + PORT_CLRI16 = Dspport + 0xF, + PORT_CLRI401 = Dspport + 0x100, +}; + +#define outb(a,b) EISAOUTB(a,b) +#define inb(a) EISAINB(a) diff --git a/pc/audio.h b/pc/audio.h new file mode 100644 index 0000000000000000000000000000000000000000..d9366805f57a0f45de46ed35ee882cfa7c9dd974 --- /dev/null +++ b/pc/audio.h @@ -0,0 +1,35 @@ +enum +{ + Bufsize = 16*1024, /* 92 ms each */ + Nbuf = 16, /* 1.5 seconds total */ + Dma = 6, + Irq = 7, + + DMA2_WRMASK = 0xd4, + DMA2_WRMODE = 0xd6, + DMA2_CLRBP = 0xd8, + + DMA6_ADDRESS = 0xc8, + DMA6_COUNT = 0xca, + DMA6_LPAGE = 0x89, + DMA6_HPAGE = 0x489, + + Dspport = 0x220, + + PORT_RESET = Dspport + 0x6, + PORT_READ = Dspport + 0xA, + PORT_WRITE = Dspport + 0xC, + PORT_WSTATUS = Dspport + 0xC, + PORT_RSTATUS = Dspport + 0xE, + + PORT_MIXER_ADDR = Dspport + 0x4, + PORT_MIXER_DATA = Dspport + 0x5, + + PORT_CLRI8 = Dspport + 0xE, + PORT_CLRI16 = Dspport + 0xF, + PORT_CLRI401 = Dspport + 0x100, +}; + +#define seteisadma(a, b) ; +#define CACHELINESZ 8 +#define UNCACHED(type, v) (type*)((ulong)(v)) diff --git a/port/devaudio.c b/port/devaudio.c new file mode 100644 index 0000000000000000000000000000000000000000..478f23c697cc5cc13de67b52048156376d9b8a77 --- /dev/null +++ b/port/devaudio.c @@ -0,0 +1,841 @@ +/* + * SB 16 driver + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "devtab.h" +#include "io.h" +#include "audio.h" + + +#define nelem(x) (sizeof (x)/sizeof(x[0])) +#define NPORT (sizeof audiodir/sizeof(Dirtab)) + +typedef struct Buf Buf; +typedef struct Level Level; + +enum +{ + Qdir = 0, + Qaudio, + Qvolume, + + Fmono = 1, + + Aread = 0, + Awrite, + + Speed = 44100, + Ncmd = 50, /* max volume command words */ +}; + +Dirtab +audiodir[] = +{ + "audio", {Qaudio}, 0, 0666, + "volume", {Qvolume}, 0, 0666, +}; + +struct Level +{ + int master; /* mixer output volume control */ + int ogain; /* extra gain after master */ + int pcm; /* mixer volume on D/A (voice) */ + int synth; /* mixer volume on synthesizer (MIDI) */ + int cd; /* mixer volume on cd */ + int line; /* mixer volume on line */ + int igain; /* mixer volume to A/D */ + int treb; /* treb control */ + int bass; /* bass control */ + int iswitch; /* input on/off switches */ +}; +struct Buf +{ + uchar* virt; + ulong phys; + Buf* next; +}; +struct Queue +{ + Lock; + Buf* first; + Buf* last; +}; +static struct +{ + Rendez vous; + int bufinit; /* boolean if buffers allocated */ + int curcount; /* how much data in current buffer */ + int active; /* boolean dma running */ + int intr; /* boolean an interrupt has happened */ + int amode; /* Aread/Awrite for /audio */ + Level left; /* all of left volumes */ + Level right; /* all of right volumes */ + int mic; /* mono level */ + int speaker; /* mono level */ + int oswitch; /* output on/off switches */ + int speed; /* pcm sample rate, doesnt change w stereo */ + int major; /* SB16 major version number (sb 4) */ + int minor; /* SB16 minor version number */ + char place[20]; /* static place to decode words */ + + Buf buf[Nbuf]; /* buffers and queues */ + Queue empty; + Queue full; + Buf* current; + Buf* filling; +} audio; + +static struct +{ + char* name; + int* ptr1; + int* ptr2; + int flag; + int ilval; + int irval; +} volumes[] = +{ + "master", &audio.left.master, &audio.right.master, 0, 50, 50, + "ogain", &audio.left.ogain, &audio.right.ogain, 0, 0, 0, + "igain", &audio.left.igain, &audio.right.igain, 0, 0, 0, + + "treb", &audio.left.treb, &audio.right.treb, 0, 50, 50, + "bass", &audio.left.bass, &audio.right.bass, 0, 50, 50, + + "pcm", &audio.left.pcm, &audio.right.pcm, 0, 90, 90, + "synth", &audio.left.synth, &audio.right.synth, 0, 90, 90, + "cd", &audio.left.cd, &audio.right.cd, 0, 81, 81, + "line", &audio.left.line, &audio.right.line, 0, 65, 65, + + "mic", &audio.mic, &audio.mic, Fmono, 0, 0, + "speaker", &audio.speaker, &audio.speaker, Fmono, 0, 0, + "oswitch", &audio.oswitch, &audio.oswitch, Fmono, 31, 31, + "iswitch", &audio.left.iswitch, &audio.right.iswitch, 0, 85, 43, + + "speed", &audio.speed, &audio.speed, Fmono, Speed, Speed, + 0 +}; + +static void swab(uchar*); + +static char Emajor[] = "SB16 version too old"; +static char Emode[] = "illegal open mode"; +static char Evolume[] = "illegal volume specifier"; + +static int +sbcmd(int val) +{ + int i, s; + + for(i=1<<16; i!=0; i--) { + s = inb(PORT_WSTATUS); + if((s & 0x80) == 0) { + outb(PORT_WRITE, val); + return 0; + } + } +/* print("SB16 sbcmd (#%.2x) timeout\n", val); /**/ + return 1; +} + +static int +sbread(void) +{ + int i, s; + + for(i=1<<16; i!=0; i--) { + s = inb(PORT_RSTATUS); + if((s & 0x80) != 0) { + return inb(PORT_READ); + } + } +/* print("SB16 sbread did not respond\n"); /**/ + return 0xbb; +} + +static int +mxcmd(int addr, int val) +{ + + outb(PORT_MIXER_ADDR, addr); + outb(PORT_MIXER_DATA, val); + return 1; +} + +static int +mxread(int addr) +{ + int s; + + outb(PORT_MIXER_ADDR, addr); + s = inb(PORT_MIXER_DATA); + return s; +} + +static void +mxcmds(int s, int v) +{ + + if(v > 100) + v = 100; + if(v < 0) + v = 0; + mxcmd(s, (v*255)/100); +} + +static void +mxcmdt(int s, int v) +{ + + if(v > 100) + v = 100; + if(v <= 0) + mxcmd(s, 0); + else + mxcmd(s, 255-100+v); +} + +static void +mxcmdu(int s, int v) +{ + + if(v > 100) + v = 100; + if(v <= 0) + v = 0; + mxcmd(s, 128-50+v); +} + +static void +mxvolume(void) +{ + ulong sp; + + sp = splhi(); + mxcmds(0x30, audio.left.master); + mxcmds(0x31, audio.right.master); + + mxcmdt(0x32, audio.left.pcm); + mxcmdt(0x33, audio.right.pcm); + + mxcmdt(0x34, audio.left.synth); + mxcmdt(0x35, audio.right.synth); + + mxcmdt(0x36, audio.left.cd); + mxcmdt(0x37, audio.right.cd); + + mxcmdt(0x38, audio.left.line); + mxcmdt(0x39, audio.right.line); + + mxcmdt(0x3a, audio.mic); + mxcmdt(0x3b, audio.speaker); + + mxcmds(0x3f, audio.left.igain); + mxcmds(0x40, audio.right.igain); + mxcmds(0x41, audio.left.ogain); + mxcmds(0x42, audio.right.ogain); + + mxcmdu(0x44, audio.left.treb); + mxcmdu(0x45, audio.right.treb); + + mxcmdu(0x46, audio.left.bass); + mxcmdu(0x47, audio.right.bass); + + mxcmd(0x3c, audio.oswitch); + mxcmd(0x3d, audio.left.iswitch); + mxcmd(0x3e, audio.right.iswitch); + splx(sp); +} + +static Buf* +getbuf(Queue *q) +{ + Buf *b; + + ilock(q); + b = q->first; + if(b) + q->first = b->next; + iunlock(q); + + return b; +} + +static void +putbuf(Queue *q, Buf *b) +{ + + ilock(q); + b->next = 0; + if(q->first) + q->last->next = b; + else + q->first = b; + q->last = b; + iunlock(q); +} + +/* + * move the dma to the next buffer + */ +static void +contindma(void) +{ + Buf *b; + ulong count, addr; + + if(!audio.active) + goto shutdown; + + b = audio.current; + if(audio.amode == Aread) { + if(b) /* shouldnt happen */ + putbuf(&audio.full, b); + b = getbuf(&audio.empty); + } else { + if(b) /* shouldnt happen */ + putbuf(&audio.empty, b); + b = getbuf(&audio.full); + } + audio.current = b; + if(b == 0) + goto shutdown; + + addr = b->phys; + count = ((Bufsize) >> 1) - 1; + +/* outb(DMA2_WRMASK, 4|(Dma&3)); /* disable dma */ + outb(DMA6_LPAGE, (addr>>16) & 0xfe); + outb(DMA2_CLRBP, 0); /* clear ff */ + outb(DMA6_ADDRESS, addr>>1); + outb(DMA6_ADDRESS, addr>>9); + + outb(DMA2_CLRBP, 0); /* clear ff */ + outb(DMA6_COUNT, count); + outb(DMA6_COUNT, count>>8); + outb(DMA2_WRMASK, (Dma&3)); /* enable dma */ + + return; + +shutdown: + outb(DMA2_WRMASK, 4|(Dma&3)); /* disable dma */ + sbcmd(0xd9); /* exit at end of count */ + sbcmd(0xd5); /* pause */ + audio.curcount = 0; + audio.active = 0; +} + +/* + * cause sb to get an interrupt per buffer. + * start first dma + */ +static void +startdma(void) +{ + ulong count; + + outb(DMA2_WRMASK, 4|(Dma&3)); /* disable dma */ + if(audio.amode == Aread) { + outb(DMA2_WRMODE, 0x44 | (Dma&3)); /* set mode */ + sbcmd(0x42); /* input sampling rate */ + } else { + outb(DMA2_WRMODE, 0x48 | (Dma&3)); /* set mode */ + sbcmd(0x41); /* output sampling rate */ + } + sbcmd(audio.speed>>8); + sbcmd(audio.speed); + + count = (Bufsize >> 1) - 1; + if(audio.amode == Aread) + sbcmd(0xbe); /* A/D, autoinit */ + else + sbcmd(0xb6); /* D/A, autoinit */ + sbcmd(0x30); /* stereo, 16 bit */ + sbcmd(count); + sbcmd(count>>8); + + audio.active = 1; + contindma(); +} + +/* + * if audio is stopped, + * start it up again. + */ +static void +pokeaudio(void) +{ + ulong sp; + + sp = splhi(); + if(!audio.active) + startdma(); + splx(sp); +} + +void +audiosbintr(void) +{ + int stat, dummy; + + stat = mxread(0x82) & 7; /* get irq status */ + if(stat) { + dummy = 0; + if(stat & 2) { + dummy = inb(PORT_CLRI16); + contindma(); + audio.intr = 1; + wakeup(&audio.vous); + } + if(stat & 1) { + dummy = inb(PORT_CLRI8); + } + if(stat & 4) { + dummy = inb(PORT_CLRI401); + } + USED(dummy); + } +} + +void +audiodmaintr(void) +{ +/* print("sb16 dma interrupt\n"); /**/ +} + +static int +anybuf(void *p) +{ + USED(p); + return audio.intr; +} + +/* + * wait for some output to get + * empty buffers back. + */ +static void +waitaudio(void) +{ + + audio.intr = 0; + pokeaudio(); + tsleep(&audio.vous, anybuf, 0, 10*1000); + if(audio.intr == 0) { +/* print("audio timeout\n"); /**/ + audio.active = 0; + pokeaudio(); + } +} + +static void +sbbufinit(void) +{ + int i; + void *p; + + for(i=0; i3 υs */ + outb(PORT_RESET, 0); + delay(1); + + i = sbread(); + if(i != 0xaa) { + print("sound blaster didnt respond #%.2x\n", i); + return; + } + + sbcmd(0xe1); /* get version */ + audio.major = sbread(); + audio.minor = sbread(); + + if(audio.major != 4) { + print("bad soundblaster model #%.2x #.2x\n", audio.major, audio.minor); + return; + } + /* + * initialize the mixer + */ + mxcmd(0x00, 0); /* Reset mixer */ + mxvolume(); + + /* + * set up irq/dma chans + */ + mxcmd(0x80, /* irq */ + (Irq==2)? 1: + (Irq==5)? 2: + (Irq==7)? 4: + (Irq==10)? 8: + 0); + mxcmd(0x81, 1<qid.path & ~CHDIR) { + default: + error(Eperm); + break; + + case Qvolume: + case Qdir: + break; + + case Qaudio: + if(audio.bufinit == 0) { + audio.bufinit = 1; + sbbufinit(); + } + + audio.amode = Awrite; + if((omode&7) == OREAD) + audio.amode = Aread; + + setempty(); + audio.curcount = 0; + break; + } + c = devopen(c, omode, audiodir, NPORT, devgen); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + + return c; +} + +void +audiocreate(Chan *c, char *name, int omode, ulong perm) +{ + USED(c); + USED(name); + USED(omode); + USED(perm); + + error(Eperm); +} + +void +audioclose(Chan *c) +{ + + switch(c->qid.path & ~CHDIR) { + default: + error(Eperm); + break; + + case Qdir: + case Qvolume: + break; + + case Qaudio: + if(c->flag & COPEN) { + while(audio.active) + waitaudio(); + setempty(); + } + break; + } +} + +long +audioread(Chan *c, char *a, long n, ulong offset) +{ + long m, o, n0, bn; + char buf[256]; + Buf *b; + int j; + + n0 = n; + switch(c->qid.path & ~CHDIR) { + default: + error(Eperm); + break; + + case Qdir: + return devdirread(c, a, n, audiodir, NPORT, devgen); + + case Qaudio: + if(audio.amode != Aread) + error(Emode); + while(n > 0) { + b = audio.filling; + if(b == 0) { + b = getbuf(&audio.full); + if(b == 0) { + waitaudio(); + continue; + } + audio.filling = b; + swab(b->virt); + audio.curcount = 0; + } + m = Bufsize-audio.curcount; + if(m > n) + m = n; + memmove(a, b->virt+audio.curcount, m); + + audio.curcount += m; + n -= m; + a += m; + if(audio.curcount >= Bufsize) { + audio.filling = 0; + putbuf(&audio.empty, b); + } + } + break; + + case Qvolume: + j = 0; + for(m=0; volumes[m].name; m++) { + o = *volumes[m].ptr1; + if(volumes[m].flag & Fmono) + j += snprint(buf+j, sizeof(buf)-j, "%s %d\n", volumes[m].name, o); + else { + bn = *volumes[m].ptr2; + if(o == bn) + j += snprint(buf+j, sizeof(buf)-j, "%s both %d\n", volumes[m].name, o); + else + j += snprint(buf+j, sizeof(buf)-j, "%s left %d right %d\n", + volumes[m].name, o, bn); + } + } + + return readstr(offset, a, n, buf); + } + return n0-n; +} + +Block* +audiobread(Chan *c, long n, ulong offset) +{ + return devbread(c, n, offset); +} + +long +audiowrite(Chan *c, char *a, long n, ulong offset) +{ + long m, n0; + int i, nf, v, left, right; + char buf[255], *field[Ncmd]; + Buf *b; + + USED(offset); + + n0 = n; + switch(c->qid.path & ~CHDIR) { + default: + error(Eperm); + break; + + case Qvolume: + v = 0; + left = 1; + right = 1; + if(n > sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = '\0'; + + nf = getfields(buf, field, Ncmd, " \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) + *volumes[v].ptr1 = m; + if(right) + *volumes[v].ptr2 = m; + mxvolume(); + goto cont0; + } + + for(m=0; volumes[m].name; m++) { + if(strcmp(field[i], volumes[m].name) == 0) { + v = m; + goto cont0; + } + } + + if(strcmp(field[i], "reset") == 0) { + resetlevel(); + mxvolume(); + 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], "both") == 0) { + left = 1; + right = 1; + goto cont0; + } + error(Evolume); + break; + cont0:; + } + break; + + case Qaudio: + if(audio.amode != Awrite) + error(Emode); + while(n > 0) { + b = audio.filling; + if(b == 0) { + b = getbuf(&audio.empty); + if(b == 0) { + waitaudio(); + 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; + if(audio.curcount >= Bufsize) { + audio.filling = 0; + swab(b->virt); + putbuf(&audio.full, b); + } + } + break; + } + return n0 - n; +} + +long +audiobwrite(Chan *c, Block *bp, ulong offset) +{ + return devbwrite(c, bp, offset); +} + +void +audioremove(Chan *c) +{ + USED(c); + + error(Eperm); +} + +void +audiowstat(Chan *c, char *dp) +{ + USED(c); + USED(dp); + + error(Eperm); +} + +static void +swab(uchar *a) +{ + ulong *p, *ep, b; + + 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; + } +}