]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/9/pc/usbuhci.c
merge
[plan9front.git] / sys / src / 9 / pc / usbuhci.c
index d5a107a6817664760e6534dab7b4190a811951f0..a5783d546f6554cb61ac5a5f3e71db6a6b8d06a9 100644 (file)
@@ -14,6 +14,7 @@
 #include       "dat.h"
 #include       "fns.h"
 #include       "io.h"
+#include       "../port/pci.h"
 #include       "../port/error.h"
 #include       "../port/usb.h"
 
@@ -30,7 +31,7 @@ enum
 {
        Resetdelay      = 100,          /* delay after a controller reset (ms) */
        Enabledelay     = 100,          /* waiting for a port to enable */
-       Abortdelay      = 5,            /* delay after cancelling Tds (ms) */
+       Abortdelay      = 10,           /* delay after cancelling Tds (ms) */
        Incr            = 64,           /* for Td and Qh pools */
 
        Tdatomic        = 8,            /* max nb. of Tds per bulk I/O op. */
@@ -245,7 +246,6 @@ struct Qh
        Qh*     next;           /* in active or free list */
        Td*     tds;            /* Td list in this Qh (initially, elink) */
        char*   tag;            /* debug and align, mostly */
-       ulong   align;
 };
 
 /*
@@ -579,17 +579,18 @@ tdalloc(void)
 {
        int i;
        Td *td;
-       Td *pool;
+       uchar *pool;
 
        lock(&tdpool);
        if(tdpool.free == nil){
                ddprint("uhci: tdalloc %d Tds\n", Incr);
-               pool = xspanalloc(Incr*sizeof(Td), Align, 0);
+               pool = xspanalloc(Incr*ROUND(sizeof(Td), Align), Align, 0);
                if(pool == nil)
                        panic("tdalloc");
                for(i=Incr; --i>=0;){
-                       pool[i].next = tdpool.free;
-                       tdpool.free = &pool[i];
+                       td = (Td*)(pool + i*ROUND(sizeof(Td), Align));
+                       td->next = tdpool.free;
+                       tdpool.free = td;
                }
                tdpool.nalloc += Incr;
                tdpool.nfree += Incr;
@@ -602,7 +603,7 @@ tdalloc(void)
 
        memset(td, 0, sizeof(Td));
        td->link = Tdterm;
-       assert(((ulong)td & 0xF) == 0);
+       assert(((uintptr)td & 0xF) == 0);
        return td;
 }
 
@@ -659,17 +660,18 @@ qhalloc(Ctlr *ctlr, Qh *prev, Qio *io, char *tag)
 {
        int i;
        Qh *qh;
-       Qh *pool;
+       uchar *pool;
 
        lock(&qhpool);
        if(qhpool.free == nil){
                ddprint("uhci: qhalloc %d Qhs\n", Incr);
-               pool = xspanalloc(Incr*sizeof(Qh), Align, 0);
+               pool = xspanalloc(Incr*ROUND(sizeof(Qh), Align), Align, 0);
                if(pool == nil)
                        panic("qhalloc");
                for(i=Incr; --i>=0;){
-                       pool[i].next = qhpool.free;
-                       qhpool.free = &pool[i];
+                       qh = (Qh*)(pool + i*ROUND(sizeof(Qh), Align));
+                       qh->next = qhpool.free;
+                       qhpool.free = qh;
                }
                qhpool.nalloc += Incr;
                qhpool.nfree += Incr;
@@ -696,7 +698,7 @@ qhalloc(Ctlr *ctlr, Qh *prev, Qio *io, char *tag)
                iunlock(ctlr);
        }
 
-       assert(((ulong)qh & 0xF) == 0);
+       assert(((uintptr)qh & 0xF) == 0);
        return qh;
 }
 
@@ -704,12 +706,8 @@ static void
 qhfree(Ctlr *ctlr, Qh *qh)
 {
        Td *td;
-       Td *ltd;
        Qh *q;
 
-       if(qh == nil)
-               return;
-
        ilock(ctlr);
        for(q = ctlr->qhs; q != nil; q = q->next)
                if(q->next == qh)
@@ -718,14 +716,15 @@ qhfree(Ctlr *ctlr, Qh *qh)
                panic("qhfree: nil q");
        q->next = qh->next;
        q->link = qh->link;
+       qh->state = Qfree;      /* paranoia */
        iunlock(ctlr);
 
