xref: /plan9/sys/src/cmd/usb/lib/dev.c (revision bfb6eab9346d861b5f68a2b1af55a1768a8fe25b)
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;
151906943f9SDavid du Colombier 	int 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;
161a23bc242SDavid du Colombier 	nr = usbcmd(d, type, Rgetdesc, Dconf<<8|n, 0, buf, Maxdevconf);
162a23bc242SDavid du Colombier 	if(nr < Dconflen){
163a23bc242SDavid du Colombier 		free(buf);
164906943f9SDavid du Colombier 		return -1;
165a23bc242SDavid du Colombier 	}
166906943f9SDavid du Colombier 	if(d->usb->conf[n] == nil)
167906943f9SDavid du Colombier 		d->usb->conf[n] = emallocz(sizeof(Conf), 1);
168a23bc242SDavid du Colombier 	nr = parseconf(d->usb, d->usb->conf[n], buf, nr);
169a23bc242SDavid du Colombier 	free(buf);
170a23bc242SDavid du Colombier 	return nr;
171906943f9SDavid du Colombier }
172906943f9SDavid du Colombier 
173906943f9SDavid du Colombier Ep*
mkep(Usbdev * d,int id)174906943f9SDavid du Colombier mkep(Usbdev *d, int id)
175906943f9SDavid du Colombier {
176906943f9SDavid du Colombier 	Ep *ep;
177906943f9SDavid du Colombier 
178906943f9SDavid du Colombier 	d->ep[id] = ep = emallocz(sizeof(Ep), 1);
179906943f9SDavid du Colombier 	ep->id = id;
180906943f9SDavid du Colombier 	return ep;
181906943f9SDavid du Colombier }
182906943f9SDavid du Colombier 
183906943f9SDavid du Colombier static char*
mkstr(uchar * b,int n)184906943f9SDavid du Colombier mkstr(uchar *b, int n)
185906943f9SDavid du Colombier {
186906943f9SDavid du Colombier 	Rune r;
187906943f9SDavid du Colombier 	char *us;
188906943f9SDavid du Colombier 	char *s;
189906943f9SDavid du Colombier 	char *e;
190906943f9SDavid du Colombier 
191906943f9SDavid du Colombier 	if(n <= 2 || (n & 1) != 0)
192906943f9SDavid du Colombier 		return strdup("none");
193906943f9SDavid du Colombier 	n = (n - 2)/2;
194906943f9SDavid du Colombier 	b += 2;
195906943f9SDavid du Colombier 	us = s = emallocz(n*UTFmax+1, 0);
196906943f9SDavid du Colombier 	e = s + n*UTFmax+1;
197906943f9SDavid du Colombier 	for(; --n >= 0; b += 2){
198906943f9SDavid du Colombier 		r = GET2(b);
199906943f9SDavid du Colombier 		s = seprint(s, e, "%C", r);
200906943f9SDavid du Colombier 	}
201906943f9SDavid du Colombier 	return us;
202906943f9SDavid du Colombier }
203906943f9SDavid du Colombier 
204906943f9SDavid du Colombier char*
loaddevstr(Dev * d,int sid)205906943f9SDavid du Colombier loaddevstr(Dev *d, int sid)
206906943f9SDavid du Colombier {
207906943f9SDavid du Colombier 	uchar buf[128];
208906943f9SDavid du Colombier 	int type;
209906943f9SDavid du Colombier 	int nr;
210906943f9SDavid du Colombier 
211906943f9SDavid du Colombier 	if(sid == 0)
212906943f9SDavid du Colombier 		return estrdup("none");
213906943f9SDavid du Colombier 	type = Rd2h|Rstd|Rdev;
214906943f9SDavid du Colombier 	nr=usbcmd(d, type, Rgetdesc, Dstr<<8|sid, 0, buf, sizeof(buf));
215906943f9SDavid du Colombier 	return mkstr(buf, nr);
216906943f9SDavid du Colombier }
217906943f9SDavid du Colombier 
218906943f9SDavid du Colombier int
loaddevdesc(Dev * d)219906943f9SDavid du Colombier loaddevdesc(Dev *d)
220906943f9SDavid du Colombier {
221906943f9SDavid du Colombier 	uchar buf[Ddevlen+255];
222906943f9SDavid du Colombier 	int nr;
223906943f9SDavid du Colombier 	int type;
224906943f9SDavid du Colombier 	Ep *ep0;
225906943f9SDavid du Colombier 
226906943f9SDavid du Colombier 	type = Rd2h|Rstd|Rdev;
227906943f9SDavid du Colombier 	nr = sizeof(buf);
228906943f9SDavid du Colombier 	memset(buf, 0, Ddevlen);
229906943f9SDavid du Colombier 	if((nr=usbcmd(d, type, Rgetdesc, Ddev<<8|0, 0, buf, nr)) < 0)
230906943f9SDavid du Colombier 		return -1;
231906943f9SDavid du Colombier 	/*
232906943f9SDavid du Colombier 	 * Several hubs are returning descriptors of 17 bytes, not 18.
233906943f9SDavid du Colombier 	 * We accept them and leave number of configurations as zero.
234906943f9SDavid du Colombier 	 * (a get configuration descriptor also fails for them!)
235906943f9SDavid du Colombier 	 */
236906943f9SDavid du Colombier 	if(nr < Ddevlen){
237906943f9SDavid du Colombier 		print("%s: %s: warning: device with short descriptor\n",
238906943f9SDavid du Colombier 			argv0, d->dir);
239906943f9SDavid du Colombier 		if(nr < Ddevlen-1){
240906943f9SDavid du Colombier 			werrstr("short device descriptor (%d bytes)", nr);
241906943f9SDavid du Colombier 			return -1;
242906943f9SDavid du Colombier 		}
243906943f9SDavid du Colombier 	}
244906943f9SDavid du Colombier 	d->usb = emallocz(sizeof(Usbdev), 1);
245906943f9SDavid du Colombier 	ep0 = mkep(d->usb, 0);
246906943f9SDavid du Colombier 	ep0->dir = Eboth;
247906943f9SDavid du Colombier 	ep0->type = Econtrol;
248906943f9SDavid du Colombier 	ep0->maxpkt = d->maxpkt = 8;		/* a default */
249906943f9SDavid du Colombier 	nr = parsedev(d, buf, nr);
250906943f9SDavid du Colombier 	if(nr >= 0){
251906943f9SDavid du Colombier 		d->usb->vendor = loaddevstr(d, d->usb->vsid);
252906943f9SDavid du Colombier 		if(strcmp(d->usb->vendor, "none") != 0){
253906943f9SDavid du Colombier 			d->usb->product = loaddevstr(d, d->usb->psid);
254906943f9SDavid du Colombier 			d->usb->serial = loaddevstr(d, d->usb->ssid);
255906943f9SDavid du Colombier 		}
256906943f9SDavid du Colombier 	}
257906943f9SDavid du Colombier 	return nr;
258906943f9SDavid du Colombier }
259906943f9SDavid du Colombier 
260906943f9SDavid du Colombier int
configdev(Dev * d)261906943f9SDavid du Colombier configdev(Dev *d)
262906943f9SDavid du Colombier {
263906943f9SDavid du Colombier 	int i;
264906943f9SDavid du Colombier 
265906943f9SDavid du Colombier 	if(d->dfd < 0)
266906943f9SDavid du Colombier 		opendevdata(d, ORDWR);
267906943f9SDavid du Colombier 	if(loaddevdesc(d) < 0)
268906943f9SDavid du Colombier 		return -1;
269906943f9SDavid du Colombier 	for(i = 0; i < d->usb->nconf; i++)
270906943f9SDavid du Colombier 		if(loaddevconf(d, i) < 0)
271906943f9SDavid du Colombier 			return -1;
272906943f9SDavid du Colombier 	return 0;
273906943f9SDavid du Colombier }
274906943f9SDavid du Colombier 
275906943f9SDavid du Colombier static void
closeconf(Conf * c)276906943f9SDavid du Colombier closeconf(Conf *c)
277906943f9SDavid du Colombier {
278906943f9SDavid du Colombier 	int i;
279906943f9SDavid du Colombier 	int a;
280906943f9SDavid du Colombier 
281906943f9SDavid du Colombier 	if(c == nil)
282906943f9SDavid du Colombier 		return;
283906943f9SDavid du Colombier 	for(i = 0; i < nelem(c->iface); i++)
284906943f9SDavid du Colombier 		if(c->iface[i] != nil){
285906943f9SDavid du Colombier 			for(a = 0; a < nelem(c->iface[i]->altc); a++)
286906943f9SDavid du Colombier 				free(c->iface[i]->altc[a]);
287906943f9SDavid du Colombier 			free(c->iface[i]);
288906943f9SDavid du Colombier 		}
289906943f9SDavid du Colombier 	free(c);
290906943f9SDavid du Colombier }
291906943f9SDavid du Colombier 
292906943f9SDavid du Colombier void
closedev(Dev * d)293906943f9SDavid du Colombier closedev(Dev *d)
294906943f9SDavid du Colombier {
295906943f9SDavid du Colombier 	int i;
296906943f9SDavid du Colombier 	Usbdev *ud;
297906943f9SDavid du Colombier 
298906943f9SDavid du Colombier 	if(d==nil || decref(d) != 0)
299906943f9SDavid du Colombier 		return;
300906943f9SDavid du Colombier 	dprint(2, "%s: closedev %#p %s\n", argv0, d, d->dir);
301906943f9SDavid du Colombier 	if(d->free != nil)
302906943f9SDavid du Colombier 		d->free(d->aux);
303906943f9SDavid du Colombier 	if(d->cfd >= 0)
304906943f9SDavid du Colombier 		close(d->cfd);
305906943f9SDavid du Colombier 	if(d->dfd >= 0)
306906943f9SDavid du Colombier 		close(d->dfd);
307906943f9SDavid du Colombier 	d->cfd = d->dfd = -1;
308906943f9SDavid du Colombier 	free(d->dir);
309906943f9SDavid du Colombier 	d->dir = nil;
310906943f9SDavid du Colombier 	ud = d->usb;
311906943f9SDavid du Colombier 	d->usb = nil;
312906943f9SDavid du Colombier 	if(ud != nil){
313906943f9SDavid du Colombier 		free(ud->vendor);
314906943f9SDavid du Colombier 		free(ud->product);
315906943f9SDavid du Colombier 		free(ud->serial);
316906943f9SDavid du Colombier 		for(i = 0; i < nelem(ud->ep); i++)
317906943f9SDavid du Colombier 			free(ud->ep[i]);
318906943f9SDavid du Colombier 		for(i = 0; i < nelem(ud->ddesc); i++)
319906943f9SDavid du Colombier 			free(ud->ddesc[i]);
320906943f9SDavid du Colombier 
321906943f9SDavid du Colombier 		for(i = 0; i < nelem(ud->conf); i++)
322906943f9SDavid du Colombier 			closeconf(ud->conf[i]);
323906943f9SDavid du Colombier 		free(ud);
324906943f9SDavid du Colombier 	}
325906943f9SDavid du Colombier 	free(d);
326906943f9SDavid du Colombier }
327906943f9SDavid du Colombier 
328906943f9SDavid du Colombier static char*
reqstr(int type,int req)329906943f9SDavid du Colombier reqstr(int type, int req)
330906943f9SDavid du Colombier {
331906943f9SDavid du Colombier 	char *s;
332906943f9SDavid du Colombier 	static char* ds[] = { "dev", "if", "ep", "oth" };
333906943f9SDavid du Colombier 	static char buf[40];
334906943f9SDavid du Colombier 
335906943f9SDavid du Colombier 	if(type&Rd2h)
336906943f9SDavid du Colombier 		s = seprint(buf, buf+sizeof(buf), "d2h");
337906943f9SDavid du Colombier 	else
338906943f9SDavid du Colombier 		s = seprint(buf, buf+sizeof(buf), "h2d");
339906943f9SDavid du Colombier 	if(type&Rclass)
340906943f9SDavid du Colombier 		s = seprint(s, buf+sizeof(buf), "|cls");
341906943f9SDavid du Colombier 	else if(type&Rvendor)
342906943f9SDavid du Colombier 		s = seprint(s, buf+sizeof(buf), "|vnd");
343906943f9SDavid du Colombier 	else
344906943f9SDavid du Colombier 		s = seprint(s, buf+sizeof(buf), "|std");
345906943f9SDavid du Colombier 	s = seprint(s, buf+sizeof(buf), "|%s", ds[type&3]);
346906943f9SDavid du Colombier 
347906943f9SDavid du Colombier 	switch(req){
348906943f9SDavid du Colombier 	case Rgetstatus: s = seprint(s, buf+sizeof(buf), " getsts"); break;
349906943f9SDavid du Colombier 	case Rclearfeature: s = seprint(s, buf+sizeof(buf), " clrfeat"); break;
350906943f9SDavid du Colombier 	case Rsetfeature: s = seprint(s, buf+sizeof(buf), " setfeat"); break;
351906943f9SDavid du Colombier 	case Rsetaddress: s = seprint(s, buf+sizeof(buf), " setaddr"); break;
352906943f9SDavid du Colombier 	case Rgetdesc: s = seprint(s, buf+sizeof(buf), " getdesc"); break;
353906943f9SDavid du Colombier 	case Rsetdesc: s = seprint(s, buf+sizeof(buf), " setdesc"); break;
354906943f9SDavid du Colombier 	case Rgetconf: s = seprint(s, buf+sizeof(buf), " getcnf"); break;
355906943f9SDavid du Colombier 	case Rsetconf: s = seprint(s, buf+sizeof(buf), " setcnf"); break;
356906943f9SDavid du Colombier 	case Rgetiface: s = seprint(s, buf+sizeof(buf), " getif"); break;
357906943f9SDavid du Colombier 	case Rsetiface: s = seprint(s, buf+sizeof(buf), " setif"); break;
358906943f9SDavid du Colombier 	}
359906943f9SDavid du Colombier 	USED(s);
360906943f9SDavid du Colombier 	return buf;
361906943f9SDavid du Colombier }
362906943f9SDavid du Colombier 
363906943f9SDavid du Colombier static int
cmdreq(Dev * d,int type,int req,int value,int index,uchar * data,int count)364906943f9SDavid du Colombier cmdreq(Dev *d, int type, int req, int value, int index, uchar *data, int count)
365906943f9SDavid du Colombier {
366906943f9SDavid du Colombier 	int ndata, n;
367906943f9SDavid du Colombier 	uchar *wp;
368906943f9SDavid du Colombier 	uchar buf[8];
369906943f9SDavid du Colombier 	char *hd, *rs;
370906943f9SDavid du Colombier 
371906943f9SDavid du Colombier 	assert(d != nil);
372906943f9SDavid du Colombier 	if(data == nil){
373906943f9SDavid du Colombier 		wp = buf;
374906943f9SDavid du Colombier 		ndata = 0;
375906943f9SDavid du Colombier 	}else{
376906943f9SDavid du Colombier 		ndata = count;
377906943f9SDavid du Colombier 		wp = emallocz(8+ndata, 0);
378906943f9SDavid du Colombier 	}
379906943f9SDavid du Colombier 	wp[0] = type;
380906943f9SDavid du Colombier 	wp[1] = req;
381906943f9SDavid du Colombier 	PUT2(wp+2, value);
382906943f9SDavid du Colombier 	PUT2(wp+4, index);
383906943f9SDavid du Colombier 	PUT2(wp+6, count);
384906943f9SDavid du Colombier 	if(data != nil)
385906943f9SDavid du Colombier 		memmove(wp+8, data, ndata);
386906943f9SDavid du Colombier 	if(usbdebug>2){
387906943f9SDavid du Colombier 		hd = hexstr(wp, ndata+8);
388906943f9SDavid du Colombier 		rs = reqstr(type, req);
389906943f9SDavid du Colombier 		fprint(2, "%s: %s val %d|%d idx %d cnt %d out[%d] %s\n",
390906943f9SDavid du Colombier 			d->dir, rs, value>>8, value&0xFF,
391906943f9SDavid du Colombier 			index, count, ndata+8, hd);
392906943f9SDavid du Colombier 		free(hd);
393906943f9SDavid du Colombier 	}
394906943f9SDavid du Colombier 	n = write(d->dfd, wp, 8+ndata);
395906943f9SDavid du Colombier 	if(wp != buf)
396906943f9SDavid du Colombier 		free(wp);
397906943f9SDavid du Colombier 	if(n < 0)
398906943f9SDavid du Colombier 		return -1;
399906943f9SDavid du Colombier 	if(n != 8+ndata){
400906943f9SDavid du Colombier 		dprint(2, "%s: cmd: short write: %d\n", argv0, n);
401906943f9SDavid du Colombier 		return -1;
402906943f9SDavid du Colombier 	}
403906943f9SDavid du Colombier 	return n;
404906943f9SDavid du Colombier }
405906943f9SDavid du Colombier 
406906943f9SDavid du Colombier static int
cmdrep(Dev * d,void * buf,int nb)407906943f9SDavid du Colombier cmdrep(Dev *d, void *buf, int nb)
408906943f9SDavid du Colombier {
409906943f9SDavid du Colombier 	char *hd;
410906943f9SDavid du Colombier 
411906943f9SDavid du Colombier 	nb = read(d->dfd, buf, nb);
412906943f9SDavid du Colombier 	if(nb >0 && usbdebug > 2){
413906943f9SDavid du Colombier 		hd = hexstr(buf, nb);
414906943f9SDavid du Colombier 		fprint(2, "%s: in[%d] %s\n", d->dir, nb, hd);
415906943f9SDavid du Colombier 		free(hd);
416906943f9SDavid du Colombier 	}
417906943f9SDavid du Colombier 	return nb;
418906943f9SDavid du Colombier }
419906943f9SDavid du Colombier 
420906943f9SDavid du Colombier int
usbcmd(Dev * d,int type,int req,int value,int index,uchar * data,int count)421906943f9SDavid du Colombier usbcmd(Dev *d, int type, int req, int value, int index, uchar *data, int count)
422906943f9SDavid du Colombier {
423*bfb6eab9SDavid du Colombier 	int i, r, nerr;
424906943f9SDavid du Colombier 	char err[64];
425906943f9SDavid du Colombier 
426906943f9SDavid du Colombier 	/*
427906943f9SDavid du Colombier 	 * Some devices do not respond to commands some times.
428906943f9SDavid du Colombier 	 * Others even report errors but later work just fine. Retry.
429906943f9SDavid du Colombier 	 */
430906943f9SDavid du Colombier 	r = -1;
431906943f9SDavid du Colombier 	*err = 0;
432906943f9SDavid du Colombier 	for(i = nerr = 0; i < Uctries; i++){
433906943f9SDavid du Colombier 		if(type & Rd2h)
434906943f9SDavid du Colombier 			r = cmdreq(d, type, req, value, index, nil, count);
435906943f9SDavid du Colombier 		else
436906943f9SDavid du Colombier 			r = cmdreq(d, type, req, value, index, data, count);
437906943f9SDavid du Colombier 		if(r > 0){
438906943f9SDavid du Colombier 			if((type & Rd2h) == 0)
439906943f9SDavid du Colombier 				break;
440906943f9SDavid du Colombier 			r = cmdrep(d, data, count);
441906943f9SDavid du Colombier 			if(r > 0)
442906943f9SDavid du Colombier 				break;
443906943f9SDavid du Colombier 			if(r == 0)
444906943f9SDavid du Colombier 				werrstr("no data from device");
445906943f9SDavid du Colombier 		}
446906943f9SDavid du Colombier 		nerr++;
447906943f9SDavid du Colombier 		if(*err == 0)
448906943f9SDavid du Colombier 			rerrstr(err, sizeof(err));
449906943f9SDavid du Colombier 		sleep(Ucdelay);
450906943f9SDavid du Colombier 	}
451*bfb6eab9SDavid du Colombier 	if(r > 0 && i >= 2)
452906943f9SDavid du Colombier 		/* let the user know the device is not in good shape */
453906943f9SDavid du Colombier 		fprint(2, "%s: usbcmd: %s: required %d attempts (%s)\n",
454906943f9SDavid du Colombier 			argv0, d->dir, i, err);
455906943f9SDavid du Colombier 	return r;
456906943f9SDavid du Colombier }
457906943f9SDavid du Colombier 
458906943f9SDavid du Colombier int
unstall(Dev * dev,Dev * ep,int dir)459906943f9SDavid du Colombier unstall(Dev *dev, Dev *ep, int dir)
460906943f9SDavid du Colombier {
461906943f9SDavid du Colombier 	int r;
462906943f9SDavid du Colombier 
463906943f9SDavid du Colombier 	if(dir == Ein)
464906943f9SDavid du Colombier 		dir = 0x80;
465906943f9SDavid du Colombier 	else
466906943f9SDavid du Colombier 		dir = 0;
467906943f9SDavid du Colombier 	r = Rh2d|Rstd|Rep;
468906943f9SDavid du Colombier 	if(usbcmd(dev, r, Rclearfeature, Fhalt, ep->id|dir, nil, 0)<0){
469906943f9SDavid du Colombier 		werrstr("unstall: %s: %r", ep->dir);
470906943f9SDavid du Colombier 		return -1;
471906943f9SDavid du Colombier 	}
472906943f9SDavid du Colombier 	if(devctl(ep, "clrhalt") < 0){
473906943f9SDavid du Colombier 		werrstr("clrhalt: %s: %r", ep->dir);
474906943f9SDavid du Colombier 		return -1;
475906943f9SDavid du Colombier 	}
476906943f9SDavid du Colombier 	return 0;
477906943f9SDavid du Colombier }
478906943f9SDavid du Colombier 
479906943f9SDavid du Colombier /*
480906943f9SDavid du Colombier  * To be sure it uses a single write.
481906943f9SDavid du Colombier  */
482906943f9SDavid du Colombier int
devctl(Dev * dev,char * fmt,...)483906943f9SDavid du Colombier devctl(Dev *dev, char *fmt, ...)
484906943f9SDavid du Colombier {
485906943f9SDavid du Colombier 	char buf[128];
486906943f9SDavid du Colombier 	va_list arg;
487906943f9SDavid du Colombier 	char *e;
488906943f9SDavid du Colombier 
489906943f9SDavid du Colombier 	va_start(arg, fmt);
490906943f9SDavid du Colombier 	e = vseprint(buf, buf+sizeof(buf), fmt, arg);
491906943f9SDavid du Colombier 	va_end(arg);
492906943f9SDavid du Colombier 	return write(dev->cfd, buf, e-buf);
493906943f9SDavid du Colombier }
494