audioif, mixer control
This commit is contained in:
parent
334c58f95e
commit
4bc74b8aef
|
@ -5,6 +5,7 @@
|
||||||
#include "fns.h"
|
#include "fns.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "../port/error.h"
|
#include "../port/error.h"
|
||||||
|
#include "../port/audioif.h"
|
||||||
|
|
||||||
typedef struct Hwdesc Hwdesc;
|
typedef struct Hwdesc Hwdesc;
|
||||||
typedef struct Ctlr Ctlr;
|
typedef struct Ctlr Ctlr;
|
||||||
|
@ -65,10 +66,6 @@ struct Ctlr {
|
||||||
ulong civstat[Ndesc];
|
ulong civstat[Ndesc];
|
||||||
ulong lvistat[Ndesc];
|
ulong lvistat[Ndesc];
|
||||||
|
|
||||||
int targetrate;
|
|
||||||
int hardrate;
|
|
||||||
|
|
||||||
int attachok;
|
|
||||||
int sis7012;
|
int sis7012;
|
||||||
|
|
||||||
/* for probe */
|
/* for probe */
|
||||||
|
@ -145,8 +142,8 @@ enum {
|
||||||
#define csr32w(c, r, w) (outl((c)->port+(r), (ulong)(w)))
|
#define csr32w(c, r, w) (outl((c)->port+(r), (ulong)(w)))
|
||||||
|
|
||||||
/* audioac97mix */
|
/* audioac97mix */
|
||||||
extern int ac97hardrate(Audio *, int);
|
extern void ac97mixreset(Audio *,
|
||||||
extern void ac97mixreset(Audio *, void (*wr)(Audio*,int,ushort),
|
void (*wr)(Audio*,int,ushort),
|
||||||
ushort (*rr)(Audio*,int));
|
ushort (*rr)(Audio*,int));
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -202,11 +199,9 @@ ac97interrupt(Ureg *, void *arg)
|
||||||
adev = arg;
|
adev = arg;
|
||||||
ctlr = adev->ctlr;
|
ctlr = adev->ctlr;
|
||||||
stat = csr32r(ctlr, Sta);
|
stat = csr32r(ctlr, Sta);
|
||||||
|
|
||||||
stat &= S2ri | Sri | Pri | Mint | Point | Piint | Moint | Miint | Gsci;
|
stat &= S2ri | Sri | Pri | Mint | Point | Piint | Moint | Miint | Gsci;
|
||||||
|
|
||||||
ilock(ctlr);
|
|
||||||
if(stat & Point){
|
if(stat & Point){
|
||||||
|
ilock(ctlr);
|
||||||
if(ctlr->sis7012)
|
if(ctlr->sis7012)
|
||||||
csr16w(ctlr, Out + Picb, csr16r(ctlr, Out + Picb) & ~Dch);
|
csr16w(ctlr, Out + Picb, csr16r(ctlr, Out + Picb) & ~Dch);
|
||||||
else
|
else
|
||||||
|
@ -232,10 +227,11 @@ ac97interrupt(Ureg *, void *arg)
|
||||||
if(ctlr->outavail > Bufsize/2)
|
if(ctlr->outavail > Bufsize/2)
|
||||||
wakeup(&ctlr->outr);
|
wakeup(&ctlr->outr);
|
||||||
stat &= ~Point;
|
stat &= ~Point;
|
||||||
|
iunlock(ctlr);
|
||||||
}
|
}
|
||||||
iunlock(ctlr);
|
|
||||||
if(stat) /* have seen 0x400, which is sdin0 resume */
|
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
|
static int
|
||||||
|
@ -272,52 +268,30 @@ ac97medianoutrate(Audio *adev)
|
||||||
return ts[Nts/2] / BytesPerSample;
|
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
|
static long
|
||||||
ac97status(Audio *adev, void *a, long n, vlong off)
|
ac97status(Audio *adev, void *a, long n, vlong)
|
||||||
{
|
{
|
||||||
|
char *p, *e;
|
||||||
Ctlr *ctlr;
|
Ctlr *ctlr;
|
||||||
char *buf;
|
int i;
|
||||||
long i, l;
|
|
||||||
ctlr = adev->ctlr;
|
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++)
|
for(i = 0; i < Ndesc; i++)
|
||||||
l += snprint(buf + l, READSTR - l, " %lud", ctlr->civstat[i]);
|
p += snprint(p, e - p, " %lud", ctlr->civstat[i]);
|
||||||
l += snprint(buf + l, READSTR - l, "\n");
|
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++)
|
for(i = 0; i < Ndesc; i++)
|
||||||
l += snprint(buf + l, READSTR - l, " %lud", ctlr->lvistat[i]);
|
p += snprint(p, e - p, " %lud", ctlr->lvistat[i]);
|
||||||
snprint(buf + l, READSTR - l, "\n");
|
p += snprint(p, e - p, "\n");
|
||||||
|
|
||||||
n = readstr(off, a, n, buf);
|
return p - (char*)a;
|
||||||
free(buf);
|
|
||||||
return n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static long
|
static long
|
||||||
|
@ -328,41 +302,6 @@ ac97buffered(Audio *adev)
|
||||||
return Bufsize - Bufsize/Ndesc - ctlr->outavail;
|
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
|
static void
|
||||||
ac97kick(Ctlr *ctlr, long reg)
|
ac97kick(Ctlr *ctlr, long reg)
|
||||||
{
|
{
|
||||||
|
@ -490,9 +429,6 @@ ac97reset(Audio *adev)
|
||||||
|
|
||||||
Found:
|
Found:
|
||||||
adev->ctlr = ctlr;
|
adev->ctlr = ctlr;
|
||||||
ctlr->targetrate = 44100;
|
|
||||||
ctlr->hardrate = 44100;
|
|
||||||
|
|
||||||
if(p->vid == 0x1039 && p->did == 0x7012)
|
if(p->vid == 0x1039 && p->did == 0x7012)
|
||||||
ctlr->sis7012 = 1;
|
ctlr->sis7012 = 1;
|
||||||
|
|
||||||
|
@ -588,13 +524,11 @@ Found:
|
||||||
csr8w(ctlr, Out+Cr, Ioce); /* | Lvbie | Feie */
|
csr8w(ctlr, Out+Cr, Ioce); /* | Lvbie | Feie */
|
||||||
csr8w(ctlr, Mic+Cr, Ioce); /* | Lvbie | Feie */
|
csr8w(ctlr, Mic+Cr, Ioce); /* | Lvbie | Feie */
|
||||||
|
|
||||||
adev->attach = ac97attach;
|
ac97mixreset(adev, ac97mixw, ac97mixr);
|
||||||
|
|
||||||
adev->write = ac97write;
|
adev->write = ac97write;
|
||||||
adev->status = ac97status;
|
adev->status = ac97status;
|
||||||
adev->ctl = ac97ctl;
|
|
||||||
adev->buffered = ac97buffered;
|
adev->buffered = ac97buffered;
|
||||||
|
|
||||||
ac97mixreset(adev, ac97mixw, ac97mixr);
|
|
||||||
|
|
||||||
intrenable(irq, ac97interrupt, adev, tbdf, adev->name);
|
intrenable(irq, ac97interrupt, adev, tbdf, adev->name);
|
||||||
|
|
||||||
|
|
|
@ -5,19 +5,7 @@
|
||||||
#include "fns.h"
|
#include "fns.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "../port/error.h"
|
#include "../port/error.h"
|
||||||
|
#include "../port/audioif.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;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum { Maxbusywait = 500000 };
|
enum { Maxbusywait = 500000 };
|
||||||
|
|
||||||
|
@ -33,22 +21,8 @@ enum {
|
||||||
Capadc18 = 0x100,
|
Capadc18 = 0x100,
|
||||||
Capadc20 = 0x200,
|
Capadc20 = 0x200,
|
||||||
Capenh = 0xfc00,
|
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,
|
Recsel = 0x1A,
|
||||||
Recgain = 0x1C,
|
|
||||||
Micgain = 0x1E,
|
|
||||||
General = 0x20,
|
General = 0x20,
|
||||||
ThreeDctl = 0x22,
|
ThreeDctl = 0x22,
|
||||||
Ac97RESER = 0x24,
|
Ac97RESER = 0x24,
|
||||||
|
@ -118,14 +92,6 @@ enum {
|
||||||
Spdifv = 1<<15,
|
Spdifv = 1<<15,
|
||||||
VID1 = 0x7c,
|
VID1 = 0x7c,
|
||||||
VID2 = 0x7e,
|
VID2 = 0x7e,
|
||||||
Speed = 0x1234567,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
Stereo,
|
|
||||||
Absolute,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -143,195 +109,125 @@ enum {
|
||||||
Vaux,
|
Vaux,
|
||||||
Vrecgain,
|
Vrecgain,
|
||||||
Vmicgain,
|
Vmicgain,
|
||||||
|
Vspeed,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Volume {
|
static Volume voltab[] = {
|
||||||
int reg;
|
[Vmaster] "master", 0x02, 63, Stereo, 0,
|
||||||
int range;
|
[Vaudio] "audio", 0x18, 31, Stereo, 0,
|
||||||
int type;
|
[Vhead] "head", 0x04, 31, Stereo, Capheadphones,
|
||||||
int cap;
|
[Vbass] "bass", 0x08, 15, Left, Captonectl,
|
||||||
char *name;
|
[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 {
|
typedef struct Mixer Mixer;
|
||||||
Volume *this;
|
struct Mixer
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
Mixer *m;
|
ushort (*rr)(Audio *, int);
|
||||||
char *buf;
|
void (*wr)(Audio *, int, ushort);
|
||||||
long l;
|
int vra;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
long
|
static int
|
||||||
ac97mixread(Audio *adev, void *a, long n, vlong off)
|
ac97volget(Audio *adev, int x, int a[2])
|
||||||
{
|
{
|
||||||
Mixer *m;
|
Mixer *m = adev->mixer;
|
||||||
char *nam, *buf;
|
Volume *vol;
|
||||||
long l;
|
|
||||||
ushort v;
|
ushort v;
|
||||||
ulong caps;
|
|
||||||
int i, rang, le, ri;
|
vol = voltab+x;
|
||||||
buf = malloc(READSTR);
|
switch(vol->type){
|
||||||
m = adev->mixer;
|
case Absolute:
|
||||||
qlock(m);
|
a[0] = m->rr(adev, vol->reg);
|
||||||
l = 0;
|
break;
|
||||||
caps = m->rr(adev, Reset);
|
default:
|
||||||
caps |= m->rr(adev, Extid) << 16;
|
v = m->rr(adev, vol->reg);
|
||||||
for(i = 0; vol[i].name != 0; ++i){
|
if(v & 0x8000){
|
||||||
if(vol[i].cap && ((vol[i].cap & caps) == 0))
|
a[0] = 0;
|
||||||
continue;
|
a[1] = 0;
|
||||||
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);
|
|
||||||
} else {
|
} else {
|
||||||
ri = ((rang-(v&rang)) * 100) / rang;
|
a[0] = vol->range - ((v>>8) & 0x7f);
|
||||||
le = ((rang-((v>>8)&rang)) * 100) / rang;
|
a[1] = vol->range - (v & 0x7f);
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
l += snprint(buf+l, READSTR-l, "\n");
|
|
||||||
}
|
}
|
||||||
qunlock(m);
|
return 0;
|
||||||
n = readstr(off, a, n, buf);
|
|
||||||
free(buf);
|
|
||||||
return n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
ac97mixwrite(Audio *adev, void *a, long n, vlong)
|
||||||
{
|
{
|
||||||
Mixer *m;
|
Mixer *m = adev->mixer;
|
||||||
char *tok[4];
|
ulong caps;
|
||||||
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]);
|
|
||||||
|
|
||||||
if(vol[i].type == Absolute){
|
caps = m->rr(adev, Reset);
|
||||||
m->wr(adev, reg, left);
|
caps |= m->rr(adev, Extid) << 16;
|
||||||
} else {
|
return genaudiovolwrite(adev, a, n, 0, voltab, ac97volset, caps);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ac97mixreset(Audio *adev, ac97wrfn wr, ac97rdfn rr)
|
ac97mixreset(Audio *adev, void (*wr)(Audio*,int,ushort), ushort (*rr)(Audio*,int))
|
||||||
{
|
{
|
||||||
Mixer *m;
|
Mixer *m;
|
||||||
int i;
|
|
||||||
ushort t;
|
ushort t;
|
||||||
if(adev->mixer == nil)
|
int i;
|
||||||
adev->mixer = malloc(sizeof(Mixer));
|
|
||||||
m = adev->mixer;
|
m = malloc(sizeof(Mixer));
|
||||||
m->wr = wr;
|
m->wr = wr;
|
||||||
m->rr = rr;
|
m->rr = rr;
|
||||||
adev->volread = ac97mixread;
|
|
||||||
adev->volwrite = ac97mixwrite;
|
|
||||||
m->wr(adev, Reset, 0);
|
m->wr(adev, Reset, 0);
|
||||||
m->wr(adev, Powerdowncsr, 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);
|
print("#A%d: ac97 vra extension not supported\n", adev->ctlrno);
|
||||||
m->vra = 0;
|
m->vra = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adev->mixer = m;
|
||||||
|
adev->volread = ac97mixread;
|
||||||
|
adev->volwrite = ac97mixwrite;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "fns.h"
|
#include "fns.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "../port/error.h"
|
#include "../port/error.h"
|
||||||
|
#include "../port/audioif.h"
|
||||||
|
|
||||||
typedef struct Ring Ring;
|
typedef struct Ring Ring;
|
||||||
typedef struct Blaster Blaster;
|
typedef struct Blaster Blaster;
|
||||||
|
@ -15,11 +16,8 @@ typedef struct Ctlr Ctlr;
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
Fmono = 1,
|
Vmaster,
|
||||||
Fin = 2,
|
Vaudio,
|
||||||
Fout = 4,
|
|
||||||
|
|
||||||
Vaudio = 0,
|
|
||||||
Vsynth,
|
Vsynth,
|
||||||
Vcd,
|
Vcd,
|
||||||
Vline,
|
Vline,
|
||||||
|
@ -27,12 +25,11 @@ enum
|
||||||
Vspeaker,
|
Vspeaker,
|
||||||
Vtreb,
|
Vtreb,
|
||||||
Vbass,
|
Vbass,
|
||||||
|
Vigain,
|
||||||
|
Vogain,
|
||||||
Vspeed,
|
Vspeed,
|
||||||
Nvol,
|
Nvol,
|
||||||
|
|
||||||
Speed = 44100,
|
|
||||||
Ncmd = 50, /* max volume command words */
|
|
||||||
|
|
||||||
Blocksize = 4096,
|
Blocksize = 4096,
|
||||||
Blocks = 65536/Blocksize,
|
Blocks = 65536/Blocksize,
|
||||||
};
|
};
|
||||||
|
@ -70,44 +67,34 @@ struct Ctlr
|
||||||
QLock;
|
QLock;
|
||||||
Rendez vous;
|
Rendez vous;
|
||||||
int active; /* boolean dma running */
|
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 major; /* SB16 major version number (sb 4) */
|
||||||
int minor; /* SB16 minor version number */
|
int minor; /* SB16 minor version number */
|
||||||
ulong totcount; /* how many bytes processed since open */
|
ulong totcount; /* how many bytes processed since open */
|
||||||
vlong tottime; /* time at which totcount bytes were processed */
|
vlong tottime; /* time at which totcount bytes were processed */
|
||||||
Ring ring; /* dma ring buffer */
|
Ring ring; /* dma ring buffer */
|
||||||
Blaster blaster;
|
Blaster blaster;
|
||||||
|
int lvol[Nvol];
|
||||||
|
int rvol[Nvol];
|
||||||
Audio *adev;
|
Audio *adev;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct
|
static Volume voltab[] = {
|
||||||
{
|
[Vmaster] "master", 0x30, 0xff, Stereo, 0,
|
||||||
char* name;
|
[Vaudio] "audio", 0x32, 0xff, Stereo, 0,
|
||||||
int flag;
|
[Vsynth] "synth", 0x34, 0xff, Stereo, 0,
|
||||||
int ilval; /* initial values */
|
[Vcd] "cd", 0x36, 0xff, Stereo, 0,
|
||||||
int irval;
|
[Vline] "line", 0x38, 0xff, Stereo, 0,
|
||||||
} volumes[] = {
|
[Vmic] "mic", 0x3a, 0xff, Mono, 0,
|
||||||
[Vaudio] "audio", Fout, 50, 50,
|
[Vspeaker] "speaker", 0x3b, 0xff, Mono, 0,
|
||||||
[Vsynth] "synth", Fin|Fout, 0, 0,
|
[Vtreb] "treb", 0x44, 0xff, Stereo, 0,
|
||||||
[Vcd] "cd", Fin|Fout, 0, 0,
|
[Vbass] "bass", 0x46, 0xff, Stereo, 0,
|
||||||
[Vline] "line", Fin|Fout, 0, 0,
|
[Vigain] "recgain", 0x3f, 0xff, Stereo, 0,
|
||||||
[Vmic] "mic", Fin|Fout|Fmono, 0, 0,
|
[Vogain] "outgain", 0x41, 0xff, Stereo, 0,
|
||||||
[Vspeaker] "speaker", Fout|Fmono, 0, 0,
|
[Vspeed] "speed", 0, 0, Absolute, 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 Emajor[] = "soundblaster not responding/wrong version";
|
||||||
static char Emode[] = "illegal open mode";
|
|
||||||
static char Evolume[] = "illegal volume specifier";
|
|
||||||
|
|
||||||
|
|
||||||
static long
|
static long
|
||||||
buffered(Ring *r)
|
buffered(Ring *r)
|
||||||
|
@ -242,109 +229,44 @@ mxread(Blaster *blaster, int addr)
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
mxcmds(Blaster *blaster, int s, int v)
|
mxsetvol(Audio *adev, int x, int a[2])
|
||||||
{
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
Blaster *blaster;
|
Blaster *blaster;
|
||||||
int *left, *right;
|
Ctlr *ctlr = adev->ctlr;
|
||||||
int source;
|
Volume *vol;
|
||||||
|
|
||||||
if(0){
|
if(x == Vspeed){
|
||||||
left = ctlr->livol;
|
ctlr->lvol[x] = ctlr->rvol[x] = a[0];
|
||||||
right = ctlr->rivol;
|
return 0;
|
||||||
}else{
|
|
||||||
left = ctlr->lovol;
|
|
||||||
right = ctlr->rovol;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vol = voltab+x;
|
||||||
blaster = &ctlr->blaster;
|
blaster = &ctlr->blaster;
|
||||||
|
|
||||||
ilock(blaster);
|
ilock(blaster);
|
||||||
|
switch(vol->type){
|
||||||
mxcmd(blaster, 0x30, 255); /* left master */
|
case Stereo:
|
||||||
mxcmd(blaster, 0x31, 255); /* right master */
|
ctlr->rvol[x] = a[1];
|
||||||
mxcmd(blaster, 0x3f, 0); /* left igain */
|
mxcmd(blaster, vol->reg+1, a[1]);
|
||||||
mxcmd(blaster, 0x40, 0); /* right igain */
|
/* no break */
|
||||||
mxcmd(blaster, 0x41, 0); /* left ogain */
|
case Mono:
|
||||||
mxcmd(blaster, 0x42, 0); /* right ogain */
|
ctlr->lvol[x] = a[0];
|
||||||
|
mxcmd(blaster, vol->reg, a[0]);
|
||||||
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 */
|
|
||||||
iunlock(blaster);
|
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
|
static void
|
||||||
|
@ -385,22 +307,20 @@ sb16startdma(Ctlr *ctlr)
|
||||||
ring = &ctlr->ring;
|
ring = &ctlr->ring;
|
||||||
ilock(blaster);
|
ilock(blaster);
|
||||||
dmaend(blaster->dma);
|
dmaend(blaster->dma);
|
||||||
if(0) {
|
if(0)
|
||||||
sbcmd(blaster, 0x42); /* input sampling rate */
|
sbcmd(blaster, 0x42); /* input sampling rate */
|
||||||
speed = ctlr->livol[Vspeed];
|
else
|
||||||
} else {
|
sbcmd(blaster, 0x41); /* output sampling rate */
|
||||||
sbcmd(blaster, 0x41); /* output sampling rate */
|
speed = ctlr->lvol[Vspeed];
|
||||||
speed = ctlr->lovol[Vspeed];
|
|
||||||
}
|
|
||||||
sbcmd(blaster, speed>>8);
|
sbcmd(blaster, speed>>8);
|
||||||
sbcmd(blaster, speed);
|
sbcmd(blaster, speed);
|
||||||
|
|
||||||
if(0)
|
if(0)
|
||||||
sbcmd(blaster, 0xbe); /* A/D, autoinit */
|
sbcmd(blaster, 0xbe); /* A/D, autoinit */
|
||||||
else
|
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;
|
count = (Blocksize>>1) - 1;
|
||||||
sbcmd(blaster, count);
|
sbcmd(blaster, count);
|
||||||
|
@ -420,7 +340,7 @@ ess1688reset(Blaster *blaster, int ctlrno)
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
outb(blaster->reset, 3);
|
outb(blaster->reset, 3);
|
||||||
delay(1); /* >3 υs */
|
delay(1); /* >3 υs */
|
||||||
outb(blaster->reset, 0);
|
outb(blaster->reset, 0);
|
||||||
delay(1);
|
delay(1);
|
||||||
|
|
||||||
|
@ -430,7 +350,7 @@ ess1688reset(Blaster *blaster, int ctlrno)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sbcmd(blaster, 0xC6)){ /* extended mode */
|
if(sbcmd(blaster, 0xC6)){ /* extended mode */
|
||||||
print("#A%d: barf 3\n", ctlrno);
|
print("#A%d: barf 3\n", ctlrno);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -457,15 +377,11 @@ ess1688startdma(Ctlr *ctlr)
|
||||||
/*
|
/*
|
||||||
* Set the speed.
|
* Set the speed.
|
||||||
*/
|
*/
|
||||||
if(0)
|
speed = ctlr->lvol[Vspeed];
|
||||||
speed = ctlr->livol[Vspeed];
|
|
||||||
else
|
|
||||||
speed = ctlr->lovol[Vspeed];
|
|
||||||
if(speed < 4000)
|
if(speed < 4000)
|
||||||
speed = 4000;
|
speed = 4000;
|
||||||
else if(speed > 48000)
|
else if(speed > 48000)
|
||||||
speed = 48000;
|
speed = 48000;
|
||||||
|
|
||||||
if(speed > 22000)
|
if(speed > 22000)
|
||||||
x = 0x80|(256-(795500+speed/2)/speed);
|
x = 0x80|(256-(795500+speed/2)/speed);
|
||||||
else
|
else
|
||||||
|
@ -477,15 +393,15 @@ ess1688startdma(Ctlr *ctlr)
|
||||||
ess1688w(blaster, 0xA2, x & 0xFF);
|
ess1688w(blaster, 0xA2, x & 0xFF);
|
||||||
|
|
||||||
if(0)
|
if(0)
|
||||||
ess1688w(blaster, 0xB8, 0x0E); /* A/D, autoinit */
|
ess1688w(blaster, 0xB8, 0x0E); /* A/D, autoinit */
|
||||||
else
|
else
|
||||||
ess1688w(blaster, 0xB8, 0x04); /* D/A, autoinit */
|
ess1688w(blaster, 0xB8, 0x04); /* D/A, autoinit */
|
||||||
x = ess1688r(blaster, 0xA8) & ~0x03;
|
x = ess1688r(blaster, 0xA8) & ~0x03;
|
||||||
ess1688w(blaster, 0xA8, x|0x01); /* 2 channels */
|
ess1688w(blaster, 0xA8, x|0x01); /* 2 channels */
|
||||||
ess1688w(blaster, 0xB9, 2); /* demand mode, 4 bytes per request */
|
ess1688w(blaster, 0xB9, 2); /* demand mode, 4 bytes per request */
|
||||||
|
|
||||||
if(1)
|
if(1)
|
||||||
ess1688w(blaster, 0xB6, 0); /* for output */
|
ess1688w(blaster, 0xB6, 0); /* for output */
|
||||||
|
|
||||||
ess1688w(blaster, 0xB7, 0x71);
|
ess1688w(blaster, 0xB7, 0x71);
|
||||||
ess1688w(blaster, 0xB7, 0xBC);
|
ess1688w(blaster, 0xB7, 0xBC);
|
||||||
|
@ -496,7 +412,7 @@ ess1688startdma(Ctlr *ctlr)
|
||||||
ess1688w(blaster, 0xB2, x|0x50);
|
ess1688w(blaster, 0xB2, x|0x50);
|
||||||
|
|
||||||
if(1)
|
if(1)
|
||||||
sbcmd(blaster, 0xD1); /* speaker on */
|
sbcmd(blaster, 0xD1); /* speaker on */
|
||||||
|
|
||||||
count = -Blocksize;
|
count = -Blocksize;
|
||||||
ess1688w(blaster, 0xA4, count & 0xFF);
|
ess1688w(blaster, 0xA4, count & 0xFF);
|
||||||
|
@ -571,19 +487,6 @@ setempty(Ctlr *ctlr)
|
||||||
iunlock(&ctlr->blaster);
|
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
|
static long
|
||||||
audiobuffered(Audio *adev)
|
audiobuffered(Audio *adev)
|
||||||
{
|
{
|
||||||
|
@ -591,17 +494,15 @@ audiobuffered(Audio *adev)
|
||||||
}
|
}
|
||||||
|
|
||||||
static long
|
static long
|
||||||
audiostatus(Audio *adev, void *a, long n, vlong off)
|
audiostatus(Audio *adev, void *a, long n, vlong)
|
||||||
{
|
{
|
||||||
char buf[300];
|
Ctlr *ctlr = adev->ctlr;
|
||||||
Ctlr *ctlr;
|
|
||||||
|
|
||||||
ctlr = adev->ctlr;
|
return snprint((char*)a, n,
|
||||||
snprint(buf, sizeof(buf),
|
"bufsize %6lud buffered %6ld "
|
||||||
"buffered %.4lx/%.4lx offset %10lud time %19lld\n",
|
"offset %10lud time %19lld\n",
|
||||||
buffered(&ctlr->ring), available(&ctlr->ring),
|
ctlr->ring.nbuf, buffered(&ctlr->ring),
|
||||||
ctlr->totcount, ctlr->tottime);
|
ctlr->totcount, ctlr->tottime);
|
||||||
return readstr(off, a, n, buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -626,7 +527,6 @@ audiowrite(Audio *adev, void *vp, long n, vlong)
|
||||||
uchar *p, *e;
|
uchar *p, *e;
|
||||||
Ctlr *ctlr;
|
Ctlr *ctlr;
|
||||||
Ring *ring;
|
Ring *ring;
|
||||||
long m;
|
|
||||||
|
|
||||||
p = vp;
|
p = vp;
|
||||||
e = p + n;
|
e = p + n;
|
||||||
|
@ -638,17 +538,15 @@ audiowrite(Audio *adev, void *vp, long n, vlong)
|
||||||
}
|
}
|
||||||
ring = &ctlr->ring;
|
ring = &ctlr->ring;
|
||||||
while(p < e) {
|
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)
|
if(!ctlr->active && ring->ri == 0)
|
||||||
ctlr->blaster.startdma(ctlr);
|
ctlr->blaster.startdma(ctlr);
|
||||||
if(!ctlr->active){
|
if(!ctlr->active)
|
||||||
setempty(ctlr);
|
setempty(ctlr);
|
||||||
continue;
|
else
|
||||||
}
|
sleep(&ctlr->vous, anybuf, ctlr);
|
||||||
sleep(&ctlr->vous, anybuf, ctlr);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
p += m;
|
p += n;
|
||||||
}
|
}
|
||||||
poperror();
|
poperror();
|
||||||
qunlock(ctlr);
|
qunlock(ctlr);
|
||||||
|
@ -673,6 +571,49 @@ audioclose(Audio *adev)
|
||||||
qunlock(ctlr);
|
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
|
static int
|
||||||
ess1688(ISAConf* sbconf, Blaster *blaster, int ctlrno)
|
ess1688(ISAConf* sbconf, Blaster *blaster, int ctlrno)
|
||||||
{
|
{
|
||||||
|
@ -685,7 +626,8 @@ ess1688(ISAConf* sbconf, Blaster *blaster, int ctlrno)
|
||||||
major = sbread(blaster);
|
major = sbread(blaster);
|
||||||
minor = sbread(blaster);
|
minor = sbread(blaster);
|
||||||
if(major != 0x68 || minor != 0x8B){
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -763,12 +705,14 @@ audioprobe(Audio *adev)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ioalloc(sbconf.port, 0x10, 0, "audio") < 0){
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
if(ioalloc(sbconf.port+0x100, 1, 0, "audio.mpu401") < 0){
|
if(ioalloc(sbconf.port+0x100, 1, 0, "audio.mpu401") < 0){
|
||||||
iofree(sbconf.port);
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -792,8 +736,6 @@ audioprobe(Audio *adev)
|
||||||
blaster->startdma = sb16startdma;
|
blaster->startdma = sb16startdma;
|
||||||
blaster->intr = sb16intr;
|
blaster->intr = sb16intr;
|
||||||
|
|
||||||
resetlevel(ctlr);
|
|
||||||
|
|
||||||
outb(blaster->reset, 1);
|
outb(blaster->reset, 1);
|
||||||
delay(1); /* >3 υs */
|
delay(1); /* >3 υs */
|
||||||
outb(blaster->reset, 0);
|
outb(blaster->reset, 0);
|
||||||
|
@ -802,6 +744,7 @@ audioprobe(Audio *adev)
|
||||||
i = sbread(blaster);
|
i = sbread(blaster);
|
||||||
if(i != 0xaa) {
|
if(i != 0xaa) {
|
||||||
print("#A%d: no response #%.2x\n", adev->ctlrno, i);
|
print("#A%d: no response #%.2x\n", adev->ctlrno, i);
|
||||||
|
Errout:
|
||||||
iofree(sbconf.port);
|
iofree(sbconf.port);
|
||||||
iofree(sbconf.port+0x100);
|
iofree(sbconf.port+0x100);
|
||||||
free(ctlr);
|
free(ctlr);
|
||||||
|
@ -813,12 +756,11 @@ audioprobe(Audio *adev)
|
||||||
ctlr->minor = sbread(blaster);
|
ctlr->minor = sbread(blaster);
|
||||||
|
|
||||||
if(ctlr->major != 4) {
|
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",
|
print("#A%d: model %#.2x %#.2x; not SB 16 compatible\n",
|
||||||
adev->ctlrno, ctlr->major, ctlr->minor);
|
adev->ctlrno, ctlr->major, ctlr->minor);
|
||||||
iofree(sbconf.port);
|
goto Errout;
|
||||||
iofree(sbconf.port+0x100);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
ctlr->major = 4;
|
ctlr->major = 4;
|
||||||
}
|
}
|
||||||
|
@ -827,7 +769,14 @@ audioprobe(Audio *adev)
|
||||||
* initialize the mixer
|
* initialize the mixer
|
||||||
*/
|
*/
|
||||||
mxcmd(blaster, 0x00, 0); /* Reset 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 */
|
/* set irq */
|
||||||
for(i=0; i<nelem(irq); i++){
|
for(i=0; i<nelem(irq); i++){
|
||||||
|
@ -857,33 +806,33 @@ audioprobe(Audio *adev)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(blaster->dma<5){
|
if(blaster->dma>=5)
|
||||||
blaster->dma = 7;
|
break;
|
||||||
continue;
|
|
||||||
}
|
blaster->dma = 7;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
print("#A%d: %s port 0x%04lux irq %d dma %d\n", adev->ctlrno, sbconf.type,
|
print("#A%d: %s port 0x%04lux irq %d dma %d\n", adev->ctlrno, sbconf.type,
|
||||||
sbconf.port, sbconf.irq, blaster->dma);
|
sbconf.port, sbconf.irq, blaster->dma);
|
||||||
|
|
||||||
ctlr->ring.nbuf = Blocks*Blocksize;
|
ctlr->ring.nbuf = Blocks*Blocksize;
|
||||||
if(dmainit(blaster->dma, ctlr->ring.nbuf)){
|
if(dmainit(blaster->dma, ctlr->ring.nbuf))
|
||||||
free(ctlr);
|
goto Errout;
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
ctlr->ring.buf = dmabva(blaster->dma);
|
ctlr->ring.buf = dmabva(blaster->dma);
|
||||||
|
print("#A%d: %s dma buffer %p-%p\n", adev->ctlrno, sbconf.type,
|
||||||
intrenable(sbconf.irq, audiointr, adev, BUSUNKNOWN, sbconf.type);
|
ctlr->ring.buf, ctlr->ring.buf+ctlr->ring.nbuf);
|
||||||
|
|
||||||
setempty(ctlr);
|
setempty(ctlr);
|
||||||
mxvolume(ctlr);
|
|
||||||
|
|
||||||
adev->write = audiowrite;
|
adev->write = audiowrite;
|
||||||
adev->close = audioclose;
|
adev->close = audioclose;
|
||||||
|
adev->volread = audiovolread;
|
||||||
|
adev->volwrite = audiovolwrite;
|
||||||
adev->status = audiostatus;
|
adev->status = audiostatus;
|
||||||
adev->buffered = audiobuffered;
|
adev->buffered = audiobuffered;
|
||||||
|
|
||||||
|
intrenable(sbconf.irq, audiointr, adev, BUSUNKNOWN, sbconf.type);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
50
sys/src/9/port/audioif.h
Normal file
50
sys/src/9/port/audioif.h
Normal 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);
|
|
@ -5,27 +5,36 @@
|
||||||
#include "fns.h"
|
#include "fns.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "../port/error.h"
|
#include "../port/error.h"
|
||||||
|
#include "../port/audioif.h"
|
||||||
|
|
||||||
typedef struct Audioprobe Audioprobe;
|
typedef struct Audioprobe Audioprobe;
|
||||||
struct Audioprobe {
|
typedef struct Audiochan Audiochan;
|
||||||
|
|
||||||
|
struct Audioprobe
|
||||||
|
{
|
||||||
char *name;
|
char *name;
|
||||||
int (*probe)(Audio*);
|
int (*probe)(Audio*);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Audiochan
|
||||||
|
{
|
||||||
|
QLock;
|
||||||
|
|
||||||
|
Chan *owner;
|
||||||
|
Audio *adev;
|
||||||
|
|
||||||
|
char *data;
|
||||||
|
char buf[1024+1];
|
||||||
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
Qdir = 0,
|
Qdir = 0,
|
||||||
Qaudio,
|
Qaudio,
|
||||||
Qaudioctl,
|
Qaudioctl,
|
||||||
Qaudiostatus,
|
Qaudiostatus,
|
||||||
Qvolume,
|
Qvolume,
|
||||||
|
|
||||||
Maxaudioprobes = 8,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int naudioprobes;
|
|
||||||
static Audioprobe audioprobes[Maxaudioprobes];
|
|
||||||
static Audio *audiodevs;
|
|
||||||
|
|
||||||
static Dirtab audiodir[] = {
|
static Dirtab audiodir[] = {
|
||||||
".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
|
".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
|
||||||
"audio", {Qaudio}, 0, 0666,
|
"audio", {Qaudio}, 0, 0666,
|
||||||
|
@ -34,10 +43,22 @@ static Dirtab audiodir[] = {
|
||||||
"volume", {Qvolume}, 0, 0666,
|
"volume", {Qvolume}, 0, 0666,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static int naudioprobes;
|
||||||
|
static Audioprobe audioprobes[16];
|
||||||
|
static Audio *audiodevs;
|
||||||
|
|
||||||
|
static char Evolume[] = "illegal volume specifier";
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
addaudiocard(char *name, int (*probefn)(Audio *))
|
addaudiocard(char *name, int (*probefn)(Audio *))
|
||||||
{
|
{
|
||||||
Audioprobe *probe;
|
Audioprobe *probe;
|
||||||
|
|
||||||
|
if(naudioprobes >= nelem(audioprobes))
|
||||||
|
return;
|
||||||
|
|
||||||
probe = &audioprobes[naudioprobes++];
|
probe = &audioprobes[naudioprobes++];
|
||||||
probe->name = name;
|
probe->name = name;
|
||||||
probe->probe = probefn;
|
probe->probe = probefn;
|
||||||
|
@ -73,48 +94,107 @@ audioreset(void)
|
||||||
*pp = nil;
|
*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*
|
static Chan*
|
||||||
audioattach(char *spec)
|
audioattach(char *spec)
|
||||||
{
|
{
|
||||||
|
static int first = 1;
|
||||||
|
Audiochan *ac;
|
||||||
|
Audio *adev;
|
||||||
Chan *c;
|
Chan *c;
|
||||||
Audio *p;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if(spec != nil && *spec != '\0')
|
if(spec != nil && *spec != '\0')
|
||||||
i = strtol(spec, 0, 10);
|
i = strtol(spec, 0, 10);
|
||||||
else
|
else
|
||||||
i = 0;
|
i = 0;
|
||||||
for(p = audiodevs; p; p = p->next)
|
for(adev = audiodevs; adev; adev = adev->next)
|
||||||
if(i-- == 0)
|
if(i-- == 0)
|
||||||
break;
|
break;
|
||||||
if(p == nil)
|
if(adev == nil)
|
||||||
error(Enodev);
|
error(Enodev);
|
||||||
|
|
||||||
c = devattach('A', spec);
|
c = devattach('A', spec);
|
||||||
c->qid.path = Qdir;
|
c->qid.path = Qdir;
|
||||||
c->aux = p;
|
|
||||||
if(p->attach)
|
if((ac = audioclone(c, adev)) == nil)
|
||||||
p->attach(p);
|
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;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Chan*
|
||||||
|
audioopen(Chan *c, int omode)
|
||||||
|
{
|
||||||
|
return devopen(c, omode, audiodir, nelem(audiodir), devgen);
|
||||||
|
}
|
||||||
|
|
||||||
static long
|
static long
|
||||||
audioread(Chan *c, void *a, long n, vlong off)
|
audioread(Chan *c, void *a, long n, vlong off)
|
||||||
{
|
{
|
||||||
|
Audiochan *ac;
|
||||||
Audio *adev;
|
Audio *adev;
|
||||||
long (*fn)(Audio *, void *, long, vlong);
|
long (*fn)(Audio *, void *, long, vlong);
|
||||||
adev = c->aux;
|
|
||||||
|
ac = c->aux;
|
||||||
|
adev = ac->adev;
|
||||||
|
|
||||||
|
fn = nil;
|
||||||
switch((ulong)c->qid.path){
|
switch((ulong)c->qid.path){
|
||||||
default:
|
|
||||||
error("audio bugger (rd)");
|
|
||||||
case Qaudioctl:
|
|
||||||
fn = adev->ctl;
|
|
||||||
break;
|
|
||||||
case Qdir:
|
case Qdir:
|
||||||
|
/* BUG: race */
|
||||||
if(adev->buffered)
|
if(adev->buffered)
|
||||||
audiodir[Qaudio].length = adev->buffered(adev);
|
audiodir[Qaudio].length = adev->buffered(adev);
|
||||||
return devdirread(c, a, n, audiodir, nelem(audiodir), devgen);
|
return devdirread(c, a, n, audiodir, nelem(audiodir), devgen);
|
||||||
case Qaudio:
|
case Qaudio:
|
||||||
fn = adev->read;
|
fn = adev->read;
|
||||||
break;
|
break;
|
||||||
|
case Qaudioctl:
|
||||||
|
fn = adev->ctl;
|
||||||
|
break;
|
||||||
case Qaudiostatus:
|
case Qaudiostatus:
|
||||||
fn = adev->status;
|
fn = adev->status;
|
||||||
break;
|
break;
|
||||||
|
@ -123,19 +203,49 @@ audioread(Chan *c, void *a, long n, vlong off)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(fn == nil)
|
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);
|
return fn(adev, a, n, off);
|
||||||
}
|
}
|
||||||
|
|
||||||
static long
|
static long
|
||||||
audiowrite(Chan *c, void *a, long n, vlong off)
|
audiowrite(Chan *c, void *a, long n, vlong off)
|
||||||
{
|
{
|
||||||
|
Audiochan *ac;
|
||||||
Audio *adev;
|
Audio *adev;
|
||||||
long (*fn)(Audio *, void *, long, vlong);
|
long (*fn)(Audio *, void *, long, vlong);
|
||||||
adev = c->aux;
|
|
||||||
|
ac = c->aux;
|
||||||
|
adev = ac->adev;
|
||||||
|
|
||||||
|
fn = nil;
|
||||||
switch((ulong)c->qid.path){
|
switch((ulong)c->qid.path){
|
||||||
default:
|
|
||||||
error("audio bugger (wr)");
|
|
||||||
case Qaudio:
|
case Qaudio:
|
||||||
fn = adev->write;
|
fn = adev->write;
|
||||||
break;
|
break;
|
||||||
|
@ -147,46 +257,195 @@ audiowrite(Chan *c, void *a, long n, vlong off)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(fn == nil)
|
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);
|
return fn(adev, a, n, off);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
audioclose(Chan *c)
|
audioclose(Chan *c)
|
||||||
{
|
{
|
||||||
|
Audiochan *ac;
|
||||||
Audio *adev;
|
Audio *adev;
|
||||||
adev = c->aux;
|
|
||||||
switch((ulong)c->qid.path){
|
ac = c->aux;
|
||||||
default:
|
adev = ac->adev;
|
||||||
return;
|
if(c->qid.path == Qaudio && (c->flag & COPEN))
|
||||||
case Qaudio:
|
if(adev->close)
|
||||||
if(adev->close == nil)
|
adev->close(adev);
|
||||||
return;
|
|
||||||
adev->close(adev);
|
if(ac->owner == c){
|
||||||
return;
|
ac->owner = nil;
|
||||||
|
c->aux = nil;
|
||||||
|
free(ac);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Walkqid*
|
static Walkqid*
|
||||||
audiowalk(Chan *c, Chan *nc, char **name, int nname)
|
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
|
static int
|
||||||
audiostat(Chan *c, uchar *dp, int n)
|
audiostat(Chan *c, uchar *dp, int n)
|
||||||
{
|
{
|
||||||
|
Audiochan *ac;
|
||||||
Audio *adev;
|
Audio *adev;
|
||||||
adev = c->aux;
|
|
||||||
|
ac = c->aux;
|
||||||
|
adev = ac->adev;
|
||||||
|
|
||||||
|
/* BUG: race */
|
||||||
if(adev->buffered && (ulong)c->qid.path == Qaudio)
|
if(adev->buffered && (ulong)c->qid.path == Qaudio)
|
||||||
audiodir[Qaudio].length = adev->buffered(adev);
|
audiodir[Qaudio].length = adev->buffered(adev);
|
||||||
|
|
||||||
return devstat(c, dp, n, audiodir, nelem(audiodir), devgen);
|
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 = {
|
Dev audiodevtab = {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
typedef struct Alarms Alarms;
|
typedef struct Alarms Alarms;
|
||||||
typedef struct Audio Audio;
|
|
||||||
typedef struct Block Block;
|
typedef struct Block Block;
|
||||||
typedef struct Chan Chan;
|
typedef struct Chan Chan;
|
||||||
typedef struct Cmdbuf Cmdbuf;
|
typedef struct Cmdbuf Cmdbuf;
|
||||||
|
@ -919,23 +918,6 @@ struct Uart
|
||||||
|
|
||||||
extern Uart* consuart;
|
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
|
* performance timers, all units in perfticks
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -4,7 +4,6 @@ Timer* addclock0link(void (*)(void), int);
|
||||||
int addphysseg(Physseg*);
|
int addphysseg(Physseg*);
|
||||||
void addbootfile(char*, uchar*, ulong);
|
void addbootfile(char*, uchar*, ulong);
|
||||||
void addwatchdog(Watchdog*);
|
void addwatchdog(Watchdog*);
|
||||||
void addaudiocard(char *, int (*)(Audio *));
|
|
||||||
Block* adjustblock(Block*, int);
|
Block* adjustblock(Block*, int);
|
||||||
void alarmkproc(void*);
|
void alarmkproc(void*);
|
||||||
Block* allocb(int);
|
Block* allocb(int);
|
||||||
|
|
Loading…
Reference in a new issue