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 case LIBUSB20_SPEED_SUPER_PLUS:
104 return ("SUPER+(10-20Gbps)");
105 default:
106 break;
107 }
108 return ("UNKNOWN ()");
109 }
110
111 const char *
dump_power_mode(uint8_t value)112 dump_power_mode(uint8_t value)
113 {
114 ; /* style fix */
115 switch (value) {
116 case LIBUSB20_POWER_OFF:
117 return ("OFF");
118 case LIBUSB20_POWER_ON:
119 return ("ON");
120 case LIBUSB20_POWER_SAVE:
121 return ("SAVE");
122 case LIBUSB20_POWER_SUSPEND:
123 return ("SUSPEND");
124 case LIBUSB20_POWER_RESUME:
125 return ("RESUME");
126 default:
127 return ("UNKNOWN");
128 }
129 }
130
131 static void
_dump_field(struct libusb20_device * pdev,const char * plevel,const char * field,uint32_t value,bool list_mode)132 _dump_field(struct libusb20_device *pdev, const char *plevel,
133 const char *field, uint32_t value, bool list_mode)
134 {
135 uint8_t temp_string[256];
136
137 if (list_mode) {
138 /* Skip fields we are not interested in. */
139 if (strcmp(field, "bLength") == 0 ||
140 strcmp(field, "bDescriptorType") == 0 ||
141 strcmp(field, "bMaxPacketSize0") == 0)
142 return;
143
144 printf("%s=%#06x ", field, value);
145 return;
146 }
147 printf("%s%s = 0x%04x ", plevel, field, value);
148
149 if (strlen(plevel) == 8) {
150 /* Endpoint Descriptor */
151
152 if (strcmp(field, "bEndpointAddress") == 0) {
153 if (value & 0x80)
154 printf(" <IN>\n");
155 else
156 printf(" <OUT>\n");
157 return;
158 }
159 if (strcmp(field, "bmAttributes") == 0) {
160 switch (value & 0x03) {
161 case 0:
162 printf(" <CONTROL>\n");
163 break;
164 case 1:
165 switch (value & 0x0C) {
166 case 0x00:
167 printf(" <ISOCHRONOUS>\n");
168 break;
169 case 0x04:
170 printf(" <ASYNC-ISOCHRONOUS>\n");
171 break;
172 case 0x08:
173 printf(" <ADAPT-ISOCHRONOUS>\n");
174 break;
175 default:
176 printf(" <SYNC-ISOCHRONOUS>\n");
177 break;
178 }
179 break;
180 case 2:
181 printf(" <BULK>\n");
182 break;
183 default:
184 printf(" <INTERRUPT>\n");
185 break;
186 }
187 return;
188 }
189 }
190 if ((field[0] == 'i') && (field[1] != 'd')) {
191 /* Indirect String Descriptor */
192 if (value == 0) {
193 printf(" <no string>\n");
194 return;
195 }
196 if (libusb20_dev_req_string_simple_sync(pdev, value,
197 temp_string, sizeof(temp_string))) {
198 printf(" <retrieving string failed>\n");
199 return;
200 }
201 printf(" <%s>\n", temp_string);
202 return;
203 }
204 if (strlen(plevel) == 2 || strlen(plevel) == 6) {
205
206 /* Device and Interface Descriptor class codes */
207
208 if (strcmp(field, "bInterfaceClass") == 0 ||
209 strcmp(field, "bDeviceClass") == 0) {
210
211 switch (value) {
212 case 0x00:
213 printf(" <Probed by interface class>\n");
214 break;
215 case 0x01:
216 printf(" <Audio device>\n");
217 break;
218 case 0x02:
219 printf(" <Communication device>\n");
220 break;
221 case 0x03:
222 printf(" <HID device>\n");
223 break;
224 case 0x05:
225 printf(" <Physical device>\n");
226 break;
227 case 0x06:
228 printf(" <Still imaging>\n");
229 break;
230 case 0x07:
231 printf(" <Printer device>\n");
232 break;
233 case 0x08:
234 printf(" <Mass storage>\n");
235 break;
236 case 0x09:
237 printf(" <HUB>\n");
238 break;
239 case 0x0A:
240 printf(" <CDC-data>\n");
241 break;
242 case 0x0B:
243 printf(" <Smart card>\n");
244 break;
245 case 0x0D:
246 printf(" <Content security>\n");
247 break;
248 case 0x0E:
249 printf(" <Video device>\n");
250 break;
251 case 0x0F:
252 printf(" <Personal healthcare>\n");
253 break;
254 case 0x10:
255 printf(" <Audio and video device>\n");
256 break;
257 case 0x11:
258 printf(" <Billboard device>\n");
259 break;
260 case 0xDC:
261 printf(" <Diagnostic device>\n");
262 break;
263 case 0xE0:
264 printf(" <Wireless controller>\n");
265 break;
266 case 0xEF:
267 printf(" <Miscellaneous device>\n");
268 break;
269 case 0xFE:
270 printf(" <Application specific>\n");
271 break;
272 case 0xFF:
273 printf(" <Vendor specific>\n");
274 break;
275 default:
276 printf(" <Unknown>\n");
277 break;
278 }
279 return;
280 }
281 }
282 /* No additional information */
283 printf("\n");
284 }
285
286 static void
dump_field(struct libusb20_device * pdev,const char * plevel,const char * field,uint32_t value)287 dump_field(struct libusb20_device *pdev, const char *plevel,
288 const char *field, uint32_t value)
289 {
290 _dump_field(pdev, plevel, field, value, false);
291 }
292
293 static void
dump_fieldl(struct libusb20_device * pdev,const char * plevel,const char * field,uint32_t value)294 dump_fieldl(struct libusb20_device *pdev, const char *plevel,
295 const char *field, uint32_t value)
296 {
297 _dump_field(pdev, plevel, field, value, true);
298 }
299
300 static void
dump_extra(struct libusb20_me_struct * str,const char * plevel)301 dump_extra(struct libusb20_me_struct *str, const char *plevel)
302 {
303 const uint8_t *ptr;
304 uint8_t x;
305
306 ptr = NULL;
307
308 while ((ptr = libusb20_desc_foreach(str, ptr))) {
309 printf("\n" "%sAdditional Descriptor\n\n", plevel);
310 printf("%sbLength = 0x%02x\n", plevel, ptr[0]);
311 printf("%sbDescriptorType = 0x%02x\n", plevel, ptr[1]);
312 if (ptr[0] > 1)
313 printf("%sbDescriptorSubType = 0x%02x\n",
314 plevel, ptr[2]);
315 printf("%s RAW dump: ", plevel);
316 for (x = 0; x != ptr[0]; x++) {
317 if ((x % 8) == 0) {
318 printf("\n%s 0x%02x | ", plevel, x);
319 }
320 printf("0x%02x%s", ptr[x],
321 (x != (ptr[0] - 1)) ? ", " : (x % 8) ? "\n" : "");
322 }
323 printf("\n");
324 }
325 }
326
327 static void
dump_endpoint(struct libusb20_device * pdev,struct libusb20_endpoint * ep)328 dump_endpoint(struct libusb20_device *pdev,
329 struct libusb20_endpoint *ep)
330 {
331 struct LIBUSB20_ENDPOINT_DESC_DECODED *edesc;
332
333 edesc = &ep->desc;
334 LIBUSB20_ENDPOINT_DESC(DUMP3, edesc);
335 dump_extra(&ep->extra, " " " " " ");
336 }
337
338 static void
dump_iface(struct libusb20_device * pdev,struct libusb20_interface * iface)339 dump_iface(struct libusb20_device *pdev,
340 struct libusb20_interface *iface)
341 {
342 struct LIBUSB20_INTERFACE_DESC_DECODED *idesc;
343 uint8_t z;
344
345 idesc = &iface->desc;
346 LIBUSB20_INTERFACE_DESC(DUMP2, idesc);
347 dump_extra(&iface->extra, " " " " " ");
348
349 for (z = 0; z != iface->num_endpoints; z++) {
350 printf("\n Endpoint %u\n", z);
351 dump_endpoint(pdev, iface->endpoints + z);
352 }
353 }
354
355 static struct usb_vendors *
load_vendors(void)356 load_vendors(void)
357 {
358 const char *dbf;
359 FILE *db = NULL;
360 struct usb_vendor_info *cv;
361 struct usb_product_info *cd;
362 struct usb_vendors *usb_vendors;
363 char buf[1024], str[1024];
364 char *ch;
365 int id;
366
367 usb_vendors = malloc(sizeof(*usb_vendors));
368 if (usb_vendors == NULL)
369 err(1, "out of memory");
370 STAILQ_INIT(usb_vendors);
371 if ((dbf = getenv("USB_VENDOR_DATABASE")) != NULL)
372 db = fopen(dbf, "r");
373 if (db == NULL) {
374 dbf = _PATH_LUSBVDB;
375 if ((db = fopen(dbf, "r")) == NULL) {
376 dbf = _PATH_USBVDB;
377 if ((db = fopen(dbf, "r")) == NULL)
378 return (usb_vendors);
379 }
380 }
381 cv = NULL;
382 cd = NULL;
383
384 for (;;) {
385 if (fgets(buf, sizeof(buf), db) == NULL)
386 break;
387
388 if ((ch = strchr(buf, '#')) != NULL)
389 *ch = '\0';
390 if (ch == buf)
391 continue;
392 ch = strchr(buf, '\0') - 1;
393 while (ch > buf && isspace(*ch))
394 *ch-- = '\0';
395 if (ch <= buf)
396 continue;
397
398 /* Can't handle subvendor / subdevice entries yet */
399 if (buf[0] == '\t' && buf[1] == '\t')
400 continue;
401
402 /* Check for vendor entry */
403 if (buf[0] != '\t' && sscanf(buf, "%04x %[^\n]", &id, str) == 2) {
404 if ((id == 0) || (strlen(str) < 1))
405 continue;
406 if ((cv = malloc(sizeof(struct usb_vendor_info))) == NULL)
407 err(1, "out of memory");
408 if ((cv->desc = strdup(str)) == NULL)
409 err(1, "out of memory");
410 cv->id = id;
411 STAILQ_INIT(&cv->devs);
412 STAILQ_INSERT_TAIL(usb_vendors, cv, link);
413 continue;
414 }
415
416 /* Check for device entry */
417 if (buf[0] == '\t' && sscanf(buf + 1, "%04x %[^\n]", &id, str) == 2) {
418 if ((id == 0) || (strlen(str) < 1))
419 continue;
420 if (cv == NULL)
421 continue;
422 if ((cd = malloc(sizeof(struct usb_product_info))) == NULL)
423 err(1, "out of memory");
424 if ((cd->desc = strdup(str)) == NULL)
425 err(1, "out of memory");
426 cd->id = id;
427 STAILQ_INSERT_TAIL(&cv->devs, cd, link);
428 continue;
429 }
430 }
431 if (ferror(db))
432 err(1, "error reading the usb id db");
433
434 fclose(db);
435 /* cleanup */
436 return (usb_vendors);
437 }
438
439 enum _device_descr_list_type {
440 _DEVICE_DESCR_LIST_TYPE_DEFAULT = 0,
441 _DEVICE_DESCR_LIST_TYPE_UGEN = 1,
442 _DEVICE_DESCR_LIST_TYPE_PRODUCT_VENDOR = 2,
443 };
444
445 static char *
_device_desc(struct libusb20_device * pdev,enum _device_descr_list_type list_type)446 _device_desc(struct libusb20_device *pdev,
447 enum _device_descr_list_type list_type)
448 {
449 static struct usb_vendors *usb_vendors = NULL;
450 char *desc = NULL;
451 const char *vendor = NULL, *product = NULL;
452 uint16_t vid;
453 uint16_t pid;
454 struct usb_vendor_info *vi;
455 struct usb_product_info *pi;
456 struct usb_device_info devinfo;
457
458 if (list_type == _DEVICE_DESCR_LIST_TYPE_UGEN) {
459 asprintf(&desc, "ugen%u.%u",
460 libusb20_dev_get_bus_number(pdev),
461 libusb20_dev_get_address(pdev));
462 return (desc);
463 }
464
465 vid = libusb20_dev_get_device_desc(pdev)->idVendor;
466 pid = libusb20_dev_get_device_desc(pdev)->idProduct;
467
468 if (usb_vendors == NULL)
469 usb_vendors = load_vendors();
470
471 STAILQ_FOREACH(vi, usb_vendors, link) {
472 if (vi->id == vid) {
473 vendor = vi->desc;
474 break;
475 }
476 }
477 if (vi != NULL) {
478 STAILQ_FOREACH(pi, &vi->devs, link) {
479 if (pi->id == pid) {
480 product = pi->desc;
481 break;
482 }
483 }
484 }
485
486 /*
487 * Try to gather the information; libusb2 unfortunately seems to
488 * only build an entire string but not save vendor/product individually.
489 */
490 if (vendor == NULL || product == NULL) {
491 char buf[64];
492 int f;
493
494 snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
495 libusb20_dev_get_bus_number(pdev),
496 libusb20_dev_get_address(pdev));
497
498 f = open(buf, O_RDWR);
499 if (f < 0)
500 goto skip_vp_recovery;
501
502 if (ioctl(f, IOUSB(USB_GET_DEVICEINFO), &devinfo))
503 goto skip_vp_recovery;
504
505
506 if (vendor == NULL)
507 vendor = devinfo.udi_vendor;
508 if (product == NULL)
509 product = devinfo.udi_product;
510
511 skip_vp_recovery:
512 if (f >= 0)
513 close(f);
514 }
515
516 if (list_type == _DEVICE_DESCR_LIST_TYPE_PRODUCT_VENDOR) {
517 asprintf(&desc, "vendor='%s' product='%s'",
518 (vendor != NULL) ? vendor : "",
519 (product != NULL) ? product : "");
520 return (desc);
521 }
522
523 if (vendor == NULL || product == NULL)
524 return (NULL);
525
526 asprintf(&desc, "ugen%u.%u: <%s %s> at usbus%u",
527 libusb20_dev_get_bus_number(pdev),
528 libusb20_dev_get_address(pdev),
529 product, vendor,
530 libusb20_dev_get_bus_number(pdev));
531
532 return (desc);
533 }
534
535 void
dump_device_info(struct libusb20_device * pdev,uint8_t show_ifdrv,bool list_mode)536 dump_device_info(struct libusb20_device *pdev, uint8_t show_ifdrv,
537 bool list_mode)
538 {
539 char buf[128];
540 uint8_t n;
541 unsigned int usage;
542 char *desc;
543
544 usage = libusb20_dev_get_power_usage(pdev);
545
546 desc = _device_desc(pdev, (list_mode) ? _DEVICE_DESCR_LIST_TYPE_UGEN :
547 _DEVICE_DESCR_LIST_TYPE_DEFAULT);
548
549 if (list_mode)
550 printf("%s: ", desc);
551 else
552 printf("%s, cfg=%u md=%s spd=%s pwr=%s (%umA)\n",
553 desc ? desc : libusb20_dev_get_desc(pdev),
554 libusb20_dev_get_config_index(pdev),
555 dump_mode(libusb20_dev_get_mode(pdev)),
556 dump_speed(libusb20_dev_get_speed(pdev)),
557 dump_power_mode(libusb20_dev_get_power_mode(pdev)),
558 usage);
559 free(desc);
560
561 if (list_mode || !show_ifdrv)
562 return;
563
564 for (n = 0; n != 255; n++) {
565 if (libusb20_dev_get_iface_desc(pdev, n, buf, sizeof(buf)))
566 break;
567 if (buf[0] == 0)
568 continue;
569 printf("ugen%u.%u.%u: %s\n",
570 libusb20_dev_get_bus_number(pdev),
571 libusb20_dev_get_address(pdev), n, buf);
572 }
573 }
574
575 void
dump_be_quirk_names(struct libusb20_backend * pbe)576 dump_be_quirk_names(struct libusb20_backend *pbe)
577 {
578 struct libusb20_quirk q;
579 uint16_t x;
580 int error;
581
582 memset(&q, 0, sizeof(q));
583
584 printf("\nDumping list of supported quirks:\n\n");
585
586 for (x = 0; x != 0xFFFF; x++) {
587
588 error = libusb20_be_get_quirk_name(pbe, x, &q);
589 if (error) {
590 if (x == 0) {
591 printf("No quirk names - maybe the USB quirk "
592 "module has not been loaded.\n");
593 }
594 break;
595 }
596 if (strcmp(q.quirkname, "UQ_NONE"))
597 printf("%s\n", q.quirkname);
598 }
599 printf("\n");
600 }
601
602 void
dump_be_dev_quirks(struct libusb20_backend * pbe)603 dump_be_dev_quirks(struct libusb20_backend *pbe)
604 {
605 struct libusb20_quirk q;
606 uint16_t x;
607 int error;
608
609 memset(&q, 0, sizeof(q));
610
611 printf("\nDumping current device quirks:\n\n");
612
613 for (x = 0; x != 0xFFFF; x++) {
614
615 error = libusb20_be_get_dev_quirk(pbe, x, &q);
616 if (error) {
617 if (x == 0) {
618 printf("No device quirks - maybe the USB quirk "
619 "module has not been loaded.\n");
620 }
621 break;
622 }
623 if (strcmp(q.quirkname, "UQ_NONE")) {
624 printf("VID=0x%04x PID=0x%04x REVLO=0x%04x "
625 "REVHI=0x%04x QUIRK=%s\n",
626 q.vid, q.pid, q.bcdDeviceLow,
627 q.bcdDeviceHigh, q.quirkname);
628 }
629 }
630 printf("\n");
631 }
632
633 void
dump_device_desc(struct libusb20_device * pdev,bool list_mode)634 dump_device_desc(struct libusb20_device *pdev, bool list_mode)
635 {
636 struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
637
638 ddesc = libusb20_dev_get_device_desc(pdev);
639 if (list_mode) {
640 char *desc;
641
642 LIBUSB20_DEVICE_DESC(DUMP0L, ddesc);
643 desc = _device_desc(pdev, _DEVICE_DESCR_LIST_TYPE_PRODUCT_VENDOR);
644 printf("%s\n", (desc != NULL) ? desc : "");
645 free(desc);
646 } else {
647 LIBUSB20_DEVICE_DESC(DUMP0, ddesc);
648 }
649 }
650
651 void
dump_config(struct libusb20_device * pdev,uint8_t all_cfg)652 dump_config(struct libusb20_device *pdev, uint8_t all_cfg)
653 {
654 struct LIBUSB20_CONFIG_DESC_DECODED *cdesc;
655 struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
656 struct libusb20_config *pcfg = NULL;
657 uint8_t cfg_index;
658 uint8_t cfg_index_end;
659 uint8_t x;
660 uint8_t y;
661
662 ddesc = libusb20_dev_get_device_desc(pdev);
663
664 if (all_cfg) {
665 cfg_index = 0;
666 cfg_index_end = ddesc->bNumConfigurations;
667 } else {
668 cfg_index = libusb20_dev_get_config_index(pdev);
669 cfg_index_end = cfg_index + 1;
670 }
671
672 for (; cfg_index != cfg_index_end; cfg_index++) {
673
674 pcfg = libusb20_dev_alloc_config(pdev, cfg_index);
675 if (!pcfg) {
676 continue;
677 }
678 printf("\n Configuration index %u\n\n", cfg_index);
679 cdesc = &(pcfg->desc);
680 LIBUSB20_CONFIG_DESC(DUMP1, cdesc);
681 dump_extra(&(pcfg->extra), " " " ");
682
683 for (x = 0; x != pcfg->num_interface; x++) {
684 printf("\n Interface %u\n", x);
685 dump_iface(pdev, pcfg->interface + x);
686 printf("\n");
687 for (y = 0; y != (pcfg->interface + x)->num_altsetting; y++) {
688 printf("\n Interface %u Alt %u\n", x, y + 1);
689 dump_iface(pdev,
690 (pcfg->interface + x)->altsetting + y);
691 printf("\n");
692 }
693 }
694 printf("\n");
695 free(pcfg);
696 }
697 }
698
699 void
dump_string_by_index(struct libusb20_device * pdev,uint8_t str_index)700 dump_string_by_index(struct libusb20_device *pdev, uint8_t str_index)
701 {
702 char *pbuf;
703 uint8_t n;
704 uint8_t len;
705
706 pbuf = malloc(256);
707 if (pbuf == NULL)
708 err(1, "out of memory");
709
710 if (str_index == 0) {
711 /* language table */
712 if (libusb20_dev_req_string_sync(pdev,
713 str_index, 0, pbuf, 256)) {
714 printf("STRING_0x%02x = <read error>\n", str_index);
715 } else {
716 printf("STRING_0x%02x = ", str_index);
717 len = (uint8_t)pbuf[0];
718 for (n = 0; n != len; n++) {
719 printf("0x%02x%s", (uint8_t)pbuf[n],
720 (n != (len - 1)) ? ", " : "");
721 }
722 printf("\n");
723 }
724 } else {
725 /* ordinary string */
726 if (libusb20_dev_req_string_simple_sync(pdev,
727 str_index, pbuf, 256)) {
728 printf("STRING_0x%02x = <read error>\n", str_index);
729 } else {
730 printf("STRING_0x%02x = <%s>\n", str_index, pbuf);
731 }
732 }
733 free(pbuf);
734 }
735
736 void
dump_device_stats(struct libusb20_device * pdev)737 dump_device_stats(struct libusb20_device *pdev)
738 {
739 struct libusb20_device_stats st;
740
741 if (libusb20_dev_get_stats(pdev, &st)) {
742 printf("{}\n");
743 } else {
744 printf("{\n"
745 " UE_CONTROL_OK : %llu\n"
746 " UE_ISOCHRONOUS_OK : %llu\n"
747 " UE_BULK_OK : %llu\n"
748 " UE_INTERRUPT_OK : %llu\n"
749 " UE_CONTROL_FAIL : %llu\n"
750 " UE_ISOCHRONOUS_FAIL : %llu\n"
751 " UE_BULK_FAIL : %llu\n"
752 " UE_INTERRUPT_FAIL : %llu\n"
753 "}\n",
754 (unsigned long long)st.xfer_ok[0],
755 (unsigned long long)st.xfer_ok[1],
756 (unsigned long long)st.xfer_ok[2],
757 (unsigned long long)st.xfer_ok[3],
758 (unsigned long long)st.xfer_fail[0],
759 (unsigned long long)st.xfer_fail[1],
760 (unsigned long long)st.xfer_fail[2],
761 (unsigned long long)st.xfer_fail[3]);
762 }
763 }
764