xref: /plan9/sys/src/cmd/usb/serial/serial.c (revision ecc2a7c84df3ab5973ff36d23cbabfee616bd61d)
1b2495906SDavid du Colombier /*
2b2495906SDavid du Colombier  * This part takes care of locking except for initialization and
3b2495906SDavid du Colombier  * other threads created by the hw dep. drivers.
4b2495906SDavid du Colombier  * BUG: An error on the device does not make the driver exit.
5b2495906SDavid du Colombier  * It probably should.
6b2495906SDavid du Colombier  */
7b2495906SDavid du Colombier 
85e4f5c78SDavid du Colombier #include <u.h>
95e4f5c78SDavid du Colombier #include <libc.h>
105e4f5c78SDavid du Colombier #include <ctype.h>
115e4f5c78SDavid du Colombier #include <thread.h>
125e4f5c78SDavid du Colombier #include "usb.h"
135e4f5c78SDavid du Colombier #include "usbfs.h"
145e4f5c78SDavid du Colombier #include "serial.h"
154d52e0f0SDavid du Colombier #include "prolific.h"
164d52e0f0SDavid du Colombier #include "ucons.h"
1780e9508eSDavid du Colombier #include "ftdi.h"
184d52e0f0SDavid du Colombier 
195e4f5c78SDavid du Colombier int serialdebug;
205e4f5c78SDavid du Colombier 
215e4f5c78SDavid du Colombier enum {
225e4f5c78SDavid du Colombier 	/* Qids. Maintain order (relative to dirtabs structs) */
235e4f5c78SDavid du Colombier 	Qroot	= 0,
245e4f5c78SDavid du Colombier 	Qctl,
255e4f5c78SDavid du Colombier 	Qdata,
265e4f5c78SDavid du Colombier 	Qmax,
275e4f5c78SDavid du Colombier };
285e4f5c78SDavid du Colombier 
295e4f5c78SDavid du Colombier typedef struct Dirtab Dirtab;
305e4f5c78SDavid du Colombier struct Dirtab {
315e4f5c78SDavid du Colombier 	char	*name;
325e4f5c78SDavid du Colombier 	int	mode;
335e4f5c78SDavid du Colombier };
345e4f5c78SDavid du Colombier 
355e4f5c78SDavid du Colombier static Dirtab dirtab[] = {
365e4f5c78SDavid du Colombier 	[Qroot]	"/",		DMDIR|0555,
37b2495906SDavid du Colombier 	[Qdata]	"eiaU",		0660,
38b2495906SDavid du Colombier 	[Qctl]	"eiaUctl",	0664,
395e4f5c78SDavid du Colombier };
405e4f5c78SDavid du Colombier 
415e4f5c78SDavid du Colombier static int sdebug;
425e4f5c78SDavid du Colombier 
434d52e0f0SDavid du Colombier int
444d52e0f0SDavid du Colombier serialnop(Serial *)
454d52e0f0SDavid du Colombier {
464d52e0f0SDavid du Colombier 	return 0;
474d52e0f0SDavid du Colombier }
484d52e0f0SDavid du Colombier 
494d52e0f0SDavid du Colombier int
504d52e0f0SDavid du Colombier serialnopctl(Serial *, int)
514d52e0f0SDavid du Colombier {
524d52e0f0SDavid du Colombier 	return 0;
534d52e0f0SDavid du Colombier }
544d52e0f0SDavid du Colombier 
555e4f5c78SDavid du Colombier static void
565e4f5c78SDavid du Colombier serialfatal(Serial *ser)
575e4f5c78SDavid du Colombier {
585e4f5c78SDavid du Colombier 	dsprint(2, "serial: fatal error, detaching\n");
595e4f5c78SDavid du Colombier 	devctl(ser->dev, "detach");
605e4f5c78SDavid du Colombier 	usbfsdel(&ser->fs);
615e4f5c78SDavid du Colombier }
625e4f5c78SDavid du Colombier 
6380e9508eSDavid du Colombier /* I sleep with the lock... only way to drain in general */
645e4f5c78SDavid du Colombier static void
655e4f5c78SDavid du Colombier serialdrain(Serial *ser)
665e4f5c78SDavid du Colombier {
6780e9508eSDavid du Colombier 	uint baud, pipesize;
685e4f5c78SDavid du Colombier 
6980e9508eSDavid du Colombier 	if(ser->maxwrite < 256)
7080e9508eSDavid du Colombier 		pipesize = 256;
7180e9508eSDavid du Colombier 	else
7280e9508eSDavid du Colombier 		pipesize = ser->maxwrite;
735e4f5c78SDavid du Colombier 	baud = ser->baud;
7480e9508eSDavid du Colombier 	/* wait for the at least 256-byte pipe to clear */
7580e9508eSDavid du Colombier 	sleep(10 + pipesize/((1 + baud)*1000));
7680e9508eSDavid du Colombier 	if(ser->clearpipes != nil)
775e4f5c78SDavid du Colombier 		ser->clearpipes(ser);
785e4f5c78SDavid du Colombier }
795e4f5c78SDavid du Colombier 
805e4f5c78SDavid du Colombier int
815e4f5c78SDavid du Colombier serialreset(Serial *ser)
825e4f5c78SDavid du Colombier {
835e4f5c78SDavid du Colombier 	/* cmd for reset */
845e4f5c78SDavid du Colombier 	serialdrain(ser);
8580e9508eSDavid du Colombier 	if(ser->reset != nil)
8680e9508eSDavid du Colombier 		ser->reset(ser);
875e4f5c78SDavid du Colombier 	return 0;
885e4f5c78SDavid du Colombier }
895e4f5c78SDavid du Colombier 
905e4f5c78SDavid du Colombier /* call this if something goes wrong */
915e4f5c78SDavid du Colombier int
925e4f5c78SDavid du Colombier serialrecover(Serial *ser, char *err)
935e4f5c78SDavid du Colombier {
945e4f5c78SDavid du Colombier 	if(strstr(err, "detached") != nil)
955e4f5c78SDavid du Colombier 		return -1;
9616146bc9SDavid du Colombier 	if(ser->recover > 1)
975e4f5c78SDavid du Colombier 		serialfatal(ser);
9816146bc9SDavid du Colombier 	ser->recover++;
995e4f5c78SDavid du Colombier 	if(serialreset(ser) < 0)
1005e4f5c78SDavid du Colombier 		return -1;
10116146bc9SDavid du Colombier 	ser->recover = 0;
1025e4f5c78SDavid du Colombier 	return 0;
1035e4f5c78SDavid du Colombier }
1045e4f5c78SDavid du Colombier 
1055e4f5c78SDavid du Colombier static int
1065e4f5c78SDavid du Colombier serialctl(Serial *p, char *cmd)
1075e4f5c78SDavid du Colombier {
1085e4f5c78SDavid du Colombier 	int c, i, n, nf, nop, nw, par, drain, set, lines;
1095e4f5c78SDavid du Colombier 	char *f[16];
1105e4f5c78SDavid du Colombier 	uchar x;
1115e4f5c78SDavid du Colombier 
1125e4f5c78SDavid du Colombier 	drain = set = lines = 0;
1135e4f5c78SDavid du Colombier 	nf = tokenize(cmd, f, nelem(f));
1145e4f5c78SDavid du Colombier 	for(i = 0; i < nf; i++){
1155e4f5c78SDavid du Colombier 		if(strncmp(f[i], "break", 5) == 0){
11680e9508eSDavid du Colombier 			if(p->setbreak != nil)
1175e4f5c78SDavid du Colombier 				p->setbreak(p, 1);
1185e4f5c78SDavid du Colombier 			continue;
1195e4f5c78SDavid du Colombier 		}
1205e4f5c78SDavid du Colombier 
1215e4f5c78SDavid du Colombier 		nop = 0;
1225e4f5c78SDavid du Colombier 		n = atoi(f[i]+1);
1235e4f5c78SDavid du Colombier 		c = *f[i];
1245e4f5c78SDavid du Colombier 		if (isascii(c) && isupper(c))
1255e4f5c78SDavid du Colombier 			c = tolower(c);
1265e4f5c78SDavid du Colombier 		switch(c){
1275e4f5c78SDavid du Colombier 		case 'b':
1285e4f5c78SDavid du Colombier 			drain++;
1295e4f5c78SDavid du Colombier 			p->baud = n;
1305e4f5c78SDavid du Colombier 			set++;
1315e4f5c78SDavid du Colombier 			break;
1325e4f5c78SDavid du Colombier 		case 'c':
1335e4f5c78SDavid du Colombier 			p->dcd = n;
1345e4f5c78SDavid du Colombier 			// lines++;
1355e4f5c78SDavid du Colombier 			++nop;
1365e4f5c78SDavid du Colombier 			break;
1375e4f5c78SDavid du Colombier 		case 'd':
1385e4f5c78SDavid du Colombier 			p->dtr = n;
1395e4f5c78SDavid du Colombier 			lines++;
1405e4f5c78SDavid du Colombier 			break;
1415e4f5c78SDavid du Colombier 		case 'e':
1425e4f5c78SDavid du Colombier 			p->dsr = n;
1435e4f5c78SDavid du Colombier 			// lines++;
1445e4f5c78SDavid du Colombier 			++nop;
1455e4f5c78SDavid du Colombier 			break;
1465e4f5c78SDavid du Colombier 		case 'f':		/* flush the pipes */
1475e4f5c78SDavid du Colombier 			drain++;
1485e4f5c78SDavid du Colombier 			break;
1495e4f5c78SDavid du Colombier 		case 'h':		/* hangup?? */
1505e4f5c78SDavid du Colombier 			p->rts = p->dtr = 0;
1515e4f5c78SDavid du Colombier 			lines++;
1525e4f5c78SDavid du Colombier 			fprint(2, "serial: %c, unsure ctl\n", c);
1535e4f5c78SDavid du Colombier 			break;
1545e4f5c78SDavid du Colombier 		case 'i':
1555e4f5c78SDavid du Colombier 			++nop;
1565e4f5c78SDavid du Colombier 			break;
1575e4f5c78SDavid du Colombier 		case 'k':
1585e4f5c78SDavid du Colombier 			drain++;
1595e4f5c78SDavid du Colombier 			p->setbreak(p, 1);
1605e4f5c78SDavid du Colombier 			sleep(n);
1615e4f5c78SDavid du Colombier 			p->setbreak(p, 0);
1625e4f5c78SDavid du Colombier 			break;
1635e4f5c78SDavid du Colombier 		case 'l':
1645e4f5c78SDavid du Colombier 			drain++;
1655e4f5c78SDavid du Colombier 			p->bits = n;
1665e4f5c78SDavid du Colombier 			set++;
1675e4f5c78SDavid du Colombier 			break;
1685e4f5c78SDavid du Colombier 		case 'm':
1695e4f5c78SDavid du Colombier 			drain++;
17080e9508eSDavid du Colombier 			if(p->modemctl != nil)
1715e4f5c78SDavid du Colombier 				p->modemctl(p, n);
1725e4f5c78SDavid du Colombier 			if(n == 0)
1735e4f5c78SDavid du Colombier 				p->cts = 0;
1745e4f5c78SDavid du Colombier 			break;
1755e4f5c78SDavid du Colombier 		case 'n':
1765e4f5c78SDavid du Colombier 			p->blocked = n;
1775e4f5c78SDavid du Colombier 			++nop;
1785e4f5c78SDavid du Colombier 			break;
1795e4f5c78SDavid du Colombier 		case 'p':		/* extended... */
1805e4f5c78SDavid du Colombier 			if(strlen(f[i]) != 2)
1815e4f5c78SDavid du Colombier 				return -1;
1825e4f5c78SDavid du Colombier 			drain++;
18380e9508eSDavid du Colombier 			par = f[i][1];
1845e4f5c78SDavid du Colombier 			if(par == 'n')
1855e4f5c78SDavid du Colombier 				p->parity = 0;
1865e4f5c78SDavid du Colombier 			else if(par == 'o')
1875e4f5c78SDavid du Colombier 				p->parity = 1;
1885e4f5c78SDavid du Colombier 			else if(par == 'e')
1895e4f5c78SDavid du Colombier 				p->parity = 2;
1905e4f5c78SDavid du Colombier 			else if(par == 'm')	/* mark parity */
1915e4f5c78SDavid du Colombier 				p->parity = 3;
1925e4f5c78SDavid du Colombier 			else if(par == 's')	/* space parity */
1935e4f5c78SDavid du Colombier 				p->parity = 4;
1945e4f5c78SDavid du Colombier 			else
1955e4f5c78SDavid du Colombier 				return -1;
1965e4f5c78SDavid du Colombier 			set++;
1975e4f5c78SDavid du Colombier 			break;
1985e4f5c78SDavid du Colombier 		case 'q':
1995e4f5c78SDavid du Colombier 			// drain++;
2005e4f5c78SDavid du Colombier 			p->limit = n;
2015e4f5c78SDavid du Colombier 			++nop;
2025e4f5c78SDavid du Colombier 			break;
2035e4f5c78SDavid du Colombier 		case 'r':
2045e4f5c78SDavid du Colombier 			drain++;
2055e4f5c78SDavid du Colombier 			p->rts = n;
2065e4f5c78SDavid du Colombier 			lines++;
2075e4f5c78SDavid du Colombier 			break;
2085e4f5c78SDavid du Colombier 		case 's':
2095e4f5c78SDavid du Colombier 			drain++;
2105e4f5c78SDavid du Colombier 			p->stop = n;
2115e4f5c78SDavid du Colombier 			set++;
2125e4f5c78SDavid du Colombier 			break;
2135e4f5c78SDavid du Colombier 		case 'w':
2145e4f5c78SDavid du Colombier 			/* ?? how do I put this */
2155e4f5c78SDavid du Colombier 			p->timer = n * 100000LL;
2165e4f5c78SDavid du Colombier 			++nop;
2175e4f5c78SDavid du Colombier 			break;
2185e4f5c78SDavid du Colombier 		case 'x':
2195e4f5c78SDavid du Colombier 			if(n == 0)
2205e4f5c78SDavid du Colombier 				x = CTLS;
2215e4f5c78SDavid du Colombier 			else
2225e4f5c78SDavid du Colombier 				x = CTLQ;
22380e9508eSDavid du Colombier 			if(p->wait4write != nil)
22480e9508eSDavid du Colombier 				nw = p->wait4write(p, &x, 1);
22580e9508eSDavid du Colombier 			else
2265e4f5c78SDavid du Colombier 				nw = write(p->epout->dfd, &x, 1);
2275e4f5c78SDavid du Colombier 			if(nw != 1){
2285e4f5c78SDavid du Colombier 				serialrecover(p, "");
2295e4f5c78SDavid du Colombier 				return -1;
2305e4f5c78SDavid du Colombier 			}
2315e4f5c78SDavid du Colombier 			break;
2325e4f5c78SDavid du Colombier 		}
233*ecc2a7c8SDavid du Colombier 		/*
234*ecc2a7c8SDavid du Colombier 		 * don't print.  the condition is harmless and the print
235*ecc2a7c8SDavid du Colombier 		 * splatters all over the display.
236*ecc2a7c8SDavid du Colombier 		 */
237*ecc2a7c8SDavid du Colombier 		USED(nop);
238*ecc2a7c8SDavid du Colombier 		if (0 && nop)
2395e4f5c78SDavid du Colombier 			fprint(2, "serial: %c, unsupported nop ctl\n", c);
2405e4f5c78SDavid du Colombier 	}
2415e4f5c78SDavid du Colombier 	if(drain)
2425e4f5c78SDavid du Colombier 		serialdrain(p);
24380e9508eSDavid du Colombier 	if(lines && !set){
24480e9508eSDavid du Colombier 		if(p->sendlines != nil && p->sendlines(p) < 0)
2455e4f5c78SDavid du Colombier 			return -1;
24680e9508eSDavid du Colombier 	} else if(set){
24780e9508eSDavid du Colombier 		if(p->setparam != nil && p->setparam(p) < 0)
24880e9508eSDavid du Colombier 			return -1;
24980e9508eSDavid du Colombier 	}
2505e4f5c78SDavid du Colombier 	return 0;
2515e4f5c78SDavid du Colombier }
2525e4f5c78SDavid du Colombier 
2535e4f5c78SDavid du Colombier char *pformat = "noems";
2545e4f5c78SDavid du Colombier 
2555e4f5c78SDavid du Colombier char *
2564d52e0f0SDavid du Colombier serdumpst(Serial *ser, char *buf, int bufsz)
2575e4f5c78SDavid du Colombier {
2585e4f5c78SDavid du Colombier 	char *e, *s;
2595e4f5c78SDavid du Colombier 
2605e4f5c78SDavid du Colombier 	e = buf + bufsz;
2615e4f5c78SDavid du Colombier 	s = seprint(buf, e, "b%d ", ser->baud);
2625e4f5c78SDavid du Colombier 	s = seprint(s, e, "c%d ", ser->dcd);	/* unimplemented */
2635e4f5c78SDavid du Colombier 	s = seprint(s, e, "d%d ", ser->dtr);
2645e4f5c78SDavid du Colombier 	s = seprint(s, e, "e%d ", ser->dsr);	/* unimplemented */
2655e4f5c78SDavid du Colombier 	s = seprint(s, e, "l%d ", ser->bits);
2665e4f5c78SDavid du Colombier 	s = seprint(s, e, "m%d ", ser->mctl);
2675e4f5c78SDavid du Colombier 	if(ser->parity >= 0 || ser->parity < strlen(pformat))
2685e4f5c78SDavid du Colombier 		s = seprint(s, e, "p%c ", pformat[ser->parity]);
2695e4f5c78SDavid du Colombier 	else
2705e4f5c78SDavid du Colombier 		s = seprint(s, e, "p%c ", '?');
2715e4f5c78SDavid du Colombier 	s = seprint(s, e, "r%d ", ser->rts);
2725e4f5c78SDavid du Colombier 	s = seprint(s, e, "s%d ", ser->stop);
2735e4f5c78SDavid du Colombier 	s = seprint(s, e, "i%d ", ser->fifo);
2745e4f5c78SDavid du Colombier 	s = seprint(s, e, "\ndev(%d) ", 0);
2755e4f5c78SDavid du Colombier 	s = seprint(s, e, "type(%d)  ", ser->type);
2765e4f5c78SDavid du Colombier 	s = seprint(s, e, "framing(%d) ", ser->nframeerr);
2775e4f5c78SDavid du Colombier 	s = seprint(s, e, "overruns(%d) ", ser->novererr);
2785e4f5c78SDavid du Colombier 	s = seprint(s, e, "berr(%d) ", ser->nbreakerr);
2795e4f5c78SDavid du Colombier 	s = seprint(s, e, " serr(%d) ", ser->nparityerr);
2805e4f5c78SDavid du Colombier 	return s;
2815e4f5c78SDavid du Colombier }
2825e4f5c78SDavid du Colombier 
2835e4f5c78SDavid du Colombier static int
2845e4f5c78SDavid du Colombier serinit(Serial *ser)
2855e4f5c78SDavid du Colombier {
2865e4f5c78SDavid du Colombier 	int res;
2875e4f5c78SDavid du Colombier 
28880e9508eSDavid du Colombier 	res = 0;
28980e9508eSDavid du Colombier 	if(ser->init != nil)
2905e4f5c78SDavid du Colombier 		res = ser->init(ser);
29180e9508eSDavid du Colombier 	if(ser->getparam != nil)
29280e9508eSDavid du Colombier 		ser->getparam(ser);
2935e4f5c78SDavid du Colombier 	ser->nframeerr = ser->nparityerr = ser->nbreakerr = ser->novererr = 0;
2945e4f5c78SDavid du Colombier 	return res;
2955e4f5c78SDavid du Colombier }
2965e4f5c78SDavid du Colombier 
2975e4f5c78SDavid du Colombier static int
2985e4f5c78SDavid du Colombier dwalk(Usbfs *fs, Fid *fid, char *name)
2995e4f5c78SDavid du Colombier {
3005e4f5c78SDavid du Colombier 	int i;
3015e4f5c78SDavid du Colombier 	char *dname;
3025e4f5c78SDavid du Colombier 	Qid qid;
3035e4f5c78SDavid du Colombier 	Serial *ser;
3045e4f5c78SDavid du Colombier 
3055e4f5c78SDavid du Colombier 	qid = fid->qid;
3065e4f5c78SDavid du Colombier 	if((qid.type & QTDIR) == 0){
3075e4f5c78SDavid du Colombier 		werrstr("walk in non-directory");
3085e4f5c78SDavid du Colombier 		return -1;
3095e4f5c78SDavid du Colombier 	}
3105e4f5c78SDavid du Colombier 
3115e4f5c78SDavid du Colombier 	if(strcmp(name, "..") == 0){
3125e4f5c78SDavid du Colombier 		/* must be /eiaU%d; i.e. our root dir. */
3135e4f5c78SDavid du Colombier 		fid->qid.path = Qroot | fs->qid;
3145e4f5c78SDavid du Colombier 		fid->qid.vers = 0;
3155e4f5c78SDavid du Colombier 		fid->qid.type = QTDIR;
3165e4f5c78SDavid du Colombier 		return 0;
3175e4f5c78SDavid du Colombier 	}
3185e4f5c78SDavid du Colombier 
3195e4f5c78SDavid du Colombier 	ser = fs->aux;
3205e4f5c78SDavid du Colombier 	for(i = 1; i < nelem(dirtab); i++){
3215e4f5c78SDavid du Colombier 		dname = smprint(dirtab[i].name, ser->fs.name);
3225e4f5c78SDavid du Colombier 		if(strcmp(name, dname) == 0){
3235e4f5c78SDavid du Colombier 			qid.path = i | fs->qid;
3245e4f5c78SDavid du Colombier 			qid.vers = 0;
3255e4f5c78SDavid du Colombier 			qid.type = dirtab[i].mode >> 24;
3265e4f5c78SDavid du Colombier 			fid->qid = qid;
3275e4f5c78SDavid du Colombier 			free(dname);
3285e4f5c78SDavid du Colombier 			return 0;
3295e4f5c78SDavid du Colombier 		} else
3305e4f5c78SDavid du Colombier 			free(dname);
3315e4f5c78SDavid du Colombier 	}
3325e4f5c78SDavid du Colombier 	werrstr(Enotfound);
3335e4f5c78SDavid du Colombier 	return -1;
3345e4f5c78SDavid du Colombier }
3355e4f5c78SDavid du Colombier 
3365e4f5c78SDavid du Colombier static void
3375e4f5c78SDavid du Colombier dostat(Usbfs *fs, int path, Dir *d)
3385e4f5c78SDavid du Colombier {
3395e4f5c78SDavid du Colombier 	Dirtab *t;
3405e4f5c78SDavid du Colombier 	Serial *ser;
3415e4f5c78SDavid du Colombier 
3425e4f5c78SDavid du Colombier 	t = &dirtab[path];
3435e4f5c78SDavid du Colombier 	d->qid.path = path;
3445e4f5c78SDavid du Colombier 	d->qid.type = t->mode >> 24;
3455e4f5c78SDavid du Colombier 	d->mode = t->mode;
3465e4f5c78SDavid du Colombier 	ser = fs->aux;
3475e4f5c78SDavid du Colombier 
3485e4f5c78SDavid du Colombier 	if(strcmp(t->name, "/") == 0)
3495e4f5c78SDavid du Colombier 		d->name = t->name;
3505e4f5c78SDavid du Colombier 	else
3515e4f5c78SDavid du Colombier 		snprint(d->name, Namesz, t->name, ser->fs.name);
3525e4f5c78SDavid du Colombier 	d->length = 0;
3535e4f5c78SDavid du Colombier }
3545e4f5c78SDavid du Colombier 
3555e4f5c78SDavid du Colombier static int
3565e4f5c78SDavid du Colombier dstat(Usbfs *fs, Qid qid, Dir *d)
3575e4f5c78SDavid du Colombier {
3585e4f5c78SDavid du Colombier 	int path;
3595e4f5c78SDavid du Colombier 
3605e4f5c78SDavid du Colombier 	path = qid.path & ~fs->qid;
3615e4f5c78SDavid du Colombier 	dostat(fs, path, d);
3625e4f5c78SDavid du Colombier 	d->qid.path |= fs->qid;
3635e4f5c78SDavid du Colombier 	return 0;
3645e4f5c78SDavid du Colombier }
3655e4f5c78SDavid du Colombier 
3665e4f5c78SDavid du Colombier static int
3675e4f5c78SDavid du Colombier dopen(Usbfs *fs, Fid *fid, int)
3685e4f5c78SDavid du Colombier {
3695e4f5c78SDavid du Colombier 	ulong path;
3705e4f5c78SDavid du Colombier 	// Serial *ser;
3715e4f5c78SDavid du Colombier 
3725e4f5c78SDavid du Colombier 	path = fid->qid.path & ~fs->qid;
3735e4f5c78SDavid du Colombier 	// ser = fs->aux;
3745e4f5c78SDavid du Colombier 	switch(path){		/* BUG: unneeded? */
3755e4f5c78SDavid du Colombier 	case Qdata:
3764d52e0f0SDavid du Colombier 		dsprint(2, "serial, opened data\n");
3775e4f5c78SDavid du Colombier 		break;
3785e4f5c78SDavid du Colombier 	case Qctl:
3794d52e0f0SDavid du Colombier 		dsprint(2, "serial, opened ctl\n");
3805e4f5c78SDavid du Colombier 		break;
3815e4f5c78SDavid du Colombier 	}
3825e4f5c78SDavid du Colombier 	return 0;
3835e4f5c78SDavid du Colombier }
3845e4f5c78SDavid du Colombier 
3855e4f5c78SDavid du Colombier 
3865e4f5c78SDavid du Colombier static void
3875e4f5c78SDavid du Colombier filldir(Usbfs *fs, Dir *d, Dirtab *tab, int i)
3885e4f5c78SDavid du Colombier {
3895e4f5c78SDavid du Colombier 	d->qid.path = i | fs->qid;
3905e4f5c78SDavid du Colombier 	d->mode = tab->mode;
3915e4f5c78SDavid du Colombier 	if((d->mode & DMDIR) != 0)
3925e4f5c78SDavid du Colombier 		d->qid.type = QTDIR;
3935e4f5c78SDavid du Colombier 	else
3945e4f5c78SDavid du Colombier 		d->qid.type = QTFILE;
3955e4f5c78SDavid du Colombier 	d->name = tab->name;
3965e4f5c78SDavid du Colombier }
3975e4f5c78SDavid du Colombier 
3985e4f5c78SDavid du Colombier static int
3995e4f5c78SDavid du Colombier dirgen(Usbfs *fs, Qid, int i, Dir *d, void *)
4005e4f5c78SDavid du Colombier {
4015e4f5c78SDavid du Colombier 	i++;				/* skip root */
40280e9508eSDavid du Colombier 	if(i >= nelem(dirtab))
4035e4f5c78SDavid du Colombier 		return -1;
40480e9508eSDavid du Colombier 	filldir(fs, d, &dirtab[i], i);
4055e4f5c78SDavid du Colombier 	return 0;
4065e4f5c78SDavid du Colombier }
4075e4f5c78SDavid du Colombier 
408bfb6eab9SDavid du Colombier enum {
409bfb6eab9SDavid du Colombier 	Serbufsize	= 255,
410bfb6eab9SDavid du Colombier };
411bfb6eab9SDavid du Colombier 
4125e4f5c78SDavid du Colombier static long
4135e4f5c78SDavid du Colombier dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
4145e4f5c78SDavid du Colombier {
4155e4f5c78SDavid du Colombier 	int dfd;
4165e4f5c78SDavid du Colombier 	long rcount;
4175e4f5c78SDavid du Colombier 	ulong path;
41880e9508eSDavid du Colombier 	char *e, *buf, *err;	/* change */
4195e4f5c78SDavid du Colombier 	Qid q;
4205e4f5c78SDavid du Colombier 	Serial *ser;
421*ecc2a7c8SDavid du Colombier 	static int errrun, good;
4225e4f5c78SDavid du Colombier 
4235e4f5c78SDavid du Colombier 	q = fid->qid;
4245e4f5c78SDavid du Colombier 	path = fid->qid.path & ~fs->qid;
4255e4f5c78SDavid du Colombier 	ser = fs->aux;
4265e4f5c78SDavid du Colombier 
427bfb6eab9SDavid du Colombier 	buf = emallocz(Serbufsize, 1);
428bfb6eab9SDavid du Colombier 	err = emallocz(Serbufsize, 1);
4295e4f5c78SDavid du Colombier 	qlock(ser);
4305e4f5c78SDavid du Colombier 	switch(path){
4315e4f5c78SDavid du Colombier 	case Qroot:
4325e4f5c78SDavid du Colombier 		count = usbdirread(fs, q, data, count, offset, dirgen, nil);
4335e4f5c78SDavid du Colombier 		break;
4345e4f5c78SDavid du Colombier 	case Qdata:
43580e9508eSDavid du Colombier 		if(count > ser->maxread)
43680e9508eSDavid du Colombier 			count = ser->maxread;
43780e9508eSDavid du Colombier 		dsprint(2, "serial: reading from data\n");
4385e4f5c78SDavid du Colombier 		do {
4394d52e0f0SDavid du Colombier 			err[0] = 0;
44080e9508eSDavid du Colombier 			dfd = ser->epin->dfd;
44180e9508eSDavid du Colombier 			if(usbdebug >= 3)
44280e9508eSDavid du Colombier 				dsprint(2, "serial: reading: %ld\n", count);
44380e9508eSDavid du Colombier 
444*ecc2a7c8SDavid du Colombier 			assert(count > 0);
44580e9508eSDavid du Colombier 			if(ser->wait4data != nil)
44680e9508eSDavid du Colombier 				rcount = ser->wait4data(ser, data, count);
44780e9508eSDavid du Colombier 			else{
44880e9508eSDavid du Colombier 				qunlock(ser);
4495e4f5c78SDavid du Colombier 				rcount = read(dfd, data, count);
4505e4f5c78SDavid du Colombier 				qlock(ser);
45180e9508eSDavid du Colombier 			}
45225fc6993SDavid du Colombier 			/*
45325fc6993SDavid du Colombier 			 * if we encounter a long run of continuous read
45425fc6993SDavid du Colombier 			 * errors, do something drastic so that our caller
45525fc6993SDavid du Colombier 			 * doesn't just spin its wheels forever.
45625fc6993SDavid du Colombier 			 */
45725fc6993SDavid du Colombier 			if(rcount < 0) {
458bfb6eab9SDavid du Colombier 				snprint(err, Serbufsize, "%r");
459*ecc2a7c8SDavid du Colombier 				++errrun;
460*ecc2a7c8SDavid du Colombier 				sleep(20);
461*ecc2a7c8SDavid du Colombier 				if (good > 0 && errrun > 10000) {
46225fc6993SDavid du Colombier 					/* the line has been dropped; give up */
46325fc6993SDavid du Colombier 					qunlock(ser);
464*ecc2a7c8SDavid du Colombier 					fprint(2, "%s: line %s is gone: %r\n",
465*ecc2a7c8SDavid du Colombier 						argv0, ser->fs.name);
46625fc6993SDavid du Colombier 					threadexitsall("serial line gone");
46725fc6993SDavid du Colombier 				}
468*ecc2a7c8SDavid du Colombier 			} else {
46925fc6993SDavid du Colombier 				errrun = 0;
470*ecc2a7c8SDavid du Colombier 				good++;
471*ecc2a7c8SDavid du Colombier 			}
47280e9508eSDavid du Colombier 			if(usbdebug >= 3)
47380e9508eSDavid du Colombier 				dsprint(2, "serial: read: %s %ld\n", err, rcount);
4745e4f5c78SDavid du Colombier 		} while(rcount < 0 && strstr(err, "timed out") != nil);
4755e4f5c78SDavid du Colombier 
47680e9508eSDavid du Colombier 		dsprint(2, "serial: read from bulk %ld, %10.10s\n", rcount, err);
4775e4f5c78SDavid du Colombier 		if(rcount < 0){
4785e4f5c78SDavid du Colombier 			dsprint(2, "serial: need to recover, data read %ld %r\n",
4795e4f5c78SDavid du Colombier 				count);
4805e4f5c78SDavid du Colombier 			serialrecover(ser, err);
4815e4f5c78SDavid du Colombier 		}
48280e9508eSDavid du Colombier 		dsprint(2, "serial: read from bulk %ld\n", rcount);
4835e4f5c78SDavid du Colombier 		count = rcount;
4845e4f5c78SDavid du Colombier 		break;
4855e4f5c78SDavid du Colombier 	case Qctl:
48680e9508eSDavid du Colombier 		if(offset != 0)
4875e4f5c78SDavid du Colombier 			count = 0;
48880e9508eSDavid du Colombier 		else {
489bfb6eab9SDavid du Colombier 			e = serdumpst(ser, buf, Serbufsize);
4905e4f5c78SDavid du Colombier 			count = usbreadbuf(data, count, 0, buf, e - buf);
49180e9508eSDavid du Colombier 		}
4925e4f5c78SDavid du Colombier 		break;
4935e4f5c78SDavid du Colombier 	}
4945e4f5c78SDavid du Colombier 	qunlock(ser);
4955e4f5c78SDavid du Colombier 	free(err);
4965e4f5c78SDavid du Colombier 	free(buf);
4975e4f5c78SDavid du Colombier 	return count;
4985e4f5c78SDavid du Colombier }
4995e4f5c78SDavid du Colombier 
5005e4f5c78SDavid du Colombier static long
50180e9508eSDavid du Colombier altwrite(Serial *ser, uchar *buf, long count)
50280e9508eSDavid du Colombier {
50380e9508eSDavid du Colombier 	int nw, dfd;
50480e9508eSDavid du Colombier 	char err[128];
50580e9508eSDavid du Colombier 
50680e9508eSDavid du Colombier 	do{
50780e9508eSDavid du Colombier 		if(ser->wait4write != nil)
50880e9508eSDavid du Colombier 			/* unlocked inside later */
50980e9508eSDavid du Colombier 			nw = ser->wait4write(ser, buf, count);
51080e9508eSDavid du Colombier 		else{
51180e9508eSDavid du Colombier 			dfd = ser->epout->dfd;
51280e9508eSDavid du Colombier 			qunlock(ser);
51380e9508eSDavid du Colombier 			nw = write(dfd, buf, count);
51480e9508eSDavid du Colombier 			qlock(ser);
51580e9508eSDavid du Colombier 		}
51680e9508eSDavid du Colombier 		rerrstr(err, sizeof err);
51780e9508eSDavid du Colombier 	} while(nw < 0 && strstr(err, "timed out") != nil);
51880e9508eSDavid du Colombier 
51980e9508eSDavid du Colombier 	if(nw != count){
52080e9508eSDavid du Colombier 		dsprint(2, "serial: need to recover, status in write %d %r\n",
52180e9508eSDavid du Colombier 			nw);
52280e9508eSDavid du Colombier 		snprint(err, sizeof err, "%r");
52380e9508eSDavid du Colombier 		serialrecover(ser, err);
52480e9508eSDavid du Colombier 	}
52580e9508eSDavid du Colombier 	return nw;
52680e9508eSDavid du Colombier }
52780e9508eSDavid du Colombier 
52880e9508eSDavid du Colombier static long
5295e4f5c78SDavid du Colombier dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong)
5305e4f5c78SDavid du Colombier {
5315e4f5c78SDavid du Colombier 	ulong path;
5325e4f5c78SDavid du Colombier 	char *cmd;
5335e4f5c78SDavid du Colombier 	Serial *ser;
5345e4f5c78SDavid du Colombier 
5355e4f5c78SDavid du Colombier 	ser = fs->aux;
5365e4f5c78SDavid du Colombier 	path = fid->qid.path & ~fs->qid;
5375e4f5c78SDavid du Colombier 
5385e4f5c78SDavid du Colombier 	qlock(ser);
5395e4f5c78SDavid du Colombier 	switch(path){
5405e4f5c78SDavid du Colombier 	case Qdata:
54180e9508eSDavid du Colombier 		count = altwrite(ser, (uchar *)buf, count);
5425e4f5c78SDavid du Colombier 		break;
5435e4f5c78SDavid du Colombier 	case Qctl:
5445e4f5c78SDavid du Colombier 		cmd = emallocz(count+1, 1);
5455e4f5c78SDavid du Colombier 		memmove(cmd, buf, count);
5465e4f5c78SDavid du Colombier 		cmd[count] = 0;
5475e4f5c78SDavid du Colombier 		if(serialctl(ser, cmd) < 0){
5485e4f5c78SDavid du Colombier 			qunlock(ser);
5495e4f5c78SDavid du Colombier 			werrstr(Ebadctl);
5505e4f5c78SDavid du Colombier 			free(cmd);
5515e4f5c78SDavid du Colombier 			return -1;
5525e4f5c78SDavid du Colombier 		}
5535e4f5c78SDavid du Colombier 		free(cmd);
5545e4f5c78SDavid du Colombier 		break;
5555e4f5c78SDavid du Colombier 	default:
5565e4f5c78SDavid du Colombier 		qunlock(ser);
5575e4f5c78SDavid du Colombier 		werrstr(Eperm);
5585e4f5c78SDavid du Colombier 		return -1;
5595e4f5c78SDavid du Colombier 	}
5605e4f5c78SDavid du Colombier 	qunlock(ser);
5615e4f5c78SDavid du Colombier 	return count;
5625e4f5c78SDavid du Colombier }
5635e4f5c78SDavid du Colombier 
5645e4f5c78SDavid du Colombier static int
5655e4f5c78SDavid du Colombier openeps(Serial *ser, int epin, int epout, int epintr)
5665e4f5c78SDavid du Colombier {
5675e4f5c78SDavid du Colombier 	ser->epin = openep(ser->dev, epin);
5685e4f5c78SDavid du Colombier 	if(ser->epin == nil){
5695e4f5c78SDavid du Colombier 		fprint(2, "serial: openep %d: %r\n", epin);
5705e4f5c78SDavid du Colombier 		return -1;
5715e4f5c78SDavid du Colombier 	}
5725e4f5c78SDavid du Colombier 	ser->epout = openep(ser->dev, epout);
5735e4f5c78SDavid du Colombier 	if(ser->epout == nil){
5745e4f5c78SDavid du Colombier 		fprint(2, "serial: openep %d: %r\n", epout);
5755e4f5c78SDavid du Colombier 		closedev(ser->epin);
5765e4f5c78SDavid du Colombier 		return -1;
5775e4f5c78SDavid du Colombier 	}
5784d52e0f0SDavid du Colombier 
57980e9508eSDavid du Colombier 	devctl(ser->epin,  "timeout 1000");
58080e9508eSDavid du Colombier 	devctl(ser->epout, "timeout 1000");
5814d52e0f0SDavid du Colombier 
5824d52e0f0SDavid du Colombier 	if(ser->hasepintr){
5835e4f5c78SDavid du Colombier 		ser->epintr = openep(ser->dev, epintr);
5845e4f5c78SDavid du Colombier 		if(ser->epintr == nil){
5855e4f5c78SDavid du Colombier 			fprint(2, "serial: openep %d: %r\n", epintr);
5865e4f5c78SDavid du Colombier 			closedev(ser->epin);
5875e4f5c78SDavid du Colombier 			closedev(ser->epout);
5885e4f5c78SDavid du Colombier 			return -1;
5895e4f5c78SDavid du Colombier 		}
5904d52e0f0SDavid du Colombier 		opendevdata(ser->epintr, OREAD);
59180e9508eSDavid du Colombier 		devctl(ser->epintr, "timeout 1000");
5924d52e0f0SDavid du Colombier 	}
5935e4f5c78SDavid du Colombier 
59480e9508eSDavid du Colombier 	if(ser->seteps!= nil)
59580e9508eSDavid du Colombier 		ser->seteps(ser);
5965e4f5c78SDavid du Colombier 	opendevdata(ser->epin, OREAD);
5975e4f5c78SDavid du Colombier 	opendevdata(ser->epout, OWRITE);
59880e9508eSDavid du Colombier 	if(ser->epin->dfd < 0 || ser->epout->dfd < 0 ||
59980e9508eSDavid du Colombier 	    (ser->hasepintr && ser->epintr->dfd < 0)){
6005e4f5c78SDavid du Colombier 		fprint(2, "serial: open i/o ep data: %r\n");
6015e4f5c78SDavid du Colombier 		closedev(ser->epin);
6025e4f5c78SDavid du Colombier 		closedev(ser->epout);
6034d52e0f0SDavid du Colombier 		if(ser->hasepintr)
6045e4f5c78SDavid du Colombier 			closedev(ser->epintr);
6055e4f5c78SDavid du Colombier 		return -1;
6065e4f5c78SDavid du Colombier 	}
6075e4f5c78SDavid du Colombier 	return 0;
6085e4f5c78SDavid du Colombier }
6095e4f5c78SDavid du Colombier 
6105e4f5c78SDavid du Colombier static int
6115e4f5c78SDavid du Colombier findendpoints(Serial *ser)
6125e4f5c78SDavid du Colombier {
6135e4f5c78SDavid du Colombier 	int i, epin, epout, epintr;
61480e9508eSDavid du Colombier 	Ep *ep, **eps;
6155e4f5c78SDavid du Colombier 	Usbdev *ud;
6165e4f5c78SDavid du Colombier 
6175e4f5c78SDavid du Colombier 	epintr = epin = epout = -1;
6185e4f5c78SDavid du Colombier 	ud = ser->dev->usb;
61980e9508eSDavid du Colombier 
62080e9508eSDavid du Colombier 	/*
62180e9508eSDavid du Colombier 	 * interfc 0 means start from the start which is equiv to
62280e9508eSDavid du Colombier 	 * iterate through endpoints probably, could be done better
62380e9508eSDavid du Colombier 	 */
62480e9508eSDavid du Colombier 	if(ser->interfc == 0)
62580e9508eSDavid du Colombier 		eps = ud->ep;
62680e9508eSDavid du Colombier 	else
62780e9508eSDavid du Colombier 		eps = ser->dev->usb->conf[0]->iface[ser->interfc]->ep;
62880e9508eSDavid du Colombier 
62980e9508eSDavid du Colombier 	for(i = 0; i < Niface; i++){
63080e9508eSDavid du Colombier 		if((ep = eps[i]) == nil)
6315e4f5c78SDavid du Colombier 			continue;
63280e9508eSDavid du Colombier 		if(ser->hasepintr && ep->type == Eintr &&
63380e9508eSDavid du Colombier 		    ep->dir == Ein && epintr == -1)
6345e4f5c78SDavid du Colombier 			epintr = ep->id;
6355e4f5c78SDavid du Colombier 		if(ep->type == Ebulk){
6365e4f5c78SDavid du Colombier 			if(ep->dir == Ein && epin == -1)
6375e4f5c78SDavid du Colombier 				epin = ep->id;
6385e4f5c78SDavid du Colombier 			if(ep->dir == Eout && epout == -1)
6395e4f5c78SDavid du Colombier 				epout = ep->id;
6405e4f5c78SDavid du Colombier 		}
6415e4f5c78SDavid du Colombier 	}
6425e4f5c78SDavid du Colombier 	dprint(2, "serial: ep ids: in %d out %d intr %d\n", epin, epout, epintr);
6434d52e0f0SDavid du Colombier 	if(epin == -1 || epout == -1 || (ser->hasepintr && epintr == -1))
6445e4f5c78SDavid du Colombier 		return -1;
6454d52e0f0SDavid du Colombier 
6465e4f5c78SDavid du Colombier 	if(openeps(ser, epin, epout, epintr) < 0)
6475e4f5c78SDavid du Colombier 		return -1;
6485e4f5c78SDavid du Colombier 
64980e9508eSDavid du Colombier 	dprint(2, "serial: ep in %s out %s\n", ser->epin->dir, ser->epout->dir);
6504d52e0f0SDavid du Colombier 	if(ser->hasepintr)
65180e9508eSDavid du Colombier 		dprint(2, "serial: ep intr %s\n", ser->epintr->dir);
6525e4f5c78SDavid du Colombier 
6535e4f5c78SDavid du Colombier 	if(usbdebug > 1 || serialdebug > 2){
6545e4f5c78SDavid du Colombier 		devctl(ser->epin,  "debug 1");
6555e4f5c78SDavid du Colombier 		devctl(ser->epout, "debug 1");
6564d52e0f0SDavid du Colombier 		if(ser->hasepintr)
6575e4f5c78SDavid du Colombier 			devctl(ser->epintr, "debug 1");
6585e4f5c78SDavid du Colombier 		devctl(ser->dev, "debug 1");
6595e4f5c78SDavid du Colombier 	}
6605e4f5c78SDavid du Colombier 	return 0;
6615e4f5c78SDavid du Colombier }
6625e4f5c78SDavid du Colombier 
66380e9508eSDavid du Colombier /* keep in sync with main.c */
6645e4f5c78SDavid du Colombier static int
6655e4f5c78SDavid du Colombier usage(void)
6665e4f5c78SDavid du Colombier {
66780e9508eSDavid du Colombier 	werrstr("usage: usb/serial [-dD] [-m mtpt] [-s srv]");
6685e4f5c78SDavid du Colombier 	return -1;
6695e4f5c78SDavid du Colombier }
6705e4f5c78SDavid du Colombier 
6715e4f5c78SDavid du Colombier static void
6725e4f5c78SDavid du Colombier serdevfree(void *a)
6735e4f5c78SDavid du Colombier {
6745e4f5c78SDavid du Colombier 	Serial *ser = a;
6755e4f5c78SDavid du Colombier 
6765e4f5c78SDavid du Colombier 	if(ser == nil)
6775e4f5c78SDavid du Colombier 		return;
6784d52e0f0SDavid du Colombier 	if(ser->hasepintr)
6795e4f5c78SDavid du Colombier 		closedev(ser->epintr);
6805e4f5c78SDavid du Colombier 	closedev(ser->epin);
6815e4f5c78SDavid du Colombier 	closedev(ser->epout);
6825e4f5c78SDavid du Colombier 	ser->epintr = ser->epin = ser->epout = nil;
68380e9508eSDavid du Colombier 	chanfree(ser->w4data);
68480e9508eSDavid du Colombier 	chanfree(ser->gotdata);
68580e9508eSDavid du Colombier 	chanfree(ser->w4empty);
6865e4f5c78SDavid du Colombier 	free(ser);
6875e4f5c78SDavid du Colombier }
6885e4f5c78SDavid du Colombier 
6895e4f5c78SDavid du Colombier static Usbfs serialfs = {
6905e4f5c78SDavid du Colombier 	.walk =	dwalk,
6915e4f5c78SDavid du Colombier 	.open =	dopen,
6925e4f5c78SDavid du Colombier 	.read =	dread,
6935e4f5c78SDavid du Colombier 	.write=	dwrite,
6945e4f5c78SDavid du Colombier 	.stat =	dstat,
6955e4f5c78SDavid du Colombier };
6965e4f5c78SDavid du Colombier 
6975e4f5c78SDavid du Colombier int
6985e4f5c78SDavid du Colombier serialmain(Dev *dev, int argc, char* argv[])
6995e4f5c78SDavid du Colombier {
7005e4f5c78SDavid du Colombier 	Serial *ser;
7014d52e0f0SDavid du Colombier 	char buf[50];
7025e4f5c78SDavid du Colombier 
7035e4f5c78SDavid du Colombier 	ARGBEGIN{
7045e4f5c78SDavid du Colombier 	case 'd':
7055e4f5c78SDavid du Colombier 		serialdebug++;
7065e4f5c78SDavid du Colombier 		break;
7075e4f5c78SDavid du Colombier 	default:
7085e4f5c78SDavid du Colombier 		return usage();
7095e4f5c78SDavid du Colombier 	}ARGEND
7105e4f5c78SDavid du Colombier 	if(argc != 0)
7115e4f5c78SDavid du Colombier 		return usage();
7125e4f5c78SDavid du Colombier 
7135e4f5c78SDavid du Colombier 	ser = dev->aux = emallocz(sizeof(Serial), 1);
714bfb6eab9SDavid du Colombier 	/* BUG: could this go wrong? channel leaks? */
71580e9508eSDavid du Colombier 	ser->w4data  = chancreate(sizeof(ulong), 0);
71680e9508eSDavid du Colombier 	ser->gotdata = chancreate(sizeof(ulong), 0);
71780e9508eSDavid du Colombier 	ser->w4empty = chancreate(sizeof(ulong), 0);
718bfb6eab9SDavid du Colombier 	ser->maxread = ser->maxwrite = sizeof ser->data;
7195e4f5c78SDavid du Colombier 	ser->dev = dev;
7205e4f5c78SDavid du Colombier 	dev->free = serdevfree;
7214d52e0f0SDavid du Colombier 
7224d52e0f0SDavid du Colombier 	snprint(buf, sizeof buf, "vid %#06x did %#06x",
7234d52e0f0SDavid du Colombier 		dev->usb->vid, dev->usb->did);
7244d52e0f0SDavid du Colombier 	ser->fs = serialfs;
7254d52e0f0SDavid du Colombier 	if(plmatch(buf) == 0){
7264d52e0f0SDavid du Colombier 		ser->hasepintr = 1;
7274d52e0f0SDavid du Colombier 		ser->Serialops = plops;
72880e9508eSDavid du Colombier 	} else if(uconsmatch(buf) == 0)
7294d52e0f0SDavid du Colombier 		ser->Serialops = uconsops;
73080e9508eSDavid du Colombier 	else if(ftmatch(ser, buf) == 0)
73180e9508eSDavid du Colombier 		ser->Serialops = ftops;
73280e9508eSDavid du Colombier 	else {
73380e9508eSDavid du Colombier 		werrstr("serial: no serial devices found");
734bfb6eab9SDavid du Colombier 		closedev(dev);
7355e4f5c78SDavid du Colombier 		return -1;
7365e4f5c78SDavid du Colombier 	}
7375e4f5c78SDavid du Colombier 
73880e9508eSDavid du Colombier 	if(findendpoints(ser) < 0){
73980e9508eSDavid du Colombier 		werrstr("serial: no endpoints found");
740bfb6eab9SDavid du Colombier 		closedev(dev);
74180e9508eSDavid du Colombier 		return -1;
74280e9508eSDavid du Colombier 	}
7435e4f5c78SDavid du Colombier 	if(serinit(ser) < 0){
7445e4f5c78SDavid du Colombier 		dprint(2, "serial: serinit: %r\n");
745bfb6eab9SDavid du Colombier 		closedev(dev);
7465e4f5c78SDavid du Colombier 		return -1;
7475e4f5c78SDavid du Colombier 	}
7485e4f5c78SDavid du Colombier 
74980e9508eSDavid du Colombier 	snprint(ser->fs.name, sizeof ser->fs.name, "eiaU%d", dev->id);
75080e9508eSDavid du Colombier 	fprint(2, "%s\n", ser->fs.name);
7515e4f5c78SDavid du Colombier 	ser->fs.dev = dev;
7525e4f5c78SDavid du Colombier 	incref(dev);
7535e4f5c78SDavid du Colombier 	ser->fs.aux = ser;
7545e4f5c78SDavid du Colombier 	usbfsadd(&ser->fs);
7555e4f5c78SDavid du Colombier 
7565e4f5c78SDavid du Colombier 	closedev(dev);
7575e4f5c78SDavid du Colombier 	return 0;
7585e4f5c78SDavid du Colombier }
759