xref: /freebsd/usr.sbin/usbconfig/dump.c (revision f078c492a9b57877c723586db26d789cda1b98ea)
1 /* $FreeBSD$ */
2 /*-
3  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4  *
5  * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stdint.h>
32 #include <err.h>
33 #include <string.h>
34 #include <pwd.h>
35 #include <grp.h>
36 #include <ctype.h>
37 
38 #include <libusb20.h>
39 #include <libusb20_desc.h>
40 
41 #include "dump.h"
42 
43 #define	DUMP0(n,type,field,...) dump_field(pdev, "  ", #field, n->field);
44 #define	DUMP1(n,type,field,...) dump_field(pdev, "    ", #field, n->field);
45 #define	DUMP2(n,type,field,...) dump_field(pdev, "      ", #field, n->field);
46 #define	DUMP3(n,type,field,...) dump_field(pdev, "        ", #field, n->field);
47 
48 const char *
49 dump_mode(uint8_t value)
50 {
51 	if (value == LIBUSB20_MODE_HOST)
52 		return ("HOST");
53 	return ("DEVICE");
54 }
55 
56 const char *
57 dump_speed(uint8_t value)
58 {
59 	;				/* style fix */
60 	switch (value) {
61 	case LIBUSB20_SPEED_LOW:
62 		return ("LOW (1.5Mbps)");
63 	case LIBUSB20_SPEED_FULL:
64 		return ("FULL (12Mbps)");
65 	case LIBUSB20_SPEED_HIGH:
66 		return ("HIGH (480Mbps)");
67 	case LIBUSB20_SPEED_VARIABLE:
68 		return ("VARIABLE (52-480Mbps)");
69 	case LIBUSB20_SPEED_SUPER:
70 		return ("SUPER (5.0Gbps)");
71 	default:
72 		break;
73 	}
74 	return ("UNKNOWN ()");
75 }
76 
77 const char *
78 dump_power_mode(uint8_t value)
79 {
80 	;				/* style fix */
81 	switch (value) {
82 	case LIBUSB20_POWER_OFF:
83 		return ("OFF");
84 	case LIBUSB20_POWER_ON:
85 		return ("ON");
86 	case LIBUSB20_POWER_SAVE:
87 		return ("SAVE");
88 	case LIBUSB20_POWER_SUSPEND:
89 		return ("SUSPEND");
90 	case LIBUSB20_POWER_RESUME:
91 		return ("RESUME");
92 	default:
93 		return ("UNKNOWN");
94 	}
95 }
96 
97 static void
98 dump_field(struct libusb20_device *pdev, const char *plevel,
99     const char *field, uint32_t value)
100 {
101 	uint8_t temp_string[256];
102 
103 	printf("%s%s = 0x%04x ", plevel, field, value);
104 
105 	if (strlen(plevel) == 8) {
106 		/* Endpoint Descriptor */
107 
108 		if (strcmp(field, "bEndpointAddress") == 0) {
109 			if (value & 0x80)
110 				printf(" <IN>\n");
111 			else
112 				printf(" <OUT>\n");
113 			return;
114 		}
115 		if (strcmp(field, "bmAttributes") == 0) {
116 			switch (value & 0x03) {
117 			case 0:
118 				printf(" <CONTROL>\n");
119 				break;
120 			case 1:
121 				switch (value & 0x0C) {
122 				case 0x00:
123 					printf(" <ISOCHRONOUS>\n");
124 					break;
125 				case 0x04:
126 					printf(" <ASYNC-ISOCHRONOUS>\n");
127 					break;
128 				case 0x08:
129 					printf(" <ADAPT-ISOCHRONOUS>\n");
130 					break;
131 				default:
132 					printf(" <SYNC-ISOCHRONOUS>\n");
133 					break;
134 				}
135 				break;
136 			case 2:
137 				printf(" <BULK>\n");
138 				break;
139 			default:
140 				printf(" <INTERRUPT>\n");
141 				break;
142 			}
143 			return;
144 		}
145 	}
146 	if ((field[0] == 'i') && (field[1] != 'd')) {
147 		/* Indirect String Descriptor */
148 		if (value == 0) {
149 			printf(" <no string>\n");
150 			return;
151 		}
152 		if (libusb20_dev_req_string_simple_sync(pdev, value,
153 		    temp_string, sizeof(temp_string))) {
154 			printf(" <retrieving string failed>\n");
155 			return;
156 		}
157 		printf(" <%s>\n", temp_string);
158 		return;
159 	}
160 	if (strlen(plevel) == 2 || strlen(plevel) == 6) {
161 
162 		/* Device and Interface Descriptor class codes */
163 
164 		if (strcmp(field, "bInterfaceClass") == 0 ||
165 		    strcmp(field, "bDeviceClass") == 0) {
166 
167 			switch (value) {
168 			case 0x00:
169 				printf(" <Probed by interface class>\n");
170 				break;
171 			case 0x01:
172 				printf(" <Audio device>\n");
173 				break;
174 			case 0x02:
175 				printf(" <Communication device>\n");
176 				break;
177 			case 0x03:
178 				printf(" <HID device>\n");
179 				break;
180 			case 0x05:
181 				printf(" <Physical device>\n");
182 				break;
183 			case 0x06:
184 				printf(" <Still imaging>\n");
185 				break;
186 			case 0x07:
187 				printf(" <Printer device>\n");
188 				break;
189 			case 0x08:
190 				printf(" <Mass storage>\n");
191 				break;
192 			case 0x09:
193 				printf(" <HUB>\n");
194 				break;
195 			case 0x0A:
196 				printf(" <CDC-data>\n");
197 				break;
198 			case 0x0B:
199 				printf(" <Smart card>\n");
200 				break;
201 			case 0x0D:
202 				printf(" <Content security>\n");
203 				break;
204 			case 0x0E:
205 				printf(" <Video device>\n");
206 				break;
207 			case 0x0F:
208 				printf(" <Personal healthcare>\n");
209 				break;
210 			case 0x10:
211 				printf(" <Audio and video device>\n");
212 				break;
213 			case 0x11:
214 				printf(" <Billboard device>\n");
215 				break;
216 			case 0xDC:
217 				printf(" <Diagnostic device>\n");
218 				break;
219 			case 0xE0:
220 				printf(" <Wireless controller>\n");
221 				break;
222 			case 0xEF:
223 				printf(" <Miscellaneous device>\n");
224 				break;
225 			case 0xFE:
226 				printf(" <Application specific>\n");
227 				break;
228 			case 0xFF:
229 				printf(" <Vendor specific>\n");
230 				break;
231 			default:
232 				printf(" <Unknown>\n");
233 				break;
234 			}
235 			return;
236 		}
237 	}
238 	/* No additional information */
239 	printf("\n");
240 }
241 
242 static void
243 dump_extra(struct libusb20_me_struct *str, const char *plevel)
244 {
245 	const uint8_t *ptr;
246 	uint8_t x;
247 
248 	ptr = NULL;
249 
250 	while ((ptr = libusb20_desc_foreach(str, ptr))) {
251 		printf("\n" "%sAdditional Descriptor\n\n", plevel);
252 		printf("%sbLength = 0x%02x\n", plevel, ptr[0]);
253 		printf("%sbDescriptorType = 0x%02x\n", plevel, ptr[1]);
254 		if (ptr[0] > 1)
255 			printf("%sbDescriptorSubType = 0x%02x\n",
256 			    plevel, ptr[2]);
257 		printf("%s RAW dump: ", plevel);
258 		for (x = 0; x != ptr[0]; x++) {
259 			if ((x % 8) == 0) {
260 				printf("\n%s 0x%02x | ", plevel, x);
261 			}
262 			printf("0x%02x%s", ptr[x],
263 			    (x != (ptr[0] - 1)) ? ", " : (x % 8) ? "\n" : "");
264 		}
265 		printf("\n");
266 	}
267 	return;
268 }
269 
270 static void
271 dump_endpoint(struct libusb20_device *pdev,
272     struct libusb20_endpoint *ep)
273 {
274 	struct LIBUSB20_ENDPOINT_DESC_DECODED *edesc;
275 
276 	edesc = &ep->desc;
277 	LIBUSB20_ENDPOINT_DESC(DUMP3, edesc);
278 	dump_extra(&ep->extra, "  " "  " "  ");
279 	return;
280 }
281 
282 static void
283 dump_iface(struct libusb20_device *pdev,
284     struct libusb20_interface *iface)
285 {
286 	struct LIBUSB20_INTERFACE_DESC_DECODED *idesc;
287 	uint8_t z;
288 
289 	idesc = &iface->desc;
290 	LIBUSB20_INTERFACE_DESC(DUMP2, idesc);
291 	dump_extra(&iface->extra, "  " "  " "  ");
292 
293 	for (z = 0; z != iface->num_endpoints; z++) {
294 		printf("\n     Endpoint %u\n", z);
295 		dump_endpoint(pdev, iface->endpoints + z);
296 	}
297 	return;
298 }
299 
300 void
301 dump_device_info(struct libusb20_device *pdev, uint8_t show_ifdrv)
302 {
303 	char buf[128];
304 	uint8_t n;
305 	unsigned int usage;
306 
307 	usage = libusb20_dev_get_power_usage(pdev);
308 
309 	printf("%s, cfg=%u md=%s spd=%s pwr=%s (%umA)\n",
310 	    libusb20_dev_get_desc(pdev),
311 	    libusb20_dev_get_config_index(pdev),
312 	    dump_mode(libusb20_dev_get_mode(pdev)),
313 	    dump_speed(libusb20_dev_get_speed(pdev)),
314 	    dump_power_mode(libusb20_dev_get_power_mode(pdev)),
315 	    usage);
316 
317 	if (!show_ifdrv)
318 		return;
319 
320 	for (n = 0; n != 255; n++) {
321 		if (libusb20_dev_get_iface_desc(pdev, n, buf, sizeof(buf)))
322 			break;
323 		if (buf[0] == 0)
324 			continue;
325 		printf("ugen%u.%u.%u: %s\n",
326 		    libusb20_dev_get_bus_number(pdev),
327 		    libusb20_dev_get_address(pdev), n, buf);
328 	}
329 }
330 
331 void
332 dump_be_quirk_names(struct libusb20_backend *pbe)
333 {
334 	struct libusb20_quirk q;
335 	uint16_t x;
336 	int error;
337 
338 	memset(&q, 0, sizeof(q));
339 
340 	printf("\nDumping list of supported quirks:\n\n");
341 
342 	for (x = 0; x != 0xFFFF; x++) {
343 
344 		error = libusb20_be_get_quirk_name(pbe, x, &q);
345 		if (error) {
346 			if (x == 0) {
347 				printf("No quirk names - maybe the USB quirk "
348 				    "module has not been loaded.\n");
349 			}
350 			break;
351 		}
352 		if (strcmp(q.quirkname, "UQ_NONE"))
353 			printf("%s\n", q.quirkname);
354 	}
355 	printf("\n");
356 	return;
357 }
358 
359 void
360 dump_be_dev_quirks(struct libusb20_backend *pbe)
361 {
362 	struct libusb20_quirk q;
363 	uint16_t x;
364 	int error;
365 
366 	memset(&q, 0, sizeof(q));
367 
368 	printf("\nDumping current device quirks:\n\n");
369 
370 	for (x = 0; x != 0xFFFF; x++) {
371 
372 		error = libusb20_be_get_dev_quirk(pbe, x, &q);
373 		if (error) {
374 			if (x == 0) {
375 				printf("No device quirks - maybe the USB quirk "
376 				    "module has not been loaded.\n");
377 			}
378 			break;
379 		}
380 		if (strcmp(q.quirkname, "UQ_NONE")) {
381 			printf("VID=0x%04x PID=0x%04x REVLO=0x%04x "
382 			    "REVHI=0x%04x QUIRK=%s\n",
383 			    q.vid, q.pid, q.bcdDeviceLow,
384 			    q.bcdDeviceHigh, q.quirkname);
385 		}
386 	}
387 	printf("\n");
388 	return;
389 }
390 
391 void
392 dump_device_desc(struct libusb20_device *pdev)
393 {
394 	struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
395 
396 	ddesc = libusb20_dev_get_device_desc(pdev);
397 	LIBUSB20_DEVICE_DESC(DUMP0, ddesc);
398 	return;
399 }
400 
401 void
402 dump_config(struct libusb20_device *pdev, uint8_t all_cfg)
403 {
404 	struct LIBUSB20_CONFIG_DESC_DECODED *cdesc;
405 	struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
406 	struct libusb20_config *pcfg = NULL;
407 	uint8_t cfg_index;
408 	uint8_t cfg_index_end;
409 	uint8_t x;
410 	uint8_t y;
411 
412 	ddesc = libusb20_dev_get_device_desc(pdev);
413 
414 	if (all_cfg) {
415 		cfg_index = 0;
416 		cfg_index_end = ddesc->bNumConfigurations;
417 	} else {
418 		cfg_index = libusb20_dev_get_config_index(pdev);
419 		cfg_index_end = cfg_index + 1;
420 	}
421 
422 	for (; cfg_index != cfg_index_end; cfg_index++) {
423 
424 		pcfg = libusb20_dev_alloc_config(pdev, cfg_index);
425 		if (!pcfg) {
426 			continue;
427 		}
428 		printf("\n Configuration index %u\n\n", cfg_index);
429 		cdesc = &(pcfg->desc);
430 		LIBUSB20_CONFIG_DESC(DUMP1, cdesc);
431 		dump_extra(&(pcfg->extra), "  " "  ");
432 
433 		for (x = 0; x != pcfg->num_interface; x++) {
434 			printf("\n    Interface %u\n", x);
435 			dump_iface(pdev, pcfg->interface + x);
436 			printf("\n");
437 			for (y = 0; y != (pcfg->interface + x)->num_altsetting; y++) {
438 				printf("\n    Interface %u Alt %u\n", x, y + 1);
439 				dump_iface(pdev,
440 				    (pcfg->interface + x)->altsetting + y);
441 				printf("\n");
442 			}
443 		}
444 		printf("\n");
445 		free(pcfg);
446 	}
447 	return;
448 }
449 
450 void
451 dump_string_by_index(struct libusb20_device *pdev, uint8_t str_index)
452 {
453 	char *pbuf;
454 	uint8_t n;
455 	uint8_t len;
456 
457 	pbuf = malloc(256);
458 	if (pbuf == NULL)
459 		err(1, "out of memory");
460 
461 	if (str_index == 0) {
462 		/* language table */
463 		if (libusb20_dev_req_string_sync(pdev,
464 		    str_index, 0, pbuf, 256)) {
465 			printf("STRING_0x%02x = <read error>\n", str_index);
466 		} else {
467 			printf("STRING_0x%02x = ", str_index);
468 			len = (uint8_t)pbuf[0];
469 			for (n = 0; n != len; n++) {
470 				printf("0x%02x%s", (uint8_t)pbuf[n],
471 				    (n != (len - 1)) ? ", " : "");
472 			}
473 			printf("\n");
474 		}
475 	} else {
476 		/* ordinary string */
477 		if (libusb20_dev_req_string_simple_sync(pdev,
478 		    str_index, pbuf, 256)) {
479 			printf("STRING_0x%02x = <read error>\n", str_index);
480 		} else {
481 			printf("STRING_0x%02x = <%s>\n", str_index, pbuf);
482 		}
483 	}
484 	free(pbuf);
485 }
486 
487 void
488 dump_device_stats(struct libusb20_device *pdev)
489 {
490 	struct libusb20_device_stats st;
491 
492 	if (libusb20_dev_get_stats(pdev, &st)) {
493 		printf("{}\n");
494 	} else {
495 		printf("{\n"
496 		    "    UE_CONTROL_OK       : %llu\n"
497 		    "    UE_ISOCHRONOUS_OK   : %llu\n"
498 		    "    UE_BULK_OK          : %llu\n"
499 		    "    UE_INTERRUPT_OK     : %llu\n"
500 		    "    UE_CONTROL_FAIL     : %llu\n"
501 		    "    UE_ISOCHRONOUS_FAIL : %llu\n"
502 		    "    UE_BULK_FAIL        : %llu\n"
503 		    "    UE_INTERRUPT_FAIL   : %llu\n"
504 		    "}\n",
505 		    (unsigned long long)st.xfer_ok[0],
506 		    (unsigned long long)st.xfer_ok[1],
507 		    (unsigned long long)st.xfer_ok[2],
508 	            (unsigned long long)st.xfer_ok[3],
509 		    (unsigned long long)st.xfer_fail[0],
510 		    (unsigned long long)st.xfer_fail[1],
511 		    (unsigned long long)st.xfer_fail[2],
512 		    (unsigned long long)st.xfer_fail[3]);
513 	}
514 }
515