xref: /freebsd/usr.sbin/usbconfig/dump.c (revision f938c0a90313125a9518307e80ca92d4c71f7745)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
5  * Copyright (c) 2024 Baptiste Daroussin <bapt@FreeBSD.org>
6  * Copyright (c) 2025 The FreeBSD Foundation
7  *
8  * Portions of this software were developed by Björn Zeeb
9  * under sponsorship from the FreeBSD Foundation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/queue.h>
34 
35 #include <stdbool.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stdint.h>
39 #include <unistd.h>
40 #include <err.h>
41 #include <string.h>
42 #include <pwd.h>
43 #include <grp.h>
44 #include <ctype.h>
45 #include <fcntl.h>
46 
47 #include <libusb20.h>
48 #include <libusb20_desc.h>
49 
50 #include <dev/usb/usb_ioctl.h>
51 
52 #include "dump.h"
53 #include "pathnames.h"
54 
55 #ifndef IOUSB
56 #define IOUSB(a) a
57 #endif
58 
59 #define	DUMP0(n,type,field,...) dump_field(pdev, "  ", #field, n->field);
60 #define	DUMP0L(n,type,field,...) dump_fieldl(pdev, "  ", #field, n->field);
61 #define	DUMP1(n,type,field,...) dump_field(pdev, "    ", #field, n->field);
62 #define	DUMP2(n,type,field,...) dump_field(pdev, "      ", #field, n->field);
63 #define	DUMP3(n,type,field,...) dump_field(pdev, "        ", #field, n->field);
64 
65 struct usb_product_info {
66 	STAILQ_ENTRY(usb_product_info)	link;
67 	int				id;
68 	char				*desc;
69 };
70 
71 struct usb_vendor_info {
72 	STAILQ_ENTRY(usb_vendor_info)	link;
73 	STAILQ_HEAD(,usb_product_info)	devs;
74 	int				id;
75 	char				*desc;
76 };
77 
78 STAILQ_HEAD(usb_vendors, usb_vendor_info);
79 
80 const char *
dump_mode(uint8_t value)81 dump_mode(uint8_t value)
82 {
83 	if (value == LIBUSB20_MODE_HOST)
84 		return ("HOST");
85 	return ("DEVICE");
86 }
87 
88 const char *
dump_speed(uint8_t value)89 dump_speed(uint8_t value)
90 {
91 	;				/* style fix */
92 	switch (value) {
93 	case LIBUSB20_SPEED_LOW:
94 		return ("LOW (1.5Mbps)");
95 	case LIBUSB20_SPEED_FULL:
96 		return ("FULL (12Mbps)");
97 	case LIBUSB20_SPEED_HIGH:
98 		return ("HIGH (480Mbps)");
99 	case LIBUSB20_SPEED_VARIABLE:
100 		return ("VARIABLE (52-480Mbps)");
101 	case LIBUSB20_SPEED_SUPER:
102 		return ("SUPER (5.0Gbps)");
103 	case LIBUSB20_SPEED_SUPER_PLUS:
104 		return ("SUPER+(10-20Gbps)");
105 	default:
106 		break;
107 	}
108 	return ("UNKNOWN ()");
109 }
110 
111 const char *
dump_power_mode(uint8_t value)112 dump_power_mode(uint8_t value)
113 {
114 	;				/* style fix */
115 	switch (value) {
116 	case LIBUSB20_POWER_OFF:
117 		return ("OFF");
118 	case LIBUSB20_POWER_ON:
119 		return ("ON");
120 	case LIBUSB20_POWER_SAVE:
121 		return ("SAVE");
122 	case LIBUSB20_POWER_SUSPEND:
123 		return ("SUSPEND");
124 	case LIBUSB20_POWER_RESUME:
125 		return ("RESUME");
126 	default:
127 		return ("UNKNOWN");
128 	}
129 }
130 
131 static void
_dump_field(struct libusb20_device * pdev,const char * plevel,const char * field,uint32_t value,bool list_mode)132 _dump_field(struct libusb20_device *pdev, const char *plevel,
133     const char *field, uint32_t value, bool list_mode)
134 {
135 	uint8_t temp_string[256];
136 
137 	if (list_mode) {
138 		/* Skip fields we are not interested in. */
139 		if (strcmp(field, "bLength") == 0 ||
140 		    strcmp(field, "bDescriptorType") == 0 ||
141 		    strcmp(field, "bMaxPacketSize0") == 0)
142 			return;
143 
144 		printf("%s=%#06x ", field, value);
145 		return;
146 	}
147 	printf("%s%s = 0x%04x ", plevel, field, value);
148 
149 	if (strlen(plevel) == 8) {
150 		/* Endpoint Descriptor */
151 
152 		if (strcmp(field, "bEndpointAddress") == 0) {
153 			if (value & 0x80)
154 				printf(" <IN>\n");
155 			else
156 				printf(" <OUT>\n");
157 			return;
158 		}
159 		if (strcmp(field, "bmAttributes") == 0) {
160 			switch (value & 0x03) {
161 			case 0:
162 				printf(" <CONTROL>\n");
163 				break;
164 			case 1:
165 				switch (value & 0x0C) {
166 				case 0x00:
167 					printf(" <ISOCHRONOUS>\n");
168 					break;
169 				case 0x04:
170 					printf(" <ASYNC-ISOCHRONOUS>\n");
171 					break;
172 				case 0x08:
173 					printf(" <ADAPT-ISOCHRONOUS>\n");
174 					break;
175 				default:
176 					printf(" <SYNC-ISOCHRONOUS>\n");
177 					break;
178 				}
179 				break;
180 			case 2:
181 				printf(" <BULK>\n");
182 				break;
183 			default:
184 				printf(" <INTERRUPT>\n");
185 				break;
186 			}
187 			return;
188 		}
189 	}
190 	if ((field[0] == 'i') && (field[1] != 'd')) {
191 		/* Indirect String Descriptor */
192 		if (value == 0) {
193 			printf(" <no string>\n");
194 			return;
195 		}
196 		if (libusb20_dev_req_string_simple_sync(pdev, value,
197 		    temp_string, sizeof(temp_string))) {
198 			printf(" <retrieving string failed>\n");
199 			return;
200 		}
201 		printf(" <%s>\n", temp_string);
202 		return;
203 	}
204 	if (strlen(plevel) == 2 || strlen(plevel) == 6) {
205 
206 		/* Device and Interface Descriptor class codes */
207 
208 		if (strcmp(field, "bInterfaceClass") == 0 ||
209 		    strcmp(field, "bDeviceClass") == 0) {
210 
211 			switch (value) {
212 			case 0x00:
213 				printf(" <Probed by interface class>\n");
214 				break;
215 			case 0x01:
216 				printf(" <Audio device>\n");
217 				break;
218 			case 0x02:
219 				printf(" <Communication device>\n");
220 				break;
221 			case 0x03:
222 				printf(" <HID device>\n");
223 				break;
224 			case 0x05:
225 				printf(" <Physical device>\n");
226 				break;
227 			case 0x06:
228 				printf(" <Still imaging>\n");
229 				break;
230 			case 0x07:
231 				printf(" <Printer device>\n");
232 				break;
233 			case 0x08:
234 				printf(" <Mass storage>\n");
235 				break;
236 			case 0x09:
237 				printf(" <HUB>\n");
238 				break;
239 			case 0x0A:
240 				printf(" <CDC-data>\n");
241 				break;
242 			case 0x0B:
243 				printf(" <Smart card>\n");
244 				break;
245 			case 0x0D:
246 				printf(" <Content security>\n");
247 				break;
248 			case 0x0E:
249 				printf(" <Video device>\n");
250 				break;
251 			case 0x0F:
252 				printf(" <Personal healthcare>\n");
253 				break;
254 			case 0x10:
255 				printf(" <Audio and video device>\n");
256 				break;
257 			case 0x11:
258 				printf(" <Billboard device>\n");
259 				break;
260 			case 0xDC:
261 				printf(" <Diagnostic device>\n");
262 				break;
263 			case 0xE0:
264 				printf(" <Wireless controller>\n");
265 				break;
266 			case 0xEF:
267 				printf(" <Miscellaneous device>\n");
268 				break;
269 			case 0xFE:
270 				printf(" <Application specific>\n");
271 				break;
272 			case 0xFF:
273 				printf(" <Vendor specific>\n");
274 				break;
275 			default:
276 				printf(" <Unknown>\n");
277 				break;
278 			}
279 			return;
280 		}
281 	}
282 	/* No additional information */
283 	printf("\n");
284 }
285 
286 static void
dump_field(struct libusb20_device * pdev,const char * plevel,const char * field,uint32_t value)287 dump_field(struct libusb20_device *pdev, const char *plevel,
288     const char *field, uint32_t value)
289 {
290 	_dump_field(pdev, plevel, field, value, false);
291 }
292 
293 static void
dump_fieldl(struct libusb20_device * pdev,const char * plevel,const char * field,uint32_t value)294 dump_fieldl(struct libusb20_device *pdev, const char *plevel,
295     const char *field, uint32_t value)
296 {
297 	_dump_field(pdev, plevel, field, value, true);
298 }
299 
300 static void
dump_extra(struct libusb20_me_struct * str,const char * plevel)301 dump_extra(struct libusb20_me_struct *str, const char *plevel)
302 {
303 	const uint8_t *ptr;
304 	uint8_t x;
305 
306 	ptr = NULL;
307 
308 	while ((ptr = libusb20_desc_foreach(str, ptr))) {
309 		printf("\n" "%sAdditional Descriptor\n\n", plevel);
310 		printf("%sbLength = 0x%02x\n", plevel, ptr[0]);
311 		printf("%sbDescriptorType = 0x%02x\n", plevel, ptr[1]);
312 		if (ptr[0] > 1)
313 			printf("%sbDescriptorSubType = 0x%02x\n",
314 			    plevel, ptr[2]);
315 		printf("%s RAW dump: ", plevel);
316 		for (x = 0; x != ptr[0]; x++) {
317 			if ((x % 8) == 0) {
318 				printf("\n%s 0x%02x | ", plevel, x);
319 			}
320 			printf("0x%02x%s", ptr[x],
321 			    (x != (ptr[0] - 1)) ? ", " : (x % 8) ? "\n" : "");
322 		}
323 		printf("\n");
324 	}
325 }
326 
327 static void
dump_endpoint(struct libusb20_device * pdev,struct libusb20_endpoint * ep)328 dump_endpoint(struct libusb20_device *pdev,
329     struct libusb20_endpoint *ep)
330 {
331 	struct LIBUSB20_ENDPOINT_DESC_DECODED *edesc;
332 
333 	edesc = &ep->desc;
334 	LIBUSB20_ENDPOINT_DESC(DUMP3, edesc);
335 	dump_extra(&ep->extra, "  " "  " "  ");
336 }
337 
338 static void
dump_iface(struct libusb20_device * pdev,struct libusb20_interface * iface)339 dump_iface(struct libusb20_device *pdev,
340     struct libusb20_interface *iface)
341 {
342 	struct LIBUSB20_INTERFACE_DESC_DECODED *idesc;
343 	uint8_t z;
344 
345 	idesc = &iface->desc;
346 	LIBUSB20_INTERFACE_DESC(DUMP2, idesc);
347 	dump_extra(&iface->extra, "  " "  " "  ");
348 
349 	for (z = 0; z != iface->num_endpoints; z++) {
350 		printf("\n     Endpoint %u\n", z);
351 		dump_endpoint(pdev, iface->endpoints + z);
352 	}
353 }
354 
355 static struct usb_vendors *
load_vendors(void)356 load_vendors(void)
357 {
358 	const char *dbf;
359 	FILE *db = NULL;
360 	struct usb_vendor_info *cv;
361 	struct usb_product_info *cd;
362 	struct usb_vendors *usb_vendors;
363 	char buf[1024], str[1024];
364 	char *ch;
365 	int id;
366 
367 	usb_vendors = malloc(sizeof(*usb_vendors));
368 	if (usb_vendors == NULL)
369 		err(1, "out of memory");
370 	STAILQ_INIT(usb_vendors);
371 	if ((dbf = getenv("USB_VENDOR_DATABASE")) != NULL)
372 		db = fopen(dbf, "r");
373 	if (db == NULL) {
374 		dbf = _PATH_LUSBVDB;
375 		if ((db = fopen(dbf, "r")) == NULL) {
376 			dbf = _PATH_USBVDB;
377 			if ((db = fopen(dbf, "r")) == NULL)
378 				return (usb_vendors);
379 		}
380 	}
381 	cv = NULL;
382 	cd = NULL;
383 
384 	for (;;) {
385 		if (fgets(buf, sizeof(buf), db) == NULL)
386 			break;
387 
388 		if ((ch = strchr(buf, '#')) != NULL)
389 			*ch = '\0';
390 		if (ch == buf)
391 			continue;
392 		ch = strchr(buf, '\0') - 1;
393 		while (ch > buf && isspace(*ch))
394 			*ch-- = '\0';
395 		if (ch <= buf)
396 			continue;
397 
398 		/* Can't handle subvendor / subdevice entries yet */
399 		if (buf[0] == '\t' && buf[1] == '\t')
400 			continue;
401 
402 		/* Check for vendor entry */
403 		if (buf[0] != '\t' && sscanf(buf, "%04x %[^\n]", &id, str) == 2) {
404 			if ((id == 0) || (strlen(str) < 1))
405 				continue;
406 			if ((cv = malloc(sizeof(struct usb_vendor_info))) == NULL)
407 				err(1, "out of memory");
408 			if ((cv->desc = strdup(str)) == NULL)
409 				err(1, "out of memory");
410 			cv->id = id;
411 			STAILQ_INIT(&cv->devs);
412 			STAILQ_INSERT_TAIL(usb_vendors, cv, link);
413 			continue;
414 		}
415 
416 		/* Check for device entry */
417 		if (buf[0] == '\t' && sscanf(buf + 1, "%04x %[^\n]", &id, str) == 2) {
418 			if ((id == 0) || (strlen(str) < 1))
419 				continue;
420 			if (cv == NULL)
421 				continue;
422 			if ((cd = malloc(sizeof(struct usb_product_info))) == NULL)
423 				err(1, "out of memory");
424 			if ((cd->desc = strdup(str)) == NULL)
425 				err(1, "out of memory");
426 			cd->id = id;
427 			STAILQ_INSERT_TAIL(&cv->devs, cd, link);
428 			continue;
429 		}
430 	}
431 	if (ferror(db))
432 		err(1, "error reading the usb id db");
433 
434 	fclose(db);
435 	/* cleanup */
436 	return (usb_vendors);
437 }
438 
439 enum _device_descr_list_type {
440 	_DEVICE_DESCR_LIST_TYPE_DEFAULT		= 0,
441 	_DEVICE_DESCR_LIST_TYPE_UGEN		= 1,
442 	_DEVICE_DESCR_LIST_TYPE_PRODUCT_VENDOR	= 2,
443 };
444 
445 static char *
_device_desc(struct libusb20_device * pdev,enum _device_descr_list_type list_type)446 _device_desc(struct libusb20_device *pdev,
447     enum _device_descr_list_type list_type)
448 {
449 	static struct usb_vendors *usb_vendors = NULL;
450 	char *desc = NULL;
451 	const char *vendor = NULL, *product = NULL;
452 	uint16_t vid;
453 	uint16_t pid;
454 	struct usb_vendor_info *vi;
455 	struct usb_product_info *pi;
456 	struct usb_device_info devinfo;
457 
458 	if (list_type == _DEVICE_DESCR_LIST_TYPE_UGEN) {
459 		asprintf(&desc, "ugen%u.%u",
460 				libusb20_dev_get_bus_number(pdev),
461 				libusb20_dev_get_address(pdev));
462 		return (desc);
463 	}
464 
465 	vid = libusb20_dev_get_device_desc(pdev)->idVendor;
466 	pid = libusb20_dev_get_device_desc(pdev)->idProduct;
467 
468 	if (usb_vendors == NULL)
469 		usb_vendors = load_vendors();
470 
471 	STAILQ_FOREACH(vi, usb_vendors, link) {
472 		if (vi->id == vid) {
473 			vendor = vi->desc;
474 			break;
475 		}
476 	}
477 	if (vi != NULL) {
478 		STAILQ_FOREACH(pi, &vi->devs, link) {
479 			if (pi->id == pid) {
480 				product = pi->desc;
481 				break;
482 			}
483 		}
484 	}
485 
486 	/*
487 	 * Try to gather the information; libusb2 unfortunately seems to
488 	 * only build an entire string but not save vendor/product individually.
489 	 */
490 	if (vendor == NULL || product == NULL) {
491 		char buf[64];
492 		int f;
493 
494 		snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
495 		    libusb20_dev_get_bus_number(pdev),
496 		    libusb20_dev_get_address(pdev));
497 
498 		f = open(buf, O_RDWR);
499 		if (f < 0)
500 			goto skip_vp_recovery;
501 
502 		if (ioctl(f, IOUSB(USB_GET_DEVICEINFO), &devinfo))
503 			goto skip_vp_recovery;
504 
505 
506 		if (vendor == NULL)
507 			vendor = devinfo.udi_vendor;
508 		if (product == NULL)
509 			product = devinfo.udi_product;
510 
511 skip_vp_recovery:
512 		if (f >= 0)
513 			close(f);
514 	}
515 
516 	if (list_type == _DEVICE_DESCR_LIST_TYPE_PRODUCT_VENDOR) {
517 		asprintf(&desc, "vendor='%s' product='%s'",
518 		    (vendor != NULL) ? vendor : "",
519 		    (product != NULL) ? product : "");
520 		return (desc);
521 	}
522 
523 	if (vendor == NULL || product == NULL)
524 		return (NULL);
525 
526 	asprintf(&desc, "ugen%u.%u: <%s %s> at usbus%u",
527 			libusb20_dev_get_bus_number(pdev),
528 			libusb20_dev_get_address(pdev),
529 			product, vendor,
530 			libusb20_dev_get_bus_number(pdev));
531 
532 	return (desc);
533 }
534 
535 void
dump_device_info(struct libusb20_device * pdev,uint8_t show_ifdrv,bool list_mode)536 dump_device_info(struct libusb20_device *pdev, uint8_t show_ifdrv,
537     bool list_mode)
538 {
539 	char buf[128];
540 	uint8_t n;
541 	unsigned int usage;
542 	char *desc;
543 
544 	usage = libusb20_dev_get_power_usage(pdev);
545 
546 	desc = _device_desc(pdev, (list_mode) ? _DEVICE_DESCR_LIST_TYPE_UGEN :
547 	    _DEVICE_DESCR_LIST_TYPE_DEFAULT);
548 
549 	if (list_mode)
550 		printf("%s: ", desc);
551 	else
552 		printf("%s, cfg=%u md=%s spd=%s pwr=%s (%umA)\n",
553 		    desc ? desc : libusb20_dev_get_desc(pdev),
554 		    libusb20_dev_get_config_index(pdev),
555 		    dump_mode(libusb20_dev_get_mode(pdev)),
556 		    dump_speed(libusb20_dev_get_speed(pdev)),
557 		    dump_power_mode(libusb20_dev_get_power_mode(pdev)),
558 		    usage);
559 	free(desc);
560 
561 	if (list_mode || !show_ifdrv)
562 		return;
563 
564 	for (n = 0; n != 255; n++) {
565 		if (libusb20_dev_get_iface_desc(pdev, n, buf, sizeof(buf)))
566 			break;
567 		if (buf[0] == 0)
568 			continue;
569 		printf("ugen%u.%u.%u: %s\n",
570 		    libusb20_dev_get_bus_number(pdev),
571 		    libusb20_dev_get_address(pdev), n, buf);
572 	}
573 }
574 
575 void
dump_be_quirk_names(struct libusb20_backend * pbe)576 dump_be_quirk_names(struct libusb20_backend *pbe)
577 {
578 	struct libusb20_quirk q;
579 	uint16_t x;
580 	int error;
581 
582 	memset(&q, 0, sizeof(q));
583 
584 	printf("\nDumping list of supported quirks:\n\n");
585 
586 	for (x = 0; x != 0xFFFF; x++) {
587 
588 		error = libusb20_be_get_quirk_name(pbe, x, &q);
589 		if (error) {
590 			if (x == 0) {
591 				printf("No quirk names - maybe the USB quirk "
592 				    "module has not been loaded.\n");
593 			}
594 			break;
595 		}
596 		if (strcmp(q.quirkname, "UQ_NONE"))
597 			printf("%s\n", q.quirkname);
598 	}
599 	printf("\n");
600 }
601 
602 void
dump_be_dev_quirks(struct libusb20_backend * pbe)603 dump_be_dev_quirks(struct libusb20_backend *pbe)
604 {
605 	struct libusb20_quirk q;
606 	uint16_t x;
607 	int error;
608 
609 	memset(&q, 0, sizeof(q));
610 
611 	printf("\nDumping current device quirks:\n\n");
612 
613 	for (x = 0; x != 0xFFFF; x++) {
614 
615 		error = libusb20_be_get_dev_quirk(pbe, x, &q);
616 		if (error) {
617 			if (x == 0) {
618 				printf("No device quirks - maybe the USB quirk "
619 				    "module has not been loaded.\n");
620 			}
621 			break;
622 		}
623 		if (strcmp(q.quirkname, "UQ_NONE")) {
624 			printf("VID=0x%04x PID=0x%04x REVLO=0x%04x "
625 			    "REVHI=0x%04x QUIRK=%s\n",
626 			    q.vid, q.pid, q.bcdDeviceLow,
627 			    q.bcdDeviceHigh, q.quirkname);
628 		}
629 	}
630 	printf("\n");
631 }
632 
633 void
dump_device_desc(struct libusb20_device * pdev,bool list_mode)634 dump_device_desc(struct libusb20_device *pdev, bool list_mode)
635 {
636 	struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
637 
638 	ddesc = libusb20_dev_get_device_desc(pdev);
639 	if (list_mode) {
640 		char *desc;
641 
642 		LIBUSB20_DEVICE_DESC(DUMP0L, ddesc);
643 		desc = _device_desc(pdev, _DEVICE_DESCR_LIST_TYPE_PRODUCT_VENDOR);
644 		printf("%s\n", (desc != NULL) ? desc : "");
645 		free(desc);
646 	} else {
647 		LIBUSB20_DEVICE_DESC(DUMP0, ddesc);
648 	}
649 }
650 
651 void
dump_config(struct libusb20_device * pdev,uint8_t all_cfg)652 dump_config(struct libusb20_device *pdev, uint8_t all_cfg)
653 {
654 	struct LIBUSB20_CONFIG_DESC_DECODED *cdesc;
655 	struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
656 	struct libusb20_config *pcfg = NULL;
657 	uint8_t cfg_index;
658 	uint8_t cfg_index_end;
659 	uint8_t x;
660 	uint8_t y;
661 
662 	ddesc = libusb20_dev_get_device_desc(pdev);
663 
664 	if (all_cfg) {
665 		cfg_index = 0;
666 		cfg_index_end = ddesc->bNumConfigurations;
667 	} else {
668 		cfg_index = libusb20_dev_get_config_index(pdev);
669 		cfg_index_end = cfg_index + 1;
670 	}
671 
672 	for (; cfg_index != cfg_index_end; cfg_index++) {
673 
674 		pcfg = libusb20_dev_alloc_config(pdev, cfg_index);
675 		if (!pcfg) {
676 			continue;
677 		}
678 		printf("\n Configuration index %u\n\n", cfg_index);
679 		cdesc = &(pcfg->desc);
680 		LIBUSB20_CONFIG_DESC(DUMP1, cdesc);
681 		dump_extra(&(pcfg->extra), "  " "  ");
682 
683 		for (x = 0; x != pcfg->num_interface; x++) {
684 			printf("\n    Interface %u\n", x);
685 			dump_iface(pdev, pcfg->interface + x);
686 			printf("\n");
687 			for (y = 0; y != (pcfg->interface + x)->num_altsetting; y++) {
688 				printf("\n    Interface %u Alt %u\n", x, y + 1);
689 				dump_iface(pdev,
690 				    (pcfg->interface + x)->altsetting + y);
691 				printf("\n");
692 			}
693 		}
694 		printf("\n");
695 		free(pcfg);
696 	}
697 }
698 
699 void
dump_string_by_index(struct libusb20_device * pdev,uint8_t str_index)700 dump_string_by_index(struct libusb20_device *pdev, uint8_t str_index)
701 {
702 	char *pbuf;
703 	uint8_t n;
704 	uint8_t len;
705 
706 	pbuf = malloc(256);
707 	if (pbuf == NULL)
708 		err(1, "out of memory");
709 
710 	if (str_index == 0) {
711 		/* language table */
712 		if (libusb20_dev_req_string_sync(pdev,
713 		    str_index, 0, pbuf, 256)) {
714 			printf("STRING_0x%02x = <read error>\n", str_index);
715 		} else {
716 			printf("STRING_0x%02x = ", str_index);
717 			len = (uint8_t)pbuf[0];
718 			for (n = 0; n != len; n++) {
719 				printf("0x%02x%s", (uint8_t)pbuf[n],
720 				    (n != (len - 1)) ? ", " : "");
721 			}
722 			printf("\n");
723 		}
724 	} else {
725 		/* ordinary string */
726 		if (libusb20_dev_req_string_simple_sync(pdev,
727 		    str_index, pbuf, 256)) {
728 			printf("STRING_0x%02x = <read error>\n", str_index);
729 		} else {
730 			printf("STRING_0x%02x = <%s>\n", str_index, pbuf);
731 		}
732 	}
733 	free(pbuf);
734 }
735 
736 void
dump_device_stats(struct libusb20_device * pdev)737 dump_device_stats(struct libusb20_device *pdev)
738 {
739 	struct libusb20_device_stats st;
740 
741 	if (libusb20_dev_get_stats(pdev, &st)) {
742 		printf("{}\n");
743 	} else {
744 		printf("{\n"
745 		    "    UE_CONTROL_OK       : %llu\n"
746 		    "    UE_ISOCHRONOUS_OK   : %llu\n"
747 		    "    UE_BULK_OK          : %llu\n"
748 		    "    UE_INTERRUPT_OK     : %llu\n"
749 		    "    UE_CONTROL_FAIL     : %llu\n"
750 		    "    UE_ISOCHRONOUS_FAIL : %llu\n"
751 		    "    UE_BULK_FAIL        : %llu\n"
752 		    "    UE_INTERRUPT_FAIL   : %llu\n"
753 		    "}\n",
754 		    (unsigned long long)st.xfer_ok[0],
755 		    (unsigned long long)st.xfer_ok[1],
756 		    (unsigned long long)st.xfer_ok[2],
757 	            (unsigned long long)st.xfer_ok[3],
758 		    (unsigned long long)st.xfer_fail[0],
759 		    (unsigned long long)st.xfer_fail[1],
760 		    (unsigned long long)st.xfer_fail[2],
761 		    (unsigned long long)st.xfer_fail[3]);
762 	}
763 }
764