]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/9/port/usbehci.c
devproc: make sure /proc/n/wait waits for the right process children
[plan9front.git] / sys / src / 9 / port / usbehci.c
index 22af2e79889ec01b8dbeeff15b6f06e9df91f392..66546675cdd2a499fddb935a06116568c7e95b48 100644 (file)
@@ -53,7 +53,7 @@ enum
        Qfree,
 
        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 pools of Tds, Qhs, etc. */
        Align           = 128,          /* in bytes for all those descriptors */
@@ -739,11 +739,12 @@ qhcoherency(Ctlr *ctlr)
        qlock(&ctlr->portlck);
        ctlr->opio->cmd |= Ciasync;     /* ask for intr. on async advance */
        coherence();
-       for(i = 0; i < 3 && qhadvanced(ctlr) == 0; i++)
-               if(!waserror()){
-                       tsleep(ctlr, qhadvanced, ctlr, Abortdelay);
-                       poperror();
-               }
+       for(i = 0; i < 3 && qhadvanced(ctlr) == 0; i++){
+               while(waserror())
+                       ;
+               tsleep(ctlr, qhadvanced, ctlr, Abortdelay);
+               poperror();
+       }
        dprint("ehci: qhcoherency: doorbell %d\n", qhadvanced(ctlr));
        if(i == 3)
                print("ehci: async advance doorbell did not ring\n");
