]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/nusb/usbd/hub.c
nusb/usbd: fix dump %U formatter
[plan9front.git] / sys / src / cmd / nusb / usbd / hub.c
index 6d64c001dcee62c9cddee6222fa4975f2e4cdff0..8fb19d2148b27a0cf3322df2b3ad66c8356b8490 100644 (file)
@@ -10,35 +10,9 @@ QLock hublock;
 static int nhubs;
 static int mustdump;
 static int pollms = Pollms;
-static Lock masklck;
 
 static char *dsname[] = { "disabled", "attached", "configed" };
 
-int
-getdevnb(uvlong *maskp)
-{
-       int i;
-
-       lock(&masklck);
-       for(i = 0; i < 8 * sizeof *maskp; i++)
-               if((*maskp & (1ULL<<i)) == 0){
-                       *maskp |= 1ULL<<i;
-                       unlock(&masklck);
-                       return i;
-               }
-       unlock(&masklck);
-       return -1;
-}
-
-void
-putdevnb(uvlong *maskp, int id)
-{
-       lock(&masklck);
-       if(id >= 0)
-               *maskp &= ~(1ULL<<id);
-       unlock(&masklck);
-}
-
 static int
 hubfeature(Hub *h, int port, int f, int on)
 {
@@ -51,60 +25,47 @@ hubfeature(Hub *h, int port, int f, int on)
        return usbcmd(h->dev, Rh2d|Rclass|Rother, cmd, f, port, nil, 0);
 }
 
