xref: /plan9-contrib/sys/src/cmd/usb/lib/dump.c (revision 096dd9dd527a50a41e6473bd0cb3ce268b05a08e)
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:	fprint(2, "Input"); tab = ioflags; break;
179 				case Toutput:	fprint(2, "Output"); tab = ioflags; break;
180 				case Tfeature:	fprint(2, "Feature"); tab = ioflags; break;
181 				case Tcoll:	fprint(2, "Collection"); indent++; break;
182 				case Tecoll:	fprint(2, "End Collection"); indent--; break;
183 				default:		fprint(2, "unexpected item %.2x", tag);
184 				}
185 				fprint(2, "=%#ux", v);
186 				if(tab != nil)
187 					pflag(tab, v);
188 			}
189 			break;
190 		case Tglobal:
191 			if (debug & Dbginfo) {
192 				fprint(2, "Global %#ux: ", v);
193 				switch(tag & Tmitem){
194 				case Tusagepage:
195 					fprint(2, "Usage Page %#ux", v);
196 					break;
197 				case Tlmin:
198 					fprint(2, "Logical Min %d", v);
199 					break;
200 				case Tlmax:
201 					fprint(2, "Logical Max %d", v);
202 					break;
203 				case Tpmin:
204 					fprint(2, "Physical Min %d", v);
205 					break;
206 				case Tpmax:
207 					fprint(2, "Physical Max %d", v);
208 					break;
209 				case Tunitexp:
210 					fprint(2, "Unit Exponent %d", v);
211 					break;
212 				case Tunit:
213 					fprint(2, "Unit %d", v);
214 					break;
215 				case Trepsize:
216 					fprint(2, "Report size %d", v);
217 					break;
218 				case TrepID:
219 					fprint(2, "Report ID %#x", v);
220 					break;
221 				case Trepcount:
222 					fprint(2, "Report Count %d", v);
223 					break;
224 				case Tpush:
225 					fprint(2, "Push %d", v);
226 					break;
227 				case Tpop:
228 					fprint(2, "Pop %d", v);
229 					break;
230 				default:
231 					fprint(2, "Unknown %#ux", v);
232 					break;
233 				}
234 			}
235 			break;
236 		case Tlocal:
237 			if (debug & Dbginfo) {
238 				fprint(2, "Local %#ux: ", v);
239 				switch(tag & Tmitem){
240 				case Tusage:
241 					fprint(2, "Usage %d", v);
242 					break;
243 				case Tumin:
244 					fprint(2, "Usage min %d", v);
245 					break;
246 				case Tumax:
247 					fprint(2, "Usage max %d", v);
248 					break;
249 				case Tdindex:
250 					fprint(2, "Designator index %d", v);
251 					break;
252 				case Tdmin:
253 					fprint(2, "Designator min %d", v);
254 					break;
255 				case Tdmax:
256 					fprint(2, "Designator max %d", v);
257 					break;
258 				case Tsindex:
259 					fprint(2, "String index %d", v);
260 					break;
261 				case Tsmin:
262 					fprint(2, "String min %d", v);
263 					break;
264 				case Tsmax:
265 					fprint(2, "String max %d", v);
266 					break;
267 				case Tsetdelim:
268 					fprint(2, "Set delim %#ux", v);
269 					break;
270 				default:
271 					fprint(2, "Unknown %#ux", v);
272 					break;
273 				}
274 			}
275 			break;
276 		}
277 		fprint(2, "\n");
278 		s += nb;
279 	}
280 }
281 
282 void
283 phub(Device *, int, ulong, void *b, int n)
284 {
285 	DHub *d;
286 
287 	if(n < DHUBLEN)
288 		return;
289 	d = b;
290 	if (debug & Dbginfo)
291 		fprint(2, "nport %d charac %#.4x pwr %dms current %dmA remov %#.2x",
292 			d->bNbrPorts, GET2(d->wHubCharacteristics), d->bPwrOn2PwrGood*2,
293 			d->bHubContrCurrent, d->DeviceRemovable[0]);
294 }
295 
296 void
297 pstring(Device *, int, ulong, void *b, int n)
298 {
299 	byte *rb;
300 	char *s;
301 	Rune r;
302 	int l;
303 
304 	if(n <= 2){
305 		fprint(2, "\"\"");
306 		return;
307 	}
308 	if(n & 1){
309 		fprint(2, "illegal count\n");
310 		return;
311 	}
312 	n = (n - 2)/2;
313 	rb = (byte*)b + 2;
314 	s = malloc(n*UTFmax+1);
315 	for(l=0; --n >= 0; rb += 2){
316 		r = GET2(rb);
317 		l += runetochar(s+l, &r);
318 	}
319 	s[l] = 0;
320 	fprint(2, "\"%s\"", s);
321 	free(s);
322 }
323 
324 void
325 pcs_raw(char *tag, byte *b, int n)
326 {
327 	int i;
328 
329 	if (debug & Dbginfo) {
330 		fprint(2, "%s", tag);
331 		for(i=2; i<n; i++)
332 			fprint(2, " %.2x", b[i]);
333 	}
334 }
335 
336 static void
337 pcs_config(Device *, int, ulong, void *b, int n)
338 {
339 	pcs_raw("CS_CONFIG", b, n);
340 }
341 
342 static void
343 pcs_string(Device *, ulong, void *b, int n)
344 {
345 	pcs_raw("CS_STRING", b, n);
346 }
347 
348 static void
349 pcs_endpoint(Device *, int, ulong, void *bb, int n)
350 {
351 	byte *b = bb;
352 
353 	if (debug & Dbginfo) {
354 		switch(b[2]) {
355 		case 0x01:
356 			fprint(2, "CS_ENDPOINT for TerminalID %d, delay %d, format_tag %#ux\n",
357 				b[3], b[4], b[5] | (b[6]<<8));
358 			break;
359 		case 0x02:
360 			fprint(2, "CS_INTERFACE FORMAT_TYPE %d, channels %d, subframesize %d, resolution %d, freqtype %d, ",
361 				b[3], b[4], b[5], b[6], b[7]);
362 			fprint(2, "freq0 %d, freq1 %d\n",
363 				b[8] | (b[9]<<8) | (b[10]<<16), b[11] | (b[12]<<8) | (b[13]<<16));
364 			break;
365 		default:
366 			pcs_raw("CS_INTERFACE", bb, n);
367 		}
368 	}
369 }
370 
371 static void
372 pcs_interface(Device *, int n, ulong, void *bb, int nb) {
373 
374 	if ((debug & Dbginfo) && n >= 0) {
375 		pcs_raw("CS_INTERFACE", bb, nb);
376 	}
377 }
378 
379 void
380 pdesc(Device *d, int c, ulong csp, byte *b, int n)
381 {
382 	void (*f)(Device *, int, ulong, void*, int);
383 	int ifc = -1;
384 	int dalt = -1;
385 	int i, ep;
386 	int class, subclass, proto;
387 	DConfig *dc;
388 	DInterface *di;
389 	DEndpoint *de;
390 	Endpt *dep;
391 	Dinf *dif;
392 
393 	class = Class(csp);
394 
395 	if (c >= nelem(d->config)) {
396 		fprint(2, "Too many interfaces (%d of %d)\n",
397 			c, nelem(d->config));
398 		return;
399 	}
400 	if (debug & Dbginfo)
401 		fprint(2, "pdesc %d.%d [%d]\n", d->id, c, n);
402 	for(; n > 2 && b[0] && b[0] <= n; b += b[0]){
403 		if (debug & Dbginfo)
404 			fprint(2, "desc %d.%d [%d] %#2.2x: ", d->id, c, b[0], b[1]);
405 		switch (b[1]) {
406 		case CONFIGURATION:
407 			if(b[0] < DCONFLEN)
408 				return;
409 			dc = (DConfig*)b;
410 			d->config[c]->nif = dc->bNumInterfaces;
411 			d->config[c]->cval = dc->bConfigurationValue;
412 			d->config[c]->attrib = dc->bmAttributes;
413 			d->config[c]->milliamps = dc->MaxPower*2;
414 			d->nif += d->config[c]->nif;
415 			if (debug & Dbginfo)
416 				fprint(2, "config %d: tdlen %d ninterface %d iconfig %d attr %#.2x power %dmA\n",
417 					dc->bConfigurationValue, GET2(dc->wTotalLength),
418 					dc->bNumInterfaces, dc->iConfiguration, dc->bmAttributes,
419 					dc->MaxPower*2);
420 			break;
421 		case INTERFACE:
422 			if(n < DINTERLEN)
423 				return;
424 			di = (DInterface *)b;
425 			class = di->bInterfaceClass;
426 			subclass = di->bInterfaceSubClass;
427 			proto = di->bInterfaceProtocol;
428 			csp = CSP(class, subclass, proto);
429 			if (debug & Dbginfo)
430 				fprint(2, "interface %d: alt %d nept %d class %#x subclass %#x proto %d [%s] iinterface %d\n",
431 					di->bInterfaceNumber, di->bAlternateSetting,
432 					di->bNumEndpoints, class, subclass, proto,
433 					sclass(csp),
434 					di->iInterface);
435 			if (c < 0) {
436 				fprint(2, "Unexpected INTERFACE message\n");
437 				return;
438 			}
439 			ifc = di->bInterfaceNumber;
440 			dalt = di->bAlternateSetting;
441 			if (ifc < 0 || ifc >= nelem(d->config[c]->iface))
442 				sysfatal("Bad interface number %d", ifc);
443 			if (dalt < 0 || dalt >= nelem(d->config[c]->iface[ifc]->dalt))
444 				sysfatal("Bad alternate number %d", dalt);
445 			if (d->config[c] == nil)
446 				sysfatal("No config");
447 			if (ifc == 0) {
448 				if (c == 0)
449 					d->ep[0]->csp = csp;
450 				d->config[c]->csp = csp;
451 			}
452 			dif = d->config[c]->iface[ifc];
453 			if (dif == nil) {
454 				d->config[c]->iface[ifc] = dif =
455 					mallocz(sizeof(Dinf), 1);
456 				dif->csp = csp;
457 			}
458 			dif->interface = di->bInterfaceNumber;
459 			break;
460 		case ENDPOINT:
461 			if(n < DENDPLEN)
462 				return;
463 			de = (DEndpoint *)b;
464 			if (debug & Dbginfo) {
465 				fprint(2, "addr %#2.2x attrib %#2.2x maxpkt %d interval %dms",
466 					de->bEndpointAddress, de->bmAttributes,
467 					GET2(de->wMaxPacketSize), de->bInterval);
468 				if(de->bEndpointAddress & 0x80)
469 					fprint(2, " [IN]");
470 				else
471 					fprint(2, " [OUT]");
472 				switch(de->bmAttributes&0x33){
473 				case 0:	fprint(2, " [Control]"); break;
474 				case 1:	fprint(2, " [Iso]");
475 						switch(de->bmAttributes&0xc){
476 						case 0x4:	fprint(2, " [Asynchronous]"); break;
477 						case 0x8:	fprint(2, " [Adaptive]"); break;
478 						case 0xc:	fprint(2, " [Synchronous]"); break;
479 						}
480 						break;
481 				case 2:	fprint(2, " [Bulk]"); break;
482 				case 3:	fprint(2, " [Interrupt]"); break;
483 				}
484 				if(b[0] == 9)
485 					fprint(2, "refresh %d synchaddress %d", b[7], b[8]);
486 				fprint(2, "\n");
487 			}
488 			if (c < 0 || ifc < 0 || dalt < 0) {
489 				fprint(2, "Unexpected ENDPOINT message\n");
490 				return;
491 			}
492 			dif = d->config[c]->iface[ifc];
493 			if (dif == nil)
494 				sysfatal("d->config[%d]->iface[%d] == nil",
495 					c, ifc);
496 			if (dif->dalt[dalt] == nil)
497 				dif->dalt[dalt] = mallocz(sizeof(Dalt),1);
498 			dif->dalt[dalt]->attrib = de->bmAttributes;
499 			dif->dalt[dalt]->interval = de->bInterval;
500 			ep = de->bEndpointAddress & 0xf;
501 			dep = d->ep[ep];
502 			if (debug)
503 				fprint(2, "%s: endpoint addr %d\n", argv0, ep);
504 			if (dep == nil) {
505 				d->ep[ep] = dep = newendpt(d, ep, class);
506 				dep->dir = (de->bEndpointAddress & 0x80)?
507 					Ein: Eout;
508 			} else if ((dep->addr&0x80) !=
509 			    (de->bEndpointAddress&0x80))
510 				dep->dir = Eboth;
511 			else
512 				fprint(2, "%s: endpoint %d already in use!\n",
513 					argv0, ep); // DEBUG
514 			if(dep->maxpkt < GET2(de->wMaxPacketSize))
515 				dep->maxpkt = GET2(de->wMaxPacketSize);
516 			dep->addr = de->bEndpointAddress;
517 			dep->type = de->bmAttributes & 0x03;
518 			dep->isotype = (de->bmAttributes>>2) & 0x03;
519 			dep->csp = csp;
520 			dep->conf = d->config[c];
521 			dep->iface = dif;
522 			for(i = 0; i < nelem(dif->endpt); i++){
523 				if(dif->endpt[i] == nil){
524 					dif->endpt[i] = dep;
525 					break;
526 				}
527 			}
528 			if(i == nelem(dif->endpt))
529 				fprint(2, "Too many endpoints\n");
530 			if (d->nif <= ep)
531 				d->nif = ep+1;
532 			break;
533 		default:
534 			assert(nelem(dprinter) == 0x100);
535 			f = dprinter[b[1]];
536 			if(f != nil) {
537 				(*f)(d, c, dalt<<24 | ifc<<16 | (csp&0xffff),
538 					b, b[0]);
539 				if (debug & Dbginfo)
540 					fprint(2, "\n");
541 			}
542 			else {
543 				if (verbose) {
544 					int i;
545 
546 					fprint(2, "(unknown type)");
547 					for(i=1; i<b[0]; i++)
548 						fprint(2, " %.2x", b[i]);
549 					fprint(2, "\n");
550 				}
551 				else if (debug & Dbginfo)
552 					fprint(2, "\n");
553 			}
554 		}
555 		n -= b[0];
556 	}
557 }
558