xref: /plan9-contrib/sys/src/cmd/usb/lib/dev.c (revision d4df0e25729142c8872e87f57ac688cf887aee08)
1906943f9SDavid du Colombier #include <u.h>
2906943f9SDavid du Colombier #include <libc.h>
3906943f9SDavid du Colombier #include <thread.h>
4906943f9SDavid du Colombier #include "usb.h"
5906943f9SDavid du Colombier 
6906943f9SDavid du Colombier /*
7906943f9SDavid du Colombier  * epN.M -> N
8906943f9SDavid du Colombier  */
9906943f9SDavid du Colombier static int
nameid(char * s)10906943f9SDavid du Colombier nameid(char *s)
11906943f9SDavid du Colombier {
12906943f9SDavid du Colombier 	char *r;
13906943f9SDavid du Colombier 	char nm[20];
14906943f9SDavid du Colombier 
15906943f9SDavid du Colombier 	r = strrchr(s, 'p');
16906943f9SDavid du Colombier 	if(r == nil)
17906943f9SDavid du Colombier 		return -1;
18906943f9SDavid du Colombier 	strecpy(nm, nm+sizeof(nm), r+1);
19906943f9SDavid du Colombier 	r = strchr(nm, '.');
20906943f9SDavid du Colombier 	if(r == nil)
21906943f9SDavid du Colombier 		return -1;
22906943f9SDavid du Colombier 	*r = 0;
23906943f9SDavid du Colombier 	return atoi(nm);
24906943f9SDavid du Colombier }
25906943f9SDavid du Colombier 
26906943f9SDavid du Colombier Dev*
openep(Dev * d,int id)27906943f9SDavid du Colombier openep(Dev *d, int id)
28906943f9SDavid du Colombier {
29906943f9SDavid du Colombier 	char *mode;	/* How many modes? */
30906943f9SDavid du Colombier 	Ep *ep;
31906943f9SDavid du Colombier 	Altc *ac;
32906943f9SDavid du Colombier 	Dev *epd;
33906943f9SDavid du Colombier 	Usbdev *ud;
34906943f9SDavid du Colombier 	char name[40];
35906943f9SDavid du Colombier 
3616146bc9SDavid du Colombier 	if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0)
3716146bc9SDavid du Colombier 		return nil;
38906943f9SDavid du Colombier 	if(d->cfd < 0 || d->usb == nil){
39906943f9SDavid du Colombier 		werrstr("device not configured");
40906943f9SDavid du Colombier 		return nil;
41906943f9SDavid du Colombier 	}
42906943f9SDavid du Colombier 	ud = d->usb;
43906943f9SDavid du Colombier 	if(id < 0 || id >= nelem(ud->ep) || ud->ep[id] == nil){
44906943f9SDavid du Colombier 		werrstr("bad enpoint number");
45906943f9SDavid du Colombier 		return nil;
46906943f9SDavid du Colombier 	}
47906943f9SDavid du Colombier 	ep = ud->ep[id];
48906943f9SDavid du Colombier 	mode = "rw";
49906943f9SDavid du Colombier 	if(ep->dir == Ein)
50906943f9SDavid du Colombier 		mode = "r";
51906943f9SDavid du Colombier 	if(ep->dir == Eout)
52906943f9SDavid du Colombier 		mode = "w";
53906943f9SDavid du Colombier 	snprint(name, sizeof(name), "/dev/usb/ep%d.%d", d->id, id);
54906943f9SDavid du Colombier 	if(access(name, AEXIST) == 0){
55906943f9SDavid du Colombier 		dprint(2, "%s: %s already exists; trying to open\n", argv0, name);
56d37e33ffSDavid du Colombier 		epd = opendev(name);
57d37e33ffSDavid du Colombier 		if(epd != nil)
58d37e33ffSDavid du Colombier 			epd->maxpkt = ep->maxpkt;	/* guess */
59d37e33ffSDavid du Colombier 		return epd;
60906943f9SDavid du Colombier 	}
61906943f9SDavid du Colombier 	if(devctl(d, "new %d %d %s", id, ep->type, mode) < 0){
62906943f9SDavid du Colombier 		dprint(2, "%s: %s: new: %r\n", argv0, d->dir);
63906943f9SDavid du Colombier 		return nil;
64906943f9SDavid du Colombier 	}
65906943f9SDavid du Colombier 	epd = opendev(name);
66906943f9SDavid du Colombier 	if(epd == nil)
67906943f9SDavid du Colombier 		return nil;
68906943f9SDavid du Colombier 	epd->id = id;
69906943f9SDavid du Colombier 	if(devctl(epd, "maxpkt %d", ep->maxpkt) < 0)
70906943f9SDavid du Colombier 		fprint(2, "%s: %s: openep: maxpkt: %r\n", argv0, epd->dir);
71906943f9SDavid du Colombier 	else
72906943f9SDavid du Colombier 		dprint(2, "%s: %s: maxpkt %d\n", argv0, epd->dir, ep->maxpkt);
73906943f9SDavid du Colombier 	epd->maxpkt = ep->maxpkt;
74906943f9SDavid du Colombier 	ac = ep->iface->altc[0];
75906943f9SDavid du Colombier 	if(ep->ntds > 1 && devctl(epd, "ntds %d", ep->ntds) < 0)
76906943f9SDavid du Colombier 		fprint(2, "%s: %s: openep: ntds: %r\n", argv0, epd->dir);
77906943f9SDavid du Colombier 	else
78906943f9SDavid du Colombier 		dprint(2, "%s: %s: ntds %d\n", argv0, epd->dir, ep->ntds);
79906943f9SDavid du Colombier 
80906943f9SDavid du Colombier 	/*
81906943f9SDavid du Colombier 	 * For iso endpoints and high speed interrupt endpoints the pollival is
82906943f9SDavid du Colombier 	 * actually 2ⁿ and not n.
83906943f9SDavid du Colombier 	 * The kernel usb driver must take that into account.
84906943f9SDavid du Colombier 	 * It's simpler this way.
85906943f9SDavid du Colombier 	 */
86906943f9SDavid du Colombier 
87906943f9SDavid du Colombier 	if(ac != nil && (ep->type == Eintr || ep->type == Eiso) && ac->interval != 0)
88906943f9SDavid du Colombier 		if(devctl(epd, "pollival %d", ac->interval) < 0)
89906943f9SDavid du Colombier 			fprint(2, "%s: %s: openep: pollival: %r\n", argv0, epd->dir);
90906943f9SDavid du Colombier 	return epd;
91906943f9SDavid du Colombier }
92906943f9SDavid du Colombier 
93906943f9SDavid du Colombier Dev*
opendev(char * fn)94906943f9SDavid du Colombier opendev(char *fn)
95906943f9SDavid du Colombier {
96906943f9SDavid du Colombier 	Dev *d;
97906943f9SDavid du Colombier 	int l;
98906943f9SDavid du Colombier 
9916146bc9SDavid du Colombier 	if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0)
10016146bc9SDavid du Colombier 		return nil;
101906943f9SDavid du Colombier 	d = emallocz(sizeof(Dev), 1);
102906943f9SDavid du Colombier 	incref(d);
103906943f9SDavid du Colombier 
104906943f9SDavid du Colombier 	l = strlen(fn);
105906943f9SDavid du Colombier 	d->dfd = -1;
10639dc1420SDavid du Colombier 	/*
10739dc1420SDavid du Colombier 	 * +30 to allocate extra size to concat "/<epfilename>"
10839dc1420SDavid du Colombier 	 * we should probably remove that feature from the manual
10939dc1420SDavid du Colombier 	 * and from the code after checking out that nobody relies on
11039dc1420SDavid du Colombier 	 * that.
11139dc1420SDavid du Colombier 	 */
11239dc1420SDavid du Colombier 	d->dir = emallocz(l + 30, 0);
113906943f9SDavid du Colombier 	strcpy(d->dir, fn);
114906943f9SDavid du Colombier 	strcpy(d->dir+l, "/ctl");
115906943f9SDavid du Colombier 	d->cfd = open(d->dir, ORDWR|OCEXEC);
116906943f9SDavid du Colombier 	d->dir[l] = 0;
117906943f9SDavid du Colombier 	d->id = nameid(fn);
118906943f9SDavid du Colombier 	if(d->cfd < 0){
119906943f9SDavid du Colombier 		werrstr("can't open endpoint %s: %r", d->dir);
120906943f9SDavid du Colombier 		free(d->dir);
121906943f9SDavid du Colombier 		free(d);
122906943f9SDavid du Colombier 		return nil;
123906943f9SDavid du Colombier 	}
124906943f9SDavid du Colombier 	dprint(2, "%s: opendev %#p %s\n", argv0, d, fn);
125906943f9SDavid du Colombier 	return d;
126906943f9SDavid du Colombier }
127906943f9SDavid du Colombier 
128906943f9SDavid du Colombier int
opendevdata(Dev * d,int mode)129906943f9SDavid du Colombier opendevdata(Dev *d, int mode)
130906943f9SDavid du Colombier {
13139dc1420SDavid du Colombier 	char buf[80]; /* more than enough for a usb path */
132906943f9SDavid du Colombier 
13339dc1420SDavid du Colombier 	seprint(buf, buf+sizeof(buf), "%s/data", d->dir);
13439dc1420SDavid du Colombier 	d->dfd = open(buf, mode|OCEXEC);
135906943f9SDavid du Colombier 	return d->dfd;
136906943f9SDavid du Colombier }
137906943f9SDavid du Colombier 
138a23bc242SDavid du Colombier enum
139a23bc242SDavid du Colombier {
1409fe70d77SDavid du Colombier 	/*
1419fe70d77SDavid du Colombier 	 * Max device conf is also limited by max control request size as
1429fe70d77SDavid du Colombier 	 * limited by Maxctllen in the kernel usb.h (both limits are arbitrary).
143a23bc242SDavid du Colombier 	 */
1440833d6c2SDavid du Colombier 	Maxdevconf = 4 * 1024,	/* asking for 16K kills Newsham's disk */
145a23bc242SDavid du Colombier };
146a23bc242SDavid du Colombier 
147906943f9SDavid du Colombier int
loaddevconf(Dev * d,int n)148906943f9SDavid du Colombier loaddevconf(Dev *d, int n)
149906943f9SDavid du Colombier {
150a23bc242SDavid du Colombier 	uchar *buf;
151*d4df0e25SDavid du Colombier 	int l, nr;
152906943f9SDavid du Colombier 	int type;
153906943f9SDavid du Colombier 
154906943f9SDavid du Colombier 	if(n >= nelem(d->usb->conf)){
155906943f9SDavid du Colombier 		werrstr("loaddevconf: bug: out of configurations in device");
156906943f9SDavid du Colombier 		fprint(2, "%s: %r\n", argv0);
157906943f9SDavid du Colombier 		return -1;
158906943f9SDavid du Colombier 	}
159a23bc242SDavid du Colombier 	buf = emallocz(Maxdevconf, 0);
160906943f9SDavid du Colombier 	type = Rd2h|Rstd|Rdev;
161*d4df0e25SDavid du Colombier 	nr = usbcmd(d, type, Rgetdesc, Dconf<<8|n, 0, buf, Dconflen);
162a23bc242SDavid du Colombier 	if(nr < Dconflen){
163a23bc242SDavid du Colombier 		free(buf);
164906943f9SDavid du Colombier 		return -1;
165a23bc242SDavid du Colombier 	}
166*d4df0e25SDavid du Colombier 	l = GET2(((DConf*)buf)->wTotalLength);
167*d4df0e25SDavid du Colombier 	if(l > Maxdevconf){
168*d4df0e25SDavid du Colombier 		free(buf);
169*d4df0e25SDavid du Colombier 		return -1;
170*d4df0e25SDavid du Colombier 	}
171*d4df0e25SDavid du Colombier 	nr = usbcmd(d, type, Rgetdesc, Dconf<<8|n, 0, buf, l);
172*d4df0e25SDavid du Colombier 	if(nr < l){
173*d4df0e25SDavid du Colombier 		free(buf);
174*d4df0e25SDavid du Colombier 		return -1;
175*d4df0e25SDavid du Colombier 	}
176*d4df0e25SDavid du Colombier 
177906943f9SDavid du Colombier 	if(d->usb->conf[n] == nil)
178906943f9SDavid du Colombier 		d->usb->conf[n] = emallocz(sizeof(Conf), 1);
179a23bc242SDavid du Colombier 	nr = parseconf(d->usb, d->usb->conf[n], buf, nr);
180a23bc242SDavid du Colombier 	free(buf);
181a23bc242SDavid du Colombier 	return nr;
182906943f9SDavid du Colombier }
183906943f9SDavid du Colombier 
184906943f9SDavid du Colombier Ep*
mkep(Usbdev * d,int id)185906943f9SDavid du Colombier mkep(Usbdev *d, int id)
186906943f9SDavid du Colombier {
187906943f9SDavid du Colombier 	Ep *ep;
188906943f9SDavid du Colombier 
189906943f9SDavid du Colombier 	d->ep[id] = ep = emallocz(sizeof(Ep), 1);
190906943f9SDavid du Colombier 	ep->id = id;
191906943f9SDavid du Colombier 	return ep;
192906943f9SDavid du Colombier }
193906943f9SDavid du Colombier 
194906943f9SDavid du Colombier static char*
mkstr(uchar * b,int n)195906943f9SDavid du Colombier mkstr(uchar *b, int n)
196906943f9SDavid du Colombier {
197906943f9SDavid du Colombier 	Rune r;
198906943f9SDavid du Colombier 	char *us;
199906943f9SDavid du Colombier 	char *s;
200906943f9SDavid du Colombier 	char *e;
201906943f9SDavid du Colombier 
202*d4df0e25SDavid du Colombier 	if(n > b[0])
203*d4df0e25SDavid du Colombier 		n = b[0];
204906943f9SDavid du Colombier 	if(n <= 2 || (n & 1) != 0)
205906943f9SDavid du Colombier 		return strdup("none");
206906943f9SDavid du Colombier 	n = (n - 2)/2;
207906943f9SDavid du Colombier 	b += 2;
208906943f9SDavid du Colombier 	us = s = emallocz(n*UTFmax+1, 0);
209906943f9SDavid du Colombier 	e = s + n*UTFmax+1;
210906943f9SDavid du Colombier 	for(; --n >= 0; b += 2){
211906943f9SDavid du Colombier 		r = GET2(b);
212906943f9SDavid du Colombier 		s = seprint(s, e, "%C", r);
213906943f9SDavid du Colombier 	}
214906943f9SDavid du Colombier 	return us;
215906943f9SDavid du Colombier }
216906943f9SDavid du Colombier 
217906943f9SDavid du Colombier char*
loaddevstr(Dev * d,int sid)218906943f9SDavid du Colombier loaddevstr(Dev *d, int sid)
219906943f9SDavid du Colombier {
220*d4df0e25SDavid du Colombier 	uchar buf[256];
221*d4df0e25SDavid du Colombier 	int type, langid, nr;
222906943f9SDavid du Colombier 
223906943f9SDavid du Colombier 	if(sid == 0)
224906943f9SDavid du Colombier 		return estrdup("none");
225906943f9SDavid du Colombier 	type = Rd2h|Rstd|Rdev;
226906943f9SDavid du Colombier 	nr = usbcmd(d, type, Rgetdesc, Dstr<<8|sid, 0, buf, sizeof(buf));
227*d4df0e25SDavid du Colombier 	if(nr < 4)
228*d4df0e25SDavid du Colombier 		langid = 0x0409;		/* english */
229*d4df0e25SDavid du Colombier 	else
230*d4df0e25SDavid du Colombier 		langid = buf[3]<<8 | buf[2];
231*d4df0e25SDavid du Colombier 	nr = usbcmd(d, type, Rgetdesc, Dstr<<8|sid, langid, buf, sizeof(buf));
232*d4df0e25SDavid du Colombier 
233906943f9SDavid du Colombier 	return mkstr(buf, nr);
234906943f9SDavid du Colombier }
235906943f9SDavid du Colombier 
236906943f9SDavid du Colombier int
loaddevdesc(Dev * d)237906943f9SDavid du Colombier loaddevdesc(Dev *d)
238906943f9SDavid du Colombier {
239*d4df0e25SDavid du Colombier 	uchar buf[Ddevlen];
240906943f9SDavid du Colombier 	int nr;
241906943f9SDavid du Colombier 	int type;
242906943f9SDavid du Colombier 	Ep *ep0;
243906943f9SDavid du Colombier 
244906943f9SDavid du Colombier 	type = Rd2h|Rstd|Rdev;
245906943f9SDavid du Colombier 	memset(buf, 0, Ddevlen);
246*d4df0e25SDavid du Colombier 	if((nr = usbcmd(d, type, Rgetdesc, Ddev<<8|0, 0, buf, Ddevlen)) < 0)
247906943f9SDavid du Colombier 		return -1;
248906943f9SDavid du Colombier 	if(nr < Ddevlen){
249906943f9SDavid du Colombier 		werrstr("short device descriptor (%d bytes)", nr);
250906943f9SDavid du Colombier 		return -1;
251906943f9SDavid du Colombier 	}
252906943f9SDavid du Colombier 	d->usb = emallocz(sizeof(Usbdev), 1);
253906943f9SDavid du Colombier 	ep0 = mkep(d->usb, 0);
254906943f9SDavid du Colombier 	ep0->dir = Eboth;
255906943f9SDavid du Colombier 	ep0->type = Econtrol;
256906943f9SDavid du Colombier 	ep0->maxpkt = d->maxpkt = 8;		/* a default */
257906943f9SDavid du Colombier 	nr = parsedev(d, buf, nr);
258906943f9SDavid du Colombier 	if(nr >= 0){
259906943f9SDavid du Colombier 		d->usb->vendor = loaddevstr(d, d->usb->vsid);
260906943f9SDavid du Colombier 		if(strcmp(d->usb->vendor, "none") != 0){
261906943f9SDavid du Colombier 			d->usb->product = loaddevstr(d, d->usb->psid);
262906943f9SDavid du Colombier 			d->usb->serial = loaddevstr(d, d->usb->ssid);
263906943f9SDavid du Colombier 		}
264906943f9SDavid du Colombier 	}
265*d4df0e25SDavid du Colombier 	else
266*d4df0e25SDavid du Colombier 		print("usbd: desc error: %r");
267906943f9SDavid du Colombier 	return nr;
268906943f9SDavid du Colombier }
269906943f9SDavid du Colombier 
270906943f9SDavid du Colombier int
configdev(Dev * d)271906943f9SDavid du Colombier configdev(Dev *d)
272906943f9SDavid du Colombier {
273906943f9SDavid du Colombier 	int i;
274906943f9SDavid du Colombier 
275906943f9SDavid du Colombier 	if(d->dfd < 0)
276906943f9SDavid du Colombier 		opendevdata(d, ORDWR);
277906943f9SDavid du Colombier 	if(loaddevdesc(d) < 0)
278906943f9SDavid du Colombier 		return -1;
279906943f9SDavid du Colombier 	for(i = 0; i < d->usb->nconf; i++)
280906943f9SDavid du Colombier 		if(loaddevconf(d, i) < 0)
281906943f9SDavid du Colombier 			return -1;
282906943f9SDavid du Colombier 	return 0;
283906943f9SDavid du Colombier }
284906943f9SDavid du Colombier 
285906943f9SDavid du Colombier static void
closeconf(Conf * c)286906943f9SDavid du Colombier closeconf(Conf *c)
287906943f9SDavid du Colombier {
288906943f9SDavid du Colombier 	int i;
289906943f9SDavid du Colombier 	int a;
290906943f9SDavid du Colombier 
291906943f9SDavid du Colombier 	if(c == nil)
292906943f9SDavid du Colombier 		return;
293906943f9SDavid du Colombier 	for(i = 0; i < nelem(c->iface); i++)
294906943f9SDavid du Colombier 		if(c->iface[i] != nil){
295906943f9SDavid du Colombier 			for(a = 0; a < nelem(c->iface[i]->altc); a++)
296906943f9SDavid du Colombier 				free(c->iface[i]->altc[a]);
297906943f9SDavid du Colombier 			free(c->iface[i]);
298906943f9SDavid du Colombier 		}
299906943f9SDavid du Colombier 	free(c);
300906943f9SDavid du Colombier }
301906943f9SDavid du Colombier 
302906943f9SDavid du Colombier void
closedev(Dev * d)303906943f9SDavid du Colombier closedev(Dev *d)
304906943f9SDavid du Colombier {
305906943f9SDavid du Colombier 	int i;
306906943f9SDavid du Colombier 	Usbdev *ud;
307906943f9SDavid du Colombier 
308906943f9SDavid du Colombier 	if(d==nil || decref(d) != 0)
309906943f9SDavid du Colombier 		return;
310906943f9SDavid du Colombier 	dprint(2, "%s: closedev %#p %s\n", argv0, d, d->dir);
311906943f9SDavid du Colombier 	if(d->free != nil)
312906943f9SDavid du Colombier 		d->free(d->aux);
313906943f9SDavid du Colombier 	if(d->cfd >= 0)
314906943f9SDavid du Colombier 		close(d->cfd);
315906943f9SDavid du Colombier 	if(d->dfd >= 0)
316906943f9SDavid du Colombier 		close(d->dfd);
317906943f9SDavid du Colombier 	d->cfd = d->dfd = -1;
318906943f9SDavid du Colombier 	free(d->dir);
319906943f9SDavid du Colombier 	d->dir = nil;
320906943f9SDavid du Colombier 	ud = d->usb;
321906943f9SDavid du Colombier 	d->usb = nil;
322906943f9SDavid du Colombier 	if(ud != nil){
323906943f9SDavid du Colombier 		free(ud->vendor);
324906943f9SDavid du Colombier 		free(ud->product);
325906943f9SDavid du Colombier 		free(ud->serial);
326906943f9SDavid du Colombier 		for(i = 0; i < nelem(ud->ep); i++)
327906943f9SDavid du Colombier 			free(ud->ep[i]);
328906943f9SDavid du Colombier 		for(i = 0; i < nelem(ud->ddesc); i++)
329906943f9SDavid du Colombier 			free(ud->ddesc[i]);
330906943f9SDavid du Colombier 
331906943f9SDavid du Colombier 		for(i = 0; i < nelem(ud->conf); i++)
332906943f9SDavid du Colombier 			closeconf(ud->conf[i]);
333906943f9SDavid du Colombier 		free(ud);
334906943f9SDavid du Colombier 	}
335906943f9SDavid du Colombier 	free(d);
336906943f9SDavid du Colombier }
337906943f9SDavid du Colombier 
338906943f9SDavid du Colombier static char*
reqstr(int type,int req)339906943f9SDavid du Colombier reqstr(int type, int req)
340906943f9SDavid du Colombier {
341906943f9SDavid du Colombier 	char *s;
342906943f9SDavid du Colombier 	static char* ds[] = { "dev", "if", "ep", "oth" };
343906943f9SDavid du Colombier 	static char buf[40];
344906943f9SDavid du Colombier 
345906943f9SDavid du Colombier 	if(type&Rd2h)
346906943f9SDavid du Colombier 		s = seprint(buf, buf+sizeof(buf), "d2h");
347906943f9SDavid du Colombier 	else
348906943f9SDavid du Colombier 		s = seprint(buf, buf+sizeof(buf), "h2d");
349906943f9SDavid du Colombier 	if(type&Rclass)
350906943f9SDavid du Colombier 		s = seprint(s, buf+sizeof(buf), "|cls");
351906943f9SDavid du Colombier 	else if(type&Rvendor)
352906943f9SDavid du Colombier 		s = seprint(s, buf+sizeof(buf), "|vnd");
353906943f9SDavid du Colombier 	else
354906943f9SDavid du Colombier 		s = seprint(s, buf+sizeof(buf), "|std");
355906943f9SDavid du Colombier 	s = seprint(s, buf+sizeof(buf), "|%s", ds[type&3]);
356906943f9SDavid du Colombier 
357906943f9SDavid du Colombier 	switch(req){
358906943f9SDavid du Colombier 	case Rgetstatus: s = seprint(s, buf+sizeof(buf), " getsts"); break;
359906943f9SDavid du Colombier 	case Rclearfeature: s = seprint(s, buf+sizeof(buf), " clrfeat"); break;
360906943f9SDavid du Colombier 	case Rsetfeature: s = seprint(s, buf+sizeof(buf), " setfeat"); break;
361906943f9SDavid du Colombier 	case Rsetaddress: s = seprint(s, buf+sizeof(buf), " setaddr"); break;
362906943f9SDavid du Colombier 	case Rgetdesc: s = seprint(s, buf+sizeof(buf), " getdesc"); break;
363906943f9SDavid du Colombier 	case Rsetdesc: s = seprint(s, buf+sizeof(buf), " setdesc"); break;
364906943f9SDavid du Colombier 	case Rgetconf: s = seprint(s, buf+sizeof(buf), " getcnf"); break;
365906943f9SDavid du Colombier 	case Rsetconf: s = seprint(s, buf+sizeof(buf), " setcnf"); break;
366906943f9SDavid du Colombier 	case Rgetiface: s = seprint(s, buf+sizeof(buf), " getif"); break;
367906943f9SDavid du Colombier 	case Rsetiface: s = seprint(s, buf+sizeof(buf), " setif"); break;
368906943f9SDavid du Colombier 	}
369906943f9SDavid du Colombier 	USED(s);
370906943f9SDavid du Colombier 	return buf;
371906943f9SDavid du Colombier }
372906943f9SDavid du Colombier 
373906943f9SDavid du Colombier static int
cmdreq(Dev * d,int type,int req,int value,int index,uchar * data,int count)374906943f9SDavid du Colombier cmdreq(Dev *d, int type, int req, int value, int index, uchar *data, int count)
375906943f9SDavid du Colombier {
376906943f9SDavid du Colombier 	int ndata, n;
377906943f9SDavid du Colombier 	uchar *wp;
378906943f9SDavid du Colombier 	uchar buf[8];
379906943f9SDavid du Colombier 	char *hd, *rs;
380906943f9SDavid du Colombier 
381906943f9SDavid du Colombier 	assert(d != nil);
382906943f9SDavid du Colombier 	if(data == nil){
383906943f9SDavid du Colombier 		wp = buf;
384906943f9SDavid du Colombier 		ndata = 0;
385906943f9SDavid du Colombier 	}else{
386906943f9SDavid du Colombier 		ndata = count;
387906943f9SDavid du Colombier 		wp = emallocz(8+ndata, 0);
388906943f9SDavid du Colombier 	}
389906943f9SDavid du Colombier 	wp[0] = type;
390906943f9SDavid du Colombier 	wp[1] = req;
391906943f9SDavid du Colombier 	PUT2(wp+2, value);
392906943f9SDavid du Colombier 	PUT2(wp+4, index);
393906943f9SDavid du Colombier 	PUT2(wp+6, count);
394906943f9SDavid du Colombier 	if(data != nil)
395906943f9SDavid du Colombier 		memmove(wp+8, data, ndata);
396906943f9SDavid du Colombier 	if(usbdebug>2){
397906943f9SDavid du Colombier 		hd = hexstr(wp, ndata+8);
398906943f9SDavid du Colombier 		rs = reqstr(type, req);
399906943f9SDavid du Colombier 		fprint(2, "%s: %s val %d|%d idx %d cnt %d out[%d] %s\n",
400906943f9SDavid du Colombier 			d->dir, rs, value>>8, value&0xFF,
401906943f9SDavid du Colombier 			index, count, ndata+8, hd);
402906943f9SDavid du Colombier 		free(hd);
403906943f9SDavid du Colombier 	}
404906943f9SDavid du Colombier 	n = write(d->dfd, wp, 8+ndata);
405906943f9SDavid du Colombier 	if(wp != buf)
406906943f9SDavid du Colombier 		free(wp);
407906943f9SDavid du Colombier 	if(n < 0)
408906943f9SDavid du Colombier 		return -1;
409906943f9SDavid du Colombier 	if(n != 8+ndata){
410906943f9SDavid du Colombier 		dprint(2, "%s: cmd: short write: %d\n", argv0, n);
411906943f9SDavid du Colombier 		return -1;
412906943f9SDavid du Colombier 	}
413906943f9SDavid du Colombier 	return n;
414906943f9SDavid du Colombier }
415906943f9SDavid du Colombier 
416906943f9SDavid du Colombier static int
cmdrep(Dev * d,void * buf,int nb)417906943f9SDavid du Colombier cmdrep(Dev *d, void *buf, int nb)
418906943f9SDavid du Colombier {
419906943f9SDavid du Colombier 	char *hd;
420906943f9SDavid du Colombier 
421906943f9SDavid du Colombier 	nb = read(d->dfd, buf, nb);
422906943f9SDavid du Colombier 	if(nb >0 && usbdebug > 2){
423906943f9SDavid du Colombier 		hd = hexstr(buf, nb);
424906943f9SDavid du Colombier 		fprint(2, "%s: in[%d] %s\n", d->dir, nb, hd);
425906943f9SDavid du Colombier 		free(hd);
426906943f9SDavid du Colombier 	}
427906943f9SDavid du Colombier 	return nb;
428906943f9SDavid du Colombier }
429906943f9SDavid du Colombier 
430906943f9SDavid du Colombier int
usbcmd(Dev * d,int type,int req,int value,int index,uchar * data,int count)431906943f9SDavid du Colombier usbcmd(Dev *d, int type, int req, int value, int index, uchar *data, int count)
432906943f9SDavid du Colombier {
433bfb6eab9SDavid du Colombier 	int i, r, nerr;
434906943f9SDavid du Colombier 	char err[64];
435906943f9SDavid du Colombier 
436906943f9SDavid du Colombier 	/*
437906943f9SDavid du Colombier 	 * Some devices do not respond to commands some times.
438906943f9SDavid du Colombier 	 * Others even report errors but later work just fine. Retry.
439906943f9SDavid du Colombier 	 */
440906943f9SDavid du Colombier 	r = -1;
441906943f9SDavid du Colombier 	*err = 0;
442906943f9SDavid du Colombier 	for(i = nerr = 0; i < Uctries; i++){
443906943f9SDavid du Colombier 		if(type & Rd2h)
444906943f9SDavid du Colombier 			r = cmdreq(d, type, req, value, index, nil, count);
445906943f9SDavid du Colombier 		else
446906943f9SDavid du Colombier 			r = cmdreq(d, type, req, value, index, data, count);
447906943f9SDavid du Colombier 		if(r > 0){
448906943f9SDavid du Colombier 			if((type & Rd2h) == 0)
449906943f9SDavid du Colombier 				break;
450906943f9SDavid du Colombier 			r = cmdrep(d, data, count);
451906943f9SDavid du Colombier 			if(r > 0)
452906943f9SDavid du Colombier 				break;
453906943f9SDavid du Colombier 			if(r == 0)
454906943f9SDavid du Colombier 				werrstr("no data from device");
455906943f9SDavid du Colombier 		}
456906943f9SDavid du Colombier 		nerr++;
457906943f9SDavid du Colombier 		if(*err == 0)
458906943f9SDavid du Colombier 			rerrstr(err, sizeof(err));
459906943f9SDavid du Colombier 		sleep(Ucdelay);
460906943f9SDavid du Colombier 	}
461bfb6eab9SDavid du Colombier 	if(r > 0 && i >= 2)
462906943f9SDavid du Colombier 		/* let the user know the device is not in good shape */
463906943f9SDavid du Colombier 		fprint(2, "%s: usbcmd: %s: required %d attempts (%s)\n",
464906943f9SDavid du Colombier 			argv0, d->dir, i, err);
465906943f9SDavid du Colombier 	return r;
466906943f9SDavid du Colombier }
467906943f9SDavid du Colombier 
468906943f9SDavid du Colombier int
unstall(Dev * dev,Dev * ep,int dir)469906943f9SDavid du Colombier unstall(Dev *dev, Dev *ep, int dir)
470906943f9SDavid du Colombier {
471906943f9SDavid du Colombier 	int r;
472906943f9SDavid du Colombier 
473906943f9SDavid du Colombier 	if(dir == Ein)
474906943f9SDavid du Colombier 		dir = 0x80;
475906943f9SDavid du Colombier 	else
476906943f9SDavid du Colombier 		dir = 0;
477906943f9SDavid du Colombier 	r = Rh2d|Rstd|Rep;
478906943f9SDavid du Colombier 	if(usbcmd(dev, r, Rclearfeature, Fhalt, ep->id|dir, nil, 0)<0){
479906943f9SDavid du Colombier 		werrstr("unstall: %s: %r", ep->dir);
480906943f9SDavid du Colombier 		return -1;
481906943f9SDavid du Colombier 	}
482906943f9SDavid du Colombier 	if(devctl(ep, "clrhalt") < 0){
483906943f9SDavid du Colombier 		werrstr("clrhalt: %s: %r", ep->dir);
484906943f9SDavid du Colombier 		return -1;
485906943f9SDavid du Colombier 	}
486906943f9SDavid du Colombier 	return 0;
487906943f9SDavid du Colombier }
488906943f9SDavid du Colombier 
489906943f9SDavid du Colombier /*
490906943f9SDavid du Colombier  * To be sure it uses a single write.
491906943f9SDavid du Colombier  */
492906943f9SDavid du Colombier int
devctl(Dev * dev,char * fmt,...)493906943f9SDavid du Colombier devctl(Dev *dev, char *fmt, ...)
494906943f9SDavid du Colombier {
495906943f9SDavid du Colombier 	char buf[128];
496906943f9SDavid du Colombier 	va_list arg;
497906943f9SDavid du Colombier 	char *e;
498906943f9SDavid du Colombier 
499906943f9SDavid du Colombier 	va_start(arg, fmt);
500906943f9SDavid du Colombier 	e = vseprint(buf, buf+sizeof(buf), fmt, arg);
501906943f9SDavid du Colombier 	va_end(arg);
502906943f9SDavid du Colombier 	return write(dev->cfd, buf, e-buf);
503906943f9SDavid du Colombier }
504