-/*
- * This may be used to detect overcurrent on the hub
- */
-static void
-checkhubstatus(Hub *h)
+static int
+configusb2hub(Hub *h, DHub *dd, int nr)
 {
-       uchar buf[4];
-       int sts;
+       uchar *PortPwrCtrlMask;
+       int i, offset, mask, nmap;
+       Port *pp;
 
-       if(h->isroot)   /* not for root hubs */
-               return;
-       if(usbcmd(h->dev, Rd2h|Rclass|Rdev, Rgetstatus, 0, 0, buf, 4) < 0){
-               dprint(2, "%s: get hub status: %r\n", h->dev->dir);
-               return;
+       h->nport = dd->bNbrPorts;
+       nmap = 1 + h->nport/8;
+       if(nr < 7 + 2*nmap){
+               fprint(2, "%s: %s: descr. too small\n", argv0, h->dev->dir);
+               return -1;
+       }
+       h->port = emallocz((h->nport+1)*sizeof(Port), 1);
+       h->pwrms = dd->bPwrOn2PwrGood*2;
+       if(h->pwrms < Powerdelay)
+               h->pwrms = Powerdelay;
+       h->maxcurrent = dd->bHubContrCurrent;
+       h->pwrmode = dd->wHubCharacteristics[0] & 3;
+       h->compound = (dd->wHubCharacteristics[0] & (1<<2))!=0;
+       h->leds = (dd->wHubCharacteristics[0] & (1<<7)) != 0;
+       PortPwrCtrlMask = dd->DeviceRemovable + nmap;
+       for(i = 1; i <= h->nport; i++){
+               pp = &h->port[i];
+               offset = i/8;
+               mask = 1<<(i%8);
+               pp->removable = (dd->DeviceRemovable[offset] & mask) != 0;
+               pp->pwrctl = (PortPwrCtrlMask[offset] & mask) != 0;
        }
-       sts = GET2(buf);
-       dprint(2, "hub %s: status %#ux\n", h->dev->dir, sts);
+       return 0;
 }
 
 static int
-confighub(Hub *h)
+configusb3hub(Hub *h, DSSHub *dd, int nr)
 {
-       int type;
-       uchar buf[128]; /* room for extra descriptors */
-       int i;
-       Usbdev *d;
-       DHub *dd;
+       int i, offset, mask, nmap;
        Port *pp;
-       int nr;
-       int nmap;
-       uchar *PortPwrCtrlMask;
-       int offset;
-       int mask;
 
-       d = h->dev->usb;
-       for(i = 0; i < nelem(d->ddesc); i++)
-               if(d->ddesc[i] == nil)
-                       break;
-               else if(d->ddesc[i]->data.bDescriptorType == Dhub){
-                       dd = (DHub*)&d->ddesc[i]->data;
-                       nr = Dhublen;
-                       goto Config;
-               }
-       type = Rd2h|Rclass|Rdev;
-       nr = usbcmd(h->dev, type, Rgetdesc, Dhub<<8|0, 0, buf, sizeof buf);
-       if(nr < Dhublen){
-               dprint(2, "%s: %s: getdesc hub: %r\n", argv0, h->dev->dir);
-               return -1;
-       }
-       dd = (DHub*)buf;
-Config:
        h->nport = dd->bNbrPorts;
        nmap = 1 + h->nport/8;
-       if(nr < 7 + 2*nmap){
+       if(nr < 10 + nmap){
                fprint(2, "%s: %s: descr. too small\n", argv0, h->dev->dir);
                return -1;
        }
@@ -116,24 +77,68 @@ Config:
        h->pwrmode = dd->wHubCharacteristics[0] & 3;
        h->compound = (dd->wHubCharacteristics[0] & (1<<2))!=0;
        h->leds = (dd->wHubCharacteristics[0] & (1<<7)) != 0;
-       PortPwrCtrlMask = dd->DeviceRemovable + nmap;
        for(i = 1; i <= h->nport; i++){
                pp = &h->port[i];
                offset = i/8;
                mask = 1<<(i%8);
                pp->removable = (dd->DeviceRemovable[offset] & mask) != 0;
-               pp->pwrctl = (PortPwrCtrlMask[offset] & mask) != 0;
+       }
+       if(usbcmd(h->dev, Rh2d|Rclass|Rdev, Rsethubdepth, h->dev->depth, 0, nil, 0) < 0){
+               fprint(2, "%s: %s: sethubdepth: %r\n", argv0, h->dev->dir);
+               return -1;
        }
        return 0;
 }
 
+static int
+confighub(Hub *h)
+{
+       uchar buf[128]; /* room for extra descriptors */
+       int i, dt, dl, nr;
+       Usbdev *d;
+       void *dd;
+
+       d = h->dev->usb;
+       if(h->dev->isusb3){
+               dt = Dsshub;
+               dl = Dsshublen;
+       } else {
+               dt = Dhub;
+               dl = Dhublen;
+       }
+       for(i = 0; i < nelem(d->ddesc); i++){
+               if(d->ddesc[i] == nil)
+                       break;
+               if(d->ddesc[i]->data.bDescriptorType == dt){
+                       dd = &d->ddesc[i]->data;
+                       nr = d->ddesc[i]->data.bLength;
+                       goto Config;
+               }
+       }
+       nr = usbcmd(h->dev, Rd2h|Rclass|Rdev, Rgetdesc, dt<<8|0, 0, buf, sizeof buf);
+       if(nr < 0){
+               fprint(2, "%s: %s: getdesc hub: %r\n", argv0, h->dev->dir);
+               return -1;
+       }
+       dd = buf;
+Config:
+       if(nr < dl){
+               fprint(2, "%s: %s: hub descriptor too small (%d < %d)\n", argv0, h->dev->dir, nr, dl);
+               return -1;
+       }
+       if(h->dev->isusb3)
+               return configusb3hub(h, dd, nr);
+       else
+               return configusb2hub(h, dd, nr);
+}
+
 static void
 configroothub(Hub *h)
 {
-       Dev *d;
-       char buf[128];
+       char buf[1024];
        char *p;
        int nr;
+       Dev *d;
 
        d = h->dev;
        h->nport = 2;
@@ -143,7 +148,7 @@ configroothub(Hub *h)
        if(nr < 0)
                goto Done;
        buf[nr] = 0;
-
+       d->isusb3 = strstr(buf, "speed super") != nil;
        p = strstr(buf, "ports ");
        if(p == nil)
                fprint(2, "%s: %s: no port information\n", argv0, d->dir);
@@ -174,11 +179,13 @@ newhub(char *fn, Dev *d)
                        fprint(2, "%s: opendev: %s: %r", argv0, fn);
                        goto Fail;
                }
+               h->dev->depth = -1;
+               configroothub(h);       /* never fails */
                if(opendevdata(h->dev, ORDWR) < 0){
                        fprint(2, "%s: opendevdata: %s: %r\n", argv0, fn);
+                       closedev(h->dev);
                        goto Fail;
                }
-               configroothub(h);       /* never fails */
        }else{
                h->dev = d;
                if(confighub(h) < 0){
@@ -186,10 +193,6 @@ newhub(char *fn, Dev *d)
                        goto Fail;
                }
        }
-       if(h->dev == nil){
-               fprint(2, "%s: opendev: %s: %r\n", argv0, fn);
-               goto Fail;
-       }
        devctl(h->dev, "hub");
        ud = h->dev->usb;
        if(h->isroot)
@@ -265,13 +268,13 @@ closehub(Hub *h)
        free(h);
 }
 
