Browse Source

Plan 9 from Bell Labs 2010-06-16

David du Colombier 14 years ago
parent
commit
e1789cbed7

+ 5 - 0
sys/man/3/ip

@@ -970,6 +970,11 @@ sending to the other end of the tunnel.
 Received packets are checked against their MAC's,
 decrypted, and queued for reading from
 .BR data .
+In the following,
+.I secret
+is the hexadecimal encoding of a key,
+without a leading
+.LR 0x .
 The control messages are:
 .TF "\fLesp \fIalg secret\fR"
 .PD

+ 1 - 1
sys/man/8/plan9.ini

@@ -123,7 +123,7 @@ The VIA Velocity Gigabit Ethernet controller.
 Known to drive the VIA8237 (ABIT AV8), but at 100Mb/s full-duplex only.
 .TP
 .B m10g
-The Myricom 10-Gigabit Ethernet controllers.
+The Myricom 10-Gigabit Ethernet 10G-PCIE-8A controller.
 Completely configurable.
 Can't boot through these due to enormous firmware loads.
 .TP

+ 1 - 1
sys/src/9/pc/ether82598.c

@@ -1,5 +1,5 @@
 /*
- * intel 10gbe pci-express driver
+ * intel 10GB ethernet pci-express driver
  * copyright © 2007, coraid, inc.
  */
 #include "u.h"

+ 19 - 9
sys/src/9/pc/etherm10g.c

@@ -1,5 +1,5 @@
 /*
- * myricom 10 Gb ethernet driver
+ * myricom 10g-pcie-8a 10 Gb ethernet driver
  * © 2007 erik quanstrom, coraid
  *
  * the card is big endian.
@@ -362,15 +362,12 @@ whichfw(Pcidev *p)
 		print("fw = %d [forced]\n", i);
 		return i;
 	}
-	if(lanes <= 4){
+	if(lanes <= 4)
 		print("fw = 4096 [lanes]\n");
-		return 4*KiB;
-	}
-	if(ecrc & 10){
+	else if(ecrc & 10)
 		print("fw = 4096 [ecrc set]\n");
-		return 4*KiB;
-	}
-	print("fw = 4096 [default]\n");
+	else
+		print("fw = 4096 [default]\n");
 	return 4*KiB;
 }
 
@@ -1556,7 +1553,20 @@ m10gpci(void)
 	Ctlr *t, *c;
 
 	t = 0;
-	for(p = 0; p = pcimatch(p, 0x14c1, 0x0008); ){
+	for(p = 0; p = pcimatch(p, Vmyricom, 0); ){
+		switch(p->did){
+		case 0x8:		/* 8a */
+			break;
+		case 0x9:		/* 8a with msi-x fw */
+		case 0xa:		/* 8b */
+		case 0xb:		/* 8b2 */
+		case 0xc:		/* 2-8b2 */
+			/* untested */
+			break;
+		default:
+			print("etherm10g: unknown myricom did %#ux\n", p->did);
+			continue;
+		}
 		c = malloc(sizeof *c);
 		if(c == nil)
 			continue;

+ 6 - 0
sys/src/9/pc/io.h

@@ -260,6 +260,12 @@ struct Pcidev
 	int	pmrb;			/* power management register block */
 };
 
+enum {
+	/* vendor ids */
+	Vintel	= 0x8086,
+	Vmyricom= 0x14c1,
+};
+
 #define PCIWINDOW	0
 #define PCIWADDR(va)	(PADDR(va)+PCIWINDOW)
 #define ISAWINDOW	0

+ 33 - 12
sys/src/cmd/usb/disk/disk.c

@@ -34,7 +34,7 @@ struct Dirtab
 static Dirtab dirtab[] =
 {
 	[Qdir]	"/",	DMDIR|0555,
-	[Qctl]	"ctl",	0444,
+	[Qctl]	"ctl",	0664,		/* nothing secret here */
 	[Qraw]	"raw",	0640,
 	[Qdata]	"data",	0640,
 };
@@ -145,12 +145,19 @@ umsinit(Ums *ums)
 		lun->umsc = lun;
 		lun->lun = i;
 		lun->flags = Fopen | Fusb | Frw10;
-		if(SRinquiry(lun) < 0 && SRinquiry(lun) < 0)
+		if(SRinquiry(lun) < 0 && SRinquiry(lun) < 0){
+			dprint(2, "disk: lun %d inquiry failed\n", i);
 			continue;
-		if(lun->inquiry[0] != 0x00){
-			/* not a disk */
-			fprint(2, "%s: lun %d is not a disk (type %#02x)\n",
-				argv0, i, lun->inquiry[0]);
+		}
+		switch(lun->inquiry[0]){
+		case Devdir:
+		case Devworm:		/* a little different than the others */
+		case Devcd:
+		case Devmo:
+			break;
+		default:
+			fprint(2, "disk: lun %d is not a disk (type %#02x)\n",
+				i, lun->inquiry[0]);
 			continue;
 		}
 		SRstart(lun, 1);
@@ -163,6 +170,7 @@ umsinit(Ums *ums)
 		umscapacity(lun);
 	}
 	if(some == 0){
+		dprint(2, "disk: all luns failed\n");
 		devctl(ums->dev, "detach");
 		return -1;
 	}
@@ -178,7 +186,7 @@ umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status)
 {
 	Cbw cbw;
 	Csw csw;
-	int n;
+	int n, nio;
 	Ums *ums;
 
 	ums = umsc->ums;
@@ -189,7 +197,7 @@ umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status)
 	cbw.flags = data->write? CbwDataOut: CbwDataIn;
 	cbw.lun = umsc->lun;
 	if(cmd->count < 1 || cmd->count > 16)
-		print("%s: umsrequest: bad cmd count: %ld\n", argv0, cmd->count);
+		print("disk: umsrequest: bad cmd count: %ld\n", cmd->count);
 
 	cbw.len = cmd->count;
 	assert(cmd->count <= sizeof(cbw.command));
@@ -208,12 +216,13 @@ umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status)
 		fprint(2, "disk: cmd: %r\n");
 		goto Fail;
 	}
+	nio = data->count;
 	if(data->count != 0){
 		if(data->write)
-			n = write(ums->epout->dfd, data->p, data->count);
+			n = nio = write(ums->epout->dfd, data->p, data->count);
 		else{
 			memset(data->p, data->count, 0);
-			n = read(ums->epin->dfd, data->p, data->count);
+			n = nio = read(ums->epin->dfd, data->p, data->count);
 		}
 		if(diskdebug)
 			if(n < 0)
@@ -242,8 +251,10 @@ umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status)
 		dprint(2, "disk: phase error\n");
 		goto Fail;
 	}
+	if(csw.dataresidue == 0 || ums->wrongresidues)
+		csw.dataresidue = data->count - nio;
 	if(diskdebug){
-		fprint(2, "status: %2.2ux residue: %ld\n",
+		fprint(2, "disk: status: %2.2ux residue: %ld\n",
 			csw.status, csw.dataresidue);
 		if(cbw.command[0] == ScmdRsense){
 			fprint(2, "sense data:");
@@ -484,6 +495,10 @@ dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong offset)
 		qunlock(ums);
 		werrstr(Eperm);
 		return -1;
+	case Qctl:
+		qunlock(ums);
+		dprint(2, "usb/disk: ctl ignored\n");
+		return count;
 	case Qraw:
 		if(lun->lbsize <= 0 && umscapacity(lun) < 0){
 			qunlock(ums);
@@ -698,6 +713,13 @@ diskmain(Dev *dev, int argc, char **argv)
 		werrstr("disk: endpoints not found");
 		return -1;
 	}
+
+	/*
+	 * SanDISK 512M gets residues wrong.
+	 */
+	if(dev->usb->vid == 0x0781 && dev->usb->did == 0x5150)
+		ums->wrongresidues = 1;
+
 	if(umsinit(ums) < 0){
 		dprint(2, "disk: umsinit: %r\n");
 		return -1;
@@ -712,6 +734,5 @@ diskmain(Dev *dev, int argc, char **argv)
 		lun->fs.aux = lun;
 		usbfsadd(&lun->fs);
 	}
-	closedev(dev);
 	return 0;
 }

+ 7 - 7
sys/src/cmd/usb/disk/scsireq.c

@@ -916,30 +916,30 @@ SRopen(ScsiReq *rp, char *unit)
 			rp->status = Status_SW;
 			break;
 
-		case 0x00:	/* Direct access (disk) */
-		case 0x05:	/* CD-ROM */
-		case 0x07:	/* rewriteable MO */
+		case Devdir:
+		case Devcd:
+		case Devmo:
 			if(dirdevopen(rp) == -1)
 				break;
 			return 0;
 
-		case 0x01:	/* Sequential eg: tape */
+		case Devseq:
 			rp->flags |= Fseqdev;
 			if(seqdevopen(rp) == -1)
 				break;
 			return 0;
 
-		case 0x02:	/* Printer */
+		case Devprint:
 			rp->flags |= Fprintdev;
 			return 0;
 
-		case 0x04:	/* Worm */
+		case Devworm:
 			rp->flags |= Fwormdev;
 			if(wormdevopen(rp) == -1)
 				break;
 			return 0;
 
-		case 0x08:	/* medium-changer */
+		case Devjuke:
 			rp->flags |= Fchanger;
 			return 0;
 		}

+ 3 - 0
sys/src/cmd/usb/ether/ether.c

@@ -907,6 +907,7 @@ etherfree(Ether *e)
 	shutdownchan(e->rc);
 	shutdownchan(e->wc);
 	e->epin = e->epout = nil;
+	devctl(e->dev, "detach");
 	free(e);
 }
 
@@ -1030,6 +1031,7 @@ etherreadproc(void *a)
 	deprint(2, "%s: writeproc exiting\n", argv0);
 	etherexiting(e);
 	closedev(e->dev);
+	usbfsdel(&e->fs);
 }
 
 static void
@@ -1173,6 +1175,7 @@ ethermain(Dev *dev, int argc, char **argv)
 	incref(e->dev);
 	proccreate(etherreadproc, e, 16*1024);
 	deprint(2, "%s: dev ref %ld\n", argv0, dev->ref);
+	incref(e->dev);
 	usbfsadd(&e->fs);
 	return 0;
 }

+ 43 - 39
sys/src/cmd/usb/kb/kb.c

@@ -114,6 +114,37 @@ static Kin ptrin =
 
 static int kbdebug;
 