-       for(td = qh->tds; td != nil; td = ltd){
-               ltd = td->next;
+       while((td = qh->tds) != nil){
+               qh->tds = td->next;
                tdfree(td);
        }
+
        lock(&qhpool);
-       qh->state = Qfree;      /* paranoia */
        qh->next = qhpool.free;
        qh->tag = nil;
        qh->io = nil;
@@ -960,15 +959,11 @@ interrupt(Ureg*, void *a)
        }
        OUTS(Status, sts & Sall);
        cmd = INS(Cmd);
-       if(cmd & Crun == 0){
-               print("uhci %#ux: not running: uhci bug?\n", ctlr->port);
-               /* BUG: should abort everything in this case */
-       }
        if(debug > 1){
                frptr = INL(Flbaseadd);
                frno = INL(Frnum);
                frno = TRUNC(frno, Nframes);
-               print("cmd %#ux sts %#ux frptr %#ux frno %d\n",
+               iprint("cmd %#ux sts %#ux frptr %#ux frno %d\n",
                        cmd, sts, frptr, frno);
        }
        ctlr->ntdintr++;
@@ -993,21 +988,27 @@ interrupt(Ureg*, void *a)
  * it is activated and tdu advanced.
  */
 static long
-putsamples(Isoio *iso, uchar *b, long count)
+putsamples(Ctlr *ctlr, Isoio *iso, uchar *b, long count)
 {
-       long tot;
-       long n;
+       long n, tot, left;
+       Td *tdu;
 
        for(tot = 0; isocanwrite(iso) && tot < count; tot += n){
                n = count-tot;
-               if(n > maxtdlen(iso->tdu) - iso->nleft)
-                       n = maxtdlen(iso->tdu) - iso->nleft;
-               memmove(iso->tdu->data+iso->nleft, b+tot, n);
+               tdu = iso->tdu;
+               left = iso->nleft;
+               if(n > maxtdlen(tdu) - left)
+                       n = maxtdlen(tdu) - left;
+               iunlock(ctlr);  /* can pagefault here */
+               memmove(tdu->data+left, b+tot, n);
+               ilock(ctlr);
+               if(tdu != iso->tdu)
+                       continue;
                iso->nleft += n;
-               if(iso->nleft == maxtdlen(iso->tdu)){
-                       tdisoinit(iso, iso->tdu, iso->nleft);
+               if(iso->nleft == maxtdlen(tdu)){
+                       tdisoinit(iso, tdu, iso->nleft);
+                       iso->tdu = tdu->next;
                        iso->nleft = 0;
-                       iso->tdu = iso->tdu->next;
                }
        }
        return tot;
@@ -1031,7 +1032,7 @@ episowrite(Ep *ep, Isoio *iso, void *a, long count)
        diprint("uhci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
 
        ctlr = ep->hp->aux;
-       qlock(iso);
+       eqlock(iso);
        if(waserror()){
                qunlock(iso);
                nexterror();
@@ -1065,9 +1066,7 @@ episowrite(Ep *ep, Isoio *iso, void *a, long count)
                }
                if(iso->state != Qrun)
                        panic("episowrite: iso not running");
-               iunlock(ctlr);          /* We could page fault here */
-               nw = putsamples(iso, b+tot, count-tot);
-               ilock(ctlr);
+               nw = putsamples(ctlr, iso, b+tot, count-tot);
        }
        while(isodelay(iso) == 0){
                iunlock(ctlr);
@@ -1104,7 +1103,7 @@ episoread(Ep *ep, Isoio *iso, void *a, int count)
 
        b = a;
        ctlr = ep->hp->aux;
-       qlock(iso);
+       eqlock(iso);
        if(waserror()){
                qunlock(iso);
                nexterror();
@@ -1152,6 +1151,8 @@ episoread(Ep *ep, Isoio *iso, void *a, int count)
                        iunlock(ctlr);          /* We could page fault here */
                        memmove(b+tot, tdu->data, nr);
                        ilock(ctlr);
+                       if(iso->tdu != tdu)
+                               continue;
                        if(nr < tdu->ndata)
                                memmove(tdu->data, tdu->data+nr, tdu->ndata - nr);
                        tdu->ndata -= nr;
@@ -1214,12 +1215,14 @@ aborttds(Qh *qh)
 {
        Td *td;
 
-       qh->state = Qdone;
        qh->elink = QHterm;
+       coherence();
        for(td = qh->tds; td != nil; td = td->next){
-               if(td->csw & Tdactive)
+               if(td->csw & Tdactive){
                        td->ndata = 0;
-               td->csw &= ~(Tdactive|Tdioc);
+                       td->csw &= ~(Tdactive|Tdioc);
+                       coherence();
+               }
        }
 }
 
@@ -1257,13 +1260,15 @@ epiowait(Ctlr *ctlr, Qio *io, int tmout, ulong load)
        else if(qh->state != Qdone && qh->state != Qclose)
                panic("epio: queue not done and not closed");
        if(timedout){
-               aborttds(io->qh);
-               io->err = "request timed out";
+               aborttds(qh);
+               qh->state = Qdone;
+               if(io->err == nil)
+                       io->err = "request timed out";
                iunlock(ctlr);
-               if(!waserror()){
-                       tsleep(&up->sleep, return0, 0, Abortdelay);
-                       poperror();
-               }
+               while(waserror())
+                       ;
+               tsleep(&up->sleep, return0, 0, Abortdelay);
+               poperror();
                ilock(ctlr);
        }
        if(qh->state != Qclose)
@@ -1291,7 +1296,6 @@ epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
        ulong load;
        char *err;
 
-       qh = io->qh;
        ctlr = ep->hp->aux;
        io->debug = ep->debug;
        tmout = ep->tmout;
@@ -1303,7 +1307,7 @@ epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
                print("uchi epio: user data: %s\n", buf);
        }
        if(mustlock){
-               qlock(io);
+               eqlock(io);
                if(waserror()){
                        qunlock(io);
                        nexterror();
@@ -1311,7 +1315,8 @@ epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
        }
        io->err = nil;
        ilock(ctlr);
-       if(qh->state == Qclose){        /* Tds released by cancelio */
+       qh = io->qh;
+       if(qh == nil || qh->state == Qclose){   /* Tds released by cancelio */
                iunlock(ctlr);
                error(io->err ? io->err : Eio);
        }
@@ -1359,6 +1364,8 @@ epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
        if(debug > 1 || ep->debug > 1)
                dumptd(td0, "epio: got tds: ");
 
+       err = io->err;
+
        tot = 0;
        c = a;
        saved = 0;
@@ -1374,16 +1381,22 @@ epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
                        if(saved++ == 0)
                                io->toggle = td->token & Tddata1;
                }else{
-                       tot += td->ndata;
-                       if(c != nil && tdtok(td) == Tdtokin && td->ndata > 0){
-                               memmove(c, td->data, td->ndata);
-                               c += td->ndata;
+                       n = td->ndata;
+                       if(err == nil && n < 0)
+                               err = Eio;
+                       if(err == nil && n > 0 && tot < count){
+                               if((tot + n) > count)
+                                       n = count - tot;
+                               if(c != nil && tdtok(td) == Tdtokin){
+                                       memmove(c, td->data, n);
+                                       c += n;
+                               }
+                               tot += n;
                        }
                }
                ntd = td->next;
                tdfree(td);
        }
-       err = io->err;
        if(mustlock){
                qunlock(io);
                poperror();
@@ -1392,8 +1405,6 @@ epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
                io, ntds, tot, err);
        if(err != nil)
                error(err);
-       if(tot < 0)
-               error(Eio);
        return tot;
 }
 
@@ -1442,7 +1453,7 @@ epread(Ep *ep, void *a, long count)
        switch(ep->ttype){
        case Tctl:
                cio = ep->aux;
-               qlock(cio);
+               eqlock(cio);
                if(waserror()){
                        qunlock(cio);
                        nexterror();
@@ -1515,7 +1526,7 @@ epctlio(Ep *ep, Ctlio *cio, void *a, long count)
                cio, ep->dev->nb, ep->nb, count);
        if(count < Rsetuplen)
                error("short usb comand");
-       qlock(cio);
+       eqlock(cio);
        free(cio->data);
        cio->data = nil;
        cio->ndata = 0;
@@ -1777,7 +1788,7 @@ epopen(Ep *ep)
        case Tintr:
                io = ep->aux = smalloc(sizeof(Qio)*2);
                io[OREAD].debug = io[OWRITE].debug = ep->debug;
-               usbid = ((ep->nb&Epmax)<<7)|(ep->dev->nb &Devmax);
+               usbid = ((ep->nb&Epmax)<<7)|(ep->dev->nb&Devmax);
                if(ep->mode != OREAD){
                        if(ep->toggle[OWRITE] != 0)
                                io[OWRITE].toggle = Tddata1;
@@ -1811,7 +1822,7 @@ cancelio(Ctlr *ctlr, Qio *io)
 
        ilock(ctlr);
        qh = io->qh;
-       if(io == nil || io->qh == nil || io->qh->state == Qclose){
+       if(qh == nil || qh->state == Qclose){
                iunlock(ctlr);
                return;
        }
@@ -1820,18 +1831,20 @@ cancelio(Ctlr *ctlr, Qio *io)
        aborttds(qh);
        qh->state = Qclose;
        iunlock(ctlr);
-       if(!waserror()){
-               tsleep(&up->sleep, return0, 0, Abortdelay);
-               poperror();
-       }
+
+       while(waserror())
+               ;
+       tsleep(&up->sleep, return0, 0, Abortdelay);
+       poperror();
 
        wakeup(io);
        qlock(io);
        /* wait for epio if running */
+       if(io->qh == qh)
+               io->qh = nil;
        qunlock(io);
 
        qhfree(ctlr, qh);
-       io->qh = nil;
 }
 
 static void
@@ -2001,7 +2014,7 @@ portenable(Hci *hp, int port, int on)
        ctlr = hp->aux;
        dprint("uhci: %#x port %d enable=%d\n", ctlr->port, port, on);
        ioport = PORT(port-1);
-       qlock(&ctlr->portlck);
+       eqlock(&ctlr->portlck);
        if(waserror()){
                qunlock(&ctlr->portlck);
                nexterror();
@@ -2058,7 +2071,7 @@ portstatus(Hci *hp, int port)
 
        ctlr = hp->aux;
        ioport = PORT(port-1);
-       qlock(&ctlr->portlck);
+       eqlock(&ctlr->portlck);
        if(waserror()){
                iunlock(ctlr);
                qunlock(&ctlr->portlck);
@@ -2117,7 +2130,9 @@ scanpci(void)
                        continue;
                switch(p->ccrp){
                case 0:
-                       io = p->mem[4].bar & ~0x0F;
+                       if((p->mem[4].bar & 1) == 0)
+                               continue;
+                       io = p->mem[4].bar & ~3;
                        break;
                default:
                        continue;
@@ -2131,15 +2146,16 @@ scanpci(void)
                        print("usbuhci: port %#ux in use\n", io);
                        continue;
                }
-               if(p->intl == 0xFF || p->intl == 0){
-                       print("usbuhci: no irq assigned for port %#ux\n", io);
-                       continue;
-               }
 
-               dprint("uhci: %#x %#x: port %#ux size %#x irq %d\n",
+               print("uhci: %#x %#x: port %#ux size %#x irq %d\n",
                        p->vid, p->did, io, p->mem[4].size, p->intl);
 
-               ctlr = smalloc(sizeof(Ctlr));
+               ctlr = malloc(sizeof(Ctlr));
+               if(ctlr == nil){
+                       iofree(io);
+                       print("usbuhci: no memory\n");
+                       continue;
+               }
                ctlr->pcidev = p;
                ctlr->port = io;
                for(i = 0; i < Nhcis; i++)
@@ -2148,7 +2164,7 @@ scanpci(void)
                                break;
                        }
                if(i == Nhcis)
-                       print("uhci: bug: no more controllers\n");
+                       print("usbuhci: bug: no more controllers\n");
        }
 }
 
@@ -2306,6 +2322,8 @@ reset(Hci *hp)
                return -1;
 
        p = ctlr->pcidev;
+       pcienable(p);
+
        hp->aux = ctlr;
        hp->port = ctlr->port;
        hp->irq = p->intl;
@@ -2315,6 +2333,8 @@ reset(Hci *hp)
        uhcireset(ctlr);
        uhcimeminit(ctlr);
 
+       pcisetbme(p);
+
        /*
         * Linkage to the generic HCI driver.
         */
@@ -2332,6 +2352,8 @@ reset(Hci *hp)
        hp->shutdown = shutdown;
        hp->debug = setdebug;
        hp->type = "uhci";
+       intrenable(hp->irq, hp->interrupt, hp, hp->tbdf, hp->type);
+
        return 0;
 }