audioif, mixer control

This commit is contained in:
cinap_lenrek 2011-05-20 18:30:46 +00:00
parent 334c58f95e
commit 4bc74b8aef
7 changed files with 624 additions and 551 deletions

View file

@ -5,6 +5,7 @@
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "../port/audioif.h"
typedef struct Hwdesc Hwdesc;
typedef struct Ctlr Ctlr;
@ -65,10 +66,6 @@ struct Ctlr {
ulong civstat[Ndesc];
ulong lvistat[Ndesc];
int targetrate;
int hardrate;
int attachok;
int sis7012;
/* for probe */
@ -145,8 +142,8 @@ enum {
#define csr32w(c, r, w) (outl((c)->port+(r), (ulong)(w)))
/* audioac97mix */
extern int ac97hardrate(Audio *, int);
extern void ac97mixreset(Audio *, void (*wr)(Audio*,int,ushort),
extern void ac97mixreset(Audio *,
void (*wr)(Audio*,int,ushort),
ushort (*rr)(Audio*,int));
static void
@ -202,11 +199,9 @@ ac97interrupt(Ureg *, void *arg)
adev = arg;
ctlr = adev->ctlr;
stat = csr32r(ctlr, Sta);
stat &= S2ri | Sri | Pri | Mint | Point | Piint | Moint | Miint | Gsci;
ilock(ctlr);
if(stat & Point){
ilock(ctlr);
if(ctlr->sis7012)
csr16w(ctlr, Out + Picb, csr16r(ctlr, Out + Picb) & ~Dch);
else
@ -232,10 +227,11 @@ ac97interrupt(Ureg *, void *arg)
if(ctlr->outavail > Bufsize/2)
wakeup(&ctlr->outr);
stat &= ~Point;
iunlock(ctlr);
}
iunlock(ctlr);
if(stat) /* have seen 0x400, which is sdin0 resume */
print("#A%d: ac97 unhandled interrupt(s): stat 0x%lux\n", adev->ctlrno, stat);
iprint("#A%d: ac97 unhandled interrupt(s): stat 0x%lux\n",
adev->ctlrno, stat);
}
static int
@ -272,52 +268,30 @@ ac97medianoutrate(Audio *adev)
return ts[Nts/2] / BytesPerSample;
}
static void
ac97volume(Audio *adev, char *msg)
{
adev->volwrite(adev, msg, strlen(msg), 0);
}
static void
ac97attach(Audio *adev)
{
Ctlr *ctlr;
ctlr = adev->ctlr;
if(!ctlr->attachok){
ac97hardrate(adev, ctlr->hardrate);
ac97volume(adev, "audio 75");
ac97volume(adev, "head 100");
ac97volume(adev, "master 100");
ctlr->attachok = 1;
}
}
static long
ac97status(Audio *adev, void *a, long n, vlong off)
ac97status(Audio *adev, void *a, long n, vlong)
{
char *p, *e;
Ctlr *ctlr;
char *buf;
long i, l;
int i;
ctlr = adev->ctlr;
l = 0;
buf = malloc(READSTR);
l += snprint(buf + l, READSTR - l, "rate %d\n", ctlr->targetrate);
l += snprint(buf + l, READSTR - l, "median rate %lud\n", ac97medianoutrate(adev));
l += snprint(buf + l, READSTR - l, "hard rate %d\n", ac97hardrate(adev, -1));
l += snprint(buf + l, READSTR - l, "civ stats");
p = a;
e = p + n;
p += snprint(p, e - p, "median rate %lud\n", ac97medianoutrate(adev));
p += snprint(p, e - p, "civ stats");
for(i = 0; i < Ndesc; i++)
l += snprint(buf + l, READSTR - l, " %lud", ctlr->civstat[i]);
l += snprint(buf + l, READSTR - l, "\n");
p += snprint(p, e - p, " %lud", ctlr->civstat[i]);
p += snprint(p, e - p, "\n");
l += snprint(buf + l, READSTR - l, "lvi stats");
p += snprint(p, e - p, "lvi stats");
for(i = 0; i < Ndesc; i++)
l += snprint(buf + l, READSTR - l, " %lud", ctlr->lvistat[i]);
snprint(buf + l, READSTR - l, "\n");
p += snprint(p, e - p, " %lud", ctlr->lvistat[i]);
p += snprint(p, e - p, "\n");
n = readstr(off, a, n, buf);
free(buf);
return n;
return p - (char*)a;
}
static long
@ -328,41 +302,6 @@ ac97buffered(Audio *adev)
return Bufsize - Bufsize/Ndesc - ctlr->outavail;
}
static long
ac97ctl(Audio *adev, void *a, long n, vlong)
{
Ctlr *ctlr;
char *tok[2], *p;
int ntok;
long t;
ctlr = adev->ctlr;
if(n > READSTR)
n = READSTR - 1;
p = malloc(READSTR);
if(waserror()){
free(p);
nexterror();
}
memmove(p, a, n);
p[n] = 0;
ntok = tokenize(p, tok, nelem(tok));
if(ntok > 1 && !strcmp(tok[0], "rate")){
t = strtol(tok[1], 0, 10);
if(t < 8000 || t > 48000)
error("rate must be between 8000 and 48000");
ctlr->targetrate = t;
ctlr->hardrate = t;
ac97hardrate(adev, ctlr->hardrate);
poperror();
free(p);
return n;
}
error("invalid ctl");
return n; /* shut up, you compiler you */
}
static void
ac97kick(Ctlr *ctlr, long reg)
{
@ -490,9 +429,6 @@ ac97reset(Audio *adev)
Found:
adev->ctlr = ctlr;
ctlr->targetrate = 44100;
ctlr->hardrate = 44100;
if(p->vid == 0x1039 && p->did == 0x7012)
ctlr->sis7012 = 1;
@ -588,13 +524,11 @@ Found:
csr8w(ctlr, Out+Cr, Ioce); /* | Lvbie | Feie */
csr8w(ctlr, Mic+Cr, Ioce); /* | Lvbie | Feie */
adev->attach = ac97attach;
ac97mixreset(adev, ac97mixw, ac97mixr);
adev->write = ac97write;
adev->status = ac97status;
adev->ctl = ac97ctl;
adev->buffered = ac97buffered;
ac97mixreset(adev, ac97mixw, ac97mixr);
intrenable(irq, ac97interrupt, adev, tbdf, adev->name);

View file

@ -5,19 +5,7 @@
#include "fns.h"
#include "io.h"
#include "../port/error.h"
typedef ushort (*ac97rdfn)(Audio *, int);
typedef void (*ac97wrfn)(Audio *, int, ushort);
typedef struct Mixer Mixer;
typedef struct Volume Volume;
struct Mixer {
QLock;
ac97wrfn wr;
ac97rdfn rr;
int vra;
};
#include "../port/audioif.h"
enum { Maxbusywait = 500000 };
@ -33,22 +21,8 @@ enum {
Capadc18 = 0x100,
Capadc20 = 0x200,
Capenh = 0xfc00,
Master = 0x02,
Headphone = 0x04,
Monomaster = 0x06,
Mastertone = 0x08,
Pcbeep = 0x0A,
Phone = 0x0C,
Mic = 0x0E,
Line = 0x10,
Cd = 0x12,
Video = 0x14,
Aux = 0x16,
Pcmout = 0x18,
Mute = 0x8000,
Recsel = 0x1A,
Recgain = 0x1C,
Micgain = 0x1E,
General = 0x20,
ThreeDctl = 0x22,
Ac97RESER = 0x24,
@ -118,14 +92,6 @@ enum {
Spdifv = 1<<15,
VID1 = 0x7c,
VID2 = 0x7e,
Speed = 0x1234567,
};
enum {
Left,
Right,
Stereo,
Absolute,
};
enum {
@ -143,195 +109,125 @@ enum {
Vaux,
Vrecgain,
Vmicgain,
Vspeed,
};
struct Volume {
int reg;
int range;
int type;
int cap;
char *name;
static Volume voltab[] = {
[Vmaster] "master", 0x02, 63, Stereo, 0,
[Vaudio] "audio", 0x18, 31, Stereo, 0,
[Vhead] "head", 0x04, 31, Stereo, Capheadphones,
[Vbass] "bass", 0x08, 15, Left, Captonectl,
[Vtreb] "treb", 0x08, 15, Right, Captonectl,
[Vbeep] "beep", 0x0a, 31, Right, 0,
[Vphone] "phone", 0x0c, 31, Right, 0,
[Vmic] "mic", 0x0e, 31, Right, Capmic,
[Vline] "line", 0x10, 31, Stereo, 0,
[Vcd] "cd", 0x12, 31, Stereo, 0,
[Vvideo] "video", 0x14, 31, Stereo, 0,
[Vaux] "aux", 0x16, 63, Stereo, 0,
[Vrecgain] "recgain", 0x1c, 15, Stereo, 0,
[Vmicgain] "micgain", 0x1e, 15, Right, Capmic,
[Vspeed] "speed", 0x2c, 0, Absolute, 0,
0
};
struct Topology {
Volume *this;
Volume *next[2];
};
Volume vol[] = {
[Vmaster] {Master, 63, Stereo, 0, "master"},
[Vaudio] {Pcmout, 31, Stereo, 0, "audio"},
[Vhead] {Headphone, 31, Stereo, Capheadphones, "head"},
[Vbass] {Mastertone, 15, Left, Captonectl, "bass"},
[Vtreb] {Mastertone, 15, Right, Captonectl, "treb"},
[Vbeep] {Pcbeep, 31, Right, 0, "beep"},
[Vphone] {Phone, 31, Right, 0, "phone"},
[Vmic] {Mic, 31, Right, Capmic, "mic"},
[Vline] {Line, 31, Stereo, 0, "line"},
[Vcd] {Cd, 31, Stereo, 0, "cd"},
[Vvideo] {Video, 31, Stereo, 0, "video"},
[Vaux] {Aux, 63, Stereo, 0, "aux"},
[Vrecgain] {Recgain, 15, Stereo, 0, "recgain"},
[Vmicgain] {Micgain, 15, Right, Capmic, "micgain"},
{0, 0, 0, 0, 0},
};
long
ac97mixtopology(Audio *adev, void *a, long n, vlong off)
typedef struct Mixer Mixer;
struct Mixer
{
Mixer *m;
char *buf;
long l;
ulong caps;
m = adev->mixer;
qlock(m);
caps = m->rr(adev, Reset);
caps |= m->rr(adev, Extid) << 16;
l = 0;
buf = malloc(READSTR);
l += snprint(buf+l, READSTR-l, "not implemented. have fun.\n");
USED(caps);
USED(l);
qunlock(m);
n = readstr(off, a, n, buf);
free(buf);
return n;
}
ushort (*rr)(Audio *, int);
void (*wr)(Audio *, int, ushort);
int vra;
};
long
ac97mixread(Audio *adev, void *a, long n, vlong off)
static int
ac97volget(Audio *adev, int x, int a[2])
{
Mixer *m;
char *nam, *buf;
long l;
Mixer *m = adev->mixer;
Volume *vol;
ushort v;
ulong caps;
int i, rang, le, ri;
buf = malloc(READSTR);
m = adev->mixer;
qlock(m);
l = 0;
caps = m->rr(adev, Reset);
caps |= m->rr(adev, Extid) << 16;
for(i = 0; vol[i].name != 0; ++i){
if(vol[i].cap && ((vol[i].cap & caps) == 0))
continue;
v = m->rr(adev, vol[i].reg);
nam = vol[i].name;
rang = vol[i].range;
if(vol[i].type == Absolute){
l += snprint(buf+l, READSTR-l, "%s %d", nam, v);
vol = voltab+x;
switch(vol->type){
case Absolute:
a[0] = m->rr(adev, vol->reg);
break;
default:
v = m->rr(adev, vol->reg);
if(v & 0x8000){
a[0] = 0;
a[1] = 0;
} else {
ri = ((rang-(v&rang)) * 100) / rang;
le = ((rang-((v>>8)&rang)) * 100) / rang;
if(vol[i].type == Stereo)
l += snprint(buf+l, READSTR-l, "%s %d %d", nam, le, ri);
if(vol[i].type == Left)
l += snprint(buf+l, READSTR-l, "%s %d", nam, le);
if(vol[i].type == Right)
l += snprint(buf+l, READSTR-l, "%s %d", nam, ri);
if(v&Mute)
l += snprint(buf+l, READSTR-l, " mute");
a[0] = vol->range - ((v>>8) & 0x7f);
a[1] = vol->range - (v & 0x7f);
}
l += snprint(buf+l, READSTR-l, "\n");
}
qunlock(m);
n = readstr(off, a, n, buf);
free(buf);
return n;
return 0;
}
long
static int
ac97volset(Audio *adev, int x, int a[2])
{
Mixer *m = adev->mixer;
Volume *vol;
ushort v, w;
vol = voltab+x;
switch(vol->type){
case Absolute:
m->wr(adev, vol->reg, a[0]);
break;
case Left:
v = (vol->range - a[0]) & 0x7f;
w = m->rr(adev, vol->reg) & 0x7f;
m->wr(adev, vol->reg, (v<<8)|w);
break;
case Right:
v = m->rr(adev, vol->reg) & 0x7f00;
w = (vol->range - a[1]) & 0x7f;
m->wr(adev, vol->reg, v|w);
break;
case Stereo:
v = (vol->range - a[0]) & 0x7f;
w = (vol->range - a[1]) & 0x7f;
m->wr(adev, vol->reg, (v<<8)|w);
break;
}
return 0;
}
static long
ac97mixread(Audio *adev, void *a, long n, vlong)
{
Mixer *m = adev->mixer;
ulong caps;
caps = m->rr(adev, Reset);
caps |= m->rr(adev, Extid) << 16;
return genaudiovolread(adev, a, n, 0, voltab, ac97volget, caps);
}
static long
ac97mixwrite(Audio *adev, void *a, long n, vlong)
{
Mixer *m;
char *tok[4];
int ntok, i, left, right, rang, reg;
ushort v;
m = adev->mixer;
qlock(m);
ntok = tokenize(a, tok, 4);
for(i = 0; vol[i].name != 0; ++i){
if(!strcmp(vol[i].name, tok[0])){
rang = vol[i].range;
reg = vol[i].reg;
left = right = 0;
if(ntok > 1)
left = right = atoi(tok[1]);
if(ntok > 2)
right = atoi(tok[2]);
Mixer *m = adev->mixer;
ulong caps;
if(vol[i].type == Absolute){
m->wr(adev, reg, left);
} else {
left = rang - ((left*rang)) / 100;
right = rang - ((right*rang)) / 100;
switch(vol[i].type){
default:
break;
case Left:
v = m->rr(adev, reg);
v = (v & 0x007f) | (left << 8);
m->wr(adev, reg, v);
break;
case Right:
v = m->rr(adev, reg);
v = (v & 0x7f00) | right;
m->wr(adev, reg, v);
break;
case Stereo:
v = (left<<8) | right;
m->wr(adev, reg, v);
break;
}
}
qunlock(m);
return n;
}
}
if(vol[i].name == nil){
char *p;
for(p = tok[0]; *p; ++p)
if(*p < '0' || *p > '9') {
qunlock(m);
error("no such volume setting");
}
rang = vol[0].range;
reg = vol[0].reg;
left = right = rang - ((atoi(tok[0])*rang)) / 100;
v = (left<<8) | right;
m->wr(adev, reg, v);
}
qunlock(m);
return n;
}
int
ac97hardrate(Audio *adev, int rate)
{
Mixer *m;
int oldrate;
m = adev->mixer;
oldrate = m->rr(adev, Pcmfrontdacrate);
if(rate > 0)
m->wr(adev, Pcmfrontdacrate, rate);
return oldrate;
caps = m->rr(adev, Reset);
caps |= m->rr(adev, Extid) << 16;
return genaudiovolwrite(adev, a, n, 0, voltab, ac97volset, caps);
}
void
ac97mixreset(Audio *adev, ac97wrfn wr, ac97rdfn rr)
ac97mixreset(Audio *adev, void (*wr)(Audio*,int,ushort), ushort (*rr)(Audio*,int))
{
Mixer *m;
int i;
ushort t;
if(adev->mixer == nil)
adev->mixer = malloc(sizeof(Mixer));
m = adev->mixer;
int i;
m = malloc(sizeof(Mixer));
m->wr = wr;
m->rr = rr;
adev->volread = ac97mixread;
adev->volwrite = ac97mixwrite;
m->wr(adev, Reset, 0);
m->wr(adev, Powerdowncsr, 0);
@ -361,4 +257,8 @@ ac97mixreset(Audio *adev, ac97wrfn wr, ac97rdfn rr)
print("#A%d: ac97 vra extension not supported\n", adev->ctlrno);
m->vra = 0;
}
adev->mixer = m;
adev->volread = ac97mixread;
adev->volwrite = ac97mixwrite;
}

View file

@ -8,6 +8,7 @@
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "../port/audioif.h"
typedef struct Ring Ring;
typedef struct Blaster Blaster;
@ -15,11 +16,8 @@ typedef struct Ctlr Ctlr;
enum
{
Fmono = 1,
Fin = 2,
Fout = 4,
Vaudio = 0,
Vmaster,
Vaudio,
Vsynth,
Vcd,
Vline,
@ -27,12 +25,11 @@ enum
Vspeaker,
Vtreb,
Vbass,
Vigain,
Vogain,
Vspeed,
Nvol,
Speed = 44100,
Ncmd = 50, /* max volume command words */
Blocksize = 4096,
Blocks = 65536/Blocksize,
};
@ -70,44 +67,34 @@ 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;
int lvol[Nvol];
int rvol[Nvol];
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 Volume voltab[] = {
[Vmaster] "master", 0x30, 0xff, Stereo, 0,
[Vaudio] "audio", 0x32, 0xff, Stereo, 0,
[Vsynth] "synth", 0x34, 0xff, Stereo, 0,
[Vcd] "cd", 0x36, 0xff, Stereo, 0,
[Vline] "line", 0x38, 0xff, Stereo, 0,
[Vmic] "mic", 0x3a, 0xff, Mono, 0,
[Vspeaker] "speaker", 0x3b, 0xff, Mono, 0,
[Vtreb] "treb", 0x44, 0xff, Stereo, 0,
[Vbass] "bass", 0x46, 0xff, Stereo, 0,
[Vigain] "recgain", 0x3f, 0xff, Stereo, 0,
[Vogain] "outgain", 0x41, 0xff, Stereo, 0,
[Vspeed] "speed", 0, 0, Absolute, 0,
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)
@ -242,109 +229,44 @@ mxread(Blaster *blaster, int addr)
return s;
}
static void
mxcmds(Blaster *blaster, int s, int v)
{
if(v > 100)
v = 100;
if(v < 0)
v = 0;
mxcmd(blaster, s, (v*255)/100);
}
static void
mxcmdt(Blaster *blaster, int s, int v)
{
if(v > 100)
v = 100;
if(v <= 0)
mxcmd(blaster, s, 0);
else
mxcmd(blaster, s, 255-100+v);
}
static void
mxcmdu(Blaster *blaster, int s, int v)
{
if(v > 100)
v = 100;
if(v <= 0)
v = 0;
mxcmd(blaster, s, 128-50+v);
}
static void
mxvolume(Ctlr *ctlr)
static int
mxsetvol(Audio *adev, int x, int a[2])
{
Blaster *blaster;
int *left, *right;
int source;
Ctlr *ctlr = adev->ctlr;
Volume *vol;
if(0){
left = ctlr->livol;
right = ctlr->rivol;
}else{
left = ctlr->lovol;
right = ctlr->rovol;
if(x == Vspeed){
ctlr->lvol[x] = ctlr->rvol[x] = a[0];
return 0;
}
vol = voltab+x;
blaster = &ctlr->blaster;
ilock(blaster);
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(blaster, 0x32, left[Vaudio]);
mxcmds(blaster, 0x33, right[Vaudio]);
mxcmds(blaster, 0x34, left[Vsynth]);
mxcmds(blaster, 0x35, right[Vsynth]);
mxcmds(blaster, 0x36, left[Vcd]);
mxcmds(blaster, 0x37, right[Vcd]);
mxcmds(blaster, 0x38, left[Vline]);
mxcmds(blaster, 0x39, right[Vline]);
mxcmds(blaster, 0x3a, left[Vmic]);
mxcmds(blaster, 0x3b, left[Vspeaker]);
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])
source |= 1<<6;
if(right[Vsynth])
source |= 1<<5;
if(left[Vaudio])
source |= 1<<4;
if(right[Vaudio])
source |= 1<<3;
if(left[Vcd])
source |= 1<<2;
if(right[Vcd])
source |= 1<<1;
if(left[Vmic])
source |= 1<<0;
if(0)
mxcmd(blaster, 0x3c, 0); /* output switch */
else
mxcmd(blaster, 0x3c, source);
mxcmd(blaster, 0x3d, source); /* input left switch */
mxcmd(blaster, 0x3e, source); /* input right switch */
switch(vol->type){
case Stereo:
ctlr->rvol[x] = a[1];
mxcmd(blaster, vol->reg+1, a[1]);
/* no break */
case Mono:
ctlr->lvol[x] = a[0];
mxcmd(blaster, vol->reg, a[0]);
}
iunlock(blaster);
return 0;
}
static int
mxgetvol(Audio *adev, int x, int a[2])
{
Ctlr *ctlr = adev->ctlr;
a[0] = ctlr->lvol[x];
a[1] = ctlr->rvol[x];
return 0;
}
static void
@ -385,22 +307,20 @@ sb16startdma(Ctlr *ctlr)
ring = &ctlr->ring;
ilock(blaster);
dmaend(blaster->dma);
if(0) {
sbcmd(blaster, 0x42); /* input sampling rate */
speed = ctlr->livol[Vspeed];
} else {
sbcmd(blaster, 0x41); /* output sampling rate */
speed = ctlr->lovol[Vspeed];
}
if(0)
sbcmd(blaster, 0x42); /* input sampling rate */
else
sbcmd(blaster, 0x41); /* output sampling rate */
speed = ctlr->lvol[Vspeed];
sbcmd(blaster, speed>>8);
sbcmd(blaster, speed);
if(0)
sbcmd(blaster, 0xbe); /* A/D, autoinit */
sbcmd(blaster, 0xbe); /* A/D, autoinit */
else
sbcmd(blaster, 0xb6); /* D/A, autoinit */
sbcmd(blaster, 0xb6); /* D/A, autoinit */
sbcmd(blaster, 0x30); /* stereo, signed 16 bit */
sbcmd(blaster, 0x30); /* stereo, signed 16 bit */
count = (Blocksize>>1) - 1;
sbcmd(blaster, count);
@ -420,7 +340,7 @@ ess1688reset(Blaster *blaster, int ctlrno)
int i;
outb(blaster->reset, 3);
delay(1); /* >3 υs */
delay(1); /* >3 υs */
outb(blaster->reset, 0);
delay(1);
@ -430,7 +350,7 @@ ess1688reset(Blaster *blaster, int ctlrno)
return 1;
}
if(sbcmd(blaster, 0xC6)){ /* extended mode */
if(sbcmd(blaster, 0xC6)){ /* extended mode */
print("#A%d: barf 3\n", ctlrno);
return 1;
}
@ -457,15 +377,11 @@ ess1688startdma(Ctlr *ctlr)
/*
* Set the speed.
*/
if(0)
speed = ctlr->livol[Vspeed];
else
speed = ctlr->lovol[Vspeed];
speed = ctlr->lvol[Vspeed];
if(speed < 4000)
speed = 4000;
else if(speed > 48000)
speed = 48000;
if(speed > 22000)
x = 0x80|(256-(795500+speed/2)/speed);
else
@ -477,15 +393,15 @@ ess1688startdma(Ctlr *ctlr)
ess1688w(blaster, 0xA2, x & 0xFF);
if(0)
ess1688w(blaster, 0xB8, 0x0E); /* A/D, autoinit */
ess1688w(blaster, 0xB8, 0x0E); /* A/D, autoinit */
else
ess1688w(blaster, 0xB8, 0x04); /* D/A, autoinit */
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 */
ess1688w(blaster, 0xA8, x|0x01); /* 2 channels */
ess1688w(blaster, 0xB9, 2); /* demand mode, 4 bytes per request */
if(1)
ess1688w(blaster, 0xB6, 0); /* for output */
ess1688w(blaster, 0xB6, 0); /* for output */
ess1688w(blaster, 0xB7, 0x71);
ess1688w(blaster, 0xB7, 0xBC);
@ -496,7 +412,7 @@ ess1688startdma(Ctlr *ctlr)
ess1688w(blaster, 0xB2, x|0x50);
if(1)
sbcmd(blaster, 0xD1); /* speaker on */
sbcmd(blaster, 0xD1); /* speaker on */
count = -Blocksize;
ess1688w(blaster, 0xA4, count & 0xFF);
@ -571,19 +487,6 @@ setempty(Ctlr *ctlr)
iunlock(&ctlr->blaster);
}
static void
resetlevel(Ctlr *ctlr)
{
int i;
for(i=0; volumes[i].name; i++) {
ctlr->lovol[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 *adev)
{
@ -591,17 +494,15 @@ audiobuffered(Audio *adev)
}
static long
audiostatus(Audio *adev, void *a, long n, vlong off)
audiostatus(Audio *adev, void *a, long n, vlong)
{
char buf[300];
Ctlr *ctlr;
Ctlr *ctlr = adev->ctlr;
ctlr = adev->ctlr;
snprint(buf, sizeof(buf),
"buffered %.4lx/%.4lx offset %10lud time %19lld\n",
buffered(&ctlr->ring), available(&ctlr->ring),
return snprint((char*)a, n,
"bufsize %6lud buffered %6ld "
"offset %10lud time %19lld\n",
ctlr->ring.nbuf, buffered(&ctlr->ring),
ctlr->totcount, ctlr->tottime);
return readstr(off, a, n, buf);
}
static int
@ -626,7 +527,6 @@ audiowrite(Audio *adev, void *vp, long n, vlong)
uchar *p, *e;
Ctlr *ctlr;
Ring *ring;
long m;
p = vp;
e = p + n;
@ -638,17 +538,15 @@ audiowrite(Audio *adev, void *vp, long n, vlong)
}
ring = &ctlr->ring;
while(p < e) {
if((m = writering(ring, p, e - p)) <= 0){
if((n = writering(ring, p, e - p)) <= 0){
if(!ctlr->active && ring->ri == 0)
ctlr->blaster.startdma(ctlr);
if(!ctlr->active){
if(!ctlr->active)
setempty(ctlr);
continue;
}
sleep(&ctlr->vous, anybuf, ctlr);
continue;
else
sleep(&ctlr->vous, anybuf, ctlr);
}
p += m;
p += n;
}
poperror();
qunlock(ctlr);
@ -673,6 +571,49 @@ audioclose(Audio *adev)
qunlock(ctlr);
}
static long
audiovolread(Audio *adev, void *a, long n, vlong)
{
return genaudiovolread(adev, a, n, 0, voltab, mxgetvol, 0);
}
static long
audiovolwrite(Audio *adev, void *a, long n, vlong)
{
Blaster *blaster;
Ctlr *ctlr;
int source;
ctlr = adev->ctlr;
blaster = &ctlr->blaster;
n = genaudiovolwrite(adev, a, n, 0, voltab, mxsetvol, 0);
source = 0;
if(ctlr->lvol[Vsynth])
source |= 1<<6;
if(ctlr->rvol[Vsynth])
source |= 1<<5;
if(ctlr->lvol[Vaudio])
source |= 1<<4;
if(ctlr->rvol[Vaudio])
source |= 1<<3;
if(ctlr->lvol[Vcd])
source |= 1<<2;
if(ctlr->rvol[Vcd])
source |= 1<<1;
if(ctlr->lvol[Vmic])
source |= 1<<0;
ilock(blaster);
mxcmd(blaster, 0x3c, source); /* output switch */
mxcmd(blaster, 0x3d, source); /* input left switch */
mxcmd(blaster, 0x3e, source); /* input right switch */
iunlock(blaster);
return n;
}
static int
ess1688(ISAConf* sbconf, Blaster *blaster, int ctlrno)
{
@ -685,7 +626,8 @@ ess1688(ISAConf* sbconf, Blaster *blaster, int ctlrno)
major = sbread(blaster);
minor = sbread(blaster);
if(major != 0x68 || minor != 0x8B){
print("#A%d: model %#.2x %#.2x; not ESS1688 compatible\n", ctlrno, major, minor);
print("#A%d: model %#.2x %#.2x; not ESS1688 compatible\n",
ctlrno, major, minor);
return -1;
}
@ -763,12 +705,14 @@ audioprobe(Audio *adev)
}
if(ioalloc(sbconf.port, 0x10, 0, "audio") < 0){
print("#A%d: cannot ioalloc range %lux+0x10\n", adev->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", adev->ctlrno, sbconf.port+0x100);
print("#A%d: cannot ioalloc range %lux+0x01\n",
adev->ctlrno, sbconf.port+0x100);
return -1;
}
@ -792,8 +736,6 @@ audioprobe(Audio *adev)
blaster->startdma = sb16startdma;
blaster->intr = sb16intr;
resetlevel(ctlr);
outb(blaster->reset, 1);
delay(1); /* >3 υs */
outb(blaster->reset, 0);
@ -802,6 +744,7 @@ audioprobe(Audio *adev)
i = sbread(blaster);
if(i != 0xaa) {
print("#A%d: no response #%.2x\n", adev->ctlrno, i);
Errout:
iofree(sbconf.port);
iofree(sbconf.port+0x100);
free(ctlr);
@ -813,12 +756,11 @@ audioprobe(Audio *adev)
ctlr->minor = sbread(blaster);
if(ctlr->major != 4) {
if(ctlr->major != 3 || ctlr->minor != 1 || ess1688(&sbconf, blaster, adev->ctlrno)){
if(ctlr->major != 3 || ctlr->minor != 1 ||
ess1688(&sbconf, blaster, adev->ctlrno)){
print("#A%d: model %#.2x %#.2x; not SB 16 compatible\n",
adev->ctlrno, ctlr->major, ctlr->minor);
iofree(sbconf.port);
iofree(sbconf.port+0x100);
return -1;
goto Errout;
}
ctlr->major = 4;
}
@ -827,7 +769,14 @@ audioprobe(Audio *adev)
* initialize the mixer
*/
mxcmd(blaster, 0x00, 0); /* Reset mixer */
mxvolume(ctlr);
for(i=0; i<Nvol; i++){
int a[2];
a[0] = 0;
a[1] = 0;
mxsetvol(adev, i, a);
}
/* set irq */
for(i=0; i<nelem(irq); i++){
@ -857,33 +806,33 @@ audioprobe(Audio *adev)
break;
}
}
if(blaster->dma<5){
blaster->dma = 7;
continue;
}
break;
if(blaster->dma>=5)
break;
blaster->dma = 7;
}
print("#A%d: %s port 0x%04lux irq %d dma %d\n", adev->ctlrno, sbconf.type,
sbconf.port, sbconf.irq, blaster->dma);
ctlr->ring.nbuf = Blocks*Blocksize;
if(dmainit(blaster->dma, ctlr->ring.nbuf)){
free(ctlr);
return -1;
}
if(dmainit(blaster->dma, ctlr->ring.nbuf))
goto Errout;
ctlr->ring.buf = dmabva(blaster->dma);
intrenable(sbconf.irq, audiointr, adev, BUSUNKNOWN, sbconf.type);
print("#A%d: %s dma buffer %p-%p\n", adev->ctlrno, sbconf.type,
ctlr->ring.buf, ctlr->ring.buf+ctlr->ring.nbuf);
setempty(ctlr);
mxvolume(ctlr);
adev->write = audiowrite;
adev->close = audioclose;
adev->volread = audiovolread;
adev->volwrite = audiovolwrite;
adev->status = audiostatus;
adev->buffered = audiobuffered;
intrenable(sbconf.irq, audiointr, adev, BUSUNKNOWN, sbconf.type);
return 0;
}

50
sys/src/9/port/audioif.h Normal file
View file

@ -0,0 +1,50 @@
typedef struct Audio Audio;
typedef struct Volume Volume;
struct Audio
{
char *name;
void *ctlr;
void *mixer;
long (*read)(Audio *, void *, long, vlong);
long (*write)(Audio *, void *, long, vlong);
void (*close)(Audio *);
long (*volread)(Audio *, void *, long, vlong);
long (*volwrite)(Audio *, void *, long, vlong);
long (*ctl)(Audio *, void *, long, vlong);
long (*status)(Audio *, void *, long, vlong);
long (*buffered)(Audio *);
int ctlrno;
Audio *next;
};
enum {
Left,
Right,
Stereo,
Absolute,
};
#define Mono Left
struct Volume
{
char *name;
int reg;
int range;
int type;
int cap;
};
extern void addaudiocard(char *, int (*)(Audio *));
extern long genaudiovolread(Audio *adev, void *a, long n, vlong off,
Volume *vol, int (*volget)(Audio *, int, int *),
ulong caps);
extern long genaudiovolwrite(Audio *adev, void *a, long n, vlong off,
Volume *vol, int (*volset)(Audio *, int, int *),
ulong caps);

View file

@ -5,27 +5,36 @@
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "../port/audioif.h"
typedef struct Audioprobe Audioprobe;
struct Audioprobe {
typedef struct Audiochan Audiochan;
struct Audioprobe
{
char *name;
int (*probe)(Audio*);
};
struct Audiochan
{
QLock;
Chan *owner;
Audio *adev;
char *data;
char buf[1024+1];
};
enum {
Qdir = 0,
Qaudio,
Qaudioctl,
Qaudiostatus,
Qvolume,
Maxaudioprobes = 8,
};
static int naudioprobes;
static Audioprobe audioprobes[Maxaudioprobes];
static Audio *audiodevs;
static Dirtab audiodir[] = {
".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
"audio", {Qaudio}, 0, 0666,
@ -34,10 +43,22 @@ static Dirtab audiodir[] = {
"volume", {Qvolume}, 0, 0666,
};
static int naudioprobes;
static Audioprobe audioprobes[16];
static Audio *audiodevs;
static char Evolume[] = "illegal volume specifier";
void
addaudiocard(char *name, int (*probefn)(Audio *))
{
Audioprobe *probe;
if(naudioprobes >= nelem(audioprobes))
return;
probe = &audioprobes[naudioprobes++];
probe->name = name;
probe->probe = probefn;
@ -73,48 +94,107 @@ audioreset(void)
*pp = nil;
}
static Audiochan*
audioclone(Chan *c, Audio *adev)
{
Audiochan *ac;
ac = malloc(sizeof(Audiochan));
if(ac == nil){
cclose(c);
return nil;
}
c->aux = ac;
ac->owner = c;
ac->adev = adev;
ac->data = nil;
return ac;
}
static Chan*
audioattach(char *spec)
{
static int first = 1;
Audiochan *ac;
Audio *adev;
Chan *c;
Audio *p;
int i;
if(spec != nil && *spec != '\0')
i = strtol(spec, 0, 10);
else
i = 0;
for(p = audiodevs; p; p = p->next)
for(adev = audiodevs; adev; adev = adev->next)
if(i-- == 0)
break;
if(p == nil)
if(adev == nil)
error(Enodev);
c = devattach('A', spec);
c->qid.path = Qdir;
c->aux = p;
if(p->attach)
p->attach(p);
if((ac = audioclone(c, adev)) == nil)
error(Enomem);
if(first && adev->volwrite){
first = 0;
strcpy(ac->buf, "speed 44100");
if(!waserror()){
adev->volwrite(adev, ac->buf, strlen(ac->buf), 0);
poperror();
}
strcpy(ac->buf, "master 100");
if(!waserror()){
adev->volwrite(adev, ac->buf, strlen(ac->buf), 0);
poperror();
}
strcpy(ac->buf, "audio 100");
if(!waserror()){
adev->volwrite(adev, ac->buf, strlen(ac->buf), 0);
poperror();
}
strcpy(ac->buf, "head 100");
if(!waserror()){
adev->volwrite(adev, ac->buf, strlen(ac->buf), 0);
poperror();
}
}
return c;
}
static Chan*
audioopen(Chan *c, int omode)
{
return devopen(c, omode, audiodir, nelem(audiodir), devgen);
}
static long
audioread(Chan *c, void *a, long n, vlong off)
{
Audiochan *ac;
Audio *adev;
long (*fn)(Audio *, void *, long, vlong);
adev = c->aux;
ac = c->aux;
adev = ac->adev;
fn = nil;
switch((ulong)c->qid.path){
default:
error("audio bugger (rd)");
case Qaudioctl:
fn = adev->ctl;
break;
case Qdir:
/* BUG: race */
if(adev->buffered)
audiodir[Qaudio].length = adev->buffered(adev);
return devdirread(c, a, n, audiodir, nelem(audiodir), devgen);
case Qaudio:
fn = adev->read;
break;
case Qaudioctl:
fn = adev->ctl;
break;
case Qaudiostatus:
fn = adev->status;
break;
@ -123,19 +203,49 @@ audioread(Chan *c, void *a, long n, vlong off)
break;
}
if(fn == nil)
error("not implemented");
error(Egreg);
switch((ulong)c->qid.path){
case Qaudioctl:
case Qaudiostatus:
case Qvolume:
qlock(ac);
if(waserror()){
qunlock(ac);
nexterror();
}
/* generate the text on first read */
if(ac->data == nil || off == 0){
long l;
ac->data = nil;
l = fn(adev, ac->buf, sizeof(ac->buf)-1, 0);
if(l < 0)
l = 0;
ac->buf[l] = 0;
ac->data = ac->buf;
}
/* then serve all requests from buffer */
n = readstr(off, a, n, ac->data);
qunlock(ac);
poperror();
return n;
}
return fn(adev, a, n, off);
}
static long
audiowrite(Chan *c, void *a, long n, vlong off)
{
Audiochan *ac;
Audio *adev;
long (*fn)(Audio *, void *, long, vlong);
adev = c->aux;
ac = c->aux;
adev = ac->adev;
fn = nil;
switch((ulong)c->qid.path){
default:
error("audio bugger (wr)");
case Qaudio:
fn = adev->write;
break;
@ -147,46 +257,195 @@ audiowrite(Chan *c, void *a, long n, vlong off)
break;
}
if(fn == nil)
error("not implemented");
error(Egreg);
switch((ulong)c->qid.path){
case Qaudioctl:
case Qvolume:
if(n >= sizeof(ac->buf))
error(Etoobig);
/* copy data to audiochan buffer so it can be modified */
qlock(ac);
if(waserror()){
qunlock(ac);
nexterror();
}
ac->data = nil;
memmove(ac->buf, a, n);
ac->buf[n] = 0;
n = fn(adev, ac->buf, n, 0);
qunlock(ac);
poperror();
return n;
}
return fn(adev, a, n, off);
}
static void
audioclose(Chan *c)
{
Audiochan *ac;
Audio *adev;
adev = c->aux;
switch((ulong)c->qid.path){
default:
return;
case Qaudio:
if(adev->close == nil)
return;
adev->close(adev);
return;
ac = c->aux;
adev = ac->adev;
if(c->qid.path == Qaudio && (c->flag & COPEN))
if(adev->close)
adev->close(adev);
if(ac->owner == c){
ac->owner = nil;
c->aux = nil;
free(ac);
}
}
static Walkqid*
audiowalk(Chan *c, Chan *nc, char **name, int nname)
{
return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
Audiochan *ac;
Audio *adev;
Walkqid *wq;
ac = c->aux;
adev = ac->adev;
wq = devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
if(wq && wq->clone){
if(audioclone(wq->clone, adev) == nil){
free(wq);
wq = nil;
}
}
return wq;
}
static int
audiostat(Chan *c, uchar *dp, int n)
{
Audiochan *ac;
Audio *adev;
adev = c->aux;
ac = c->aux;
adev = ac->adev;
/* BUG: race */
if(adev->buffered && (ulong)c->qid.path == Qaudio)
audiodir[Qaudio].length = adev->buffered(adev);
return devstat(c, dp, n, audiodir, nelem(audiodir), devgen);
}
static Chan*
audioopen(Chan *c, int omode)
/*
* audioread() made sure the buffer is big enougth so a full volume
* table can be serialized in one pass.
*/
long
genaudiovolread(Audio *adev, void *a, long n, vlong,
Volume *vol, int (*volget)(Audio *, int, int *), ulong caps)
{
return devopen(c, omode, audiodir, nelem(audiodir), devgen);
int i, j, v[2];
char *p, *e;
p = a;
e = p + n;
for(i = 0; vol[i].name != 0; ++i){
if(vol[i].cap && (vol[i].cap & caps) == 0)
continue;
v[0] = 0;
v[1] = 0;
if((*volget)(adev, i, v) != 0)
continue;
if(vol[i].type == Absolute)
p += snprint(p, e - p, "%s %d\n", vol[i].name, v[0]);
else {
for(j=0; j<2; j++){
if(v[j] < 0)
v[j] = 0;
if(v[j] > vol[i].range)
v[j] = vol[i].range;
v[j] = (v[j]*100)/vol[i].range;
}
switch(vol[i].type){
case Left:
p += snprint(p, e - p, "%s %d\n", vol[i].name, v[0]);
break;
case Right:
p += snprint(p, e - p, "%s %d\n", vol[i].name, v[1]);
break;
case Stereo:
p += snprint(p, e - p, "%s %d %d\n", vol[i].name, v[0], v[1]);
break;
}
}
}
return p - (char*)a;
}
/*
* genaudiovolwrite modifies the buffer that gets passed to it. this
* is ok as long as it is called from inside Audio.volwrite() because
* audiowrite() copies the data to Audiochan.buf[] and inserts a
* terminating \0 byte before calling Audio.volwrite().
*/
long
genaudiovolwrite(Audio *adev, void *a, long n, vlong,
Volume *vol, int (*volset)(Audio *, int, int *), ulong caps)
{
int ntok, i, j, v[2];
char *p, *e, *x, *tok[4];
p = a;
e = p + n;
for(;p < e; p = x){
if(x = strchr(p, '\n'))
*x++ = 0;
else
x = e;
ntok = tokenize(p, tok, 4);
if(ntok <= 0)
continue;
if(ntok == 1){
tok[1] = tok[0];
tok[0] = "master";
ntok = 2;
}
for(i = 0; vol[i].name != 0; i++){
if(vol[i].cap && (vol[i].cap & caps) == 0)
continue;
if(cistrcmp(vol[i].name, tok[0]))
continue;
if((ntok>2) && (!cistrcmp(tok[1], "out") || !cistrcmp(tok[1], "in")))
memmove(tok+1, tok+2, --ntok);
v[0] = 0;
v[1] = 0;
if(ntok > 1)
v[0] = v[1] = atoi(tok[1]);
if(ntok > 2)
v[1] = atoi(tok[2]);
if(vol[i].type == Absolute)
(*volset)(adev, i, v);
else {
for(j=0; j<2; j++){
v[j] = (50+(v[j]*vol[i].range))/100;
if(v[j] < 0)
v[j] = 0;
if(v[j] > vol[i].range)
v[j] = vol[i].range;
}
(*volset)(adev, i, v);
}
break;
}
if(vol[i].name == nil)
error(Evolume);
}
return n;
}
Dev audiodevtab = {

View file

@ -1,5 +1,4 @@
typedef struct Alarms Alarms;
typedef struct Audio Audio;
typedef struct Block Block;
typedef struct Chan Chan;
typedef struct Cmdbuf Cmdbuf;
@ -919,23 +918,6 @@ struct Uart
extern Uart* consuart;
struct Audio {
Audio *next;
char *name;
void *ctlr;
void *mixer;
void (*attach)(Audio *);
long (*read)(Audio *, void *, long, vlong);
long (*write)(Audio *, void *, long, vlong);
long (*volread)(Audio *, void *, long, vlong);
long (*volwrite)(Audio *, void *, long, vlong);
void (*close)(Audio *);
long (*ctl)(Audio *, void *, long, vlong);
long (*status)(Audio *, void *, long, vlong);
long (*buffered)(Audio *);
int ctlrno;
};
/*
* performance timers, all units in perfticks
*/

View file

@ -4,7 +4,6 @@ Timer* addclock0link(void (*)(void), int);
int addphysseg(Physseg*);
void addbootfile(char*, uchar*, ulong);
void addwatchdog(Watchdog*);
void addaudiocard(char *, int (*)(Audio *));
Block* adjustblock(Block*, int);
void alarmkproc(void*);
Block* allocb(int);