-static int
+static u32int
 portstatus(Hub *h, int p)
 {
        Dev *d;
        uchar buf[4];
+       u32int sts;
        int t;
-       int sts;
        int dbg;
 
        dbg = usbdebug;
@@ -282,34 +285,38 @@ portstatus(Hub *h, int p)
        if(usbcmd(d, t, Rgetstatus, 0, p, buf, sizeof(buf)) < 0)
                sts = -1;
        else
-               sts = GET2(buf);
+               sts = GET4(buf);
        usbdebug = dbg;
        return sts;
 }
 
 static char*
-stsstr(int sts)
+stsstr(int sts, int isusb3)
 {
        static char s[80];
        char *e;
 
        e = s;
-       if(sts&PSsuspend)
-               *e++ = 'z';
-       if(sts&PSreset)
-               *e++ = 'r';
-       if(sts&PSslow)
-               *e++ = 'l';
-       if(sts&PShigh)
-               *e++ = 'h';
-       if(sts&PSchange)
-               *e++ = 'c';
-       if(sts&PSenable)
-               *e++ = 'e';
-       if(sts&PSstatuschg)
-               *e++ = 's';
        if(sts&PSpresent)
                *e++ = 'p';
+       if(sts&PSenable)
+               *e++ = 'e';
+       if(sts&PSovercurrent)
+               *e++ = 'o';
+       if(sts&PSreset)
+               *e++ = 'r';
+       if(!isusb3){
+               if(sts&PSslow)
+                       *e++ = 'l';
+               if(sts&PShigh)
+                       *e++ = 'h';
+               if(sts&PSchange)
+                       *e++ = 'c';
+               if(sts&PSstatuschg)
+                       *e++ = 's';
+               if(sts&PSsuspend)
+                       *e++ = 'z';
+       }
        if(e == s)
                *e++ = '-';
        *e = 0;
@@ -322,6 +329,8 @@ getmaxpkt(Dev *d, int islow)
        uchar buf[64];  /* More room to try to get device-specific descriptors */
        DDev *dd;
 
+       if(d->isusb3)
+               return 512;
        dd = (DDev*)buf;
        if(islow)
                dd->bMaxPacketSize0 = 8;
@@ -336,7 +345,7 @@ getmaxpkt(Dev *d, int islow)
  * BUG: does not consider max. power avail.
  */
 static Dev*
-portattach(Hub *h, int p, int sts)
+portattach(Hub *h, int p, u32int sts)
 {
        Dev *d;
        Port *pp;
@@ -352,31 +361,43 @@ portattach(Hub *h, int p, int sts)
        nd = nil;
        pp->state = Pattached;
        dprint(2, "%s: %s: port %d attach sts %#ux\n", argv0, d->dir, p, sts);
-       sleep(Connectdelay);
-       if(hubfeature(h, p, Fportenable, 1) < 0)
-               dprint(2, "%s: %s: port %d: enable: %r\n", argv0, d->dir, p);
-       sleep(Enabledelay);
-       if(hubfeature(h, p, Fportreset, 1) < 0){
-               dprint(2, "%s: %s: port %d: reset: %r\n", argv0, d->dir, p);
-               goto Fail;
-       }
-       sleep(Resetdelay);
-       sts = portstatus(h, p);
-       if(sts < 0)
-               goto Fail;
-       if((sts & PSenable) == 0){
-               dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
-               hubfeature(h, p, Fportenable, 1);
+       if(h->dev->isusb3){
+               sleep(Connectdelay);
                sts = portstatus(h, p);
-               if((sts & PSenable) == 0)
+               if(sts == -1)
+                       goto Fail;
+               if((sts & PSenable) == 0){
+                       dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
+                       goto Fail;
+               }
+               sp = "super";
+       } else {
+               sleep(Connectdelay);
+               if(hubfeature(h, p, Fportenable, 1) < 0)
+                       dprint(2, "%s: %s: port %d: enable: %r\n", argv0, d->dir, p);
+               sleep(Enabledelay);
+               if(hubfeature(h, p, Fportreset, 1) < 0){
+                       dprint(2, "%s: %s: port %d: reset: %r\n", argv0, d->dir, p);
                        goto Fail;
+               }
+               sleep(Resetdelay);
+               sts = portstatus(h, p);
+               if(sts == -1)
+                       goto Fail;
+               if((sts & PSenable) == 0){
+                       dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
+                       hubfeature(h, p, Fportenable, 1);
+                       sts = portstatus(h, p);
+                       if((sts & PSenable) == 0)
+                               goto Fail;
+               }
+               sp = "full";
+               if(sts & PSslow)
+                       sp = "low";
+               if(sts & PShigh)
+                       sp = "high";
        }
-       sp = "full";
-       if(sts & PSslow)
-               sp = "low";
-       if(sts & PShigh)
-               sp = "high";
-       dprint(2, "%s: %s: port %d: attached status %#ux\n", argv0, d->dir, p, sts);
+       dprint(2, "%s: %s: port %d: attached status %#ux, speed %s\n", argv0, d->dir, p, sts, sp);
 
        if(devctl(d, "newdev %s %d", sp, p) < 0){
                fprint(2, "%s: %s: port %d: newdev: %r\n", argv0, d->dir, p);
@@ -399,6 +420,8 @@ portattach(Hub *h, int p, int sts)
                fprint(2, "%s: %s: port %d: opendev: %r\n", argv0, d->dir, p);
                goto Fail;
        }
+       nd->depth = h->dev->depth+1;
+       nd->isusb3 = h->dev->isusb3;
        if(usbdebug > 2)
                devctl(nd, "debug 1");
        if(opendevdata(nd, ORDWR) < 0){
@@ -413,7 +436,6 @@ portattach(Hub *h, int p, int sts)
                dprint(2, "%s: %s: port %d: set address: %r\n", argv0, d->dir, p);
                goto Fail;
        }
-
        mp=getmaxpkt(nd, strcmp(sp, "low") == 0);
        if(mp < 0){
                dprint(2, "%s: %s: port %d: getmaxpkt: %r\n", argv0, d->dir, p);
@@ -422,9 +444,6 @@ portattach(Hub *h, int p, int sts)
                dprint(2, "%s; %s: port %d: maxpkt %d\n", argv0, d->dir, p, mp);
                devctl(nd, "maxpkt %d", mp);
        }
-       if((sts & PSslow) != 0 && strcmp(sp, "full") == 0)
-               dprint(2, "%s: %s: port %d: %s is full speed when port is low\n",
-                       argv0, d->dir, p, nd->dir);
        if(configdev(nd) < 0){
                dprint(2, "%s: %s: port %d: configdev: %r\n", argv0, d->dir, p);
                goto Fail;
@@ -438,17 +457,16 @@ portattach(Hub *h, int p, int sts)
                if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0)
                        goto Fail;
        }
-       dprint(2, "%s: %U", argv0, nd);
        pp->state = Pconfiged;
-       dprint(2, "%s: %s: port %d: configed: %s\n",
-                       argv0, d->dir, p, nd->dir);
+       dprint(2, "%s: %s: port %d: configured: %s\n", argv0, d->dir, p, nd->dir);
        return pp->dev = nd;
 Fail:
        pp->state = Pdisabled;
        pp->sts = 0;
        if(pp->hub != nil)
                pp->hub = nil;  /* hub closed by enumhub */
-       hubfeature(h, p, Fportenable, 0);
+       if(!h->dev->isusb3)
+               hubfeature(h, p, Fportenable, 0);
        if(nd != nil)
                devctl(nd, "detach");
        closedev(nd);
@@ -477,10 +495,9 @@ portdetach(Hub *h, int p)
                closehub(pp->hub);
                pp->hub = nil;
        }
-       if(pp->devmaskp != nil)
-               putdevnb(pp->devmaskp, pp->devnb);
-       pp->devmaskp = nil;
        if(pp->dev != nil){
+               detachdev(pp);
+
                devctl(pp->dev, "detach");
                closedev(pp->dev);
                pp->dev = nil;
@@ -523,7 +540,7 @@ portresetwanted(Hub *h, int p)
 static void
 portreset(Hub *h, int p)
 {
-       int sts;
+       u32int sts;
        Dev *d, *nd;
        Port *pp;
 
@@ -537,10 +554,12 @@ portreset(Hub *h, int p)
        }
        sleep(Resetdelay);
        sts = portstatus(h, p);
-       if(sts < 0)
+       if(sts == -1)
                goto Fail;
        if((sts & PSenable) == 0){
                dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
+               if(h->dev->isusb3)
+                       goto Fail;
                hubfeature(h, p, Fportenable, 1);
                sts = portstatus(h, p);
                if((sts & PSenable) == 0)
@@ -562,24 +581,27 @@ portreset(Hub *h, int p)
                if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0)
                        goto Fail;
        }
-       if(nd->dfd >= 0)
+       if(nd->dfd >= 0){
                close(nd->dfd);
+               nd->dfd = -1;
+       }
        return;
 Fail:
        pp->state = Pdisabled;
        pp->sts = 0;
        if(pp->hub != nil)
                pp->hub = nil;  /* hub closed by enumhub */
-       hubfeature(h, p, Fportenable, 0);
+       if(!h->dev->isusb3)
+               hubfeature(h, p, Fportenable, 0);
        if(nd != nil)
                devctl(nd, "detach");
        closedev(nd);
 }
 
 static int
