shithub: riscv

Download patch

ref: 065d601916d74f1b5e95d114f7fd8b8bd1747184
parent: 14bb9734a611556f14ce17d810993588b3075a1b
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Mon Feb 21 14:50:16 EST 2022

nusb: Fix handling of interface altsetting.

The altsetting was handled only for a single endpoint
(per interface number), but has to be handled for each
endpoint (per interface *AND* altsetting number).

A multi function device (like a disk) can have
multiple interfaces, all with the same interface number
but varying altsetting numbers and each of these
interfaces would list distict endpoint configurations.

Multiple interfaces can even share some endpoints (they
use the same endpoint addresses), but
we still have to duplicate them for each
interface+altsetting number (as they'r part of
actually distict interfaces with distict endpoint
configurations).

It is also important to *NOT* make endpoints bi-directional
(dir == Eboth) when only one direction is used in a
interface/altsetting and the other direction in another.
This was the case for nusb/disk with some seagate drive
where endpoints where shared between the UAS and
usb storage class interface (but with distict altsettings).

The duplicate endpoints (as in using the same endpoint address)
are chained together by a next pointer and the head
is stored in Usbdev.ep[addr], where addr is the endpoint
address. These Ep structures will have distinct endpoint
numbers Ep.id (when they have conflicting types), but all
will share the endpoint address (lower 4 bits of the
endpoint number).

The consequence is that all of the endpoints configuration
(attributes, interval) is now stored in the Ep struct and
no more Altc struct is present.

A pointer to the Ep struct has to be passed to openep()
for it to configure the endpoint.

For the Iface struct, we will now create multiple of them:
one for each interface *AND* altsetting nunber,
chained together on a next pointer and the head being
stored in conf->iface[ifaceid].

--
cinap

--- a/sys/man/2/nusb
+++ b/sys/man/2/nusb
@@ -24,14 +24,17 @@
 	int	id;		/* usb id for device or ep. number */
 	int	dfd;		/* descriptor for the data file */
 	int	cfd;		/* descriptor for the control file */
+	int	isusb3;		/* this is a usb3 device */
+	int	depth;		/* hub depth for usb3 hubs */
 	int	maxpkt;		/* cached from usb description */
 	Usbdev*	usb;		/* USB description */
+	Ep*	ep;		/* endpoint from epopen() */
 	void*	aux;		/* for the device driver */
-	void	(*free)(void*);	/* idem. to release aux */
 	char*	hname;		/* hash name, unique for device */
 };
 .sp 0.3v
 struct Usbdev {
+	int	ver;		/* usb version */
 	ulong	csp;		/* USB class/subclass/proto */
 	int	vid;		/* vendor id */
 	int	did;		/* product (device) id */
@@ -39,36 +42,42 @@
 	char*	vendor;
 	char*	product;
 	char*	serial;
-	int	ls;		/* low speed */
+	int	vsid;
+	int	psid;
+	int	ssid;
 	int	class;		/* from descriptor */
 	int	nconf;		/* from descriptor */
 	Conf*	conf[Nconf];	/* configurations */
-	Ep*	ep[Nep];	/* all endpoints in device */
+	Ep*	ep[Epmax+1];	/* all endpoints in device (chained), indexed by address */
 	Desc*	ddesc[Nddesc];	/* (raw) device specific descriptors */
 };
 .sp 0.3v
 struct Ep {
-	uchar	addr;		/* endpt address */
-	uchar	dir;		/* direction, Ein/Eout */
-	uchar	type;		/* Econtrol, Eiso, Ebulk, Eintr */
-	uchar	isotype;	/* Eunknown, Easync, Eadapt, Esync */
-	int	id;
-	int	maxpkt;		/* max. packet size */
-	Conf*	conf;		/* the endpoint belongs to */
 	Iface*	iface;		/* the endpoint belongs to */
-};
-.sp 0.3v
-struct Altc {
+	Conf*	conf;		/* the endpoint belongs to */
+
+	int	id;		/* endpoint number: (id & Epmax) == endpoint address */
+	uchar	dir;		/* direction, Ein/Eout/Eboth */
+	uchar	type;		/* Econtrol, Eiso, Ebulk, Eintr */
+
 	int	attrib;
-	int	interval;
+	int	pollival;
+	int	maxpkt;		/* max. packet size */
+	int	ntds;		/* nb. of Tds per µframe */
+
+	/* chain of endpoints with same address (used in different interfaces/altsettings) */
+	Ep*	next;
+
 	void*	aux;		/* for the driver program */
 };
 .sp 0.3v
 struct Iface {
-	int 	id;		/* interface number */
+	int	id;		/* interface number */
+	int	alt;		/* altsetting for this interface */
 	ulong	csp;		/* USB class/subclass/proto */
-	Altc*	altc[Naltc];
-	Ep*	ep[Nep];
+	Iface*	next;		/* chain of interfaces of different altsettings */
+	Ep*	ep[Nep];	/* consecutive array of endpoints in this interface (not including ep0) */
+
 	void*	aux;		/* for the driver program */
 };
 .sp 0.3v
@@ -83,7 +92,6 @@
 	Conf*	conf;		/* where this descriptor was read */
 	Iface*	iface;		/* last iface before desc in conf. */
 	Ep*	ep;		/* last endpt before desc in conf. */
-	Altc*	altc;		/* last alt.c. before desc in conf. */
 	DDesc	data;		/* unparsed standard USB descriptor */
 };
 .sp 0.3v
@@ -116,7 +124,7 @@
 char*	loaddevstr(Dev *d, int sid);
 Dev*	opendev(char *fn);
 int	opendevdata(Dev *d, int mode);
-Dev*	openep(Dev *d, int id);
+Dev*	openep(Dev *d, Ep *e);
 int	unstall(Dev *dev, Dev *ep, int dir);
 int	usbcmd(Dev *d, int type, int req,
 		int value, int index, uchar *data, int count);
@@ -160,11 +168,7 @@
 to drop references (and release resources when the last one vanishes).
 As an aid to the driver, the field
 .B aux
-may keep driver-specific data and the function
-.B free
-will be called (if not null) to release the
-.B aux
-structure when the reference count goes down to zero.
+may keep driver-specific data.
 .PP
 .I Dev.dir
 holds the path for the endpoint's directory.
@@ -173,6 +177,9 @@
 .B id
 keeps the device number for setup endpoints and the endpoint number
 for all other endpoints.
+The endpoint number identifies
+.I devusb
+endpoint and is unique within a device.
 For example, it would be
 .B 3
 for
@@ -184,6 +191,12 @@
 It is easy to remember this because the former is created to operate
 on the device, while the later has been created as a particular endpoint
 to perform I/O.
+.PP
+The field
+.B ep
+holds the endpoint structure that was passed in
+.I epopen
+which gives easy access to the endpoint configuration.
 .PP
 Fields
 .B dfd
--- a/sys/src/cmd/nusb/audio/audio.c
+++ b/sys/src/cmd/nusb/audio/audio.c
@@ -40,15 +40,15 @@
 	uchar *b;
 	int i;
 
-	if(dd == nil || dd->iface == nil || dd->altc == nil)
+	if(dd == nil || dd->iface == nil)
 		return;
 	if(Subclass(dd->iface->csp) != 2)
 		return;
 
-	c = dd->altc->aux;
+	c = dd->iface->aux;
 	if(c == nil){
 		c = mallocz(sizeof(*c), 1);
-		dd->altc->aux = c;
+		dd->iface->aux = c;
 	}
 
 	b = (uchar*)&dd->data;
