]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/9/pc/audioac97.c
audioac97, audiohda: dont block on close, just pad the last block with zeros
[plan9front.git] / sys / src / 9 / pc / audioac97.c
index e8e674489166b0c5dc606ec63a6042db670b4132..4773248707b67dee0084f3cafcb8897a5c092f6a 100644 (file)
@@ -5,10 +5,11 @@
 #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,
@@ -22,12 +23,23 @@ struct Hwdesc {
 
 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];
@@ -35,43 +47,16 @@ struct Ctlr {
        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;
 };
@@ -137,6 +122,76 @@ enum {
                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)))
@@ -145,8 +200,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
@@ -182,245 +237,168 @@ ac97mixr(Audio *adev, int port)
        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*
@@ -428,50 +406,24 @@ ac97match(Pcidev *p)
 {
        /* 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;
 }
@@ -516,25 +468,34 @@ ac97reset(Audio *adev)
 
 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);
@@ -542,29 +503,36 @@ Found:
        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);
 
@@ -611,13 +579,13 @@ Found:
        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);
 
@@ -627,5 +595,5 @@ Found:
 void
 audioac97link(void)
 {
-       addaudiocard("ac97audio", ac97reset);
+       addaudiocard("ac97", ac97reset);
 }