Browse Source

all usb host drivers: fix descriptor alignment
begin work on usb2, usbkeyb..

Change-Id: I54648eed26f6589db3ce3e0694bfbbea7f94d6c2

Aki Nyrhinen 8 years ago
parent
commit
a2d9363bff

+ 7 - 3
sys/src/9/k10/k8cpu.json

@@ -27,7 +27,7 @@
 				"rtc",
 				"kprof",
 				"pmc",
-			        "regress",
+				"regress",
 				"segment",
 				"acpi",
 				"zp",
@@ -36,7 +36,8 @@
 				"ether",
 				"ip",
 				"pci",
-				"uart"
+				"uart",
+				"usb"
 			],
 			"Ip": [
 				"tcp",
@@ -54,7 +55,10 @@
 				"ether8139",
 				"ethermedium",
 				"loopbackmedium",
-				"netdevmedium"
+				"netdevmedium",
+				"usbuhci",
+				"usbohci",
+				"usbehci"
 			],
 			"Sd": [
 				"sdiahci"

+ 13 - 9
sys/src/9/k10/usbohci.c

@@ -243,14 +243,17 @@ struct Td
 	uint32_t	be;
 	uint16_t	offsets[8];	/* used by Iso Tds only */
 
+	uint32_t	nbytes;		/* bytes in this Td */
+	uint32_t	cbp0;		/* initial value for cbp */
+	uint32_t	last;		/* true for last Td in Qio */
+	uint32_t	_160[5];
+
 	Td*	next;		/* in free or Ed tds list */
 	Td*	anext;		/* in avail td list (iso) */
 	Ep*	ep;		/* using this Td for I/O */
 	Qio*	io;		/* using this Td for I/O */
 	Block*	bp;		/* data for this Td */
-	uint32_t	nbytes;		/* bytes in this Td */
-	uint32_t	cbp0;		/* initial value for cbp */
-	uint32_t	last;		/* true for last Td in Qio */
+	uint64_t _64[3];
 };
 
 /*
@@ -260,7 +263,7 @@ struct Hcca
 {
 	uint32_t	intrtable[32];
 	uint16_t	framenumber;
-	uint16_t	pad1;
+	uint16_t	_16;
 	uint32_t	donehead;
 	unsigned char	reserved[116];
 };
@@ -299,16 +302,16 @@ struct Ohci
 	uint32_t	rhdescb;		/*4c*/
 	uint32_t	rhsts;			/*50*/
 	uint32_t	rhportsts[15];		/*54*/
-	uint32_t	pad25[20];		/*90*/
+	uint32_t	_640[20];		/*90*/
 
 	/* unknown */
 	uint32_t	hostueaddr;		/*e0*/
 	uint32_t	hostuests;		/*e4*/
 	uint32_t	hosttimeoutctrl;		/*e8*/
-	uint32_t	pad59;			/*ec*/
-	uint32_t	pad60;			/*f0*/
+	uint32_t	_32_1;			/*ec*/
+	uint32_t	_32_2;			/*f0*/
 	uint32_t	hostrevision;		/*f4*/
-	uint32_t	pad62[2];
+	uint32_t	_64[2];
 					/*100*/
 };
 
@@ -617,7 +620,8 @@ tdalloc(void)
 	memset(td, 0, sizeof(Td));
 	unlock(&tdpool);
 
-	assert(((uintptr)td & 0xF) == 0);
+	if(((uint64_t)td & 0xF) != 0)
+		panic("usbohci: tdalloc td 0x%p (not 16-aligned)", td);
 	return td;
 }
 

+ 5 - 3
sys/src/9/k10/usbuhci.c

@@ -248,12 +248,13 @@ struct Qh
 	uint32_t	elink;		/* link to element (eg. Td; updated by hw) */
 
 	uint32_t	state;		/* Qidle -> Qinstall -> Qrun -> Qdone | Qclose */
-	Qio*	io;		/* for this queue */
+	uint32_t	_32;
 
+	Qio*	io;		/* for this queue */
 	Qh*	next;		/* in active or free list */
+
 	Td*	tds;		/* Td list in this Qh (initially, elink) */
 	char*	tag;		/* debug and align, mostly */
-	uint32_t	align;
 };
 
 /*
@@ -706,7 +707,8 @@ qhalloc(Ctlr *ctlr, Qh *prev, Qio *io, char *tag)
 		iunlock(ctlr);
 	}
 
-	assert(((uint64_t)qh & 0xF) == 0);
+	if(((uint64_t)qh & 0xF) != 0)
+		panic("usbuhci: qhalloc qh 0x%p (not 16-aligned)", qh);
 	return qh;
 }
 

+ 14 - 5
sys/src/9/port/usbehci.c

@@ -269,7 +269,7 @@ struct Itd
 	uint32_t	buffer[7];	/* buffer pointers, addrs, maxsz */
 	uint32_t	xbuffer[7];	/* high 32 bits of buffer for 64-bits */
 
-	uint32_t	_pad0;		/* pad to next cache line */
+	uint32_t	_32;		/* pad to next cache line */
 	/* cache-line boundary here */
 
 	/* software */
@@ -343,11 +343,12 @@ struct Qh
 	uint32_t	xbuffer[5];	/* high 32 bits of buffer for 64-bits */
 
 	/* software */
+	int32_t	state;		/* Qidle -> Qinstall -> Qrun -> Qdone | Qclose */
+	int32_t	sched;		/* slot for for intr. Qhs */
+	// 96
 	Qh*	next;		/* in controller list/tree of Qhs */