+static int
+setbootproto(KDev* f, int eid)
+{
+	int r, id;
+
+	r = Rh2d|Rclass|Riface;
+	id = f->dev->usb->ep[eid]->iface->id;
+	return usbcmd(f->dev, r, Setproto, Bootproto, id, nil, 0);
+}
+
+/*
+ * Try to recover from a babble error. A port reset is the only way out.
+ * BUG: we should be careful not to reset a bundle with several devices.
+ */
+static void
+recoverkb(KDev *f)
+{
+	int i;
+
+	close(f->dev->dfd);		/* it's for usbd now */
+	devctl(f->dev, "reset");
+	for(i = 0; i < 10; i++){
+		sleep(500);
+		if(opendevdata(f->dev, ORDWR) >= 0){
+			setbootproto(f, f->ep->id);
+			break;
+		}
+		/* else usbd still working... */
+	}
+}
+
 static void
 kbfatal(KDev *kd, char *sts)
 {
@@ -189,13 +220,13 @@ ptrwork(void* a)
 {
 	static char maptab[] = {0x0, 0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7};
 	int x, y, b, c, ptrfd;
-	int	mfd;
+	int	mfd, nerrs;
 	char	buf[32];
 	char	mbuf[80];
 	KDev*	f = a;
 	int	hipri;
 
-	hipri = 0;
+	hipri = nerrs = 0;
 	ptrfd = f->ep->dfd;
 	mfd = f->in->fd;
 
@@ -208,8 +239,13 @@ ptrwork(void* a)
 		c = read(ptrfd, buf, f->ep->maxpkt);
 		assert(f->dev != nil);
 		assert(f->ep != nil);
-		if(c < 0)
+		if(c < 0){
 			dprint(2, "kb: mouse: %s: read: %r\n", f->ep->dir);
+			if(++nerrs < 3){
+				recoverkb(f);
+				continue;
+			}
+		}
 		if(c <= 0)
 			kbfatal(f, nil);
 		if(c < 3)
@@ -226,7 +262,7 @@ ptrwork(void* a)
 			b |= 0x08;
 		if(c > 3 && buf[3] == -1)	/* down */
 			b |= 0x10;
-		if(kbdebug)
+		if(kbdebug > 1)
 			fprint(2, "kb: m%11d %11d %11d\n", x, y, b);
 		seprint(mbuf, mbuf+sizeof(mbuf), "m%11d %11d %11d", x, y,b);
 		if(write(mfd, mbuf, strlen(mbuf)) < 0)
@@ -262,7 +298,7 @@ putscan(int kbinfd, uchar esc, uchar sc)
 	uchar s[2] = {SCesc1, 0};
 
 	if(sc == 0x41){
-		kbdebug++;
+		kbdebug += 2;
 		return;
 	}
 	if(sc == 0x42){
@@ -396,37 +432,6 @@ kbdbusy(uchar* buf, int n)
 	return 1;
 }
 
-static int
-setbootproto(KDev* f, int eid)
-{
-	int r, id;
-
-	r = Rh2d|Rclass|Riface;
-	id = f->dev->usb->ep[eid]->iface->id;
-	return usbcmd(f->dev, r, Setproto, Bootproto, id, nil, 0);
-}
-
-/*
- * Try to recover from a babble error. A port reset is the only way out.
- * BUG: we should be careful not to reset a bundle with several devices.
- */
-static void
-recoverkb(KDev *f)
-{
-	int i;
-
-	close(f->dev->dfd);		/* it's for usbd now */
-	devctl(f->dev, "reset");
-	for(i = 0; i < 10; i++){
-		sleep(500);
-		if(opendevdata(f->dev, ORDWR) >= 0){
-			setbootproto(f, f->ep->id);
-			break;
-		}
-		/* else usbd still working... */
-	}
-}
-
 static void
 kbdwork(void *a)
 {
@@ -454,7 +459,7 @@ kbdwork(void *a)
 		assert(f->ep != nil);
 		if(c < 0){
 			rerrstr(err, sizeof(err));
-			dprint(2, "kb: %s: read: %s\n", f->ep->dir, err);
+			fprint(2, "kb: %s: read: %s\n", f->ep->dir, err);
 			if(strstr(err, "babble") != 0 && ++nerrs < 3){
 				recoverkb(f);
 				continue;
@@ -466,7 +471,7 @@ kbdwork(void *a)
 			continue;
 		if(kbdbusy(buf + 2, c - 2))
 			continue;
-		if(usbdebug > 1 || kbdebug > 1){
+		if(usbdebug > 2 || kbdebug > 1){
 			fprint(2, "kbd mod %x: ", buf[0]);
 			for(i = 2; i < c; i++)
 				fprint(2, "kc %x ", buf[i]);
@@ -594,6 +599,5 @@ kbmain(Dev *d, int argc, char* argv[])
 		if(ep->iface->csp == PtrCSP)
 			kbstart(d, ep, &ptrin, ptrwork, accel);
 	}
-	closedev(d);
 	return 0;
 }

+ 1 - 2
sys/src/cmd/usb/lib/fsdir.c

@@ -101,8 +101,7 @@ usbfsdelnth(int i)
 			dprint(2, "no dev\n");
 		if(fs[i]->end != nil)
 			fs[i]->end(fs[i]);
-		else
-			closedev(fs[i]->dev);
+		closedev(fs[i]->dev);
 		fsused--;
 	}
 	fs[i] = nil;

+ 237 - 143
sys/src/cmd/usb/serial/ftdi.c

@@ -15,7 +15,6 @@
 Cinfo ftinfo[] = {
 	{ FTVid, FTACTZWAVEDid },
 	{ FTSheevaVid, FTSheevaDid },
-	{ FTVid, FTOpenrdDid },
 	{ FTVid, FTIRTRANSDid },
 	{ FTVid, FTIPLUSDid },
 	{ FTVid, FTSIODid },
@@ -200,50 +199,59 @@ Cinfo ftinfo[] = {
 	{ ICOMID1Vid, ICOMID1Did },
 	{ PAPOUCHVid, PAPOUCHTMUDid },
 	{ FTVid, FTACGHFDUALDid },
+	{ FT8U232AMDid, FT4232HDid },
 	{ 0,	0 },
 };
 
 enum {
-	Packsz = 1024,
+	Packsz		= 64,		/* default size */
+	Maxpacksz	= 512,
+	Bufsiz		= 4 * 1024,
 };
 
 static int
-ftdiread(Serial *ser, int val, int index, int req, uchar *buf)
+ftdiread(Serialport *p, int val, int index, int req, uchar *buf)
 {
 	int res;
+	Serial *ser;
+
+	ser = p->s;
 
 	if(req != FTGETE2READ)
-		index |= ser->interfc + 1;
-	dsprint(2, "serial: ftdiread req: %#x val: %#x idx:%d buf:%p\n",
-		req, val, index, buf);
+		index |= p->interfc + 1;
+	dsprint(2, "serial: ftdiread %#p [%d] req: %#x val: %#x idx:%d buf:%p\n",
+		p, p->interfc, req, val, index, buf);
 	res = usbcmd(ser->dev,  Rd2h | Rftdireq | Rdev, req, val, index, buf, 1);
 	dsprint(2, "serial: ftdiread res:%d\n", res);
 	return res;
 }
 
 static int
-ftdiwrite(Serial *ser, int val, int index, int req)
+ftdiwrite(Serialport *p, int val, int index, int req)
 {
 	int res;
+	Serial *ser;
 
-	index |= ser->interfc + 1;
-	dsprint(2, "serial: ftdiwrite  req: %#x val: %#x idx:%d\n",
-		req, val, index);
+	ser = p->s;
+
+	index |= p->interfc + 1;
+	dsprint(2, "serial: ftdiwrite %#p [%d] req: %#x val: %#x idx:%d\n",
+		p, p->interfc, req, val, index);
 	res = usbcmd(ser->dev, Rh2d | Rftdireq | Rdev, req, val, index, nil, 0);
 	dsprint(2, "serial: ftdiwrite res:%d\n", res);
 	return res;
 }
 
 static int
-ftmodemctl(Serial *ser, int set)
+ftmodemctl(Serialport *p, int set)
 {
 	if(set == 0){
-		ser->mctl = 0;
-		ftdiwrite(ser, 0, 0, FTSETMODEMCTRL);
+		p->mctl = 0;
+		ftdiwrite(p, 0, 0, FTSETMODEMCTRL);
 		return 0;
 	}
-	ser->mctl = 1;
-	ftdiwrite(ser, 0, FTRTSCTSHS, FTSETFLOWCTRL);
+	p->mctl = 1;
+	ftdiwrite(p, 0, FTRTSCTSHS, FTSETFLOWCTRL);
 	return 0;
 }
 
@@ -378,6 +386,9 @@ ftbaudcalcdiv(Serial *ser, int baud)
  		break;
 	case FT232BM:
 	case FT2232C:
+	case FTKINDR:
+	case FT2232H:
+	case FT4232H:
 		if(baud <= 3000000)
 			divval = ft232bmbaud2div(baud);
 		else
@@ -391,20 +402,20 @@ ftbaudcalcdiv(Serial *ser, int baud)
 }
 
 static int
-ftsetparam(Serial *ser)
+ftsetparam(Serialport *p)
 {
 	int res;
 	ushort val;
 	ulong bauddiv;
 
 	val = 0;
-	if(ser->stop == 1)
+	if(p->stop == 1)
 		val |= FTSETDATASTOPBITS1;
-	else if(ser->stop == 2)
+	else if(p->stop == 2)
 		val |= FTSETDATASTOPBITS2;
-	else if(ser->stop == 15)
+	else if(p->stop == 15)
 		val |= FTSETDATASTOPBITS15;
-	switch(ser->parity){
+	switch(p->parity){
 	case 0:
 		val |= FTSETDATAParNONE;
 		break;
@@ -424,16 +435,16 @@ ftsetparam(Serial *ser)
 
 	dsprint(2, "serial: setparam\n");
 
-	res = ftdiwrite(ser, val, 0, FTSETDATA);
+	res = ftdiwrite(p, val, 0, FTSETDATA);
 	if(res < 0)
 		return res;
 
-	res = ftmodemctl(ser, ser->mctl);
+	res = ftmodemctl(p, p->mctl);
 	if(res < 0)
 		return res;
 
-	bauddiv = ftbaudcalcdiv(ser, ser->baud);
-	res = ftdiwrite(ser, bauddiv, 0x1&(bauddiv>>16), FTSETBaudRate);
+	bauddiv = ftbaudcalcdiv(p->s, p->baud);
+	res = ftdiwrite(p, bauddiv, (bauddiv>>16) & 1, FTSETBaudRate);
 
 	dsprint(2, "serial: setparam res: %d\n", res);
 	return res;
@@ -443,40 +454,46 @@ ftsetparam(Serial *ser)
 static void
 ftgettype(Serial *ser)
 {
-	int inter, nifcs, i, outhdrsz, dno;
+	int i, outhdrsz, dno, pksz;
 	ulong baudbase;
 	Conf *cnf;
 
-	inter = 0;
+	pksz = Packsz;
  	/* Assume it is not the original SIO device for now. */
 	baudbase = ClockNew / 2;
 	outhdrsz = 0;
-	nifcs = 0;
 	dno = ser->dev->usb->dno;
 	cnf = ser->dev->usb->conf[0];
+	ser->nifcs = 0;
 	for(i = 0; i < Niface; i++)
 		if(cnf->iface[i] != nil)
-			nifcs++;
-	if(nifcs> 1) {
-		/* Multiple interfaces.  Assume FT2232C. */
-		ser->type = FT2232C;
+			ser->nifcs++;
+	if(ser->nifcs > 1) {
 		/*
-		 * BUG: If there is more than one interface, we use the second.
-		 * We only support 1 interface at the moment...
-		 * This works well for the sheeva/JTAG, but in other
-		 * cases we are ignoring nifcs-1 interfaces and using
-		 * the second (weird).  The problem is we have to rethink
-		 * the whole scheme to make it work with various ifaces.
+		 * Multiple interfaces.  default assume FT2232C,
 		 */
-		inter = PITA;
+		if(dno == 0x500)
+			ser->type = FT2232C;
+		else if(dno == 0x600)
+			ser->type = FTKINDR;
+		else if(dno == 0x700){
+			ser->type = FT2232H;
+			pksz = Maxpacksz;
+		} else if(dno == 0x800){
+			ser->type = FT4232H;
+			pksz = Maxpacksz;
+		} else
+			ser->type = FT2232C;
+
+		ser->jtag = 0;
 
 		/*
 		 * BM-type devices have a bug where dno gets set
 		 * to 0x200 when serial is 0.
 		 */
 		if(dno < 0x500)
-			fprint(2, "serial: warning: dno too low for "
-				"multi-interface device\n");
+			fprint(2, "serial: warning: dno %d too low for "
+				"multi-interface device\n", dno);
 	} else if(dno < 0x200) {
 		/* Old device.  Assume it is the original SIO. */
 		ser->type = SIO;
@@ -492,10 +509,11 @@ ftgettype(Serial *ser)
 	else			/* Assume it is an FT232BM (or FT245BM) */
 		ser->type = FT232BM;
 
+	ser->maxrtrans = ser->maxwtrans = pksz;
 	ser->baudbase = baudbase;
 	ser->outhdrsz = outhdrsz;
 	ser->inhdrsz = 2;
-	ser->interfc = inter;
+
 	dsprint (2, "serial: detected type: %#x\n", ser->type);
 }
 
@@ -521,81 +539,87 @@ ftmatch(Serial *ser, char *info)
 }
 
 static int
-ftuseinhdr(Serial *ser, uchar *b)
+ftuseinhdr(Serialport *p, uchar *b)
 {
 	if(b[0] & FTICTS)
-		ser->cts = 1;
+		p->cts = 1;
 	else
-		ser->cts = 0;
+		p->cts = 0;
 	if(b[0] & FTIDSR)
-		ser->dsr = 1;
+		p->dsr = 1;
 	else
-		ser->dsr = 0;
+		p->dsr = 0;
 	if(b[0] & FTIRI)
-		ser->ring = 1;
+		p->ring = 1;
 	else
-		ser->ring = 0;
+		p->ring = 0;
 	if(b[0] & FTIRLSD)
-		ser->rlsd = 1;
+		p->rlsd = 1;
 	else
-		ser->rlsd = 0;
+		p->rlsd = 0;
 
 	if(b[1] & FTIOE)
-		ser->novererr++;
+		p->novererr++;
 	if(b[1] & FTIPE)
-		ser->nparityerr++;
+		p->nparityerr++;
 	if(b[1] & FTIFE)
-		ser->nframeerr++;
+		p->nframeerr++;
 	if(b[1] & FTIBI)
-		ser->nbreakerr++;
+		p->nbreakerr++;
 	return 0;
 }
 
 static int
-ftsetouthdr(Serial *ser, uchar *b, int len)
+ftsetouthdr(Serialport *p, uchar *b, int len)
 {
-	if(ser->outhdrsz != 0)
+	if(p->s->outhdrsz != 0)
 		b[0] = FTOPORT | (FTOLENMSK & len);
-	return ser->outhdrsz;
+	return p->s->outhdrsz;
 }
 
 static int
-wait4data(Serial *ser, uchar *data, int count)
+wait4data(Serialport *p, uchar *data, int count)
 {
+	int d;
+	Serial *ser;
+
+	ser = p->s;
+
 	qunlock(ser);
-	recvul(ser->w4data);
+	d = sendul(p->w4data, 1);
+	if(d <= 0)
+		return -1;
 	qlock(ser);
-	if(ser->ndata >= count)
-		ser->ndata -= count;
+	if(p->ndata >= count)
+		p->ndata -= count;
 	else{
-		count = ser->ndata;
-		ser->ndata = 0;
+		count = p->ndata;
+		p->ndata = 0;
 	}
 	assert(count >= 0);
-	assert(ser->ndata >= 0);
-	memmove(data, ser->data, count);
-	if(ser->ndata != 0)
-		memmove(ser->data, ser->data+count, ser->ndata);
+	assert(p->ndata >= 0);
+	memmove(data, p->data, count);
+	if(p->ndata != 0)
+		memmove(p->data, p->data+count, p->ndata);
 
-	sendul(ser->gotdata, 1);
+	recvul(p->gotdata);
 	return count;
 }
 
 static int
-wait4write(Serial *ser, uchar *data, int count)
+wait4write(Serialport *p, uchar *data, int count)
 {
 	int off, fd;
 	uchar *b;
+	Serial *ser;
 
-	// qunlock(ser);
-	// recvul(ser->w4empty);	should I really?
-	// qlock(ser);
+	ser = p->s;
 
 	b = emallocz(count+ser->outhdrsz, 1);
-	off = ftsetouthdr(ser, b, count);
+	off = ftsetouthdr(p, b, count);
 	memmove(b+off, data, count);
 
-	fd = ser->epout->dfd;
+	fd = p->epout->dfd;
 	qunlock(ser);
 	count = write(fd, b, count+off);
 	qlock(ser);
@@ -606,62 +630,110 @@ wait4write(Serial *ser, uchar *data, int count)
 typedef struct Packser Packser;
 struct Packser{
 	int	nb;
-	uchar	b[Packsz];
+	uchar	b[Bufsiz];
 };
 
 typedef struct Areader Areader;
 struct Areader{
-	Serial	*s;
+	Serialport	*p;
 	Channel	*c;
 };
 
+static void
+shutdownchan(Channel *c)
+{
+	Packser *bp;
+
+	while((bp=nbrecvp(c)) != nil)
+		free(bp);
+	chanfree(c);
+}
+
+int
+cpdata(Serial *ser, Serialport *port, uchar *out, uchar *in, int sz)
+{
+	int i, ncp, ntotcp, pksz;
+
+	pksz = ser->maxrtrans;
+	ntotcp = 0;
+
+	for(i = 0; i < sz; i+= pksz){
+		ftuseinhdr(port, in + i);
+		if(sz - i > pksz)
+			ncp = pksz - ser->inhdrsz;
+		else
+			ncp = sz - i - ser->inhdrsz;
+		memmove(out, in + i + ser->inhdrsz, ncp);
+		out += ncp;
+		ntotcp += ncp;
+	}
+	return ntotcp;
+}
+
 static void
 epreader(void *u)
 {
-	int dfd, rcount;
+	int dfd, rcount, cl;
 	char err[40];
 	Areader *a;
 	Channel *c;
-	Packser *p;
+	Packser *pk;
 	Serial *ser;
+	Serialport *p;
 
 	threadsetname("epreader proc");
 	a = u;
-	ser = a->s;
+	p = a->p;
+	ser = p->s;
 	c = a->c;
 	free(a);
 
 	qlock(ser);
-	dfd = ser->epin->dfd;
+	dfd = p->epin->dfd;
 	qunlock(ser);
 
-	p = nil;
+	pk = nil;
 	do {
-		if (p == nil)
-			p = emallocz(sizeof(Packser), 1);
-		rcount = read(dfd, p->b, sizeof p->b);
+		if (pk == nil)
+			pk = emallocz(sizeof(Packser), 1);
+		rcount = read(dfd, pk->b, sizeof pk->b);
 		if(serialdebug > 5)
-			dsprint(2, "%d %#ux%#ux ", rcount, ser->data[0],
-				ser->data[1]);
-		if(rcount <= 0)
+			dsprint(2, "%d %#ux%#ux ", rcount, p->data[0],
+				p->data[1]);
+		if(rcount < 0)
 			break;
+		if(rcount == 0)
+			continue;
 		if(rcount >= ser->inhdrsz){
-			ftuseinhdr(ser, p->b);
-			rcount -= ser->inhdrsz;
-			memmove(p->b, p->b + ser->inhdrsz, rcount);
+			rcount = cpdata(ser, p, pk->b, pk->b, rcount);
 			if(rcount != 0){
-				p->nb = rcount;
-				sendp(c, p);
-				p = nil;
-			}
+				pk->nb = rcount;
+				cl = sendp(c, pk);
+				if(cl < 0){
+					/*
+					 * if it was a time-out, I don't want
+					 * to give back an error.
+					 */
+					rcount = 0;
+					break;
+				}
+			}else
+				free(pk);
+			pk = nil;
 		}
 	} while(rcount >= 0 || (rcount < 0 && strstr(err, "timed out") != nil));
 
 	if(rcount < 0)
-		fprint(2, "%s: error reading %s: %r\n", argv0, ser->fs.name);
-	free(p);
-	sendp(c, nil);
+		fprint(2, "%s: error reading %s: %r\n", argv0, p->fs.name);
+	free(pk);
+	nbsendp(c, nil);
+	if(p->w4data != nil)
+		chanclose(p->w4data);
+	if(p->gotdata != nil)
+		chanclose(p->gotdata);
+	devctl(ser->dev, "detach");
 	closedev(ser->dev);
+	usbfsdel(&p->fs);
 }
 
 static void
@@ -669,106 +741,123 @@ statusreader(void *u)
 {
 	Areader *a;
 	Channel *c;
-	Packser *p;
+	Packser *pk;
+	Serialport *p;
 	Serial *ser;
+	int cl;
 
-	ser = u;
+	p = u;
+	ser = p->s;
 	threadsetname("statusreader thread");
 	/* big buffering, fewer bytes lost */
 	c = chancreate(sizeof(Packser *), 128);
 	a = emallocz(sizeof(Areader), 1);
-	a->s = ser;
+	a->p = p;
 	a->c = c;
 	incref(ser->dev);
 	proccreate(epreader, a, 16*1024);
 
-	while((p = recvp(c)) != nil){
-		memmove(ser->data, p->b, p->nb);
-		ser->ndata = p->nb;
-		free(p);
-		dsprint(2, "serial: status reader %d \n", ser->ndata);
+	while((pk = recvp(c)) != nil){
+		memmove(p->data, pk->b, pk->nb);
+		p->ndata = pk->nb;
+		free(pk);
+		dsprint(2, "serial: status reader %d \n", p->ndata);
 		/* consume it all */
-		while(ser->ndata != 0){
+		while(p->ndata != 0){
 			dsprint(2, "serial: status reader to consume: %d\n",
-				ser->ndata);
-			sendul(ser->w4data, 1);
-			recvul(ser->gotdata);
+				p->ndata);
+			cl = recvul(p->w4data);
+			if(cl  < 0)
+				break;
+			cl = sendul(p->gotdata, 1);
+			if(cl  < 0)
+				break;
 		}
 	}
-	/* don't free a; epreader may still be using it. */
-	free(c);
+
+	shutdownchan(c);
+	devctl(ser->dev, "detach");
 	closedev(ser->dev);
+	usbfsdel(&p->fs);
 }
 
 static int
 ftreset(Serial *ser)
 {
-	ftdiwrite(ser, FTRESETCTLVAL, 0, FTRESET);
+	Serialport *p;
+	int i;
+
+	p = ser->p;
+	for(i = 0; i < Maxifc; i++)
+		if(!p[i].isjtag && p[i].s != nil)
+			ftdiwrite(&p[i], FTRESETCTLVAL, 0, FTRESET);
 	return 0;
 }
 
 static int
-ftinit(Serial *ser)
+ftinit(Serialport *p)
 {
-	qlock(ser);
-	serialreset(ser);
-	qunlock(ser);
+	Serial *ser;
 
+	ser = p->s;
 	incref(ser->dev);
-	threadcreate(statusreader, ser, 8*1024);
+	threadcreate(statusreader, p, 8*1024);
 	return 0;
 }
 
 static int
-ftsetbreak(Serial *ser, int val)
+ftsetbreak(Serialport *p, int val)
 {
-	return ftdiwrite(ser, (val != 0? FTSETBREAK: 0), 0, FTSETDATA);
+	return ftdiwrite(p, (val != 0? FTSETBREAK: 0), 0, FTSETDATA);
 }
 
 static int
-ftclearpipes(Serial *ser)
+ftclearpipes(Serialport *p)
 {
 	/* maybe can be done in one... */
-	ftdiwrite(ser, FTRESETCTLVALPURGETX, 0, FTRESET);
-	ftdiwrite(ser, FTRESETCTLVALPURGERX, 0, FTRESET);
+	ftdiwrite(p, FTRESETCTLVALPURGETX, 0, FTRESET);
+	ftdiwrite(p, FTRESETCTLVALPURGERX, 0, FTRESET);
 	return 0;
 }
 
 static int
-setctlline(Serial *ser, uchar val)
+setctlline(Serialport *p, uchar val)
 {
-	return ftdiwrite(ser, val | (val << 8), 0, FTSETMODEMCTRL);
+	return ftdiwrite(p, val | (val << 8), 0, FTSETMODEMCTRL);
 }
 
 static void
-updatectlst(Serial *ser, int val)
+updatectlst(Serialport *p, int val)
 {
-	if(ser->rts)
-		ser->ctlstate |= val;
+	if(p->rts)
+		p->ctlstate |= val;
 	else
-		ser->ctlstate &= ~val;
+		p->ctlstate &= ~val;
 }
 
 static int
-setctl(Serial *ser)
+setctl(Serialport *p)
 {
 	int res;
+	Serial *ser;
+
+	ser = p->s;
 
 	if(ser->dev->usb->vid == FTVid && ser->dev->usb->did ==  FTHETIRA1Did){
 		fprint(2, "serial: cannot set lines for this device\n");
-		updatectlst(ser, CtlRTS|CtlDTR);
-		ser->rts = ser->dtr = 1;
+		updatectlst(p, CtlRTS|CtlDTR);
+		p->rts = p->dtr = 1;
 		return -1;
 	}
 
 	/* NB: you can not set DTR and RTS with one control message */
-	updatectlst(ser, CtlRTS);
-	res = setctlline(ser, (CtlRTS<<8)|ser->ctlstate);
+	updatectlst(p, CtlRTS);
+	res = setctlline(p, (CtlRTS<<8)|p->ctlstate);
 	if(res < 0)
 		return res;
 
-	updatectlst(ser, CtlDTR);
-	res = setctlline(ser, (CtlDTR<<8)|ser->ctlstate);
+	updatectlst(p, CtlDTR);
+	res = setctlline(p, (CtlDTR<<8)|p->ctlstate);
 	if(res < 0)
 		return res;
 
@@ -776,25 +865,30 @@ setctl(Serial *ser)
 }
 
 static int
-ftsendlines(Serial *ser)
+ftsendlines(Serialport *p)
 {
 	int res;
 
-	dsprint(2, "serial: sendlines: %#2.2x\n", ser->ctlstate);
-	res = setctl(ser);
+	dsprint(2, "serial: sendlines: %#2.2x\n", p->ctlstate);
+	res = setctl(p);
 	dsprint(2, "serial: sendlines res: %d\n", res);
 	return 0;
 }
 
 static int
-ftseteps(Serial *ser)
+ftseteps(Serialport *p)
 {
 	char *s;
+	Serial *ser;
+
+	ser = p->s;
+
+	s = smprint("maxpkt %d", ser->maxrtrans);
+	devctl(p->epin, s);
+	free(s);
 
-	s = smprint("maxpkt %d", Packsz);
-	devctl(ser->epin, s);
-	devctl(ser->epout, s);
-	ser->maxread = ser->maxwrite = Packsz;
+	s = smprint("maxpkt %d", ser->maxwtrans);
+	devctl(p->epout, s);
 	free(s);
 	return 0;
 }

+ 24 - 13
sys/src/cmd/usb/serial/ftdi.h

@@ -1,7 +1,6 @@
 enum {
 	/* used by devices which don't provide their own Vid */
 	FTVid		= 0x0403,
-	FTOpenrdDid	= 0x9E90,
 
 	FTSheevaVid	= 0x9E88,
 	FTSheevaDid	= 0x9E8F,
@@ -13,24 +12,24 @@ enum {
 	FTRELAISDid	= 0xFA10,	/* Relais device */
 
 	/* NF reader */
-	FTNFRICVid = 0x0DCD,
-	FTNFRICDid = 0x0001,
+	FTNFRICVid	= 0x0DCD,
+	FTNFRICDid	= 0x0001,
 
-	FTACTZWAVEDid = 0xF2D0,		/* www.irtrans.de device */
+	FTACTZWAVEDid	= 0xF2D0,		/* www.irtrans.de device */
 
 	/*
 	 * ACT Solutions HomePro ZWave interface
 	 * http://www.act-solutions.com/HomePro.htm)
 	 */
-	FTIRTRANSDid = 0xFC60,
+	FTIRTRANSDid	= 0xFC60,
 
 	/*
 	 * www.thoughttechnology.com/ TT-USB
 	 */
-	FTTTUSBDid = 0xFF20,
+	FTTTUSBDid	= 0xFF20,
 
 	/* iPlus device */
-	FTIPLUSDid = 0xD070,
+	FTIPLUSDid	= 0xD070,
 
 	/* www.crystalfontz.com devices */
 	FTXF632Did = 0xFC08,	/* 632: 16x2 Character Display */
@@ -46,7 +45,7 @@ enum {
 	 * Video Networks Limited / Homechoice in the UK
 	 * use an ftdi-based device for their 1Mb broadband
 	 */
-	FTVNHCPCUSBDDid = 0xfe38,
+	FTVNHCPCUSBDDid	= 0xfe38,
 
 	/*
 	 * PCDJ use ftdi based dj-controllers
@@ -381,6 +380,11 @@ enum {
 	 * ACG Identification Technologies GmbH products http://www.acg.de/
 	 */
 	FTACGHFDUALDid	= 0xDD20,		/* HF Dual ISO Reader (RFID) */
+	/*
+	 * new high speed devices
+	 */
+	FT4232HDid	= 0x6011,		/* FTDI FT4232H based device */
+	
 };
 
 /* Commands */
@@ -395,7 +399,11 @@ enum {
 	FTSETERRORCHAR,			/* Set the error character */
 	FTSETLATENCYTIMER,		/* Set the latency timer */
 	FTGETLATENCYTIMER,		/* Get the latency timer */
-	FTGETE2READ	= 0x90,		/* Read an address */
+	FTSETBITMODE,			/* Set bigbang mode */
+	FTGETPINS,			/* Read pins state */
+	FTGETE2READ	= 0x90,		/* Read an address from EEPROM */
+	FTSETE2WRITE,			/* Write to address on the EEPROM */
+	FTSETE2ERASE,			/* Erase address on the EEPROM */
 };
 
 enum {
@@ -446,10 +454,13 @@ enum {
 
 /* chip type */
 enum {
-	SIO	= 1,
-	FT8U232AM= 2,
-	FT232BM	= 3,
-	FT2232C	= 4,
+	SIO		= 1,
+	FT8U232AM	= 2,
+	FT232BM		= 3,
+	FT2232C		= 4,
+	FTKINDR		= 5,
+	FT2232H		= 6,
+	FT4232H		= 7,
 };
 
 enum {

+ 109 - 77
sys/src/cmd/usb/serial/prolific.c

@@ -81,9 +81,12 @@ dumpbuf(uchar *buf, int bufsz)
 }
 
 static int
-vendorread(Serial *ser, int val, int index, uchar *buf)
+vendorread(Serialport *p, int val, int index, uchar *buf)
 {
 	int res;
+	Serial *ser;
+
+	ser = p->s;
 
 	dsprint(2, "serial: vendorread val: 0x%x idx:%d buf:%p\n",
 		val, index, buf);
@@ -94,9 +97,12 @@ vendorread(Serial *ser, int val, int index, uchar *buf)
 }
 
 static int
-vendorwrite(Serial *ser, int val, int index)
+vendorwrite(Serialport *p, int val, int index)
 {
 	int res;
+	Serial *ser;
+
+	ser = p->s;
 
 	dsprint(2, "serial: vendorwrite val: 0x%x idx:%d\n", val, index);
 	res = usbcmd(ser->dev, Rh2d | Rvendor | Rdev, VendorWriteReq,
@@ -107,31 +113,39 @@ vendorwrite(Serial *ser, int val, int index)
 
 /* BUG: I could probably read Dcr0 and set only the bits */
 static int
-plmodemctl(Serial *ser, int set)
+plmodemctl(Serialport *p, int set)
 {
+	Serial *ser;
+
+	ser = p->s;
+
 	if(set == 0){
-		ser->mctl = 0;
-		vendorwrite(ser, Dcr0Idx|DcrSet, Dcr0Init);
+		p->mctl = 0;
+		vendorwrite(p, Dcr0Idx|DcrSet, Dcr0Init);
 		return 0;
 	}
 
-	ser->mctl = 1;
+	p->mctl = 1;
 	if(ser->type == TypeHX)
-		vendorwrite(ser, Dcr0Idx|DcrSet, Dcr0Init|Dcr0HwFcX);
+		vendorwrite(p, Dcr0Idx|DcrSet, Dcr0Init|Dcr0HwFcX);
 	else
-		vendorwrite(ser, Dcr0Idx|DcrSet, Dcr0Init|Dcr0HwFcH);
+		vendorwrite(p, Dcr0Idx|DcrSet, Dcr0Init|Dcr0HwFcH);
 	return 0;
 }
 
 static int
-plgetparam(Serial *ser)
+plgetparam(Serialport *p)
 {
 	uchar buf[ParamReqSz];
 	int res;
+	Serial *ser;
+
+	ser = p->s;
+
 
 	res = usbcmd(ser->dev, Rd2h | Rclass | Riface, GetLineReq,
 		0, 0, buf, sizeof buf);
-	ser->baud = GET4(buf);
+	p->baud = GET4(buf);
 
 	/*
 	 * with the Pl9 interface it is not possible to set `1.5' as stop bits
@@ -143,11 +157,11 @@ plgetparam(Serial *ser)
 	if(buf[4] == 1)
 		fprint(2, "warning, stop bit set to 1.5 unsupported");
 	else if(buf[4] == 0)
-		ser->stop = 1;
+		p->stop = 1;
 	else if(buf[4] == 2)
-		ser->stop = 2;
-	ser->parity = buf[5];
-	ser->bits = buf[6];
+		p->stop = 2;
+	p->parity = buf[5];
+	p->bits = buf[6];
 
 	dsprint(2, "serial: getparam: ");
 	if(serialdebug)
@@ -157,27 +171,30 @@ plgetparam(Serial *ser)
 }
 
 static int
-plsetparam(Serial *ser)
+plsetparam(Serialport *p)
 {
 	uchar buf[ParamReqSz];
 	int res;
+	Serial *ser;
+
+	ser = p->s;
 
-	PUT4(buf, ser->baud);
+	PUT4(buf, p->baud);
 
-	if(ser->stop == 1)
+	if(p->stop == 1)
 		buf[4] = 0;
-	else if(ser->stop == 2)
+	else if(p->stop == 2)
 		buf[4] = 2; 			/* see comment in getparam */
-	buf[5] = ser->parity;
-	buf[6] = ser->bits;
+	buf[5] = p->parity;
+	buf[6] = p->bits;
 
 	dsprint(2, "serial: setparam: ");
 	if(serialdebug)
 		dumpbuf(buf, sizeof buf);
 	res = usbcmd(ser->dev, Rh2d | Rclass | Riface, SetLineReq,
 		0, 0, buf, sizeof buf);
-	plmodemctl(ser, ser->mctl);
-	plgetparam(ser);		/* make sure our state corresponds */
+	plmodemctl(p, p->mctl);
+	plgetparam(p);		/* make sure our state corresponds */
 
 	dsprint(2, "serial: setparam res: %d\n", res);
 	return res;
@@ -215,15 +232,16 @@ heuristicid(ulong csp, ulong maxpkt)
 }
 
 static int
-plinit(Serial *ser)
+plinit(Serialport *p)
 {
 	char *st;
 	uchar *buf;
 	ulong csp, maxpkt, dno;
+	Serial *ser;
 
+	ser = p->s;
 	buf = emallocz(VendorReqSz, 1);
-	qlock(ser);
-	serialreset(ser);
+	dsprint(2, "plinit\n");
 
 	csp = ser->dev->usb->csp;
 	maxpkt = ser->dev->maxpkt;
@@ -234,105 +252,117 @@ plinit(Serial *ser)
 
 	dsprint(2, "serial: type %d\n", ser->type);
 
-	vendorread(ser, 0x8484, 0, buf);
-	vendorwrite(ser, 0x0404, 0);
-	vendorread(ser, 0x8484, 0, buf);
-	vendorread(ser, 0x8383, 0, buf);
-	vendorread(ser, 0x8484, 0, buf);
-	vendorwrite(ser, 0x0404, 1);
-	vendorread(ser, 0x8484, 0, buf);
-	vendorread(ser, 0x8383, 0, buf);
+	vendorread(p, 0x8484, 0, buf);
+	vendorwrite(p, 0x0404, 0);
+	vendorread(p, 0x8484, 0, buf);
+	vendorread(p, 0x8383, 0, buf);
+	vendorread(p, 0x8484, 0, buf);
+	vendorwrite(p, 0x0404, 1);
+	vendorread(p, 0x8484, 0, buf);
+	vendorread(p, 0x8383, 0, buf);
 
-	vendorwrite(ser, Dcr0Idx|DcrSet, Dcr0Init);
-	vendorwrite(ser, Dcr1Idx|DcrSet, Dcr1Init);
+	vendorwrite(p, Dcr0Idx|DcrSet, Dcr0Init);
+	vendorwrite(p, Dcr1Idx|DcrSet, Dcr1Init);
 
 	if(ser->type == TypeHX)
-		vendorwrite(ser, Dcr2Idx|DcrSet, Dcr2InitX);
+		vendorwrite(p, Dcr2Idx|DcrSet, Dcr2InitX);
 	else
-		vendorwrite(ser, Dcr2Idx|DcrSet, Dcr2InitH);
+		vendorwrite(p, Dcr2Idx|DcrSet, Dcr2InitH);
 
-	plgetparam(ser);
+	plgetparam(p);
 	qunlock(ser);
 	free(buf);
 	st = emallocz(255, 1);
 	qlock(ser);
 	if(serialdebug)
-		serdumpst(ser, st, 255);
+		serdumpst(p, st, 255);
 	dsprint(2, st);
-	qunlock(ser);
 	free(st);
-	/* ser gets freed by closedev, the process has a reference */
+	/* p gets freed by closedev, the process has a reference */
 	incref(ser->dev);
-	proccreate(statusreader, ser, 8*1024);
+	proccreate(statusreader, p, 8*1024);
 	return 0;
 }
 
 static int
-plsetbreak(Serial *ser, int val)
+plsetbreak(Serialport *p, int val)
 {
+	Serial *ser;
+
+	ser = p->s;
 	return usbcmd(ser->dev, Rh2d | Rclass | Riface,
 		(val != 0? BreakOn: BreakOff), val, 0, nil, 0);
 }
 
 static int
-plclearpipes(Serial *ser)
+plclearpipes(Serialport *p)
 {
+	Serial *ser;
+
+	ser = p->s;
+
 	if(ser->type == TypeHX){
-		vendorwrite(ser, PipeDSRst, 0);
-		vendorwrite(ser, PipeUSRst, 0);
+		vendorwrite(p, PipeDSRst, 0);
+		vendorwrite(p, PipeUSRst, 0);
 	}else{
-		if(unstall(ser->dev, ser->epout, Eout) < 0)
+		if(unstall(ser->dev, p->epout, Eout) < 0)
 			dprint(2, "disk: unstall epout: %r\n");
-		if(unstall(ser->dev, ser->epin, Ein) < 0)
+		if(unstall(ser->dev, p->epin, Ein) < 0)
 			dprint(2, "disk: unstall epin: %r\n");
-		if(unstall(ser->dev, ser->epintr, Ein) < 0)
+		if(unstall(ser->dev, p->epintr, Ein) < 0)
 			dprint(2, "disk: unstall epintr: %r\n");
 	}
 	return 0;
 }
 
 static int
-setctlline(Serial *ser, uchar val)
+setctlline(Serialport *p, uchar val)
 {
+	Serial *ser;
+
+	ser = p->s;
 	return usbcmd(ser->dev, Rh2d | Rclass | Riface, SetCtlReq,
 		val, 0, nil, 0);
 }
 
 static void
-composectl(Serial *ser)
+composectl(Serialport *p)
 {
-	if(ser->rts)
-		ser->ctlstate |= CtlRTS;
+	if(p->rts)
+		p->ctlstate |= CtlRTS;
 	else
-		ser->ctlstate &= ~CtlRTS;
-	if(ser->dtr)
-		ser->ctlstate |= CtlDTR;
+		p->ctlstate &= ~CtlRTS;
+	if(p->dtr)
+		p->ctlstate |= CtlDTR;
 	else
-		ser->ctlstate &= ~CtlDTR;
+		p->ctlstate &= ~CtlDTR;
 }
 
-int
-plsendlines(Serial *ser)
+static int
+plsendlines(Serialport *p)
 {
 	int res;
 
-	dsprint(2, "serial: sendlines: %#2.2x\n", ser->ctlstate);
-	composectl(ser);
-	res = setctlline(ser, ser->ctlstate);
+	dsprint(2, "serial: sendlines: %#2.2x\n", p->ctlstate);
+	composectl(p);
+	res = setctlline(p, p->ctlstate);
 	dsprint(2, "serial: sendlines res: %d\n", res);
 	return 0;
 }
 
 static int
-plreadstatus(Serial *ser)
+plreadstatus(Serialport *p)
 {
 	int nr, dfd;
 	char err[40];
 	uchar buf[VendorReqSz];
+	Serial *ser;
+
+	ser = p->s;
 
 	qlock(ser);
 	dsprint(2, "serial: reading from interrupt\n");
-	dfd = ser->epintr->dfd;
+	dfd = p->epintr->dfd;
 
 	qunlock(ser);
 	nr = read(dfd, buf, sizeof buf);
@@ -350,17 +380,17 @@ plreadstatus(Serial *ser)
 	if(nr < 0)
 		dsprint(2, "serial: reading status: %r");
 	else if(nr >= sizeof buf - 1){
-		ser->dcd = buf[8] & DcdStatus;
-		ser->dsr = buf[8] & DsrStatus;
-		ser->cts = buf[8] & BreakerrStatus;
-		ser->ring = buf[8] & RingStatus;
-		ser->cts = buf[8] & CtsStatus;
+		p->dcd = buf[8] & DcdStatus;
+		p->dsr = buf[8] & DsrStatus;
+		p->cts = buf[8] & BreakerrStatus;
+		p->ring = buf[8] & RingStatus;
+		p->cts = buf[8] & CtsStatus;
 		if(buf[8] & FrerrStatus)
-			ser->nframeerr++;
+			p->nframeerr++;
 		if(buf[8] & ParerrStatus)
-			ser->nparityerr++;
+			p->nparityerr++;
 		if(buf[8] & OvererrStatus)
-			ser->novererr++;
+			p->novererr++;
 	} else
 		dsprint(2, "serial: bad status read %d\n", nr);
 	dsprint(2, "serial: finished read from interrupt %d\n", nr);
@@ -371,11 +401,13 @@ plreadstatus(Serial *ser)
 static void
 statusreader(void *u)
 {
+	Serialport *p;
 	Serial *ser;
 
-	ser = u;
+	p = u;
+	ser = p->s;
 	threadsetname("statusreaderproc");
-	while(plreadstatus(ser) >= 0)
+	while(plreadstatus(p) >= 0)
 		;
 	fprint(2, "serial: statusreader exiting\n");
 	closedev(ser->dev);
@@ -387,10 +419,10 @@ statusreader(void *u)
  */
 
 static int
-plseteps(Serial *ser)
+plseteps(Serialport *p)
 {
-	devctl(ser->epin,  "maxpkt 256");
-	devctl(ser->epout, "maxpkt 256");
+	devctl(p->epin,  "maxpkt 256");
+	devctl(p->epout, "maxpkt 256");
 	return 0;
 }
 

+ 219 - 148
sys/src/cmd/usb/serial/serial.c

@@ -1,8 +1,6 @@
 /*
  * This part takes care of locking except for initialization and
  * other threads created by the hw dep. drivers.
- * BUG: An error on the device does not make the driver exit.
- * It probably should.
  */
 
 #include <u.h>
@@ -40,48 +38,65 @@ static Dirtab dirtab[] = {
 
 static int sdebug;
 
-int
-serialnop(Serial *)
-{
-	return 0;
-}
-
-int
-serialnopctl(Serial *, int)
-{
-	return 0;
-}
-
 static void
 serialfatal(Serial *ser)
 {
+	Serialport *p;
+	int i;
+
 	dsprint(2, "serial: fatal error, detaching\n");
 	devctl(ser->dev, "detach");
-	usbfsdel(&ser->fs);
+
+	for(i = 0; i < ser->nifcs; i++){
+		p = &ser->p[i];
+		if(p->isjtag)
+			continue;
+		usbfsdel(&p->fs);
+		if(p->w4data != nil)
+			chanclose(p->w4data);
+		if(p->gotdata != nil)
+			chanclose(p->gotdata);
+		if(p->readc)
+			chanclose(p->readc);
+	}
 }
 
 /* I sleep with the lock... only way to drain in general */
 static void
-serialdrain(Serial *ser)
+serialdrain(Serialport *p)
 {
+	Serial *ser;
 	uint baud, pipesize;
 
-	if(ser->maxwrite < 256)
+	ser = p->s;
+	baud = p->baud;
+
+	if(p->baud == ~0)
+		return;
+	if(ser->maxwtrans < 256)
 		pipesize = 256;
 	else
-		pipesize = ser->maxwrite;
-	baud = ser->baud;
+		pipesize = ser->maxwtrans;
 	/* wait for the at least 256-byte pipe to clear */
 	sleep(10 + pipesize/((1 + baud)*1000));
 	if(ser->clearpipes != nil)
-		ser->clearpipes(ser);
+		ser->clearpipes(p);
 }
 
+/* BUG: separate reset per port */
 int
 serialreset(Serial *ser)
 {
+	Serialport *p;
+	int i;
+
 	/* cmd for reset */
-	serialdrain(ser);
+	for(i = 0; i < ser->nifcs; i++){
+		p = &ser->p[i];
+		if(p->isjtag)
+			continue;
+		serialdrain(p);
+	}
 	if(ser->reset != nil)
 		ser->reset(ser);
 	return 0;
@@ -103,18 +118,20 @@ serialrecover(Serial *ser, char *err)
 }
 
 static int
-serialctl(Serial *p, char *cmd)
+serialctl(Serialport *p, char *cmd)
 {
+	Serial *ser;
 	int c, i, n, nf, nop, nw, par, drain, set, lines;
 	char *f[16];
 	uchar x;
 
+	ser = p->s;
 	drain = set = lines = 0;
 	nf = tokenize(cmd, f, nelem(f));
 	for(i = 0; i < nf; i++){
 		if(strncmp(f[i], "break", 5) == 0){
-			if(p->setbreak != nil)
-				p->setbreak(p, 1);
+			if(ser->setbreak != nil)
+				ser->setbreak(p, 1);
 			continue;
 		}
 
@@ -156,9 +173,9 @@ serialctl(Serial *p, char *cmd)
 			break;
 		case 'k':
 			drain++;
-			p->setbreak(p, 1);
+			ser->setbreak(p, 1);
 			sleep(n);
-			p->setbreak(p, 0);
+			ser->setbreak(p, 0);
 			break;
 		case 'l':
 			drain++;
@@ -167,8 +184,8 @@ serialctl(Serial *p, char *cmd)
 			break;
 		case 'm':
 			drain++;
-			if(p->modemctl != nil)
-				p->modemctl(p, n);
+			if(ser->modemctl != nil)
+				ser->modemctl(p, n);
 			if(n == 0)
 				p->cts = 0;
 			break;
@@ -220,12 +237,12 @@ serialctl(Serial *p, char *cmd)
 				x = CTLS;
 			else
 				x = CTLQ;
-			if(p->wait4write != nil)
-				nw = p->wait4write(p, &x, 1);
+			if(ser->wait4write != nil)
+				nw = ser->wait4write(p, &x, 1);
 			else
 				nw = write(p->epout->dfd, &x, 1);
 			if(nw != 1){
-				serialrecover(p, "");
+				serialrecover(ser, "");
 				return -1;
 			}
 			break;
@@ -241,10 +258,10 @@ serialctl(Serial *p, char *cmd)
 	if(drain)
 		serialdrain(p);
 	if(lines && !set){
-		if(p->sendlines != nil && p->sendlines(p) < 0)
+		if(ser->sendlines != nil && ser->sendlines(p) < 0)
 			return -1;
 	} else if(set){
-		if(p->setparam != nil && p->setparam(p) < 0)
+		if(ser->setparam != nil && ser->setparam(p) < 0)
 			return -1;
 	}
 	return 0;
@@ -253,44 +270,51 @@ serialctl(Serial *p, char *cmd)
 char *pformat = "noems";
 
 char *
-serdumpst(Serial *ser, char *buf, int bufsz)
+serdumpst(Serialport *p, char *buf, int bufsz)
 {
 	char *e, *s;
+	Serial *ser;
+
+	ser = p->s;
 
 	e = buf + bufsz;
-	s = seprint(buf, e, "b%d ", ser->baud);
-	s = seprint(s, e, "c%d ", ser->dcd);	/* unimplemented */
-	s = seprint(s, e, "d%d ", ser->dtr);
-	s = seprint(s, e, "e%d ", ser->dsr);	/* unimplemented */
-	s = seprint(s, e, "l%d ", ser->bits);
-	s = seprint(s, e, "m%d ", ser->mctl);
-	if(ser->parity >= 0 || ser->parity < strlen(pformat))
-		s = seprint(s, e, "p%c ", pformat[ser->parity]);
+	s = seprint(buf, e, "b%d ", p->baud);
+	s = seprint(s, e, "c%d ", p->dcd);	/* unimplemented */
+	s = seprint(s, e, "d%d ", p->dtr);
+	s = seprint(s, e, "e%d ", p->dsr);	/* unimplemented */
+	s = seprint(s, e, "l%d ", p->bits);
+	s = seprint(s, e, "m%d ", p->mctl);
+	if(p->parity >= 0 || p->parity < strlen(pformat))
+		s = seprint(s, e, "p%c ", pformat[p->parity]);
 	else
 		s = seprint(s, e, "p%c ", '?');
-	s = seprint(s, e, "r%d ", ser->rts);
-	s = seprint(s, e, "s%d ", ser->stop);
-	s = seprint(s, e, "i%d ", ser->fifo);
+	s = seprint(s, e, "r%d ", p->rts);
+	s = seprint(s, e, "s%d ", p->stop);
+	s = seprint(s, e, "i%d ", p->fifo);
 	s = seprint(s, e, "\ndev(%d) ", 0);
 	s = seprint(s, e, "type(%d)  ", ser->type);
-	s = seprint(s, e, "framing(%d) ", ser->nframeerr);
-	s = seprint(s, e, "overruns(%d) ", ser->novererr);
-	s = seprint(s, e, "berr(%d) ", ser->nbreakerr);
-	s = seprint(s, e, " serr(%d) ", ser->nparityerr);
+	s = seprint(s, e, "framing(%d) ", p->nframeerr);
+	s = seprint(s, e, "overruns(%d) ", p->novererr);
+	s = seprint(s, e, "berr(%d) ", p->nbreakerr);
+	s = seprint(s, e, " serr(%d) ", p->nparityerr);
 	return s;
 }
 
 static int
-serinit(Serial *ser)
+serinit(Serialport *p)
 {
 	int res;
-
 	res = 0;
+	Serial *ser;
+
+	ser = p->s;
+
 	if(ser->init != nil)
-		res = ser->init(ser);
+		res = ser->init(p);
 	if(ser->getparam != nil)
-		ser->getparam(ser);
-	ser->nframeerr = ser->nparityerr = ser->nbreakerr = ser->novererr = 0;
+		ser->getparam(p);
+	p->nframeerr = p->nparityerr = p->nbreakerr = p->novererr = 0;
+
 	return res;
 }
 
@@ -300,7 +324,7 @@ dwalk(Usbfs *fs, Fid *fid, char *name)
 	int i;
 	char *dname;
 	Qid qid;
-	Serial *ser;
+	Serialport *p;
 
 	qid = fid->qid;
 	if((qid.type & QTDIR) == 0){
@@ -316,9 +340,9 @@ dwalk(Usbfs *fs, Fid *fid, char *name)
 		return 0;
 	}
 
-	ser = fs->aux;
+	p = fs->aux;
 	for(i = 1; i < nelem(dirtab); i++){
-		dname = smprint(dirtab[i].name, ser->fs.name);
+		dname = smprint(dirtab[i].name, p->fs.name);
 		if(strcmp(name, dname) == 0){
 			qid.path = i | fs->qid;
 			qid.vers = 0;
@@ -337,18 +361,18 @@ static void
 dostat(Usbfs *fs, int path, Dir *d)
 {
 	Dirtab *t;
-	Serial *ser;
+	Serialport *p;
 
 	t = &dirtab[path];
 	d->qid.path = path;
 	d->qid.type = t->mode >> 24;
 	d->mode = t->mode;
-	ser = fs->aux;
+	p = fs->aux;
 
 	if(strcmp(t->name, "/") == 0)
 		d->name = t->name;
 	else
-		snprint(d->name, Namesz, t->name, ser->fs.name);
+		snprint(d->name, Namesz, t->name, p->fs.name);
 	d->length = 0;
 }
 
@@ -367,10 +391,10 @@ static int
 dopen(Usbfs *fs, Fid *fid, int)
 {
 	ulong path;
-	// Serial *ser;
+	// Serialport *p;
 
 	path = fid->qid.path & ~fs->qid;
-	// ser = fs->aux;
+	// p = fs->aux;
 	switch(path){		/* BUG: unneeded? */
 	case Qdata:
 		dsprint(2, "serial, opened data\n");
@@ -417,12 +441,14 @@ dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
 	ulong path;
 	char *e, *buf, *err;	/* change */
 	Qid q;
+	Serialport *p;
 	Serial *ser;
 	static int errrun, good;
 
 	q = fid->qid;
 	path = fid->qid.path & ~fs->qid;
-	ser = fs->aux;
+	p = fs->aux;
+	ser = p->s;
 
 	buf = emallocz(Serbufsize, 1);
 	err = emallocz(Serbufsize, 1);
@@ -437,13 +463,13 @@ dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
 		dsprint(2, "serial: reading from data\n");
 		do {
 			err[0] = 0;
-			dfd = ser->epin->dfd;
+			dfd = p->epin->dfd;
 			if(usbdebug >= 3)
 				dsprint(2, "serial: reading: %ld\n", count);
 
 			assert(count > 0);
 			if(ser->wait4data != nil)
-				rcount = ser->wait4data(ser, data, count);
+				rcount = ser->wait4data(p, data, count);
 			else{
 				qunlock(ser);
 				rcount = read(dfd, data, count);
@@ -462,7 +488,7 @@ dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
 					/* the line has been dropped; give up */
 					qunlock(ser);
 					fprint(2, "%s: line %s is gone: %r\n",
-						argv0, ser->fs.name);
+						argv0, p->fs.name);
 					threadexitsall("serial line gone");
 				}
 			} else {
@@ -486,7 +512,7 @@ dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
 		if(offset != 0)
 			count = 0;
 		else {
-			e = serdumpst(ser, buf, Serbufsize);
+			e = serdumpst(p, buf, Serbufsize);
 			count = usbreadbuf(data, count, 0, buf, e - buf);
 		}
 		break;
@@ -498,17 +524,19 @@ dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
 }
 
 static long
-altwrite(Serial *ser, uchar *buf, long count)
+altwrite(Serialport *p, uchar *buf, long count)
 {
 	int nw, dfd;
 	char err[128];
+	Serial *ser;
 
+	ser = p->s;
 	do{
 		if(ser->wait4write != nil)
 			/* unlocked inside later */
-			nw = ser->wait4write(ser, buf, count);
+			nw = ser->wait4write(p, buf, count);
 		else{
-			dfd = ser->epout->dfd;
+			dfd = p->epout->dfd;
 			qunlock(ser);
 			nw = write(dfd, buf, count);
 			qlock(ser);
@@ -520,7 +548,7 @@ altwrite(Serial *ser, uchar *buf, long count)
 		dsprint(2, "serial: need to recover, status in write %d %r\n",
 			nw);
 		snprint(err, sizeof err, "%r");
-		serialrecover(ser, err);
+		serialrecover(p->s, err);
 	}
 	return nw;
 }
@@ -530,21 +558,23 @@ dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong)
 {
 	ulong path;
 	char *cmd;
+	Serialport *p;
 	Serial *ser;
 
-	ser = fs->aux;
+	p = fs->aux;
+	ser = p->s;
 	path = fid->qid.path & ~fs->qid;
 
 	qlock(ser);
 	switch(path){
 	case Qdata:
-		count = altwrite(ser, (uchar *)buf, count);
+		count = altwrite(p, (uchar *)buf, count);
 		break;
 	case Qctl:
 		cmd = emallocz(count+1, 1);
 		memmove(cmd, buf, count);
 		cmd[count] = 0;
-		if(serialctl(ser, cmd) < 0){
+		if(serialctl(p, cmd) < 0){
 			qunlock(ser);
 			werrstr(Ebadctl);
 			free(cmd);
@@ -562,69 +592,69 @@ dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong)
 }
 
 static int
-openeps(Serial *ser, int epin, int epout, int epintr)
+openeps(Serialport *p, int epin, int epout, int epintr)
 {
-	ser->epin = openep(ser->dev, epin);
-	if(ser->epin == nil){
+	Serial *ser;
+
+	ser = p->s;
+	p->epin = openep(ser->dev, epin);
+	if(p->epin == nil){
 		fprint(2, "serial: openep %d: %r\n", epin);
 		return -1;
 	}
-	ser->epout = openep(ser->dev, epout);
-	if(ser->epout == nil){
+	p->epout = openep(ser->dev, epout);
+	if(p->epout == nil){
 		fprint(2, "serial: openep %d: %r\n", epout);
-		closedev(ser->epin);
+		closedev(p->epin);
 		return -1;
 	}
 
-	devctl(ser->epin,  "timeout 1000");
-	devctl(ser->epout, "timeout 1000");
+	devctl(p->epin,  "timeout 1000");
+	devctl(p->epout, "timeout 1000");
 
 	if(ser->hasepintr){
-		ser->epintr = openep(ser->dev, epintr);
-		if(ser->epintr == nil){
+		p->epintr = openep(ser->dev, epintr);
+		if(p->epintr == nil){
 			fprint(2, "serial: openep %d: %r\n", epintr);
-			closedev(ser->epin);
-			closedev(ser->epout);
+			closedev(p->epin);
+			closedev(p->epout);
 			return -1;
 		}
-		opendevdata(ser->epintr, OREAD);
-		devctl(ser->epintr, "timeout 1000");
+		opendevdata(p->epintr, OREAD);
+		devctl(p->epintr, "timeout 1000");
 	}
 
 	if(ser->seteps!= nil)
-		ser->seteps(ser);
-	opendevdata(ser->epin, OREAD);
-	opendevdata(ser->epout, OWRITE);
-	if(ser->epin->dfd < 0 || ser->epout->dfd < 0 ||
-	    (ser->hasepintr && ser->epintr->dfd < 0)){
+		ser->seteps(p);
+	opendevdata(p->epin, OREAD);
+	opendevdata(p->epout, OWRITE);
+	if(p->epin->dfd < 0 ||p->epout->dfd < 0 ||
+	    (ser->hasepintr && p->epintr->dfd < 0)){
 		fprint(2, "serial: open i/o ep data: %r\n");
-		closedev(ser->epin);
-		closedev(ser->epout);
+		closedev(p->epin);
+		closedev(p->epout);
 		if(ser->hasepintr)
-			closedev(ser->epintr);
+			closedev(p->epintr);
 		return -1;
 	}
 	return 0;
 }
 
 static int
-findendpoints(Serial *ser)
+findendpoints(Serial *ser, int ifc)
 {
 	int i, epin, epout, epintr;
 	Ep *ep, **eps;
-	Usbdev *ud;
+	///Usbdev *ud;
 
 	epintr = epin = epout = -1;
-	ud = ser->dev->usb;
+	//ud = ser->dev->usb;
 
 	/*
 	 * interfc 0 means start from the start which is equiv to
 	 * iterate through endpoints probably, could be done better
 	 */
-	if(ser->interfc == 0)
-		eps = ud->ep;
-	else
-		eps = ser->dev->usb->conf[0]->iface[ser->interfc]->ep;
+	eps = ser->dev->usb->conf[0]->iface[ifc]->ep;
 
 	for(i = 0; i < Niface; i++){
 		if((ep = eps[i]) == nil)
@@ -639,22 +669,22 @@ findendpoints(Serial *ser)
 				epout = ep->id;
 		}
 	}
-	dprint(2, "serial: ep ids: in %d out %d intr %d\n", epin, epout, epintr);
+	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))
 		return -1;
 
-	if(openeps(ser, epin, epout, epintr) < 0)
+	if(openeps(&ser->p[ifc], epin, epout, epintr) < 0)
 		return -1;
 
-	dprint(2, "serial: ep in %s out %s\n", ser->epin->dir, ser->epout->dir);
+	dprint(2, "serial: ep in %s out %s\n", ser->p[ifc].epin->dir, ser->p[ifc].epout->dir);
 	if(ser->hasepintr)
-		dprint(2, "serial: ep intr %s\n", ser->epintr->dir);
+		dprint(2, "serial: ep intr %s\n", ser->p[ifc].epintr->dir);
 
 	if(usbdebug > 1 || serialdebug > 2){
-		devctl(ser->epin,  "debug 1");
-		devctl(ser->epout, "debug 1");
+		devctl(ser->p[ifc].epin,  "debug 1");
+		devctl(ser->p[ifc].epout, "debug 1");
 		if(ser->hasepintr)
-			devctl(ser->epintr, "debug 1");
+			devctl(ser->p[ifc].epintr, "debug 1");
 		devctl(ser->dev, "debug 1");
 	}
 	return 0;
@@ -672,17 +702,28 @@ static void
 serdevfree(void *a)
 {
 	Serial *ser = a;
+	Serialport *p;
+	int i;
 
 	if(ser == nil)
 		return;
-	if(ser->hasepintr)
-		closedev(ser->epintr);
-	closedev(ser->epin);
-	closedev(ser->epout);
-	ser->epintr = ser->epin = ser->epout = nil;
-	chanfree(ser->w4data);
-	chanfree(ser->gotdata);
-	chanfree(ser->w4empty);
+
+	for(i = 0; i < ser->nifcs; i++){
+		p = &ser->p[i];
+
+		if(ser->hasepintr)
+			closedev(p->epintr);
+		closedev(p->epin);
+		closedev(p->epout);
+		p->epintr = p->epin = p->epout = nil;
+		if(p->w4data != nil)
+			chanfree(p->w4data);
+		if(p->gotdata != nil)
+			chanfree(p->gotdata);
+		if(p->readc)
+			chanfree(p->readc);
+
+	}
 	free(ser);
 }
 
@@ -694,11 +735,28 @@ static Usbfs serialfs = {
 	.stat =	dstat,
 };
 
+static void
+serialfsend(Usbfs *fs)
+{
+	Serialport *p;
+
+	p = fs->aux;
+
+	if(p->w4data != nil)
+		chanclose(p->w4data);
+	if(p->gotdata != nil)
+		chanclose(p->gotdata);
+	if(p->readc)
+		chanclose(p->readc);
+}
+
 int
 serialmain(Dev *dev, int argc, char* argv[])
 {
 	Serial *ser;
+	Serialport *p;
 	char buf[50];
+	int i;
 
 	ARGBEGIN{
 	case 'd':
@@ -711,17 +769,15 @@ serialmain(Dev *dev, int argc, char* argv[])
 		return usage();
 
 	ser = dev->aux = emallocz(sizeof(Serial), 1);
-	/* BUG: could this go wrong? channel leaks? */
-	ser->w4data  = chancreate(sizeof(ulong), 0);
-	ser->gotdata = chancreate(sizeof(ulong), 0);
-	ser->w4empty = chancreate(sizeof(ulong), 0);
-	ser->maxread = ser->maxwrite = sizeof ser->data;
+	ser->maxrtrans = ser->maxwtrans = sizeof ser->p[0].data;
+	ser->maxread = ser->maxwrite = sizeof ser->p[0].data;
 	ser->dev = dev;
 	dev->free = serdevfree;
+	ser->jtag = -1;
+	ser->nifcs = 1;
 
 	snprint(buf, sizeof buf, "vid %#06x did %#06x",
 		dev->usb->vid, dev->usb->did);
-	ser->fs = serialfs;
 	if(plmatch(buf) == 0){
 		ser->hasepintr = 1;
 		ser->Serialops = plops;
@@ -731,35 +787,50 @@ serialmain(Dev *dev, int argc, char* argv[])
 		ser->Serialops = ftops;
 	else {
 		werrstr("serial: no serial devices found");
-		closedev(dev);
 		return -1;
 	}
 
-	if(findendpoints(ser) < 0){
-		werrstr("serial: no endpoints found");
-		closedev(dev);
-		return -1;
+	for(i = 0; i < ser->nifcs; i++){
+		p = &ser->p[i];
+		p->interfc = i;
+		if(i == ser->jtag){
+			p->isjtag++;
+			continue;
+		}
+		p->s = ser;
+		p->fs = serialfs;
+		if(findendpoints(ser, i) < 0){
+			werrstr("serial: no endpoints found for ifc %d", i);
+			return -1;
+		}
+		p->w4data  = chancreate(sizeof(ulong), 0);
+		p->gotdata = chancreate(sizeof(ulong), 0);
 	}
-	if(serinit(ser) < 0){
-		dprint(2, "serial: serinit: %r\n");
-		closedev(dev);
-		return -1;
+
+	qlock(ser);
+	serialreset(ser);
+	for(i = 0; i < ser->nifcs; i++){
+		p = &ser->p[i];
+		if(p->isjtag){
+			dsprint(2, "serial: ignoring JTAG interface %d %p\n", i, p);
+			continue;
+		}
+		dprint(2, "serial: valid interface, calling serinit\n");
+		if(serinit(p) < 0){
+			dprint(2, "serial: serinit: %r\n");
+			return -1;
+		}
+
+		dsprint(2, "serial: adding interface %d, %p\n", p->interfc, p);
+		snprint(p->fs.name, sizeof p->fs.name, "eiaU%d", dev->id+i-1);
+		fprint(2, "%s\n", p->fs.name);
+		p->fs.dev = dev;
+		incref(dev);
+		p->fs.aux = p;
+		p->fs.end = serialfsend;
+		usbfsadd(&p->fs);
 	}
 
-	dirtab[Qdata].name = smprint("eiau%d", dev->id);
-	dirtab[Qctl].name  = smprint("eiau%dctl", dev->id);
-	/*
-	 * it would nice to get rid of this extra level of directory,
-	 * thus reducing /dev/eiaU6/eiau6* to /dev/eiau6*
-	 * for easier binding into /dev with `bind -a /dev/eiaU* /dev'.
-	 */
-	snprint(ser->fs.name, sizeof ser->fs.name, "eiaU%d", dev->id);
-	fprint(2, "%s\n", ser->fs.name);
-	ser->fs.dev = dev;
-	incref(dev);
-	ser->fs.aux = ser;
-	usbfsadd(&ser->fs);
-
-	closedev(dev);
+	qunlock(ser);
 	return 0;
 }

+ 50 - 39
sys/src/cmd/usb/serial/serial.h

@@ -1,42 +1,40 @@
-typedef struct Serialops Serialops;
 typedef struct Serial Serial;
+typedef struct Serialops Serialops;
+typedef struct Serialport Serialport;
 
 struct Serialops {
-	int	(*seteps)(Serial*);
-	int	(*init)(Serial*);
-	int	(*getparam)(Serial*);
-	int	(*setparam)(Serial*);
-	int	(*clearpipes)(Serial*);
+	int	(*seteps)(Serialport*);
+	int	(*init)(Serialport*);
+	int	(*getparam)(Serialport*);
+	int	(*setparam)(Serialport*);
+	int	(*clearpipes)(Serialport*);
 	int	(*reset)(Serial*);
-	int	(*sendlines)(Serial*);
-	int	(*modemctl)(Serial*, int);
-	int	(*setbreak)(Serial*, int);
-	int	(*readstatus)(Serial*);
-	int	(*wait4data)(Serial*, uchar *, int);
-	int	(*wait4write)(Serial*, uchar *, int);
+	int	(*sendlines)(Serialport*);
+	int	(*modemctl)(Serialport*, int);
+	int	(*setbreak)(Serialport*, int);
+	int	(*readstatus)(Serialport*);
+	int	(*wait4data)(Serialport*, uchar *, int);
+	int	(*wait4write)(Serialport*, uchar *, int);
 };
 
-enum{
+enum {
 	DataBufSz = 8*1024,
+	Maxifc = 16,
 };
 
-/*
- * TODO: this should have an array of serial devices and
- * have the common part separated.  Means rethinking locking though.
- */
-struct Serial {
-	QLock;
-	Dev	*dev;		/* usb device*/
-	Dev	*ep;		/* endpoint to get events */
-	Dev	*epintr;
+
+struct Serialport {
+	Serial	*s;		/* device we belong to */
+	int	isjtag;
+
+	Dev	*epintr;	/* may not exist */
+
 	Dev	*epin;
 	Dev	*epout;
-	Usbfs	fs;
-	int	type;
-	int	recover;
-	int	hasepintr;
 
+	Usbfs	fs;
 	uchar	ctlstate;
+
 	/* serial parameters */
 	uint	baud;
 	int	stop;
@@ -61,23 +59,39 @@ struct Serial {
 	int	novererr;
 	int	enabled;
 
-	int	maxread;
-	int	maxwrite;
-
-	int	inhdrsz;
-	int	outhdrsz;
-	Serialops;
-
-	int	baudbase;	/* for special baud base settings, see ftdi */
 	int	interfc;	/* interfc on the device for ftdi */
 
 	Channel *w4data;
 	Channel *gotdata;
-	Channel *w4empty;
+	Channel *readc;		/* to uncouple reads, only used in ftdi... */
 	int	ndata;
 	uchar	data[DataBufSz];
 };
 
+struct Serial {
+	QLock;
+	Dev	*dev;		/* usb device*/
+
+	int	type;		/* serial model subtype */
+	int	recover;	/* # of non-fatal recovery tries */
+	Serialops;
+
+	int	hasepintr;
+
+	int	jtag;		/* index of jtag interface, -1 none */
+	int	nifcs;		/* # of serial interfaces, including JTAG */
+	Serialport p[Maxifc];
+	int	maxrtrans;
+	int	maxwtrans;
+
+	int	maxread;
+	int	maxwrite;
+
+	int	inhdrsz;
+	int	outhdrsz;
+	int	baudbase;	/* for special baud base settings, see ftdi */
+};
+
 enum {
 	/* soft flow control chars */
 	CTLS	= 023,
@@ -108,7 +122,4 @@ extern int serialdebug;
 
 int	serialrecover(Serial *ser, char *err);
 int	serialreset(Serial *ser);
-char	*serdumpst(Serial *ser, char *buf, int bufsz);
-
-int	serialnop(Serial *);
-int	serialnopctl(Serial *, int);
+char	*serdumpst(Serialport *p, char *buf, int bufsz);

+ 9 - 4
sys/src/cmd/usb/serial/ucons.c

@@ -28,11 +28,16 @@ uconsmatch(char *info)
 }
 
 static int
-ucseteps(Serial *ser)
+ucseteps(Serialport *p)
 {
-	ser->maxread = ser->maxwrite = 8;
-	devctl(ser->epin,  "maxpkt 8");
-	devctl(ser->epout, "maxpkt 8");
+	Serial *ser;
+
+	ser = p->s;
+
+	p->baud = ~0;	/* not real port */
+	ser->maxrtrans = ser->maxwtrans = 8;
+	devctl(p->epin,  "maxpkt 8");
+	devctl(p->epout, "maxpkt 8");
 	return 0;
 }
 

+ 2 - 3
sys/src/cmd/usb/usbd/dev.c

@@ -147,10 +147,9 @@ startdevproc(void *a)
 	}else{
 		sa->pp->dev = opendev(d->dir);
 		sendul(sa->rc, 0);
-		if(dt->init(d, argc, argv) < 0){
+		if(dt->init(d, argc, argv) < 0)
 			fprint(2, "%s: %s: %r\n", argv0, dt->name);
-			closedev(d);
-		}
+		closedev(d);
 		free(sa);
 	}
 	threadexits(nil);

+ 2 - 3
sys/src/cmd/usb/usbd/usbd.c

@@ -647,7 +647,7 @@ work(void *a)
 		dprint(2, "%s: %s starting\n", argv0, fn);
 		h = newhub(fn, nil);
 		if(h == nil)
-			fprint(2, "%s: %s: %r\n", argv0, fn);
+			fprint(2, "%s: %s: newhub failed: %r\n", argv0, fn);
 		free(fn);
 	}
 	/*
@@ -661,7 +661,7 @@ work(void *a)
 	 * have to poll the root hub(s) in any case.
 	 */
 	for(;;){
-	Again:
+Again:
 		for(h = hubs; h != nil; h = h->next)
 			for(i = 1; i <= h->nport; i++)
 				if(enumhub(h, i) < 0){
@@ -676,7 +676,6 @@ work(void *a)
 		if(mustdump)
 			dump();
 	}
-
 }
 
 static int

+ 4 - 2
sys/src/cmd/usb/usbd/usbdb

@@ -2,6 +2,8 @@
 # others are not yet converted to sit in the usbd device driver library
 embed
 	kb	csp=0x010103 csp=0x020103	args=
-	disk	class=storage	args=
-	ether	class=comms	args=
+	disk	class=storage			args=
+#	ether	class=comms			args=
+	ether	class=255 csp=0x00ffff		args=
+#	serial	class=255 csp=0xffffff vid=0x9e88 did=0x9e8f	args=
 auto