xref: /freebsd/usr.sbin/usbconfig/dump.c (revision 370e009188ba90c3290b1479aa06ec98b66e140a)
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 }
268 
269 static void
270 dump_endpoint(struct libusb20_device *pdev,
271     struct libusb20_endpoint *ep)
272 {
273 	struct LIBUSB20_ENDPOINT_DESC_DECODED *edesc;
274 
275 	edesc = &ep->desc;
276 	LIBUSB20_ENDPOINT_DESC(DUMP3, edesc);
277 	dump_extra(&ep->extra, "  " "  " "  ");
278 }
279 
280 static void
281 dump_iface(struct libusb20_device *pdev,
282     struct libusb20_interface *iface)
283 {
284 	struct LIBUSB20_INTERFACE_DESC_DECODED *idesc;
285 	uint8_t z;
286 
287 	idesc = &iface->desc;
288 	LIBUSB20_INTERFACE_DESC(DUMP2, idesc);
289 	dump_extra(&iface->extra, "  " "  " "  ");
290 
291 	for (z = 0; z != iface->num_endpoints; z++) {
292 		printf("\n     Endpoint %u\n", z);
293 		dump_endpoint(pdev, iface->endpoints + z);
294 	}
295 }
296 
297 void
298 dump_device_info(struct libusb20_device *pdev, uint8_t show_ifdrv)
299 {
300 	char buf[128];
301 	uint8_t n;
302 	unsigned int usage;
303 
304 	usage = libusb20_dev_get_power_usage(pdev);
305 
306 	printf("%s, cfg=%u md=%s spd=%s pwr=%s (%umA)\n",
307 	    libusb20_dev_get_desc(pdev),
308 	    libusb20_dev_get_config_index(pdev),
309 	    dump_mode(libusb20_dev_get_mode(pdev)),
310 	    dump_speed(libusb20_dev_get_speed(pdev)),
311 	    dump_power_mode(libusb20_dev_get_power_mode(pdev)),
312 	    usage);
313 
314 	if (!show_ifdrv)
315 		return;
316 
317 	for (n = 0; n != 255; n++) {
318 		if (libusb20_dev_get_iface_desc(pdev, n, buf, sizeof(buf)))
319 			break;
320 		if (buf[0] == 0)
321 			continue;
322 		printf("ugen%u.%u.%u: %s\n",
323 		    libusb20_dev_get_bus_number(pdev),
324 		    libusb20_dev_get_address(pdev), n, buf);
325 	}
326 }
327 
328 void
329 dump_be_quirk_names(struct libusb20_backend *pbe)
330 {
331 	struct libusb20_quirk q;
332 	uint16_t x;
333 	int error;
334 
335 	memset(&q, 0, sizeof(q));
336 
337 	printf("\nDumping list of supported quirks:\n\n");
338 
339 	for (x = 0; x != 0xFFFF; x++) {
340 
341 		error = libusb20_be_get_quirk_name(pbe, x, &q);
342 		if (error) {
343 			if (x == 0) {
344 				printf("No quirk names - maybe the USB quirk "
345 				    "module has not been loaded.\n");
346 			}
347 			break;
348 		}
349 		if (strcmp(q.quirkname, "UQ_NONE"))
350 			printf("%s\n", q.quirkname);
351 	}
352 	printf("\n");
353 }
354 
355 void
356 dump_be_dev_quirks(struct libusb20_backend *pbe)
357 {
358 	struct libusb20_quirk q;
359 	uint16_t x;
360 	int error;
361 
362 	memset(&q, 0, sizeof(q));
363 
364 	printf("\nDumping current device quirks:\n\n");
365 
366 	for (x = 0; x != 0xFFFF; x++) {
367 
368 		error = libusb20_be_get_dev_quirk(pbe, x, &q);
369 		if (error) {
370 			if (x == 0) {
371 				printf("No device quirks - maybe the USB quirk "
372 				    "module has not been loaded.\n");
373 			}
374 			break;
375 		}
376 		if (strcmp(q.quirkname, "UQ_NONE")) {
377 			printf("VID=0x%04x PID=0x%04x REVLO=0x%04x "
378 			    "REVHI=0x%04x QUIRK=%s\n",
379 			    q.vid, q.pid, q.bcdDeviceLow,
380 			    q.bcdDeviceHigh, q.quirkname);
381 		}
382 	}
383 	printf("\n");
384 }
385 
386 void
387 dump_device_desc(struct libusb20_device *pdev)
388 {
389 	struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
390 
391 	ddesc = libusb20_dev_get_device_desc(pdev);
392 	LIBUSB20_DEVICE_DESC(DUMP0, ddesc);
393 }
394 
395 void
396 dump_config(struct libusb20_device *pdev, uint8_t all_cfg)
397 {
398 	struct LIBUSB20_CONFIG_DESC_DECODED *cdesc;
399 	struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
400 	struct libusb20_config *pcfg = NULL;
401 	uint8_t cfg_index;
402 	uint8_t cfg_index_end;
403 	uint8_t x;
404 	uint8_t y;
405 
406 	ddesc = libusb20_dev_get_device_desc(pdev);
407 
408 	if (all_cfg) {
409 		cfg_index = 0;
410 		cfg_index_end = ddesc->bNumConfigurations;
411 	} else {
412 		cfg_index = libusb20_dev_get_config_index(pdev);
413 		cfg_index_end = cfg_index + 1;
414 	}
415 
416 	for (; cfg_index != cfg_index_end; cfg_index++) {
417 
418 		pcfg = libusb20_dev_alloc_config(pdev, cfg_index);
419 		if (!pcfg) {
420 			continue;
421 		}
422 		printf("\n Configuration index %u\n\n", cfg_index);
423 		cdesc = &(pcfg->desc);
424 		LIBUSB20_CONFIG_DESC(DUMP1, cdesc);
425 		dump_extra(&(pcfg->extra), "  " "  ");
426 
427 		for (x = 0; x != pcfg->num_interface; x++) {
428 			printf("\n    Interface %u\n", x);
429 			dump_iface(pdev, pcfg->interface + x);
430 			printf("\n");
431 			for (y = 0; y != (pcfg->interface + x)->num_altsetting; y++) {
432 				printf("\n    Interface %u Alt %u\n", x, y + 1);
433 				dump_iface(pdev,
434 				    (pcfg->interface + x)->altsetting + y);
435 				printf("\n");
436 			}
437 		}
438 		printf("\n");
439 		free(pcfg);
440 	}
441 }
442 
443 void
444 dump_string_by_index(struct libusb20_device *pdev, uint8_t str_index)
445 {
446 	char *pbuf;
447 	uint8_t n;
448 	uint8_t len;
449 
450 	pbuf = malloc(256);
451 	if (pbuf == NULL)
452 		err(1, "out of memory");
453 
454 	if (str_index == 0) {
455 		/* language table */
456 		if (libusb20_dev_req_string_sync(pdev,
457 		    str_index, 0, pbuf, 256)) {
458 			printf("STRING_0x%02x = <read error>\n", str_index);
459 		} else {
460 			printf("STRING_0x%02x = ", str_index);
461 			len = (uint8_t)pbuf[0];
462 			for (n = 0; n != len; n++) {
463 				printf("0x%02x%s", (uint8_t)pbuf[n],
464 				    (n != (len - 1)) ? ", " : "");
465 			}
466 			printf("\n");
467 		}
468 	} else {
469 		/* ordinary string */
470 		if (libusb20_dev_req_string_simple_sync(pdev,
471 		    str_index, pbuf, 256)) {
472 			printf("STRING_0x%02x = <read error>\n", str_index);
473 		} else {
474 			printf("STRING_0x%02x = <%s>\n", str_index, pbuf);
475 		}
476 	}
477 	free(pbuf);
478 }
479 
480 void
481 dump_device_stats(struct libusb20_device *pdev)
482 {
483 	struct libusb20_device_stats st;
484 
485 	if (libusb20_dev_get_stats(pdev, &st)) {
486 		printf("{}\n");
487 	} else {
488 		printf("{\n"
489 		    "    UE_CONTROL_OK       : %llu\n"
490 		    "    UE_ISOCHRONOUS_OK   : %llu\n"
491 		    "    UE_BULK_OK          : %llu\n"
492 		    "    UE_INTERRUPT_OK     : %llu\n"
493 		    "    UE_CONTROL_FAIL     : %llu\n"
494 		    "    UE_ISOCHRONOUS_FAIL : %llu\n"
495 		    "    UE_BULK_FAIL        : %llu\n"
496 		    "    UE_INTERRUPT_FAIL   : %llu\n"
497 		    "}\n",
498 		    (unsigned long long)st.xfer_ok[0],
499 		    (unsigned long long)st.xfer_ok[1],
500 		    (unsigned long long)st.xfer_ok[2],
501 	            (unsigned long long)st.xfer_ok[3],
502 		    (unsigned long long)st.xfer_fail[0],
503 		    (unsigned long long)st.xfer_fail[1],
504 		    (unsigned long long)st.xfer_fail[2],
505 		    (unsigned long long)st.xfer_fail[3]);
506 	}
507 }
508