xref: /plan9/sys/src/cmd/usb/lib/dump.c (revision 96cbc34f1b36a29efdcfd47b10e70703a690febc)
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <bio.h>
5 #include "usb.h"
6 
7 int verbose;
8 
9 typedef struct Flags Flags;
10 typedef struct Classes Classes;
11 
12 struct Flags {
13 	int	bit;
14 	char*	name0;
15 	char*	name1;
16 };
17 
18 struct Classes {
19 	char*	name;
20 	struct {
21 		char*	name;
22 		char*	proto[4];
23 	} subclass[4];
24 };
25 
26 static Classes	classname[] = {
27 	[CL_AUDIO]	{"audio",	{[1]{"control"}, [2]{"stream"}, [3]{"midi"}}},
28 	[CL_COMMS]	{"comms",	{[1] {"abstract", {[1]"AT"}}}},
29 	[CL_HID]	{"hid",		{[1] {"boot", {[1]"kbd", [2]"mouse"}}}},
30 	[CL_PRINTER]	{"printer",	{[1]"printer", {[1]"uni", [2]"bi"}}},
31 	[CL_HUB]	{"hub",		{[1]{"hub"}}},
32 	[CL_DATA]	{"data"},
33 };
34 
35 static	void	pflag(Flags*, uint);
36 
37 char *
38 sclass(char *p, char *e, ulong csp)
39 {
40 	Classes *cs;
41 	int c, s, pr;
42 
43 	c = Class(csp);
44 	s = Subclass(csp);
45 	pr = Proto(csp);
46 	if(c < 0 || c >= nelem(classname) || (cs = &classname[c])->name == nil)
47 		return seprint(p, e, "%d.%d.%d", c, s, pr);
48 	p = seprint(p, e, "%s.", cs->name);
49 	if(s < 0 || s >= nelem(cs->subclass) || cs->subclass[s].name == nil)
50 		p = seprint(p, e, "%d.%d", s, pr);
51 	else{
52 		p = seprint(p, e, "%s.", cs->subclass[s].name);
53 		if(pr < 0 || pr >= nelem(cs->subclass[s].proto) || cs->subclass[s].proto[pr] == nil)
54 			p = seprint(p, e, "%d", pr);
55 		else
56 			p = seprint(p, e, "%s", cs->subclass[s].proto[pr]);
57 	}
58 	return p;
59 }
60 
61 void
62 pdevice(Device *, int, ulong, void *b, int n)
63 {
64 	DDevice *d;
65 	char scbuf[64];
66 
67 	if(n < DDEVLEN)
68 		return;
69 	d = b;
70 	if(debug & Dbginfo) {
71 		fprint(2, "usb (bcd)%c%c%c%c",
72 				'0'+((d->bcdUSB[1]>>4)&0xf), '0'+(d->bcdUSB[1]&0xf),
73 				'0'+((d->bcdUSB[0]>>4)&0xf), '0'+(d->bcdUSB[0]&0xf));
74 		sclass(scbuf, scbuf + sizeof scbuf,
75 			CSP(d->bDeviceClass, d->bDeviceSubClass, d->bDeviceProtocol)),
76 		fprint(2, " class %d subclass %d proto %d [%s] max0 %d",
77 			d->bDeviceClass, d->bDeviceSubClass, d->bDeviceProtocol,
78 			scbuf,
79 			d->bMaxPacketSize0);
80 		fprint(2, " vendor %#x product %#x device (bcd)%c%c%c%c",
81 			GET2(d->idVendor), GET2(d->idProduct),
82 			'0'+((d->bcdDevice[1]>>4)&0xf), '0'+(d->bcdDevice[1]&0xf),
83 			'0'+((d->bcdDevice[0]>>4)&0xf), '0'+(d->bcdDevice[0]&0xf));
84 		fprint(2, " man %d prod %d serial %d nconfig %d",
85 			d->iManufacturer, d->iProduct, d->iSerialNumber,
86 			d->bNumConfigurations);
87 	}
88 }
89 
90 void
91 phid(Device *, int, ulong, void *b, int n)
92 {
93 	DHid *d;
94 
95 	if(n < DHIDLEN){
96 		fprint(2, "%s: hid too short\n", argv0);
97 		return;
98 	}
99 	d = b;
100 	if(debug & Dbginfo)
101 		fprint(2, "HID (bcd)%c%c%c%c country %d nhidclass %d classdtype %#x dlen %d\n",
102 			'0'+((d->bcdHID[1]>>4)&0xf), '0'+(d->bcdHID[1]&0xf),
103 			'0'+((d->bcdHID[0]>>4)&0xf), '0'+(d->bcdHID[0]&0xf),
104 			d->bCountryCode, d->bNumDescriptors,
105 			d->bClassDescriptorType, GET2(d->wItemLength));
106 }
107 
108 static	Flags	ioflags[] = {
109 	{0, "Data", "Constant"},
110 	{1, "Array", "Variable"},
111 	{2, "Absolute", "Relative"},
112 	{3, "NoWrap", "Wrap"},
113 	{4, "Linear", "NonLinear"},
114 	{5, "PreferredState", "No Preferred State"},
115 	{6, "No Null position", "Null state"},
116 	{7, "Non Volatile", "Volatile"},
117 	{8, "Bit Field", "Buffered Bytes"},
118 	{-1, nil, nil},
119 };
120 
121 static void
122 pflag(Flags *tab, uint v)
123 {
124 	char buf[200], *s;
125 	int n;
126 
127 	n = 0;
128 	buf[0] = 0;
129 	for(; tab->name0 != nil; tab++){
130 		if(v & (1<<tab->bit))
131 			s = tab->name1;
132 		else
133 			s = tab->name0;
134 		if(s != nil && *s)
135 			n += snprint(buf+n, sizeof(buf)-n, ", %s", s);
136 	}
137 	if((debug & Dbginfo) && buf[0])
138 		fprint(2, "[%s]", buf+2);
139 }
140 
141 void
142 preport(Device *, int, ulong, byte *b, int n)
143 {
144 	byte *s, *es;
145 	int tag, nb, i, indent;
146 	int v;
147 	Flags *tab;
148 
149 	s = b+2;
150 	es = b+n;
151 	indent = 0;
152 	while(s < es){
153 		for(i=0; i<indent; i++)
154 			fprint(2, " ");
155 		tag = *s++;
156 		if(tag == Tlong){
157 			fprint(2, "long report tag");
158 			return;
159 		}
160 		if((nb = tag&3) == 3)
161 			nb = 4;
162 		v = 0;
163 		for(i=0; i<nb; i++)
164 			v |= s[i] << (i*8);
165 		switch(tag & Tmtype){
166 		case Treserved:
167 			if(tag == Tlong){
168 				fprint(2, "long report tag");
169 				return;
170 			}
171 			fprint(2, "illegal tag");
172 			return;
173 		case Tmain:
174 			tab = nil;
175 			if (debug & Dbginfo) {
176 				switch(tag & Tmitem){
177 				case Tinput:
178 					fprint(2, "Input");
179 					tab = ioflags;
180 					break;
181 				case Toutput:
182 					fprint(2, "Output");
183 					tab = ioflags;
184 					break;
185 				case Tfeature:
186 					fprint(2, "Feature");
187 					tab = ioflags;
188 					break;
189 				case Tcoll:
190 					fprint(2, "Collection");
191 					indent++;
192 					break;
193 				case Tecoll:
194 					fprint(2, "End Collection");
195 					indent--;
196 					break;
197 				default:
198 					fprint(2, "unexpected item %.2x", tag);
199 				}
200 				fprint(2, "=%#ux", v);
201 				if(tab != nil)
202 					pflag(tab, v);
203 			}
204 			break;
205 		case Tglobal:
206 			if (debug & Dbginfo) {
207 				fprint(2, "Global %#ux: ", v);
208 				switch(tag & Tmitem){
209 				case Tusagepage:
210 					fprint(2, "Usage Page %#ux", v);
211 					break;
212 				case Tlmin:
213 					fprint(2, "Logical Min %d", v);
214 					break;
215 				case Tlmax:
216 					fprint(2, "Logical Max %d", v);
217 					break;
218 				case Tpmin:
219 					fprint(2, "Physical Min %d", v);
220 					break;
221 				case Tpmax:
222 					fprint(2, "Physical Max %d", v);
223 					break;
224 				case Tunitexp:
225 					fprint(2, "Unit Exponent %d", v);
226 					break;
227 				case Tunit:
228 					fprint(2, "Unit %d", v);
229 					break;
230 				case Trepsize:
231 					fprint(2, "Report size %d", v);
232 					break;
233 				case TrepID:
234 					fprint(2, "Report ID %#x", v);
235 					break;
236 				case Trepcount:
237 					fprint(2, "Report Count %d", v);
238 					break;
239 				case Tpush:
240 					fprint(2, "Push %d", v);
241 					break;
242 				case Tpop:
243 					fprint(2, "Pop %d", v);
244 					break;
245 				default:
246 					fprint(2, "Unknown %#ux", v);
247 					break;
248 				}
249 			}
250 			break;
251 		case Tlocal:
252 			if (debug & Dbginfo) {
253 				fprint(2, "Local %#ux: ", v);
254 				switch(tag & Tmitem){
255 				case Tusage:
256 					fprint(2, "Usage %d", v);
257 					break;
258 				case Tumin:
259 					fprint(2, "Usage min %d", v);
260 					break;
261 				case Tumax:
262 					fprint(2, "Usage max %d", v);
263 					break;
264 				case Tdindex:
265 					fprint(2, "Designator index %d", v);
266 					break;
267 				case Tdmin:
268 					fprint(2, "Designator min %d", v);
269 					break;
270 				case Tdmax:
271 					fprint(2, "Designator max %d", v);
272 					break;
273 				case Tsindex:
274 					fprint(2, "String index %d", v);
275 					break;
276 				case Tsmin:
277 					fprint(2, "String min %d", v);
278 					break;
279 				case Tsmax:
280 					fprint(2, "String max %d", v);
281 					break;
282 				case Tsetdelim:
283 					fprint(2, "Set delim %#ux", v);
284 					break;
285 				default:
286 					fprint(2, "Unknown %#ux", v);
287 					break;
288 				}
289 			}
290 			break;
291 		}
292 		fprint(2, "\n");
293 		s += nb;
294 	}
295 }
296 
297 void
298 phub(Device *, int, ulong, void *b, int n)
299 {
300 	DHub *d;
301 
302 	if(n < DHUBLEN)
303 		return;
304 	d = b;
305 	if (debug & Dbginfo)
306 		fprint(2, "nport %d charac %#.4x pwr %dms current %dmA remov %#.2x",
307 			d->bNbrPorts, GET2(d->wHubCharacteristics),
308 			d->bPwrOn2PwrGood*2, d->bHubContrCurrent,
309 			d->DeviceRemovable[0]);
310 }
311 
312 void
313 pstring(Device *, int, ulong, void *b, int n)
314 {
315 	byte *rb;
316 	char *s;
317 	Rune r;
318 	int l;
319 
320 	if(n <= 2){
321 		fprint(2, "\"\"");
322 		return;
323 	}
324 	if(n & 1){
325 		fprint(2, "illegal count\n");
326 		return;
327 	}
328 	n = (n - 2)/2;
329 	rb = (byte*)b + 2;
330 	s = malloc(n*UTFmax+1);
331 	for(l=0; --n >= 0; rb += 2){
332 		r = GET2(rb);
333 		l += runetochar(s+l, &r);
334 	}
335 	s[l] = 0;
336 	fprint(2, "\"%s\"", s);
337 	free(s);
338 }
339 
340 void
341 pcs_raw(char *tag, byte *b, int n)
342 {
343 	int i;
344 
345 	if (debug & Dbginfo) {
346 		fprint(2, "%s", tag);
347 		for(i=2; i<n; i++)
348 			fprint(2, " %.2x", b[i]);
349 	}
350 }
351 
352 static void
353 pcs_config(Device *, int, ulong, void *b, int n)
354 {
355 	pcs_raw("CS_CONFIG", b, n);
356 }
357 
358 static void
359 pcs_string(Device *, ulong, void *b, int n)
360 {
361 	pcs_raw("CS_STRING", b, n);
362 }
363 
364 static void
365 pcs_endpoint(Device *, int, ulong, void *bb, int n)
366 {
367 	byte *b = bb;
368 
369 	if (debug & Dbginfo) {
370 		switch(b[2]) {
371 		case 0x01:
372 			fprint(2,
373 		"CS_ENDPOINT for TerminalID %d, delay %d, format_tag %#ux\n",
374 				b[3], b[4], b[5] | (b[6]<<8));
375 			break;
376 		case 0x02:
377 			fprint(2,
378 "CS_INTERFACE FORMAT_TYPE %d, channels %d, subframesize %d, resolution %d, freqtype %d, ",
379 				b[3], b[4], b[5], b[6], b[7]);
380 			fprint(2, "freq0 %d, freq1 %d\n",
381 				b[8]  |  b[9]<<8 | b[10]<<16,
382 				b[11] | b[12]<<8 | b[13]<<16);
383 			break;
384 		default:
385 			pcs_raw("CS_INTERFACE", bb, n);
386 		}
387 	}
388 }
389 
390 static void
391 pcs_interface(Device *, int n, ulong, void *bb, int nb)
392 {
393 
394 	if ((debug & Dbginfo) && n >= 0)
395 		pcs_raw("CS_INTERFACE", bb, nb);
396 }
397 
398 void
399 pdesc(Device *d, int c, ulong csp, byte *b, int n)
400 {
401 	int class, subclass, proto, dalt, i, ep, ifc, len;
402 	DConfig *dc;
403 	DEndpoint *de;
404 	DInterface *di;
405 	Dinf *dif;
406 	Endpt *dep;
407 	void (*f)(Device *, int, ulong, void*, int);
408 	char scbuf[64];
409 
410 	class = Class(csp);
411 	dalt = -1;
412 	ifc = -1;
413 	if (c >= nelem(d->config)) {
414 		fprint(2, "Too many interfaces (%d of %d)\n",
415 			c, nelem(d->config));
416 		return;
417 	}
418 	if(debug & Dbginfo)
419 		fprint(2, "pdesc %d.%d [%d]\n", d->id, c, n);
420 	len = -1;
421 	while(n > 2 && b[0] && b[0] <= n){
422 		if (debug & Dbginfo)
423 			fprint(2, "desc %d.%d [%d] %#2.2x: ", d->id, c, b[0], b[1]);
424 		switch (b[1]) {
425 		case CONFIGURATION:
426 			if(b[0] < DCONFLEN){
427 				if(debug & Dbginfo)
428 					fprint(2, "short config %d < %d", b[0], DCONFLEN);
429 				return;
430 			}
431 			dc = (DConfig*)b;
432 			d->config[c]->nif = dc->bNumInterfaces;
433 			d->config[c]->cval = dc->bConfigurationValue;
434 			d->config[c]->attrib = dc->bmAttributes;
435 			d->config[c]->milliamps = dc->MaxPower*2;
436 			d->nif += d->config[c]->nif;
437 			if (debug & Dbginfo)
438 				fprint(2, "config %d: tdlen %d ninterface %d iconfig %d attr %#.2x power %dmA\n",
439 					dc->bConfigurationValue,
440 					GET2(dc->wTotalLength),
441 					dc->bNumInterfaces, dc->iConfiguration,
442 					dc->bmAttributes, dc->MaxPower*2);
443 			break;
444 		case INTERFACE:
445 			if(n < DINTERLEN){
446 				if(debug & Dbginfo)
447 					fprint(2, "short interface %d < %d", b[0], DINTERLEN);
448 				return;
449 			}
450 			di = (DInterface *)b;
451 			class = di->bInterfaceClass;
452 			subclass = di->bInterfaceSubClass;
453 			proto = di->bInterfaceProtocol;
454 			csp = CSP(class, subclass, proto);
455 			if(debug & Dbginfo){
456 				sclass(scbuf, scbuf + sizeof scbuf, csp);
457 				fprint(2, "interface %d: alt %d nept %d class %#x subclass %#x proto %d [%s] iinterface %d\n",
458 					di->bInterfaceNumber,
459 					di->bAlternateSetting,
460 					di->bNumEndpoints, class, subclass,
461 					proto, scbuf, di->iInterface);
462 			}
463 			if (c < 0) {
464 				fprint(2, "Unexpected INTERFACE message\n");
465 				return;
466 			}
467 			ifc = di->bInterfaceNumber;
468 			dalt = di->bAlternateSetting;
469 			if (ifc < 0 || ifc >= nelem(d->config[c]->iface))
470 				sysfatal("Bad interface number %d", ifc);
471 			if (dalt < 0 ||
472 			    dalt >= nelem(d->config[c]->iface[ifc]->dalt))
473 				sysfatal("Bad alternate number %d", dalt);
474 			if (d->config[c] == nil)
475 				sysfatal("No config");
476 			if (ifc == 0) {
477 				if (c == 0)
478 					d->ep[0]->csp = csp;
479 				d->config[c]->csp = csp;
480 			}
481 			dif = d->config[c]->iface[ifc];
482 			if (dif == nil) {
483 				d->config[c]->iface[ifc] = dif =
484 					mallocz(sizeof(Dinf), 1);
485 				dif->csp = csp;
486 			}
487 			dif->interface = di->bInterfaceNumber;
488 			break;
489 		case ENDPOINT:
490 			if(n < DENDPLEN)
491 				return;
492 			de = (DEndpoint *)b;
493 			if(debug & Dbginfo) {
494 				fprint(2, "addr %#2.2x attrib %#2.2x maxpkt %d interval %dms",
495 					de->bEndpointAddress, de->bmAttributes,
496 					GET2(de->wMaxPacketSize), de->bInterval);
497 				if(de->bEndpointAddress & 0x80)
498 					fprint(2, " [IN]");
499 				else
500 					fprint(2, " [OUT]");
501 				switch(de->bmAttributes&0x33){
502 				case 0:
503 					fprint(2, " [Control]");
504 					break;
505 				case 1:
506 					fprint(2, " [Iso]");
507 					switch(de->bmAttributes&0xc){
508 					case 0x4:
509 						fprint(2, " [Asynchronous]");
510 						break;
511 					case 0x8:
512 						fprint(2, " [Adaptive]");
513 						break;
514 					case 0xc:
515 						fprint(2, " [Synchronous]");
516 						break;
517 					}
518 					break;
519 				case 2:
520 					fprint(2, " [Bulk]");
521 					break;
522 				case 3:
523 					fprint(2, " [Interrupt]");
524 					break;
525 				}
526 				if(b[0] == 9)
527 					fprint(2, "refresh %d synchaddress %d",
528 						b[7], b[8]);
529 				fprint(2, "\n");
530 			}
531 			if (c < 0 || ifc < 0 || dalt < 0) {
532 				fprint(2, "Unexpected ENDPOINT message\n");
533 				return;
534 			}
535 			dif = d->config[c]->iface[ifc];
536 			if (dif == nil)
537 				sysfatal("d->config[%d]->iface[%d] == nil",
538 					c, ifc);
539 			if (dif->dalt[dalt] == nil)
540 				dif->dalt[dalt] = mallocz(sizeof(Dalt),1);
541 			dif->dalt[dalt]->attrib = de->bmAttributes;
542 			dif->dalt[dalt]->interval = de->bInterval;
543 			ep = de->bEndpointAddress & 0xf;
544 			dep = d->ep[ep];
545 			if(debug)
546 				fprint(2, "%s: endpoint addr %d\n", argv0, ep);
547 			if(dep == nil){
548 				d->ep[ep] = dep = newendpt(d, ep, class);
549 				dep->dir = de->bEndpointAddress & 0x80
550 					? Ein : Eout;
551 			}else if ((dep->addr&0x80) !=
552 			    (de->bEndpointAddress&0x80))
553 				dep->dir = Eboth;
554 			dep->maxpkt = GET2(de->wMaxPacketSize);
555 			dep->addr = de->bEndpointAddress;
556 			dep->type = de->bmAttributes & 0x03;
557 			dep->isotype = (de->bmAttributes>>2) & 0x03;
558 			dep->csp = csp;
559 			dep->conf = d->config[c];
560 			dep->iface = dif;
561 			for(i = 0; i < nelem(dif->endpt); i++)
562 				if(dif->endpt[i] == nil){
563 					dif->endpt[i] = dep;
564 					break;
565 				}
566 			if(i == nelem(dif->endpt))
567 				fprint(2, "Too many endpoints\n");
568 			if (d->nif <= ep)
569 				d->nif = ep+1;
570 			break;
571 		default:
572 			assert(nelem(dprinter) == 0x100);
573 			f = dprinter[b[1]];
574 			if(f != nil){
575 				(*f)(d, c, dalt<<24 | ifc<<16 | (csp&0xffff),
576 					b, b[0]);
577 				if(debug & Dbginfo)
578 					fprint(2, "\n");
579 			}else
580 				if(verbose){
581 					int i;
582 
583 					switch(b[1]){
584 					case DEVICE:
585 						fprint(2, "(device)");
586 						break;
587 					case STRING:
588 						fprint(2, "(string)");
589 						break;
590 					default:
591 						fprint(2, "(unknown type)");
592 					}
593 					for(i=1; i<b[0]; i++)
594 						fprint(2, " %.2x", b[i]);
595 					fprint(2, "\n");
596 				}else if(debug & Dbginfo)
597 					fprint(2, "\n");
598 			break;
599 		}
600 		len = b[0];
601 		n -= len;
602 		b += len;
603 	}
604 	if(n)
605 		fprint(2, "pdesc: %d bytes left unparsed, b[0]=%d, b[-len]=%d, len=%d\n", n, b[0], b[-len], len);
606 }
607