diff --git a/usr.sbin/usbconfig/dump.h b/usr.sbin/usbconfig/dump.h --- a/usr.sbin/usbconfig/dump.h +++ b/usr.sbin/usbconfig/dump.h @@ -32,10 +32,11 @@ const char *dump_speed(uint8_t value); const char *dump_power_mode(uint8_t value); void dump_string_by_index(struct libusb20_device *pdev, uint8_t index); -void dump_device_info(struct libusb20_device *pdev, uint8_t show_drv); +void dump_device_info(struct libusb20_device *pdev, uint8_t show_drv, + bool list_mode); void dump_be_quirk_names(struct libusb20_backend *pbe); void dump_be_dev_quirks(struct libusb20_backend *pbe); -void dump_device_desc(struct libusb20_device *pdev); +void dump_device_desc(struct libusb20_device *pdev, bool list_mode); void dump_device_stats(struct libusb20_device *pdev); void dump_config(struct libusb20_device *pdev, uint8_t all_cfg); diff --git a/usr.sbin/usbconfig/dump.c b/usr.sbin/usbconfig/dump.c --- a/usr.sbin/usbconfig/dump.c +++ b/usr.sbin/usbconfig/dump.c @@ -3,6 +3,10 @@ * * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * Copyright (c) 2024 Baptiste Daroussin + * Copyright (c) 2025 The FreeBSD Foundation + * + * Portions of this software were developed by Björn Zeeb + * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,22 +32,32 @@ #include +#include #include #include #include +#include #include #include #include #include #include +#include #include #include +#include + #include "dump.h" #include "pathnames.h" +#ifndef IOUSB +#define IOUSB(a) a +#endif + #define DUMP0(n,type,field,...) dump_field(pdev, " ", #field, n->field); +#define DUMP0L(n,type,field,...) dump_fieldl(pdev, " ", #field, n->field); #define DUMP1(n,type,field,...) dump_field(pdev, " ", #field, n->field); #define DUMP2(n,type,field,...) dump_field(pdev, " ", #field, n->field); #define DUMP3(n,type,field,...) dump_field(pdev, " ", #field, n->field); @@ -113,11 +127,21 @@ } static void -dump_field(struct libusb20_device *pdev, const char *plevel, - const char *field, uint32_t value) +_dump_field(struct libusb20_device *pdev, const char *plevel, + const char *field, uint32_t value, bool list_mode) { uint8_t temp_string[256]; + if (list_mode) { + /* Skip fields we are not interested in. */ + if (strcmp(field, "bLength") == 0 || + strcmp(field, "bDescriptorType") == 0 || + strcmp(field, "bMaxPacketSize0") == 0) + return; + + printf("%s=%#06x ", field, value); + return; + } printf("%s%s = 0x%04x ", plevel, field, value); if (strlen(plevel) == 8) { @@ -257,6 +281,20 @@ printf("\n"); } +static void +dump_field(struct libusb20_device *pdev, const char *plevel, + const char *field, uint32_t value) +{ + _dump_field(pdev, plevel, field, value, false); +} + +static void +dump_fieldl(struct libusb20_device *pdev, const char *plevel, + const char *field, uint32_t value) +{ + _dump_field(pdev, plevel, field, value, true); +} + static void dump_extra(struct libusb20_me_struct *str, const char *plevel) { @@ -396,16 +434,34 @@ return (usb_vendors); } +enum _device_descr_list_type { + _DEVICE_DESCR_LIST_TYPE_DEFAULT = 0, + _DEVICE_DESCR_LIST_TYPE_UGEN = 1, + _DEVICE_DESCR_LIST_TYPE_PRODUCT_VENDOR = 2, +}; + static char * -_device_desc(struct libusb20_device *pdev) +_device_desc(struct libusb20_device *pdev, + enum _device_descr_list_type list_type) { static struct usb_vendors *usb_vendors = NULL; char *desc = NULL; const char *vendor = NULL, *product = NULL; - uint16_t vid = libusb20_dev_get_device_desc(pdev)->idVendor; - uint16_t pid = libusb20_dev_get_device_desc(pdev)->idProduct; + uint16_t vid; + uint16_t pid; struct usb_vendor_info *vi; struct usb_product_info *pi; + struct usb_device_info devinfo; + + if (list_type == _DEVICE_DESCR_LIST_TYPE_UGEN) { + asprintf(&desc, "ugen%u.%u", + libusb20_dev_get_bus_number(pdev), + libusb20_dev_get_address(pdev)); + return (desc); + } + + vid = libusb20_dev_get_device_desc(pdev)->idVendor; + pid = libusb20_dev_get_device_desc(pdev)->idProduct; if (usb_vendors == NULL) usb_vendors = load_vendors(); @@ -424,6 +480,44 @@ } } } + + /* + * Try to gather the information; libusb2 unfortunately seems to + * only build an entire string but not save vendor/product individually. + */ + if (vendor == NULL || product == NULL) { + char buf[64]; + int f; + + snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u", + libusb20_dev_get_bus_number(pdev), + libusb20_dev_get_address(pdev)); + + f = open(buf, O_RDWR); + if (f < 0) + goto skip_vp_recovery; + + if (ioctl(f, IOUSB(USB_GET_DEVICEINFO), &devinfo)) + goto skip_vp_recovery; + + + if (vendor == NULL) + vendor = devinfo.udi_vendor; + if (product == NULL) + product = devinfo.udi_product; + +skip_vp_recovery: + if (f >= 0) + close(f); + } + + if (list_type == _DEVICE_DESCR_LIST_TYPE_PRODUCT_VENDOR) { + asprintf(&desc, "vendor='%s' product='%s'", + (vendor != NULL) ? vendor : "", + (product != NULL) ? product : ""); + return (desc); + } + if (vendor == NULL || product == NULL) return (NULL); @@ -433,12 +527,12 @@ product, vendor, libusb20_dev_get_bus_number(pdev)); - return (desc); } void -dump_device_info(struct libusb20_device *pdev, uint8_t show_ifdrv) +dump_device_info(struct libusb20_device *pdev, uint8_t show_ifdrv, + bool list_mode) { char buf[128]; uint8_t n; @@ -447,18 +541,22 @@ usage = libusb20_dev_get_power_usage(pdev); - desc = _device_desc(pdev); - - printf("%s, cfg=%u md=%s spd=%s pwr=%s (%umA)\n", - desc ? desc : libusb20_dev_get_desc(pdev), - libusb20_dev_get_config_index(pdev), - dump_mode(libusb20_dev_get_mode(pdev)), - dump_speed(libusb20_dev_get_speed(pdev)), - dump_power_mode(libusb20_dev_get_power_mode(pdev)), - usage); + desc = _device_desc(pdev, (list_mode) ? _DEVICE_DESCR_LIST_TYPE_UGEN : + _DEVICE_DESCR_LIST_TYPE_DEFAULT); + + if (list_mode) + printf("%s: ", desc); + else + printf("%s, cfg=%u md=%s spd=%s pwr=%s (%umA)\n", + desc ? desc : libusb20_dev_get_desc(pdev), + libusb20_dev_get_config_index(pdev), + dump_mode(libusb20_dev_get_mode(pdev)), + dump_speed(libusb20_dev_get_speed(pdev)), + dump_power_mode(libusb20_dev_get_power_mode(pdev)), + usage); free(desc); - if (!show_ifdrv) + if (list_mode || !show_ifdrv) return; for (n = 0; n != 255; n++) { @@ -531,12 +629,21 @@ } void -dump_device_desc(struct libusb20_device *pdev) +dump_device_desc(struct libusb20_device *pdev, bool list_mode) { struct LIBUSB20_DEVICE_DESC_DECODED *ddesc; ddesc = libusb20_dev_get_device_desc(pdev); - LIBUSB20_DEVICE_DESC(DUMP0, ddesc); + if (list_mode) { + char *desc; + + LIBUSB20_DEVICE_DESC(DUMP0L, ddesc); + desc = _device_desc(pdev, _DEVICE_DESCR_LIST_TYPE_PRODUCT_VENDOR); + printf("%s\n", (desc != NULL) ? desc : ""); + free(desc); + } else { + LIBUSB20_DEVICE_DESC(DUMP0, ddesc); + } } void diff --git a/usr.sbin/usbconfig/usbconfig.8 b/usr.sbin/usbconfig/usbconfig.8 --- a/usr.sbin/usbconfig/usbconfig.8 +++ b/usr.sbin/usbconfig/usbconfig.8 @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd January 29, 2022 +.Dd February 12, 2025 .Dt USBCONFIG 8 .Os .Sh NAME @@ -30,6 +30,7 @@ .Nd configure the USB subsystem .Sh SYNOPSIS .Nm +.Op Fl l .Op Fl v .Op Fl a Ar addr .Op Fl i Ar interface_index @@ -40,6 +41,7 @@ .Sm off .Oo Oo Cm /dev/ Oc Cm ugen Oc Ar unit Cm \&. Ar addr .Sm on +.Op Fl l .Op Fl v .Op Fl i Ar interface_index .Op Ar cmds ... @@ -70,6 +72,8 @@ Specify interface index as indicated by the command description. If this argument is not specified a value of zero will be used for the interface index. +.It Fl l Cm dump_device_desc +Show numeral only key=value output as one long line. .It Fl u Ar unit Limit device range to USB devices connected to the given USBUS unit. .It Fl v diff --git a/usr.sbin/usbconfig/usbconfig.c b/usr.sbin/usbconfig/usbconfig.c --- a/usr.sbin/usbconfig/usbconfig.c +++ b/usr.sbin/usbconfig/usbconfig.c @@ -25,6 +25,7 @@ * SUCH DAMAGE. */ +#include #include #include #include @@ -91,6 +92,7 @@ uint8_t got_dump_string:1; uint8_t got_do_request:1; uint8_t got_detach_kernel_driver:1; + uint8_t opt_dump_in_list_mode:1; }; struct token { @@ -504,11 +506,13 @@ if (opt->got_list || dump_any) { dump_device_info(pdev, - opt->got_show_iface_driver); + opt->got_show_iface_driver, + opt->opt_dump_in_list_mode && opt->got_dump_device_desc); } if (opt->got_dump_device_desc) { - printf("\n"); - dump_device_desc(pdev); + if (!opt->opt_dump_in_list_mode) + printf("\n"); + dump_device_desc(pdev, opt->opt_dump_in_list_mode); } if (opt->got_dump_all_config) { printf("\n"); @@ -518,14 +522,14 @@ dump_config(pdev, 0); } else if (opt->got_dump_all_desc) { printf("\n"); - dump_device_desc(pdev); + dump_device_desc(pdev, false); dump_config(pdev, 1); } if (opt->got_dump_stats) { printf("\n"); dump_device_stats(pdev); } - if (dump_any) { + if (dump_any && !opt->opt_dump_in_list_mode) { printf("\n"); } if (libusb20_dev_close(pdev)) { @@ -559,7 +563,7 @@ if (pbe == NULL) err(1, "could not access USB backend\n"); - while ((ch = getopt(argc, argv, "a:d:hi:u:v")) != -1) { + while ((ch = getopt(argc, argv, "a:d:hi:lu:v")) != -1) { switch (ch) { case 'a': opt->addr = num_id(optarg, "addr"); @@ -596,6 +600,10 @@ opt->iface = num_id(optarg, "iface"); break; + case 'l': + opt->opt_dump_in_list_mode = 1; + break; + case 'u': opt->bus = num_id(optarg, "busnum"); opt->got_bus = 1;