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