#include "fns.h"
#include "io.h"
#include "../port/error.h"
+#include "../port/audioif.h"
+typedef struct Ring Ring;
typedef struct Hwdesc Hwdesc;
typedef struct Ctlr Ctlr;
-static uint sis7012 = 0;
enum {
Ioc = 1<<31,
enum {
Ndesc = 32,
- Nts = 33,
Bufsize = 32768, /* bytes, must be divisible by ndesc */
+ Blocksize = Bufsize/Ndesc,
Maxbusywait = 500000, /* microseconds, roughly */
BytesPerSample = 4,
};
+struct Ring
+{
+ Rendez r;
+
+ uchar *buf;
+ ulong nbuf;
+
+ ulong ri;
+ ulong wi;
+};
+
struct Ctlr {
/* keep these first, they want to be 8-aligned */
Hwdesc indesc[Ndesc];
Hwdesc micdesc[Ndesc];
Lock;
- Rendez outr;
ulong port;
ulong mixport;
- char *out;
- char *in;
- char *mic;
-
- char *outp;
- char *inp;
- char *micp;
-
- /* shared variables, ilock to access */
- int outavail;
- int inavail;
- int micavail;
+ Ring inring, micring, outring;
- /* interrupt handler alone */
- int outciv;
- int inciv;
- int micciv;
-
- int tsouti;
- uvlong tsoutp;
- ulong tsout[Nts];
- int tsoutb[Nts];
-
- ulong civstat[Ndesc];
- ulong lvistat[Ndesc];
-
- int targetrate;
- int hardrate;
-
- int attachok;
+ int sis7012;
/* for probe */
+ Audio *adev;
Pcidev *pcidev;
Ctlr *next;
};
Casp = 1<<0, /* set to 1 on read if zero, cleared by hardware */
};
+static long
+buffered(Ring *r)
+{
+ ulong ri, wi;
+
+ ri = r->ri;
+ wi = r->wi;
+ if(wi >= ri)
+ return wi - ri;
+ else
+ return r->nbuf - (ri - wi);
+}
+
+static long
+available(Ring *r)
+{
+ long m;
+
+ m = (r->nbuf - BytesPerSample) - buffered(r);
+ if(m < 0)
+ m = 0;
+ return m;
+}
+
+static long
+readring(Ring *r, uchar *p, long n)
+{
+ long n0, m;
+
+ n0 = n;
+ while(n > 0){
+ if((m = buffered(r)) <= 0)
+ break;
+ if(m > n)
+ m = n;
+ if(p){
+ if(r->ri + m > r->nbuf)
+ m = r->nbuf - r->ri;
+ memmove(p, r->buf + r->ri, m);
+ p += m;
+ }
+ r->ri = (r->ri + m) % r->nbuf;
+ n -= m;
+ }
+ return n0 - n;
+}
+
+static long
+writering(Ring *r, uchar *p, long n)
+{
+ long n0, m;
+
+ n0 = n;
+ while(n > 0){
+ if((m = available(r)) <= 0)
+ break;
+ if(m > n)
+ m = n;
+ if(p){
+ if(r->wi + m > r->nbuf)
+ m = r->nbuf - r->wi;
+ memmove(r->buf + r->wi, p, m);
+ p += m;
+ }
+ r->wi = (r->wi + m) % r->nbuf;
+ n -= m;
+ }
+ return n0 - n;
+}
+
#define csr8r(c, r) (inb((c)->port+(r)))
#define csr16r(c, r) (ins((c)->port+(r)))
#define csr32r(c, r) (inl((c)->port+(r)))
#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
return ins(ctlr->mixport+port);
}
-static int
-outavail(void *arg)
-{
- Ctlr *ctlr;
- ctlr = arg;
- return ctlr->outavail > 0;
-}
-
static void
ac97interrupt(Ureg *, void *arg)
{
Audio *adev;
Ctlr *ctlr;
- int civ, n, i;
ulong stat;
- uvlong now;
adev = arg;
ctlr = adev->ctlr;
stat = csr32r(ctlr, Sta);
-
stat &= S2ri | Sri | Pri | Mint | Point | Piint | Moint | Miint | Gsci;
-
- ilock(ctlr);
- if(stat & Point){
- if(sis7012)
- csr16w(ctlr, Out + Picb, csr16r(ctlr, Out + Picb) & ~Dch);
- else
- csr16w(ctlr, Out + Sr, csr16r(ctlr, Out + Sr) & ~Dch);
-
- civ = csr8r(ctlr, Out + Civ);
- n = 0;
- while(ctlr->outciv != civ){
- ctlr->civstat[ctlr->outciv++]++;
- if(ctlr->outciv == Ndesc)
- ctlr->outciv = 0;
- n += Bufsize/Ndesc;
+ if(stat & (Point|Piint|Mint)){
+ ilock(ctlr);
+ if(stat & Point){
+ ctlr->outring.ri = csr8r(ctlr, Out + Civ) * Blocksize;
+ wakeup(&ctlr->outring.r);
+
+ if(ctlr->sis7012)
+ csr16w(ctlr, Out + Picb, csr16r(ctlr, Out + Picb) & ~Dch);
+ else
+ csr16w(ctlr, Out + Sr, csr16r(ctlr, Out + Sr) & ~Dch);
+ stat &= ~Point;
}
-
- now = fastticks(0);
- i = ctlr->tsouti;
- ctlr->tsoutb[i] = n;
- ctlr->tsout[i] = now - ctlr->tsoutp;
- ctlr->tsouti = (i + 1) % Nts;
- ctlr->tsoutp = now;
- ctlr->outavail += n;
-
- if(ctlr->outavail > Bufsize/2)
- wakeup(&ctlr->outr);
- stat &= ~Point;
+ if(stat & Piint){
+ ctlr->inring.wi = csr8r(ctlr, In + Civ) * Blocksize;
+ wakeup(&ctlr->inring.r);
+
+ if(ctlr->sis7012)
+ csr16w(ctlr, In + Picb, csr16r(ctlr, In + Picb) & ~Dch);
+ else
+ csr16w(ctlr, In + Sr, csr16r(ctlr, In + Sr) & ~Dch);
+ stat &= ~Piint;
+ }
+ if(stat & Mint){
+ ctlr->micring.wi = csr8r(ctlr, Mic + Civ) * Blocksize;
+ wakeup(&ctlr->micring.r);
+
+ if(ctlr->sis7012)
+ csr16w(ctlr, Mic + Picb, csr16r(ctlr, Mic + Picb) & ~Dch);
+ else
+ csr16w(ctlr, Mic + Sr, csr16r(ctlr, Mic + Sr) & ~Dch);
+ stat &= ~Mint;
+ }
+ 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
-off2lvi(char *base, char *p)
+static long
+ac97buffered(Audio *adev)
{
- int lvi;
- lvi = p - base;
- return lvi / (Bufsize/Ndesc);
+ Ctlr *ctlr = adev->ctlr;
+ return buffered(&ctlr->outring);
}
static long
-ac97medianoutrate(Audio *adev)
+ac97status(Audio *adev, void *a, long n, vlong)
{
- ulong ts[Nts], t;
- uvlong hz;
- int i, j;
- Ctlr *ctlr;
- ctlr = adev->ctlr;
- fastticks(&hz);
- for(i = 0; i < Nts; i++)
- if(ctlr->tsout[i] > 0)
- ts[i] = (ctlr->tsoutb[i] * hz) / ctlr->tsout[i];
- else
- ts[i] = 0;
- for(i = 1; i < Nts; i++){
- t = ts[i];
- j = i;
- while(j > 0 && ts[j-1] > t){
- ts[j] = ts[j-1];
- j--;
- }
- ts[j] = t;
- }
- return ts[Nts/2] / BytesPerSample;
+ Ctlr *ctlr = adev->ctlr;
+ return snprint((char*)a, n, "bufsize %6d buffered %6ld\n",
+ Blocksize, buffered(&ctlr->outring));
}
-static void
-ac97volume(Audio *adev, char *msg)
+static int
+inavail(void *arg)
{
- adev->volwrite(adev, msg, strlen(msg), 0);
+ Ring *r = arg;
+ return buffered(r) > 0;
}
-static void
-ac97attach(Audio *adev)
+static int
+outavail(void *arg)
{
- 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;
- }
+ Ring *r = arg;
+ return available(r) > 0;
}
-static long
-ac97status(Audio *adev, void *a, long n, vlong off)
+static int
+outrate(void *arg)
{
- Ctlr *ctlr;
- char *buf;
- long i, l;
- 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");
- for(i = 0; i < Ndesc; i++)
- l += snprint(buf + l, READSTR - l, " %lud", ctlr->civstat[i]);
- l += snprint(buf + l, READSTR - l, "\n");
-
- l += snprint(buf + l, READSTR - l, "lvi stats");
- for(i = 0; i < Ndesc; i++)
- l += snprint(buf + l, READSTR - l, " %lud", ctlr->lvistat[i]);
- snprint(buf + l, READSTR - l, "\n");
-
- n = readstr(off, a, n, buf);
- free(buf);
- return n;
+ Ctlr *ctlr = arg;
+ int delay = ctlr->adev->delay*BytesPerSample;
+ return (delay <= 0) || (buffered(&ctlr->outring) <= delay);
}
static long
-ac97buffered(Audio *adev)
+ac97read(Audio *adev, void *vp, long n, vlong)
{
+ uchar *p, *e;
Ctlr *ctlr;
+ Ring *ring;
+ ulong oi, ni;
+
+ p = vp;
+ e = p + n;
ctlr = adev->ctlr;
- return Bufsize - Bufsize/Ndesc - ctlr->outavail;
+ ring = &ctlr->inring;
+ while(p < e) {
+ oi = ring->ri / Blocksize;
+ if((n = readring(ring, p, e - p)) <= 0){
+ csr8w(ctlr, In + Lvi, (oi - 1) % Ndesc);
+ csr8w(ctlr, In + Cr, Ioce | Rpbm);
+ sleep(&ring->r, inavail, ring);
+ continue;
+ }
+ ni = ring->ri / Blocksize;
+ while(oi != ni){
+ csr8w(ctlr, In + Lvi, (oi - 1) % Ndesc);
+ csr8w(ctlr, In + Cr, Ioce | Rpbm);
+ oi = (oi + 1) % Ndesc;
+ }
+ p += n;
+ }
+ return p - (uchar*)vp;
}
static long
-ac97ctl(Audio *adev, void *a, long n, vlong)
+ac97write(Audio *adev, void *vp, long n, vlong)
{
+ uchar *p, *e;
Ctlr *ctlr;
- char *tok[2], *p;
- int ntok;
- long t;
+ Ring *ring;
+ ulong oi, ni;
+ p = vp;
+ e = p + n;
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;
+ ring = &ctlr->outring;
+ while(p < e) {
+ oi = ring->wi / Blocksize;
+ if((n = writering(ring, p, e - p)) <= 0){
+ sleep(&ring->r, outavail, ring);
+ continue;
+ }
+ ni = ring->wi / Blocksize;
+ while(oi != ni){
+ csr8w(ctlr, Out+Lvi, oi);
+ csr8w(ctlr, Out+Cr, Ioce | Rpbm);
+ oi = (oi + 1) % Ndesc;
+ }
+ p += n;
}
- error("invalid ctl");
- return n; /* shut up, you compiler you */
+ sleep(&ring->r, outrate, ctlr);
+ return p - (uchar*)vp;
}
static void
-ac97kick(Ctlr *ctlr, long reg)
-{
- csr8w(ctlr, reg+Cr, Ioce | Rpbm);
-}
-
-static long
-ac97write(Audio *adev, void *a, long nwr, vlong)
+ac97close(Audio *adev, int mode)
{
Ctlr *ctlr;
- char *p, *sp, *ep;
- int len, lvi, olvi;
- int t;
- long n;
+ Ring *ring;
+
+ if(mode == OREAD)
+ return;
ctlr = adev->ctlr;
- ilock(ctlr);
- p = ctlr->outp;
- sp = a;
- ep = ctlr->out + Bufsize;
- olvi = off2lvi(ctlr->out, p);
- n = nwr;
- while(n > 0){
- len = ep - p;
- if(ctlr->outavail < len)
- len = ctlr->outavail;
- if(n < len)
- len = n;
- ctlr->outavail -= len;
- iunlock(ctlr);
- memmove(p, sp, len);
- ilock(ctlr);
- p += len;
- sp += len;
- n -= len;
- if(p == ep)
- p = ctlr->out;
- lvi = off2lvi(ctlr->out, p);
- if(olvi != lvi){
- t = olvi;
- while(t != lvi){
- t = (t + 1) % Ndesc;
- ctlr->lvistat[t]++;
- csr8w(ctlr, Out+Lvi, t);
- ac97kick(ctlr, Out);
- }
- olvi = lvi;
- }
- if(ctlr->outavail == 0){
- ctlr->outp = p;
- iunlock(ctlr);
- sleep(&ctlr->outr, outavail, ctlr);
- ilock(ctlr);
- }
- }
- ctlr->outp = p;
- iunlock(ctlr);
- return nwr;
+ ring = &ctlr->outring;
+ while(ring->wi % Blocksize)
+ if(writering(ring, (uchar*)"", 1) <= 0)
+ break;
}
static Pcidev*
{
/* not all of the matched devices have been tested */
while(p = pcimatch(p, 0, 0))
- switch(p->vid){
- default:
- break;
- case 0x1039:
- switch(p->did){
- default:
- break;
- case 0x7012:
- sis7012 = 1;
- return p;
- }
- case 0x1022:
- switch(p->did){
- default:
- break;
- case 0x746d:
- case 0x7445:
- return p;
- }
- case 0x10de:
- switch(p->did){
- default:
- break;
- case 0x01b1:
- case 0x006a:
- case 0x00da:
- case 0x00ea:
- return p;
- }
- case 0x8086:
- switch(p->did){
- default:
- break;
- case 0x2415:
- case 0x2425:
- case 0x2445:
- case 0x2485:
- case 0x24c5:
- case 0x24d5:
- case 0x25a6:
- case 0x266e:
- case 0x7195:
- return p;
- }
+ switch((p->vid<<16)|p->did){
+ case (0x1039<<16)|0x7012:
+ case (0x1022<<16)|0x746d:
+ case (0x1022<<16)|0x7445:
+ case (0x10de<<16)|0x01b1:
+ case (0x10de<<16)|0x006a:
+ case (0x10de<<16)|0x00da:
+ case (0x10de<<16)|0x00ea:
+ case (0x8086<<16)|0x2415:
+ case (0x8086<<16)|0x2425:
+ case (0x8086<<16)|0x2445:
+ case (0x8086<<16)|0x2485:
+ case (0x8086<<16)|0x24c5:
+ case (0x8086<<16)|0x24d5:
+ case (0x8086<<16)|0x25a6:
+ case (0x8086<<16)|0x266e:
+ case (0x8086<<16)|0x7195:
+ return p;
}
return nil;
}
Found:
adev->ctlr = ctlr;
- ctlr->targetrate = 44100;
- ctlr->hardrate = 44100;
-
- if(p->mem[0].size == 64){
- ctlr->port = p->mem[0].bar & ~3;
- ctlr->mixport = p->mem[1].bar & ~3;
- } else if(p->mem[1].size == 64){
- ctlr->port = p->mem[1].bar & ~3;
- ctlr->mixport = p->mem[0].bar & ~3;
- } else if(p->mem[0].size == 256){ /* sis7012 */
- ctlr->port = p->mem[1].bar & ~3;
- ctlr->mixport = p->mem[0].bar & ~3;
- } else if(p->mem[1].size == 256){
- ctlr->port = p->mem[0].bar & ~3;
- ctlr->mixport = p->mem[1].bar & ~3;
+ ctlr->adev = adev;
+
+ i = 1;
+ if(p->mem[0].size == 64)
+ i = 0;
+ else if(p->mem[1].size == 64)
+ i = 1;
+ else if(p->mem[0].size == 256) /* sis7012 */
+ i = 1;
+ else if(p->mem[1].size == 256)
+ i = 0;
+ ctlr->port = p->mem[i].bar & ~3;
+ if(ioalloc(ctlr->port, p->mem[i].size, 0, "ac97") < 0){
+ print("ac97: ioalloc failed for port 0x%04lux\n", ctlr->port);
+ return -1;
+ }
+ i = (i+1) & 1;
+ ctlr->mixport = p->mem[i].bar & ~3;
+ if(ioalloc(ctlr->mixport, p->mem[i].size, 0, "ac97mix") < 0){
+ print("ac97: ioalloc failed for mixport 0x%04lux\n", ctlr->mixport);
+ iofree(ctlr->port);
+ return -1;
}
irq = p->intl;
tbdf = p->tbdf;
+ if(p->vid == 0x1039 && p->did == 0x7012)
+ ctlr->sis7012 = 1;
print("#A%d: ac97 port 0x%04lux mixport 0x%04lux irq %d\n",
adev->ctlrno, ctlr->port, ctlr->mixport, irq);
pcisetbme(p);
pcisetioe(p);
- ctlr->mic = xspanalloc(Bufsize, 8, 0);
- ctlr->in = xspanalloc(Bufsize, 8, 0);
- ctlr->out = xspanalloc(Bufsize, 8, 0);
+ ctlr->micring.buf = xspanalloc(Bufsize, 8, 0);
+ ctlr->micring.nbuf = Bufsize;
+ ctlr->micring.ri = 0;
+ ctlr->micring.wi = 0;
+
+ ctlr->inring.buf = xspanalloc(Bufsize, 8, 0);
+ ctlr->inring.nbuf = Bufsize;
+ ctlr->inring.ri = 0;
+ ctlr->inring.wi = 0;
+
+ ctlr->outring.buf = xspanalloc(Bufsize, 8, 0);
+ ctlr->outring.nbuf = Bufsize;
+ ctlr->outring.ri = 0;
+ ctlr->outring.wi = 0;
for(i = 0; i < Ndesc; i++){
- int size, off = i * (Bufsize/Ndesc);
+ int size, off = i * Blocksize;
- if(sis7012)
- size = (Bufsize/Ndesc);
+ if(ctlr->sis7012)
+ size = Blocksize;
else
- size = (Bufsize/Ndesc) / 2;
-
- ctlr->micdesc[i].addr = PCIWADDR(ctlr->mic + off);
+ size = Blocksize / 2;
+ ctlr->micdesc[i].addr = PCIWADDR(ctlr->micring.buf + off);
ctlr->micdesc[i].size = Ioc | size;
- ctlr->indesc[i].addr = PCIWADDR(ctlr->in + off);
+ ctlr->indesc[i].addr = PCIWADDR(ctlr->inring.buf + off);
ctlr->indesc[i].size = Ioc | size;
- ctlr->outdesc[i].addr = PCIWADDR(ctlr->out + off);
+ ctlr->outdesc[i].addr = PCIWADDR(ctlr->outring.buf + off);
ctlr->outdesc[i].size = Ioc | size;
}
- ctlr->outavail = Bufsize - Bufsize/Ndesc;
- ctlr->outp = ctlr->out;
-
ctl = csr32r(ctlr, Cnt);
ctl &= ~(EnaRESER | Aclso);
csr8w(ctlr, Out+Cr, Ioce); /* | Lvbie | Feie */
csr8w(ctlr, Mic+Cr, Ioce); /* | Lvbie | Feie */
- adev->attach = ac97attach;
+ ac97mixreset(adev, ac97mixw, ac97mixr);
+
+ adev->read = ac97read;
adev->write = ac97write;
- adev->status = ac97status;
- adev->ctl = ac97ctl;
+ adev->close = ac97close;
adev->buffered = ac97buffered;
-
- ac97mixreset(adev, ac97mixw, ac97mixr);
+ adev->status = ac97status;
intrenable(irq, ac97interrupt, adev, tbdf, adev->name);
void
audioac97link(void)
{
- addaudiocard("ac97audio", ac97reset);
+ addaudiocard("ac97", ac97reset);
}