@@ -754,11 +755,9 @@ qhcoherency(Ctlr *ctlr)
 static void
 qhfree(Ctlr *ctlr, Qh *qh)
 {
-       Td *td, *ltd;
+       Td *td;
        Qh *q;
 
-       if(qh == nil)
-               return;
        ilock(ctlr);
        if(qh->sched < 0){
                for(q = ctlr->qhs; q != nil; q = q->next)
@@ -771,12 +770,13 @@ qhfree(Ctlr *ctlr, Qh *qh)
                coherence();
        }else
                unschedq(ctlr, qh);
+       qh->state = Qfree;      /* paranoia */
        iunlock(ctlr);
 
        qhcoherency(ctlr);
 
-       for(td = qh->tds; td != nil; td = ltd){
-               ltd = td->next;
+       while((td = qh->tds) != nil){
+               qh->tds = td->next;
                tdfree(td);
        }
 
@@ -1338,8 +1338,7 @@ isohsinterrupt(Ctlr *ctlr, Isoio *iso)
        Itd *tdi;
 
        tdi = iso->tdi;
-       assert(tdi != nil);
-       if(itdactive(tdi))                      /* not all tds are done */
+       if(tdi == nil || itdactive(tdi))                        /* not all tds are done */
                return 0;
        ctlr->nisointr++;
        ddiprint("isohsintr: iso %#p: tdi %#p tdu %#p\n", iso, tdi, iso->tdu);
@@ -1409,8 +1408,7 @@ isofsinterrupt(Ctlr *ctlr, Isoio *iso)
        Sitd *stdi;
 
        stdi = iso->stdi;
-       assert(stdi != nil);
-       if((stdi->csw & Stdactive) != 0)                /* nothing new done */
+       if(stdi == nil || (stdi->csw & Stdactive) != 0)         /* nothing new done */
                return 0;
        ctlr->nisointr++;
        ddiprint("isofsintr: iso %#p: tdi %#p tdu %#p\n", iso, stdi, iso->stdu);
@@ -1479,7 +1477,7 @@ qhinterrupt(Ctlr *ctlr, Qh *qh)
                panic("qhinterrupt: qh state");
        td = qh->tds;
        if(td == nil)
-               panic("qhinterrupt: no tds");
+               return 0;
        if((td->csw & Tdactive) == 0)
                ddqprint("qhinterrupt port %#p qh %#p\n", ctlr->capio, qh);
        for(; td != nil; td = td->next){
@@ -1514,36 +1512,30 @@ qhinterrupt(Ctlr *ctlr, Qh *qh)
 }
 
 static int
-ehciintr(Hci *hp)
+ctlrinterrupt(Ctlr *ctlr)
 {
-       Ctlr *ctlr;
        Eopio *opio;
        Isoio *iso;
        ulong sts;
        Qh *qh;
        int i, some;
 
-       ctlr = hp->aux;
        opio = ctlr->opio;
-
        /*
         * Will we know in USB 3.0 who the interrupt was for?.
         * Do they still teach indexing in CS?
         * This is Intel's doing.
         */
-       ilock(ctlr);
-       ctlr->nintr++;
        sts = opio->sts & Sintrs;
-       if(sts == 0){           /* not ours; shared intr. */
-               iunlock(ctlr);
+       if(sts == 0)            /* not ours; shared intr. */
                return 0;
-       }
        opio->sts = sts;
        coherence();
+       ctlr->nintr++;
        if((sts & Sherr) != 0)
-               print("ehci: port %#p fatal host system error\n", ctlr->capio);
+               iprint("ehci: port %#p fatal host system error\n", ctlr->capio);
        if((sts & Shalted) != 0)
-               print("ehci: port %#p: halted\n", ctlr->capio);
+               iprint("ehci: port %#p: halted\n", ctlr->capio);
        if((sts & Sasync) != 0){
                dprint("ehci: doorbell\n");
                wakeup(ctlr);
@@ -1557,12 +1549,12 @@ ehciintr(Hci *hp)
        if((sts & (Serrintr|Sintr)) != 0){
                ctlr->ntdintr++;
                if(ehcidebug > 1){
-                       print("ehci port %#p frames %#p nintr %d ntdintr %d",
+                       iprint("ehci port %#p frames %#p nintr %d ntdintr %d",
                                ctlr->capio, ctlr->frames,
                                ctlr->nintr, ctlr->ntdintr);
-                       print(" nqhintr %d nisointr %d\n",
+                       iprint(" nqhintr %d nisointr %d\n",
                                ctlr->nqhintr, ctlr->nisointr);
-                       print("\tcmd %#lux sts %#lux intr %#lux frno %uld",
+                       iprint("\tcmd %#lux sts %#lux intr %#lux frno %uld",
                                opio->cmd, opio->sts, opio->intr, opio->frno);
                }
 
@@ -1583,17 +1575,27 @@ ehciintr(Hci *hp)
                qh = ctlr->qhs;
                i = 0;
                do{
-                       if (qh == nil)
-                               panic("ehciintr: nil qh");
+                       if(qh == nil)
+                               break;
                        if(qh->state == Qrun)
                                some += qhinterrupt(ctlr, qh);
                        qh = qh->next;
                }while(qh != ctlr->qhs && i++ < 100);
                if(i > 100)
-                       print("echi: interrupt: qh loop?\n");
+                       iprint("echi: interrupt: qh loop?\n");
        }
-//     if (some == 0)
-//             panic("ehciintr: no work");
+       return some;
+}
+
+static int
+ehciintr(Hci *hp)
+{
+       Ctlr *ctlr;
+       int some;
+
+       ctlr = hp->aux;
+       ilock(ctlr);
+       some = ctlrinterrupt(ctlr);
        iunlock(ctlr);
        return some;
 }
@@ -1688,22 +1690,24 @@ portreset(Hci *hp, int port, int on)
        if (opio->sts & Shalted)
                iprint("ehci %#p: halted yet trying to reset port\n",
                        ctlr->capio);
-       *portscp = (*portscp & ~Psenable) | Psreset;    /* initiate reset */
-       coherence();
 
+       *portscp = (*portscp & ~Psenable) | Psreset;    /* initiate reset */
        /*
         * usb 2 spec: reset must finish within 20 ms.
         * linux says spec says it can take 50 ms. for hubs.
         */
+       delay(50);
+       *portscp &= ~Psreset;   /* terminate reset */
+
+       delay(10);
        for(i = 0; *portscp & Psreset && i < 10; i++)
                delay(10);
+
        if (*portscp & Psreset)
-               iprint("ehci %#p: port %d didn't reset within %d ms; sts %#lux\n",
-                       ctlr->capio, port, i * 10, *portscp);
-       *portscp &= ~Psreset;           /* force appearance of reset done */
-       coherence();
-       delay(10);                      /* ehci spec: enable within 2 ms. */
+               iprint("ehci %#p: port %d didn't reset; sts %#lux\n",
+                       ctlr->capio, port, *portscp);
 
+       delay(10);                      /* ehci spec: enable within 2 ms. */
        if((*portscp & Psenable) == 0)
                portlend(ctlr, port, "full");
 
@@ -1881,6 +1885,8 @@ episohscpy(Ctlr *ctlr, Ep *ep, Isoio* iso, uchar *b, long 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;
@@ -1917,6 +1923,8 @@ episofscpy(Ctlr *ctlr, Ep *ep, Isoio* iso, uchar *b, long count)
                        iunlock(ctlr);          /* We could page fault here */
                        memmove(b+tot, stdu->data, nr);
                        ilock(ctlr);
+                       if(iso->stdu != stdu)
+                               continue;
                        if(nr < stdu->ndata)
                                memmove(stdu->data, stdu->data+nr,
                                        stdu->ndata - nr);
@@ -2150,14 +2158,17 @@ epgettd(Qio *io, int flags, void *a, int count, int maxpkt)
        if(count <= Align - sizeof(Td)){
                td->data = td->sbuff;
                td->buff = nil;
-       }else
-               td->data = td->buff = smalloc(Tdmaxpkt);
+       } else if(count <= 0x4000){
+               td->buff = td->data = smalloc(count);
+       } else {
+               td->buff = smalloc(count + 0x1000);
+               td->data = (uchar*)ROUND((uintptr)td->buff, 0x1000);
+       }
 
        pa = PADDR(td->data);
        for(i = 0; i < nelem(td->buffer); i++){
                td->buffer[i] = pa;
-               if(i > 0)
-                       td->buffer[i] &= ~0xFFF;
+               pa &= ~0xFFF;
                pa += 0x1000;
        }
        td->ndata = count;
@@ -2177,16 +2188,16 @@ aborttds(Qh *qh)
 {
        Td *td;
 
-       qh->state = Qdone;
-       coherence();
        if(qh->sched >= 0 && (qh->eps0 & Qhspeedmask) != Qhhigh)
                qh->eps0 |= Qhint;      /* inactivate on next pass */
+       qh->csw = (qh->csw & ~Tdactive) | Tdhalt;
        coherence();
        for(td = qh->tds; td != nil; td = td->next){
-               if(td->csw & Tdactive)
+               if(td->csw & Tdactive){
                        td->ndata = 0;
-               td->csw |= Tdhalt;
-               coherence();
+                       td->csw |= Tdhalt;
+                       coherence();
+               }
        }
 }
 
@@ -2214,6 +2225,8 @@ ehcipoll(void* a)
        hp = a;
        ctlr = hp->aux;
        poll = &ctlr->poll;
+       while(waserror())
+               ;
        for(;;){
                if(ctlr->nreqs == 0){
                        if(0)ddprint("ehcipoll %#p sleep\n", ctlr->capio);
@@ -2285,9 +2298,7 @@ epiowait(Hci *hp, Qio *io, int tmout, ulong load)
        ilock(ctlr);
        /* Are we missing interrupts? */
        if(qh->state == Qrun){
-               iunlock(ctlr);
-               ehciintr(hp);
-               ilock(ctlr);
+               ctlrinterrupt(ctlr);
                if(qh->state == Qdone){
                        dqprint("ehci %#p: polling required\n", ctlr->capio);
                        ctlr->poll.must = 1;
@@ -2301,18 +2312,19 @@ epiowait(Hci *hp, Qio *io, int tmout, ulong load)
        }else if(qh->state != Qdone && qh->state != Qclose)
                panic("ehci: epio: queue state %d", qh->state);
        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)
                qh->state = Qidle;
-       coherence();
        qhlinktd(qh, nil);
        ctlr->load -= load;
        ctlr->nreqs--;
@@ -2337,7 +2349,6 @@ epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
        Qh* qh;
        Td *td, *ltd, *td0, *ntd;
 
-       qh = io->qh;
        ctlr = ep->hp->aux;
        io->debug = ep->debug;
        tmout = ep->tmout;
@@ -2357,7 +2368,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);
        }
@@ -2414,6 +2426,7 @@ epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
                dumptd(td0, "epio: got: ");
                qhdump(qh);
        }
+       err = io->err;
 
        tot = 0;
        c = a;
@@ -2435,16 +2448,19 @@ epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
                                io->toggle = td->csw & Tddata1;
                                coherence();
                        }
-                       tot += td->ndata;
-                       if(c != nil && (td->csw & Tdtok) == Tdtokin && td->ndata > 0){
-                               memmove(c, td->data, td->ndata);
-                               c += td->ndata;
+                       if(err == nil && (n = td->ndata) > 0 && tot < count){
+                               if((tot + n) > count)
+                                       n = count - tot;
+                               if(c != nil && (td->csw & Tdtok) == Tdtokin){
+                                       memmove(c, td->data, n);
+                                       c += n;
+                               }
+                               tot += n;
                        }
                }
                ntd = td->next;
                tdfree(td);
        }
-       err = io->err;
        if(mustlock){
                qunlock(io);
                poperror();
@@ -2455,8 +2471,6 @@ epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
                return 0;       /* that's our convention */
        if(err != nil)
                error(err);
-       if(tot < 0)
-               error(Eio);
        return tot;
 }
 
@@ -2725,10 +2739,11 @@ isohsinit(Ep *ep, Isoio *iso)
                td = itdalloc();
                td->data = iso->data + i * 8 * iso->maxsize;
                pa = PADDR(td->data) & ~0xFFF;
-               for(p = 0; p < 8; p++)
-                       td->buffer[i] = pa + p * 0x1000;
-               td->buffer[0] = PADDR(iso->data) & ~0xFFF |
-                       ep->nb << Itdepshift | ep->dev->nb << Itddevshift;
+               for(p = 0; p < nelem(td->buffer); p++){
+                       td->buffer[p] = pa;
+                       pa += 0x1000;
+               }
+               td->buffer[0] |= ep->nb << Itdepshift | ep->dev->nb << Itddevshift;
                if(ep->mode == OREAD)
                        td->buffer[1] |= Itdin;
                else
@@ -2949,7 +2964,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;
        }
@@ -2958,17 +2973,18 @@ 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
@@ -3238,12 +3254,9 @@ init(Hci *hp)
        opio->cmd |= Case;
        coherence();
        ehcirun(ctlr, 1);
-       /*
-        * route all ports by default to only one ehci (the first).
-        * it's not obvious how multiple ehcis could work and on some
-        * machines, setting Callmine on all ehcis makes the machine seize up.
-        */
-       opio->config = (ctlrno == 0 ? Callmine : 0);
+
+       /* route all ports to us */
+       opio->config = Callmine;
        coherence();
 
        for (i = 0; i < hp->nports; i++)