xref: /freebsd/usr.sbin/usbconfig/dump.c (revision c66308d7bcc3931b60a096bfe6ba0f50105e77de)
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 	default:
104 		break;
105 	}
106 	return ("UNKNOWN ()");
107 }
108 
109 const char *
dump_power_mode(uint8_t value)110 dump_power_mode(uint8_t value)
111 {
112 	;				/* style fix */
113 	switch (value) {
114 	case LIBUSB20_POWER_OFF:
115 		return ("OFF");
116 	case LIBUSB20_POWER_ON:
117 		return ("ON");
118 	case LIBUSB20_POWER_SAVE:
119 		return ("SAVE");
120 	case LIBUSB20_POWER_SUSPEND:
121 		return ("SUSPEND");
122 	case LIBUSB20_POWER_RESUME:
123 		return ("RESUME");
124 	default:
125 		return ("UNKNOWN");
126 	}
127 }
128 
129 static void
_dump_field(struct libusb20_device * pdev,const char * plevel,const char * field,uint32_t value,bool list_mode)130 _dump_field(struct libusb20_device *pdev, const char *plevel,
131     const char *field, uint32_t value, bool list_mode)
132 {
133 	uint8_t temp_string[256];
134 
135 	if (list_mode) {
136 		/* Skip fields we are not interested in. */
137 		if (strcmp(field, "bLength") == 0 ||
138 		    strcmp(field, "bDescriptorType") == 0 ||
139 		    strcmp(field, "bMaxPacketSize0") == 0)
140 			return;
141 
142 		printf("%s=%#06x ", field, value);
143 		return;
144 	}
145 	printf("%s%s = 0x%04x ", plevel, field, value);
146 
147 	if (strlen(plevel) == 8) {
148 		/* Endpoint Descriptor */
149 
150 		if (strcmp(field, "bEndpointAddress") == 0) {
151 			if (value & 0x80)
152 				printf(" <IN>\n");
153 			else
154 				printf(" <OUT>\n");
155 			return;
156 		}
157 		if (strcmp(field, "bmAttributes") == 0) {
158 			switch (value & 0x03) {
159 			case 0:
160 				printf(" <CONTROL>\n");
161 				break;
162 			case 1:
163 				switch (value & 0x0C) {
164 				case 0x00:
165 					printf(" <ISOCHRONOUS>\n");
166 					break;
167 				case 0x04:
168 					printf(" <ASYNC-ISOCHRONOUS>\n");
169 					break;
170 				case 0x08:
171 					printf(" <ADAPT-ISOCHRONOUS>\n");
172 					break;
173 				default:
174 					printf(" <SYNC-ISOCHRONOUS>\n");
175 					break;
176 				}
177 				break;
178 			case 2:
179 				printf(" <BULK>\n");
180 				break;
181 			default:
182 				printf(" <INTERRUPT>\n");
183 				break;
184 			}
185 			return;
186 		}
187 	}
188 	if ((field[0] == 'i') && (field[1] != 'd')) {
189 		/* Indirect String Descriptor */
190 		if (value == 0) {
191 			printf(" <no string>\n");
192 			return;
193 		}
194 		if (libusb20_dev_req_string_simple_sync(pdev, value,
195 		    temp_string, sizeof(temp_string))) {
196 			printf(" <retrieving string failed>\n");
197 			return;
198 		}
199 		printf(" <%s>\n", temp_string);
200 		return;
201 	}
202 	if (strlen(plevel) == 2 || strlen(plevel) == 6) {
203 
204 		/* Device and Interface Descriptor class codes */
205 
206 		if (strcmp(field, "bInterfaceClass") == 0 ||
207 		    strcmp(field, "bDeviceClass") == 0) {
208 
209 			switch (value) {
210 			case 0x00:
211 				printf(" <Probed by interface class>\n");
212 				break;
213 			case 0x01:
214 				printf(" <Audio device>\n");
215 				break;
216 			case 0x02:
217 				printf(" <Communication device>\n");
218 				break;
219 			case 0x03:
220 				printf(" <HID device>\n");
221 				break;
222 			case 0x05:
223 				printf(" <Physical device>\n");
224 				break;
225 			case 0x06:
226 				printf(" <Still imaging>\n");
227 				break;
228 			case 0x07:
229 				printf(" <Printer device>\n");
230 				break;
231 			case 0x08:
232 				printf(" <Mass storage>\n");
233 				break;
234 			case 0x09:
235 				printf(" <HUB>\n");
236 				break;
237 			case 0x0A:
238 				printf(" <CDC-data>\n");
239 				break;
240 			case 0x0B:
241 				printf(" <Smart card>\n");
242 				break;
243 			case 0x0D:
244 				printf(" <Content security>\n");
245 				break;
246 			case 0x0E:
247 				printf(" <Video device>\n");
248 				break;
249 			case 0x0F:
250 				printf(" <Personal healthcare>\n");
251 				break;
252 			case 0x10:
253 				printf(" <Audio and video device>\n");
254 				break;
255 			case 0x11:
256 				printf(" <Billboard device>\n");
257 				break;
258 			case 0xDC:
259 				printf(" <Diagnostic device>\n");
260 				break;
261 			case 0xE0:
262 				printf(" <Wireless controller>\n");
263 				break;
264 			case 0xEF:
265 				printf(" <Miscellaneous device>\n");
266 				break;
267 			case 0xFE:
268 				printf(" <Application specific>\n");
269 				break;
270 			case 0xFF:
271 				printf(" <Vendor specific>\n");
272 				break;
273 			default:
274 				printf(" <Unknown>\n");
275 				break;
276 			}
277 			return;
278 		}
279 	}
280 	/* No additional information */
281 	printf("\n");
282 }
283 
284 static void
dump_field(struct libusb20_device * pdev,const char * plevel,const char * field,uint32_t value)285 dump_field(struct libusb20_device *pdev, const char *plevel,
286     const char *field, uint32_t value)
287 {
288 	_dump_field(pdev, plevel, field, value, false);
289 }
290 
291 static void
dump_fieldl(struct libusb20_device * pdev,const char * plevel,const char * field,uint32_t value)292 dump_fieldl(struct libusb20_device *pdev, const char *plevel,
293     const char *field, uint32_t value)
294 {
295 	_dump_field(pdev, plevel, field, value, true);
296 }
297 
298 static void
dump_extra(struct libusb20_me_struct * str,const char * plevel)299 dump_extra(struct libusb20_me_struct *str, const char *plevel)
300 {
301 	const uint8_t *ptr;
302 	uint8_t x;
303 
304 	ptr = NULL;
305 
306 	while ((ptr = libusb20_desc_foreach(str, ptr))) {
307 		printf("\n" "%sAdditional Descriptor\n\n", plevel);
308 		printf("%sbLength = 0x%02x\n", plevel, ptr[0]);
309 		printf("%sbDescriptorType = 0x%02x\n", plevel, ptr[1]);
310 		if (ptr[0] > 1)
311 			printf("%sbDescriptorSubType = 0x%02x\n",
312 			    plevel, ptr[2]);
313 		printf("%s RAW dump: ", plevel);
314 		for (x = 0; x != ptr[0]; x++) {
315 			if ((x % 8) == 0) {
316 				printf("\n%s 0x%02x | ", plevel, x);
317 			}
318 			printf("0x%02x%s", ptr[x],
319 			    (x != (ptr[0] - 1)) ? ", " : (x % 8) ? "\n" : "");
320 		}
321 		printf("\n");
322 	}
323 }
324 
325 static void
dump_endpoint(struct libusb20_device * pdev,struct libusb20_endpoint * ep)326 dump_endpoint(struct libusb20_device *pdev,
327     struct libusb20_endpoint *ep)
328 {
329 	struct LIBUSB20_ENDPOINT_DESC_DECODED *edesc;
330 
331 	edesc = &ep->desc;
332 	LIBUSB20_ENDPOINT_DESC(DUMP3, edesc);
333 	dump_extra(&ep->extra, "  " "  " "  ");
334 }
335 
336 static void
dump_iface(struct libusb20_device * pdev,struct libusb20_interface * iface)337 dump_iface(struct libusb20_device *pdev,
338     struct libusb20_interface *iface)
339 {
340 	struct LIBUSB20_INTERFACE_DESC_DECODED *idesc;
341 	uint8_t z;
342 
343 	idesc = &iface->desc;
344 	LIBUSB20_INTERFACE_DESC(DUMP2, idesc);
345 	dump_extra(&iface->extra, "  " "  " "  ");
346 
347 	for (z = 0; z != iface->num_endpoints; z++) {
348 		printf("\n     Endpoint %u\n", z);
349 		dump_endpoint(pdev, iface->endpoints + z);
350 	}
351 }
352 
353 static struct usb_vendors *
load_vendors(void)354 load_vendors(void)
355 {
356 	const char *dbf;
357 	FILE *db = NULL;
358 	struct usb_vendor_info *cv;
359 	struct usb_product_info *cd;
360 	struct usb_vendors *usb_vendors;
361 	char buf[1024], str[1024];
362 	char *ch;
363 	int id;
364 
365 	usb_vendors = malloc(sizeof(*usb_vendors));
366 	if (usb_vendors == NULL)
367 		err(1, "out of memory");
368 	STAILQ_INIT(usb_vendors);
369 	if ((dbf = getenv("USB_VENDOR_DATABASE")) != NULL)
370 		db = fopen(dbf, "r");
371 	if (db == NULL) {
372 		dbf = _PATH_LUSBVDB;
373 		if ((db = fopen(dbf, "r")) == NULL) {
374 			dbf = _PATH_USBVDB;
375 			if ((db = fopen(dbf, "r")) == NULL)
376 				return (usb_vendors);
377 		}
378 	}
379 	cv = NULL;
380 	cd = NULL;
381 
382 	for (;;) {
383 		if (fgets(buf, sizeof(buf), db) == NULL)
384 			break;
385 
386 		if ((ch = strchr(buf, '#')) != NULL)
387 			*ch = '\0';
388 		if (ch == buf)
389 			continue;
390 		ch = strchr(buf, '\0') - 1;
391 		while (ch > buf && isspace(*ch))
392 			*ch-- = '\0';
393 		if (ch <= buf)
394 			continue;
395 
396 		/* Can't handle subvendor / subdevice entries yet */
397 		if (buf[0] == '\t' && buf[1] == '\t')
398 			continue;
399 
400 		/* Check for vendor entry */
401 		if (buf[0] != '\t' && sscanf(buf, "%04x %[^\n]", &id, str) == 2) {
402 			if ((id == 0) || (strlen(str) < 1))
403 				continue;
404 			if ((cv = malloc(sizeof(struct usb_vendor_info))) == NULL)
405 				err(1, "out of memory");
406 			if ((cv->desc = strdup(str)) == NULL)
407 				err(1, "out of memory");
408 			cv->id = id;
409 			STAILQ_INIT(&cv->devs);
410 			STAILQ_INSERT_TAIL(usb_vendors, cv, link);
411 			continue;
412 		}
413 
414 		/* Check for device entry */
415 		if (buf[0] == '\t' && sscanf(buf + 1, "%04x %[^\n]", &id, str) == 2) {
416 			if ((id == 0) || (strlen(str) < 1))
417 				continue;
418 			if (cv == NULL)
419 				continue;
420 			if ((cd = malloc(sizeof(struct usb_product_info))) == NULL)
421 				err(1, "out of memory");
422 			if ((cd->desc = strdup(str)) == NULL)
423 				err(1, "out of memory");
424 			cd->id = id;
425 			STAILQ_INSERT_TAIL(&cv->devs, cd, link);
426 			continue;
427 		}
428 	}
429 	if (ferror(db))
430 		err(1, "error reading the usb id db");
431 
432 	fclose(db);
433 	/* cleanup */
434 	return (usb_vendors);
435 }
436 
437 enum _device_descr_list_type {
438 	_DEVICE_DESCR_LIST_TYPE_DEFAULT		= 0,
439 	_DEVICE_DESCR_LIST_TYPE_UGEN		= 1,
440 	_DEVICE_DESCR_LIST_TYPE_PRODUCT_VENDOR	= 2,
441 };
442 
443 static char *
_device_desc(struct libusb20_device * pdev,enum _device_descr_list_type list_type)444 _device_desc(struct libusb20_device *pdev,
445     enum _device_descr_list_type list_type)
446 {
447 	static struct usb_vendors *usb_vendors = NULL;
448 	char *desc = NULL;
449 	const char *vendor = NULL, *product = NULL;
450 	uint16_t vid;
451 	uint16_t pid;
452 	struct usb_vendor_info *vi;
453 	struct usb_product_info *pi;
454 	struct usb_device_info devinfo;
455 
456 	if (list_type == _DEVICE_DESCR_LIST_TYPE_UGEN) {
457 		asprintf(&desc, "ugen%u.%u",
458 				libusb20_dev_get_bus_number(pdev),
459 				libusb20_dev_get_address(pdev));
460 		return (desc);
461 	}
462 
463 	vid = libusb20_dev_get_device_desc(pdev)->idVendor;
464 	pid = libusb20_dev_get_device_desc(pdev)->idProduct;
465 
466 	if (usb_vendors == NULL)
467 		usb_vendors = load_vendors();
468 
469 	STAILQ_FOREACH(vi, usb_vendors, link) {
470 		if (vi->id == vid) {
471 			vendor = vi->desc;
472 			break;
473 		}
474 	}
475 	if (vi != NULL) {
476 		STAILQ_FOREACH(pi, &vi->devs, link) {
477 			if (pi->id == pid) {
478 				product = pi->desc;
479 				break;
480 			}
481 		}
482 	}
483 
484 	/*
485 	 * Try to gather the information; libusb2 unfortunately seems to
486 	 * only build an entire string but not save vendor/product individually.
487 	 */
488 	if (vendor == NULL || product == NULL) {
489 		char buf[64];
490 		int f;
491 
492 		snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
493 		    libusb20_dev_get_bus_number(pdev),
494 		    libusb20_dev_get_address(pdev));
495 
496 		f = open(buf, O_RDWR);
497 		if (f < 0)
498 			goto skip_vp_recovery;
499 
500 		if (ioctl(f, IOUSB(USB_GET_DEVICEINFO), &devinfo))
501 			goto skip_vp_recovery;
502 
503 
504 		if (vendor == NULL)
505 			vendor = devinfo.udi_vendor;
506 		if (product == NULL)
507 			product = devinfo.udi_product;
508 
509 skip_vp_recovery:
510 		if (f >= 0)
511 			close(f);
512 	}
513 
514 	if (list_type == _DEVICE_DESCR_LIST_TYPE_PRODUCT_VENDOR) {
515 		asprintf(&desc, "vendor='%s' product='%s'",
516 		    (vendor != NULL) ? vendor : "",
517 		    (product != NULL) ? product : "");
518 		return (desc);
519 	}
520 
521 	if (vendor == NULL || product == NULL)
522 		return (NULL);
523 
524 	asprintf(&desc, "ugen%u.%u: <%s %s> at usbus%u",
525 			libusb20_dev_get_bus_number(pdev),
526 			libusb20_dev_get_address(pdev),
527 			product, vendor,
528 			libusb20_dev_get_bus_number(pdev));
529 
530 	return (desc);
531 }
532 
533 void
dump_device_info(struct libusb20_device * pdev,uint8_t show_ifdrv,bool list_mode)534 dump_device_info(struct libusb20_device *pdev, uint8_t show_ifdrv,
535     bool list_mode)
536 {
537 	char buf[128];
538 	uint8_t n;
539 	unsigned int usage;
540 	char *desc;
541 
542 	usage = libusb20_dev_get_power_usage(pdev);
543 
544 	desc = _device_desc(pdev, (list_mode) ? _DEVICE_DESCR_LIST_TYPE_UGEN :
545 	    _DEVICE_DESCR_LIST_TYPE_DEFAULT);
546 
547 	if (list_mode)
548 		printf("%s: ", desc);
549 	else
550 		printf("%s, cfg=%u md=%s spd=%s pwr=%s (%umA)\n",
551 		    desc ? desc : libusb20_dev_get_desc(pdev),
552 		    libusb20_dev_get_config_index(pdev),
553 		    dump_mode(libusb20_dev_get_mode(pdev)),
554 		    dump_speed(libusb20_dev_get_speed(pdev)),
555 		    dump_power_mode(libusb20_dev_get_power_mode(pdev)),
556 		    usage);
557 	free(desc);
558 
559 	if (list_mode || !show_ifdrv)
560 		return;
561 
562 	for (n = 0; n != 255; n++) {
563 		if (libusb20_dev_get_iface_desc(pdev, n, buf, sizeof(buf)))
564 			break;
565 		if (buf[0] == 0)
566 			continue;
567 		printf("ugen%u.%u.%u: %s\n",
568 		    libusb20_dev_get_bus_number(pdev),
569 		    libusb20_dev_get_address(pdev), n, buf);
570 	}
571 }
572 
573 void
dump_be_quirk_names(struct libusb20_backend * pbe)574 dump_be_quirk_names(struct libusb20_backend *pbe)
575 {
576 	struct libusb20_quirk q;
577 	uint16_t x;
578 	int error;
579 
580 	memset(&q, 0, sizeof(q));
581 
582 	printf("\nDumping list of supported quirks:\n\n");
583 
584 	for (x = 0; x != 0xFFFF; x++) {
585 
586 		error = libusb20_be_get_quirk_name(pbe, x, &q);
587 		if (error) {
588 			if (x == 0) {
589 				printf("No quirk names - maybe the USB quirk "
590 				    "module has not been loaded.\n");
591 			}
592 			break;
593 		}
594 		if (strcmp(q.quirkname, "UQ_NONE"))
595 			printf("%s\n", q.quirkname);
596 	}
597 	printf("\n");
598 }
599 
600 void
dump_be_dev_quirks(struct libusb20_backend * pbe)601 dump_be_dev_quirks(struct libusb20_backend *pbe)
602 {
603 	struct libusb20_quirk q;
604 	uint16_t x;
605 	int error;
606 
607 	memset(&q, 0, sizeof(q));
608 
609 	printf("\nDumping current device quirks:\n\n");
610 
611 	for (x = 0; x != 0xFFFF; x++) {
612 
613 		error = libusb20_be_get_dev_quirk(pbe, x, &q);
614 		if (error) {
615 			if (x == 0) {
616 				printf("No device quirks - maybe the USB quirk "
617 				    "module has not been loaded.\n");
618 			}
619 			break;
620 		}
621 		if (strcmp(q.quirkname, "UQ_NONE")) {
622 			printf("VID=0x%04x PID=0x%04x REVLO=0x%04x "
623 			    "REVHI=0x%04x QUIRK=%s\n",
624 			    q.vid, q.pid, q.bcdDeviceLow,
625 			    q.bcdDeviceHigh, q.quirkname);
626 		}
627 	}
628 	printf("\n");
629 }
630 
631 void
dump_device_desc(struct libusb20_device * pdev,bool list_mode)632 dump_device_desc(struct libusb20_device *pdev, bool list_mode)
633 {
634 	struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
635 
636 	ddesc = libusb20_dev_get_device_desc(pdev);
637 	if (list_mode) {
638 		char *desc;
639 
640 		LIBUSB20_DEVICE_DESC(DUMP0L, ddesc);
641 		desc = _device_desc(pdev, _DEVICE_DESCR_LIST_TYPE_PRODUCT_VENDOR);
642 		printf("%s\n", (desc != NULL) ? desc : "");
643 		free(desc);
644 	} else {
645 		LIBUSB20_DEVICE_DESC(DUMP0, ddesc);
646 	}
647 }
648 
649 void
dump_config(struct libusb20_device * pdev,uint8_t all_cfg)650 dump_config(struct libusb20_device *pdev, uint8_t all_cfg)
651 {
652 	struct LIBUSB20_CONFIG_DESC_DECODED *cdesc;
653 	struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
654 	struct libusb20_config *pcfg = NULL;
655 	uint8_t cfg_index;
656 	uint8_t cfg_index_end;
657 	uint8_t x;
658 	uint8_t y;
659 
660 	ddesc = libusb20_dev_get_device_desc(pdev);
661 
662 	if (all_cfg) {
663 		cfg_index = 0;
664 		cfg_index_end = ddesc->bNumConfigurations;
665 	} else {
666 		cfg_index = libusb20_dev_get_config_index(pdev);
667 		cfg_index_end = cfg_index + 1;
668 	}
669 
670 	for (; cfg_index != cfg_index_end; cfg_index++) {
671 
672 		pcfg = libusb20_dev_alloc_config(pdev, cfg_index);
673 		if (!pcfg) {
674 			continue;
675 		}
676 		printf("\n Configuration index %u\n\n", cfg_index);
677 		cdesc = &(pcfg->desc);
678 		LIBUSB20_CONFIG_DESC(DUMP1, cdesc);
679 		dump_extra(&(pcfg->extra), "  " "  ");
680 
681 		for (x = 0; x != pcfg->num_interface; x++) {
682 			printf("\n    Interface %u\n", x);
683 			dump_iface(pdev, pcfg->interface + x);
684 			printf("\n");
685 			for (y = 0; y != (pcfg->interface + x)->num_altsetting; y++) {
686 				printf("\n    Interface %u Alt %u\n", x, y + 1);
687 				dump_iface(pdev,
688 				    (pcfg->interface + x)->altsetting + y);
689 				printf("\n");
690 			}
691 		}
692 		printf("\n");
693 		free(pcfg);
694 	}
695 }
696 
697 void
dump_string_by_index(struct libusb20_device * pdev,uint8_t str_index)698 dump_string_by_index(struct libusb20_device *pdev, uint8_t str_index)
699 {
700 	char *pbuf;
701 	uint8_t n;
702 	uint8_t len;
703 
704 	pbuf = malloc(256);
705 	if (pbuf == NULL)
706 		err(1, "out of memory");
707 
708 	if (str_index == 0) {
709 		/* language table */
710 		if (libusb20_dev_req_string_sync(pdev,
711 		    str_index, 0, pbuf, 256)) {
712 			printf("STRING_0x%02x = <read error>\n", str_index);
713 		} else {
714 			printf("STRING_0x%02x = ", str_index);
715 			len = (uint8_t)pbuf[0];
716 			for (n = 0; n != len; n++) {
717 				printf("0x%02x%s", (uint8_t)pbuf[n],
718 				    (n != (len - 1)) ? ", " : "");
719 			}
720 			printf("\n");
721 		}
722 	} else {
723 		/* ordinary string */
724 		if (libusb20_dev_req_string_simple_sync(pdev,
725 		    str_index, pbuf, 256)) {
726 			printf("STRING_0x%02x = <read error>\n", str_index);
727 		} else {
728 			printf("STRING_0x%02x = <%s>\n", str_index, pbuf);
729 		}
730 	}
731 	free(pbuf);
732 }
733 
734 void
dump_device_stats(struct libusb20_device * pdev)735 dump_device_stats(struct libusb20_device *pdev)
736 {
737 	struct libusb20_device_stats st;
738 
739 	if (libusb20_dev_get_stats(pdev, &st)) {
740 		printf("{}\n");
741 	} else {
742 		printf("{\n"
743 		    "    UE_CONTROL_OK       : %llu\n"
744 		    "    UE_ISOCHRONOUS_OK   : %llu\n"
745 		    "    UE_BULK_OK          : %llu\n"
746 		    "    UE_INTERRUPT_OK     : %llu\n"
747 		    "    UE_CONTROL_FAIL     : %llu\n"
748 		    "    UE_ISOCHRONOUS_FAIL : %llu\n"
749 		    "    UE_BULK_FAIL        : %llu\n"
750 		    "    UE_INTERRUPT_FAIL   : %llu\n"
751 		    "}\n",
752 		    (unsigned long long)st.xfer_ok[0],
753 		    (unsigned long long)st.xfer_ok[1],
754 		    (unsigned long long)st.xfer_ok[2],
755 	            (unsigned long long)st.xfer_ok[3],
756 		    (unsigned long long)st.xfer_fail[0],
757 		    (unsigned long long)st.xfer_fail[1],
758 		    (unsigned long long)st.xfer_fail[2],
759 		    (unsigned long long)st.xfer_fail[3]);
760 	}
761 }
762