@@ -87,16 +87,13 @@
 Dev*
 setupep(Dev *d, Ep *e, int speed)
 {
-	Altc *x;
 	Aconf *c;
 	Range *f;
-	int i;
 
-	for(i = 0; i < nelem(e->iface->altc); i++){
-		if((x = e->iface->altc[i]) == nil)
+	for(;e != nil; e = e->next){
+		c = e->iface->aux;
+		if(c == nil)
 			continue;
-		if((c = x->aux) == nil)
-			continue;
 		for(f = c->freq; f != nil; f = f->next)
 			if(speed >= f->min && speed <= f->max)
 				goto Foundaltc;
@@ -105,7 +102,7 @@
 	return nil;
 
 Foundaltc:
-	if(usbcmd(d, Rh2d|Rstd|Riface, Rsetiface, i, e->iface->id, nil, 0) < 0){
+	if(usbcmd(d, Rh2d|Rstd|Riface, Rsetiface, e->iface->alt, e->iface->id, nil, 0) < 0){
 		werrstr("set altc: %r");
 		return nil;
 	}
@@ -116,15 +113,14 @@
 		b[0] = speed;
 		b[1] = speed >> 8;
 		b[2] = speed >> 16;
-		if(usbcmd(d, Rh2d|Rclass|Rep, Rsetcur, 0x100, e->addr, b, 3) < 0)
+		if(usbcmd(d, Rh2d|Rclass|Rep, Rsetcur, 0x100, (e->type==Ein?0x80:0)|(e->id&Epmax), b, 3) < 0)
 			fprint(2, "warning: set freq: %r\n");
 	}
 
-	if((d = openep(d, e->id)) == nil){
+	if((d = openep(d, e)) == nil){
 		werrstr("openep: %r");
 		return nil;
 	}
-	devctl(d, "pollival %d", x->interval);
 	devctl(d, "samplesz %d", audiochan*audiores/8);
 	devctl(d, "sampledelay %d", audiodelay);
 	devctl(d, "hz %d", speed);
@@ -224,7 +220,7 @@
 		parsedescr(d->usb->ddesc[i]);
 	for(i = 0; i < nelem(d->usb->ep); i++){
 		e = d->usb->ep[i];
-		if(e != nil && e->type == Eiso && e->iface != nil && e->iface->csp == CSP(Claudio, 2, 0)){
+		if(e != nil && e->type == Eiso && e->iface->csp == CSP(Claudio, 2, 0)){
 			switch(e->dir){
 			case Ein:
 				if(audioepin != nil)
--- a/sys/src/cmd/nusb/cam/ctl.c
+++ b/sys/src/cmd/nusb/cam/ctl.c
@@ -248,11 +248,10 @@
 			val = max;
 		else{
 			val = floor((t - min) / step) * step + min;
-			if(t >= val + step / 2.0)
-				t += step;
 		}
 	}else{
 		mini = -1;
+		minδ = 0;
 		for(i = 0; i < f->bFrameIntervalType; i++){
 			δ = fabs(((u32int)GET4(f->dwFrameInterval[i])) - t);
 			if(mini < 0 || δ < minδ){
--- a/sys/src/cmd/nusb/cam/video.c
+++ b/sys/src/cmd/nusb/cam/video.c
@@ -197,43 +197,41 @@
 	qunlock(&c->qulock);
 }
 
-static Altc *
-selaltc(Cam *c, ProbeControl *pc)
+static Ep*
+selaltc(Cam *c, ProbeControl *pc, Ep *ep)
 {
-	int k;
 	uvlong bw, bw1, minbw;
-	int mink;
 	Format *fo;
 	VSUncompressedFrame *f;
-	Iface *iface;
-	Altc *altc;
-	
+	Ep *mink;
+
 	if(getframedesc(c, pc->bFormatIndex, pc->bFrameIndex, &fo, &f) < 0){
 		werrstr("selaltc: PROBE_CONTROL returned invalid bFormatIndex,bFrameIndex=%d,%d", pc->bFormatIndex, pc->bFrameIndex);
 		return nil;
 	}
+	mink = nil;
+	minbw = ~0ULL;
 	bw = (uvlong)GET2(f->wWidth) * GET2(f->wHeight) * fo->desc->bBitsPerPixel * 10e6 / GET4(c->pc.dwFrameInterval);
-	iface = c->iface;
-	mink = -1;
-	for(k = 0; k < nelem(iface->altc); k++){
-		altc = iface->altc[k];
-		if(altc == nil) continue;
-		bw1 = altc->maxpkt * altc->ntds * 8 * 1000 * 8;
-		if(bw1 < bw) continue;
-		if(mink < 0 || bw1 < minbw){
-			minbw = bw1;
-			mink = k;
+	for(;ep != nil; ep = ep->next){
+		if(ep->iface->id != c->iface->id)
+			continue;
+		bw1 = ep->maxpkt * ep->ntds * 8 * 1000 * 8;
+		if(bw1 >= bw) {
+			if(mink == nil || bw1 < minbw){
+				minbw = bw1;
+				mink = ep;
+			}
 		}
 	}
-	if(mink < 0){
+	if(mink == nil){
 		werrstr("device does not have enough bandwidth (need %lld bit/s)", bw);
 		return nil;
 	}
-	if(usbcmd(c->dev, 0x01, Rsetiface, mink, iface->id, nil, 0) < 0){
-		werrstr("selaltc: SET_INTERFACE(%d, %d): %r", iface->id, mink);
+	if(usbcmd(c->dev, 0x01, Rsetiface, mink->iface->alt, mink->iface->id, nil, 0) < 0){
+		werrstr("selaltc: SET_INTERFACE(%d, %d): %r", ep->iface->id, ep->iface->alt);
 		return nil;
 	}
-	return iface->altc[mink];
+	return mink;
 }
 
 static void
@@ -261,8 +259,7 @@
 videoopen(Cam *c, int fr)
 {
 	Dev *d;
-	Altc *altc;
-	Dev *ep;
+	Ep *e;
 	Format *f;
 
 	qlock(&c->qulock);
@@ -282,24 +279,20 @@
 	if(usbcmd(d, 0x21, SET_CUR, VS_PROBE_CONTROL << 8, c->iface->id, (uchar *) &c->pc, sizeof(ProbeControl)) < sizeof(ProbeControl)) goto err;
 	if(usbcmd(d, 0xA1, GET_CUR, VS_PROBE_CONTROL << 8, c->iface->id, (uchar *) &c->pc, sizeof(ProbeControl)) < 0) goto err;
 	if(usbcmd(d, 0x21, SET_CUR, VS_COMMIT_CONTROL << 8, c->iface->id, (uchar *) &c->pc, sizeof(ProbeControl)) < sizeof(ProbeControl)) goto err;
-	altc = selaltc(c, &c->pc);
-	if(altc == nil)
+	e = selaltc(c, &c->pc, d->usb->ep[c->hdr->bEndpointAddress & Epmax]);
+	if(e == nil)
 		goto err;
-	ep = openep(d, c->hdr->bEndpointAddress & 0x7f);
-	if(ep == nil){
+	c->ep = openep(d, e);
+	if(c->ep == nil){
 		usbcmd(d, 0x01, Rsetiface, 0, c->iface->id, nil, 0);
 		goto err;
 	}
-	devctl(ep, "pollival %d", altc->interval);
-	devctl(ep, "uframes 1");
-	devctl(ep, "ntds %d", altc->ntds);
-	devctl(ep, "maxpkt %d", altc->maxpkt);
-	if(opendevdata(ep, OREAD) < 0){
+	devctl(c->ep, "uframes 1");
+	if(opendevdata(c->ep, OREAD) < 0){
 		usbcmd(d, 0x01, Rsetiface, 0, c->iface->id, nil, 0);
-		closedev(ep);
+		closedev(c->ep);
 		goto err;
 	}
-	c->ep = ep;
 	mkframes(c);
 	c->active = 1;
 	c->framemode = fr;
--- a/sys/src/cmd/nusb/disk/disk.c
+++ b/sys/src/cmd/nusb/disk/disk.c
@@ -386,7 +386,6 @@
 	}
 	if(some == 0){
 		dprint(2, "disk: all luns failed\n");
-		devctl(dev, "detach");
 		return -1;
 	}
 	return 0;
@@ -950,46 +949,61 @@
 int
 findendpoints(Ums *ums)
 {
-	Ep *ep;
+	Ep *ep, *ein, *eout;
 	Usbdev *ud;
-	ulong csp, sc;
-	int i, epin, epout;
+	ulong csp;
+	int i;
 
-	epin = epout = -1;
 	ud = dev->usb;
+
+	ein = eout = nil;
 	for(i = 0; i < nelem(ud->ep); i++){
 		if((ep = ud->ep[i]) == nil)
 			continue;
 		csp = ep->iface->csp;
-		sc = Subclass(csp);
-		if(!(Class(csp) == Clstorage && (Proto(csp) == Protobulk || Proto(csp) == Protouas)))
+		if(Class(csp) != Clstorage || Proto(csp) != Protobulk || ep->type != Ebulk)
 			continue;
-		if(sc != Subatapi && sc != Sub8070 && sc != Subscsi)
-			fprint(2, "disk: subclass %#ulx not supported. trying anyway\n", sc);
-		if(ep->type == Ebulk){
-			if(ep->dir == Eboth || ep->dir == Ein)
-				if(epin == -1)
-					epin =  ep->id;
-			if(ep->dir == Eboth || ep->dir == Eout)
-				if(epout == -1)
-					epout = ep->id;
-		}
+		if(ep->dir == Eboth || ep->dir == Ein)
+			if(ein == nil)
+				ein =  ep;
+		if(ep->dir == Eboth || ep->dir == Eout)
+			if(eout == nil)
+				eout = ep;
 	}
-	dprint(2, "disk: ep ids: in %d out %d\n", epin, epout);
-	if(epin == -1 || epout == -1)
+	if(ein != nil && eout != nil)
+		goto Found;
+
+	/* try UAS protocol */
+	ein = eout = nil;
+	for(i = 0; i < nelem(ud->ep); i++){
+		if((ep = ud->ep[i]) == nil)
+			continue;
+		csp = ep->iface->csp;
+		if(Class(csp) != Clstorage || Proto(csp) != Protouas || ep->type != Ebulk)
+			continue;
+		if(ep->dir == Eboth || ep->dir == Ein)
+			if(ein == nil)
+				ein =  ep;
+		if(ep->dir == Eboth || ep->dir == Eout)
+			if(eout == nil)
+				eout = ep;
+	}
+	if(ein == nil || eout == nil)
 		return -1;
-	ums->epin = openep(dev, epin);
+Found:
+	dprint(2, "disk: ep ids: in %d out %d\n", ein->id, eout->id);
+	ums->epin = openep(dev, ein);
 	if(ums->epin == nil){
-		fprint(2, "disk: openep %d: %r\n", epin);
+		fprint(2, "disk: openep %d: %r\n", ein->id);
 		return -1;
 	}
-	if(epout == epin){
+	if(ein == eout){
 		incref(ums->epin);
 		ums->epout = ums->epin;
 	}else
-		ums->epout = openep(dev, epout);
+		ums->epout = openep(dev, eout);
 	if(ums->epout == nil){
-		fprint(2, "disk: openep %d: %r\n", epout);
+		fprint(2, "disk: openep %d: %r\n", eout->id);
 		closedev(ums->epin);
 		return -1;
 	}
@@ -1064,6 +1078,7 @@
 	case 'd':
 		scsidebug(diskdebug);
 		diskdebug++;
+		usbdebug++;
 		break;
 	default:
 		usage();
--- a/sys/src/cmd/nusb/ether/ether.c
+++ b/sys/src/cmd/nusb/ether/ether.c
@@ -586,55 +586,49 @@
 	}
 }
 
-static void
-setalt(Dev *d, int ifcid, int altid)
-{
-	if(usbcmd(d, Rh2d|Rstd|Riface, Rsetiface, altid, ifcid, nil, 0) < 0)
-		dprint(2, "%s: setalt ifc %d alt %d: %r\n", argv0, ifcid, altid);
-}
-
 static int
-ifaceinit(Dev *d, Iface *ifc, int *ei, int *eo)
+ifaceinit(Dev *d, Iface *iface, Ep **ein, Ep **eout)
 {
-	int i, epin, epout;
 	Ep *ep;
+	int i;
 
-	if(ifc == nil)
+	if(iface == nil)
 		return -1;
-
-	epin = epout = -1;
-	for(i = 0; (epin < 0 || epout < 0) && i < nelem(ifc->ep); i++)
-		if((ep = ifc->ep[i]) != nil && ep->type == Ebulk){
-			if(ep->dir == Eboth || ep->dir == Ein)
-				if(epin == -1)
-					epin =  ep->id;
-			if(ep->dir == Eboth || ep->dir == Eout)
-				if(epout == -1)
-					epout = ep->id;
+	*ein = *eout = nil;
+	for(i = 0; i < nelem(iface->ep); i++){
+		ep = iface->ep[i];
+		if(ep == nil)
+			break;
+		if(ep->type != Ebulk)
+			continue;
+		if(ep->dir == Eboth || ep->dir == Ein){
+			if(*ein == nil)
+				*ein = ep;
 		}
-	if(epin == -1 || epout == -1)
-		return -1;
-
-	for(i = 0; i < nelem(ifc->altc); i++)
-		if(ifc->altc[i] != nil)
-			setalt(d, ifc->id, i);
-
-	*ei = epin;
-	*eo = epout;
+		if(ep->dir == Eboth || ep->dir == Eout){
+			if(*eout == nil)
+				*eout = ep;
+		}
+		if(*ein != nil && *eout != nil)
+			goto Found;
+	}
+	return -1;
+Found:
+	if(usbcmd(d, Rh2d|Rstd|Riface, Rsetiface, iface->alt, iface->id, nil, 0) < 0)
+		dprint(2, "%s: setalt ifc %d alt %d: %r\n", argv0, iface->id, iface->alt);
 	return 0;
 }
 
 int
-findendpoints(Dev *d, int *ei, int *eo)
+findendpoints(Dev *d, Ep **ein, Ep **eout)
 {
 	int i, j, ctlid, datid;
-	Iface *ctlif, *datif;
+	Iface *iface, *ctlif, *datif;
 	Usbdev *ud;
 	Desc *desc;
 	Conf *c;
 
 	ud = d->usb;
-	*ei = *eo = -1;
 
 	/* look for union descriptor with ethernet ctrl interface */
 	for(i = 0; i < nelem(ud->ddesc); i++){
@@ -651,16 +645,15 @@
 
 		ctlif = datif = nil;
 		for(j = 0; j < nelem(c->iface); j++){
-			if(c->iface[j] == nil)
-				continue;
-			if(c->iface[j]->id == ctlid)
-				ctlif = c->iface[j];
-			if(c->iface[j]->id == datid)
-				datif = c->iface[j];
-
+			for(iface = c->iface[j]; iface != nil; iface = iface->next){
+				if(iface->id == ctlid)
+					ctlif = iface;
+				if(iface->id == datid)
+					datif = iface;
+			}
 			if(datif != nil && ctlif != nil){
 				if(Subclass(ctlif->csp) == Scether)
-					if(ifaceinit(d, datif, ei, eo) != -1)
+					if(ifaceinit(d, datif, ein, eout) != -1)
 						return 0;
 				break;
 			}
@@ -668,12 +661,19 @@
 	}
 
 	/* try any other one that seems to be ok */
-	for(i = 0; i < nelem(ud->conf); i++)
-		if((c = ud->conf[i]) != nil)
-			for(j = 0; j < nelem(c->iface); j++)
-				if(ifaceinit(d,c->iface[j],ei,eo) != -1)
-					return 0;
+	for(i = 0; i < nelem(ud->conf); i++){
+		if((c = ud->conf[i]) != nil){
+			for(j = 0; j < nelem(c->iface); j++){
+				for(datif = c->iface[j]; datif != nil; datif = datif->next){
+					if(ifaceinit(d, datif, ein, eout) != -1)
+						return 0;
+				}
+			}
+		}
+	}
 
+	*ein = *eout = nil;
+
 	return -1;
 }
 
@@ -909,7 +909,8 @@
 threadmain(int argc, char **argv)
 {
 	char s[64], *t;
-	int et, ei, eo;
+	int et;
+	Ep *ein, *eout;
 
 	fmtinstall('E', eipfmt);
 
@@ -948,7 +949,8 @@
 
 	if((epctl = getdev(*argv)) == nil)
 		sysfatal("getdev: %r");
-	if(findendpoints(epctl, &ei, &eo) < 0)
+
+	if(findendpoints(epctl, &ein, &eout) < 0)
 		sysfatal("no endpoints found");
 
 	werrstr("");
@@ -957,14 +959,14 @@
 	if(epreceive == nil || eptransmit == nil)
 		sysfatal("bug in init");
 
-	if((epin = openep(epctl, ei)) == nil)
+	if((epin = openep(epctl, ein)) == nil)
 		sysfatal("openep: %r");
-	if(ei == eo){
+	if(ein == eout){
 		incref(epin);
 		epout = epin;
 		opendevdata(epin, ORDWR);
 	} else {
-		if((epout = openep(epctl, eo)) == nil)
+		if((epout = openep(epctl, eout)) == nil)
 			sysfatal("openep: %r");
 		opendevdata(epin, OREAD);
 		opendevdata(epout, OWRITE);
--- a/sys/src/cmd/nusb/joy/joy.c
+++ b/sys/src/cmd/nusb/joy/joy.c
@@ -199,13 +199,12 @@
 }
 
 static int
-setproto(KDev *f, int eid)
+setproto(KDev *f, Iface *iface)
 {
-	int id, proto;
+	int proto;
 
 	proto = Bootproto;
-	id = f->dev->usb->ep[eid]->iface->id;
-	f->nrep = usbcmd(f->dev, Rd2h|Rstd|Riface, Rgetdesc, Dreport<<8, id, 
+	f->nrep = usbcmd(f->dev, Rd2h|Rstd|Riface, Rgetdesc, Dreport<<8, iface->id, 
 		f->rep, sizeof(f->rep));
 	if(f->nrep > 0){
 		if(debug){
@@ -227,10 +226,10 @@
 	 * if a HID's subclass code is 1 (boot mode), it will support
 	 * setproto, otherwise it is not guaranteed to.
 	 */
-	if(Subclass(f->dev->usb->ep[eid]->iface->csp) != 1)
+	if(Subclass(iface->csp) != 1)
 		return 0;
 
-	return usbcmd(f->dev, Rh2d|Rclass|Riface, Setproto, proto, id, nil, 0);
+	return usbcmd(f->dev, Rh2d|Rclass|Riface, Setproto, proto, iface->id, nil, 0);
 }
 
 static void
@@ -387,11 +386,11 @@
 	kd = emallocz(sizeof(KDev), 1);
 	incref(d);
 	kd->dev = d;
-	if(setproto(kd, ep->id) < 0){
+	if(setproto(kd, ep->iface) < 0){
 		fprint(2, "%s: %s: setproto: %r\n", argv0, d->dir);
 		goto Err;
 	}
-	kd->ep = openep(kd->dev, ep->id);
+	kd->ep = openep(kd->dev, ep);
 	if(kd->ep == nil){
 		fprint(2, "%s: %s: openep %d: %r\n", argv0, d->dir, ep->id);
 		goto Err;
--- a/sys/src/cmd/nusb/kb/kb.c
+++ b/sys/src/cmd/nusb/kb/kb.c
@@ -323,12 +323,10 @@
 }
 
 static int
-setproto(Hiddev *f, int eid)
+setproto(Hiddev *f, Iface *iface)
 {
 	int proto;
-	Iface *iface;
 
-	iface = f->dev->usb->ep[eid]->iface;
 
 	/*
 	 * DWC OTG controller misses some split transaction inputs.
@@ -433,7 +431,7 @@
 		qlock(&l);
 	}
 Resetdone:
-	if(setproto(f, f->ep->id) < 0){
+	if(setproto(f, f->ep->ep->iface) < 0){
 		rerrstr(err, sizeof(err));
 		qunlock(&l);
 		hdfatal(f, err);
@@ -883,11 +881,11 @@
 	f->kinfd = -1;
 	incref(d);
 	f->dev = d;
-	if(setproto(f, ep->id) < 0){
+	if(setproto(f, ep->iface) < 0){
 		fprint(2, "%s: %s: setproto: %r\n", argv0, d->dir);
 		goto Err;
 	}
-	f->ep = openep(f->dev, ep->id);
+	f->ep = openep(f->dev, ep);
 	if(f->ep == nil){
 		fprint(2, "%s: %s: openep %d: %r\n", argv0, d->dir, ep->id);
 		goto Err;
--- a/sys/src/cmd/nusb/lib/dev.c
+++ b/sys/src/cmd/nusb/lib/dev.c
@@ -24,63 +24,47 @@
 }
 
 Dev*
-openep(Dev *d, int id)
+openep(Dev *d, Ep *ep)
 {
 	char *mode;	/* How many modes? */
-	Ep *ep;
-	Altc *ac;
 	Dev *epd;
-	Usbdev *ud;
 	char name[40];
 
-	if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0)
-		return nil;
 	if(d->cfd < 0 || d->usb == nil){
 		werrstr("device not configured");
 		return nil;
 	}
-	ud = d->usb;
-	if(id < 0 || id >= nelem(ud->ep) || ud->ep[id] == nil){
-		werrstr("bad enpoint number");
-		return nil;
-	}
-	ep = ud->ep[id];
-	snprint(name, sizeof(name), "/dev/usb/ep%d.%d", d->id, id);
+	snprint(name, sizeof(name), "/dev/usb/ep%d.%d", d->id, ep->id);
 	if(access(name, AEXIST) == 0){
 		dprint(2, "%s: %s already exists; trying to open\n", argv0, name);
 		epd = opendev(name);
-		if(epd != nil){
-			epd->id = id;
-			epd->maxpkt = ep->maxpkt;	/* guess */
+	} else {
+		mode = "rw";
+		if(ep->dir == Ein)
+			mode = "r";
+		if(ep->dir == Eout)
+			mode = "w";
+		if(devctl(d, "new %d %d %s", ep->id, ep->type, mode) < 0){
+			dprint(2, "%s: %s: new: %r\n", argv0, d->dir);
+			return nil;
 		}
-		return epd;
+		epd = opendev(name);
 	}
-	mode = "rw";
-	if(ep->dir == Ein)
-		mode = "r";
-	if(ep->dir == Eout)
-		mode = "w";
-	if(devctl(d, "new %d %d %s", id, ep->type, mode) < 0){
-		dprint(2, "%s: %s: new: %r\n", argv0, d->dir);
-		return nil;
-	}
-	epd = opendev(name);
 	if(epd == nil)
 		return nil;
-	epd->id = id;
+	epd->ep = ep;
+	epd->id = ep->id;
 	if(devctl(epd, "maxpkt %d", ep->maxpkt) < 0)
 		fprint(2, "%s: %s: openep: maxpkt: %r\n", argv0, epd->dir);
 	else
 		dprint(2, "%s: %s: maxpkt %d\n", argv0, epd->dir, ep->maxpkt);
 	epd->maxpkt = ep->maxpkt;
-	ac = ep->iface->altc[0];
 	if(ep->ntds > 1 && devctl(epd, "ntds %d", ep->ntds) < 0)
 		fprint(2, "%s: %s: openep: ntds: %r\n", argv0, epd->dir);
 	else
 		dprint(2, "%s: %s: ntds %d\n", argv0, epd->dir, ep->ntds);
-
-	if(ac != nil && (ep->type == Eintr || ep->type == Eiso) && ac->interval != 0)
-		if(devctl(epd, "pollival %d", ac->interval) < 0)
+	if((ep->type == Eintr || ep->type == Eiso) && ep->pollival != 0)
+		if(devctl(epd, "pollival %d", ep->pollival) < 0)
 			fprint(2, "%s: %s: openep: pollival: %r\n", argv0, epd->dir);
 	return epd;
 }
@@ -91,8 +75,6 @@
 	Dev *d;
 	int l;
 
-	if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0)
-		return nil;
 	d = emallocz(sizeof(Dev), 1);
 	incref(d);
 
@@ -109,6 +91,7 @@
 	strcpy(d->dir+l, "/ctl");
 	d->cfd = open(d->dir, ORDWR|OCEXEC);
 	d->dir[l] = 0;
+	d->ep = nil;
 	d->id = nameid(fn);
 	if(d->cfd < 0){
 		werrstr("can't open endpoint %s: %r", d->dir);
@@ -173,8 +156,13 @@
 {
 	Ep *ep;
 
-	d->ep[id] = ep = emallocz(sizeof(Ep), 1);
+	ep = emallocz(sizeof(Ep), 1);
 	ep->id = id;
+
+	id &= Epmax;
+	ep->next = d->ep[id];
+	d->ep[id] = ep;
+
 	return ep;
 }
 
@@ -258,6 +246,7 @@
 	ep0->dir = Eboth;
 	ep0->type = Econtrol;
 	ep0->maxpkt = d->maxpkt = 8;		/* a default */
+	d->ep = ep0;
 	nr = parsedev(d, buf, nr);
 	if(nr >= 0){
 		d->usb->vendor = loaddevstr(d, d->usb->vsid);
@@ -289,17 +278,17 @@
 static void
 closeconf(Conf *c)
 {
+	Iface *f;
 	int i;
-	int a;
 
 	if(c == nil)
 		return;
-	for(i = 0; i < nelem(c->iface); i++)
-		if(c->iface[i] != nil){
-			for(a = 0; a < nelem(c->iface[i]->altc); a++)
-				free(c->iface[i]->altc[a]);
-			free(c->iface[i]);
+	for(i = 0; i < nelem(c->iface); i++){
+		while((f = c->iface[i]) != nil) {
+			c->iface[i] = f->next;
+			free(f);
 		}
+	}
 	free(c);
 }
 
@@ -307,6 +296,7 @@
 closedev(Dev *d)
 {
 	int i;
+	Ep *ep;
 	Usbdev *ud;
 
 	if(d==nil || decref(d) != 0)
@@ -327,11 +317,14 @@
 		free(ud->vendor);
 		free(ud->product);
 		free(ud->serial);
-		for(i = 0; i < nelem(ud->ep); i++)
-			free(ud->ep[i]);
+		for(i = 0; i < nelem(ud->ep); i++){
+			while((ep = ud->ep[i]) != nil){
+				ud->ep[i] = ep->next;
+				free(ep);
+			}
+		}
 		for(i = 0; i < nelem(ud->ddesc); i++)
 			free(ud->ddesc[i]);
-
 		for(i = 0; i < nelem(ud->conf); i++)
 			closeconf(ud->conf[i]);
 		free(ud);
--- a/sys/src/cmd/nusb/lib/dump.c
+++ b/sys/src/cmd/nusb/lib/dump.c
@@ -52,24 +52,12 @@
 fmtprintiface(Fmt *f, Iface *i)
 {
 	int	j;
-	Altc	*a;
 	Ep	*ep;
 	char	*eds, *ets;
 
-	fmtprint(f, "\t\tiface csp %s.%uld.%uld\n",
-		classname(Class(i->csp)), Subclass(i->csp), Proto(i->csp));
-	for(j = 0; j < Naltc; j++){
-		a=i->altc[j];
-		if(a == nil)
-			break;
-		fmtprint(f, "\t\t  alt %d attr %d ival %d",
-			j, a->attrib, a->interval);
-		if(a->aux != nil)
-			fmtprint(f, " devspec %p\n", a->aux);
-		else
-			fmtprint(f, "\n");
-	}
-	for(j = 0; j < Nep; j++){
+	fmtprint(f, "\t\tiface csp %s.%uld.%uld alt %d\n",
+		classname(Class(i->csp)), Subclass(i->csp), Proto(i->csp), i->alt);
+	for(j = 0; j < nelem(i->ep); j++){
 		ep = i->ep[j];
 		if(ep == nil)
 			break;
@@ -79,9 +67,9 @@
 		if(ep->type <= nelem(etype))
 			ets = etype[ep->type];
 		fmtprint(f, "\t\t  ep id %d addr %d dir %s type %s"
-			" itype %d maxpkt %d ntds %d\n",
-			ep->id, ep->addr, eds, ets, ep->isotype,
-			ep->maxpkt, ep->ntds);
+			"  attrib %x maxpkt %d ntds %d pollival %d\n",
+			ep->id, ep->id & Epmax, eds, ets, ep->attrib,
+			ep->maxpkt, ep->ntds, ep->pollival);
 	}
 }
 
@@ -90,16 +78,16 @@
 {
 	int i;
 	Conf *c;
+	Iface *fc;
 	char *hd;
 
 	c = d->conf[ci];
 	fmtprint(f, "\tconf: cval %d attrib %x %d mA\n",
 		c->cval, c->attrib, c->milliamps);
-	for(i = 0; i < Niface; i++)
-		if(c->iface[i] == nil)
-			break;
-		else
-			fmtprintiface(f, c->iface[i]);
+	for(i = 0; i < Niface; i++){
+		for(fc = c->iface[i]; fc != nil; fc = fc->next)
+			fmtprintiface(f, fc);
+	}
 	for(i = 0; i < Nddesc; i++)
 		if(d->ddesc[i] == nil)
 			break;
--- a/sys/src/cmd/nusb/lib/parse.c
+++ b/sys/src/cmd/nusb/lib/parse.c
@@ -11,7 +11,6 @@
 	char *hd;
 
 	d = xd->usb;
-	assert(d != nil);
 	dd = (DDev*)b;
 	if(usbdebug>1){
 		hd = hexstr(b, Ddevlen);
@@ -50,49 +49,47 @@
 }
 
 static int
-parseiface(Usbdev *d, Conf *c, uchar *b, int n, Iface **ipp, Altc **app)
+parseiface(Usbdev *d, Conf *c, uchar *b, int n, Iface **ipp)
 {
-	int class, subclass, proto;
-	int ifid, altid;
+	int class, subclass, proto, alt, id;
+	ulong csp;
 	DIface *dip;
 	Iface *ip;
 
-	assert(d != nil && c != nil);
 	if(n < Difacelen){
 		werrstr("short interface descriptor");
 		return -1;
 	}
 	dip = (DIface *)b;
-	ifid = dip->bInterfaceNumber;
-	if(ifid < 0 || ifid >= nelem(c->iface)){
-		werrstr("bad interface number %d", ifid);
-		return -1;
-	}
-	if(c->iface[ifid] == nil)
-		c->iface[ifid] = emallocz(sizeof(Iface), 1);
-	ip = c->iface[ifid];
 	class = dip->bInterfaceClass;
 	subclass = dip->bInterfaceSubClass;
 	proto = dip->bInterfaceProtocol;
-	ip->csp = CSP(class, subclass, proto);
-	if(ip->csp == 0)
-		ip->csp = d->csp;
-	if(d->csp == 0)				/* use csp from 1st iface */
-		d->csp = ip->csp;		/* if device has none */
+	csp = CSP(class, subclass, proto);
+	if(csp == 0)
+		csp = d->csp;
 	if(d->class == 0)
 		d->class = class;
-	ip->id = ifid;
-	if(c == d->conf[0] && ifid == 0)	/* ep0 was already there */
-		d->ep[0]->iface = ip;
-	altid = dip->bAlternateSetting;
-	if(altid < 0 || altid >= nelem(ip->altc)){
-		werrstr("bad alternate conf. number %d", altid);
+	alt = dip->bAlternateSetting;
+	id = dip->bInterfaceNumber;
+	if(id < 0 || id >= nelem(c->iface)){
+		werrstr("bad interface number %d", id);
 		return -1;
 	}
-	if(ip->altc[altid] == nil)
-		ip->altc[altid] = emallocz(sizeof(Altc), 1);
+	for(ip = c->iface[id]; ip != nil; ip = ip->next)
+		if(ip->csp == csp && ip->alt == alt)
+			goto Found;
+	ip = emallocz(sizeof(Iface), 1);
+	ip->id = id;
+	ip->csp = csp;
+	ip->alt = alt;
+	ip->next = c->iface[id];
+	c->iface[id] = ip;
+	if(d->csp == 0)			/* use csp from 1st iface */
+		d->csp = ip->csp;	/* if device has none */
+	if(c == d->conf[0] && id == 0)	/* ep0 was already there */
+		d->ep[0]->iface = ip;
+Found:
 	*ipp = ip;
-	*app = ip->altc[altid];
 	return Difacelen;
 }
 
@@ -99,13 +96,12 @@
 extern Ep* mkep(Usbdev *, int);
 
 static int
-parseendpt(Usbdev *d, Conf *c, Iface *ip, Altc *altc, uchar *b, int n, Ep **epp)
+parseendpt(Usbdev *d, Conf *c, Iface *ip, uchar *b, int n, Ep **epp)
 {
-	int i, dir, epid, type, addr;
-	Ep *ep;
+	int addr, dir, type, id, i;
 	DEp *dep;
+	Ep *ep;
 
-	assert(d != nil && c != nil && ip != nil && altc != nil);
 	if(n < Deplen){
 		werrstr("short endpoint descriptor");
 		return -1;
@@ -118,54 +114,67 @@
 		dir = Ein;
 	else
 		dir = Eout;
-	epid = addr & 0xF;	/* default map to 0..15 */
-	assert(epid < nelem(d->ep));
-	ep = d->ep[epid];
-	if(ep == nil){
-		ep = mkep(d, epid);
-		ep->dir = dir;
-	}else if((ep->addr & 0x80) != (addr & 0x80)){
-		if(ep->type == type && type != Eiso)
-			ep->dir = Eboth;
-		else {
-			/*
-			 * resolve conflict when same endpoint number
-			 * is used for different input and output types.
-			 * map input endpoint to 16..31 and output to 0..15.
-			 */
-			ep->id = ((ep->addr & 0x80) != 0)<<4 | (ep->addr & 0xF);
-			d->ep[ep->id] = ep;
-			epid = ep->id ^ 0x10;
-			ep = mkep(d, epid);
-			ep->dir = dir;
+
+	/*
+	 * the endpoint id is created once, setting
+	 * type and direction, meaning the endpoint's
+	 * id must be unique for (type, dir) relative
+	 * to all other potential endpoints from
+	 * interfaces/altsettings on this device.
+	 *
+	 * the low Epmax bits of the id contains the
+	 * endpoint address that must be preserved.
+	 */
+	id = addr & Epmax;
+Again:
+	for(ep = d->ep[id & Epmax]; ep != nil; ep = ep->next){
+		if(ep->id != id)
+			continue;
+		if(ep->type != type){
+			id += Epmax+1;
+			goto Again;
 		}
+		if(ep->dir == dir)
+			break;
+		/*
+		 * Ein/Eout endpoints from the same
+		 * interface/altsetting can be merged
+		 * into one. (except for iso).
+		 */
+		if(ep->iface != ip || type == Eiso){
+			id += Epmax+1;
+			goto Again;
+		}
+		dir = Eboth;
+		break;
 	}
+
+	if(ep == nil || ep->iface != ip)
+		ep = mkep(d, id);
+
+	ep->dir = dir;
+	ep->type = type;
+	ep->iface = ip;
+	ep->conf = c;
 	ep->maxpkt = GET2(dep->wMaxPacketSize);
 	ep->ntds = 1 + ((ep->maxpkt >> 11) & 3);
 	ep->maxpkt &= 0x7FF;
-	ep->addr = addr;
-	ep->type = type;
-	ep->isotype = (dep->bmAttributes>>2) & 0x03;
- 	ep->isousage = (dep->bmAttributes>>4) & 0x03;
-	ep->conf = c;
-	ep->iface = ip;
-	if(ep->type != Eiso || ep->isousage == Edata || ep->isousage == Eimplicit){
-		altc->attrib = dep->bmAttributes;
-		altc->interval = dep->bInterval;
-		altc->maxpkt = ep->maxpkt;
-		altc->ntds = ep->ntds;
-	}
-	for(i = 0; i < nelem(ip->ep); i++)
-		if(ip->ep[i] == nil)
+	ep->attrib = dep->bmAttributes;
+	ep->pollival = dep->bInterval;
+
+	for(i = 0; i < nelem(ip->ep); i++){
+		if(ip->ep[i] == nil){
+			ip->ep[i] = ep;
 			break;
-	if(i == nelem(ip->ep)){
-		werrstr("parseendpt: bug: too many end points on interface "
-			"with csp %#lux", ip->csp);
-		fprint(2, "%s: %r\n", argv0);
-		return -1;
+		}
+		if(ip->ep[i] == ep)
+			break;
 	}
-	*epp = ip->ep[i] = ep;
-	return Dep;
+	if(i >= nelem(ip->ep))
+		fprint(2, "%s: parseendpt: too many endpoints in interface", argv0);
+
+	*epp = ep;
+	return Deplen;
 }
 
 static char*
@@ -189,14 +198,11 @@
 	int	len, nd, tot;
 	Iface	*ip;
 	Ep 	*ep;
-	Altc	*altc;
 	char	*hd;
 
-	assert(d != nil && c != nil);
 	tot = 0;
 	ip = nil;
 	ep = nil;
-	altc = nil;
 	for(nd = 0; nd < nelem(d->ddesc); nd++)
 		if(d->ddesc[nd] == nil)
 			break;
@@ -216,23 +222,23 @@
 			ddprint(2, "%s\tparsedesc: %r", argv0);
 			break;
 		case Diface:
-			if(parseiface(d, c, b, n, &ip, &altc) < 0){
+			if(parseiface(d, c, b, n, &ip) < 0){
 				ddprint(2, "%s\tparsedesc: %r\n", argv0);
 				return -1;
 			}
 			break;
 		case Dep:
-			if(ip == nil || altc == nil){
+			if(ip == nil){
 				werrstr("unexpected endpoint descriptor");
 				break;
 			}
-			if(parseendpt(d, c, ip, altc, b, n, &ep) < 0){
+			if(parseendpt(d, c, ip, b, n, &ep) < 0){
 				ddprint(2, "%s\tparsedesc: %r\n", argv0);
 				return -1;
 			}
 			break;
 		default:
-			if(nd == nelem(d->ddesc)){
+			if(nd >= nelem(d->ddesc)){
 				fprint(2, "%s: parsedesc: too many "
 					"device-specific descriptors for device"
 					" %s %s\n",
@@ -242,7 +248,6 @@
 			d->ddesc[nd] = emallocz(sizeof(Desc)+b[0], 0);
 			d->ddesc[nd]->iface = ip;
 			d->ddesc[nd]->ep = ep;
-			d->ddesc[nd]->altc = altc;
 			d->ddesc[nd]->conf = c;
 			memmove(&d->ddesc[nd]->data, b, len);
 			++nd;
@@ -262,7 +267,6 @@
 	int	nr;
 	char	*hd;
 
-	assert(d != nil && c != nil);
 	dc = (DConf*)b;
 	if(usbdebug>1){
 		hd = hexstr(b, Dconflen);
--- a/sys/src/cmd/nusb/lib/usb.h
+++ b/sys/src/cmd/nusb/lib/usb.h
@@ -1,4 +1,3 @@
-typedef struct Altc Altc;
 typedef struct Conf Conf;
 typedef struct DConf DConf;
 typedef struct DDesc DDesc;
@@ -12,17 +11,17 @@
 typedef struct Usbdev Usbdev;
 
 enum {
-	/* fundamental constants */
-	Nep	= 256,	/* max. endpoints per usb device & per interface */
-
 	/* tunable parameters */
+	Nep	= 64,	/* max. endpoints per interface */
 	Nconf	= 16,	/* max. configurations per usb device */
 	Nddesc	= 8*Nep, /* max. device-specific descriptors per usb device */
 	Niface	= 16,	/* max. interfaces per configuration */
-	Naltc	= 256,	/* max. alt configurations per interface */
 	Uctries	= 4,	/* no. of tries for usbcmd */
 	Ucdelay	= 50,	/* delay before retrying */
 
+	/* fundamental constants */
+	Epmax	= 0xF,		/* endpoint address mask */
+
 	/* request type */
 	Rh2d	= 0<<7,		/* host to device */
 	Rd2h	= 1<<7,		/* device to host */ 
@@ -110,17 +109,6 @@
 	Ebulk = 2,
 	Eintr = 3,
 
-	/* endpoint isotype */
-	Eunknown = 0,
-	Easync = 1,
-	Eadapt = 2,
-	Esync = 3,
-
-	/* endpoint isousage */
-	Edata = 0,
-	Efeedback = 1,
-	Eimplicit = 2,
-
 	/* config attrib */
 	Cbuspowered = 1<<7,
 	Cselfpowered = 1<<6,
@@ -182,6 +170,7 @@
 	int	depth;		/* hub depth for usb3 hubs */
 	int	maxpkt;		/* cached from usb description */
 	Usbdev*	usb;		/* USB description */
+	Ep*	ep;		/* endpoint from epopen() */
 	void*	aux;		/* for the device driver */
 	char*	hname;		/* hash name, unique for device */
 };
@@ -205,40 +194,38 @@
 	int	class;		/* from descriptor */
 	int	nconf;		/* from descriptor */
 	Conf*	conf[Nconf];	/* configurations */
-	Ep*	ep[Nep];	/* all endpoints in device */
+	Ep*	ep[Epmax+1];	/* all endpoints in device (chained), indexed by address */
 	Desc*	ddesc[Nddesc];	/* (raw) device specific descriptors */
 };
 
 struct Ep
 {
-	uchar	addr;		/* endpt address, 0-15 (|0x80 if Ein) */
-	uchar	dir;		/* direction, Ein/Eout */
+	Iface*	iface;		/* the endpoint belongs to */
+	Conf*	conf;		/* the endpoint belongs to */
+
+	int	id;		/* endpoint number: (id & Epmax) == endpoint address */
+	uchar	dir;		/* direction, Ein/Eout/Eboth */
 	uchar	type;		/* Econtrol, Eiso, Ebulk, Eintr */
-	uchar	isotype;	/* Eunknown, Easync, Eadapt, Esync */
-	uchar	isousage;	/* Edata, Efeedback, Eimplicit */
-	
-	int	id;
+
+	int	attrib;
+	int	pollival;
 	int	maxpkt;		/* max. packet size */
 	int	ntds;		/* nb. of Tds per µframe */
-	Conf*	conf;		/* the endpoint belongs to */
-	Iface*	iface;		/* the endpoint belongs to */
-};
 
-struct Altc
-{
-	int	attrib;
-	int	interval;
-	int	maxpkt;
-	int	ntds;
+	/* chain of endpoints with same address (used in different interfaces/altsettings) */
+	Ep*	next;
+
 	void*	aux;		/* for the driver program */
 };
 
 struct Iface
 {
-	int 	id;		/* interface number */
+	int	id;		/* interface number */
+	int	alt;		/* altsetting for this interface */
 	ulong	csp;		/* USB class/subclass/proto */
-	Altc*	altc[Naltc];
-	Ep*	ep[Nep];
+	Iface*	next;		/* chain of interfaces of different altsettings */
+	Ep*	ep[Nep];	/* consecutive array of endpoints in this interface (not including ep0) */
+
 	void*	aux;		/* for the driver program */
 };
 
@@ -269,7 +256,6 @@
 	Conf*	conf;		/* where this descriptor was read */
 	Iface*	iface;		/* last iface before desc in conf. */
 	Ep*	ep;		/* last endpt before desc in conf. */
-	Altc*	altc;		/* last alt.c. before desc in conf. */
 	DDesc	data;		/* unparsed standard USB descriptor */
 };
 
@@ -360,7 +346,7 @@
 char*	loaddevstr(Dev *d, int sid);
 Dev*	opendev(char *fn);
 int	opendevdata(Dev *d, int mode);
-Dev*	openep(Dev *d, int id);
+Dev*	openep(Dev *d, Ep *e);
 int	parseconf(Usbdev *d, Conf *c, uchar *b, int n);
 int	parsedesc(Usbdev *d, Conf *c, uchar *b, int n);
 int	parsedev(Dev *xd, uchar *b, int n);
@@ -369,5 +355,3 @@
 Dev*	getdev(char *devid);
 
 extern int usbdebug;	/* more messages for bigger values */
-
-
--- a/sys/src/cmd/nusb/ptp/ptp.c
+++ b/sys/src/cmd/nusb/ptp/ptp.c
@@ -958,7 +958,7 @@
 }
 
 static int
-findendpoints(Dev *d, int *epin, int *epout, int *epint)
+findendpoints(Dev *d, Ep **epin, Ep **epout, Ep **epint)
 {
 	int i;
 	Ep *ep;
@@ -965,22 +965,22 @@
 	Usbdev *ud;
 
 	ud = d->usb;
-	*epin = *epout = *epint = -1;
+	*epin = *epout = *epint = nil;
 	for(i=0; i<nelem(ud->ep); i++){
 		if((ep = ud->ep[i]) == nil)
 			continue;
-		if(ep->type == Eintr && *epint == -1)
-			*epint = ep->id;
+		if(ep->type == Eintr && *epint == nil)
+			*epint = ep;
 		if(ep->type != Ebulk)
 			continue;
 		if(ep->dir == Eboth || ep->dir == Ein)
-			if(*epin == -1)
-				*epin =  ep->id;
+			if(*epin == nil)
+				*epin =  ep;
 		if(ep->dir == Eboth || ep->dir == Eout)
-			if(*epout == -1)
-				*epout = ep->id;
+			if(*epout == nil)
+				*epout = ep;
 	}
-	if(*epin >= 0 && *epout >= 0)
+	if(*epin != nil && *epout != nil)
 		return 0;
 	return -1;
 }
@@ -1009,7 +1009,7 @@
 threadmain(int argc, char **argv)
 {
 	char name[64], desc[64];
-	int epin, epout, epint;
+	Ep *epin, *epout, *epint;
 	Dev *d;
 
 	ARGBEGIN {
--- a/sys/src/cmd/nusb/serial/ftdi.c
+++ b/sys/src/cmd/nusb/serial/ftdi.c
@@ -1385,7 +1385,6 @@
 		chanclose(p->w4data);
 	if(p->gotdata != nil)
 		chanclose(p->gotdata);
-	devctl(ser->dev, "detach");
 	closedev(ser->dev);
 }
 
@@ -1426,7 +1425,6 @@
 	}
 
 	shutdownchan(c);
-	devctl(ser->dev, "detach");
 	closedev(ser->dev);
 }
 
--- a/sys/src/cmd/nusb/serial/serial.c
+++ b/sys/src/cmd/nusb/serial/serial.c
@@ -22,9 +22,7 @@
 	Serialport *p;
 	int i;
 
-	dsprint(2, "serial: fatal error, detaching\n");
-	devctl(ser->dev, "detach");
-
+	dsprint(2, "serial: fatal error, closing\n");
 	for(i = 0; i < ser->nifcs; i++){
 		p = &ser->p[i];
 		if(p->w4data != nil)
@@ -564,7 +562,7 @@
 }
 
 static int
-openeps(Serialport *p, int epin, int epout, int epintr)
+openeps(Serialport *p, Ep *epin, Ep *epout, Ep *epintr)
 {
 	Serial *ser;
 
@@ -571,7 +569,7 @@
 	ser = p->s;
 	p->epin = openep(ser->dev, epin);
 	if(p->epin == nil){
-		fprint(2, "serial: openep %d: %r\n", epin);
+		fprint(2, "serial: openep %d: %r\n", epin->id);
 		return -1;
 	}
 	if(epin == epout){
@@ -580,7 +578,7 @@
 	} else
 		p->epout = openep(ser->dev, epout);
 	if(p->epout == nil){
-		fprint(2, "serial: openep %d: %r\n", epout);
+		fprint(2, "serial: openep %d: %r\n", epout->id);
 		closedev(p->epin);
 		return -1;
 	}
@@ -587,7 +585,7 @@
 	if(ser->hasepintr){
 		p->epintr = openep(ser->dev, epintr);
 		if(p->epintr == nil){
-			fprint(2, "serial: openep %d: %r\n", epintr);
+			fprint(2, "serial: openep %d: %r\n", epintr->id);
 			closedev(p->epin);
 			closedev(p->epout);
 			return -1;
@@ -619,34 +617,30 @@
 static int
 findendpoints(Serial *ser, int ifc)
 {
-	int i, epin, epout, epintr;
-	Ep *ep, **eps;
+	Ep **eps, *ep, *epin, *epout, *epintr;
+	int i;
 
-	epintr = epin = epout = -1;
-
 	/*
 	 * interfc 0 means start from the start which is equiv to
 	 * iterate through endpoints probably, could be done better
 	 */
 	eps = ser->dev->usb->conf[0]->iface[ifc]->ep;
-
+	epintr = epin = epout = nil;
 	for(i = 0; i < Nep; i++){
 		if((ep = eps[i]) == nil)
-			continue;
-		if(ser->hasepintr && ep->type == Eintr &&
-		    ep->dir == Ein && epintr == -1)
-			epintr = ep->id;
+			break;
+		if(ser->hasepintr && ep->type == Eintr && ep->dir == Ein && epintr == nil)
+			epintr = ep;
 		if(ep->type == Ebulk){
-			if((ep->dir == Ein || ep->dir == Eboth) && epin == -1)
-				epin = ep->id;
-			if((ep->dir == Eout || ep->dir == Eboth) && epout == -1)
-				epout = ep->id;
+			if((ep->dir == Ein || ep->dir == Eboth) && epin == nil)
+				epin = ep;
+			if((ep->dir == Eout || ep->dir == Eboth) && epout == nil)
+				epout = ep;
 		}
 	}
-	dprint(2, "serial[%d]: ep ids: in %d out %d intr %d\n", ifc, epin, epout, epintr);
-	if(epin == -1 || epout == -1 || (ser->hasepintr && epintr == -1))
+	if(epin == nil || epout == nil || (ser->hasepintr && epintr == nil))
 		return -1;
-
+	dprint(2, "serial[%d]: ep ids: in %d out %d intr %d\n", ifc, epin->id, epout->id, epintr!=nil?epintr->id:-1);
 	if(openeps(&ser->p[ifc], epin, epout, epintr) < 0)
 		return -1;
 
--- a/sys/src/cmd/nusb/usbd/usbd.c
+++ b/sys/src/cmd/nusb/usbd/usbd.c
@@ -411,11 +411,18 @@
 		return 0;
 	}
 
-	/* create all endpoint files for default conf #1 */
+	/*
+	 * create all endpoint files for default conf #1
+	 * but do not create endpoints that have an altsetting.
+	 */
 	for(id=1; id<nelem(d->usb->ep); id++){
 		Ep *ep = d->usb->ep[id];
-		if(ep != nil && ep->conf != nil && ep->conf->cval == 1){
-			Dev *epd = openep(d, id);
+		if(ep != nil
+		&& ep->next == nil
+		&& ep->conf != nil
+		&& ep->conf->cval == 1
+		&& ep->iface->alt == 0){
+			Dev *epd = openep(d, ep);
 			if(epd != nil)
 				closedev(epd);
 		}