xref: /plan9/sys/src/cmd/usb/serial/serial.c (revision 84860c5d80372cab6c5e871d17f5695a8d0192c9)
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  */
5b2495906SDavid du Colombier 
65e4f5c78SDavid du Colombier #include <u.h>
75e4f5c78SDavid du Colombier #include <libc.h>
85e4f5c78SDavid du Colombier #include <ctype.h>
95e4f5c78SDavid du Colombier #include <thread.h>
105e4f5c78SDavid du Colombier #include "usb.h"
115e4f5c78SDavid du Colombier #include "usbfs.h"
125e4f5c78SDavid du Colombier #include "serial.h"
134d52e0f0SDavid du Colombier #include "prolific.h"
144d52e0f0SDavid du Colombier #include "ucons.h"
1580e9508eSDavid du Colombier #include "ftdi.h"
164d52e0f0SDavid du Colombier 
175e4f5c78SDavid du Colombier int serialdebug;
185e4f5c78SDavid du Colombier 
195e4f5c78SDavid du Colombier enum {
205e4f5c78SDavid du Colombier 	/* Qids. Maintain order (relative to dirtabs structs) */
215e4f5c78SDavid du Colombier 	Qroot	= 0,
225e4f5c78SDavid du Colombier 	Qctl,
235e4f5c78SDavid du Colombier 	Qdata,
245e4f5c78SDavid du Colombier 	Qmax,
255e4f5c78SDavid du Colombier };
265e4f5c78SDavid du Colombier 
275e4f5c78SDavid du Colombier typedef struct Dirtab Dirtab;
285e4f5c78SDavid du Colombier struct Dirtab {
295e4f5c78SDavid du Colombier 	char	*name;
305e4f5c78SDavid du Colombier 	int	mode;
315e4f5c78SDavid du Colombier };
325e4f5c78SDavid du Colombier 
335e4f5c78SDavid du Colombier static Dirtab dirtab[] = {
345e4f5c78SDavid du Colombier 	[Qroot]	"/",		DMDIR|0555,
35d584e620SDavid du Colombier 	[Qdata]	"%s",		0660,
36d584e620SDavid du Colombier 	[Qctl]	"%sctl",	0664,
375e4f5c78SDavid du Colombier };
385e4f5c78SDavid du Colombier 
395e4f5c78SDavid du Colombier static int sdebug;
405e4f5c78SDavid du Colombier 
415e4f5c78SDavid du Colombier static void
425e4f5c78SDavid du Colombier serialfatal(Serial *ser)
435e4f5c78SDavid du Colombier {
44d5789509SDavid du Colombier 	Serialport *p;
45d5789509SDavid du Colombier 	int i;
46d5789509SDavid du Colombier 
475e4f5c78SDavid du Colombier 	dsprint(2, "serial: fatal error, detaching\n");
485e4f5c78SDavid du Colombier 	devctl(ser->dev, "detach");
49d5789509SDavid du Colombier 
50d5789509SDavid du Colombier 	for(i = 0; i < ser->nifcs; i++){
51d5789509SDavid du Colombier 		p = &ser->p[i];
52d5789509SDavid du Colombier 		usbfsdel(&p->fs);
53d5789509SDavid du Colombier 		if(p->w4data != nil)
54d5789509SDavid du Colombier 			chanclose(p->w4data);
55d5789509SDavid du Colombier 		if(p->gotdata != nil)
56d5789509SDavid du Colombier 			chanclose(p->gotdata);
57d5789509SDavid du Colombier 		if(p->readc)
58d5789509SDavid du Colombier 			chanclose(p->readc);
59d5789509SDavid du Colombier 	}
605e4f5c78SDavid du Colombier }
615e4f5c78SDavid du Colombier 
6280e9508eSDavid du Colombier /* I sleep with the lock... only way to drain in general */
635e4f5c78SDavid du Colombier static void
64d5789509SDavid du Colombier serialdrain(Serialport *p)
655e4f5c78SDavid du Colombier {
66d5789509SDavid du Colombier 	Serial *ser;
6780e9508eSDavid du Colombier 	uint baud, pipesize;
685e4f5c78SDavid du Colombier 
69d5789509SDavid du Colombier 	ser = p->s;
70d5789509SDavid du Colombier 	baud = p->baud;
71d5789509SDavid du Colombier 
72d5789509SDavid du Colombier 	if(p->baud == ~0)
73d5789509SDavid du Colombier 		return;
74d5789509SDavid du Colombier 	if(ser->maxwtrans < 256)
7580e9508eSDavid du Colombier 		pipesize = 256;
7680e9508eSDavid du Colombier 	else
77d5789509SDavid du Colombier 		pipesize = ser->maxwtrans;
7880e9508eSDavid du Colombier 	/* wait for the at least 256-byte pipe to clear */
7980e9508eSDavid du Colombier 	sleep(10 + pipesize/((1 + baud)*1000));
8080e9508eSDavid du Colombier 	if(ser->clearpipes != nil)
81d5789509SDavid du Colombier 		ser->clearpipes(p);
825e4f5c78SDavid du Colombier }
835e4f5c78SDavid du Colombier 
845e4f5c78SDavid du Colombier int
855e4f5c78SDavid du Colombier serialreset(Serial *ser)
865e4f5c78SDavid du Colombier {
87d5789509SDavid du Colombier 	Serialport *p;
88c8a340cdSDavid du Colombier 	int i, res;
89d5789509SDavid du Colombier 
90c8a340cdSDavid du Colombier 	res = 0;
915e4f5c78SDavid du Colombier 	/* cmd for reset */
92d5789509SDavid du Colombier 	for(i = 0; i < ser->nifcs; i++){
93d5789509SDavid du Colombier 		p = &ser->p[i];
94d5789509SDavid du Colombier 		serialdrain(p);
95d5789509SDavid du Colombier 	}
9680e9508eSDavid du Colombier 	if(ser->reset != nil)
97c8a340cdSDavid du Colombier 		res = ser->reset(ser, nil);
98c8a340cdSDavid du Colombier 	return res;
995e4f5c78SDavid du Colombier }
1005e4f5c78SDavid du Colombier 
101c8a340cdSDavid du Colombier /* call this if something goes wrong, must be qlocked */
1025e4f5c78SDavid du Colombier int
103c8a340cdSDavid du Colombier serialrecover(Serial *ser, Serialport *p, Dev *ep, char *err)
1045e4f5c78SDavid du Colombier {
105c8a340cdSDavid du Colombier 	if(p != nil)
106c8a340cdSDavid du Colombier 		dprint(2, "serial[%d], %s: %s, level %d\n", p->interfc,
107c8a340cdSDavid du Colombier 			p->name, err, ser->recover);
108c8a340cdSDavid du Colombier 	else
109c8a340cdSDavid du Colombier 		dprint(2, "serial[%s], global error, level %d\n",
110c8a340cdSDavid du Colombier 			ser->p[0].name, ser->recover);
111c8a340cdSDavid du Colombier 	ser->recover++;
1125e4f5c78SDavid du Colombier 	if(strstr(err, "detached") != nil)
1135e4f5c78SDavid du Colombier 		return -1;
114c8a340cdSDavid du Colombier 	if(ser->recover < 3){
115*84860c5dSDavid du Colombier 		if(p != nil){
116c8a340cdSDavid du Colombier 			if(ep != nil){
117*84860c5dSDavid du Colombier 				if(ep == p->epintr)
118c8a340cdSDavid du Colombier 					unstall(ser->dev, p->epintr, Ein);
119*84860c5dSDavid du Colombier 				if(ep == p->epin)
120c8a340cdSDavid du Colombier 					unstall(ser->dev, p->epin, Ein);
121*84860c5dSDavid du Colombier 				if(ep == p->epout)
122c8a340cdSDavid du Colombier 					unstall(ser->dev, p->epout, Eout);
123c8a340cdSDavid du Colombier 				return 0;
124c8a340cdSDavid du Colombier 			}
125*84860c5dSDavid du Colombier 
126c8a340cdSDavid du Colombier 			if(p->epintr != nil)
127c8a340cdSDavid du Colombier 				unstall(ser->dev, p->epintr, Ein);
128c8a340cdSDavid du Colombier 			if(p->epin != nil)
129c8a340cdSDavid du Colombier 				unstall(ser->dev, p->epin, Ein);
130c8a340cdSDavid du Colombier 			if(p->epout != nil)
131c8a340cdSDavid du Colombier 				unstall(ser->dev, p->epout, Eout);
132c8a340cdSDavid du Colombier 		}
133c8a340cdSDavid du Colombier 		return 0;
134c8a340cdSDavid du Colombier 	}
135c8a340cdSDavid du Colombier 	if(ser->recover > 4 && ser->recover < 8)
1365e4f5c78SDavid du Colombier 		serialfatal(ser);
137c8a340cdSDavid du Colombier 	if(ser->recover > 8){
138c8a340cdSDavid du Colombier 		ser->reset(ser, p);
139c8a340cdSDavid du Colombier 		return 0;
140c8a340cdSDavid du Colombier 	}
1415e4f5c78SDavid du Colombier 	if(serialreset(ser) < 0)
1425e4f5c78SDavid du Colombier 		return -1;
1435e4f5c78SDavid du Colombier 	return 0;
1445e4f5c78SDavid du Colombier }
1455e4f5c78SDavid du Colombier 
1465e4f5c78SDavid du Colombier static int
147d5789509SDavid du Colombier serialctl(Serialport *p, char *cmd)
1485e4f5c78SDavid du Colombier {
149d5789509SDavid du Colombier 	Serial *ser;
1505e4f5c78SDavid du Colombier 	int c, i, n, nf, nop, nw, par, drain, set, lines;
1515e4f5c78SDavid du Colombier 	char *f[16];
1525e4f5c78SDavid du Colombier 	uchar x;
1535e4f5c78SDavid du Colombier 
154d5789509SDavid du Colombier 	ser = p->s;
1555e4f5c78SDavid du Colombier 	drain = set = lines = 0;
1565e4f5c78SDavid du Colombier 	nf = tokenize(cmd, f, nelem(f));
1575e4f5c78SDavid du Colombier 	for(i = 0; i < nf; i++){
1585e4f5c78SDavid du Colombier 		if(strncmp(f[i], "break", 5) == 0){
159d5789509SDavid du Colombier 			if(ser->setbreak != nil)
160d5789509SDavid du Colombier 				ser->setbreak(p, 1);
1615e4f5c78SDavid du Colombier 			continue;
1625e4f5c78SDavid du Colombier 		}
1635e4f5c78SDavid du Colombier 
1645e4f5c78SDavid du Colombier 		nop = 0;
1655e4f5c78SDavid du Colombier 		n = atoi(f[i]+1);
1665e4f5c78SDavid du Colombier 		c = *f[i];
1675e4f5c78SDavid du Colombier 		if (isascii(c) && isupper(c))
1685e4f5c78SDavid du Colombier 			c = tolower(c);
1695e4f5c78SDavid du Colombier 		switch(c){
1705e4f5c78SDavid du Colombier 		case 'b':
1715e4f5c78SDavid du Colombier 			drain++;
1725e4f5c78SDavid du Colombier 			p->baud = n;
1735e4f5c78SDavid du Colombier 			set++;
1745e4f5c78SDavid du Colombier 			break;
1755e4f5c78SDavid du Colombier 		case 'c':
1765e4f5c78SDavid du Colombier 			p->dcd = n;
1775e4f5c78SDavid du Colombier 			// lines++;
1785e4f5c78SDavid du Colombier 			++nop;
1795e4f5c78SDavid du Colombier 			break;
1805e4f5c78SDavid du Colombier 		case 'd':
1815e4f5c78SDavid du Colombier 			p->dtr = n;
1825e4f5c78SDavid du Colombier 			lines++;
1835e4f5c78SDavid du Colombier 			break;
1845e4f5c78SDavid du Colombier 		case 'e':
1855e4f5c78SDavid du Colombier 			p->dsr = n;
1865e4f5c78SDavid du Colombier 			// lines++;
1875e4f5c78SDavid du Colombier 			++nop;
1885e4f5c78SDavid du Colombier 			break;
1895e4f5c78SDavid du Colombier 		case 'f':		/* flush the pipes */
1905e4f5c78SDavid du Colombier 			drain++;
1915e4f5c78SDavid du Colombier 			break;
1925e4f5c78SDavid du Colombier 		case 'h':		/* hangup?? */
1935e4f5c78SDavid du Colombier 			p->rts = p->dtr = 0;
1945e4f5c78SDavid du Colombier 			lines++;
1955e4f5c78SDavid du Colombier 			fprint(2, "serial: %c, unsure ctl\n", c);
1965e4f5c78SDavid du Colombier 			break;
1975e4f5c78SDavid du Colombier 		case 'i':
1985e4f5c78SDavid du Colombier 			++nop;
1995e4f5c78SDavid du Colombier 			break;
2005e4f5c78SDavid du Colombier 		case 'k':
2015e4f5c78SDavid du Colombier 			drain++;
202d5789509SDavid du Colombier 			ser->setbreak(p, 1);
2035e4f5c78SDavid du Colombier 			sleep(n);
204d5789509SDavid du Colombier 			ser->setbreak(p, 0);
2055e4f5c78SDavid du Colombier 			break;
2065e4f5c78SDavid du Colombier 		case 'l':
2075e4f5c78SDavid du Colombier 			drain++;
2085e4f5c78SDavid du Colombier 			p->bits = n;
2095e4f5c78SDavid du Colombier 			set++;
2105e4f5c78SDavid du Colombier 			break;
2115e4f5c78SDavid du Colombier 		case 'm':
2125e4f5c78SDavid du Colombier 			drain++;
213d5789509SDavid du Colombier 			if(ser->modemctl != nil)
214d5789509SDavid du Colombier 				ser->modemctl(p, n);
2155e4f5c78SDavid du Colombier 			if(n == 0)
2165e4f5c78SDavid du Colombier 				p->cts = 0;
2175e4f5c78SDavid du Colombier 			break;
2185e4f5c78SDavid du Colombier 		case 'n':
2195e4f5c78SDavid du Colombier 			p->blocked = n;
2205e4f5c78SDavid du Colombier 			++nop;
2215e4f5c78SDavid du Colombier 			break;
2225e4f5c78SDavid du Colombier 		case 'p':		/* extended... */
2235e4f5c78SDavid du Colombier 			if(strlen(f[i]) != 2)
2245e4f5c78SDavid du Colombier 				return -1;
2255e4f5c78SDavid du Colombier 			drain++;
22680e9508eSDavid du Colombier 			par = f[i][1];
2275e4f5c78SDavid du Colombier 			if(par == 'n')
2285e4f5c78SDavid du Colombier 				p->parity = 0;
2295e4f5c78SDavid du Colombier 			else if(par == 'o')
2305e4f5c78SDavid du Colombier 				p->parity = 1;
2315e4f5c78SDavid du Colombier 			else if(par == 'e')
2325e4f5c78SDavid du Colombier 				p->parity = 2;
2335e4f5c78SDavid du Colombier 			else if(par == 'm')	/* mark parity */
2345e4f5c78SDavid du Colombier 				p->parity = 3;
2355e4f5c78SDavid du Colombier 			else if(par == 's')	/* space parity */
2365e4f5c78SDavid du Colombier 				p->parity = 4;
2375e4f5c78SDavid du Colombier 			else
2385e4f5c78SDavid du Colombier 				return -1;
2395e4f5c78SDavid du Colombier 			set++;
2405e4f5c78SDavid du Colombier 			break;
2415e4f5c78SDavid du Colombier 		case 'q':
2425e4f5c78SDavid du Colombier 			// drain++;
2435e4f5c78SDavid du Colombier 			p->limit = n;
2445e4f5c78SDavid du Colombier 			++nop;
2455e4f5c78SDavid du Colombier 			break;
2465e4f5c78SDavid du Colombier 		case 'r':
2475e4f5c78SDavid du Colombier 			drain++;
2485e4f5c78SDavid du Colombier 			p->rts = n;
2495e4f5c78SDavid du Colombier 			lines++;
2505e4f5c78SDavid du Colombier 			break;
2515e4f5c78SDavid du Colombier 		case 's':
2525e4f5c78SDavid du Colombier 			drain++;
2535e4f5c78SDavid du Colombier 			p->stop = n;
2545e4f5c78SDavid du Colombier 			set++;
2555e4f5c78SDavid du Colombier 			break;
2565e4f5c78SDavid du Colombier 		case 'w':
2575e4f5c78SDavid du Colombier 			/* ?? how do I put this */
2585e4f5c78SDavid du Colombier 			p->timer = n * 100000LL;
2595e4f5c78SDavid du Colombier 			++nop;
2605e4f5c78SDavid du Colombier 			break;
2615e4f5c78SDavid du Colombier 		case 'x':
2625e4f5c78SDavid du Colombier 			if(n == 0)
2635e4f5c78SDavid du Colombier 				x = CTLS;
2645e4f5c78SDavid du Colombier 			else
2655e4f5c78SDavid du Colombier 				x = CTLQ;
266d5789509SDavid du Colombier 			if(ser->wait4write != nil)
267d5789509SDavid du Colombier 				nw = ser->wait4write(p, &x, 1);
26880e9508eSDavid du Colombier 			else
2695e4f5c78SDavid du Colombier 				nw = write(p->epout->dfd, &x, 1);
2705e4f5c78SDavid du Colombier 			if(nw != 1){
271*84860c5dSDavid du Colombier 				serialrecover(ser, p, p->epout, "");
2725e4f5c78SDavid du Colombier 				return -1;
2735e4f5c78SDavid du Colombier 			}
2745e4f5c78SDavid du Colombier 			break;
2755e4f5c78SDavid du Colombier 		}
276ecc2a7c8SDavid du Colombier 		/*
277ecc2a7c8SDavid du Colombier 		 * don't print.  the condition is harmless and the print
278ecc2a7c8SDavid du Colombier 		 * splatters all over the display.
279ecc2a7c8SDavid du Colombier 		 */
280ecc2a7c8SDavid du Colombier 		USED(nop);
281ecc2a7c8SDavid du Colombier 		if (0 && nop)
2825e4f5c78SDavid du Colombier 			fprint(2, "serial: %c, unsupported nop ctl\n", c);
2835e4f5c78SDavid du Colombier 	}
2845e4f5c78SDavid du Colombier 	if(drain)
2855e4f5c78SDavid du Colombier 		serialdrain(p);
28680e9508eSDavid du Colombier 	if(lines && !set){
287d5789509SDavid du Colombier 		if(ser->sendlines != nil && ser->sendlines(p) < 0)
2885e4f5c78SDavid du Colombier 			return -1;
28980e9508eSDavid du Colombier 	} else if(set){
290d5789509SDavid du Colombier 		if(ser->setparam != nil && ser->setparam(p) < 0)
29180e9508eSDavid du Colombier 			return -1;
29280e9508eSDavid du Colombier 	}
293c8a340cdSDavid du Colombier 	ser->recover = 0;
2945e4f5c78SDavid du Colombier 	return 0;
2955e4f5c78SDavid du Colombier }
2965e4f5c78SDavid du Colombier 
2975e4f5c78SDavid du Colombier char *pformat = "noems";
2985e4f5c78SDavid du Colombier 
2995e4f5c78SDavid du Colombier char *
300d5789509SDavid du Colombier serdumpst(Serialport *p, char *buf, int bufsz)
3015e4f5c78SDavid du Colombier {
3025e4f5c78SDavid du Colombier 	char *e, *s;
303d5789509SDavid du Colombier 	Serial *ser;
304d5789509SDavid du Colombier 
305d5789509SDavid du Colombier 	ser = p->s;
3065e4f5c78SDavid du Colombier 
3075e4f5c78SDavid du Colombier 	e = buf + bufsz;
308d5789509SDavid du Colombier 	s = seprint(buf, e, "b%d ", p->baud);
309d5789509SDavid du Colombier 	s = seprint(s, e, "c%d ", p->dcd);	/* unimplemented */
310d5789509SDavid du Colombier 	s = seprint(s, e, "d%d ", p->dtr);
311d5789509SDavid du Colombier 	s = seprint(s, e, "e%d ", p->dsr);	/* unimplemented */
312d5789509SDavid du Colombier 	s = seprint(s, e, "l%d ", p->bits);
313d5789509SDavid du Colombier 	s = seprint(s, e, "m%d ", p->mctl);
314d5789509SDavid du Colombier 	if(p->parity >= 0 || p->parity < strlen(pformat))
315d5789509SDavid du Colombier 		s = seprint(s, e, "p%c ", pformat[p->parity]);
3165e4f5c78SDavid du Colombier 	else
3175e4f5c78SDavid du Colombier 		s = seprint(s, e, "p%c ", '?');
318d5789509SDavid du Colombier 	s = seprint(s, e, "r%d ", p->rts);
319d5789509SDavid du Colombier 	s = seprint(s, e, "s%d ", p->stop);
320d5789509SDavid du Colombier 	s = seprint(s, e, "i%d ", p->fifo);
3215e4f5c78SDavid du Colombier 	s = seprint(s, e, "\ndev(%d) ", 0);
3225e4f5c78SDavid du Colombier 	s = seprint(s, e, "type(%d)  ", ser->type);
323d5789509SDavid du Colombier 	s = seprint(s, e, "framing(%d) ", p->nframeerr);
324d5789509SDavid du Colombier 	s = seprint(s, e, "overruns(%d) ", p->novererr);
325d5789509SDavid du Colombier 	s = seprint(s, e, "berr(%d) ", p->nbreakerr);
326ed868a7cSDavid du Colombier 	s = seprint(s, e, " serr(%d)\n", p->nparityerr);
3275e4f5c78SDavid du Colombier 	return s;
3285e4f5c78SDavid du Colombier }
3295e4f5c78SDavid du Colombier 
3305e4f5c78SDavid du Colombier static int
331d5789509SDavid du Colombier serinit(Serialport *p)
3325e4f5c78SDavid du Colombier {
3335e4f5c78SDavid du Colombier 	int res;
33480e9508eSDavid du Colombier 	res = 0;
335d5789509SDavid du Colombier 	Serial *ser;
336d5789509SDavid du Colombier 
337d5789509SDavid du Colombier 	ser = p->s;
338d5789509SDavid du Colombier 
33980e9508eSDavid du Colombier 	if(ser->init != nil)
340d5789509SDavid du Colombier 		res = ser->init(p);
34180e9508eSDavid du Colombier 	if(ser->getparam != nil)
342d5789509SDavid du Colombier 		ser->getparam(p);
343d5789509SDavid du Colombier 	p->nframeerr = p->nparityerr = p->nbreakerr = p->novererr = 0;
344d5789509SDavid du Colombier 
3455e4f5c78SDavid du Colombier 	return res;
3465e4f5c78SDavid du Colombier }
3475e4f5c78SDavid du Colombier 
3485e4f5c78SDavid du Colombier static int
3495e4f5c78SDavid du Colombier dwalk(Usbfs *fs, Fid *fid, char *name)
3505e4f5c78SDavid du Colombier {
3515e4f5c78SDavid du Colombier 	int i;
3525e4f5c78SDavid du Colombier 	char *dname;
3535e4f5c78SDavid du Colombier 	Qid qid;
354d5789509SDavid du Colombier 	Serialport *p;
3555e4f5c78SDavid du Colombier 
3565e4f5c78SDavid du Colombier 	qid = fid->qid;
3575e4f5c78SDavid du Colombier 	if((qid.type & QTDIR) == 0){
3585e4f5c78SDavid du Colombier 		werrstr("walk in non-directory");
3595e4f5c78SDavid du Colombier 		return -1;
3605e4f5c78SDavid du Colombier 	}
3615e4f5c78SDavid du Colombier 
3625e4f5c78SDavid du Colombier 	if(strcmp(name, "..") == 0){
3635e4f5c78SDavid du Colombier 		/* must be /eiaU%d; i.e. our root dir. */
3645e4f5c78SDavid du Colombier 		fid->qid.path = Qroot | fs->qid;
3655e4f5c78SDavid du Colombier 		fid->qid.vers = 0;
3665e4f5c78SDavid du Colombier 		fid->qid.type = QTDIR;
3675e4f5c78SDavid du Colombier 		return 0;
3685e4f5c78SDavid du Colombier 	}
3695e4f5c78SDavid du Colombier 
370d5789509SDavid du Colombier 	p = fs->aux;
3715e4f5c78SDavid du Colombier 	for(i = 1; i < nelem(dirtab); i++){
372d584e620SDavid du Colombier 		dname = smprint(dirtab[i].name, p->name);
3735e4f5c78SDavid du Colombier 		if(strcmp(name, dname) == 0){
3745e4f5c78SDavid du Colombier 			qid.path = i | fs->qid;
3755e4f5c78SDavid du Colombier 			qid.vers = 0;
3765e4f5c78SDavid du Colombier 			qid.type = dirtab[i].mode >> 24;
3775e4f5c78SDavid du Colombier 			fid->qid = qid;
3785e4f5c78SDavid du Colombier 			free(dname);
3795e4f5c78SDavid du Colombier 			return 0;
3805e4f5c78SDavid du Colombier 		} else
3815e4f5c78SDavid du Colombier 			free(dname);
3825e4f5c78SDavid du Colombier 	}
3835e4f5c78SDavid du Colombier 	werrstr(Enotfound);
3845e4f5c78SDavid du Colombier 	return -1;
3855e4f5c78SDavid du Colombier }
3865e4f5c78SDavid du Colombier 
3875e4f5c78SDavid du Colombier static void
3885e4f5c78SDavid du Colombier dostat(Usbfs *fs, int path, Dir *d)
3895e4f5c78SDavid du Colombier {
3905e4f5c78SDavid du Colombier 	Dirtab *t;
391d5789509SDavid du Colombier 	Serialport *p;
3925e4f5c78SDavid du Colombier 
3935e4f5c78SDavid du Colombier 	t = &dirtab[path];
3945e4f5c78SDavid du Colombier 	d->qid.path = path;
3955e4f5c78SDavid du Colombier 	d->qid.type = t->mode >> 24;
3965e4f5c78SDavid du Colombier 	d->mode = t->mode;
397d5789509SDavid du Colombier 	p = fs->aux;
3985e4f5c78SDavid du Colombier 
3995e4f5c78SDavid du Colombier 	if(strcmp(t->name, "/") == 0)
4005e4f5c78SDavid du Colombier 		d->name = t->name;
4015e4f5c78SDavid du Colombier 	else
402d5789509SDavid du Colombier 		snprint(d->name, Namesz, t->name, p->fs.name);
4035e4f5c78SDavid du Colombier 	d->length = 0;
4045e4f5c78SDavid du Colombier }
4055e4f5c78SDavid du Colombier 
4065e4f5c78SDavid du Colombier static int
4075e4f5c78SDavid du Colombier dstat(Usbfs *fs, Qid qid, Dir *d)
4085e4f5c78SDavid du Colombier {
4095e4f5c78SDavid du Colombier 	int path;
4105e4f5c78SDavid du Colombier 
4115e4f5c78SDavid du Colombier 	path = qid.path & ~fs->qid;
4125e4f5c78SDavid du Colombier 	dostat(fs, path, d);
4135e4f5c78SDavid du Colombier 	d->qid.path |= fs->qid;
4145e4f5c78SDavid du Colombier 	return 0;
4155e4f5c78SDavid du Colombier }
4165e4f5c78SDavid du Colombier 
4175e4f5c78SDavid du Colombier static int
4185e4f5c78SDavid du Colombier dopen(Usbfs *fs, Fid *fid, int)
4195e4f5c78SDavid du Colombier {
4205e4f5c78SDavid du Colombier 	ulong path;
421ed868a7cSDavid du Colombier 	Serialport *p;
4225e4f5c78SDavid du Colombier 
4235e4f5c78SDavid du Colombier 	path = fid->qid.path & ~fs->qid;
424ed868a7cSDavid du Colombier 	p = fs->aux;
4255e4f5c78SDavid du Colombier 	switch(path){		/* BUG: unneeded? */
4265e4f5c78SDavid du Colombier 	case Qdata:
4274d52e0f0SDavid du Colombier 		dsprint(2, "serial, opened data\n");
4285e4f5c78SDavid du Colombier 		break;
4295e4f5c78SDavid du Colombier 	case Qctl:
4304d52e0f0SDavid du Colombier 		dsprint(2, "serial, opened ctl\n");
431d584e620SDavid du Colombier 		if(p->isjtag)
432d584e620SDavid du Colombier 			return 0;
433ed868a7cSDavid du Colombier 		serialctl(p, "l8 i1");	/* default line parameters */
4345e4f5c78SDavid du Colombier 		break;
4355e4f5c78SDavid du Colombier 	}
4365e4f5c78SDavid du Colombier 	return 0;
4375e4f5c78SDavid du Colombier }
4385e4f5c78SDavid du Colombier 
4395e4f5c78SDavid du Colombier 
4405e4f5c78SDavid du Colombier static void
441d584e620SDavid du Colombier filldir(Usbfs *fs, Dir *d, Dirtab *tab, int i, void *v)
4425e4f5c78SDavid du Colombier {
443d584e620SDavid du Colombier 	Serialport *p;
444d584e620SDavid du Colombier 
445d584e620SDavid du Colombier 	p = v;
4465e4f5c78SDavid du Colombier 	d->qid.path = i | fs->qid;
4475e4f5c78SDavid du Colombier 	d->mode = tab->mode;
4485e4f5c78SDavid du Colombier 	if((d->mode & DMDIR) != 0)
4495e4f5c78SDavid du Colombier 		d->qid.type = QTDIR;
4505e4f5c78SDavid du Colombier 	else
4515e4f5c78SDavid du Colombier 		d->qid.type = QTFILE;
452d584e620SDavid du Colombier 	sprint(d->name, tab->name, p->name);	/* hope it fits */
4535e4f5c78SDavid du Colombier }
4545e4f5c78SDavid du Colombier 
4555e4f5c78SDavid du Colombier static int
456d584e620SDavid du Colombier dirgen(Usbfs *fs, Qid, int i, Dir *d, void *p)
4575e4f5c78SDavid du Colombier {
4585e4f5c78SDavid du Colombier 	i++;				/* skip root */
45980e9508eSDavid du Colombier 	if(i >= nelem(dirtab))
4605e4f5c78SDavid du Colombier 		return -1;
461d584e620SDavid du Colombier 	filldir(fs, d, &dirtab[i], i, p);
4625e4f5c78SDavid du Colombier 	return 0;
4635e4f5c78SDavid du Colombier }
4645e4f5c78SDavid du Colombier 
465bfb6eab9SDavid du Colombier enum {
466bfb6eab9SDavid du Colombier 	Serbufsize	= 255,
467bfb6eab9SDavid du Colombier };
468bfb6eab9SDavid du Colombier 
4695e4f5c78SDavid du Colombier static long
4705e4f5c78SDavid du Colombier dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
4715e4f5c78SDavid du Colombier {
4725e4f5c78SDavid du Colombier 	int dfd;
4735e4f5c78SDavid du Colombier 	long rcount;
4745e4f5c78SDavid du Colombier 	ulong path;
47580e9508eSDavid du Colombier 	char *e, *buf, *err;	/* change */
4765e4f5c78SDavid du Colombier 	Qid q;
477d5789509SDavid du Colombier 	Serialport *p;
4785e4f5c78SDavid du Colombier 	Serial *ser;
479ecc2a7c8SDavid du Colombier 	static int errrun, good;
4805e4f5c78SDavid du Colombier 
4815e4f5c78SDavid du Colombier 	q = fid->qid;
4825e4f5c78SDavid du Colombier 	path = fid->qid.path & ~fs->qid;
483d5789509SDavid du Colombier 	p = fs->aux;
484d5789509SDavid du Colombier 	ser = p->s;
4855e4f5c78SDavid du Colombier 
486bfb6eab9SDavid du Colombier 	buf = emallocz(Serbufsize, 1);
487bfb6eab9SDavid du Colombier 	err = emallocz(Serbufsize, 1);
4885e4f5c78SDavid du Colombier 	qlock(ser);
4895e4f5c78SDavid du Colombier 	switch(path){
4905e4f5c78SDavid du Colombier 	case Qroot:
491d584e620SDavid du Colombier 		count = usbdirread(fs, q, data, count, offset, dirgen, p);
4925e4f5c78SDavid du Colombier 		break;
4935e4f5c78SDavid du Colombier 	case Qdata:
49480e9508eSDavid du Colombier 		if(count > ser->maxread)
49580e9508eSDavid du Colombier 			count = ser->maxread;
49680e9508eSDavid du Colombier 		dsprint(2, "serial: reading from data\n");
4975e4f5c78SDavid du Colombier 		do {
4984d52e0f0SDavid du Colombier 			err[0] = 0;
499d5789509SDavid du Colombier 			dfd = p->epin->dfd;
50080e9508eSDavid du Colombier 			if(usbdebug >= 3)
50180e9508eSDavid du Colombier 				dsprint(2, "serial: reading: %ld\n", count);
50280e9508eSDavid du Colombier 
503ecc2a7c8SDavid du Colombier 			assert(count > 0);
50480e9508eSDavid du Colombier 			if(ser->wait4data != nil)
505d5789509SDavid du Colombier 				rcount = ser->wait4data(p, data, count);
50680e9508eSDavid du Colombier 			else{
50780e9508eSDavid du Colombier 				qunlock(ser);
5085e4f5c78SDavid du Colombier 				rcount = read(dfd, data, count);
5095e4f5c78SDavid du Colombier 				qlock(ser);
51080e9508eSDavid du Colombier 			}
51125fc6993SDavid du Colombier 			/*
51225fc6993SDavid du Colombier 			 * if we encounter a long run of continuous read
51325fc6993SDavid du Colombier 			 * errors, do something drastic so that our caller
51425fc6993SDavid du Colombier 			 * doesn't just spin its wheels forever.
51525fc6993SDavid du Colombier 			 */
51625fc6993SDavid du Colombier 			if(rcount < 0) {
517bfb6eab9SDavid du Colombier 				snprint(err, Serbufsize, "%r");
518ecc2a7c8SDavid du Colombier 				++errrun;
519ecc2a7c8SDavid du Colombier 				sleep(20);
520ecc2a7c8SDavid du Colombier 				if (good > 0 && errrun > 10000) {
52125fc6993SDavid du Colombier 					/* the line has been dropped; give up */
52225fc6993SDavid du Colombier 					qunlock(ser);
523ecc2a7c8SDavid du Colombier 					fprint(2, "%s: line %s is gone: %r\n",
524d5789509SDavid du Colombier 						argv0, p->fs.name);
52525fc6993SDavid du Colombier 					threadexitsall("serial line gone");
52625fc6993SDavid du Colombier 				}
527ecc2a7c8SDavid du Colombier 			} else {
52825fc6993SDavid du Colombier 				errrun = 0;
529ecc2a7c8SDavid du Colombier 				good++;
530ecc2a7c8SDavid du Colombier 			}
53180e9508eSDavid du Colombier 			if(usbdebug >= 3)
53280e9508eSDavid du Colombier 				dsprint(2, "serial: read: %s %ld\n", err, rcount);
5335e4f5c78SDavid du Colombier 		} while(rcount < 0 && strstr(err, "timed out") != nil);
5345e4f5c78SDavid du Colombier 
53580e9508eSDavid du Colombier 		dsprint(2, "serial: read from bulk %ld, %10.10s\n", rcount, err);
5365e4f5c78SDavid du Colombier 		if(rcount < 0){
5375e4f5c78SDavid du Colombier 			dsprint(2, "serial: need to recover, data read %ld %r\n",
5385e4f5c78SDavid du Colombier 				count);
539*84860c5dSDavid du Colombier 			serialrecover(ser, p, p->epin, err);
5405e4f5c78SDavid du Colombier 		}
54180e9508eSDavid du Colombier 		dsprint(2, "serial: read from bulk %ld\n", rcount);
5425e4f5c78SDavid du Colombier 		count = rcount;
5435e4f5c78SDavid du Colombier 		break;
5445e4f5c78SDavid du Colombier 	case Qctl:
54580e9508eSDavid du Colombier 		if(offset != 0)
5465e4f5c78SDavid du Colombier 			count = 0;
54780e9508eSDavid du Colombier 		else {
548d584e620SDavid du Colombier 			if(!p->isjtag){
549d5789509SDavid du Colombier 				e = serdumpst(p, buf, Serbufsize);
5505e4f5c78SDavid du Colombier 				count = usbreadbuf(data, count, 0, buf, e - buf);
55180e9508eSDavid du Colombier 			}
552d584e620SDavid du Colombier 		}
5535e4f5c78SDavid du Colombier 		break;
5545e4f5c78SDavid du Colombier 	}
555c8a340cdSDavid du Colombier 	if(count >= 0)
556c8a340cdSDavid du Colombier 		ser->recover = 0;
5575e4f5c78SDavid du Colombier 	qunlock(ser);
5585e4f5c78SDavid du Colombier 	free(err);
5595e4f5c78SDavid du Colombier 	free(buf);
5605e4f5c78SDavid du Colombier 	return count;
5615e4f5c78SDavid du Colombier }
5625e4f5c78SDavid du Colombier 
5635e4f5c78SDavid du Colombier static long
564d5789509SDavid du Colombier altwrite(Serialport *p, uchar *buf, long count)
56580e9508eSDavid du Colombier {
56680e9508eSDavid du Colombier 	int nw, dfd;
56780e9508eSDavid du Colombier 	char err[128];
568d5789509SDavid du Colombier 	Serial *ser;
56980e9508eSDavid du Colombier 
570d5789509SDavid du Colombier 	ser = p->s;
57180e9508eSDavid du Colombier 	do{
572d584e620SDavid du Colombier 		dsprint(2, "serial: write to bulk %ld\n", count);
573d584e620SDavid du Colombier 
57480e9508eSDavid du Colombier 		if(ser->wait4write != nil)
57580e9508eSDavid du Colombier 			/* unlocked inside later */
576d5789509SDavid du Colombier 			nw = ser->wait4write(p, buf, count);
57780e9508eSDavid du Colombier 		else{
578d5789509SDavid du Colombier 			dfd = p->epout->dfd;
57980e9508eSDavid du Colombier 			qunlock(ser);
58080e9508eSDavid du Colombier 			nw = write(dfd, buf, count);
58180e9508eSDavid du Colombier 			qlock(ser);
58280e9508eSDavid du Colombier 		}
58380e9508eSDavid du Colombier 		rerrstr(err, sizeof err);
584d584e620SDavid du Colombier 		dsprint(2, "serial: written %s %d\n", err, nw);
58580e9508eSDavid du Colombier 	} while(nw < 0 && strstr(err, "timed out") != nil);
58680e9508eSDavid du Colombier 
58780e9508eSDavid du Colombier 	if(nw != count){
58880e9508eSDavid du Colombier 		dsprint(2, "serial: need to recover, status in write %d %r\n",
58980e9508eSDavid du Colombier 			nw);
59080e9508eSDavid du Colombier 		snprint(err, sizeof err, "%r");
591*84860c5dSDavid du Colombier 		serialrecover(p->s, p, p->epout, err);
59280e9508eSDavid du Colombier 	}
59380e9508eSDavid du Colombier 	return nw;
59480e9508eSDavid du Colombier }
59580e9508eSDavid du Colombier 
59680e9508eSDavid du Colombier static long
5975e4f5c78SDavid du Colombier dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong)
5985e4f5c78SDavid du Colombier {
5995e4f5c78SDavid du Colombier 	ulong path;
6005e4f5c78SDavid du Colombier 	char *cmd;
601d5789509SDavid du Colombier 	Serialport *p;
6025e4f5c78SDavid du Colombier 	Serial *ser;
6035e4f5c78SDavid du Colombier 
604d5789509SDavid du Colombier 	p = fs->aux;
605d5789509SDavid du Colombier 	ser = p->s;
6065e4f5c78SDavid du Colombier 	path = fid->qid.path & ~fs->qid;
6075e4f5c78SDavid du Colombier 
6085e4f5c78SDavid du Colombier 	qlock(ser);
6095e4f5c78SDavid du Colombier 	switch(path){
6105e4f5c78SDavid du Colombier 	case Qdata:
611d5789509SDavid du Colombier 		count = altwrite(p, (uchar *)buf, count);
6125e4f5c78SDavid du Colombier 		break;
6135e4f5c78SDavid du Colombier 	case Qctl:
614d584e620SDavid du Colombier 		if(p->isjtag)
615d584e620SDavid du Colombier 			break;
6165e4f5c78SDavid du Colombier 		cmd = emallocz(count+1, 1);
6175e4f5c78SDavid du Colombier 		memmove(cmd, buf, count);
6185e4f5c78SDavid du Colombier 		cmd[count] = 0;
619d5789509SDavid du Colombier 		if(serialctl(p, cmd) < 0){
6205e4f5c78SDavid du Colombier 			qunlock(ser);
6215e4f5c78SDavid du Colombier 			werrstr(Ebadctl);
6225e4f5c78SDavid du Colombier 			free(cmd);
6235e4f5c78SDavid du Colombier 			return -1;
6245e4f5c78SDavid du Colombier 		}
6255e4f5c78SDavid du Colombier 		free(cmd);
6265e4f5c78SDavid du Colombier 		break;
6275e4f5c78SDavid du Colombier 	default:
6285e4f5c78SDavid du Colombier 		qunlock(ser);
6295e4f5c78SDavid du Colombier 		werrstr(Eperm);
6305e4f5c78SDavid du Colombier 		return -1;
6315e4f5c78SDavid du Colombier 	}
632c8a340cdSDavid du Colombier 	if(count >= 0)
633c8a340cdSDavid du Colombier 		ser->recover = 0;
634c8a340cdSDavid du Colombier 	else
635*84860c5dSDavid du Colombier 		serialrecover(ser, p, p->epout, "writing");
6365e4f5c78SDavid du Colombier 	qunlock(ser);
6375e4f5c78SDavid du Colombier 	return count;
6385e4f5c78SDavid du Colombier }
6395e4f5c78SDavid du Colombier 
6405e4f5c78SDavid du Colombier static int
641d5789509SDavid du Colombier openeps(Serialport *p, int epin, int epout, int epintr)
6425e4f5c78SDavid du Colombier {
643d5789509SDavid du Colombier 	Serial *ser;
644d5789509SDavid du Colombier 
645d5789509SDavid du Colombier 	ser = p->s;
646d5789509SDavid du Colombier 	p->epin = openep(ser->dev, epin);
647d5789509SDavid du Colombier 	if(p->epin == nil){
6485e4f5c78SDavid du Colombier 		fprint(2, "serial: openep %d: %r\n", epin);
6495e4f5c78SDavid du Colombier 		return -1;
6505e4f5c78SDavid du Colombier 	}
651d5789509SDavid du Colombier 	p->epout = openep(ser->dev, epout);
652d5789509SDavid du Colombier 	if(p->epout == nil){
6535e4f5c78SDavid du Colombier 		fprint(2, "serial: openep %d: %r\n", epout);
654d5789509SDavid du Colombier 		closedev(p->epin);
6555e4f5c78SDavid du Colombier 		return -1;
6565e4f5c78SDavid du Colombier 	}
6574d52e0f0SDavid du Colombier 
658d584e620SDavid du Colombier 	if(!p->isjtag){
659d5789509SDavid du Colombier 		devctl(p->epin,  "timeout 1000");
660d5789509SDavid du Colombier 		devctl(p->epout, "timeout 1000");
661d584e620SDavid du Colombier 	}
6624d52e0f0SDavid du Colombier 
6634d52e0f0SDavid du Colombier 	if(ser->hasepintr){
664d5789509SDavid du Colombier 		p->epintr = openep(ser->dev, epintr);
665d5789509SDavid du Colombier 		if(p->epintr == nil){
6665e4f5c78SDavid du Colombier 			fprint(2, "serial: openep %d: %r\n", epintr);
667d5789509SDavid du Colombier 			closedev(p->epin);
668d5789509SDavid du Colombier 			closedev(p->epout);
6695e4f5c78SDavid du Colombier 			return -1;
6705e4f5c78SDavid du Colombier 		}
671d5789509SDavid du Colombier 		opendevdata(p->epintr, OREAD);
672d5789509SDavid du Colombier 		devctl(p->epintr, "timeout 1000");
6734d52e0f0SDavid du Colombier 	}
6745e4f5c78SDavid du Colombier 
67580e9508eSDavid du Colombier 	if(ser->seteps!= nil)
676d5789509SDavid du Colombier 		ser->seteps(p);
677d5789509SDavid du Colombier 	opendevdata(p->epin, OREAD);
678d5789509SDavid du Colombier 	opendevdata(p->epout, OWRITE);
679d5789509SDavid du Colombier 	if(p->epin->dfd < 0 ||p->epout->dfd < 0 ||
680d5789509SDavid du Colombier 	    (ser->hasepintr && p->epintr->dfd < 0)){
6815e4f5c78SDavid du Colombier 		fprint(2, "serial: open i/o ep data: %r\n");
682d5789509SDavid du Colombier 		closedev(p->epin);
683d5789509SDavid du Colombier 		closedev(p->epout);
6844d52e0f0SDavid du Colombier 		if(ser->hasepintr)
685d5789509SDavid du Colombier 			closedev(p->epintr);
6865e4f5c78SDavid du Colombier 		return -1;
6875e4f5c78SDavid du Colombier 	}
6885e4f5c78SDavid du Colombier 	return 0;
6895e4f5c78SDavid du Colombier }
6905e4f5c78SDavid du Colombier 
6915e4f5c78SDavid du Colombier static int
692d5789509SDavid du Colombier findendpoints(Serial *ser, int ifc)
6935e4f5c78SDavid du Colombier {
6945e4f5c78SDavid du Colombier 	int i, epin, epout, epintr;
69580e9508eSDavid du Colombier 	Ep *ep, **eps;
6965e4f5c78SDavid du Colombier 
6975e4f5c78SDavid du Colombier 	epintr = epin = epout = -1;
69880e9508eSDavid du Colombier 
69980e9508eSDavid du Colombier 	/*
70080e9508eSDavid du Colombier 	 * interfc 0 means start from the start which is equiv to
70180e9508eSDavid du Colombier 	 * iterate through endpoints probably, could be done better
70280e9508eSDavid du Colombier 	 */
703d5789509SDavid du Colombier 	eps = ser->dev->usb->conf[0]->iface[ifc]->ep;
70480e9508eSDavid du Colombier 
70580e9508eSDavid du Colombier 	for(i = 0; i < Niface; i++){
70680e9508eSDavid du Colombier 		if((ep = eps[i]) == nil)
7075e4f5c78SDavid du Colombier 			continue;
70880e9508eSDavid du Colombier 		if(ser->hasepintr && ep->type == Eintr &&
70980e9508eSDavid du Colombier 		    ep->dir == Ein && epintr == -1)
7105e4f5c78SDavid du Colombier 			epintr = ep->id;
7115e4f5c78SDavid du Colombier 		if(ep->type == Ebulk){
7125e4f5c78SDavid du Colombier 			if(ep->dir == Ein && epin == -1)
7135e4f5c78SDavid du Colombier 				epin = ep->id;
7145e4f5c78SDavid du Colombier 			if(ep->dir == Eout && epout == -1)
7155e4f5c78SDavid du Colombier 				epout = ep->id;
7165e4f5c78SDavid du Colombier 		}
7175e4f5c78SDavid du Colombier 	}
718d5789509SDavid du Colombier 	dprint(2, "serial[%d]: ep ids: in %d out %d intr %d\n", ifc, epin, epout, epintr);
7194d52e0f0SDavid du Colombier 	if(epin == -1 || epout == -1 || (ser->hasepintr && epintr == -1))
7205e4f5c78SDavid du Colombier 		return -1;
7214d52e0f0SDavid du Colombier 
722d5789509SDavid du Colombier 	if(openeps(&ser->p[ifc], epin, epout, epintr) < 0)
7235e4f5c78SDavid du Colombier 		return -1;
7245e4f5c78SDavid du Colombier 
725d5789509SDavid du Colombier 	dprint(2, "serial: ep in %s out %s\n", ser->p[ifc].epin->dir, ser->p[ifc].epout->dir);
7264d52e0f0SDavid du Colombier 	if(ser->hasepintr)
727d5789509SDavid du Colombier 		dprint(2, "serial: ep intr %s\n", ser->p[ifc].epintr->dir);
7285e4f5c78SDavid du Colombier 
7295e4f5c78SDavid du Colombier 	if(usbdebug > 1 || serialdebug > 2){
730d5789509SDavid du Colombier 		devctl(ser->p[ifc].epin,  "debug 1");
731d5789509SDavid du Colombier 		devctl(ser->p[ifc].epout, "debug 1");
7324d52e0f0SDavid du Colombier 		if(ser->hasepintr)
733d5789509SDavid du Colombier 			devctl(ser->p[ifc].epintr, "debug 1");
7345e4f5c78SDavid du Colombier 		devctl(ser->dev, "debug 1");
7355e4f5c78SDavid du Colombier 	}
7365e4f5c78SDavid du Colombier 	return 0;
7375e4f5c78SDavid du Colombier }
7385e4f5c78SDavid du Colombier 
73980e9508eSDavid du Colombier /* keep in sync with main.c */
7405e4f5c78SDavid du Colombier static int
7415e4f5c78SDavid du Colombier usage(void)
7425e4f5c78SDavid du Colombier {
74380e9508eSDavid du Colombier 	werrstr("usage: usb/serial [-dD] [-m mtpt] [-s srv]");
7445e4f5c78SDavid du Colombier 	return -1;
7455e4f5c78SDavid du Colombier }
7465e4f5c78SDavid du Colombier 
7475e4f5c78SDavid du Colombier static void
7485e4f5c78SDavid du Colombier serdevfree(void *a)
7495e4f5c78SDavid du Colombier {
7505e4f5c78SDavid du Colombier 	Serial *ser = a;
751d5789509SDavid du Colombier 	Serialport *p;
752d5789509SDavid du Colombier 	int i;
7535e4f5c78SDavid du Colombier 
7545e4f5c78SDavid du Colombier 	if(ser == nil)
7555e4f5c78SDavid du Colombier 		return;
756d5789509SDavid du Colombier 
757d5789509SDavid du Colombier 	for(i = 0; i < ser->nifcs; i++){
758d5789509SDavid du Colombier 		p = &ser->p[i];
759d5789509SDavid du Colombier 
7604d52e0f0SDavid du Colombier 		if(ser->hasepintr)
761d5789509SDavid du Colombier 			closedev(p->epintr);
762d5789509SDavid du Colombier 		closedev(p->epin);
763d5789509SDavid du Colombier 		closedev(p->epout);
764d5789509SDavid du Colombier 		p->epintr = p->epin = p->epout = nil;
765d5789509SDavid du Colombier 		if(p->w4data != nil)
766d5789509SDavid du Colombier 			chanfree(p->w4data);
767d5789509SDavid du Colombier 		if(p->gotdata != nil)
768d5789509SDavid du Colombier 			chanfree(p->gotdata);
769d5789509SDavid du Colombier 		if(p->readc)
770d5789509SDavid du Colombier 			chanfree(p->readc);
771d5789509SDavid du Colombier 
772d5789509SDavid du Colombier 	}
7735e4f5c78SDavid du Colombier 	free(ser);
7745e4f5c78SDavid du Colombier }
7755e4f5c78SDavid du Colombier 
7765e4f5c78SDavid du Colombier static Usbfs serialfs = {
7775e4f5c78SDavid du Colombier 	.walk =	dwalk,
7785e4f5c78SDavid du Colombier 	.open =	dopen,
7795e4f5c78SDavid du Colombier 	.read =	dread,
7805e4f5c78SDavid du Colombier 	.write=	dwrite,
7815e4f5c78SDavid du Colombier 	.stat =	dstat,
7825e4f5c78SDavid du Colombier };
7835e4f5c78SDavid du Colombier 
784d5789509SDavid du Colombier static void
785d5789509SDavid du Colombier serialfsend(Usbfs *fs)
786d5789509SDavid du Colombier {
787d5789509SDavid du Colombier 	Serialport *p;
788d5789509SDavid du Colombier 
789d5789509SDavid du Colombier 	p = fs->aux;
790d5789509SDavid du Colombier 
791d5789509SDavid du Colombier 	if(p->w4data != nil)
792d5789509SDavid du Colombier 		chanclose(p->w4data);
793d5789509SDavid du Colombier 	if(p->gotdata != nil)
794d5789509SDavid du Colombier 		chanclose(p->gotdata);
795d5789509SDavid du Colombier 	if(p->readc)
796d5789509SDavid du Colombier 		chanclose(p->readc);
797d5789509SDavid du Colombier }
798d5789509SDavid du Colombier 
7995e4f5c78SDavid du Colombier int
8005e4f5c78SDavid du Colombier serialmain(Dev *dev, int argc, char* argv[])
8015e4f5c78SDavid du Colombier {
8025e4f5c78SDavid du Colombier 	Serial *ser;
803d5789509SDavid du Colombier 	Serialport *p;
8044d52e0f0SDavid du Colombier 	char buf[50];
805ed868a7cSDavid du Colombier 	int i, devid;
8065e4f5c78SDavid du Colombier 
807ed868a7cSDavid du Colombier 	devid = dev->id;
8085e4f5c78SDavid du Colombier 	ARGBEGIN{
8095e4f5c78SDavid du Colombier 	case 'd':
8105e4f5c78SDavid du Colombier 		serialdebug++;
8115e4f5c78SDavid du Colombier 		break;
812ed868a7cSDavid du Colombier 	case 'N':
813ed868a7cSDavid du Colombier 		devid = atoi(EARGF(usage()));
814ed868a7cSDavid du Colombier 		break;
8155e4f5c78SDavid du Colombier 	default:
8165e4f5c78SDavid du Colombier 		return usage();
8175e4f5c78SDavid du Colombier 	}ARGEND
8185e4f5c78SDavid du Colombier 	if(argc != 0)
8195e4f5c78SDavid du Colombier 		return usage();
8205e4f5c78SDavid du Colombier 
8215e4f5c78SDavid du Colombier 	ser = dev->aux = emallocz(sizeof(Serial), 1);
822d5789509SDavid du Colombier 	ser->maxrtrans = ser->maxwtrans = sizeof ser->p[0].data;
823d5789509SDavid du Colombier 	ser->maxread = ser->maxwrite = sizeof ser->p[0].data;
8245e4f5c78SDavid du Colombier 	ser->dev = dev;
8255e4f5c78SDavid du Colombier 	dev->free = serdevfree;
826d5789509SDavid du Colombier 	ser->jtag = -1;
827d5789509SDavid du Colombier 	ser->nifcs = 1;
8284d52e0f0SDavid du Colombier 
8294d52e0f0SDavid du Colombier 	snprint(buf, sizeof buf, "vid %#06x did %#06x",
8304d52e0f0SDavid du Colombier 		dev->usb->vid, dev->usb->did);
8314d52e0f0SDavid du Colombier 	if(plmatch(buf) == 0){
8324d52e0f0SDavid du Colombier 		ser->hasepintr = 1;
8334d52e0f0SDavid du Colombier 		ser->Serialops = plops;
83480e9508eSDavid du Colombier 	} else if(uconsmatch(buf) == 0)
8354d52e0f0SDavid du Colombier 		ser->Serialops = uconsops;
83680e9508eSDavid du Colombier 	else if(ftmatch(ser, buf) == 0)
83780e9508eSDavid du Colombier 		ser->Serialops = ftops;
83880e9508eSDavid du Colombier 	else {
83980e9508eSDavid du Colombier 		werrstr("serial: no serial devices found");
8405e4f5c78SDavid du Colombier 		return -1;
8415e4f5c78SDavid du Colombier 	}
842d5789509SDavid du Colombier 	for(i = 0; i < ser->nifcs; i++){
843d5789509SDavid du Colombier 		p = &ser->p[i];
844d5789509SDavid du Colombier 		p->interfc = i;
845d5789509SDavid du Colombier 		p->s = ser;
846d5789509SDavid du Colombier 		p->fs = serialfs;
847d584e620SDavid du Colombier 		if(i == ser->jtag){
848d584e620SDavid du Colombier 			p->isjtag++;
849d584e620SDavid du Colombier 		}
850d5789509SDavid du Colombier 		if(findendpoints(ser, i) < 0){
851d5789509SDavid du Colombier 			werrstr("serial: no endpoints found for ifc %d", i);
85280e9508eSDavid du Colombier 			return -1;
85380e9508eSDavid du Colombier 		}
854d5789509SDavid du Colombier 		p->w4data  = chancreate(sizeof(ulong), 0);
855d5789509SDavid du Colombier 		p->gotdata = chancreate(sizeof(ulong), 0);
856d5789509SDavid du Colombier 	}
857d5789509SDavid du Colombier 
858d5789509SDavid du Colombier 	qlock(ser);
859d5789509SDavid du Colombier 	serialreset(ser);
860d5789509SDavid du Colombier 	for(i = 0; i < ser->nifcs; i++){
861d5789509SDavid du Colombier 		p = &ser->p[i];
862d5789509SDavid du Colombier 		dprint(2, "serial: valid interface, calling serinit\n");
863d5789509SDavid du Colombier 		if(serinit(p) < 0){
8645e4f5c78SDavid du Colombier 			dprint(2, "serial: serinit: %r\n");
8655e4f5c78SDavid du Colombier 			return -1;
8665e4f5c78SDavid du Colombier 		}
8675e4f5c78SDavid du Colombier 
868d5789509SDavid du Colombier 		dsprint(2, "serial: adding interface %d, %p\n", p->interfc, p);
869d584e620SDavid du Colombier 		if(p->isjtag){
870d584e620SDavid du Colombier 			snprint(p->name, sizeof p->name, "jtag");
871d584e620SDavid du Colombier 			dsprint(2, "serial: JTAG interface %d %p\n", i, p);
872d584e620SDavid du Colombier 			snprint(p->fs.name, sizeof p->fs.name, "jtag%d.%d", devid, i);
873d584e620SDavid du Colombier 		} else {
874d584e620SDavid du Colombier 			snprint(p->name, sizeof p->name, "eiaU");
875ed868a7cSDavid du Colombier 			if(i == 0)
876ed868a7cSDavid du Colombier 				snprint(p->fs.name, sizeof p->fs.name, "eiaU%d", devid);
877ed868a7cSDavid du Colombier 			else
878ed868a7cSDavid du Colombier 				snprint(p->fs.name, sizeof p->fs.name, "eiaU%d.%d", devid, i);
879d584e620SDavid du Colombier 		}
880c8a340cdSDavid du Colombier 		fprint(2, "%s...", p->fs.name);
881d5789509SDavid du Colombier 		p->fs.dev = dev;
8825e4f5c78SDavid du Colombier 		incref(dev);
883d5789509SDavid du Colombier 		p->fs.aux = p;
884d5789509SDavid du Colombier 		p->fs.end = serialfsend;
885d5789509SDavid du Colombier 		usbfsadd(&p->fs);
886d5789509SDavid du Colombier 	}
8875e4f5c78SDavid du Colombier 
888d5789509SDavid du Colombier 	qunlock(ser);
8895e4f5c78SDavid du Colombier 	return 0;
8905e4f5c78SDavid du Colombier }
891