xref: /plan9-contrib/sys/src/cmd/usb/lib/dev.c (revision d4df0e25729142c8872e87f57ac688cf887aee08)
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include "usb.h"
5 
6 /*
7  * epN.M -> N
8  */
9 static int
nameid(char * s)10 nameid(char *s)
11 {
12 	char *r;
13 	char nm[20];
14 
15 	r = strrchr(s, 'p');
16 	if(r == nil)
17 		return -1;
18 	strecpy(nm, nm+sizeof(nm), r+1);
19 	r = strchr(nm, '.');
20 	if(r == nil)
21 		return -1;
22 	*r = 0;
23 	return atoi(nm);
24 }
25 
26 Dev*
openep(Dev * d,int id)27 openep(Dev *d, int id)
28 {
29 	char *mode;	/* How many modes? */
30 	Ep *ep;
31 	Altc *ac;
32 	Dev *epd;
33 	Usbdev *ud;
34 	char name[40];
35 
36 	if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0)
37 		return nil;
38 	if(d->cfd < 0 || d->usb == nil){
39 		werrstr("device not configured");
40 		return nil;
41 	}
42 	ud = d->usb;
43 	if(id < 0 || id >= nelem(ud->ep) || ud->ep[id] == nil){
44 		werrstr("bad enpoint number");
45 		return nil;
46 	}
47 	ep = ud->ep[id];
48 	mode = "rw";
49 	if(ep->dir == Ein)
50 		mode = "r";
51 	if(ep->dir == Eout)
52 		mode = "w";
53 	snprint(name, sizeof(name), "/dev/usb/ep%d.%d", d->id, id);
54 	if(access(name, AEXIST) == 0){
55 		dprint(2, "%s: %s already exists; trying to open\n", argv0, name);
56 		epd = opendev(name);
57 		if(epd != nil)
58 			epd->maxpkt = ep->maxpkt;	/* guess */
59 		return epd;
60 	}
61 	if(devctl(d, "new %d %d %s", id, ep->type, mode) < 0){
62 		dprint(2, "%s: %s: new: %r\n", argv0, d->dir);
63 		return nil;
64 	}
65 	epd = opendev(name);
66 	if(epd == nil)
67 		return nil;
68 	epd->id = id;
69 	if(devctl(epd, "maxpkt %d", ep->maxpkt) < 0)
70 		fprint(2, "%s: %s: openep: maxpkt: %r\n", argv0, epd->dir);
71 	else
72 		dprint(2, "%s: %s: maxpkt %d\n", argv0, epd->dir, ep->maxpkt);
73 	epd->maxpkt = ep->maxpkt;
74 	ac = ep->iface->altc[0];
75 	if(ep->ntds > 1 && devctl(epd, "ntds %d", ep->ntds) < 0)
76 		fprint(2, "%s: %s: openep: ntds: %r\n", argv0, epd->dir);
77 	else
78 		dprint(2, "%s: %s: ntds %d\n", argv0, epd->dir, ep->ntds);
79 
80 	/*
81 	 * For iso endpoints and high speed interrupt endpoints the pollival is
82 	 * actually 2ⁿ and not n.
83 	 * The kernel usb driver must take that into account.
84 	 * It's simpler this way.
85 	 */
86 
87 	if(ac != nil && (ep->type == Eintr || ep->type == Eiso) && ac->interval != 0)
88 		if(devctl(epd, "pollival %d", ac->interval) < 0)
89 			fprint(2, "%s: %s: openep: pollival: %r\n", argv0, epd->dir);
90 	return epd;
91 }
92 
93 Dev*
opendev(char * fn)94 opendev(char *fn)
95 {
96 	Dev *d;
97 	int l;
98 
99 	if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0)
100 		return nil;
101 	d = emallocz(sizeof(Dev), 1);
102 	incref(d);
103 
104 	l = strlen(fn);
105 	d->dfd = -1;
106 	/*
107 	 * +30 to allocate extra size to concat "/<epfilename>"
108 	 * we should probably remove that feature from the manual
109 	 * and from the code after checking out that nobody relies on
110 	 * that.
111 	 */
112 	d->dir = emallocz(l + 30, 0);
113 	strcpy(d->dir, fn);
114 	strcpy(d->dir+l, "/ctl");
115 	d->cfd = open(d->dir, ORDWR|OCEXEC);
116 	d->dir[l] = 0;
117 	d->id = nameid(fn);
118 	if(d->cfd < 0){
119 		werrstr("can't open endpoint %s: %r", d->dir);
120 		free(d->dir);
121 		free(d);
122 		return nil;
123 	}
124 	dprint(2, "%s: opendev %#p %s\n", argv0, d, fn);
125 	return d;
126 }
127 
128 int
opendevdata(Dev * d,int mode)129 opendevdata(Dev *d, int mode)
130 {
131 	char buf[80]; /* more than enough for a usb path */
132 
133 	seprint(buf, buf+sizeof(buf), "%s/data", d->dir);
134 	d->dfd = open(buf, mode|OCEXEC);
135 	return d->dfd;
136 }
137 
138 enum
139 {
140 	/*
141 	 * Max device conf is also limited by max control request size as
142 	 * limited by Maxctllen in the kernel usb.h (both limits are arbitrary).
143 	 */
144 	Maxdevconf = 4 * 1024,	/* asking for 16K kills Newsham's disk */
145 };
146 
147 int
loaddevconf(Dev * d,int n)148 loaddevconf(Dev *d, int n)
149 {
150 	uchar *buf;
151 	int l, nr;
152 	int type;
153 
154 	if(n >= nelem(d->usb->conf)){
155 		werrstr("loaddevconf: bug: out of configurations in device");
156 		fprint(2, "%s: %r\n", argv0);
157 		return -1;
158 	}
159 	buf = emallocz(Maxdevconf, 0);
160 	type = Rd2h|Rstd|Rdev;
161 	nr = usbcmd(d, type, Rgetdesc, Dconf<<8|n, 0, buf, Dconflen);
162 	if(nr < Dconflen){
163 		free(buf);
164 		return -1;
165 	}
166 	l = GET2(((DConf*)buf)->wTotalLength);
167 	if(l > Maxdevconf){
168 		free(buf);
169 		return -1;
170 	}
171 	nr = usbcmd(d, type, Rgetdesc, Dconf<<8|n, 0, buf, l);
172 	if(nr < l){
173 		free(buf);
174 		return -1;
175 	}
176 
177 	if(d->usb->conf[n] == nil)
178 		d->usb->conf[n] = emallocz(sizeof(Conf), 1);
179 	nr = parseconf(d->usb, d->usb->conf[n], buf, nr);
180 	free(buf);
181 	return nr;
182 }
183 
184 Ep*
mkep(Usbdev * d,int id)185 mkep(Usbdev *d, int id)
186 {
187 	Ep *ep;
188 
189 	d->ep[id] = ep = emallocz(sizeof(Ep), 1);
190 	ep->id = id;
191 	return ep;
192 }
193 
194 static char*
mkstr(uchar * b,int n)195 mkstr(uchar *b, int n)
196 {
197 	Rune r;
198 	char *us;
199 	char *s;
200 	char *e;
201 
202 	if(n > b[0])
203 		n = b[0];
204 	if(n <= 2 || (n & 1) != 0)
205 		return strdup("none");
206 	n = (n - 2)/2;
207 	b += 2;
208 	us = s = emallocz(n*UTFmax+1, 0);
209 	e = s + n*UTFmax+1;
210 	for(; --n >= 0; b += 2){
211 		r = GET2(b);
212 		s = seprint(s, e, "%C", r);
213 	}
214 	return us;
215 }
216 
217 char*
loaddevstr(Dev * d,int sid)218 loaddevstr(Dev *d, int sid)
219 {
220 	uchar buf[256];
221 	int type, langid, nr;
222 
223 	if(sid == 0)
224 		return estrdup("none");
225 	type = Rd2h|Rstd|Rdev;
226 	nr = usbcmd(d, type, Rgetdesc, Dstr<<8|sid, 0, buf, sizeof(buf));
227 	if(nr < 4)
228 		langid = 0x0409;		/* english */
229 	else
230 		langid = buf[3]<<8 | buf[2];
231 	nr = usbcmd(d, type, Rgetdesc, Dstr<<8|sid, langid, buf, sizeof(buf));
232 
233 	return mkstr(buf, nr);
234 }
235 
236 int
loaddevdesc(Dev * d)237 loaddevdesc(Dev *d)
238 {
239 	uchar buf[Ddevlen];
240 	int nr;
241 	int type;
242 	Ep *ep0;
243 
244 	type = Rd2h|Rstd|Rdev;
245 	memset(buf, 0, Ddevlen);
246 	if((nr = usbcmd(d, type, Rgetdesc, Ddev<<8|0, 0, buf, Ddevlen)) < 0)
247 		return -1;
248 	if(nr < Ddevlen){
249 		werrstr("short device descriptor (%d bytes)", nr);
250 		return -1;
251 	}
252 	d->usb = emallocz(sizeof(Usbdev), 1);
253 	ep0 = mkep(d->usb, 0);
254 	ep0->dir = Eboth;
255 	ep0->type = Econtrol;
256 	ep0->maxpkt = d->maxpkt = 8;		/* a default */
257 	nr = parsedev(d, buf, nr);
258 	if(nr >= 0){
259 		d->usb->vendor = loaddevstr(d, d->usb->vsid);
260 		if(strcmp(d->usb->vendor, "none") != 0){
261 			d->usb->product = loaddevstr(d, d->usb->psid);
262 			d->usb->serial = loaddevstr(d, d->usb->ssid);
263 		}
264 	}
265 	else
266 		print("usbd: desc error: %r");
267 	return nr;
268 }
269 
270 int
configdev(Dev * d)271 configdev(Dev *d)
272 {
273 	int i;
274 
275 	if(d->dfd < 0)
276 		opendevdata(d, ORDWR);
277 	if(loaddevdesc(d) < 0)
278 		return -1;
279 	for(i = 0; i < d->usb->nconf; i++)
280 		if(loaddevconf(d, i) < 0)
281 			return -1;
282 	return 0;
283 }
284 
285 static void
closeconf(Conf * c)286 closeconf(Conf *c)
287 {
288 	int i;
289 	int a;
290 
291 	if(c == nil)
292 		return;
293 	for(i = 0; i < nelem(c->iface); i++)
294 		if(c->iface[i] != nil){
295 			for(a = 0; a < nelem(c->iface[i]->altc); a++)
296 				free(c->iface[i]->altc[a]);
297 			free(c->iface[i]);
298 		}
299 	free(c);
300 }
301 
302 void
closedev(Dev * d)303 closedev(Dev *d)
304 {
305 	int i;
306 	Usbdev *ud;
307 
308 	if(d==nil || decref(d) != 0)
309 		return;
310 	dprint(2, "%s: closedev %#p %s\n", argv0, d, d->dir);
311 	if(d->free != nil)
312 		d->free(d->aux);
313 	if(d->cfd >= 0)
314 		close(d->cfd);
315 	if(d->dfd >= 0)
316 		close(d->dfd);
317 	d->cfd = d->dfd = -1;
318 	free(d->dir);
319 	d->dir = nil;
320 	ud = d->usb;
321 	d->usb = nil;
322 	if(ud != nil){
323 		free(ud->vendor);
324 		free(ud->product);
325 		free(ud->serial);
326 		for(i = 0; i < nelem(ud->ep); i++)
327 			free(ud->ep[i]);
328 		for(i = 0; i < nelem(ud->ddesc); i++)
329 			free(ud->ddesc[i]);
330 
331 		for(i = 0; i < nelem(ud->conf); i++)
332 			closeconf(ud->conf[i]);
333 		free(ud);
334 	}
335 	free(d);
336 }
337 
338 static char*
reqstr(int type,int req)339 reqstr(int type, int req)
340 {
341 	char *s;
342 	static char* ds[] = { "dev", "if", "ep", "oth" };
343 	static char buf[40];
344 
345 	if(type&Rd2h)
346 		s = seprint(buf, buf+sizeof(buf), "d2h");
347 	else
348 		s = seprint(buf, buf+sizeof(buf), "h2d");
349 	if(type&Rclass)
350 		s = seprint(s, buf+sizeof(buf), "|cls");
351 	else if(type&Rvendor)
352 		s = seprint(s, buf+sizeof(buf), "|vnd");
353 	else
354 		s = seprint(s, buf+sizeof(buf), "|std");
355 	s = seprint(s, buf+sizeof(buf), "|%s", ds[type&3]);
356 
357 	switch(req){
358 	case Rgetstatus: s = seprint(s, buf+sizeof(buf), " getsts"); break;
359 	case Rclearfeature: s = seprint(s, buf+sizeof(buf), " clrfeat"); break;
360 	case Rsetfeature: s = seprint(s, buf+sizeof(buf), " setfeat"); break;
361 	case Rsetaddress: s = seprint(s, buf+sizeof(buf), " setaddr"); break;
362 	case Rgetdesc: s = seprint(s, buf+sizeof(buf), " getdesc"); break;
363 	case Rsetdesc: s = seprint(s, buf+sizeof(buf), " setdesc"); break;
364 	case Rgetconf: s = seprint(s, buf+sizeof(buf), " getcnf"); break;
365 	case Rsetconf: s = seprint(s, buf+sizeof(buf), " setcnf"); break;
366 	case Rgetiface: s = seprint(s, buf+sizeof(buf), " getif"); break;
367 	case Rsetiface: s = seprint(s, buf+sizeof(buf), " setif"); break;
368 	}
369 	USED(s);
370 	return buf;
371 }
372 
373 static int
cmdreq(Dev * d,int type,int req,int value,int index,uchar * data,int count)374 cmdreq(Dev *d, int type, int req, int value, int index, uchar *data, int count)
375 {
376 	int ndata, n;
377 	uchar *wp;
378 	uchar buf[8];
379 	char *hd, *rs;
380 
381 	assert(d != nil);
382 	if(data == nil){
383 		wp = buf;
384 		ndata = 0;
385 	}else{
386 		ndata = count;
387 		wp = emallocz(8+ndata, 0);
388 	}
389 	wp[0] = type;
390 	wp[1] = req;
391 	PUT2(wp+2, value);
392 	PUT2(wp+4, index);
393 	PUT2(wp+6, count);
394 	if(data != nil)
395 		memmove(wp+8, data, ndata);
396 	if(usbdebug>2){
397 		hd = hexstr(wp, ndata+8);
398 		rs = reqstr(type, req);
399 		fprint(2, "%s: %s val %d|%d idx %d cnt %d out[%d] %s\n",
400 			d->dir, rs, value>>8, value&0xFF,
401 			index, count, ndata+8, hd);
402 		free(hd);
403 	}
404 	n = write(d->dfd, wp, 8+ndata);
405 	if(wp != buf)
406 		free(wp);
407 	if(n < 0)
408 		return -1;
409 	if(n != 8+ndata){
410 		dprint(2, "%s: cmd: short write: %d\n", argv0, n);
411 		return -1;
412 	}
413 	return n;
414 }
415 
416 static int
cmdrep(Dev * d,void * buf,int nb)417 cmdrep(Dev *d, void *buf, int nb)
418 {
419 	char *hd;
420 
421 	nb = read(d->dfd, buf, nb);
422 	if(nb >0 && usbdebug > 2){
423 		hd = hexstr(buf, nb);
424 		fprint(2, "%s: in[%d] %s\n", d->dir, nb, hd);
425 		free(hd);
426 	}
427 	return nb;
428 }
429 
430 int
usbcmd(Dev * d,int type,int req,int value,int index,uchar * data,int count)431 usbcmd(Dev *d, int type, int req, int value, int index, uchar *data, int count)
432 {
433 	int i, r, nerr;
434 	char err[64];
435 
436 	/*
437 	 * Some devices do not respond to commands some times.
438 	 * Others even report errors but later work just fine. Retry.
439 	 */
440 	r = -1;
441 	*err = 0;
442 	for(i = nerr = 0; i < Uctries; i++){
443 		if(type & Rd2h)
444 			r = cmdreq(d, type, req, value, index, nil, count);
445 		else
446 			r = cmdreq(d, type, req, value, index, data, count);
447 		if(r > 0){
448 			if((type & Rd2h) == 0)
449 				break;
450 			r = cmdrep(d, data, count);
451 			if(r > 0)
452 				break;
453 			if(r == 0)
454 				werrstr("no data from device");
455 		}
456 		nerr++;
457 		if(*err == 0)
458 			rerrstr(err, sizeof(err));
459 		sleep(Ucdelay);
460 	}
461 	if(r > 0 && i >= 2)
462 		/* let the user know the device is not in good shape */
463 		fprint(2, "%s: usbcmd: %s: required %d attempts (%s)\n",
464 			argv0, d->dir, i, err);
465 	return r;
466 }
467 
468 int
unstall(Dev * dev,Dev * ep,int dir)469 unstall(Dev *dev, Dev *ep, int dir)
470 {
471 	int r;
472 
473 	if(dir == Ein)
474 		dir = 0x80;
475 	else
476 		dir = 0;
477 	r = Rh2d|Rstd|Rep;
478 	if(usbcmd(dev, r, Rclearfeature, Fhalt, ep->id|dir, nil, 0)<0){
479 		werrstr("unstall: %s: %r", ep->dir);
480 		return -1;
481 	}
482 	if(devctl(ep, "clrhalt") < 0){
483 		werrstr("clrhalt: %s: %r", ep->dir);
484 		return -1;
485 	}
486 	return 0;
487 }
488 
489 /*
490  * To be sure it uses a single write.
491  */
492 int
devctl(Dev * dev,char * fmt,...)493 devctl(Dev *dev, char *fmt, ...)
494 {
495 	char buf[128];
496 	va_list arg;
497 	char *e;
498 
499 	va_start(arg, fmt);
500 	e = vseprint(buf, buf+sizeof(buf), fmt, arg);
501 	va_end(arg);
502 	return write(dev->cfd, buf, e-buf);
503 }
504