xref: /freebsd/usr.sbin/usbconfig/dump.c (revision aa1a8ff2d6dbc51ef058f46f3db5a8bb77967145)
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  *
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 <sys/queue.h>
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stdint.h>
34 #include <err.h>
35 #include <string.h>
36 #include <pwd.h>
37 #include <grp.h>
38 #include <ctype.h>
39 
40 #include <libusb20.h>
41 #include <libusb20_desc.h>
42 
43 #include "dump.h"
44 #include "pathnames.h"
45 
46 #define	DUMP0(n,type,field,...) dump_field(pdev, "  ", #field, n->field);
47 #define	DUMP1(n,type,field,...) dump_field(pdev, "    ", #field, n->field);
48 #define	DUMP2(n,type,field,...) dump_field(pdev, "      ", #field, n->field);
49 #define	DUMP3(n,type,field,...) dump_field(pdev, "        ", #field, n->field);
50 
51 struct usb_product_info {
52 	STAILQ_ENTRY(usb_product_info)	link;
53 	int				id;
54 	char				*desc;
55 };
56 
57 struct usb_vendor_info {
58 	STAILQ_ENTRY(usb_vendor_info)	link;
59 	STAILQ_HEAD(,usb_product_info)	devs;
60 	int				id;
61 	char				*desc;
62 };
63 
64 STAILQ_HEAD(usb_vendors, usb_vendor_info);
65 
66 const char *
67 dump_mode(uint8_t value)
68 {
69 	if (value == LIBUSB20_MODE_HOST)
70 		return ("HOST");
71 	return ("DEVICE");
72 }
73 
74 const char *
75 dump_speed(uint8_t value)
76 {
77 	;				/* style fix */
78 	switch (value) {
79 	case LIBUSB20_SPEED_LOW:
80 		return ("LOW (1.5Mbps)");
81 	case LIBUSB20_SPEED_FULL:
82 		return ("FULL (12Mbps)");
83 	case LIBUSB20_SPEED_HIGH:
84 		return ("HIGH (480Mbps)");
85 	case LIBUSB20_SPEED_VARIABLE:
86 		return ("VARIABLE (52-480Mbps)");
87 	case LIBUSB20_SPEED_SUPER:
88 		return ("SUPER (5.0Gbps)");
89 	default:
90 		break;
91 	}
92 	return ("UNKNOWN ()");
93 }
94 
95 const char *
96 dump_power_mode(uint8_t value)
97 {
98 	;				/* style fix */
99 	switch (value) {
100 	case LIBUSB20_POWER_OFF:
101 		return ("OFF");
102 	case LIBUSB20_POWER_ON:
103 		return ("ON");
104 	case LIBUSB20_POWER_SAVE:
105 		return ("SAVE");
106 	case LIBUSB20_POWER_SUSPEND:
107 		return ("SUSPEND");
108 	case LIBUSB20_POWER_RESUME:
109 		return ("RESUME");
110 	default:
111 		return ("UNKNOWN");
112 	}
113 }
114 
115 static void
116 dump_field(struct libusb20_device *pdev, const char *plevel,
117     const char *field, uint32_t value)
118 {
119 	uint8_t temp_string[256];
120 
121 	printf("%s%s = 0x%04x ", plevel, field, value);
122 
123 	if (strlen(plevel) == 8) {
124 		/* Endpoint Descriptor */
125 
126 		if (strcmp(field, "bEndpointAddress") == 0) {
127 			if (value & 0x80)
128 				printf(" <IN>\n");
129 			else
130 				printf(" <OUT>\n");
131 			return;
132 		}
133 		if (strcmp(field, "bmAttributes") == 0) {
134 			switch (value & 0x03) {
135 			case 0:
136 				printf(" <CONTROL>\n");
137 				break;
138 			case 1:
139 				switch (value & 0x0C) {
140 				case 0x00:
141 					printf(" <ISOCHRONOUS>\n");
142 					break;
143 				case 0x04:
144 					printf(" <ASYNC-ISOCHRONOUS>\n");
145 					break;
146 				case 0x08:
147 					printf(" <ADAPT-ISOCHRONOUS>\n");
148 					break;
149 				default:
150 					printf(" <SYNC-ISOCHRONOUS>\n");
151 					break;
152 				}
153 				break;
154 			case 2:
155 				printf(" <BULK>\n");
156 				break;
157 			default:
158 				printf(" <INTERRUPT>\n");
159 				break;
160 			}
161 			return;
162 		}
163 	}
164 	if ((field[0] == 'i') && (field[1] != 'd')) {
165 		/* Indirect String Descriptor */
166 		if (value == 0) {
167 			printf(" <no string>\n");
168 			return;
169 		}
170 		if (libusb20_dev_req_string_simple_sync(pdev, value,
171 		    temp_string, sizeof(temp_string))) {
172 			printf(" <retrieving string failed>\n");
173 			return;
174 		}
175 		printf(" <%s>\n", temp_string);
176 		return;
177 	}
178 	if (strlen(plevel) == 2 || strlen(plevel) == 6) {
179 
180 		/* Device and Interface Descriptor class codes */
181 
182 		if (strcmp(field, "bInterfaceClass") == 0 ||
183 		    strcmp(field, "bDeviceClass") == 0) {
184 
185 			switch (value) {
186 			case 0x00:
187 				printf(" <Probed by interface class>\n");
188 				break;
189 			case 0x01:
190 				printf(" <Audio device>\n");
191 				break;
192 			case 0x02:
193 				printf(" <Communication device>\n");
194 				break;
195 			case 0x03:
196 				printf(" <HID device>\n");
197 				break;
198 			case 0x05:
199 				printf(" <Physical device>\n");
200 				break;
201 			case 0x06:
202 				printf(" <Still imaging>\n");
203 				break;
204 			case 0x07:
205 				printf(" <Printer device>\n");
206 				break;
207 			case 0x08:
208 				printf(" <Mass storage>\n");
209 				break;
210 			case 0x09:
211 				printf(" <HUB>\n");
212 				break;
213 			case 0x0A:
214 				printf(" <CDC-data>\n");
215 				break;
216 			case 0x0B:
217 				printf(" <Smart card>\n");
218 				break;
219 			case 0x0D:
220 				printf(" <Content security>\n");
221 				break;
222 			case 0x0E:
223 				printf(" <Video device>\n");
224 				break;
225 			case 0x0F:
226 				printf(" <Personal healthcare>\n");
227 				break;
228 			case 0x10:
229 				printf(" <Audio and video device>\n");
230 				break;
231 			case 0x11:
232 				printf(" <Billboard device>\n");
233 				break;
234 			case 0xDC:
235 				printf(" <Diagnostic device>\n");
236 				break;
237 			case 0xE0:
238 				printf(" <Wireless controller>\n");
239 				break;
240 			case 0xEF:
241 				printf(" <Miscellaneous device>\n");
242 				break;
243 			case 0xFE:
244 				printf(" <Application specific>\n");
245 				break;
246 			case 0xFF:
247 				printf(" <Vendor specific>\n");
248 				break;
249 			default:
250 				printf(" <Unknown>\n");
251 				break;
252 			}
253 			return;
254 		}
255 	}
256 	/* No additional information */
257 	printf("\n");
258 }
259 
260 static void
261 dump_extra(struct libusb20_me_struct *str, const char *plevel)
262 {
263 	const uint8_t *ptr;
264 	uint8_t x;
265 
266 	ptr = NULL;
267 
268 	while ((ptr = libusb20_desc_foreach(str, ptr))) {
269 		printf("\n" "%sAdditional Descriptor\n\n", plevel);
270 		printf("%sbLength = 0x%02x\n", plevel, ptr[0]);
271 		printf("%sbDescriptorType = 0x%02x\n", plevel, ptr[1]);
272 		if (ptr[0] > 1)
273 			printf("%sbDescriptorSubType = 0x%02x\n",
274 			    plevel, ptr[2]);
275 		printf("%s RAW dump: ", plevel);
276 		for (x = 0; x != ptr[0]; x++) {
277 			if ((x % 8) == 0) {
278 				printf("\n%s 0x%02x | ", plevel, x);
279 			}
280 			printf("0x%02x%s", ptr[x],
281 			    (x != (ptr[0] - 1)) ? ", " : (x % 8) ? "\n" : "");
282 		}
283 		printf("\n");
284 	}
285 }
286 
287 static void
288 dump_endpoint(struct libusb20_device *pdev,
289     struct libusb20_endpoint *ep)
290 {
291 	struct LIBUSB20_ENDPOINT_DESC_DECODED *edesc;
292 
293 	edesc = &ep->desc;
294 	LIBUSB20_ENDPOINT_DESC(DUMP3, edesc);
295 	dump_extra(&ep->extra, "  " "  " "  ");
296 }
297 
298 static void
299 dump_iface(struct libusb20_device *pdev,
300     struct libusb20_interface *iface)
301 {
302 	struct LIBUSB20_INTERFACE_DESC_DECODED *idesc;
303 	uint8_t z;
304 
305 	idesc = &iface->desc;
306 	LIBUSB20_INTERFACE_DESC(DUMP2, idesc);
307 	dump_extra(&iface->extra, "  " "  " "  ");
308 
309 	for (z = 0; z != iface->num_endpoints; z++) {
310 		printf("\n     Endpoint %u\n", z);
311 		dump_endpoint(pdev, iface->endpoints + z);
312 	}
313 }
314 
315 static struct usb_vendors *
316 load_vendors(void)
317 {
318 	const char *dbf;
319 	FILE *db = NULL;
320 	struct usb_vendor_info *cv;
321 	struct usb_product_info *cd;
322 	struct usb_vendors *usb_vendors;
323 	char buf[1024], str[1024];
324 	char *ch;
325 	int id;
326 
327 	usb_vendors = malloc(sizeof(*usb_vendors));
328 	if (usb_vendors == NULL)
329 		err(1, "out of memory");
330 	STAILQ_INIT(usb_vendors);
331 	if ((dbf = getenv("USB_VENDOR_DATABASE")) != NULL)
332 		db = fopen(dbf, "r");
333 	if (db == NULL) {
334 		dbf = _PATH_LUSBVDB;
335 		if ((db = fopen(dbf, "r")) == NULL) {
336 			dbf = _PATH_USBVDB;
337 			if ((db = fopen(dbf, "r")) == NULL)
338 				return (usb_vendors);
339 		}
340 	}
341 	cv = NULL;
342 	cd = NULL;
343 
344 	for (;;) {
345 		if (fgets(buf, sizeof(buf), db) == NULL)
346 			break;
347 
348 		if ((ch = strchr(buf, '#')) != NULL)
349 			*ch = '\0';
350 		if (ch == buf)
351 			continue;
352 		ch = strchr(buf, '\0') - 1;
353 		while (ch > buf && isspace(*ch))
354 			*ch-- = '\0';
355 		if (ch <= buf)
356 			continue;
357 
358 		/* Can't handle subvendor / subdevice entries yet */
359 		if (buf[0] == '\t' && buf[1] == '\t')
360 			continue;
361 
362 		/* Check for vendor entry */
363 		if (buf[0] != '\t' && sscanf(buf, "%04x %[^\n]", &id, str) == 2) {
364 			if ((id == 0) || (strlen(str) < 1))
365 				continue;
366 			if ((cv = malloc(sizeof(struct usb_vendor_info))) == NULL)
367 				err(1, "out of memory");
368 			if ((cv->desc = strdup(str)) == NULL)
369 				err(1, "out of memory");
370 			cv->id = id;
371 			STAILQ_INIT(&cv->devs);
372 			STAILQ_INSERT_TAIL(usb_vendors, cv, link);
373 			continue;
374 		}
375 
376 		/* Check for device entry */
377 		if (buf[0] == '\t' && sscanf(buf + 1, "%04x %[^\n]", &id, str) == 2) {
378 			if ((id == 0) || (strlen(str) < 1))
379 				continue;
380 			if (cv == NULL)
381 				continue;
382 			if ((cd = malloc(sizeof(struct usb_product_info))) == NULL)
383 				err(1, "out of memory");
384 			if ((cd->desc = strdup(str)) == NULL)
385 				err(1, "out of memory");
386 			cd->id = id;
387 			STAILQ_INSERT_TAIL(&cv->devs, cd, link);
388 			continue;
389 		}
390 	}
391 	if (ferror(db))
392 		err(1, "error reading the usb id db");
393 
394 	fclose(db);
395 	/* cleanup */
396 	return (usb_vendors);
397 }
398 
399 static char *
400 _device_desc(struct libusb20_device *pdev)
401 {
402 	static struct usb_vendors *usb_vendors = NULL;
403 	char *desc = NULL;
404 	const char *vendor = NULL, *product = NULL;
405 	uint16_t vid = libusb20_dev_get_device_desc(pdev)->idVendor;
406 	uint16_t pid = libusb20_dev_get_device_desc(pdev)->idProduct;
407 	struct usb_vendor_info *vi;
408 	struct usb_product_info *pi;
409 
410 	if (usb_vendors == NULL)
411 		usb_vendors = load_vendors();
412 
413 	STAILQ_FOREACH(vi, usb_vendors, link) {
414 		if (vi->id == vid) {
415 			vendor = vi->desc;
416 			break;
417 		}
418 	}
419 	if (vi != NULL) {
420 		STAILQ_FOREACH(pi, &vi->devs, link) {
421 			if (pi->id == pid) {
422 				product = pi->desc;
423 				break;
424 			}
425 		}
426 	}
427 	if (vendor == NULL || product == NULL)
428 		return (NULL);
429 
430 	asprintf(&desc, "ugen%u.%u: <%s %s> at usbus%u",
431 			libusb20_dev_get_bus_number(pdev),
432 			libusb20_dev_get_address(pdev),
433 			product, vendor,
434 			libusb20_dev_get_bus_number(pdev));
435 
436 
437 	return (desc);
438 }
439 
440 void
441 dump_device_info(struct libusb20_device *pdev, uint8_t show_ifdrv)
442 {
443 	char buf[128];
444 	uint8_t n;
445 	unsigned int usage;
446 	char *desc;
447 
448 	usage = libusb20_dev_get_power_usage(pdev);
449 
450 	desc = _device_desc(pdev);
451 
452 	printf("%s, cfg=%u md=%s spd=%s pwr=%s (%umA)\n",
453 	    desc ? desc : libusb20_dev_get_desc(pdev),
454 	    libusb20_dev_get_config_index(pdev),
455 	    dump_mode(libusb20_dev_get_mode(pdev)),
456 	    dump_speed(libusb20_dev_get_speed(pdev)),
457 	    dump_power_mode(libusb20_dev_get_power_mode(pdev)),
458 	    usage);
459 	free(desc);
460 
461 	if (!show_ifdrv)
462 		return;
463 
464 	for (n = 0; n != 255; n++) {
465 		if (libusb20_dev_get_iface_desc(pdev, n, buf, sizeof(buf)))
466 			break;
467 		if (buf[0] == 0)
468 			continue;
469 		printf("ugen%u.%u.%u: %s\n",
470 		    libusb20_dev_get_bus_number(pdev),
471 		    libusb20_dev_get_address(pdev), n, buf);
472 	}
473 }
474 
475 void
476 dump_be_quirk_names(struct libusb20_backend *pbe)
477 {
478 	struct libusb20_quirk q;
479 	uint16_t x;
480 	int error;
481 
482 	memset(&q, 0, sizeof(q));
483 
484 	printf("\nDumping list of supported quirks:\n\n");
485 
486 	for (x = 0; x != 0xFFFF; x++) {
487 
488 		error = libusb20_be_get_quirk_name(pbe, x, &q);
489 		if (error) {
490 			if (x == 0) {
491 				printf("No quirk names - maybe the USB quirk "
492 				    "module has not been loaded.\n");
493 			}
494 			break;
495 		}
496 		if (strcmp(q.quirkname, "UQ_NONE"))
497 			printf("%s\n", q.quirkname);
498 	}
499 	printf("\n");
500 }
501 
502 void
503 dump_be_dev_quirks(struct libusb20_backend *pbe)
504 {
505 	struct libusb20_quirk q;
506 	uint16_t x;
507 	int error;
508 
509 	memset(&q, 0, sizeof(q));
510 
511 	printf("\nDumping current device quirks:\n\n");
512 
513 	for (x = 0; x != 0xFFFF; x++) {
514 
515 		error = libusb20_be_get_dev_quirk(pbe, x, &q);
516 		if (error) {
517 			if (x == 0) {
518 				printf("No device quirks - maybe the USB quirk "
519 				    "module has not been loaded.\n");
520 			}
521 			break;
522 		}
523 		if (strcmp(q.quirkname, "UQ_NONE")) {
524 			printf("VID=0x%04x PID=0x%04x REVLO=0x%04x "
525 			    "REVHI=0x%04x QUIRK=%s\n",
526 			    q.vid, q.pid, q.bcdDeviceLow,
527 			    q.bcdDeviceHigh, q.quirkname);
528 		}
529 	}
530 	printf("\n");
531 }
532 
533 void
534 dump_device_desc(struct libusb20_device *pdev)
535 {
536 	struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
537 
538 	ddesc = libusb20_dev_get_device_desc(pdev);
539 	LIBUSB20_DEVICE_DESC(DUMP0, ddesc);
540 }
541 
542 void
543 dump_config(struct libusb20_device *pdev, uint8_t all_cfg)
544 {
545 	struct LIBUSB20_CONFIG_DESC_DECODED *cdesc;
546 	struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
547 	struct libusb20_config *pcfg = NULL;
548 	uint8_t cfg_index;
549 	uint8_t cfg_index_end;
550 	uint8_t x;
551 	uint8_t y;
552 
553 	ddesc = libusb20_dev_get_device_desc(pdev);
554 
555 	if (all_cfg) {
556 		cfg_index = 0;
557 		cfg_index_end = ddesc->bNumConfigurations;
558 	} else {
559 		cfg_index = libusb20_dev_get_config_index(pdev);
560 		cfg_index_end = cfg_index + 1;
561 	}
562 
563 	for (; cfg_index != cfg_index_end; cfg_index++) {
564 
565 		pcfg = libusb20_dev_alloc_config(pdev, cfg_index);
566 		if (!pcfg) {
567 			continue;
568 		}
569 		printf("\n Configuration index %u\n\n", cfg_index);
570 		cdesc = &(pcfg->desc);
571 		LIBUSB20_CONFIG_DESC(DUMP1, cdesc);
572 		dump_extra(&(pcfg->extra), "  " "  ");
573 
574 		for (x = 0; x != pcfg->num_interface; x++) {
575 			printf("\n    Interface %u\n", x);
576 			dump_iface(pdev, pcfg->interface + x);
577 			printf("\n");
578 			for (y = 0; y != (pcfg->interface + x)->num_altsetting; y++) {
579 				printf("\n    Interface %u Alt %u\n", x, y + 1);
580 				dump_iface(pdev,
581 				    (pcfg->interface + x)->altsetting + y);
582 				printf("\n");
583 			}
584 		}
585 		printf("\n");
586 		free(pcfg);
587 	}
588 }
589 
590 void
591 dump_string_by_index(struct libusb20_device *pdev, uint8_t str_index)
592 {
593 	char *pbuf;
594 	uint8_t n;
595 	uint8_t len;
596 
597 	pbuf = malloc(256);
598 	if (pbuf == NULL)
599 		err(1, "out of memory");
600 
601 	if (str_index == 0) {
602 		/* language table */
603 		if (libusb20_dev_req_string_sync(pdev,
604 		    str_index, 0, pbuf, 256)) {
605 			printf("STRING_0x%02x = <read error>\n", str_index);
606 		} else {
607 			printf("STRING_0x%02x = ", str_index);
608 			len = (uint8_t)pbuf[0];
609 			for (n = 0; n != len; n++) {
610 				printf("0x%02x%s", (uint8_t)pbuf[n],
611 				    (n != (len - 1)) ? ", " : "");
612 			}
613 			printf("\n");
614 		}
615 	} else {
616 		/* ordinary string */
617 		if (libusb20_dev_req_string_simple_sync(pdev,
618 		    str_index, pbuf, 256)) {
619 			printf("STRING_0x%02x = <read error>\n", str_index);
620 		} else {
621 			printf("STRING_0x%02x = <%s>\n", str_index, pbuf);
622 		}
623 	}
624 	free(pbuf);
625 }
626 
627 void
628 dump_device_stats(struct libusb20_device *pdev)
629 {
630 	struct libusb20_device_stats st;
631 
632 	if (libusb20_dev_get_stats(pdev, &st)) {
633 		printf("{}\n");
634 	} else {
635 		printf("{\n"
636 		    "    UE_CONTROL_OK       : %llu\n"
637 		    "    UE_ISOCHRONOUS_OK   : %llu\n"
638 		    "    UE_BULK_OK          : %llu\n"
639 		    "    UE_INTERRUPT_OK     : %llu\n"
640 		    "    UE_CONTROL_FAIL     : %llu\n"
641 		    "    UE_ISOCHRONOUS_FAIL : %llu\n"
642 		    "    UE_BULK_FAIL        : %llu\n"
643 		    "    UE_INTERRUPT_FAIL   : %llu\n"
644 		    "}\n",
645 		    (unsigned long long)st.xfer_ok[0],
646 		    (unsigned long long)st.xfer_ok[1],
647 		    (unsigned long long)st.xfer_ok[2],
648 	            (unsigned long long)st.xfer_ok[3],
649 		    (unsigned long long)st.xfer_fail[0],
650 		    (unsigned long long)st.xfer_fail[1],
651 		    (unsigned long long)st.xfer_fail[2],
652 		    (unsigned long long)st.xfer_fail[3]);
653 	}
654 }
655