xref: /plan9/sys/src/cmd/usb/lib/dev.c (revision 16146bc9f7f774945213b79ebee114e739fe0b99)
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
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*
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 
36*16146bc9SDavid du Colombier 	if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0)
37*16146bc9SDavid 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);
56906943f9SDavid du Colombier 		return opendev(name);
57906943f9SDavid du Colombier 	}
58906943f9SDavid du Colombier 	if(devctl(d, "new %d %d %s", id, ep->type, mode) < 0){
59906943f9SDavid du Colombier 		dprint(2, "%s: %s: new: %r\n", argv0, d->dir);
60906943f9SDavid du Colombier 		return nil;
61906943f9SDavid du Colombier 	}
62906943f9SDavid du Colombier 	epd = opendev(name);
63906943f9SDavid du Colombier 	if(epd == nil)
64906943f9SDavid du Colombier 		return nil;
65906943f9SDavid du Colombier 	epd->id = id;
66906943f9SDavid du Colombier 	if(devctl(epd, "maxpkt %d", ep->maxpkt) < 0)
67906943f9SDavid du Colombier 		fprint(2, "%s: %s: openep: maxpkt: %r\n", argv0, epd->dir);
68906943f9SDavid du Colombier 	else
69906943f9SDavid du Colombier 		dprint(2, "%s: %s: maxpkt %d\n", argv0, epd->dir, ep->maxpkt);
70906943f9SDavid du Colombier 	epd->maxpkt = ep->maxpkt;
71906943f9SDavid du Colombier 	ac = ep->iface->altc[0];
72906943f9SDavid du Colombier 	if(ep->ntds > 1 && devctl(epd, "ntds %d", ep->ntds) < 0)
73906943f9SDavid du Colombier 		fprint(2, "%s: %s: openep: ntds: %r\n", argv0, epd->dir);
74906943f9SDavid du Colombier 	else
75906943f9SDavid du Colombier 		dprint(2, "%s: %s: ntds %d\n", argv0, epd->dir, ep->ntds);
76906943f9SDavid du Colombier 
77906943f9SDavid du Colombier 	/*
78906943f9SDavid du Colombier 	 * For iso endpoints and high speed interrupt endpoints the pollival is
79906943f9SDavid du Colombier 	 * actually 2ⁿ and not n.
80906943f9SDavid du Colombier 	 * The kernel usb driver must take that into account.
81906943f9SDavid du Colombier 	 * It's simpler this way.
82906943f9SDavid du Colombier 	 */
83906943f9SDavid du Colombier 
84906943f9SDavid du Colombier 	if(ac != nil && (ep->type == Eintr || ep->type == Eiso) && ac->interval != 0)
85906943f9SDavid du Colombier 		if(devctl(epd, "pollival %d", ac->interval) < 0)
86906943f9SDavid du Colombier 			fprint(2, "%s: %s: openep: pollival: %r\n", argv0, epd->dir);
87906943f9SDavid du Colombier 	return epd;
88906943f9SDavid du Colombier }
89906943f9SDavid du Colombier 
90906943f9SDavid du Colombier Dev*
91906943f9SDavid du Colombier opendev(char *fn)
92906943f9SDavid du Colombier {
93906943f9SDavid du Colombier 	Dev *d;
94906943f9SDavid du Colombier 	int l;
95906943f9SDavid du Colombier 
96*16146bc9SDavid du Colombier 	if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0)
97*16146bc9SDavid du Colombier 		return nil;
98906943f9SDavid du Colombier 	d = emallocz(sizeof(Dev), 1);
99906943f9SDavid du Colombier 	incref(d);
100906943f9SDavid du Colombier 
101906943f9SDavid du Colombier 	l = strlen(fn);
102906943f9SDavid du Colombier 	d->dfd = -1;
103906943f9SDavid du Colombier 	d->dir = emallocz(l + strlen("/data") + 1, 0);
104906943f9SDavid du Colombier 	strcpy(d->dir, fn);
105906943f9SDavid du Colombier 	strcpy(d->dir+l, "/ctl");
106906943f9SDavid du Colombier 	d->cfd = open(d->dir, ORDWR|OCEXEC);
107906943f9SDavid du Colombier 	d->dir[l] = 0;
108906943f9SDavid du Colombier 	d->id = nameid(fn);
109906943f9SDavid du Colombier 	if(d->cfd < 0){
110906943f9SDavid du Colombier 		werrstr("can't open endpoint %s: %r", d->dir);
111906943f9SDavid du Colombier 		free(d->dir);
112906943f9SDavid du Colombier 		free(d);
113906943f9SDavid du Colombier 		return nil;
114906943f9SDavid du Colombier 	}
115906943f9SDavid du Colombier 	dprint(2, "%s: opendev %#p %s\n", argv0, d, fn);
116906943f9SDavid du Colombier 	return d;
117906943f9SDavid du Colombier }
118906943f9SDavid du Colombier 
119906943f9SDavid du Colombier int
120906943f9SDavid du Colombier opendevdata(Dev *d, int mode)
121906943f9SDavid du Colombier {
122906943f9SDavid du Colombier 	int l;
123906943f9SDavid du Colombier 
124906943f9SDavid du Colombier 	l = strlen(d->dir);
125906943f9SDavid du Colombier 	strcpy(d->dir+l, "/data");
126906943f9SDavid du Colombier 	d->dfd = open(d->dir, mode|OCEXEC);
127906943f9SDavid du Colombier 	d->dir[l] = 0;
128906943f9SDavid du Colombier 	return d->dfd;
129906943f9SDavid du Colombier }
130906943f9SDavid du Colombier 
131906943f9SDavid du Colombier int
132906943f9SDavid du Colombier loaddevconf(Dev *d, int n)
133906943f9SDavid du Colombier {
134906943f9SDavid du Colombier 	uchar buf[1024];	/* enough room for extra descriptors */
135906943f9SDavid du Colombier 	int nr;
136906943f9SDavid du Colombier 	int type;
137906943f9SDavid du Colombier 
138906943f9SDavid du Colombier 	if(n >= nelem(d->usb->conf)){
139906943f9SDavid du Colombier 		werrstr("loaddevconf: bug: out of configurations in device");
140906943f9SDavid du Colombier 		fprint(2, "%s: %r\n", argv0);
141906943f9SDavid du Colombier 		return -1;
142906943f9SDavid du Colombier 	}
143906943f9SDavid du Colombier 	type = Rd2h|Rstd|Rdev;
144906943f9SDavid du Colombier 	nr = usbcmd(d, type, Rgetdesc, Dconf<<8|n, 0, buf, 1024);
145906943f9SDavid du Colombier 	if(nr < Dconflen)
146906943f9SDavid du Colombier 		return -1;
147906943f9SDavid du Colombier 	if(d->usb->conf[n] == nil)
148906943f9SDavid du Colombier 		d->usb->conf[n] = emallocz(sizeof(Conf), 1);
149906943f9SDavid du Colombier 	return parseconf(d->usb, d->usb->conf[n], buf, nr);
150906943f9SDavid du Colombier }
151906943f9SDavid du Colombier 
152906943f9SDavid du Colombier Ep*
153906943f9SDavid du Colombier mkep(Usbdev *d, int id)
154906943f9SDavid du Colombier {
155906943f9SDavid du Colombier 	Ep *ep;
156906943f9SDavid du Colombier 
157906943f9SDavid du Colombier 	d->ep[id] = ep = emallocz(sizeof(Ep), 1);
158906943f9SDavid du Colombier 	ep->id = id;
159906943f9SDavid du Colombier 	return ep;
160906943f9SDavid du Colombier }
161906943f9SDavid du Colombier 
162906943f9SDavid du Colombier static char*
163906943f9SDavid du Colombier mkstr(uchar *b, int n)
164906943f9SDavid du Colombier {
165906943f9SDavid du Colombier 	Rune r;
166906943f9SDavid du Colombier 	char *us;
167906943f9SDavid du Colombier 	char *s;
168906943f9SDavid du Colombier 	char *e;
169906943f9SDavid du Colombier 
170906943f9SDavid du Colombier 	if(n <= 2 || (n & 1) != 0)
171906943f9SDavid du Colombier 		return strdup("none");
172906943f9SDavid du Colombier 	n = (n - 2)/2;
173906943f9SDavid du Colombier 	b += 2;
174906943f9SDavid du Colombier 	us = s = emallocz(n*UTFmax+1, 0);
175906943f9SDavid du Colombier 	e = s + n*UTFmax+1;
176906943f9SDavid du Colombier 	for(; --n >= 0; b += 2){
177906943f9SDavid du Colombier 		r = GET2(b);
178906943f9SDavid du Colombier 		s = seprint(s, e, "%C", r);
179906943f9SDavid du Colombier 	}
180906943f9SDavid du Colombier 	return us;
181906943f9SDavid du Colombier }
182906943f9SDavid du Colombier 
183906943f9SDavid du Colombier char*
184906943f9SDavid du Colombier loaddevstr(Dev *d, int sid)
185906943f9SDavid du Colombier {
186906943f9SDavid du Colombier 	uchar buf[128];
187906943f9SDavid du Colombier 	int type;
188906943f9SDavid du Colombier 	int nr;
189906943f9SDavid du Colombier 
190906943f9SDavid du Colombier 	if(sid == 0)
191906943f9SDavid du Colombier 		return estrdup("none");
192906943f9SDavid du Colombier 	type = Rd2h|Rstd|Rdev;
193906943f9SDavid du Colombier 	nr=usbcmd(d, type, Rgetdesc, Dstr<<8|sid, 0, buf, sizeof(buf));
194906943f9SDavid du Colombier 	return mkstr(buf, nr);
195906943f9SDavid du Colombier }
196906943f9SDavid du Colombier 
197906943f9SDavid du Colombier int
198906943f9SDavid du Colombier loaddevdesc(Dev *d)
199906943f9SDavid du Colombier {
200906943f9SDavid du Colombier 	uchar buf[Ddevlen+255];
201906943f9SDavid du Colombier 	int nr;
202906943f9SDavid du Colombier 	int type;
203906943f9SDavid du Colombier 	Ep *ep0;
204906943f9SDavid du Colombier 
205906943f9SDavid du Colombier 	type = Rd2h|Rstd|Rdev;
206906943f9SDavid du Colombier 	nr = sizeof(buf);
207906943f9SDavid du Colombier 	memset(buf, 0, Ddevlen);
208906943f9SDavid du Colombier 	if((nr=usbcmd(d, type, Rgetdesc, Ddev<<8|0, 0, buf, nr)) < 0)
209906943f9SDavid du Colombier 		return -1;
210906943f9SDavid du Colombier 	/*
211906943f9SDavid du Colombier 	 * Several hubs are returning descriptors of 17 bytes, not 18.
212906943f9SDavid du Colombier 	 * We accept them and leave number of configurations as zero.
213906943f9SDavid du Colombier 	 * (a get configuration descriptor also fails for them!)
214906943f9SDavid du Colombier 	 */
215906943f9SDavid du Colombier 	if(nr < Ddevlen){
216906943f9SDavid du Colombier 		print("%s: %s: warning: device with short descriptor\n",
217906943f9SDavid du Colombier 			argv0, d->dir);
218906943f9SDavid du Colombier 		if(nr < Ddevlen-1){
219906943f9SDavid du Colombier 			werrstr("short device descriptor (%d bytes)", nr);
220906943f9SDavid du Colombier 			return -1;
221906943f9SDavid du Colombier 		}
222906943f9SDavid du Colombier 	}
223906943f9SDavid du Colombier 	d->usb = emallocz(sizeof(Usbdev), 1);
224906943f9SDavid du Colombier 	ep0 = mkep(d->usb, 0);
225906943f9SDavid du Colombier 	ep0->dir = Eboth;
226906943f9SDavid du Colombier 	ep0->type = Econtrol;
227906943f9SDavid du Colombier 	ep0->maxpkt = d->maxpkt = 8;		/* a default */
228906943f9SDavid du Colombier 	nr = parsedev(d, buf, nr);
229906943f9SDavid du Colombier 	if(nr >= 0){
230906943f9SDavid du Colombier 		d->usb->vendor = loaddevstr(d, d->usb->vsid);
231906943f9SDavid du Colombier 		if(strcmp(d->usb->vendor, "none") != 0){
232906943f9SDavid du Colombier 			d->usb->product = loaddevstr(d, d->usb->psid);
233906943f9SDavid du Colombier 			d->usb->serial = loaddevstr(d, d->usb->ssid);
234906943f9SDavid du Colombier 		}
235906943f9SDavid du Colombier 	}
236906943f9SDavid du Colombier 	return nr;
237906943f9SDavid du Colombier }
238906943f9SDavid du Colombier 
239906943f9SDavid du Colombier int
240906943f9SDavid du Colombier configdev(Dev *d)
241906943f9SDavid du Colombier {
242906943f9SDavid du Colombier 	int i;
243906943f9SDavid du Colombier 
244906943f9SDavid du Colombier 	if(d->dfd < 0)
245906943f9SDavid du Colombier 		opendevdata(d, ORDWR);
246906943f9SDavid du Colombier 	if(loaddevdesc(d) < 0)
247906943f9SDavid du Colombier 		return -1;
248906943f9SDavid du Colombier 	for(i = 0; i < d->usb->nconf; i++)
249906943f9SDavid du Colombier 		if(loaddevconf(d, i) < 0)
250906943f9SDavid du Colombier 			return -1;
251906943f9SDavid du Colombier 	return 0;
252906943f9SDavid du Colombier }
253906943f9SDavid du Colombier 
254906943f9SDavid du Colombier static void
255906943f9SDavid du Colombier closeconf(Conf *c)
256906943f9SDavid du Colombier {
257906943f9SDavid du Colombier 	int i;
258906943f9SDavid du Colombier 	int a;
259906943f9SDavid du Colombier 
260906943f9SDavid du Colombier 	if(c == nil)
261906943f9SDavid du Colombier 		return;
262906943f9SDavid du Colombier 	for(i = 0; i < nelem(c->iface); i++)
263906943f9SDavid du Colombier 		if(c->iface[i] != nil){
264906943f9SDavid du Colombier 			for(a = 0; a < nelem(c->iface[i]->altc); a++)
265906943f9SDavid du Colombier 				free(c->iface[i]->altc[a]);
266906943f9SDavid du Colombier 			free(c->iface[i]);
267906943f9SDavid du Colombier 		}
268906943f9SDavid du Colombier 	free(c);
269906943f9SDavid du Colombier }
270906943f9SDavid du Colombier 
271906943f9SDavid du Colombier void
272906943f9SDavid du Colombier closedev(Dev *d)
273906943f9SDavid du Colombier {
274906943f9SDavid du Colombier 	int i;
275906943f9SDavid du Colombier 	Usbdev *ud;
276906943f9SDavid du Colombier 
277906943f9SDavid du Colombier 	if(d==nil || decref(d) != 0)
278906943f9SDavid du Colombier 		return;
279906943f9SDavid du Colombier 	dprint(2, "%s: closedev %#p %s\n", argv0, d, d->dir);
280906943f9SDavid du Colombier 	if(d->free != nil)
281906943f9SDavid du Colombier 		d->free(d->aux);
282906943f9SDavid du Colombier 	if(d->cfd >= 0)
283906943f9SDavid du Colombier 		close(d->cfd);
284906943f9SDavid du Colombier 	if(d->dfd >= 0)
285906943f9SDavid du Colombier 		close(d->dfd);
286906943f9SDavid du Colombier 	d->cfd = d->dfd = -1;
287906943f9SDavid du Colombier 	free(d->dir);
288906943f9SDavid du Colombier 	d->dir = nil;
289906943f9SDavid du Colombier 	ud = d->usb;
290906943f9SDavid du Colombier 	d->usb = nil;
291906943f9SDavid du Colombier 	if(ud != nil){
292906943f9SDavid du Colombier 		free(ud->vendor);
293906943f9SDavid du Colombier 		free(ud->product);
294906943f9SDavid du Colombier 		free(ud->serial);
295906943f9SDavid du Colombier 		for(i = 0; i < nelem(ud->ep); i++)
296906943f9SDavid du Colombier 			free(ud->ep[i]);
297906943f9SDavid du Colombier 		for(i = 0; i < nelem(ud->ddesc); i++)
298906943f9SDavid du Colombier 			free(ud->ddesc[i]);
299906943f9SDavid du Colombier 
300906943f9SDavid du Colombier 		for(i = 0; i < nelem(ud->conf); i++)
301906943f9SDavid du Colombier 			closeconf(ud->conf[i]);
302906943f9SDavid du Colombier 		free(ud);
303906943f9SDavid du Colombier 	}
304906943f9SDavid du Colombier 	free(d);
305906943f9SDavid du Colombier }
306906943f9SDavid du Colombier 
307906943f9SDavid du Colombier static char*
308906943f9SDavid du Colombier reqstr(int type, int req)
309906943f9SDavid du Colombier {
310906943f9SDavid du Colombier 	char *s;
311906943f9SDavid du Colombier 	static char* ds[] = { "dev", "if", "ep", "oth" };
312906943f9SDavid du Colombier 	static char buf[40];
313906943f9SDavid du Colombier 
314906943f9SDavid du Colombier 	if(type&Rd2h)
315906943f9SDavid du Colombier 		s = seprint(buf, buf+sizeof(buf), "d2h");
316906943f9SDavid du Colombier 	else
317906943f9SDavid du Colombier 		s = seprint(buf, buf+sizeof(buf), "h2d");
318906943f9SDavid du Colombier 	if(type&Rclass)
319906943f9SDavid du Colombier 		s = seprint(s, buf+sizeof(buf), "|cls");
320906943f9SDavid du Colombier 	else if(type&Rvendor)
321906943f9SDavid du Colombier 		s = seprint(s, buf+sizeof(buf), "|vnd");
322906943f9SDavid du Colombier 	else
323906943f9SDavid du Colombier 		s = seprint(s, buf+sizeof(buf), "|std");
324906943f9SDavid du Colombier 	s = seprint(s, buf+sizeof(buf), "|%s", ds[type&3]);
325906943f9SDavid du Colombier 
326906943f9SDavid du Colombier 	switch(req){
327906943f9SDavid du Colombier 	case Rgetstatus: s = seprint(s, buf+sizeof(buf), " getsts"); break;
328906943f9SDavid du Colombier 	case Rclearfeature: s = seprint(s, buf+sizeof(buf), " clrfeat"); break;
329906943f9SDavid du Colombier 	case Rsetfeature: s = seprint(s, buf+sizeof(buf), " setfeat"); break;
330906943f9SDavid du Colombier 	case Rsetaddress: s = seprint(s, buf+sizeof(buf), " setaddr"); break;
331906943f9SDavid du Colombier 	case Rgetdesc: s = seprint(s, buf+sizeof(buf), " getdesc"); break;
332906943f9SDavid du Colombier 	case Rsetdesc: s = seprint(s, buf+sizeof(buf), " setdesc"); break;
333906943f9SDavid du Colombier 	case Rgetconf: s = seprint(s, buf+sizeof(buf), " getcnf"); break;
334906943f9SDavid du Colombier 	case Rsetconf: s = seprint(s, buf+sizeof(buf), " setcnf"); break;
335906943f9SDavid du Colombier 	case Rgetiface: s = seprint(s, buf+sizeof(buf), " getif"); break;
336906943f9SDavid du Colombier 	case Rsetiface: s = seprint(s, buf+sizeof(buf), " setif"); break;
337906943f9SDavid du Colombier 	}
338906943f9SDavid du Colombier 	USED(s);
339906943f9SDavid du Colombier 	return buf;
340906943f9SDavid du Colombier }
341906943f9SDavid du Colombier 
342906943f9SDavid du Colombier static int
343906943f9SDavid du Colombier cmdreq(Dev *d, int type, int req, int value, int index, uchar *data, int count)
344906943f9SDavid du Colombier {
345906943f9SDavid du Colombier 	int ndata, n;
346906943f9SDavid du Colombier 	uchar *wp;
347906943f9SDavid du Colombier 	uchar buf[8];
348906943f9SDavid du Colombier 	char *hd, *rs;
349906943f9SDavid du Colombier 
350906943f9SDavid du Colombier 	assert(d != nil);
351906943f9SDavid du Colombier 	if(data == nil){
352906943f9SDavid du Colombier 		wp = buf;
353906943f9SDavid du Colombier 		ndata = 0;
354906943f9SDavid du Colombier 	}else{
355906943f9SDavid du Colombier 		ndata = count;
356906943f9SDavid du Colombier 		wp = emallocz(8+ndata, 0);
357906943f9SDavid du Colombier 	}
358906943f9SDavid du Colombier 	wp[0] = type;
359906943f9SDavid du Colombier 	wp[1] = req;
360906943f9SDavid du Colombier 	PUT2(wp+2, value);
361906943f9SDavid du Colombier 	PUT2(wp+4, index);
362906943f9SDavid du Colombier 	PUT2(wp+6, count);
363906943f9SDavid du Colombier 	if(data != nil)
364906943f9SDavid du Colombier 		memmove(wp+8, data, ndata);
365906943f9SDavid du Colombier 	if(usbdebug>2){
366906943f9SDavid du Colombier 		hd = hexstr(wp, ndata+8);
367906943f9SDavid du Colombier 		rs = reqstr(type, req);
368906943f9SDavid du Colombier 		fprint(2, "%s: %s val %d|%d idx %d cnt %d out[%d] %s\n",
369906943f9SDavid du Colombier 			d->dir, rs, value>>8, value&0xFF,
370906943f9SDavid du Colombier 			index, count, ndata+8, hd);
371906943f9SDavid du Colombier 		free(hd);
372906943f9SDavid du Colombier 	}
373906943f9SDavid du Colombier 	n = write(d->dfd, wp, 8+ndata);
374906943f9SDavid du Colombier 	if(wp != buf)
375906943f9SDavid du Colombier 		free(wp);
376906943f9SDavid du Colombier 	if(n < 0)
377906943f9SDavid du Colombier 		return -1;
378906943f9SDavid du Colombier 	if(n != 8+ndata){
379906943f9SDavid du Colombier 		dprint(2, "%s: cmd: short write: %d\n", argv0, n);
380906943f9SDavid du Colombier 		return -1;
381906943f9SDavid du Colombier 	}
382906943f9SDavid du Colombier 	return n;
383906943f9SDavid du Colombier }
384906943f9SDavid du Colombier 
385906943f9SDavid du Colombier static int
386906943f9SDavid du Colombier cmdrep(Dev *d, void *buf, int nb)
387906943f9SDavid du Colombier {
388906943f9SDavid du Colombier 	char *hd;
389906943f9SDavid du Colombier 
390906943f9SDavid du Colombier 	nb = read(d->dfd, buf, nb);
391906943f9SDavid du Colombier 	if(nb >0 && usbdebug > 2){
392906943f9SDavid du Colombier 		hd = hexstr(buf, nb);
393906943f9SDavid du Colombier 		fprint(2, "%s: in[%d] %s\n", d->dir, nb, hd);
394906943f9SDavid du Colombier 		free(hd);
395906943f9SDavid du Colombier 	}
396906943f9SDavid du Colombier 	return nb;
397906943f9SDavid du Colombier }
398906943f9SDavid du Colombier 
399906943f9SDavid du Colombier int
400906943f9SDavid du Colombier usbcmd(Dev *d, int type, int req, int value, int index, uchar *data, int count)
401906943f9SDavid du Colombier {
402906943f9SDavid du Colombier 	int r;
403906943f9SDavid du Colombier 	int i;
404906943f9SDavid du Colombier 	int nerr;
405906943f9SDavid du Colombier 	char err[64];
406906943f9SDavid du Colombier 
407906943f9SDavid du Colombier 	/*
408906943f9SDavid du Colombier 	 * Some devices do not respond to commands some times.
409906943f9SDavid du Colombier 	 * Others even report errors but later work just fine. Retry.
410906943f9SDavid du Colombier 	 */
411906943f9SDavid du Colombier 	r = -1;
412906943f9SDavid du Colombier 	*err = 0;
413906943f9SDavid du Colombier 	for(i = nerr = 0; i < Uctries; i++){
414906943f9SDavid du Colombier 		if(type&Rd2h)
415906943f9SDavid du Colombier 			r = cmdreq(d, type, req, value, index, nil, count);
416906943f9SDavid du Colombier 		else
417906943f9SDavid du Colombier 			r = cmdreq(d, type, req, value, index, data, count);
418906943f9SDavid du Colombier 		if(r > 0){
419906943f9SDavid du Colombier 			if((type&Rd2h) == 0)
420906943f9SDavid du Colombier 				break;
421906943f9SDavid du Colombier 			r = cmdrep(d, data, count);
422906943f9SDavid du Colombier 			if(r > 0)
423906943f9SDavid du Colombier 				break;
424906943f9SDavid du Colombier 			if(r == 0)
425906943f9SDavid du Colombier 				werrstr("no data from device");
426906943f9SDavid du Colombier 		}
427906943f9SDavid du Colombier 		nerr++;
428906943f9SDavid du Colombier 		if(*err == 0)
429906943f9SDavid du Colombier 			rerrstr(err, sizeof(err));
430906943f9SDavid du Colombier 		sleep(Ucdelay);
431906943f9SDavid du Colombier 	}
432906943f9SDavid du Colombier 	if(r > 0 && i >= 2){
433906943f9SDavid du Colombier 		/* let the user know the device is not in good shape */
434906943f9SDavid du Colombier 		fprint(2, "%s: usbcmd: %s: required %d attempts (%s)\n",
435906943f9SDavid du Colombier 			argv0, d->dir, i, err);
436906943f9SDavid du Colombier 	}
437906943f9SDavid du Colombier 	return r;
438906943f9SDavid du Colombier }
439906943f9SDavid du Colombier 
440906943f9SDavid du Colombier int
441906943f9SDavid du Colombier unstall(Dev *dev, Dev *ep, int dir)
442906943f9SDavid du Colombier {
443906943f9SDavid du Colombier 	int r;
444906943f9SDavid du Colombier 
445906943f9SDavid du Colombier 	if(dir == Ein)
446906943f9SDavid du Colombier 		dir = 0x80;
447906943f9SDavid du Colombier 	else
448906943f9SDavid du Colombier 		dir = 0;
449906943f9SDavid du Colombier 	r = Rh2d|Rstd|Rep;
450906943f9SDavid du Colombier 	if(usbcmd(dev, r, Rclearfeature, Fhalt, ep->id|dir, nil, 0)<0){
451906943f9SDavid du Colombier 		werrstr("unstall: %s: %r", ep->dir);
452906943f9SDavid du Colombier 		return -1;
453906943f9SDavid du Colombier 	}
454906943f9SDavid du Colombier 	if(devctl(ep, "clrhalt") < 0){
455906943f9SDavid du Colombier 		werrstr("clrhalt: %s: %r", ep->dir);
456906943f9SDavid du Colombier 		return -1;
457906943f9SDavid du Colombier 	}
458906943f9SDavid du Colombier 	return 0;
459906943f9SDavid du Colombier }
460906943f9SDavid du Colombier 
461906943f9SDavid du Colombier /*
462906943f9SDavid du Colombier  * To be sure it uses a single write.
463906943f9SDavid du Colombier  */
464906943f9SDavid du Colombier int
465906943f9SDavid du Colombier devctl(Dev *dev, char *fmt, ...)
466906943f9SDavid du Colombier {
467906943f9SDavid du Colombier 	char buf[128];
468906943f9SDavid du Colombier 	va_list arg;
469906943f9SDavid du Colombier 	char *e;
470906943f9SDavid du Colombier 
471906943f9SDavid du Colombier 	va_start(arg, fmt);
472906943f9SDavid du Colombier 	e = vseprint(buf, buf+sizeof(buf), fmt, arg);
473906943f9SDavid du Colombier 	va_end(arg);
474906943f9SDavid du Colombier 	return write(dev->cfd, buf, e-buf);
475906943f9SDavid du Colombier }
476