-	int	state;		/* Qidle -> Qinstall -> Qrun -> Qdone | Qclose */
 	Qio*	io;		/* for this queue */
 	Td*	tds;		/* for this queue */
-	int	sched;		/* slot for for intr. Qhs */
 	Qh*	inext;		/* next in list of intr. qhs */
 };
 
@@ -434,7 +435,9 @@ edalloc(void)
 	unlock(&edpool);
 
 	memset(ed, 0, sizeof(Ed));	/* safety */
-	assert(((uint64_t)ed & 0xF) == 0);
+	if(((uint64_t)ed & 0xF) != 0)
+		panic("usbehci: tdalloc ed 0x%p (not 16-aligned)", ed);
+
 	return ed;
 }
 
@@ -3172,7 +3175,7 @@ ehcimeminit(Ctlr *ctlr)
 	opio = ctlr->opio;
 	frsize = ctlr->nframes * sizeof(uint32_t);
 	assert((frsize & 0xFFF) == 0);		/* must be 4k aligned */
-	mallocalign(frsize, frsize, 0, 0);
+	ctlr->frames = mallocalign(frsize, frsize, 0, 0);
 	if(ctlr->frames == nil)
 		panic("ehci reset: no memory");
 
@@ -3188,6 +3191,12 @@ ehcimeminit(Ctlr *ctlr)
 
 	dprint("ehci %#p flb %#lux frno %#lux\n",
 		ctlr->capio, opio->frbase, opio->frno);
+
+	print("sizeof(Itd) %d\n", sizeof(Itd));
+	print("sizeof(Sitd) %d\n", sizeof(Sitd));
+	print("sizeof(Td) %d\n", sizeof(Td));
+	print("sizeof(Qh) %d\n", sizeof(Qh));
+
 }
 
 static void

+ 438 - 0
sys/src/cmd/usb2/keyb.c

