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