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