@@ -0,0 +1,438 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+
+typedef struct Usbkeyb Usbkeyb;
+typedef struct Dirtab Dirtab;
+
+enum {
+	Qdir = 0,
+	Qkeyb,
+	Qmouse,
+	Qmax,
+};
+
+struct Usbkeyb {
+	Usbkeyb *next;
+	Dev *dev;
+	char name[32];
+	int pid;
+};
+
+struct Dirtab {
+	char *name;
+	Qid qid;
+	int64_t length;
+	int32_t mode;
+};
+
+static uint32_t time0;
+static Usbkeyb *keybs;
+static Dirtab dirtab[] = {
+	{".",	{Qdir, 0, QTDIR},	0,	0555},
+	{"keyb",	{Qkeyb, 0, 0},	0,	0444},
+	{"mouse",	{Qmouse, 0, 0},	0,	0444},
+};
+
+static int
+keybdirgen(int qidpath, Dir *dirp, void *v)
+{
+	if(qidpath >= Qmax)
+		return -1;
+	memset(dirp, 0, sizeof dirp[0]);
+	dirp->qid = dirtab[qidpath].qid;
+	dirp->name = estrdup9p(dirtab[qidpath].name);
+	dirp->mode = dirtab[qidpath].mode;
+	dirp->atime = time0;
+	dirp->mtime = time0;
+	dirp->uid = estrdup9p("");
+	dirp->gid = estrdup9p("");
+	dirp->muid = estrdup9p("");
+	return 0;
+}
+
+static void
+keybattach(Req *r)
+{
+	char *spec;
+
+	spec = r->ifcall.aname;
+	if(spec && spec[0]){
+		respond(r, "invalid attach specifier");
+		return;
+	}
+	r->ofcall.qid = dirtab[Qdir].qid;
+	r->fid->qid = r->ofcall.qid;
+	respond(r, nil);
+}
+
+static char *
+keybwalk1(Fid *fid, char *name, Qid *qid)
+{
+	int i;
+	if(fid->qid.path != Qdir)
+		return "fswalk1: bad qid";
+	for(i = 1; i < Qmax; i++){
+		if(!strcmp(dirtab[i].name, name)){
+			fid->qid = dirtab[i].qid;
+			*qid = dirtab[i].qid;
+			return nil;
+		}
+	}
+	return "no such file or directory";
+}
+
+static void
+keybstat(Req *r)
+{
+	if(keybdirgen((int)r->fid->qid.path, &r->d, nil) == -1)
+		respond(r, "bad qid");
+	respond(r, nil);
+}
+
+static void
+keybread(Req *r)
+{
+	int qidpath;
+
+	qidpath = r->fid->qid.path;
+	switch(qidpath){
+	case Qdir:
+		dirread9p(r, keybdirgen, nil);
+		respond(r, nil);
+		return;
+	default:
+		respond(r, "not implemented");
+		return;
+	}
+}
+
+static void
+keybwrite(Req *r)
+{
+	respond(r, "prohibition ale");
+}
+
+static void
+keybopen(Req *r)
+{
+	if(r->fid->qid.path >= Qmax){
+		respond(r, "bad qid");
+		return;
+	}
+	if(r->ifcall.mode != OREAD)
+		respond(r, "permission denied");
+
+	respond(r, nil);
+}
+
+Srv keybsrv = {
+	.attach = keybattach,
+	.open = keybopen,
+	.read = keybread,
+	.write = keybwrite,
+	.stat = keybstat,
+	.walk1 = keybwalk1,
+};
+
+static void
+usage(void)
+{
+	fprint(2, "usage: usb/keyb [-D] [-m mtpt] [-S srvname]\n");
+	exits("usage");
+}
+
+static int
+cmdreq(Dev *d, int type, int req, int value, int index, uint8_t *data, int count)
+{
+	int ndata, n;
+	uint8_t *wp;
+	uint8_t buf[8];
+	char *hd, *rs;
+
+	assert(d != nil);
+	if(data == nil){
+		wp = buf;
+		ndata = 0;
+	}else{
+		ndata = count;
+		wp = emallocz(8+ndata, 0);
+	}
+	wp[0] = type;
+	wp[1] = req;
+	PUT2(wp+2, value);
+	PUT2(wp+4, index);
+	PUT2(wp+6, count);
+	if(data != nil)
+		memmove(wp+8, data, ndata);
+	if(usbdebug>2){
+		hd = hexstr(wp, ndata+8);
+		rs = reqstr(type, req);
+		fprint(2, "%s: %s val %d|%d idx %d cnt %d out[%d] %s\n",
+			d->dir, rs, value>>8, value&0xFF,
+			index, count, ndata+8, hd);
+		free(hd);
+	}
+	n = write(d->dfd, wp, 8+ndata);
+	if(wp != buf)
+		free(wp);
+	if(n < 0)
+		return -1;
+	if(n != 8+ndata){
+		dprint(2, "%s: cmd: short write: %d\n", argv0, n);
+		return -1;
+	}
+	return n;
+}
+
+static int
+cmdrep(Dev *d, void *buf, int nb)
+{
+	char *hd;
+
+	nb = read(d->dfd, buf, nb);
+	if(nb >0 && usbdebug > 2){
+		hd = hexstr(buf, nb);
+		fprint(2, "%s: in[%d] %s\n", d->dir, nb, hd);
+		free(hd);
+	}
+	return nb;
+}
+
+int
+usbcmd(Dev *d, int type, int req, int value, int index, uint8_t *data, int count)
+{
+	int i, r, nerr;
+	char err[64];
+
+	/*
+	 * Some devices do not respond to commands some times.
+	 * Others even report errors but later work just fine. Retry.
+	 */
+	r = -1;
+	*err = 0;
+	for(i = nerr = 0; i < Uctries; i++){
+		if(type & Rd2h)
+			r = cmdreq(d, type, req, value, index, nil, count);
+		else
+			r = cmdreq(d, type, req, value, index, data, count);
+		if(r > 0){
+			if((type & Rd2h) == 0)
+				break;
+			r = cmdrep(d, data, count);
+			if(r > 0)
+				break;
+			if(r == 0)
+				werrstr("no data from device");
+		}
+		nerr++;
+		if(*err == 0)
+			rerrstr(err, sizeof(err));
+		sleep(Ucdelay);
+	}
+	if(r > 0 && i >= 2)
+		/* let the user know the device is not in good shape */
+		fprint(2, "%s: usbcmd: %s: required %d attempts (%s)\n",
+			argv0, d->dir, i, err);
+	return r;
+}
+
+int
+loaddevdesc(Dev *d)
+{
+	uint8_t buf[Ddevlen+255];
+	int nr;
+	int type;
+	Ep *ep0;
+
+	type = Rd2h|Rstd|Rdev;
+	nr = sizeof(buf);
+	memset(buf, 0, Ddevlen);
+	if((nr=usbcmd(d, type, Rgetdesc, Ddev<<8|0, 0, buf, nr)) < 0)
+		return -1;
+	/*
+	 * Several hubs are returning descriptors of 17 bytes, not 18.
+	 * We accept them and leave number of configurations as zero.
+	 * (a get configuration descriptor also fails for them!)
+	 */
+	if(nr < Ddevlen){
+		print("%s: %s: warning: device with short descriptor\n",
+			argv0, d->dir);
+		if(nr < Ddevlen-1){
+			werrstr("short device descriptor (%d bytes)", nr);
+			return -1;
+		}
+	}
+	d->usb = emallocz(sizeof(Usbdev), 1);
+	ep0 = mkep(d->usb, 0);
+	ep0->dir = Eboth;
+	ep0->type = Econtrol;
+	ep0->maxpkt = d->maxpkt = 8;		/* a default */
+	nr = parsedev(d, buf, nr);
+	if(nr >= 0){
+		d->usb->vendor = loaddevstr(d, d->usb->vsid);
+		if(strcmp(d->usb->vendor, "none") != 0){
+			d->usb->product = loaddevstr(d, d->usb->psid);
+			d->usb->serial = loaddevstr(d, d->usb->ssid);
+		}
+	}
+	return nr;
+}
+
+static void
+keybscanproc(void)
+{
+	static char path[256];
+	static char buf[256];
+	Keyb *keyb, **kpp;
+	Dir *dir, *dirs;
+	int i, nrd, ndirs;
+	int fd, waitfd;
+	int pid;
+
+	snprint(path, sizeof path, "/proc/%d/wait", getpid());
+	waitfd = open(path, OREAD);
+	if(waitfd == -1)
+		sysfatal("open %s", path);
+
+	for(;;){
+		fd = open("/dev/usb", OREAD);
+		if(fd == -1)
+			sysfatal("/dev/usb: %r");
+		ndirs = dirreadall(fd, &dirs);
+		close(fd);
+
+		kpp = &keybs;
+		for(i = 0; i < ndirs; i++){
+			dir = dirs+i;
+			if(strcmp(dir->name, "ctl") == 0 || strstr(dir->name, ".0") == nil)
+				continue;
+			snprint(path, sizeof path, "/dev/usb/%s/ctl", dir->name);
+			fd = open(path, ORDWR);
+			if(fd == -1)
+				continue; /* went away */
+			nrd = read(fd, buf, sizeof buf - 1);
+			close(fd);
+			if(nrd == -1)
+				continue; /* went away */
+			buf[nrd-1] = '\0';
+			if(strstr(buf, "enabled ") != nil && strstr(buf, " busy") == nil){
+				/* is it a keyboard? */
+				if(strstr(buf, "csp 0x010103")){
+
+					for(keyb = keybs; keyb != nil; keyb = keyb->next)
+						if(!strcmp(keyb->name, dir->name))
+							break;
+					if(keyb != nil)
+						continue; /* already in use */
+
+					keyb = mallocz(sizeof keyb[0], 1);
+					snprint(path, sizeof path, "/dev/usb/%s/ctl", dir->name);
+					strncpy(keyb->name, dir->name);
+					keyb->name[sizeof keyb->name - 1] = '\0';
+					keyb->dev = dev;
+
+					keyb->ep = openep(keyb->dev, ep->id);
+					if(keyb->ep == nil){
+						fprint(2, "keyb: %s: openep %d: %r\n", dev->dir, ep->id);
+						closedev(dev);
+						free(keyb);
+						continue;
+					}
+
+
+					switch(pid = rfork(RFPROC|RFMEM)){
+					case -1:
+						fprint(2, "rfork failed. keep moving...");
+						free(keyb);
+						continue;
+					case 0:
+						keybproc(keyb);
+						exits(nil);
+					default:
+						keyb->pid = pid;
+						break;
+					}
+					*kpp = keyb;
+					kpp = &keyb->next;
+				}
+			}
+		}
+		free(dirs);
+
+		/* collect dead children */
+		for(;;){
+			dir = dirfstat(waitfd);
+			if(dir == nil)
+				sysfatal("dirfstat waitfd %s", path);
+			if(dir->length > 0){
+				nrd = read(waitfd, buf, sizeof buf-1);
+				if(nrd == -1){
+					fprint(2, "read waitfd");
+					break;
+				}
+				buf[nrd] = '\0';
+				pid = atoi(fld[0]);
+				for(kpp = &keybs; *kpp != nil; kpp = &(*kpp)->next){
+					keyb = *kpp;
+					if(keyb->pid == pid){
+						close(keyb->ctlfd);
+						if(keyb->datafd != -1)
+							close(keyb->datafd);
+						if(keyb->epfd != -1)
+							close(keyb->epfd);
+						*kpp = keyb->next;
+						free(keyb);
+						break;
+					}
+				}
+				free(dir);
+				continue;
+			}
+			free(dir);
+			break;
+		}
+		sleep(1000);
+	}
+}
+
+void
+main(int argc, char **argv)
+{
+	char *mtpt, *srvname;
+	srvname = nil;
+	mtpt = "/dev/usb";
+
+	time0 = time(0);
+	ARGBEGIN{
+	case 'm':
+		mtpt = EARGF(usage());
+		break;
+	case 'S':
+		srvname = EARGF(usage());
+		break;
+	case 'D':
+		chatty9p++;
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	/* don't leave standard descriptors open to confuse mk */
+	close(0);
+	close(1);
+	close(2);
+	postmountsrv(&keybsrv, srvname, mtpt, MBEFORE);
+	exits(nil);
+}

+ 11 - 0
sys/src/cmd/usb2/keyb.json

@@ -0,0 +1,11 @@
+{
+	"Include": [
+		"../../cmd.json"
+	],
+	"Install": "/$ARCH/bin/usb",
+	"Name": "keyb",
+	"Program": "keyb",
+	"SourceFiles": [
+		"keyb.c"
+	]
+}

+ 615 - 0
sys/src/cmd/usb2/usb.c

@@ -0,0 +1,615 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include "usb.h"
+
+
+enum {
+	/* fundamental constants */
+
+	Uctries	= 4,	/* no. of tries for usbcmd */
+	Ucdelay	= 50,	/* delay before retrying */
+
+	/* request type */
+	Rh2d	= 0<<7,		/* host to device */
+	Rd2h	= 1<<7,		/* device to host */
+
+	Rstd	= 0<<5,		/* types */
+	Rclass	= 1<<5,
+	Rvendor	= 2<<5,
+
+	Rdevice	= 0,		/* recipients */
+	Riface	= 1,
+	Rendpt	= 2,		/* endpoint */
+	Rother	= 3,
+
+	/* standard requests */
+	Rgetstatus	= 0,
+	Rclearfeature	= 1,
+	Rsetfeature	= 3,
+	Rsetaddress	= 5,
+	Rgetdesc	= 6, // set/get descriptor (addressed with type and index)
+	Rsetdesc	= 7,
+	Rgetconf	= 8, // set/get configuration (1 byte)
+	Rsetconf	= 9,
+	Rgetiface	= 10, // set/get interface(index) + alt setting(value) (1 byte)
+	Rsetiface	= 11,
+	Rsynchframe	= 12,
+
+	Rgetcur	= 0x81,
+	Rgetmin	= 0x82,
+	Rgetmax	= 0x83,
+	Rgetres	= 0x84,
+	Rsetcur	= 0x01,
+	Rsetmin	= 0x02,
+	Rsetmax	= 0x03,
+	Rsetres	= 0x04,
+
+	/* dev classes */
+	Clnone		= 0,		/* not in usb */
+	Claudio		= 1,
+	Clcomms		= 2,
+	Clhid		= 3,
+	Clprinter	= 7,
+	Clstorage	= 8,
+	Clhub		= 9,
+	Cldata		= 10,
+
+	/* standard descriptor sizes */
+	Ddevicelen		= 18,
+	Dconfiglen	= 9,
+	Difacelen	= 9,
+	Dendptlen		= 7,
+
+	/* descriptor types */
+	Ddevice		= 1,
+	Dconfig		= 2,
+	Dstring		= 3,
+	Diface		= 4,
+	Dendpt		= 5,
+	Dreport		= 0x22,
+	Dfunction	= 0x24,
+	Dphysical	= 0x23,
+
+	/* feature selectors */
+	Fdevremotewakeup = 1,
+	Fhalt 	= 0,
+
+	/* device state */
+	Detached = 0,
+	Attached,
+	Enabled,
+	Assigned,
+	Configured,
+
+	/* endpoint direction */
+	Ein = 0,
+	Eout,
+	Eboth,
+
+	/* endpoint type */
+	Econtrol = 0,
+	Eiso = 1,
+	Ebulk = 2,
+	Eintr = 3,
+
+	/* endpoint isotype */
+	Eunknown = 0,
+	Easync = 1,
+	Eadapt = 2,
+	Esync = 3,
+
+	/* config attrib */
+	Cbuspowered = 1<<7,
+	Cselfpowered = 1<<6,
+	Cremotewakeup = 1<<5,
+
+	/* report types */
+	Tmtype	= 3<<2,
+	Tmitem	= 0xF0,
+	Tmain	= 0<<2,
+		Tinput	= 0x80,
+		Toutput	= 0x90,
+		Tfeature = 0xB0,
+		Tcoll	= 0xA0,
+		Tecoll	= 0xC0,
+	 Tglobal	= 1<<2,
+		Tusagepage = 0x00,
+		Tlmin	= 0x10,
+		Tlmax	= 0x20,
+		Tpmin	= 0x30,
+		Tpmax	= 0x40,
+		Tunitexp	= 0x50,
+		Tunit	= 0x60,
+		Trepsize	= 0x70,
+		TrepID	= 0x80,
+		Trepcount = 0x90,
+		Tpush	= 0xA0,
+		Tpop	= 0xB0,
+	 Tlocal	= 2<<2,
+		Tusage	= 0x00,
+		Tumin	= 0x10,
+		Tumax	= 0x20,
+		Tdindex	= 0x30,
+		Tdmin	= 0x40,
+		Tdmax	= 0x50,
+		Tsindex	= 0x70,
+		Tsmin	= 0x80,
+		Tsmax	= 0x90,
+		Tsetdelim = 0xA0,
+	 Treserved	= 3<<2,
+	 Tint32_t	= 0xFE,
+
+};
+
+typedef struct Usbdevdesc Usbdevdesc;
+typedef struct Usbconfdesc Usbconfdesc;
+typedef struct Usbstringdesc0 Usbstringdesc0;
+typedef struct Usbstringdesc Usbstringdesc;
+typedef struct Usbifacedesc Usbifacedesc;
+typedef struct Usbendptdesc Usbendptdesc;
+
+struct Usbdevdesc {
+	uint8_t bLength;
+	uint8_t bDescriptorType;
+	//	Device Descriptor (0x01, Ddevice)
+	uint8_t bcdUSB[2];
+	//	USB Specification Number which device complies too.
+	uint8_t bDeviceClass;
+	/*	Class Code (Assigned by USB Org)
+	 *	0x00: each interface specifies it’s own class code
+	 *	0xFF, the class code is vendor specified.
+	 *	Otherwise field is valid Class Code. */
+	uint8_t bDeviceSubClass;
+	//	Subclass Code (Assigned by USB Org)
+	uint8_t bDeviceProtocol;
+	//	Protocol Code (Assigned by USB Org)
+	uint8_t bMaxPacketSize;
+	//	Valid Sizes are 8, 16, 32, 64
+	uint8_t idVendor[2];
+	uint8_t idProduct[2];
+	uint8_t bcDdeviceice[2];
+	uint8_t iManufacturer;
+	uint8_t iProduct;
+	uint8_t iSerialNumber;
+	uint8_t bNumConfigurations;
+};
+
+struct Usbconfdesc {
+	uint8_t bLength;
+	uint8_t bDescriptorType;
+	//	Configuration Descriptor (0x02, Dconfig)
+	uint8_t wTotalLength[2];
+	//	Total length in bytes of data returned
+	uint8_t bNumInterfaces;
+	//	Number of Interfaces
+	uint8_t bConfigurationValue;
+	//	Value to use as an argument to select this configuration
+	uint8_t iConfiguration;
+	//	Index of String Descriptor describing this configuration
+	uint8_t bmAttributes;
+	/*	D7 Reserved, set to 1. (USB 1.0 Bus Powered)
+	 *	D6 Self Powered
+	 *	D5 Remote Wakeup
+	 *	D4..0 Reserved, set to 0. */
+	uint8_t bMaxPower;
+	//	Maximum Power Consumption in 2mA units
+};
+
+struct Usbstringdesc0 {
+	uint8_t bLength;
+	//	Size of Descriptor in Bytes
+	uint8_t bDescriptorType;
+	//	String Descriptor (0x03, Dstring)
+	uint8_t wLANGID0[2];
+	//	Supported Language Code Zero. bLength indicates how many wLANGIDs there are.
+};
+
+struct Usbstringdesc {
+	uint8_t bLength;
+	//	Size of Descriptor in Bytes
+	uint8_t bDescriptorType;
+	//	String Descriptor (0x03, Dstring)
+	uint8_t bString[1];
+	//	Unicode Encoded String
+};
+
+struct Usbifacedesc {
+	uint8_t bLength;
+	//	Size of Descriptor in Bytes (9 Bytes)
+	uint8_t bDescriptorType;
+	//	Interface Descriptor (0x04, Diface)
+	uint8_t bInterfaceNumber;
+	//	Number of Interface
+	uint8_t bAlternateSetting;
+	//	Value used to select alternative setting
+	uint8_t bNumEndpoints;
+	//	Number of Endpoints used for this interface
+	uint8_t bInterfaceClass;
+	//	Class Code (Assigned by USB Org)
+	uint8_t bInterfaceSubClass;
+	//	Subclass Code (Assigned by USB Org)
+	uint8_t bInterfaceProtocol;
+	//	Protocol Code (Assigned by USB Org)
+	uint8_t iInterface;
+	//	Index of String Descriptor Describing this interface
+	uint8_t extra[256-9];
+};
+
+struct Usbendptdesc {
+	uint8_t bLength;
+	//	Size of Descriptor in Bytes (7 bytes)
+	uint8_t bDescriptorType;
+	//	Endpoint Descriptor (0x05, Dendpt)
+	uint8_t bEndpointAddress;
+	/*	Endpoint Address
+	 *	Bits 0..3b Endpoint Number.
+	 *	Bits 4..6b Reserved. Set to Zero
+	 *	Bits 7 Direction 0 = Out, 1 = In (Ignored for Control Endpoints) */
+	uint8_t bmAttributes;
+	/*	Bits 0..1 Transfer Type
+	 *	00 = Control
+	 *	01 = Isochronous
+	 *	10 = Bulk
+	 *	11 = Interrupt
+	 *	Bits 2..7 are reserved. If Isochronous endpoint,
+	 *	Bits 3..2 = Synchronisation Type (Iso Mode)
+	 *	00 = No Synchonisation
+	 *	01 = Asynchronous
+	 *	10 = Adaptive
+	 *	11 = Synchronous
+	 *	Bits 5..4 = Usage Type (Iso Mode)
+	 *	00 = Data Endpoint
+	 *	01 = Feedback Endpoint
+	 *	10 = Explicit Feedback Data Endpoint
+	 *	11 = Reserved */
+	uint8_t wMaxPacketSize[2];
+	//	Maximum Packet Size this endpoint is capable of sending or receiving
+	uint8_t bInterval;
+	/*	Interval for polling endpoint data transfers. Value in frame counts.
+	 *	Ignored for Bulk & Control Endpoints. Isochronous must equal 1,
+	 *	may range from 1 to 255 for interrupt endpoints. */
+};
+
+static void
+put16(uint8_t *buf, uint16_t val)
+{
+	buf[0] = val & 0xff;
+	buf[1] = (val >> 8) & 0xff;
+}
+
+static uint16_t
+get16(uint8_t *buf)
+{
+	uint16_t val;
+	val = (uint16_t)buf[0] + ((uint16_t)buf[1] << 8);
+	return val;
+}
+
+static int
+cmdreq(int fd, int type, int req, int value, int index, uint8_t *data, int count)
+{
+	int ndata, n;
+	uint8_t *wp;
+	uint8_t buf[8];
+
+	if(data == nil){
+		wp = buf;
+		ndata = 0;
+	}else{
+		ndata = count;
+		wp = malloc(8+ndata);
+	}
+	wp[0] = type;
+	wp[1] = req;
+	put16(wp+2, value);
+	put16(wp+4, index);
+	put16(wp+6, count);
+	if(data != nil)
+		memmove(wp+8, data, ndata);
+
+	n = write(fd, wp, 8+ndata);
+	if(wp != buf)
+		free(wp);
+	if(n < 0)
+		return -1;
+	if(n != 8+ndata){
+		fprint(2, "%s: cmd: short write: %d\n", argv0, n);
+		return -1;
+	}
+	return n;
+}
+
+static int
+cmdrep(int fd, void *buf, int nb)
+{
+	nb = read(fd, buf, nb);
+
+	return nb;
+}
+
+static int
+usbcmd(int fd, int type, int req, int value, int index, uint8_t *data, int count)
+{
+	int i, r, nerr;
+	char err[64];
+
+	/*
+	 * Some devices do not respond to commands some times.
+	 * Others even report errors but later work just fine. Retry.
+	 */
+	r = -1;
+	*err = 0;
+	for(i = nerr = 0; i < Uctries; i++){
+		if(type & Rd2h)
+			r = cmdreq(fd, type, req, value, index, nil, count);
+		else
+			r = cmdreq(fd, type, req, value, index, data, count);
+		if(r > 0){
+			if((type & Rd2h) == 0)
+				break;
+			r = cmdrep(fd, data, count);
+			if(r > 0)
+				break;
+			if(r == 0)
+				werrstr("no data from device");
+		}
+		nerr++;
+		if(*err == 0)
+			rerrstr(err, sizeof(err));
+		sleep(Ucdelay);
+	}
+	if(r > 0 && i >= 2)
+		fprint(2, "usbcmd required %d attempts (%s)\n", i, err);
+	return r;
+}
+
+int
+usbdescread(int fd, uint8_t *buf, int len, int desctype, int index)
+{
+	return usbcmd(fd, Rd2h|Rstd|Rdevice, Rgetdesc, desctype<<8|0, index, buf, len);
+}
+
+int
+usbconfprint(int fd, Usbconfig *cp)
+{
+	int nwr, ntot;
+	int i;
+
+	ntot = 0;
+	nwr = fprint(fd, "config %d iface %d alt %d\n", cp->config, cp->iface, cp->alt);
+	if(nwr == -1)
+		return -1;
+	ntot += nwr;
+	for(i = 0; i < cp->nendpt; i++){
+		nwr = fprint(
+			fd,
+			"	endpt[%d]: addr %d type %d maxpkt %d pollival %d\n",
+			i,
+			cp->endpt[i].addr,
+			cp->endpt[i].type,
+			cp->endpt[i].maxpkt,
+			cp->endpt[i].pollival
+		);
+		if(nwr == -1)
+			return -1;
+		ntot += nwr;
+	}
+	if(cp->nextra > 0){
+		for(i = 0; i < cp->nextra; i++){
+			nwr = fprint(fd, "%s0x%02x%s", i%16==0 ? "\t" : "", cp->extra[i], i < cp->nextra-1 ? ", " : "\n");
+			if(nwr == -1)
+				return -1;
+			ntot += nwr;
+		}
+	}
+	return ntot;
+}
+
+int
+usbconfread(int fd, Usbconfig **confp)
+{
+	Usbconfig *confs;
+	int nconfs, aconfs;
+	uint8_t buf[1024];
+	Usbdevdesc devdesc;
+	Usbconfdesc confdesc;
+	Usbifacedesc ifacedesc;
+	Usbendptdesc endptdesc;
+
+	int nrd;
+	int cfg, ncfg;
+	int ifi, niface;
+	int epi;
+
+
+	nrd = usbdescread(fd, buf, sizeof buf, Ddevice, 0);
+	if(nrd == -1)
+		sysfatal("readdesc Ddevice");
+	memmove(&devdesc, buf, sizeof devdesc);
+
+	nconfs = 0;
+	aconfs = 4;
+	confs = malloc(aconfs * sizeof confs[0]);
+
+	ncfg = devdesc.bNumConfigurations;
+	for(cfg = 0; cfg < ncfg; cfg++){
+		int off = 0;
+		nrd = usbdescread(fd, buf, sizeof buf, Dconfig, cfg);
+		if(nrd == -1)
+			sysfatal("readdesc Dconfig");
+
+		if(off >= nrd || off+buf[off] > nrd){
+			print("premature end of config data");
+			goto done;
+		}
+		if(buf[off+1] != Dconfig)
+			sysfatal("non-config descriptor %d\n", confdesc.bDescriptorType);
+
+		memmove(&confdesc, buf+off, buf[off]);
+		off += confdesc.bLength;
+		niface = confdesc.bNumInterfaces;
+		for(ifi = 0; ifi < niface; ifi++){
+			Usbconfig *cp;
+ifskip:
+			if(off >= nrd || off+buf[off] > nrd){
+				print("premature end of config data");
+				goto done;
+			}
+			if(buf[off+1] != Diface){
+				print("non-iface descriptor %d in config\n", buf[off+1]);
+				off += buf[off];
+				goto ifskip;
+			}
+			memmove(&ifacedesc, buf+off, buf[off]);
+			if(nconfs == aconfs){
+				aconfs += 4;
+				confs = realloc(confs, aconfs * sizeof confs[0]);
+			}
+			cp = &confs[nconfs++];
+			memset(cp, 0, sizeof cp[0]);
+			cp->config = confdesc.bConfigurationValue;
+			cp->iface = ifacedesc.bInterfaceNumber;
+			cp->alt = ifacedesc.bAlternateSetting;
+			cp->nendpt = ifacedesc.bNumEndpoints;
+			if(cp->nendpt > nelem(cp->endpt)){
+				print("out of endpoints in usbconfig (need %d have %d)\n", cp->nendpt, nelem(cp->endpt));
+				cp->nendpt = nelem(cp->endpt);
+			}
+
+			off += ifacedesc.bLength;
+			for(epi = 0; epi < cp->nendpt; epi++){
+epskip:
+				if(off >= nrd || off+buf[off] > nrd){
+					print("premature end of config data\n");
+					goto done;
+				}
+				if(buf[off+1] != Dendpt){
+					if(cp->nextra+buf[off] <= sizeof cp->extra){
+						memmove(cp->extra + cp->nextra, buf+off, buf[off]);
+						cp->nextra += buf[off];
+					} else {
+						print("out of extra space in usbconfig, desc type 0x%02x\n", buf[off+1]);
+					}
+					off += buf[off];
+					goto epskip;
+				}
+				memmove(&endptdesc, buf+off, buf[off]);
+				off += endptdesc.bLength;
+				cp->endpt[epi].addr = endptdesc.bEndpointAddress;
+				cp->endpt[epi].type = endptdesc.bmAttributes;
+				cp->endpt[epi].maxpkt = get16(endptdesc.wMaxPacketSize);
+				cp->endpt[epi].pollival = endptdesc.bInterval;
+			}
+		}
+	}
+done:
+
+	aconfs = nconfs;
+	confs = realloc(confs, aconfs * sizeof confs[0]);
+	*confp = confs;
+	return nconfs;
+}
+
+int
+usbopen(int fd, Usbconfig *cp, int epi, int *ctlp)
+{
+	char *p, buf[1024];
+	int epaddr, eptype, isread;
+	int ctl, epctl, epdata;
+	int n;
+
+	ctl = -1;
+	epctl = -1;
+	epdata = -1;
+	epaddr = cp->endpt[epi].addr&7;
+	eptype = cp->endpt[epi].type&3;
+	isread = (cp->endpt[epi].addr&128) != 0;
+
+	if(usbcmd(fd, Rh2d|Rstd|Rdevice, Rsetconf, cp->config, 0, nil, 0) == -1){
+		fprint(2, "usbopen: Rsetconf fail\n");
+		goto fail;
+	}
+
+	if(usbcmd(fd, Rh2d|Rstd|Riface, Rsetiface, cp->alt, cp->iface, nil, 0) == -1){
+		fprint(2, "usbopen: Rsetiface fail\n");
+		goto fail;
+	}
+
+	fd2path(fd, buf, sizeof buf);
+	if((p = strrchr(buf, '/')) == nil){
+		fprint(2, "usbopen: no '.' in data path '%s'\n", buf);
+		goto fail;
+	}
+	strcpy(p, "/ctl");
+	if((ctl = open(buf, ORDWR)) == -1){
+		fprint(2, "usbopen: open '%s' failed: %r\n", buf);
+		goto fail;
+	}
+
+	n = snprint(buf, sizeof buf, "new %d %d %s", epaddr, eptype, isread ? "r" : "w");
+	if(write(ctl, buf, n) != n){
+		fprint(2, "usbopen: write failed '%s': %r\n", buf);
+		//goto fail;
+	}
+
+	fd2path(fd, buf, sizeof buf);
+	if((p = strrchr(buf, '.')) == nil){
+		fprint(2, "usbopen: no '.' in ctl path '%s'\n", buf);
+		goto fail;
+	}
+	snprint(p, sizeof buf - (p-buf), ".%d/ctl", epaddr);
+	if((epctl = open(buf, ORDWR)) == -1){
+		fprint(2, "usbopen: open '%s': %r\n", buf);
+		goto fail;
+	}
+	n = snprint(buf, sizeof buf, "maxpkt %d", cp->endpt[epi].maxpkt);
+	if(write(epctl, buf, n) != n){
+		fprint(2, "usbopen: '%s': %r\n", buf);
+		goto fail;
+	}
+	if(eptype == Eintr || eptype == Eiso){
+		n = snprint(buf, sizeof buf, "pollival %d", cp->endpt[epi].pollival);
+		if(write(epctl, buf, n) != n){
+			fprint(2, "usbopen: pollival failed\n");
+			goto fail;
+		}
+	}
+
+
+	fd2path(fd, buf, sizeof buf);
+	if((p = strrchr(buf, '.')) == nil){
+		fprint(2, "usbopen: no '.' in ctl path '%s'\n", buf);
+		goto fail;
+	}
+	snprint(p, sizeof buf - (p-buf), ".%d/data", epaddr);
+	if((epdata = open(buf, isread ? OREAD : OWRITE)) == -1){
+		fprint(2, "usbopen: open '%s': %r\n", buf);
+		goto fail;
+	}
+
+	if(ctlp != nil)
+		*ctlp = epctl;
+	else
+		close(epctl);
+	if(ctl != -1)
+		close(ctl);
+	return epdata;
+
+fail:
+	if(ctl != -1)
+		close(ctl);
+	if(epctl != -1)
+		close(epctl);
+	if(epdata != -1)
+		close(epdata);
+	return -1;
+}

+ 22 - 0
sys/src/cmd/usb2/usb.h

@@ -0,0 +1,22 @@
+
+typedef struct Usbconfig Usbconfig;
+
+struct Usbconfig {
+	int config;
+	int iface;
+	int alt;
+	int nendpt;
+	int nextra;
+	struct {
+		int addr;
+		int type;
+		int maxpkt;
+		int pollival;
+	} endpt[16];
+	uint8_t extra[236];
+};
+
+int usbdescread(int fd, uint8_t *buf, int len, int desctype, int index);
+int usbconfread(int fd, Usbconfig **confp);
+int usbconfprint(int fd, Usbconfig *cp);
+int usbopen(int fd, Usbconfig *cp, int epi, int *ctlp);

+ 12 - 0
sys/src/cmd/usb2/usb.json

@@ -0,0 +1,12 @@
+{
+	"Include": [
+		"../cmd.json"
+	],
+	"Install": "/$ARCH/bin/usb",
+	"Name": "usbugly",
+	"Program": "usbugly",
+	"SourceFiles": [
+		"usb.c",
+		"usbugly.c"
+	]
+}

+ 48 - 0
sys/src/cmd/usb2/usbugly.c

@@ -0,0 +1,48 @@
+#include <u.h>
+#include <libc.h>
+#include "usb.h"
+
+void
+main(int argc, char *argv[])
+{
+	Usbconfig *confs;
+	int nconfs;
+	int fd, epdata, epctl;
+	int i;
+
+	confs = nil;
+	epdata = -1;
+	epctl = -1;
+	fd = open(argv[1], ORDWR);
+	nconfs = usbconfread(fd, &confs);
+	for(i = 0; i < nconfs; i++){
+		if(usbconfprint(1, confs+i) == -1){
+			fprint(2, "usbprint fail!\n");
+			goto fail;
+		}
+	}
+	epdata = usbopen(fd, confs, 0, &epctl);
+	if(epdata == -1){
+		fprint(2, "usbopen fail!\n");
+		goto fail;
+	}
+
+	for(i = 0; i < 128; i++){
+		char buf[8];
+		int j, n;
+		n = read(epdata, buf, sizeof buf);
+		for(j = 0; j < n; j++)
+			print("0x%02x%s", buf[j], j == n-1 ? "\n" : ", ");
+	}
+
+fail:
+	if(confs != nil)
+		free(confs);
+	close(fd);
+	if(epdata != -1)
+		close(epdata);
+	if(epctl != -1)
+		close(epctl);
+	print("sizeof(confs[0]) = %d\n", sizeof confs[0]);
+	exits(nil);
+}