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 *
dump_mode(uint8_t value)67 dump_mode(uint8_t value)
68 {
69 if (value == LIBUSB20_MODE_HOST)
70 return ("HOST");
71 return ("DEVICE");
72 }
73
74 const char *
dump_speed(uint8_t value)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 *
dump_power_mode(uint8_t value)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
dump_field(struct libusb20_device * pdev,const char * plevel,const char * field,uint32_t value)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
dump_extra(struct libusb20_me_struct * str,const char * plevel)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
dump_endpoint(struct libusb20_device * pdev,struct libusb20_endpoint * ep)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
dump_iface(struct libusb20_device * pdev,struct libusb20_interface * iface)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 *
load_vendors(void)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 *
_device_desc(struct libusb20_device * pdev)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
dump_device_info(struct libusb20_device * pdev,uint8_t show_ifdrv)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
dump_be_quirk_names(struct libusb20_backend * pbe)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
dump_be_dev_quirks(struct libusb20_backend * pbe)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
dump_device_desc(struct libusb20_device * pdev)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
dump_config(struct libusb20_device * pdev,uint8_t all_cfg)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
dump_string_by_index(struct libusb20_device * pdev,uint8_t str_index)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
dump_device_stats(struct libusb20_device * pdev)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