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