-portgone(Port *pp, int sts)
+portgone(Port *pp, u32int sts)
 {
-       if(sts < 0)
+       if(sts == -1)
                return 1;
        /*
         * If it was enabled and it's not now then it may be reconnect.
@@ -593,7 +615,7 @@ portgone(Port *pp, int sts)
 static int
 enumhub(Hub *h, int p)
 {
-       int sts;
+       u32int sts;
        Dev *d;
        Port *pp;
        int onhubs;
@@ -605,31 +627,33 @@ enumhub(Hub *h, int p)
                fprint(2, "%s: %s: port %d enumhub\n", argv0, d->dir, p);
 
        sts = portstatus(h, p);
-       if(sts < 0){
+       if(sts == -1){
                hubfail(h);             /* avoid delays on detachment */
                return -1;
        }
        pp = &h->port[p];
        onhubs = nhubs;
-       if((sts & PSsuspend) != 0){
-               if(hubfeature(h, p, Fportenable, 1) < 0)
-                       dprint(2, "%s: %s: port %d: enable: %r\n", argv0, d->dir, p);
-               sleep(Enabledelay);
-               sts = portstatus(h, p);
-               fprint(2, "%s: %s: port %d: resumed (sts %#ux)\n", argv0, d->dir, p, sts);
+       if(!h->dev->isusb3){
+               if((sts & PSsuspend) != 0){
+                       if(hubfeature(h, p, Fportenable, 1) < 0)
+                               dprint(2, "%s: %s: port %d: enable: %r\n", argv0, d->dir, p);
+                       sleep(Enabledelay);
+                       sts = portstatus(h, p);
+                       fprint(2, "%s: %s: port %d: resumed (sts %#ux)\n", argv0, d->dir, p, sts);
+               }
        }
        if((pp->sts & PSpresent) == 0 && (sts & PSpresent) != 0){
                if(portattach(h, p, sts) != nil)
-                       if(startdev(pp) < 0)
+                       if(attachdev(pp) < 0)
                                portdetach(h, p);
-       }else if(portgone(pp, sts))
+       }else if(portgone(pp, sts)){
                portdetach(h, p);
-       else if(portresetwanted(h, p))
+       }else if(portresetwanted(h, p))
                portreset(h, p);
        else if(pp->sts != sts){
-               dprint(2, "%s: %s port %d: sts %s %#x ->",
-                       argv0, d->dir, p, stsstr(pp->sts), pp->sts);
-               dprint(2, " %s %#x\n",stsstr(sts), sts);
+               dprint(2, "%s: %s port %d: sts %s %#ux ->",
+                       argv0, d->dir, p, stsstr(pp->sts, h->dev->isusb3), pp->sts);
+               dprint(2, " %s %#ux\n",stsstr(sts, h->dev->isusb3), sts);
        }
        pp->sts = sts;
        if(onhubs != nhubs)
@@ -646,7 +670,7 @@ dump(void)
        mustdump = 0;
        for(h = hubs; h != nil; h = h->next)
                for(i = 1; i <= h->nport; i++)
-                       fprint(2, "%s: hub %#p %s port %d: %U",
+                       fprint(2, "%s: hub %#p %s port %d: %U\n",
                                argv0, h, h->dev->dir, i, h->port[i].dev);
 
 }
@@ -666,6 +690,10 @@ work(void)
                        fprint(2, "%s: %s: newhub failed: %r\n", argv0, fn);
                free(fn);
        }
+
+       if(hubs == nil)
+               return;
+
        /*
         * Enumerate (and acknowledge after first enumeration).
         * Do NOT perform enumeration concurrently for the same
@@ -686,6 +714,7 @@ Again:
                                        goto Again;
                                }
                qunlock(&hublock);
+               checkidle();
                sleep(pollms);
                if(mustdump)
                        dump();