]> 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 e6ff879e3042cc36d780b1e5de4091fc34844d92..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 */
@@ -215,6 +215,7 @@ struct Isoio
        ulong   maxsize;        /* ntds * ep->maxpkt */
        long    nleft;          /* number of bytes left from last write */
        int     debug;          /* debug flag from the endpoint */
+       int     delay;          /* max number of bytes to buffer */
        int     hs;             /* is high speed? */
        Isoio*  next;           /* in list of active Isoios */
        ulong   td0frno;        /* first frame used in ctlr */
@@ -513,7 +514,10 @@ static Qh*
 qhlinkqh(Qh *qh, Qh *next)
 {
        qh->next = next;
-       qh->link = PADDR(next)|Lqh;
+       if(next == nil)
+               qh->link = Lterm;
+       else
+               qh->link = PADDR(next)|Lqh;
        coherence();
        return qh;
 }
@@ -735,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");
@@ -750,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)
@@ -767,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);
        }
 
@@ -1290,6 +1294,43 @@ itdactive(Itd *td)
        return 0;
 }
 
+static int
+isodelay(void *a)
+{
+       Isoio *iso;
+       int delay;
+
+       iso = a;
+       if(iso->state == Qclose || iso->err || iso->delay == 0)
+               return 1;
+
+       delay = 0;
+       if(iso->hs){
+               Itd *i;
+
+               for(i = iso->tdi; i->next != iso->tdu; i = i->next){
+                       if(!itdactive(i))
+                               continue;
+                       delay += i->mdata;
+                       if(delay > iso->delay)
+                               break;
+               }
+       } else {
+               Sitd *i;
+
+               for(i = iso->stdi; i->next != iso->stdu; i = i->next){
+                       if((i->csw & Stdactive) == 0)
+                               continue;
+                       delay += i->mdata;
+                       if(delay > iso->delay)
+                               break;
+               }
+       }
+
+       return delay <= iso->delay;
+}
+
+
 static int
 isohsinterrupt(Ctlr *ctlr, Isoio *iso)
 {
@@ -1297,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);
@@ -1368,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);
@@ -1438,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){
@@ -1473,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);
@@ -1516,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);
                }
 
@@ -1542,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;
 }
@@ -1573,7 +1616,7 @@ portenable(Hci *hp, int port, int on)
        ctlr = hp->aux;
        opio = ctlr->opio;
        s = opio->portsc[port-1];
-       qlock(&ctlr->portlck);
+       eqlock(&ctlr->portlck);
        if(waserror()){
                qunlock(&ctlr->portlck);
                nexterror();
@@ -1624,7 +1667,7 @@ portlend(Ctlr *ctlr, int port, char *ss)
 static int
 portreset(Hci *hp, int port, int on)
 {
-       ulong s;
+       ulong *portscp;
        Eopio *opio;
        Ctlr *ctlr;
        int i;
@@ -1634,37 +1677,43 @@ portreset(Hci *hp, int port, int on)
 
        ctlr = hp->aux;
        opio = ctlr->opio;
-       qlock(&ctlr->portlck);
+       eqlock(&ctlr->portlck);
        if(waserror()){
                iunlock(ctlr);
                qunlock(&ctlr->portlck);
                nexterror();
        }
-       s = opio->portsc[port-1];
-       dprint("ehci %#p port %d reset; sts %#lux\n", ctlr->capio, port, s);
+       portscp = &opio->portsc[port-1];
+       dprint("ehci %#p port %d reset; sts %#lux\n", ctlr->capio, port, *portscp);
        ilock(ctlr);
-       s &= ~(Psenable|Psreset);
-       opio->portsc[port-1] = s | Psreset;     /* initiate reset */
-       coherence();
+       /* Shalted must be zero, else Psreset will stay set */
+       if (opio->sts & Shalted)
+               iprint("ehci %#p: halted yet trying to reset port\n",
+                       ctlr->capio);
 
-       for(i = 0; i < 50; i++){                /* was 10 */
-               delay(10);
-               if((opio->portsc[port-1] & Psreset) == 0)
-                       break;
-       }
-       if (opio->portsc[port-1] & Psreset)
-               iprint("ehci %#p: port %d didn't reset after %d ms; sts %#lux\n",
-                       ctlr->capio, port, i * 10, opio->portsc[port-1]);
-       opio->portsc[port-1] &= ~Psreset;  /* force appearance of reset done */
-       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);
-       if((opio->portsc[port-1] & Psenable) == 0)
+       for(i = 0; *portscp & Psreset && i < 10; i++)
+               delay(10);
+
+       if (*portscp & Psreset)
+               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");
 
        iunlock(ctlr);
        dprint("ehci %#p after port %d reset; sts %#lux\n",
-               ctlr->capio, port, opio->portsc[port-1]);
+               ctlr->capio, port, *portscp);
        qunlock(&ctlr->portlck);
        poperror();
        return 0;
@@ -1679,7 +1728,7 @@ portstatus(Hci *hp, int port)
 
        ctlr = hp->aux;
        opio = ctlr->opio;
-       qlock(&ctlr->portlck);
+       eqlock(&ctlr->portlck);
        if(waserror()){
                iunlock(ctlr);
                qunlock(&ctlr->portlck);
@@ -1836,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;
@@ -1872,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);
@@ -1898,7 +1951,7 @@ episoread(Ep *ep, Isoio *iso, void *a, long count)
 
        b = a;
        ctlr = ep->hp->aux;
