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