diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2017-07-31 03:22:23 +0200 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2017-07-31 03:22:23 +0200 |
commit | 6e65596827f7ee292221697ff5248c9bc9520851 (patch) | |
tree | bd256a20fa5860839a618b6c806eff1197901946 | |
parent | 215b67ff3d630c56418026dda7ae68f23111c4a5 (diff) |
xhci: experimental usb3 support
-rw-r--r-- | sys/src/9/pc/usbohci.c | 2 | ||||
-rw-r--r-- | sys/src/9/pc/usbxhci.c | 66 | ||||
-rw-r--r-- | sys/src/9/port/devusb.c | 136 | ||||
-rw-r--r-- | sys/src/9/port/usb.h | 7 | ||||
-rw-r--r-- | sys/src/9/port/usbehci.c | 2 |
5 files changed, 148 insertions, 65 deletions
diff --git a/sys/src/9/pc/usbohci.c b/sys/src/9/pc/usbohci.c index c70b59790..ef9b07df9 100644 --- a/sys/src/9/pc/usbohci.c +++ b/sys/src/9/pc/usbohci.c @@ -949,7 +949,7 @@ seprintep(char* s, char* e, Ep *ep) case Tctl: cio = ep->aux; s = seprintio(s, e, cio, "c"); - s = seprint(s, e, "\trepl %d ndata %d\n", ep->rhrepl, cio->ndata); + s = seprint(s, e, "\trepl %llux ndata %d\n", ep->rhrepl, cio->ndata); break; case Tbulk: case Tintr: diff --git a/sys/src/9/pc/usbxhci.c b/sys/src/9/pc/usbxhci.c index c23ee0a66..9ab4b648c 100644 --- a/sys/src/9/pc/usbxhci.c +++ b/sys/src/9/pc/usbxhci.c @@ -387,8 +387,10 @@ init(Hci *hp) ctlr->nscratch = (ctlr->mmio[HCSPARAMS2] >> 27) & 0x1F; ctlr->nintrs = (ctlr->mmio[HCSPARAMS1] >> 8) & 0x7FF; ctlr->nslots = (ctlr->mmio[HCSPARAMS1] >> 0) & 0xFF; - hp->nports = (ctlr->mmio[HCSPARAMS1] >> 24) & 0xFF; + hp->highspeed = 1; + hp->superspeed = 0; + hp->nports = (ctlr->mmio[HCSPARAMS1] >> 24) & 0xFF; ctlr->port = malloc(hp->nports * sizeof(Port)); for(i=0; i<hp->nports; i++) ctlr->port[i].reg = &ctlr->opr[0x400/4 + i*4]; @@ -403,6 +405,8 @@ init(Hci *hp) pp = &ctlr->port[i-1]; pp->proto = x[0]>>16; memmove(pp->spec, &x[1], 4); + if(memcmp(pp->spec, "USB ", 4) == 0 && pp->proto >= 0x0300) + hp->superspeed |= 1<<(i-1); i++; } } @@ -1151,6 +1155,9 @@ epread(Ep *ep, void *va, long n) char *err; Wait ws[1]; + if(ep->dev->isroot) + error(Egreg); + p = va; if(ep->ttype == Tctl){ io = (Epio*)ep->aux + OREAD; @@ -1212,6 +1219,9 @@ epwrite(Ep *ep, void *va, long n) uchar *p; char *err; + if(ep->dev->isroot) + error(Egreg); + p = va; if(ep->ttype == Tctl){ int dir, len; @@ -1335,41 +1345,46 @@ portstatus(Hci *hp, int port) { Ctlr *ctlr = hp->aux; Port *pp = &ctlr->port[port-1]; - u32int psc = pp->reg[PORTSC]; - int ps = 0; - - if(memcmp(pp->spec, "USB", 3) != 0 || pp->proto > 0x0200) - return 0; + u32int psc, ps = 0; + psc = pp->reg[PORTSC]; if(psc & CCS) ps |= HPpresent; if(psc & PED) ps |= HPenable; if(psc & OCA) ps |= HPovercurrent; if(psc & PR) ps |= HPreset; - else { - switch((psc>>10)&0xF){ - case 1: - /* full speed */ - break; - case 2: - ps |= HPslow; - break; - case 3: - ps |= HPhigh; - break; - case 4: /* super speed */ - break; + + if((hp->superspeed & (1<<(port-1))) != 0){ + ps |= psc & (PLS|PP); + if(psc & CSC) ps |= 1<<0+16; + if(psc & OCC) ps |= 1<<3+16; + if(psc & PRC) ps |= 1<<4+16; + if(psc & WRC) ps |= 1<<5+16; + if(psc & PLC) ps |= 1<<6+16; + if(psc & CEC) ps |= 1<<7+16; + } else { + if((ps & HPreset) == 0){ + switch((psc>>10)&15){ + case 1: + /* full speed */ + break; + case 2: + ps |= HPslow; + break; + case 3: + ps |= HPhigh; + break; + } } + if(psc & PP) ps |= HPpower; + if(psc & CSC) ps |= HPstatuschg; + if(psc & PRC) ps |= HPchange; } - if(psc & PP) ps |= HPpower; - - if(psc & CSC) ps |= HPstatuschg; - if(psc & PRC) ps |= HPchange; return ps; } static int -portenable(Hci *, int, int) +portenable(Hci *, int port, int on) { return 0; } @@ -1380,9 +1395,6 @@ portreset(Hci *hp, int port, int on) Ctlr *ctlr = hp->aux; Port *pp = &ctlr->port[port-1]; - if(memcmp(pp->spec, "USB", 3) != 0 || pp->proto > 0x0200) - return 0; - if(on){ pp->reg[PORTSC] |= PR; tsleep(&up->sleep, return0, nil, 200); diff --git a/sys/src/9/port/devusb.c b/sys/src/9/port/devusb.c index 0eb8db37a..4ecd08c28 100644 --- a/sys/src/9/port/devusb.c +++ b/sys/src/9/port/devusb.c @@ -73,7 +73,7 @@ enum /* Ep. ctls */ CMnew = 0, /* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */ - CMnewdev, /* newdev full|low|high portnb (allocate new devices) */ + CMnewdev, /* newdev full|low|high|super portnb (allocate new devices) */ CMhub, /* hub (set the device as a hub) */ CMspeed, /* speed full|low|high|no */ CMmaxpkt, /* maxpkt size */ @@ -296,6 +296,7 @@ seprintep(char *s, char *se, Ep *ep, int all) s = seprint(s, se, " hz %ld", ep->hz); s = seprint(s, se, " hub %d", ep->dev->hub); s = seprint(s, se, " port %d", ep->dev->port); + s = seprint(s, se, " rootport %d", ep->dev->rootport); s = seprint(s, se, " addr %d", ep->dev->addr); if(ep->inuse) s = seprint(s, se, " busy"); @@ -474,11 +475,8 @@ newdev(Hci *hp, int ishub, int isroot) d->isroot = isroot; d->rootport = 0; d->routestr = 0; - d->depth = 0; - if(hp->highspeed != 0) - d->speed = Highspeed; - else - d->speed = Fullspeed; + d->depth = -1; + d->speed = Fullspeed; d->state = Dconfig; /* address not yet set */ ep->dev = d; ep->ep0 = ep; /* no ref counted here */ @@ -746,6 +744,17 @@ usbreset(void) print("usbreset: bug: Nhcis (%d) too small\n", Nhcis); } +static int +numbits(uint n) +{ + int c = 0; + while(n != 0){ + c++; + n = (n-1) & n; + } + return c; +} + static void usbinit(void) { @@ -758,13 +767,31 @@ usbinit(void) for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){ hp = hcis[ctlrno]; if(hp != nil){ + int n; + if(hp->init != nil) hp->init(hp); - d = newdev(hp, 1, 1); /* new root hub */ - d->dev->state = Denabled; /* although addr == 0 */ - d->maxpkt = 64; - snprint(info, sizeof(info), "ports %d", hp->nports); - kstrdup(&d->info, info); + + hp->superspeed &= (1<<hp->nports)-1; + n = hp->nports - numbits(hp->superspeed); + if(n > 0){ + d = newdev(hp, 1, 1); /* new LS/FS/HS root hub */ + d->maxpkt = 64; + if(hp->highspeed != 0) + d->dev->speed = Highspeed; + d->dev->state = Denabled; /* although addr == 0 */ + snprint(info, sizeof(info), "roothub ports %d", n); + kstrdup(&d->info, info); + } + n = numbits(hp->superspeed); + if(n > 0){ + d = newdev(hp, 1, 1); /* new SS root hub */ + d->maxpkt = 512; + d->dev->speed = Superspeed; + d->dev->state = Denabled; /* although addr == 0 */ + snprint(info, sizeof(info), "roothub ports %d", n); + kstrdup(&d->info, info); + } } } } @@ -805,6 +832,7 @@ usbload(int speed, int maxpkt) l = 0; bs = 10UL * maxpkt; switch(speed){ + case Superspeed: case Highspeed: l = 55*8*2 + 2 * (3 + bs) + Hostns; break; @@ -985,20 +1013,57 @@ ctlread(Chan *c, void *a, long n, vlong offset) static long rhubread(Ep *ep, void *a, long n) { - char *b; + uchar b[8]; - if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2) - return -1; - if(ep->rhrepl < 0) + if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2 || ep->rhrepl == -1) return -1; - b = a; - memset(b, 0, n); - PUT2(b, ep->rhrepl); + b[0] = ep->rhrepl; + b[1] = ep->rhrepl>>8; + b[2] = ep->rhrepl>>16; + b[3] = ep->rhrepl>>24; + b[4] = ep->rhrepl>>32; + b[5] = ep->rhrepl>>40; + b[6] = ep->rhrepl>>48; + b[7] = ep->rhrepl>>56; + ep->rhrepl = -1; + + if(n > sizeof(b)) + n = sizeof(b); + memmove(a, b, n); + return n; } +static int +rootport(Ep *ep, int port) +{ + Hci *hp; + Udev *hub; + uint mask; + int rootport; + + hp = ep->hp; + hub = ep->dev; + if(!hub->isroot) + return hub->rootport; + + mask = hp->superspeed; + if(hub->speed != Superspeed) + mask = (1<<hp->nports)-1 & ~mask; + + for(rootport = 1; mask != 0; rootport++){ + if(mask & 1){ + if(--port == 0) + return rootport; + } + mask >>= 1; + } + + return 0; +} + static long rhubwrite(Ep *ep, void *a, long n) { @@ -1019,8 +1084,8 @@ rhubwrite(Ep *ep, void *a, long n) hp = ep->hp; cmd = s[Rreq]; feature = GET2(s+Rvalue); - port = GET2(s+Rindex); - if(port < 1 || port > hp->nports) + port = rootport(ep, GET2(s+Rindex)); + if(port == 0) error("bad hub port number"); switch(feature){ case Rportenable: @@ -1089,16 +1154,15 @@ setmaxpkt(Ep *ep, char* s) { long spp, max; /* samples per packet */ - if(ep->dev->speed == Highspeed || ep->dev->speed == Superspeed) - spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000; - else + if(ep->dev->speed == Fullspeed) spp = (ep->hz * ep->pollival + 999) / 1000; + else + spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000; ep->maxpkt = spp * ep->samplesz; deprint("usb: %s: setmaxpkt: hz %ld poll %ld" " ntds %d %s speed -> spp %ld maxpkt %ld\n", s, ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed], spp, ep->maxpkt); - switch(ep->dev->speed){ case Fullspeed: max = 1024; @@ -1170,16 +1234,20 @@ epctl(Ep *ep, Chan *c, void *a, long n) error("not a hub setup endpoint"); l = name2speed(cb->f[1]); if(l == Nospeed) - error("speed must be full|low|high"); + error("speed must be full|low|high|super"); + if(l != d->speed && (l == Superspeed || d->speed == Superspeed)) + error("wrong speed for superspeed hub/device"); nep = newdev(ep->hp, 0, 0); nep->dev->speed = l; - if(nep->dev->speed != Lowspeed) + if(l == Superspeed) + nep->maxpkt = 512; + else if(l != Lowspeed) nep->maxpkt = 64; /* assume full speed */ nep->dev->hub = d->addr; nep->dev->port = atoi(cb->f[2]); nep->dev->depth = d->depth+1; - nep->dev->rootport = d->depth == 0 ? nep->dev->port : d->rootport; - nep->dev->routestr = d->routestr | ((nep->dev->port&15) << 4*d->depth) >> 4; + nep->dev->rootport = rootport(ep, nep->dev->port); + nep->dev->routestr = d->routestr | (((nep->dev->port&15) << 4*nep->dev->depth) >> 4); /* next read request will read * the name for the new endpoint */ @@ -1195,7 +1263,9 @@ epctl(Ep *ep, Chan *c, void *a, long n) l = name2speed(cb->f[1]); deprint("usb epctl %s %d\n", cb->f[0], l); if(l == Nospeed) - error("speed must be full|low|high"); + error("speed must be full|low|high|super"); + if(l != d->speed && (l == Superspeed || d->speed == Superspeed)) + error("cannot change speed on superspeed device"); qlock(ep->ep0); d->speed = l; qunlock(ep->ep0); @@ -1223,13 +1293,13 @@ epctl(Ep *ep, Chan *c, void *a, long n) error("not an intr or iso endpoint"); l = strtoul(cb->f[1], nil, 0); deprint("usb epctl %s %d\n", cb->f[0], l); - if(ep->dev->speed == Highspeed || ep->dev->speed == Superspeed){ + if(ep->dev->speed == Fullspeed || ep->dev->speed == Lowspeed){ + if(l < 1 || l > 255) + error("pollival not in [1:255]"); + } else { if(l < 1 || l > 16) error("pollival power not in [1:16]"); l = 1 << l-1; - } else { - if(l < 1 || l > 255) - error("pollival not in [1:255]"); } qlock(ep); ep->pollival = l; diff --git a/sys/src/9/port/usb.h b/sys/src/9/port/usb.h index 655ee4f4f..25258f318 100644 --- a/sys/src/9/port/usb.h +++ b/sys/src/9/port/usb.h @@ -128,7 +128,8 @@ struct Hci int tbdf; /* type+busno+devno+funcno */ int ctlrno; /* controller number */ int nports; /* number of ports in hub */ - int highspeed; + int highspeed; /* supports highspeed devices */ + uint superspeed; /* bitmap of superspeed ports */ Hciimpl; /* HCI driver */ }; @@ -164,7 +165,7 @@ struct Ep int ttype; /* tranfer type */ ulong load; /* in µs, for a transfer of maxpkt bytes */ void* aux; /* for controller specific info */ - int rhrepl; /* fake root hub replies */ + u64int rhrepl; /* fake root hub replies */ int toggle[2]; /* saved toggles (while ep is not in use) */ long pollival; /* poll interval ([µ]frames; intr/iso) */ long hz; /* poll frequency (iso) */ @@ -184,7 +185,7 @@ struct Udev int state; /* state for the device */ int ishub; /* hubs can allocate devices */ int isroot; /* is a root hub */ - int speed; /* Full/Low/High/No -speed */ + int speed; /* Full/Low/High/Super/No -speed */ int hub; /* device address for the parent hub */ int port; /* port number in the parent hub */ int addr; /* device address */ diff --git a/sys/src/9/port/usbehci.c b/sys/src/9/port/usbehci.c index 648a91c8a..0cba5d99e 100644 --- a/sys/src/9/port/usbehci.c +++ b/sys/src/9/port/usbehci.c @@ -1803,7 +1803,7 @@ seprintep(char *s, char *e, Ep *ep) case Tctl: cio = ep->aux; s = seprintio(s, e, cio, "c"); - s = seprint(s, e, "\trepl %d ndata %d\n", ep->rhrepl, cio->ndata); + s = seprint(s, e, "\trepl %llux ndata %d\n", ep->rhrepl, cio->ndata); break; case Tbulk: case Tintr: |