#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 "ureg.h" typedef struct AQueue AQueue;typedef struct Buf Buf;typedef struct Level Level; #define NAUDIO (sizeof audiodir/sizeof(Dirtab))#define CSR (*(ulong*)SOUNDOUTDMA)#define DMA1B (*(ulong*)SOUNDOUTBASE)#define DMA1L (*(ulong*)SOUNDOUTLIMIT)#define DMA2B (*(ulong*)SOUNDOUTCHAINBASE)#define DMA2L (*(ulong*)SOUNDOUTCHAINLIMIT)#define MONCMD (*(ulong*)MONCSR)#define MONDAT (*(ulong*)KMSDATA) enum{ Qdir = 0, Qaudio, Qvolume, Fmono = 1, Fin = 2, Fout = 4, Aclosed = 0, Aread, Awrite, Vaudio = 0, Vdeemp, Nvol, Ncmd = 50, /* max volume command words */ Outcint = 1<<27, Outrw = 1<<26, /* always 0 */ Outchain = 1<<25, Outenable = 1<<24, Outinitbuff = 1<<21, Outcreset = 1<<20, Outclrcint = 1<<19, Outwr = 1<<18, /* 0 = read */ Outsetchain = 1<<17, Outsetenable = 1<<16, Bufsize = 8*1024, Nbuf = 10,}; Dirtabaudiodir[] ={ "audio", {Qaudio}, 0, 0600, "volume", {Qvolume}, 0, 0600,}; struct Buf{ uchar* virt; ulong phys; Buf* next;};struct AQueue{ Lock; Buf* first; Buf* last;};static struct{ QLock; 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; /* Aclosed/Aread/Awrite for /audio */ int rivol[Nvol]; /* right/left input/output volumes */ int livol[Nvol]; int rovol[Nvol]; int lovol[Nvol]; int c4cmd; Buf buf[Nbuf]; /* buffers and queues */ AQueue empty; AQueue full; Buf* current; Buf* chain; Buf* filling;} audio; static struct{ char* name; int flag; int ilval; int irval;} volumes[] ={[Vaudio] {"audio", Fmono|Fout, 50, 50,},[Vdeemp] {"deemp", Fmono|Fout, 0, 0,}, 0}; static char Emode[] = "illegal open mode";static char Evolume[] = "illegal volume specifier"; static Lock blaster; static void swab(uchar*, int); /* * send a command to the * monitor sub-system. * SoundOutEnable is left on. */static voidmonxmit(int cmd, long data){ /* * this routine suffers from usual * NEXT copious documentation. * 1. other (which?) MONCMD bits/modes * are not preserved across call. * 2. the delays are arbitrary. * 3. the use of bit9 and bit31 are * mystical. */ MONCMD = (1<<9) | cmd; delay(1); MONDAT = data; delay(1); MONCMD = (1<<31)|(1<<9);} static voidsetvolume(int tag, int isin, int isleft, int val){ if(!isleft || isin) return; if(tag == Vaudio){ val = val*31/100; if(val < 0) val = 0; if(val > 31) val = 31; audio.c4cmd |= (1<<4); if(val != 0) audio.c4cmd &= ~(1<<4); monxmit(0xc4, audio.c4cmd<<24); monxmit(0xc2, (0xdf-val)<<24); } if(tag == Vdeemp){ audio.c4cmd |= (1<<3); if(val == 0) audio.c4cmd &= ~(1<<3); monxmit(0xc4, audio.c4cmd<<24); }} static Buf*getbuf(AQueue *q){ Buf *b; ilock(q); b = q->first; if(b) q->first = b->next; iunlock(q); return b;} static voidputbuf(AQueue *q, Buf *b){ ilock(q); b->next = 0; if(q->first) q->last->next = b; else q->first = b; q->last = b; iunlock(q);} static voidsetempty(void){ int i; ilock(&blaster); audio.empty.first = 0; audio.empty.last = 0; audio.full.first = 0; audio.full.last = 0; audio.current = 0; audio.filling = 0; for(i=0; i<Nbuf; i++) putbuf(&audio.empty, &audio.buf[i]); iunlock(&blaster);} /* * if audio is stopped, * start it up again. */static voidpokeaudio(void){ Buf *b1, *b2; if(audio.active) return; ilock(&blaster); b1 = audio.current; if(b1) putbuf(&audio.empty, b1); audio.current = 0; b1 = audio.chain; if(b1 == 0){ b1 = getbuf(&audio.full); audio.chain = b1; } b2 = getbuf(&audio.full); if(b2 == 0){ iunlock(&blaster); return; } audio.chain = b2; audio.current = b1; audio.active = 1; CSR = 0; CSR = Outcreset|Outinitbuff; DMA1L = b1->phys+Bufsize; DMA1B = b1->phys; DMA2L = b2->phys+Bufsize; DMA2B = b2->phys; CSR = Outsetenable|Outsetchain; iunlock(&blaster);} voidaudiodmaintr(void){ long csr; Buf *b; ilock(&blaster); csr = CSR; audio.intr = 1; b = audio.current; if(b) putbuf(&audio.empty, b); /* * we took the last interrupt * without a chain. * just shut down. */ if((csr & Outenable) == 0) { CSR = Outcreset | Outclrcint; audio.current = 0; audio.active = 0; iunlock(&blaster); wakeup(&audio.vous); return; } b = audio.chain; audio.current = b; b = getbuf(&audio.full); audio.chain = b; if(b) { /* * we have another full buffer, * chain it on the end. */ DMA2L = b->phys+Bufsize; DMA2B = b->phys; CSR = Outsetchain | Outclrcint; }else{ /* * there is no full buffer, * just clear the interrupt * and let the last buffer go out */ CSR = Outclrcint; } iunlock(&blaster); wakeup(&audio.vous);} static intanybuf(void *p){ USED(p); return audio.intr;} /* * wait for some output to get * empty buffers back. */static voidwaitaudio(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 voidaudiobufinit(void){ int i; ulong l, p; /* sound out enable, analog out enable */ monxmit(0x0f, 0); for(i=0; i<Nbuf; i++) { p = (ulong)xspanalloc(Bufsize, BY2PG, 0); audio.buf[i].virt = (uchar*)kmappa(p); audio.buf[i].phys = (ulong)p; for(l = BY2PG; l < Bufsize; l += BY2PG) kmappa(p+l); }} static voidsetvolumes(void){ int v; for(v=0; volumes[v].name; v++){ setvolume(v, 1, 1, audio.livol[v]); setvolume(v, 0, 1, audio.lovol[v]); if(volumes[v].flag != Fmono){ setvolume(v, 1, 0, audio.rivol[v]); setvolume(v, 0, 0, audio.rovol[v]); } }} static voidresetlevel(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; }} voidaudioreset(void){} voidaudioinit(void){ resetlevel(); setvolumes(); audio.amode = Aclosed;} Chan*audioattach(char *param){ return devattach('A', param);} Chan*audioclone(Chan *c, Chan *nc){ return devclone(c, nc);} intaudiowalk(Chan *c, char *name){ return devwalk(c, name, audiodir, NAUDIO, devgen);} voidaudiostat(Chan *c, char *db){ devstat(c, db, audiodir, NAUDIO, devgen);} Chan*audioopen(Chan *c, int omode){ int amode; switch(c->qid.path & ~CHDIR) { default: error(Eperm); break; case Qvolume: case Qdir: break; case Qaudio: amode = Awrite; if((omode&7) == OREAD) amode = Aread; qlock(&audio); if(audio.amode != Aclosed){ qunlock(&audio); error(Einuse); } if(audio.bufinit == 0) { audio.bufinit = 1; audiobufinit(); } audio.amode = amode; setempty(); audio.curcount = 0; qunlock(&audio); break; } c = devopen(c, omode, audiodir, NAUDIO, devgen); c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; return c;} voidaudiocreate(Chan *c, char *name, int omode, ulong perm){ USED(c); USED(name); USED(omode); USED(perm); error(Eperm);} voidaudioclose(Chan *c){ switch(c->qid.path & ~CHDIR) { default: error(Eperm); break; case Qdir: case Qvolume: break; case Qaudio: if(c->flag & COPEN) { qlock(&audio); audio.amode = Aclosed; if(waserror()){ qunlock(&audio); nexterror(); } while(audio.active) waitaudio(); setempty(); poperror(); ilock(&blaster); CSR = 0; CSR = Outcreset|Outinitbuff;Lines 1–500 of 726. Load more
#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 "ureg.h" typedef struct AQueue AQueue; typedef struct Buf Buf; typedef struct Level Level; #define NAUDIO (sizeof audiodir/sizeof(Dirtab)) #define CSR (*(ulong*)SOUNDOUTDMA) #define DMA1B (*(ulong*)SOUNDOUTBASE) #define DMA1L (*(ulong*)SOUNDOUTLIMIT) #define DMA2B (*(ulong*)SOUNDOUTCHAINBASE) #define DMA2L (*(ulong*)SOUNDOUTCHAINLIMIT) #define MONCMD (*(ulong*)MONCSR) #define MONDAT (*(ulong*)KMSDATA) enum { Qdir = 0, Qaudio, Qvolume, Fmono = 1, Fin = 2, Fout = 4, Aclosed = 0, Aread, Awrite, Vaudio = 0, Vdeemp, Nvol, Ncmd = 50, /* max volume command words */ Outcint = 1<<27, Outrw = 1<<26, /* always 0 */ Outchain = 1<<25, Outenable = 1<<24, Outinitbuff = 1<<21, Outcreset = 1<<20, Outclrcint = 1<<19, Outwr = 1<<18, /* 0 = read */ Outsetchain = 1<<17, Outsetenable = 1<<16, Bufsize = 8*1024, Nbuf = 10, }; Dirtab audiodir[] = { "audio", {Qaudio}, 0, 0600, "volume", {Qvolume}, 0, 0600, }; struct Buf { uchar* virt; ulong phys; Buf* next; }; struct AQueue { Lock; Buf* first; Buf* last; }; static struct { QLock; 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; /* Aclosed/Aread/Awrite for /audio */ int rivol[Nvol]; /* right/left input/output volumes */ int livol[Nvol]; int rovol[Nvol]; int lovol[Nvol]; int c4cmd; Buf buf[Nbuf]; /* buffers and queues */ AQueue empty; AQueue full; Buf* current; Buf* chain; Buf* filling; } audio; static struct { char* name; int flag; int ilval; int irval; } volumes[] = { [Vaudio] {"audio", Fmono|Fout, 50, 50,}, [Vdeemp] {"deemp", Fmono|Fout, 0, 0,}, 0 }; static char Emode[] = "illegal open mode"; static char Evolume[] = "illegal volume specifier"; static Lock blaster; static void swab(uchar*, int); /* * send a command to the * monitor sub-system. * SoundOutEnable is left on. */ static void monxmit(int cmd, long data) { /* * this routine suffers from usual * NEXT copious documentation. * 1. other (which?) MONCMD bits/modes * are not preserved across call. * 2. the delays are arbitrary. * 3. the use of bit9 and bit31 are * mystical. */ MONCMD = (1<<9) | cmd; delay(1); MONDAT = data; delay(1); MONCMD = (1<<31)|(1<<9); } static void setvolume(int tag, int isin, int isleft, int val) { if(!isleft || isin) return; if(tag == Vaudio){ val = val*31/100; if(val < 0) val = 0; if(val > 31) val = 31; audio.c4cmd |= (1<<4); if(val != 0) audio.c4cmd &= ~(1<<4); monxmit(0xc4, audio.c4cmd<<24); monxmit(0xc2, (0xdf-val)<<24); } if(tag == Vdeemp){ audio.c4cmd |= (1<<3); if(val == 0) audio.c4cmd &= ~(1<<3); monxmit(0xc4, audio.c4cmd<<24); } } static Buf* getbuf(AQueue *q) { Buf *b; ilock(q); b = q->first; if(b) q->first = b->next; iunlock(q); return b; } static void putbuf(AQueue *q, Buf *b) { ilock(q); b->next = 0; if(q->first) q->last->next = b; else q->first = b; q->last = b; iunlock(q); } static void setempty(void) { int i; ilock(&blaster); audio.empty.first = 0; audio.empty.last = 0; audio.full.first = 0; audio.full.last = 0; audio.current = 0; audio.filling = 0; for(i=0; i<Nbuf; i++) putbuf(&audio.empty, &audio.buf[i]); iunlock(&blaster); } /* * if audio is stopped, * start it up again. */ static void pokeaudio(void) { Buf *b1, *b2; if(audio.active) return; ilock(&blaster); b1 = audio.current; if(b1) putbuf(&audio.empty, b1); audio.current = 0; b1 = audio.chain; if(b1 == 0){ b1 = getbuf(&audio.full); audio.chain = b1; } b2 = getbuf(&audio.full); if(b2 == 0){ iunlock(&blaster); return; } audio.chain = b2; audio.current = b1; audio.active = 1; CSR = 0; CSR = Outcreset|Outinitbuff; DMA1L = b1->phys+Bufsize; DMA1B = b1->phys; DMA2L = b2->phys+Bufsize; DMA2B = b2->phys; CSR = Outsetenable|Outsetchain; iunlock(&blaster); } void audiodmaintr(void) { long csr; Buf *b; ilock(&blaster); csr = CSR; audio.intr = 1; b = audio.current; if(b) putbuf(&audio.empty, b); /* * we took the last interrupt * without a chain. * just shut down. */ if((csr & Outenable) == 0) { CSR = Outcreset | Outclrcint; audio.current = 0; audio.active = 0; iunlock(&blaster); wakeup(&audio.vous); return; } b = audio.chain; audio.current = b; b = getbuf(&audio.full); audio.chain = b; if(b) { /* * we have another full buffer, * chain it on the end. */ DMA2L = b->phys+Bufsize; DMA2B = b->phys; CSR = Outsetchain | Outclrcint; }else{ /* * there is no full buffer, * just clear the interrupt * and let the last buffer go out */ CSR = Outclrcint; } iunlock(&blaster); wakeup(&audio.vous); } 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 audiobufinit(void) { int i; ulong l, p; /* sound out enable, analog out enable */ monxmit(0x0f, 0); for(i=0; i<Nbuf; i++) { p = (ulong)xspanalloc(Bufsize, BY2PG, 0); audio.buf[i].virt = (uchar*)kmappa(p); audio.buf[i].phys = (ulong)p; for(l = BY2PG; l < Bufsize; l += BY2PG) kmappa(p+l); } } static void setvolumes(void) { int v; for(v=0; volumes[v].name; v++){ setvolume(v, 1, 1, audio.livol[v]); setvolume(v, 0, 1, audio.lovol[v]); if(volumes[v].flag != Fmono){ setvolume(v, 1, 0, audio.rivol[v]); setvolume(v, 0, 0, audio.rovol[v]); } } } 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; } } void audioreset(void) { } void audioinit(void) { resetlevel(); setvolumes(); audio.amode = Aclosed; } Chan* audioattach(char *param) { return devattach('A', param); } Chan* audioclone(Chan *c, Chan *nc) { return devclone(c, nc); } int audiowalk(Chan *c, char *name) { return devwalk(c, name, audiodir, NAUDIO, devgen); } void audiostat(Chan *c, char *db) { devstat(c, db, audiodir, NAUDIO, devgen); } Chan* audioopen(Chan *c, int omode) { int amode; switch(c->qid.path & ~CHDIR) { default: error(Eperm); break; case Qvolume: case Qdir: break; case Qaudio: amode = Awrite; if((omode&7) == OREAD) amode = Aread; qlock(&audio); if(audio.amode != Aclosed){ qunlock(&audio); error(Einuse); } if(audio.bufinit == 0) { audio.bufinit = 1; audiobufinit(); } audio.amode = amode; setempty(); audio.curcount = 0; qunlock(&audio); break; } c = devopen(c, omode, audiodir, NAUDIO, 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) { qlock(&audio); audio.amode = Aclosed; if(waserror()){ qunlock(&audio); nexterror(); } while(audio.active) waitaudio(); setempty(); poperror(); ilock(&blaster); CSR = 0; CSR = Outcreset|Outinitbuff;