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