-       qlock(iso);
+       eqlock(iso);
        if(waserror()){
                qunlock(iso);
                nexterror();
@@ -1951,33 +2004,44 @@ episoread(Ep *ep, Isoio *iso, void *a, long count)
  * 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, n;
+       long left, tot, n;
+       Sitd *stdu;
+       Itd *tdu;
 
        for(tot = 0; isocanwrite(iso) && tot < count; tot += n){
                n = count-tot;
+               left = iso->nleft;
                if(iso->hs != 0){
-                       if(n > iso->tdu->mdata - iso->nleft)
-                               n = iso->tdu->mdata - iso->nleft;
-                       memmove(iso->tdu->data + iso->nleft, b + tot, n);
-                       coherence();
+                       tdu = iso->tdu;
+                       if(n > tdu->mdata - left)
+                               n = tdu->mdata - left;
+                       iunlock(ctlr);          /* We could page fault here */
+                       memmove(tdu->data + left, b + tot, n);
+                       ilock(ctlr);
+                       if(iso->tdu != tdu)
+                               continue;
                        iso->nleft += n;
-                       if(iso->nleft == iso->tdu->mdata){
-                               itdinit(iso, iso->tdu);
+                       if(iso->nleft == tdu->mdata){
+                               itdinit(iso, tdu);
+                               iso->tdu = tdu->next;
                                iso->nleft = 0;
-                               iso->tdu = iso->tdu->next;
                        }
                }else{
-                       if(n > iso->stdu->mdata - iso->nleft)
-                               n = iso->stdu->mdata - iso->nleft;
-                       memmove(iso->stdu->data + iso->nleft, b + tot, n);
-                       coherence();
+                       stdu = iso->stdu;
+                       if(n > stdu->mdata - left)
+                               n = stdu->mdata - left;
+                       iunlock(ctlr);          /* We could page fault here */
+                       memmove(stdu->data + left, b + tot, n);
+                       ilock(ctlr);
+                       if(iso->stdu != stdu)
+                               continue;
                        iso->nleft += n;
-                       if(iso->nleft == iso->stdu->mdata){
-                               sitdinit(iso, iso->stdu);
+                       if(iso->nleft == stdu->mdata){
+                               sitdinit(iso, stdu);
+                               iso->stdu = stdu->next;
                                iso->nleft = 0;
-                               iso->stdu = iso->stdu->next;
                        }
                }
        }
@@ -1996,11 +2060,12 @@ episowrite(Ep *ep, Isoio *iso, void *a, long count)
        int tot, nw;
        char *err;
 
+       iso->delay = ep->sampledelay * ep->samplesz;
        iso->debug = ep->debug;
        diprint("ehci: 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();
@@ -2035,8 +2100,11 @@ 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);
+               nw = putsamples(ctlr, iso, b+tot, count-tot);
+       }
+       while(isodelay(iso) == 0){
+               iunlock(ctlr);
+               sleep(iso, isodelay, iso);
                ilock(ctlr);
        }
        if(iso->state != Qclose)
@@ -2090,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;
@@ -2117,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();
+               }
        }
 }
 
@@ -2154,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);
@@ -2225,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;
@@ -2241,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--;
@@ -2277,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;
@@ -2289,7 +2360,7 @@ epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
                print("echi epio: user data: %s\n", buf);
        }
        if(mustlock){
-               qlock(io);
+               eqlock(io);
                if(waserror()){
                        qunlock(io);
                        nexterror();
@@ -2297,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);
        }
@@ -2354,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;
@@ -2363,25 +2436,31 @@ epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
                ntds++;
                /*
                 * Use td tok, not io tok, because of setup packets.
-                * Also, if the Td was stalled or active (previous Td
-                * was a short packet), we must save the toggle as it is.
+                * Also, we must save the next toggle value from the
+                * last completed Td (in case of a short packet, or
+                * fewer than the requested number of packets in the
+                * Td being transferred).
                 */
-               if(td->csw & (Tdhalt|Tdactive)){
-                       if(saved++ == 0) {
+               if(td->csw & (Tdhalt|Tdactive))
+                       saved++;
+               else{
+                       if(!saved){
                                io->toggle = td->csw & Tddata1;
                                coherence();
                        }
-               }else{
-                       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();
@@ -2392,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;
 }
 
@@ -2415,7 +2492,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();
@@ -2486,7 +2563,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;
@@ -2662,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
@@ -2886,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;
        }
@@ -2895,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
@@ -3155,6 +3234,7 @@ init(Hci *hp)
 {
        Ctlr *ctlr;
        Eopio *opio;
+       static int ctlrno;
        int i;
 
        hp->highspeed = 1;
@@ -3174,7 +3254,9 @@ init(Hci *hp)
        opio->cmd |= Case;
        coherence();
        ehcirun(ctlr, 1);
-       opio->config = Callmine;        /* reclaim all ports */
+
+       /* route all ports to us */
+       opio->config = Callmine;
        coherence();
 
        for (i = 0; i < hp->nports; i++)
@@ -3182,6 +3264,7 @@ init(Hci *hp)
        iunlock(ctlr);
        if(ehcidebug > 1)
                dump(hp);
+       ctlrno++;
 }
 
 void