Imported Upstream version 013
[platform/upstream/usbutils.git] / lsusb.c
diff --git a/lsusb.c b/lsusb.c
index 620eccc..1d7c105 100644 (file)
--- a/lsusb.c
+++ b/lsusb.c
@@ -1,28 +1,13 @@
-/*****************************************************************************/
-
+// SPDX-License-Identifier: GPL-2.0+
 /*
- *      lsusb.c  --  lspci like utility for the USB bus
- *
- *      Copyright (C) 1999-2001, 2003
- *        Thomas Sailer (t.sailer@alumni.ethz.ch)
- *      Copyright (C) 2003-2005 David Brownell
- *
- *      This program is free software; you can redistribute it and/or modify
- *      it under the terms of the GNU General Public License as published by
- *      the Free Software Foundation; either version 2 of the License, or
- *      (at your option) any later version.
- *
- *      This program is distributed in the hope that it will be useful,
- *      but WITHOUT ANY WARRANTY; without even the implied warranty of
- *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *      GNU General Public License for more details.
+ * lspci like utility for the USB bus
  *
+ * Copyright (C) 1999-2001, 2003 Thomas Sailer (t.sailer@alumni.ethz.ch)
+ * Copyright (C) 2003-2005 David Brownell
  */
 
-/*****************************************************************************/
-
 #include "config.h"
-#include <sys/types.h>
+#include <stdint.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <locale.h>
@@ -31,6 +16,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <stdbool.h>
 
 #ifdef HAVE_BYTESWAP_H
 #include <byteswap.h>
 
 #include "lsusb.h"
 #include "names.h"
+#include "sysfs.h"
 #include "usbmisc.h"
+#include "desc-defs.h"
+#include "desc-dump.h"
 
 #include <getopt.h>
 
 #define USB_DC_20_EXTENSION            0x02
 #define USB_DC_SUPERSPEED              0x03
 #define USB_DC_CONTAINER_ID            0x04
+#define USB_DC_PLATFORM                0x05
+#define USB_DC_SUPERSPEEDPLUS          0x0a
+#define USB_DC_BILLBOARD               0x0d
+#define USB_DC_BILLBOARD_ALT_MODE      0x0f
+#define USB_DC_CONFIGURATION_SUMMARY   0x10
 
 /* Conventional codes for class-specific descriptors.  The convention is
  * defined in the USB "Common Class" Spec (3.11).  Individual class specs
 #define USB_AUDIO_CLASS_2              0x20
 #endif
 
+/* USB DCD for Audio Devices Release 3.0: Section A.6, pp139 */
+#ifndef USB_AUDIO_CLASS_3
+#define USB_AUDIO_CLASS_3              0x30
+#endif
+
+#ifndef USB_VIDEO_PROTOCOL_15
+#define USB_VIDEO_PROTOCOL_15          0x01
+#endif
+
 #define VERBLEVEL_DEFAULT 0    /* 0 gives lspci behaviour; 1, lsusb-0.9 */
 
 #define CTRL_RETRIES    2
 
 #define        HUB_STATUS_BYTELEN      3       /* max 3 bytes status = hub + 23 ports */
 
-static const char procbususb[] = "/proc/bus/usb";
-static unsigned int verblevel = VERBLEVEL_DEFAULT;
+#define BILLBOARD_MAX_NUM_ALT_MODE     (0x34)
+
+/* from WebUSB specification : https://wicg.github.io/webusb/ */
+#define WEBUSB_GUID            "{3408b638-09a9-47a0-8bfd-a0768815b665}"
+#define WEBUSB_GET_URL         0x02
+#define USB_DT_WEBUSB_URL      0x03
+
+unsigned int verblevel = VERBLEVEL_DEFAULT;
 static int do_report_desc = 1;
 static const char * const encryption_type[] = {
        "UNSECURE",
@@ -118,20 +127,41 @@ static const char * const encryption_type[] = {
        "RESERVED"
 };
 
+static const char * const vconn_power[] = {
+       "1W",
+       "1.5W",
+       "2W",
+       "3W",
+       "4W",
+       "5W",
+       "6W",
+       "reserved"
+};
+
+static const char * const alt_mode_state[] = {
+       "Unspecified Error",
+       "Alternate Mode configuration not attempted",
+       "Alternate Mode configuration attempted but unsuccessful",
+       "Alternate Mode configuration successful"
+};
+
 static void dump_interface(libusb_device_handle *dev, const struct libusb_interface *interface);
 static void dump_endpoint(libusb_device_handle *dev, const struct libusb_interface_descriptor *interface, const struct libusb_endpoint_descriptor *endpoint);
 static void dump_audiocontrol_interface(libusb_device_handle *dev, const unsigned char *buf, int protocol);
 static void dump_audiostreaming_interface(libusb_device_handle *dev, const unsigned char *buf, int protocol);
 static void dump_midistreaming_interface(libusb_device_handle *dev, const unsigned char *buf);
-static void dump_videocontrol_interface(libusb_device_handle *dev, const unsigned char *buf);
+static void dump_videocontrol_interface(libusb_device_handle *dev, const unsigned char *buf, int protocol);
 static void dump_videostreaming_interface(const unsigned char *buf);
 static void dump_dfu_interface(const unsigned char *buf);
 static char *dump_comm_descriptor(libusb_device_handle *dev, const unsigned char *buf, char *indent);
 static void dump_hid_device(libusb_device_handle *dev, const struct libusb_interface_descriptor *interface, const unsigned char *buf);
-static void dump_audiostreaming_endpoint(const unsigned char *buf, int protocol);
+static void dump_printer_device(libusb_device_handle *dev, const struct libusb_interface_descriptor *interface, const unsigned char *buf);
+static void dump_audiostreaming_endpoint(libusb_device_handle *dev, const unsigned char *buf, int protocol);
 static void dump_midistreaming_endpoint(const unsigned char *buf);
 static void dump_hub(const char *prefix, const unsigned char *p, int tt_type);
 static void dump_ccid_device(const unsigned char *buf);
+static void dump_billboard_device_capability_desc(libusb_device_handle *dev, unsigned char *buf);
+static void dump_billboard_alt_mode_capability_desc(libusb_device_handle *dev, unsigned char *buf);
 
 /* ---------------------------------------------------------------------- */
 
@@ -140,10 +170,15 @@ static unsigned int convert_le_u32 (const unsigned char *buf)
        return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
 }
 
+static unsigned int convert_le_u16 (const unsigned char *buf)
+{
+       return buf[0] | (buf[1] << 8);
+}
+
 /* ---------------------------------------------------------------------- */
 
 /* workaround libusb API goofs:  "byte" should never be sign extended;
- * using "char" is trouble.  Likewise, sizes should never be negative.
+ * using "char" is trouble.
  */
 
 static inline int typesafe_control_msg(libusb_device_handle *dev,
@@ -154,15 +189,12 @@ static inline int typesafe_control_msg(libusb_device_handle *dev,
        int ret = libusb_control_transfer(dev, requesttype, request, value,
                                        idx, bytes, size, timeout);
 
-       if (ret < 0)
-               return -ret;
-       else
-               return ret;
+       return ret;
 }
 
 #define usb_control_msg                typesafe_control_msg
 
-static int get_protocol_string(char *buf, size_t size, u_int8_t cls, u_int8_t subcls, u_int8_t proto)
+static int get_protocol_string(char *buf, size_t size, uint8_t cls, uint8_t subcls, uint8_t proto)
 {
        const char *cp;
 
@@ -174,19 +206,7 @@ static int get_protocol_string(char *buf, size_t size, u_int8_t cls, u_int8_t su
        return snprintf(buf, size, "%s", cp);
 }
 
-static int get_audioterminal_string(char *buf, size_t size, u_int16_t termt)
-{
-       const char *cp;
-
-       if (size < 1)
-               return 0;
-       *buf = 0;
-       if (!(cp = names_audioterminal(termt)))
-               return 0;
-       return snprintf(buf, size, "%s", cp);
-}
-
-static int get_videoterminal_string(char *buf, size_t size, u_int16_t termt)
+static int get_videoterminal_string(char *buf, size_t size, uint16_t termt)
 {
        const char *cp;
 
@@ -211,9 +231,9 @@ static const char *get_guid(const unsigned char *buf)
                        "-%02x%02x"
                        "-%02x%02x"
                        "-%02x%02x%02x%02x%02x%02x}",
-              buf[0], buf[1], buf[2], buf[3],
-              buf[4], buf[5],
-              buf[6], buf[7],
+              buf[3], buf[2], buf[1], buf[0],
+              buf[5], buf[4],
+              buf[7], buf[6],
               buf[8], buf[9],
               buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
        return guid;
@@ -247,13 +267,14 @@ static void dump_junk(const unsigned char *buf, const char *indent, unsigned int
  */
 
 static void dump_device(
-       libusb_device_handle *dev,
+       libusb_device *dev,
        struct libusb_device_descriptor *descriptor
 )
 {
        char vendor[128], product[128];
        char cls[128], subcls[128], proto[128];
-       char *mfg, *prod, *serial;
+       char mfg[128] = {0}, prod[128] = {0}, serial[128] = {0};
+       char sysfs_name[PATH_MAX];
 
        get_vendor_string(vendor, sizeof(vendor), descriptor->idVendor);
        get_product_string(product, sizeof(product),
@@ -264,9 +285,11 @@ static void dump_device(
        get_protocol_string(proto, sizeof(proto), descriptor->bDeviceClass,
                        descriptor->bDeviceSubClass, descriptor->bDeviceProtocol);
 
-       mfg = get_dev_string(dev, descriptor->iManufacturer);
-       prod = get_dev_string(dev, descriptor->iProduct);
-       serial = get_dev_string(dev, descriptor->iSerialNumber);
+       if (get_sysfs_name(sysfs_name, sizeof(sysfs_name), dev) >= 0) {
+               read_sysfs_prop(mfg, sizeof(vendor), sysfs_name, "manufacturer");
+               read_sysfs_prop(prod, sizeof(vendor), sysfs_name, "product");
+               read_sysfs_prop(serial, sizeof(vendor), sysfs_name, "serial");
+       }
 
        printf("Device Descriptor:\n"
               "  bLength             %5u\n"
@@ -295,10 +318,6 @@ static void dump_device(
               descriptor->iProduct, prod,
               descriptor->iSerialNumber, serial,
               descriptor->bNumConfigurations);
-
-       free(mfg);
-       free(prod);
-       free(serial);
 }
 
 static void dump_wire_adapter(const unsigned char *buf)
@@ -337,7 +356,7 @@ static void dump_security(const unsigned char *buf)
        printf("    Security Descriptor:\n"
               "      bLength             %5u\n"
               "      bDescriptorType     %5u\n"
-              "      wTotalLength        %5u\n"
+              "      wTotalLength       0x%04x\n"
               "      bNumEncryptionTypes %5u\n",
               buf[0], buf[1], (buf[3] << 8 | buf[2]), buf[4]);
 }
@@ -385,7 +404,7 @@ static void dump_association(libusb_device_handle *dev, const unsigned char *buf
        free(func);
 }
 
-static void dump_config(libusb_device_handle *dev, struct libusb_config_descriptor *config)
+static void dump_config(libusb_device_handle *dev, struct libusb_config_descriptor *config, unsigned speed)
 {
        char *cfg;
        int i;
@@ -395,7 +414,7 @@ static void dump_config(libusb_device_handle *dev, struct libusb_config_descript
        printf("  Configuration Descriptor:\n"
               "    bLength             %5u\n"
               "    bDescriptorType     %5u\n"
-              "    wTotalLength        %5u\n"
+              "    wTotalLength       0x%04x\n"
               "    bNumInterfaces      %5u\n"
               "    bConfigurationValue %5u\n"
               "    iConfiguration      %5u %s\n"
@@ -418,7 +437,7 @@ static void dump_config(libusb_device_handle *dev, struct libusb_config_descript
                printf("      Remote Wakeup\n");
        if (config->bmAttributes & 0x10)
                printf("      Battery Powered\n");
-       printf("    MaxPower            %5umA\n", config->MaxPower * 2);
+       printf("    MaxPower            %5umA\n", config->MaxPower * (speed >= 0x0300 ? 8 : 2));
 
        /* avoid re-ordering or hiding descriptors for display */
        if (config->extra_length) {
@@ -491,7 +510,7 @@ static void dump_altsetting(libusb_device_handle *dev, const struct libusb_inter
        if (interface->extra_length) {
                size = interface->extra_length;
                buf = interface->extra;
-               while (size >= 2 * sizeof(u_int8_t)) {
+               while (size >= 2 * sizeof(uint8_t)) {
                        if (buf[0] < 2) {
                                dump_junk(buf, "      ", size);
                                break;
@@ -528,7 +547,7 @@ static void dump_altsetting(libusb_device_handle *dev, const struct libusb_inter
                                case USB_CLASS_VIDEO:
                                        switch (interface->bInterfaceSubClass) {
                                        case 1:
-                                               dump_videocontrol_interface(dev, buf);
+                                               dump_videocontrol_interface(dev, buf, interface->bInterfaceProtocol);
                                                break;
                                        case 2:
                                                dump_videostreaming_interface(buf);
@@ -549,6 +568,9 @@ static void dump_altsetting(libusb_device_handle *dev, const struct libusb_inter
                                case LIBUSB_CLASS_HID:
                                        dump_hid_device(dev, interface, buf);
                                        break;
+                               case LIBUSB_CLASS_PRINTER:
+                                       dump_printer_device(dev, interface, buf);
+                                       break;
                                case USB_CLASS_CCID:
                                        dump_ccid_device(buf);
                                        break;
@@ -592,7 +614,7 @@ static void dump_altsetting(libusb_device_handle *dev, const struct libusb_inter
                                        case USB_DT_CS_ENDPOINT:
                                                switch (interface->bInterfaceSubClass) {
                                                case 2:
-                                                       dump_audiostreaming_endpoint(buf, interface->bInterfaceProtocol);
+                                                       dump_audiostreaming_endpoint(dev, buf, interface->bInterfaceProtocol);
                                                        break;
                                                default:
                                                        goto dump;
@@ -649,7 +671,7 @@ static void dump_pipe_desc(const unsigned char *buf)
                [0xE0 ... 0xEF] = "Vendor specific",
                [0xF0 ... 0xFF] = "Reserved",
        };
-       
+
        if (buf[0] == 4 && buf[1] == 0x24) {
                printf("        %s (0x%02x)\n", pipe_name[buf[2]], buf[2]);
        } else {
@@ -714,7 +736,7 @@ static void dump_endpoint(libusb_device_handle *dev, const struct libusb_interfa
        if (endpoint->extra_length) {
                size = endpoint->extra_length;
                buf = endpoint->extra;
-               while (size >= 2 * sizeof(u_int8_t)) {
+               while (size >= 2 * sizeof(uint8_t)) {
                        if (buf[0] < 2) {
                                dump_junk(buf, "        ", size);
                                break;
@@ -722,7 +744,7 @@ static void dump_endpoint(libusb_device_handle *dev, const struct libusb_interfa
                        switch (buf[1]) {
                        case USB_DT_CS_ENDPOINT:
                                if (interface->bInterfaceClass == 1 && interface->bInterfaceSubClass == 2)
-                                       dump_audiostreaming_endpoint(buf, interface->bInterfaceProtocol);
+                                       dump_audiostreaming_endpoint(dev, buf, interface->bInterfaceProtocol);
                                else if (interface->bInterfaceClass == 1 && interface->bInterfaceSubClass == 3)
                                        dump_midistreaming_endpoint(buf);
                                break;
@@ -846,138 +868,121 @@ static void dump_unit(unsigned int data, unsigned int len)
  * Audio Class descriptor dump
  */
 
-struct bmcontrol {
-       const char *name;
-       unsigned int bit;
-};
-
-static const struct bmcontrol uac2_interface_header_bmcontrols[] = {
-       { "Latency control",    0 },
-       { NULL }
-};
-
-static const struct bmcontrol uac_fu_bmcontrols[] = {
-       { "Mute",               0 },
-       { "Volume",             1 },
-       { "Bass",               2 },
-       { "Mid",                3 },
-       { "Treble",             4 },
-       { "Graphic Equalizer",  5 },
-       { "Automatic Gain",     6 },
-       { "Delay",              7 },
-       { "Bass Boost",         8 },
-       { "Loudness",           9 },
-       { "Input gain",         10 },
-       { "Input gain pad",     11 },
-       { "Phase inverter",     12 },
-       { NULL }
-};
-
-static const struct bmcontrol uac2_input_term_bmcontrols[] = {
-       { "Copy Protect",       0 },
-       { "Connector",          1 },
-       { "Overload",           2 },
-       { "Cluster",            3 },
-       { "Underflow",          4 },
-       { "Overflow",           5 },
-       { NULL }
-};
-
-static const struct bmcontrol uac2_output_term_bmcontrols[] = {
-       { "Copy Protect",       0 },
-       { "Connector",          1 },
-       { "Overload",           2 },
-       { "Underflow",          3 },
-       { "Overflow",           4 },
-       { NULL }
-};
-
-static const struct bmcontrol uac2_mixer_unit_bmcontrols[] = {
-       { "Cluster",            0 },
-       { "Underflow",          1 },
-       { "Overflow",           2 },
-       { NULL }
-};
+static void dump_audio_subtype(libusb_device_handle *dev,
+                               const char *name,
+                               const struct desc * const desc[3],
+                               const unsigned char *buf,
+                               int protocol,
+                               unsigned int indent)
+{
+       static const char * const strings[] = { "UAC1", "UAC2", "UAC3" };
+       unsigned int idx = 0;
 
-static const struct bmcontrol uac2_extension_unit_bmcontrols[] = {
-       { "Enable",             0 },
-       { "Cluster",            1 },
-       { "Underflow",          2 },
-       { "Overflow",           3 },
-       { NULL }
-};
+       switch (protocol) {
+       case USB_AUDIO_CLASS_2: idx = 1; break;
+       case USB_AUDIO_CLASS_3: idx = 2; break;
+       }
 
-static const struct bmcontrol uac2_clock_source_bmcontrols[] = {
-       { "Clock Frequency",    0 },
-       { "Clock Validity",     1 },
-       { NULL }
-};
+       printf("(%s)\n", name);
 
-static const struct bmcontrol uac2_clock_selector_bmcontrols[] = {
-       { "Clock Selector",     0 },
-       { NULL }
-};
+       if (desc[idx] == NULL) {
+               printf("%*sWarning: %s descriptors are illegal for %s\n",
+                      indent * 2, "", name, strings[idx]);
+               return;
+       }
 
-static const struct bmcontrol uac2_clock_multiplier_bmcontrols[] = {
-       { "Clock Numerator",    0 },
-       { "Clock Denominator",  1 },
-       { NULL }
-};
+       /* Skip the first three bytes; those common fields have already
+        * been dumped. */
+       desc_dump(dev, desc[idx], buf + 3, buf[0] - 3, indent);
+}
 
-static const struct bmcontrol uac2_selector_bmcontrols[] = {
-       { "Selector",   0 },
-       { NULL }
+/* USB Audio Class subtypes */
+enum uac_interface_subtype {
+       UAC_INTERFACE_SUBTYPE_AC_DESCRIPTOR_UNDEFINED = 0x00,
+       UAC_INTERFACE_SUBTYPE_HEADER                  = 0x01,
+       UAC_INTERFACE_SUBTYPE_INPUT_TERMINAL          = 0x02,
+       UAC_INTERFACE_SUBTYPE_OUTPUT_TERMINAL         = 0x03,
+       UAC_INTERFACE_SUBTYPE_EXTENDED_TERMINAL       = 0x04,
+       UAC_INTERFACE_SUBTYPE_MIXER_UNIT              = 0x05,
+       UAC_INTERFACE_SUBTYPE_SELECTOR_UNIT           = 0x06,
+       UAC_INTERFACE_SUBTYPE_FEATURE_UNIT            = 0x07,
+       UAC_INTERFACE_SUBTYPE_EFFECT_UNIT             = 0x08,
+       UAC_INTERFACE_SUBTYPE_PROCESSING_UNIT         = 0x09,
+       UAC_INTERFACE_SUBTYPE_EXTENSION_UNIT          = 0x0a,
+       UAC_INTERFACE_SUBTYPE_CLOCK_SOURCE            = 0x0b,
+       UAC_INTERFACE_SUBTYPE_CLOCK_SELECTOR          = 0x0c,
+       UAC_INTERFACE_SUBTYPE_CLOCK_MULTIPLIER        = 0x0d,
+       UAC_INTERFACE_SUBTYPE_SAMPLE_RATE_CONVERTER   = 0x0e,
+       UAC_INTERFACE_SUBTYPE_CONNECTORS              = 0x0f,
+       UAC_INTERFACE_SUBTYPE_POWER_DOMAIN            = 0x10,
 };
 
-static void dump_audio_bmcontrols(const char *prefix, int bmcontrols, const struct bmcontrol *list, int protocol)
+/*
+ * UAC1, UAC2, and UAC3 define bDescriptorSubtype differently for the
+ * AudioControl interface, so we need to do some ugly remapping:
+ *
+ * val  | UAC1            | UAC2                  | UAC3
+ * -----|-----------------|-----------------------|---------------------
+ * 0x00 | AC UNDEFINED    | AC UNDEFINED          | AC UNDEFINED
+ * 0x01 | HEADER          | HEADER                | HEADER
+ * 0x02 | INPUT_TERMINAL  | INPUT_TERMINAL        | INPUT_TERMINAL
+ * 0x03 | OUTPUT_TERMINAL | OUTPUT_TERMINAL       | OUTPUT_TERMINAL
+ * 0x04 | MIXER_UNIT      | MIXER_UNIT            | EXTENDED_TERMINAL
+ * 0x05 | SELECTOR_UNIT   | SELECTOR_UNIT         | MIXER_UNIT
+ * 0x06 | FEATURE_UNIT    | FEATURE_UNIT          | SELECTOR_UNIT
+ * 0x07 | PROCESSING_UNIT | EFFECT_UNIT           | FEATURE_UNIT
+ * 0x08 | EXTENSION_UNIT  | PROCESSING_UNIT       | EFFECT_UNIT
+ * 0x09 | -               | EXTENSION_UNIT        | PROCESSING_UNIT
+ * 0x0a | -               | CLOCK_SOURCE          | EXTENSION_UNIT
+ * 0x0b | -               | CLOCK_SELECTOR        | CLOCK_SOURCE
+ * 0x0c | -               | CLOCK_MULTIPLIER      | CLOCK_SELECTOR
+ * 0x0d | -               | SAMPLE_RATE_CONVERTER | CLOCK_MULTIPLIER
+ * 0x0e | -               | -                     | SAMPLE_RATE_CONVERTER
+ * 0x0f | -               | -                     | CONNECTORS
+ * 0x10 | -               | -                     | POWER_DOMAIN
+ */
+static enum uac_interface_subtype get_uac_interface_subtype(unsigned char c, int protocol)
 {
-       while (list->name) {
-               switch (protocol) {
-               case USB_AUDIO_CLASS_1:
-                       if (bmcontrols & (1 << list->bit))
-                               printf("%s%s Control\n", prefix, list->name);
-
-                       break;
-
-               case USB_AUDIO_CLASS_2: {
-                       const char * const ctrl_type[] = { "read-only", "ILLEGAL (0b10)", "read/write" };
-                       int ctrl = (bmcontrols >> (list->bit * 2)) & 0x3;
-
-                       if (ctrl)
-                               printf("%s%s Control (%s)\n", prefix, list->name, ctrl_type[ctrl-1]);
-
-                       break;
+       switch (protocol) {
+       case USB_AUDIO_CLASS_1:
+               switch(c) {
+               case 0x04: return UAC_INTERFACE_SUBTYPE_MIXER_UNIT;
+               case 0x05: return UAC_INTERFACE_SUBTYPE_SELECTOR_UNIT;
+               case 0x06: return UAC_INTERFACE_SUBTYPE_FEATURE_UNIT;
+               case 0x07: return UAC_INTERFACE_SUBTYPE_PROCESSING_UNIT;
+               case 0x08: return UAC_INTERFACE_SUBTYPE_EXTENSION_UNIT;
                }
-
-               } /* switch */
-
-               list++;
+               break;
+       case USB_AUDIO_CLASS_2:
+               switch(c) {
+               case 0x04: return UAC_INTERFACE_SUBTYPE_MIXER_UNIT;
+               case 0x05: return UAC_INTERFACE_SUBTYPE_SELECTOR_UNIT;
+               case 0x06: return UAC_INTERFACE_SUBTYPE_FEATURE_UNIT;
+               case 0x07: return UAC_INTERFACE_SUBTYPE_EFFECT_UNIT;
+               case 0x08: return UAC_INTERFACE_SUBTYPE_PROCESSING_UNIT;
+               case 0x09: return UAC_INTERFACE_SUBTYPE_EXTENSION_UNIT;
+               case 0x0a: return UAC_INTERFACE_SUBTYPE_CLOCK_SOURCE;
+               case 0x0b: return UAC_INTERFACE_SUBTYPE_CLOCK_SELECTOR;
+               case 0x0c: return UAC_INTERFACE_SUBTYPE_CLOCK_MULTIPLIER;
+               case 0x0d: return UAC_INTERFACE_SUBTYPE_SAMPLE_RATE_CONVERTER;
+               }
+               break;
+       case USB_AUDIO_CLASS_3:
+               /* No mapping required. */
+               break;
+       default:
+               /* Unknown protocol */
+               break;
        }
-}
 
-static const char * const chconfig_uac2[] = {
-       "Front Left (FL)", "Front Right (FR)", "Front Center (FC)", "Low Frequency Effects (LFE)",
-       "Back Left (BL)", "Back Right (BR)", "Front Left of Center (FLC)", "Front Right of Center (FRC)", "Back Center (BC)",
-       "Side Left (SL)", "Side Right (SR)",
-       "Top Center (TC)", "Top Front Left (TFL)", "Top Front Center (TFC)", "Top Front Right (TFR)", "Top Back Left (TBL)",
-       "Top Back Center (TBC)", "Top Back Right (TBR)", "Top Front Left of Center (TFLC)", "Top Front Right of Center (TFRC)",
-       "Left Low Frequency Effects (LLFE)", "Right Low Frequency Effects (RLFE)",
-       "Top Side Left (TSL)", "Top Side Right (TSR)", "Bottom Center (BC)",
-       "Back Left of Center (BLC)", "Back Right of Center (BRC)"
-};
+       /* If the protocol was unknown, or the value was not known to require
+        * mapping, just return it unchanged. */
+       return c;
+}
 
 static void dump_audiocontrol_interface(libusb_device_handle *dev, const unsigned char *buf, int protocol)
 {
-       static const char * const chconfig[] = {
-               "Left Front (L)", "Right Front (R)", "Center Front (C)", "Low Freqency Enhancement (LFE)",
-               "Left Surround (LS)", "Right Surround (RS)", "Left of Center (LC)", "Right of Center (RC)",
-               "Surround (S)", "Side Left (SL)", "Side Right (SR)", "Top (T)"
-       };
-       static const char * const clock_source_attrs[] = {
-               "External", "Internal fixed", "Internal variable", "Internal programmable"
-       };
-       unsigned int i, chcfg, j, k, N, termt, subtype;
-       char *chnames = NULL, *term = NULL, termts[128];
+       enum uac_interface_subtype subtype;
 
        if (buf[1] != USB_DT_CS_INTERFACE)
                printf("      Warning: Invalid descriptor\n");
@@ -989,537 +994,63 @@ static void dump_audiocontrol_interface(libusb_device_handle *dev, const unsigne
               "        bDescriptorSubtype  %5u ",
               buf[0], buf[1], buf[2]);
 
-       /*
-        * This is an utter mess - UAC2 defines some bDescriptorSubtype differently, so we have to do some ugly remapping here:
-        *
-        * bDescriptorSubtype           UAC1                    UAC2
-        * ------------------------------------------------------------------------
-        * 0x07                         PROCESSING_UNIT         EFFECT_UNIT
-        * 0x08                         EXTENSION_UNIT          PROCESSING_UNIT
-        * 0x09                         -                       EXTENSION_UNIT
-        *
-        */
-
-       if (protocol == USB_AUDIO_CLASS_2)
-               switch(buf[2]) {
-               case 0x07: subtype = 0xf0; break; /* effect unit */
-               case 0x08: subtype = 0x07; break; /* processing unit */
-               case 0x09: subtype = 0x08; break; /* extension unit */
-               default: subtype = buf[2]; break; /* everything else is identical */
-               }
-       else
-               subtype = buf[2];
+       subtype = get_uac_interface_subtype(buf[2], protocol);
 
        switch (subtype) {
-       case 0x01:  /* HEADER */
-               printf("(HEADER)\n");
-               switch (protocol) {
-               case USB_AUDIO_CLASS_1:
-                       if (buf[0] < 8+buf[7])
-                               printf("      Warning: Descriptor too short\n");
-                       printf("        bcdADC              %2x.%02x\n"
-                              "        wTotalLength        %5u\n"
-                              "        bInCollection       %5u\n",
-                              buf[4], buf[3], buf[5] | (buf[6] << 8), buf[7]);
-                       for (i = 0; i < buf[7]; i++)
-                               printf("        baInterfaceNr(%2u)   %5u\n", i, buf[8+i]);
-                       dump_junk(buf, "        ", 8+buf[7]);
-                       break;
-               case USB_AUDIO_CLASS_2:
-                       if (buf[0] < 9)
-                               printf("      Warning: Descriptor too short\n");
-                       printf("        bcdADC              %2x.%02x\n"
-                              "        bCategory           %5u\n"
-                              "        wTotalLength        %5u\n"
-                              "        bmControl            0x%02x\n",
-                              buf[4], buf[3], buf[5], buf[6] | (buf[7] << 8), buf[8]);
-                       dump_audio_bmcontrols("          ", buf[8], uac2_interface_header_bmcontrols, protocol);
-                       break;
-               }
+       case UAC_INTERFACE_SUBTYPE_HEADER:
+               dump_audio_subtype(dev, "HEADER", desc_audio_ac_header, buf, protocol, 4);
                break;
 
-       case 0x02:  /* INPUT_TERMINAL */
-               printf("(INPUT_TERMINAL)\n");
-               termt = buf[4] | (buf[5] << 8);
-               get_audioterminal_string(termts, sizeof(termts), termt);
-
-               switch (protocol) {
-               case USB_AUDIO_CLASS_1:
-                       chnames = get_dev_string(dev, buf[10]);
-                       term = get_dev_string(dev, buf[11]);
-                       if (buf[0] < 12)
-                               printf("      Warning: Descriptor too short\n");
-                       chcfg = buf[8] | (buf[9] << 8);
-                       printf("        bTerminalID         %5u\n"
-                              "        wTerminalType      0x%04x %s\n"
-                              "        bAssocTerminal      %5u\n"
-                              "        bNrChannels         %5u\n"
-                              "        wChannelConfig     0x%04x\n",
-                              buf[3], termt, termts, buf[6], buf[7], chcfg);
-                       for (i = 0; i < 12; i++)
-                               if ((chcfg >> i) & 1)
-                                       printf("          %s\n", chconfig[i]);
-                       printf("        iChannelNames       %5u %s\n"
-                              "        iTerminal           %5u %s\n",
-                              buf[10], chnames, buf[11], term);
-                       dump_junk(buf, "        ", 12);
-                       break;
-               case USB_AUDIO_CLASS_2:
-                       chnames = get_dev_string(dev, buf[13]);
-                       term = get_dev_string(dev, buf[16]);
-                       if (buf[0] < 17)
-                               printf("      Warning: Descriptor too short\n");
-                       chcfg = buf[9] | (buf[10] << 8) | (buf[11] << 16) | (buf[12] << 24);
-                       printf("        bTerminalID         %5u\n"
-                              "        wTerminalType      0x%04x %s\n"
-                              "        bAssocTerminal      %5u\n"
-                              "        bCSourceID          %5d\n"
-                              "        bNrChannels         %5u\n"
-                              "        bmChannelConfig   0x%08x\n",
-                              buf[3], termt, termts, buf[6], buf[7], buf[8], chcfg);
-                       for (i = 0; i < 26; i++)
-                               if ((chcfg >> i) & 1)
-                                       printf("          %s\n", chconfig_uac2[i]);
-                       printf("        bmControls    0x%04x\n", buf[14] | (buf[15] << 8));
-                       dump_audio_bmcontrols("          ", buf[14] | (buf[15] << 8), uac2_input_term_bmcontrols, protocol);
-                       printf("        iChannelNames       %5u %s\n"
-                              "        iTerminal           %5u %s\n",
-                              buf[13], chnames, buf[16], term);
-                       dump_junk(buf, "        ", 17);
-                       break;
-               } /* switch (protocol) */
-
+       case UAC_INTERFACE_SUBTYPE_INPUT_TERMINAL:
+               dump_audio_subtype(dev, "INPUT_TERMINAL", desc_audio_ac_input_terminal, buf, protocol, 4);
                break;
 
-       case 0x03:  /* OUTPUT_TERMINAL */
-               printf("(OUTPUT_TERMINAL)\n");
-               switch (protocol) {
-               case USB_AUDIO_CLASS_1:
-                       term = get_dev_string(dev, buf[8]);
-                       termt = buf[4] | (buf[5] << 8);
-                       get_audioterminal_string(termts, sizeof(termts), termt);
-                       if (buf[0] < 9)
-                               printf("      Warning: Descriptor too short\n");
-                       printf("        bTerminalID         %5u\n"
-                              "        wTerminalType      0x%04x %s\n"
-                              "        bAssocTerminal      %5u\n"
-                              "        bSourceID           %5u\n"
-                              "        iTerminal           %5u %s\n",
-                              buf[3], termt, termts, buf[6], buf[7], buf[8], term);
-                       dump_junk(buf, "        ", 9);
-                       break;
-               case USB_AUDIO_CLASS_2:
-                       term = get_dev_string(dev, buf[11]);
-                       termt = buf[4] | (buf[5] << 8);
-                       get_audioterminal_string(termts, sizeof(termts), termt);
-                       if (buf[0] < 12)
-                               printf("      Warning: Descriptor too short\n");
-                       printf("        bTerminalID         %5u\n"
-                              "        wTerminalType      0x%04x %s\n"
-                              "        bAssocTerminal      %5u\n"
-                              "        bSourceID           %5u\n"
-                              "        bCSourceID          %5u\n"
-                              "        bmControls         0x%04x\n",
-                              buf[3], termt, termts, buf[6], buf[7], buf[8], buf[9] | (buf[10] << 8));
-                       dump_audio_bmcontrols("          ", buf[9] | (buf[10] << 8), uac2_output_term_bmcontrols, protocol);
-                       printf("        iTerminal           %5u %s\n", buf[11], term);
-                       dump_junk(buf, "        ", 12);
-                       break;
-               } /* switch (protocol) */
-
+       case UAC_INTERFACE_SUBTYPE_OUTPUT_TERMINAL:
+               dump_audio_subtype(dev, "OUTPUT_TERMINAL", desc_audio_ac_output_terminal, buf, protocol, 4);
                break;
 
-       case 0x04:  /* MIXER_UNIT */
-               printf("(MIXER_UNIT)\n");
-
-               switch (protocol) {
-               case USB_AUDIO_CLASS_1:
-                       j = buf[4];
-                       k = buf[j+5];
-                       if (j == 0 || k == 0) {
-                               printf("      Warning: mixer with %5u input and %5u output channels.\n", j, k);
-                               N = 0;
-                       } else {
-                               N = 1+(j*k-1)/8;
-                       }
-                       chnames = get_dev_string(dev, buf[8+j]);
-                       term = get_dev_string(dev, buf[9+j+N]);
-                       if (buf[0] < 10+j+N)
-                               printf("      Warning: Descriptor too short\n");
-                       chcfg = buf[6+j] | (buf[7+j] << 8);
-                       printf("        bUnitID             %5u\n"
-                              "        bNrInPins           %5u\n",
-                              buf[3], buf[4]);
-                       for (i = 0; i < j; i++)
-                               printf("        baSourceID(%2u)      %5u\n", i, buf[5+i]);
-                       printf("        bNrChannels         %5u\n"
-                              "        wChannelConfig     0x%04x\n",
-                              buf[5+j], chcfg);
-                       for (i = 0; i < 12; i++)
-                               if ((chcfg >> i) & 1)
-                                       printf("          %s\n", chconfig[i]);
-                       printf("        iChannelNames       %5u %s\n",
-                              buf[8+j], chnames);
-                       for (i = 0; i < N; i++)
-                               printf("        bmControls         0x%02x\n", buf[9+j+i]);
-                       printf("        iMixer              %5u %s\n", buf[9+j+N], term);
-                       dump_junk(buf, "        ", 10+j+N);
-                       break;
-
-               case USB_AUDIO_CLASS_2:
-                       j = buf[4];
-                       k = buf[0] - 13 - j;
-                       chnames = get_dev_string(dev, buf[10+j]);
-                       term = get_dev_string(dev, buf[12+j+k]);
-                       chcfg =  buf[6+j] | (buf[7+j] << 8) | (buf[8+j] << 16) | (buf[9+j] << 24);
-
-                       printf("        bUnitID             %5u\n"
-                              "        bNrPins             %5u\n",
-                              buf[3], buf[4]);
-                       for (i = 0; i < j; i++)
-                               printf("        baSourceID(%2u)      %5u\n", i, buf[5+i]);
-                       printf("        bNrChannels         %5u\n"
-                              "        bmChannelConfig    0x%08x\n", buf[5+j], chcfg);
-                       for (i = 0; i < 26; i++)
-                               if ((chcfg >> i) & 1)
-                                       printf("          %s\n", chconfig_uac2[i]);
-                       printf("        iChannelNames       %5u %s\n", buf[10+j], chnames);
-
-                       N = 0;
-                       for (i = 0; i < k; i++)
-                               N |= buf[11+j+i] << (i * 8);
-
-                       dump_bytes(buf+11+j, k);
-
-                       printf("        bmControls         %02x\n", buf[11+j+k]);
-                       dump_audio_bmcontrols("          ", buf[11+j+k], uac2_mixer_unit_bmcontrols, protocol);
-
-                       printf("        iMixer             %5u %s\n", buf[12+j+k], term);
-                       dump_junk(buf, "        ", 13+j+k);
-                       break;
-               } /* switch (protocol) */
+       case UAC_INTERFACE_SUBTYPE_MIXER_UNIT:
+               dump_audio_subtype(dev, "MIXER_UNIT", desc_audio_ac_mixer_unit, buf, protocol, 4);
                break;
 
-       case 0x05:  /* SELECTOR_UNIT */
-               printf("(SELECTOR_UNIT)\n");
-               switch (protocol) {
-               case USB_AUDIO_CLASS_1:
-                       if (buf[0] < 6+buf[4])
-                               printf("      Warning: Descriptor too short\n");
-                       term = get_dev_string(dev, buf[5+buf[4]]);
-
-                       printf("        bUnitID             %5u\n"
-                              "        bNrInPins           %5u\n",
-                              buf[3], buf[4]);
-                       for (i = 0; i < buf[4]; i++)
-                               printf("        baSource(%2u)        %5u\n", i, buf[5+i]);
-                       printf("        iSelector           %5u %s\n",
-                              buf[5+buf[4]], term);
-                       dump_junk(buf, "        ", 6+buf[4]);
-                       break;
-               case USB_AUDIO_CLASS_2:
-                       if (buf[0] < 7+buf[4])
-                               printf("      Warning: Descriptor too short\n");
-                       term = get_dev_string(dev, buf[6+buf[4]]);
-
-                       printf("        bUnitID             %5u\n"
-                              "        bNrInPins           %5u\n",
-                              buf[3], buf[4]);
-                       for (i = 0; i < buf[4]; i++)
-                               printf("        baSource(%2u)        %5u\n", i, buf[5+i]);
-                       printf("        bmControls           0x%02x\n", buf[5+buf[4]]);
-                       dump_audio_bmcontrols("          ", buf[5+buf[4]], uac2_selector_bmcontrols, protocol);
-                       printf("        iSelector           %5u %s\n",
-                              buf[6+buf[4]], term);
-                       dump_junk(buf, "        ", 7+buf[4]);
-                       break;
-               } /* switch (protocol) */
-
+       case UAC_INTERFACE_SUBTYPE_SELECTOR_UNIT:
+               dump_audio_subtype(dev, "SELECTOR_UNIT", desc_audio_ac_selector_unit, buf, protocol, 4);
                break;
 
-       case 0x06:  /* FEATURE_UNIT */
-               printf("(FEATURE_UNIT)\n");
-
-               switch (protocol) {
-               case USB_AUDIO_CLASS_1:
-                       j = buf[5];
-                       if (!j)
-                               j = 1;
-                       k = (buf[0] - 7) / j;
-                       if (buf[0] < 7+buf[5]*k)
-                               printf("      Warning: Descriptor too short\n");
-                       term = get_dev_string(dev, buf[6+buf[5]*k]);
-                       printf("        bUnitID             %5u\n"
-                              "        bSourceID           %5u\n"
-                              "        bControlSize        %5u\n",
-                              buf[3], buf[4], buf[5]);
-                       for (i = 0; i < k; i++) {
-                               chcfg = buf[6+buf[5]*i];
-                               if (buf[5] > 1)
-                                       chcfg |= (buf[7+buf[5]*i] << 8);
-                               for (j = 0; j < buf[5]; j++)
-                                       printf("        bmaControls(%2u)      0x%02x\n", i, buf[6+buf[5]*i+j]);
-
-                               dump_audio_bmcontrols("          ", chcfg, uac_fu_bmcontrols, protocol);
-                       }
-                       printf("        iFeature            %5u %s\n", buf[6+buf[5]*k], term);
-                       dump_junk(buf, "        ", 7+buf[5]*k);
-                       break;
-               case USB_AUDIO_CLASS_2:
-                       if (buf[0] < 10)
-                               printf("      Warning: Descriptor too short\n");
-                       k = (buf[0] - 6) / 4;
-                       printf("        bUnitID             %5u\n"
-                              "        bSourceID           %5u\n",
-                              buf[3], buf[4]);
-                       for (i = 0; i < k; i++) {
-                               chcfg = buf[5+(4*i)] |
-                                       buf[6+(4*i)] << 8 |
-                                       buf[7+(4*i)] << 16 |
-                                       buf[8+(4*i)] << 24;
-                               printf("        bmaControls(%2u)      0x%08x\n", i, chcfg);
-                               dump_audio_bmcontrols("          ", chcfg, uac_fu_bmcontrols, protocol);
-                       }
-                       term = get_dev_string(dev, buf[5+k*4]);
-                       printf("        iFeature            %5u %s\n", buf[5+(k*4)], term);
-                       dump_junk(buf, "        ", 6+(k*4));
-                       break;
-               } /* switch (protocol) */
-
+       case UAC_INTERFACE_SUBTYPE_FEATURE_UNIT:
+               dump_audio_subtype(dev, "FEATURE_UNIT", desc_audio_ac_feature_unit, buf, protocol, 4);
                break;
 
-       case 0x07:  /* PROCESSING_UNIT */
-               printf("(PROCESSING_UNIT)\n");
-
-               switch (protocol) {
-               case USB_AUDIO_CLASS_1:
-                       j = buf[6];
-                       k = buf[11+j];
-                       chnames = get_dev_string(dev, buf[10+j]);
-                       term = get_dev_string(dev, buf[12+j+k]);
-                       chcfg = buf[8+j] | (buf[9+j] << 8);
-                       if (buf[0] < 13+j+k)
-                               printf("      Warning: Descriptor too short\n");
-                       printf("        bUnitID             %5u\n"
-                              "        wProcessType        %5u\n"
-                              "        bNrPins             %5u\n",
-                              buf[3], buf[4] | (buf[5] << 8), buf[6]);
-                       for (i = 0; i < j; i++)
-                               printf("        baSourceID(%2u)      %5u\n", i, buf[7+i]);
-                       printf("        bNrChannels         %5u\n"
-                              "        wChannelConfig     0x%04x\n", buf[7+j], chcfg);
-                       for (i = 0; i < 12; i++)
-                               if ((chcfg >> i) & 1)
-                                       printf("          %s\n", chconfig[i]);
-                       printf("        iChannelNames       %5u %s\n"
-                              "        bControlSize        %5u\n", buf[10+j], chnames, buf[11+j]);
-                       for (i = 0; i < k; i++)
-                               printf("        bmControls(%2u)       0x%02x\n", i, buf[12+j+i]);
-                       if (buf[12+j] & 1)
-                               printf("          Enable Processing\n");
-                       printf("        iProcessing         %5u %s\n"
-                              "        Process-Specific    ", buf[12+j+k], term);
-                       dump_bytes(buf+(13+j+k), buf[0]-(13+j+k));
-                       break;
-               case USB_AUDIO_CLASS_2:
-                       j = buf[6];
-                       k = buf[0] - 17 - j;
-                       chnames = get_dev_string(dev, buf[12+j]);
-                       term = get_dev_string(dev, buf[15+j+k]);
-                       chcfg =  buf[8+j] |
-                               (buf[9+j] << 8) |
-                               (buf[10+j] << 16) |
-                               (buf[11+j] << 24);
-
-                       printf("        bUnitID             %5u\n"
-                              "        wProcessType        %5u\n"
-                              "        bNrPins             %5u\n",
-                              buf[3], buf[4] | (buf[5] << 8), buf[6]);
-                       for (i = 0; i < j; i++)
-                               printf("        baSourceID(%2u)      %5u\n", i, buf[5+i]);
-                       printf("        bNrChannels         %5u\n"
-                              "        bmChannelConfig    0x%08x\n", buf[7+j], chcfg);
-                       for (i = 0; i < 26; i++)
-                               if ((chcfg >> i) & 1)
-                                       printf("          %s\n", chconfig_uac2[i]);
-                       printf("        iChannelNames       %5u %s\n"
-                              "        bmControls        0x%04x\n", buf[12+j], chnames, buf[13+j] | (buf[14+j] << 8));
-                       if (buf[12+j] & 1)
-                               printf("          Enable Processing\n");
-                       printf("        iProcessing         %5u %s\n"
-                              "        Process-Specific    ", buf[15+j], term);
-                       dump_bytes(buf+(16+j), k);
-                       break;
-               } /* switch (protocol) */
-
+       case UAC_INTERFACE_SUBTYPE_PROCESSING_UNIT:
+               dump_audio_subtype(dev, "PROCESSING_UNIT", desc_audio_ac_processing_unit, buf, protocol, 4);
                break;
 
-       case 0x08:  /* EXTENSION_UNIT */
-               printf("(EXTENSION_UNIT)\n");
-
-               switch (protocol) {
-               case USB_AUDIO_CLASS_1:
-                       j = buf[6];
-                       k = buf[11+j];
-                       chnames = get_dev_string(dev, buf[10+j]);
-                       term = get_dev_string(dev, buf[12+j+k]);
-                       chcfg = buf[8+j] | (buf[9+j] << 8);
-                       if (buf[0] < 13+j+k)
-                               printf("      Warning: Descriptor too short\n");
-                       printf("        bUnitID             %5u\n"
-                              "        wExtensionCode      %5u\n"
-                              "        bNrPins             %5u\n",
-                              buf[3], buf[4] | (buf[5] << 8), buf[6]);
-                       for (i = 0; i < j; i++)
-                               printf("        baSourceID(%2u)      %5u\n", i, buf[7+i]);
-                       printf("        bNrChannels         %5u\n"
-                              "        wChannelConfig      %5u\n", buf[7+j], chcfg);
-                       for (i = 0; i < 12; i++)
-                               if ((chcfg >> i) & 1)
-                                       printf("          %s\n", chconfig[i]);
-                       printf("        iChannelNames       %5u %s\n"
-                              "        bControlSize        %5u\n", buf[10+j], chnames, buf[11+j]);
-                       for (i = 0; i < k; i++)
-                               printf("        bmControls(%2u)       0x%02x\n", i, buf[12+j+i]);
-                       if (buf[12+j] & 1)
-                               printf("          Enable Processing\n");
-                       printf("        iExtension          %5u %s\n",
-                              buf[12+j+k], term);
-                       dump_junk(buf, "        ", 13+j+k);
-                       break;
-               case USB_AUDIO_CLASS_2:
-                       j = buf[6];
-                       chnames = get_dev_string(dev, buf[13+j]);
-                       term = get_dev_string(dev, buf[15+j]);
-                       chcfg = buf[9+j] | (buf[10+j] << 8) | (buf[11+j] << 16) | (buf[12+j] << 24);
-                       if (buf[0] < 16+j)
-                               printf("      Warning: Descriptor too short\n");
-                       printf("        bUnitID             %5u\n"
-                              "        wExtensionCode      %5u\n"
-                              "        bNrPins             %5u\n",
-                              buf[3], buf[4] | (buf[5] << 8), buf[6]);
-                       for (i = 0; i < j; i++)
-                               printf("        baSourceID(%2u)      %5u\n", i, buf[7+i]);
-                       printf("        bNrChannels         %5u\n"
-                              "        wChannelConfig      %5u\n", buf[7+j], chcfg);
-                       for (i = 0; i < 12; i++)
-                               if ((chcfg >> i) & 1)
-                                       printf("          %s\n", chconfig[i]);
-                       printf("        iChannelNames       %5u %s\n"
-                              "        bmControls        0x%02x\n", buf[13+j], chnames, buf[14+j]);
-                       dump_audio_bmcontrols("          ", buf[14+j], uac2_extension_unit_bmcontrols, protocol);
-
-                       printf("        iExtension          %5u %s\n",
-                              buf[15+j], term);
-                       dump_junk(buf, "        ", 16+j);
-                       break;
-               } /* switch (protocol) */
-
+       case UAC_INTERFACE_SUBTYPE_EXTENSION_UNIT:
+               dump_audio_subtype(dev, "EXTENSION_UNIT", desc_audio_ac_extension_unit, buf, protocol, 4);
                break;
 
-       case 0x0a:  /* CLOCK_SOURCE */
-               printf ("(CLOCK_SOURCE)\n");
-               if (protocol != USB_AUDIO_CLASS_2)
-                       printf("      Warning: CLOCK_SOURCE descriptors are illegal for UAC1\n");
-
-               if (buf[0] < 8)
-                       printf("      Warning: Descriptor too short\n");
-
-               printf("        bClockID            %5u\n"
-                      "        bmAttributes         0x%02x %s Clock %s\n",
-                      buf[3], buf[4], clock_source_attrs[buf[4] & 3],
-                      (buf[4] & 4) ? "(synced to SOF)" : "");
-
-               printf("        bmControls           0x%02x\n", buf[5]);
-               dump_audio_bmcontrols("          ", buf[5], uac2_clock_source_bmcontrols, protocol);
-
-               term = get_dev_string(dev, buf[7]);
-               printf("        bAssocTerminal      %5u\n", buf[6]);
-               printf("        iClockSource        %5u %s\n", buf[7], term);
-               dump_junk(buf, "        ", 8);
+       case UAC_INTERFACE_SUBTYPE_CLOCK_SOURCE:
+               dump_audio_subtype(dev, "CLOCK_SOURCE", desc_audio_ac_clock_source, buf, protocol, 4);
                break;
 
-       case 0x0b:  /* CLOCK_SELECTOR */
-               printf("(CLOCK_SELECTOR)\n");
-               if (protocol != USB_AUDIO_CLASS_2)
-                       printf("      Warning: CLOCK_SELECTOR descriptors are illegal for UAC1\n");
-
-               if (buf[0] < 7+buf[4])
-                       printf("      Warning: Descriptor too short\n");
-               term = get_dev_string(dev, buf[6+buf[4]]);
-
-               printf("        bUnitID             %5u\n"
-                      "        bNrInPins           %5u\n",
-                      buf[3], buf[4]);
-               for (i = 0; i < buf[4]; i++)
-                       printf("        baCSourceID(%2u)     %5u\n", i, buf[5+i]);
-               printf("        bmControls           0x%02x\n", buf[5+buf[4]]);
-               dump_audio_bmcontrols("          ", buf[5+buf[4]], uac2_clock_selector_bmcontrols, protocol);
-
-               printf("        iClockSelector      %5u %s\n",
-                      buf[6+buf[4]], term);
-               dump_junk(buf, "        ", 7+buf[4]);
+       case UAC_INTERFACE_SUBTYPE_CLOCK_SELECTOR:
+               dump_audio_subtype(dev, "CLOCK_SELECTOR", desc_audio_ac_clock_selector, buf, protocol, 4);
                break;
 
-       case 0x0c:  /* CLOCK_MULTIPLIER */
-               printf("(CLOCK_MULTIPLIER)\n");
-               if (protocol != USB_AUDIO_CLASS_2)
-                       printf("      Warning: CLOCK_MULTIPLIER descriptors are illegal for UAC1\n");
-
-               if (buf[0] < 7)
-                       printf("      Warning: Descriptor too short\n");
-
-               printf("        bClockID            %5u\n"
-                      "        bCSourceID          %5u\n",
-                      buf[3], buf[4]);
-
-               printf("        bmControls           0x%02x\n", buf[5]);
-               dump_audio_bmcontrols("          ", buf[5], uac2_clock_multiplier_bmcontrols, protocol);
-
-               term = get_dev_string(dev, buf[6]);
-               printf("        iClockMultiplier    %5u %s\n", buf[6], term);
-               dump_junk(buf, "        ", 7);
+       case UAC_INTERFACE_SUBTYPE_CLOCK_MULTIPLIER:
+               dump_audio_subtype(dev, "CLOCK_MULTIPLIER", desc_audio_ac_clock_multiplier, buf, protocol, 4);
                break;
 
-       case 0x0d:  /* SAMPLE_RATE_CONVERTER_UNIT */
-               printf("(SAMPLE_RATE_CONVERTER_UNIT)\n");
-               if (protocol != USB_AUDIO_CLASS_2)
-                       printf("      Warning: SAMPLE_RATE_CONVERTER_UNIT descriptors are illegal for UAC1\n");
-
-               if (buf[0] < 8)
-                       printf("      Warning: Descriptor too short\n");
-
-               term = get_dev_string(dev, buf[7]);
-               printf("        bUnitID             %5u\n"
-                      "        bSourceID           %5u\n"
-                      "        bCSourceInID        %5u\n"
-                      "        bCSourceOutID       %5u\n"
-                      "        iSRC                %5u %s\n",
-                      buf[3], buf[4], buf[5], buf[6], buf[7], term);
-               dump_junk(buf, "        ", 8);
+       case UAC_INTERFACE_SUBTYPE_SAMPLE_RATE_CONVERTER:
+               dump_audio_subtype(dev, "SAMPLING_RATE_CONVERTER", desc_audio_ac_clock_multiplier, buf, protocol, 4);
                break;
 
-       case 0xf0:  /* EFFECT_UNIT - the real value is 0x07, see above for the reason for remapping */
-               printf("(EFFECT_UNIT)\n");
+       case UAC_INTERFACE_SUBTYPE_EFFECT_UNIT:
+               dump_audio_subtype(dev, "EFFECT_UNIT", desc_audio_ac_effect_unit, buf, protocol, 4);
+               break;
 
-               if (buf[0] < 16)
-                       printf("      Warning: Descriptor too short\n");
-               k = (buf[0] - 16) / 4;
-               term = get_dev_string(dev, buf[15+k*4]);
-               printf("        bUnitID             %5u\n"
-                      "        wEffectType         %5u\n"
-                      "        bSourceID           %5u\n",
-                      buf[3], buf[4] | (buf[5] << 8), buf[6]);
-               for (i = 0; i < k; i++) {
-                       chcfg = buf[7+(4*i)] |
-                               buf[8+(4*i)] << 8 |
-                               buf[9+(4*i)] << 16 |
-                               buf[10+(4*i)] << 24;
-                       printf("        bmaControls(%2u)      0x%08x\n", i, chcfg);
-                       /* TODO: parse effect-specific controls */
-               }
-               printf("        iEffect             %5u %s\n", buf[15+(k*4)], term);
-               dump_junk(buf, "        ", 16+(k*4));
+       case UAC_INTERFACE_SUBTYPE_POWER_DOMAIN:
+               dump_audio_subtype(dev, "POWER_DOMAIN", desc_audio_ac_power_domain, buf, protocol, 4);
                break;
 
        default:
@@ -1528,16 +1059,8 @@ static void dump_audiocontrol_interface(libusb_device_handle *dev, const unsigne
                dump_bytes(buf+3, buf[0]-3);
                break;
        }
-
-       free(chnames);
-       free(term);
 }
 
-static const struct bmcontrol uac2_as_interface_bmcontrols[] = {
-       { "Active Alternate Setting",   0 },
-       { "Valid Alternate Setting",    1 },
-       { NULL }
-};
 
 static void dump_audiostreaming_interface(libusb_device_handle *dev, const unsigned char *buf, int protocol)
 {
@@ -1550,7 +1073,6 @@ static void dump_audiostreaming_interface(libusb_device_handle *dev, const unsig
                "IEC1937_MPEG-2_Layer1_LS", "IEC1937_MPEG-2_Layer2/3_LS" };
        unsigned int i, j, fmttag;
        const char *fmtptr = "undefined";
-       char *name = NULL;
 
        if (buf[1] != USB_DT_CS_INTERFACE)
                printf("      Warning: Invalid descriptor\n");
@@ -1563,54 +1085,7 @@ static void dump_audiostreaming_interface(libusb_device_handle *dev, const unsig
               buf[0], buf[1], buf[2]);
        switch (buf[2]) {
        case 0x01: /* AS_GENERAL */
-               printf("(AS_GENERAL)\n");
-
-               switch (protocol) {
-               case USB_AUDIO_CLASS_1:
-                       if (buf[0] < 7)
-                               printf("      Warning: Descriptor too short\n");
-                       fmttag = buf[5] | (buf[6] << 8);
-                       if (fmttag <= 5)
-                               fmtptr = fmtItag[fmttag];
-                       else if (fmttag >= 0x1000 && fmttag <= 0x1002)
-                               fmtptr = fmtIItag[fmttag & 0xfff];
-                       else if (fmttag >= 0x2000 && fmttag <= 0x2006)
-                               fmtptr = fmtIIItag[fmttag & 0xfff];
-                       printf("        bTerminalLink       %5u\n"
-                              "        bDelay              %5u frames\n"
-                              "        wFormatTag          %5u %s\n",
-                              buf[3], buf[4], fmttag, fmtptr);
-                       dump_junk(buf, "        ", 7);
-                       break;
-               case USB_AUDIO_CLASS_2:
-                       if (buf[0] < 16)
-                               printf("      Warning: Descriptor too short\n");
-                       printf("        bTerminalLink       %5u\n"
-                              "        bmControls           0x%02x\n",
-                              buf[3], buf[4]);
-                       dump_audio_bmcontrols("          ", buf[4], uac2_as_interface_bmcontrols, protocol);
-
-                       printf("        bFormatType         %5u\n", buf[5]);
-                       fmttag = buf[6] | (buf[7] << 8) | (buf[8] << 16) | (buf[9] << 24);
-                       printf("        bmFormats         0x%08x\n", fmttag);
-                       for (i=0; i<5; i++)
-                               if ((fmttag >> i) & 1)
-                                       printf("          %s\n", fmtItag[i+1]);
-
-                       j = buf[11] | (buf[12] << 8) | (buf[13] << 16) | (buf[14] << 24);
-                       printf("        bNrChannels         %5u\n"
-                              "        bmChannelConfig   0x%08x\n",
-                              buf[10], j);
-                       for (i = 0; i < 26; i++)
-                               if ((j >> i) & 1)
-                                       printf("          %s\n", chconfig_uac2[i]);
-
-                       name = get_dev_string(dev, buf[15]);
-                       printf("        iChannelNames       %5u %s\n", buf[15], name);
-                       dump_junk(buf, "        ", 16);
-                       break;
-               } /* switch (protocol) */
-
+               dump_audio_subtype(dev, "AS_GENERAL", desc_audio_as_interface, buf, protocol, 4);
                break;
 
        case 0x02: /* FORMAT_TYPE */
@@ -1865,66 +1340,23 @@ static void dump_audiostreaming_interface(libusb_device_handle *dev, const unsig
                dump_bytes(buf+3, buf[0]-3);
                break;
        }
-
-       free(name);
 }
 
-static const struct bmcontrol uac2_audio_endpoint_bmcontrols[] = {
-       { "Pitch",              0 },
-       { "Data Overrun",       1 },
-       { "Data Underrun",      2 },
-       { NULL }
-};
-
-static void dump_audiostreaming_endpoint(const unsigned char *buf, int protocol)
+static void dump_audiostreaming_endpoint(libusb_device_handle *dev, const unsigned char *buf, int protocol)
 {
-       static const char * const lockdelunits[] = { "Undefined", "Milliseconds", "Decoded PCM samples", "Reserved" };
-       unsigned int lckdelidx;
+       static const char * const subtype[] = { "invalid", "EP_GENERAL" };
 
        if (buf[1] != USB_DT_CS_ENDPOINT)
                printf("      Warning: Invalid descriptor\n");
-       else if (buf[0] < ((protocol == USB_AUDIO_CLASS_1) ? 7 : 8))
-               printf("      Warning: Descriptor too short\n");
-       printf("        AudioControl Endpoint Descriptor:\n"
+
+       printf("        AudioStreaming Endpoint Descriptor:\n"
               "          bLength             %5u\n"
               "          bDescriptorType     %5u\n"
-              "          bDescriptorSubtype  %5u (%s)\n"
-              "          bmAttributes         0x%02x\n",
-              buf[0], buf[1], buf[2], buf[2] == 1 ? "EP_GENERAL" : "invalid", buf[3]);
-
-       switch (protocol) {
-       case USB_AUDIO_CLASS_1:
-               if (buf[3] & 1)
-                       printf("            Sampling Frequency\n");
-               if (buf[3] & 2)
-                       printf("            Pitch\n");
-               if (buf[3] & 128)
-                       printf("            MaxPacketsOnly\n");
-               lckdelidx = buf[4];
-               if (lckdelidx > 3)
-                       lckdelidx = 3;
-               printf("          bLockDelayUnits     %5u %s\n"
-                      "          wLockDelay          %5u %s\n",
-                      buf[4], lockdelunits[lckdelidx], buf[5] | (buf[6] << 8), lockdelunits[lckdelidx]);
-               dump_junk(buf, "        ", 7);
-               break;
-
-       case USB_AUDIO_CLASS_2:
-               if (buf[3] & 128)
-                       printf("            MaxPacketsOnly\n");
-
-               printf("          bmControls           0x%02x\n", buf[4]);
-               dump_audio_bmcontrols("          ", buf[4], uac2_audio_endpoint_bmcontrols, protocol);
+              "          bDescriptorSubtype  %5u ",
+              buf[0], buf[1], buf[2]);
 
-               lckdelidx = buf[5];
-               if (lckdelidx > 3)
-                       lckdelidx = 3;
-               printf("          bLockDelayUnits     %5u %s\n"
-                      "          wLockDelay          %5u\n",
-                      buf[5], lockdelunits[lckdelidx], buf[6] | (buf[7] << 8));
-               dump_junk(buf, "        ", 8);
-               break;
-       } /* switch protocol */
+       dump_audio_subtype(dev, subtype[buf[2] == 1],
+                       desc_audio_as_isochronous_audio_data_endpoint, buf, protocol, 5);
 }
 
 static void dump_midistreaming_interface(libusb_device_handle *dev, const unsigned char *buf)
@@ -1950,7 +1382,7 @@ static void dump_midistreaming_interface(libusb_device_handle *dev, const unsign
                        printf("      Warning: Descriptor too short\n");
                tlength = buf[5] | (buf[6] << 8);
                printf("        bcdADC              %2x.%02x\n"
-                      "        wTotalLength        %5u\n",
+                      "        wTotalLength       0x%04x\n",
                       buf[4], buf[3], tlength);
                dump_junk(buf, "        ", 7);
                break;
@@ -2074,14 +1506,14 @@ static void dump_midistreaming_endpoint(const unsigned char *buf)
  * Video Class descriptor dump
  */
 
-static void dump_videocontrol_interface(libusb_device_handle *dev, const unsigned char *buf)
+static void dump_videocontrol_interface(libusb_device_handle *dev, const unsigned char *buf, int protocol)
 {
        static const char * const ctrlnames[] = {
                "Brightness", "Contrast", "Hue", "Saturation", "Sharpness", "Gamma",
                "White Balance Temperature", "White Balance Component", "Backlight Compensation",
                "Gain", "Power Line Frequency", "Hue, Auto", "White Balance Temperature, Auto",
                "White Balance Component, Auto", "Digital Multiplier", "Digital Multiplier Limit",
-               "Analog Video Standard", "Analog Video Lock Status"
+               "Analog Video Standard", "Analog Video Lock Status", "Contrast, Auto"
        };
        static const char * const camctrlnames[] = {
                "Scanning Mode", "Auto-Exposure Mode", "Auto-Exposure Priority",
@@ -2089,7 +1521,15 @@ static void dump_videocontrol_interface(libusb_device_handle *dev, const unsigne
                "Focus (Relative)", "Iris (Absolute)", "Iris (Relative)", "Zoom (Absolute)",
                "Zoom (Relative)", "PanTilt (Absolute)", "PanTilt (Relative)",
                "Roll (Absolute)", "Roll (Relative)", "Reserved", "Reserved", "Focus, Auto",
-               "Privacy"
+               "Privacy", "Focus, Simple", "Window", "Region of Interest"
+       };
+       static const char * const enctrlnames[] = {
+               "Select Layer", "Profile and Toolset", "Video Resolution", "Minimum Frame Interval",
+               "Slice Mode", "Rate Control Mode", "Average Bit Rate", "CPB Size", "Peak Bit Rate",
+               "Quantization Parameter", "Synchronization and Long-Term Reference Frame",
+               "Long-Term Buffer", "Picture Long-Term Reference", "LTR Validation",
+               "Level IDC", "SEI Message", "QP Range", "Priority ID", "Start or Stop Layer/View",
+               "Error Resiliency"
        };
        static const char * const stdnames[] = {
                "None", "NTSC - 525/60", "PAL - 625/50", "SECAM - 625/50",
@@ -2114,7 +1554,7 @@ static void dump_videocontrol_interface(libusb_device_handle *dev, const unsigne
                        printf("      Warning: Descriptor too short\n");
                freq = buf[7] | (buf[8] << 8) | (buf[9] << 16) | (buf[10] << 24);
                printf("        bcdUVC              %2x.%02x\n"
-                      "        wTotalLength        %5u\n"
+                      "        wTotalLength       0x%04x\n"
                       "        dwClockFrequency    %5u.%06uMHz\n"
                       "        bInCollection       %5u\n",
                       buf[4], buf[3], buf[5] | (buf[6] << 8), freq / 1000000,
@@ -2150,9 +1590,16 @@ static void dump_videocontrol_interface(libusb_device_handle *dev, const unsigne
                        for (i = 0; i < 3 && i < buf[14]; i++)
                                ctrls = (ctrls << 8) | buf[8+n-i-1];
                        printf("        bmControls           0x%08x\n", ctrls);
-                       for (i = 0; i < 19; i++)
-                               if ((ctrls >> i) & 1)
-                                       printf("          %s\n", camctrlnames[i]);
+                       if (protocol == USB_VIDEO_PROTOCOL_15) {
+                               for (i = 0; i < 22; i++)
+                                       if ((ctrls >> i) & 1)
+                                               printf("          %s\n", camctrlnames[i]);
+                       }
+                       else {
+                               for (i = 0; i < 19; i++)
+                                       if ((ctrls >> i) & 1)
+                                               printf("          %s\n", camctrlnames[i]);
+                       }
                }
                dump_junk(buf, "        ", 8+n);
                break;
@@ -2161,7 +1608,7 @@ static void dump_videocontrol_interface(libusb_device_handle *dev, const unsigne
                printf("(OUTPUT_TERMINAL)\n");
                term = get_dev_string(dev, buf[8]);
                termt = buf[4] | (buf[5] << 8);
-               get_audioterminal_string(termts, sizeof(termts), termt);
+               get_videoterminal_string(termts, sizeof(termts), termt);
                if (buf[0] < 9)
                        printf("      Warning: Descriptor too short\n");
                printf("        bTerminalID         %5u\n"
@@ -2205,12 +1652,19 @@ static void dump_videocontrol_interface(libusb_device_handle *dev, const unsigne
                for (i = 0; i < 3 && i < n; i++)
                        ctrls = (ctrls << 8) | buf[8+n-i-1];
                printf("        bmControls     0x%08x\n", ctrls);
-               for (i = 0; i < 18; i++)
-                       if ((ctrls >> i) & 1)
-                               printf("          %s\n", ctrlnames[i]);
+               if (protocol == USB_VIDEO_PROTOCOL_15) {
+                       for (i = 0; i < 19; i++)
+                               if ((ctrls >> i) & 1)
+                                       printf("          %s\n", ctrlnames[i]);
+               }
+               else {
+                       for (i = 0; i < 18; i++)
+                               if ((ctrls >> i) & 1)
+                                       printf("          %s\n", ctrlnames[i]);
+               }
                stds = buf[9+n];
                printf("        iProcessing         %5u %s\n"
-                      "        bmVideoStandards     0x%2x\n", buf[8+n], term, stds);
+                      "        bmVideoStandards     0x%02x\n", buf[8+n], term, stds);
                for (i = 0; i < 6; i++)
                        if ((stds >> i) & 1)
                                printf("          %s\n", stdnames[i]);
@@ -2225,8 +1679,8 @@ static void dump_videocontrol_interface(libusb_device_handle *dev, const unsigne
                        printf("      Warning: Descriptor too short\n");
                printf("        bUnitID             %5u\n"
                       "        guidExtensionCode         %s\n"
-                      "        bNumControl         %5u\n"
-                      "        bNrPins             %5u\n",
+                      "        bNumControls        %5u\n"
+                      "        bNrInPins           %5u\n",
                       buf[3], get_guid(&buf[4]), buf[20], buf[21]);
                for (i = 0; i < p; i++)
                        printf("        baSourceID(%2u)      %5u\n", i, buf[22+i]);
@@ -2238,6 +1692,31 @@ static void dump_videocontrol_interface(libusb_device_handle *dev, const unsigne
                dump_junk(buf, "        ", 24+p+n);
                break;
 
+       case 0x07: /* ENCODING UNIT */
+               printf("(ENCODING UNIT)\n");
+               term = get_dev_string(dev, buf[5]);
+               if (buf[0] < 13)
+                       printf("      Warning: Descriptor too short\n");
+               printf("        bUnitID             %5u\n"
+                      "        bSourceID           %5u\n"
+                      "        iEncoding           %5u %s\n"
+                      "        bControlSize        %5u\n",
+                      buf[3], buf[4], buf[5], term, buf[6]);
+               ctrls = 0;
+               for (i = 0; i < 3; i++)
+                       ctrls = (ctrls << 8) | buf[9-i];
+               printf("        bmControls              0x%08x\n", ctrls);
+               for (i = 0; i < 20;  i++)
+                       if ((ctrls >> i) & 1)
+                               printf("          %s\n", enctrlnames[i]);
+               for (i = 0; i< 3; i++)
+                       ctrls = (ctrls << 8) | buf[12-i];
+               printf("        bmControlsRuntime       0x%08x\n", ctrls);
+               for (i = 0; i < 20; i++)
+                       if ((ctrls >> i) & 1)
+                               printf("          %s\n", enctrlnames[i]);
+               break;
+
        default:
                printf("(unknown)\n"
                       "        Invalid desc subtype:");
@@ -2276,7 +1755,7 @@ static void dump_videostreaming_interface(const unsigned char *buf)
                if (buf[0] < 13+p*n)
                        printf("      Warning: Descriptor too short\n");
                printf("        bNumFormats                     %5u\n"
-                      "        wTotalLength                    %5u\n"
+                      "        wTotalLength                   0x%04x\n"
                       "        bEndPointAddress                %5u\n"
                       "        bmInfo                          %5u\n"
                       "        bTerminalLink                   %5u\n"
@@ -2289,7 +1768,7 @@ static void dump_videostreaming_interface(const unsigned char *buf)
                for (i = 0; i < p; i++)
                        printf(
                        "        bmaControls(%2u)                 %5u\n",
-                               i, buf[13+p*n]);
+                               i, buf[13+i*n]);
                dump_junk(buf, "        ", 13+p*n);
                break;
 
@@ -2300,7 +1779,7 @@ static void dump_videostreaming_interface(const unsigned char *buf)
                if (buf[0] < 9+p*n)
                        printf("      Warning: Descriptor too short\n");
                printf("        bNumFormats                 %5u\n"
-                      "        wTotalLength                %5u\n"
+                      "        wTotalLength               0x%04x\n"
                       "        bEndpointAddress            %5u\n"
                       "        bTerminalLink               %5u\n"
                       "        bControlSize                %5u\n",
@@ -2308,7 +1787,7 @@ static void dump_videostreaming_interface(const unsigned char *buf)
                for (i = 0; i < p; i++)
                        printf(
                        "        bmaControls(%2u)             %5u\n",
-                               i, buf[9+p*n]);
+                               i, buf[9+i*n]);
                dump_junk(buf, "        ", 9+p*n);
                break;
 
@@ -2326,7 +1805,7 @@ static void dump_videostreaming_interface(const unsigned char *buf)
                               "        wHeight(%2u)                     %5u\n",
                               i, buf[5+4*i] | (buf[6+4*i] << 8),
                               i, buf[7+4*i] | (buf[8+4*i] << 8));
-               printf("        bNumCompressionPatterns           %3u\n", n);
+               printf("        bNumCompressionPatterns           %3u\n", m);
                for (i = 0; i < m; i++)
                        printf("        bCompression(%2u)                %5u\n",
                               i, buf[6+4*n+i]);
@@ -2376,9 +1855,9 @@ static void dump_videostreaming_interface(const unsigned char *buf)
                        printf("Random pattern of fields 1 and 2\n");
                        break;
                }
-               printf("          bCopyProtect                  %5u\n", buf[26]);
+               printf("        bCopyProtect                    %5u\n", buf[26]);
                if (buf[2] == 0x10)
-                       printf("          bVariableSize                 %5u\n", buf[27]);
+                       printf("        bVariableSize                 %5u\n", buf[27]);
                dump_junk(buf, "        ", len);
                break;
 
@@ -2480,7 +1959,7 @@ static void dump_videostreaming_interface(const unsigned char *buf)
                        printf("Random pattern of fields 1 and 2\n");
                        break;
                }
-               printf("          bCopyProtect                  %5u\n", buf[10]);
+               printf("        bCopyProtect                    %5u\n", buf[10]);
                dump_junk(buf, "        ", 11);
                break;
 
@@ -2513,6 +1992,18 @@ static void dump_videostreaming_interface(const unsigned char *buf)
                dump_junk(buf, "        ", 6);
                break;
 
+       case 0x12: /* FORMAT_STREAM_BASED */
+               printf("(FORMAT_STREAM_BASED)\n");
+               if (buf[0] != 24)
+                       printf("      Warning: Incorrect descriptor length\n");
+
+               printf("        bFormatIndex                    %5u\n"
+                      "        guidFormat                            %s\n"
+                      "        dwPacketLength                %7u\n",
+                      buf[3], get_guid(&buf[4]), buf[20]);
+               dump_junk(buf, "        ", 24);
+               break;
+
        default:
                printf("        Invalid desc subtype:");
                dump_bytes(buf+3, buf[0]-3);
@@ -2710,16 +2201,16 @@ static void dump_ccid_device(const unsigned char *buf)
        if ((us & 0x0020))
                fputs("          Auto baud rate change\n", stdout);
        if ((us & 0x0040))
-               fputs("          Auto parameter negotation made by CCID\n", stdout);
+               fputs("          Auto parameter negotiation made by CCID\n", stdout);
        else if ((us & 0x0080))
                fputs("          Auto PPS made by CCID\n", stdout);
        else if ((us & (0x0040 | 0x0080)))
-               fputs("        WARNING: conflicting negotation features\n", stdout);
+               fputs("        WARNING: conflicting negotiation features\n", stdout);
 
        if ((us & 0x0100))
                fputs("          CCID can set ICC in clock stop mode\n", stdout);
        if ((us & 0x0200))
-               fputs("          NAD value other than 0x00 accpeted\n", stdout);
+               fputs("          NAD value other than 0x00 accepted\n", stdout);
        if ((us & 0x0400))
                fputs("          Auto IFSD exchange\n", stdout);
 
@@ -2883,6 +2374,63 @@ static void dump_report_desc(unsigned char *b, int l)
        }
 }
 
+static void dump_printer_device(libusb_device_handle *dev,
+                               const struct libusb_interface_descriptor *interface,
+                               const unsigned char *buf)
+{
+       unsigned int i;
+       unsigned int n;
+
+       if (interface->bInterfaceProtocol != 0x04)  /* IPP-over-USB */
+               return;
+
+       printf("        IPP Printer Descriptor:\n"
+              "          bLength             %5u\n"
+              "          bDescriptorType     %5u\n"
+              "          bcdReleaseNumber    %5u\n"
+              "          bcdNumDescriptors   %5u\n",
+              buf[0], buf[1], buf[2], buf[3]);
+
+       n = 4;
+       for (i = 0 ; i < buf[3] ; i++) {
+               switch (buf[n]) {
+               case 0x00: {  /* Basic capabilities */
+                       uint16_t caps = le16_to_cpu(*((uint16_t*)&buf[n+2]));
+                       char *uuid = get_dev_string(dev, buf[n+5]);
+
+                       printf("            iIPPVersionsSupported %5u\n", buf[n+4]);
+                       printf("            iIPPPrinterUUID       %5u %s\n", buf[n+5], uuid);
+                       printf("            wBasicCapabilities   0x%04x ", caps);
+                       if (caps & 0x01)
+                               printf(" Print");
+                       if (caps & 0x02)
+                               printf(" Scan");
+                       if (caps & 0x04)
+                               printf(" Fax");
+                       if (caps & 0x08)
+                               printf(" Other");
+                       if (caps & 0x10)
+                               printf(" HTTP-over-USB");
+                       if ((caps & 0x60) == 0x00)
+                               printf(" No-Auth");
+                       else if ((caps & 0x60) == 0x20)
+                               printf(" Username-Auth");
+                       else if ((caps & 0x60) == 0x40)
+                               printf(" Reserved-Auth");
+                       else if ((caps & 0x60) == 0x60)
+                               printf(" Negotiable-Auth");
+                       printf("\n");
+                       break;
+               }
+               default:
+                       /* Vendor Specific, Ignore for now. */
+                       printf("            UnknownCapabilities   %5u %5u\n", buf[n], buf[n+1]);
+                       break;
+               }
+               n += 2 + buf[n+1];
+       }
+}
+
 static void dump_hid_device(libusb_device_handle *dev,
                            const struct libusb_interface_descriptor *interface,
                            const unsigned char *buf)
@@ -3219,6 +2767,19 @@ dump_comm_descriptor(libusb_device_handle *dev, const unsigned char *buf, char *
                if (buf[11] & 0x08)
                        printf("%s    max datagram size\n", indent);
                break;
+       case 0x1c:              /* MBIM extended functional desc */
+               type = "MBIM Extended";
+               if (buf[0] != 8)
+                       goto bad;
+               printf("%sCDC MBIM Extended:\n"
+                      "%s  bcdMBIMExtendedVersion          %2x.%02x\n"
+                      "%s  bMaxOutstandingCommandMessages    %3d\n"
+                      "%s  wMTU                            %5d\n",
+                      indent,
+                      indent, buf[4], buf[3],
+                      indent, buf[5],
+                      indent, buf[6] | (buf[7] << 8));
+               break;
        default:
                /* FIXME there are about a dozen more descriptor types */
                printf("%sUNRECOGNIZED CDC: ", indent);
@@ -3244,23 +2805,23 @@ static void do_hub(libusb_device_handle *fd, unsigned tt_type, unsigned speed)
                        + 2 /* bitmasks */ * HUB_STATUS_BYTELEN];
        int i, ret, value;
        unsigned int link_state;
-       char *link_state_descriptions[] = {
-               " U0",
-               " U1",
-               " U2",
-               " suspend",
-               " SS.disabled",
-               " Rx.Detect",
-               " SS.Inactive",
-               " Polling",
-               " Recovery",
-               " Hot Reset",
-               " Compliance",
-               " Loopback",
+       const char * const link_state_descriptions[] = {
+               "U0",
+               "U1",
+               "U2",
+               "suspend",
+               "SS.disabled",
+               "Rx.Detect",
+               "SS.Inactive",
+               "Polling",
+               "Recovery",
+               "Hot Reset",
+               "Compliance",
+               "Loopback",
        };
 
-       /* USB 3.0 hubs have a slightly different descriptor */
-       if (speed == 0x0300)
+       /* USB 3.x hubs have a slightly different descriptor */
+       if (speed >= 0x0300)
                value = 0x2A;
        else
                value = 0x29;
@@ -3270,14 +2831,17 @@ static void do_hub(libusb_device_handle *fd, unsigned tt_type, unsigned speed)
                        LIBUSB_REQUEST_GET_DESCRIPTOR,
                        value << 8, 0,
                        buf, sizeof buf, CTRL_TIMEOUT);
-       if (ret < 9 /* at least one port's bitmasks */) {
-               if (ret >= 0)
-                       fprintf(stderr,
-                               "incomplete hub descriptor, %d bytes\n",
-                               ret);
+       if (ret < 0) {
                /* Linux returns EHOSTUNREACH for suspended devices */
-               else if (errno != EHOSTUNREACH)
-                       perror("can't get hub descriptor");
+               if (errno != EHOSTUNREACH)
+                       fprintf(stderr, "can't get hub descriptor, %s (%s)\n",
+                               libusb_error_name(ret), strerror(errno));
+               return;
+       }
+       if (ret < 9 /* at least one port's bitmasks */) {
+               fprintf(stderr,
+                       "incomplete hub descriptor, %d bytes\n",
+                       ret);
                return;
        }
        dump_hub("", buf, tt_type);
@@ -3311,12 +2875,13 @@ static void do_hub(libusb_device_handle *fd, unsigned tt_type, unsigned speed)
                                        (status[2] & 0x04) ? " C_SUSPEND" : "",
                                        (status[2] & 0x02) ? " C_ENABLE" : "",
                                        (status[2] & 0x01) ? " C_CONNECT" : "");
-                       printf("%s%s%s%s%s%s%s%s%s%s\n",
+                       printf("%s%s%s%s%s%s%s%s%s%s%s\n",
                                        (status[1] & 0x10) ? " indicator" : "",
                                        (status[1] & 0x08) ? " test" : "",
                                        (status[1] & 0x04) ? " highspeed" : "",
                                        (status[1] & 0x02) ? " lowspeed" : "",
                                        (status[1] & 0x01) ? " power" : "",
+                                       (status[0] & 0x20) ? " L1" : "",
                                        (status[0] & 0x10) ? " RESET" : "",
                                        (status[0] & 0x08) ? " oc" : "",
                                        (status[0] & 0x04) ? " suspend" : "",
@@ -3338,7 +2903,7 @@ static void do_hub(libusb_device_handle *fd, unsigned tt_type, unsigned speed)
                        /* Link state is bits 8:5 */
                        if (link_state < (sizeof(link_state_descriptions) /
                                                sizeof(*link_state_descriptions)))
-                               printf("%s", link_state_descriptions[link_state]);
+                               printf(" %s", link_state_descriptions[link_state]);
                        printf("%s%s%s%s\n",
                                        (status[0] & 0x10) ? " RESET" : "",
                                        (status[0] & 0x08) ? " oc" : "",
@@ -3638,15 +3203,24 @@ static void dump_usb2_device_capability_desc(unsigned char *buf)
                        buf[0], buf[1], buf[2], wide);
        if (!(wide & 0x02))
                printf("      (Missing must-be-set LPM bit!)\n");
-       else
-               printf("      Link Power Management (LPM)"
+       else if (!(wide & 0x04))
+               printf("      HIRD Link Power Management (LPM)"
+                               " Supported\n");
+       else {
+               printf("      BESL Link Power Management (LPM)"
                                " Supported\n");
+               if (wide & 0x08)
+                       printf("    BESL value    %5u us \n", wide & 0xf00);
+               if (wide & 0x10)
+                       printf("    Deep BESL value    %5u us \n",
+                                       wide & 0xf000);
+       }
 }
 
 static void dump_ss_device_capability_desc(unsigned char *buf)
 {
        if (buf[0] < 10) {
-               printf("  Bad SuperSpeed USB Device Capability descriptor.\n");
+               fprintf(stderr, "  Bad SuperSpeed USB Device Capability descriptor.\n");
                return;
        }
        printf("  SuperSpeed USB Device Capability:\n"
@@ -3695,10 +3269,46 @@ static void dump_ss_device_capability_desc(unsigned char *buf)
        printf("    bU2DevExitLat    %8u micro seconds\n", buf[8] + (buf[9] << 8));
 }
 
+static void dump_ssp_device_capability_desc(unsigned char *buf)
+{
+       int i;
+       unsigned int bm_attr, ss_attr;
+       char bitrate_prefix[] = " KMG";
+
+       if (buf[0] < 12) {
+               fprintf(stderr, "  Bad SuperSpeedPlus USB Device Capability descriptor.\n");
+               return;
+       }
+
+       bm_attr = convert_le_u32(buf + 4);
+       printf("  SuperSpeedPlus USB Device Capability:\n"
+                       "    bLength             %5u\n"
+                       "    bDescriptorType     %5u\n"
+                       "    bDevCapabilityType  %5u\n"
+                       "    bmAttributes         0x%08x\n",
+                       buf[0], buf[1], buf[2], bm_attr);
+
+       printf("      Sublink Speed Attribute count %u\n", buf[4] & 0x1f);
+       printf("      Sublink Speed ID count %u\n", (bm_attr >> 5) & 0xf);
+       printf("    wFunctionalitySupport   0x%02x%02x\n", buf[9], buf[8]);
+
+       for (i = 0; i <= (buf[4] & 0x1f); i++) {
+               ss_attr = convert_le_u32(buf + 12 + (i * 4));
+               printf("    bmSublinkSpeedAttr[%u]   0x%08x\n", i, ss_attr);
+               printf("      Speed Attribute ID: %u %u%cb/s %s %s SuperSpeed%s\n",
+                      ss_attr & 0x0f,
+                      ss_attr >> 16,
+                      (bitrate_prefix[((ss_attr >> 4) & 0x3)]),
+                      (ss_attr & 0x40)? "Asymmetric" : "Symmetric",
+                      (ss_attr & 0x80)? "TX" : "RX",
+                      (ss_attr & 0x4000)? "Plus": "" );
+       }
+}
+
 static void dump_container_id_device_capability_desc(unsigned char *buf)
 {
        if (buf[0] < 20) {
-               printf("  Bad Container ID Device Capability descriptor.\n");
+               fprintf(stderr, "  Bad Container ID Device Capability descriptor.\n");
                return;
        }
        printf("  Container ID Device Capability:\n"
@@ -3711,13 +3321,191 @@ static void dump_container_id_device_capability_desc(unsigned char *buf)
                        get_guid(&buf[4]));
 }
 
+static char *get_webusb_url(libusb_device_handle *fd, uint8_t vendor_req, uint8_t id)
+{
+       unsigned char url_buf[255];
+       char *scheme;
+       char *url, *chr;
+       unsigned char i;
+       int ret;
+
+       ret = usb_control_msg(fd,
+                       LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_DEVICE | LIBUSB_REQUEST_TYPE_VENDOR,
+                       vendor_req, id, WEBUSB_GET_URL,
+                       url_buf, sizeof(url_buf), CTRL_TIMEOUT);
+       if (ret <= 0)
+               return strdup("");
+       else if (url_buf[0] <= 3 || url_buf[1] != USB_DT_WEBUSB_URL || ret != url_buf[0])
+               return strdup("");
+
+       switch (url_buf[2]) {
+       case 0:
+               scheme = "http://";
+               break;
+       case 1:
+               scheme = "https://";
+               break;
+       case 255:
+               scheme = "";
+               break;
+       default:
+               fprintf(stderr, "Bad URL scheme.\n");
+               return strdup("");
+       }
+       url = malloc(strlen(scheme) + (url_buf[0] - 3)  + 1);
+       if (!url)
+               return strdup("");
+       strcpy(url, scheme);
+       chr = url + strlen(scheme);
+       for (i = 3; i < url_buf[0]; i++)
+               /* crude UTF-8 to ASCII conversion */
+               if (url_buf[i] < 0x80)
+                       *chr++ = url_buf[i];
+       *chr = '\0';
+
+       return url;
+}
+
+static void dump_platform_device_capability_desc(libusb_device_handle *fd, unsigned char *buf)
+{
+       unsigned char desc_len = buf[0];
+       unsigned char cap_data_len = desc_len - 20;
+       unsigned char i;
+       const char *guid;
+       if (desc_len < 20) {
+               fprintf(stderr, "  Bad Platform Device Capability descriptor.\n");
+               return;
+       }
+       printf("  Platform Device Capability:\n"
+                       "    bLength             %5u\n"
+                       "    bDescriptorType     %5u\n"
+                       "    bDevCapabilityType  %5u\n"
+                       "    bReserved           %5u\n",
+                       buf[0], buf[1], buf[2], buf[3]);
+       guid = get_guid(&buf[4]);
+       printf("    PlatformCapabilityUUID    %s\n", guid);
+
+       if (!strcmp(WEBUSB_GUID , guid) && desc_len == 24) {
+               /* WebUSB platform descriptor */
+               char *url = get_webusb_url(fd, buf[22], buf[23]);
+               printf("      WebUSB:\n"
+                               "        bcdVersion   %2x.%02x\n"
+                               "        bVendorCode  %5u\n"
+                               "        iLandingPage %5u %s\n",
+                               buf[21], buf[20], buf[22], buf[23], url);
+               free(url);
+               return;
+       }
+
+       for (i = 0; i < cap_data_len; i++) {
+               printf("    CapabilityData[%u]    0x%02x\n", i, buf[20 + i]);
+       }
+}
+
+static void dump_billboard_device_capability_desc(libusb_device_handle *dev, unsigned char *buf)
+{
+       char *url, *alt_mode_str;
+       int w_vconn_power, alt_mode, i, svid, state;
+       const char *vconn;
+       unsigned char *bmConfigured;
+
+       if (buf[0] < 48) {
+               fprintf(stderr, "  Bad Billboard Capability descriptor.\n");
+               return;
+       }
+
+       if (buf[4] > BILLBOARD_MAX_NUM_ALT_MODE) {
+               fprintf(stderr, "  Invalid value for bNumberOfAlternateModes.\n");
+               return;
+       }
+
+       if (buf[0] < (44 + buf[4] * 4)) {
+               fprintf(stderr, "  bLength does not match with bNumberOfAlternateModes.\n");
+               return;
+       }
+
+       url = get_dev_string(dev, buf[3]);
+       w_vconn_power = convert_le_u16(buf+6);
+       if (w_vconn_power & (1 << 15)) {
+               vconn = "VCONN power not required";
+       } else if (w_vconn_power < 7) {
+               vconn = vconn_power[w_vconn_power & 0x7];
+       } else {
+               vconn = "reserved";
+       }
+       printf("  Billboard Capability:\n"
+                       "    bLength                 %5u\n"
+                       "    bDescriptorType         %5u\n"
+                       "    bDevCapabilityType      %5u\n"
+                       "    iAddtionalInfoURL       %5u %s\n"
+                       "    bNumberOfAlternateModes %5u\n"
+                       "    bPreferredAlternateMode %5u\n"
+                       "    VCONN Power             %5u %s\n",
+                       buf[0], buf[1], buf[2],
+                       buf[3], url,
+                       buf[4], buf[5],
+                       w_vconn_power, vconn);
+
+       bmConfigured = &buf[8];
+
+       printf("    bmConfigured               ");
+       dump_bytes(bmConfigured, 32);
+
+       printf(
+                       "    bcdVersion              %2x.%02x\n"
+                       "    bAdditionalFailureInfo  %5u\n"
+                       "    bReserved               %5u\n",
+                       (buf[41] == 0) ? 1 : buf[41], buf[40],
+                       buf[42], buf[43]);
+
+       printf("    Alternate Modes supported by Device Container:\n");
+       i = 44; /* Alternate mode 0 starts at index 44 */
+       for (alt_mode = 0; alt_mode < buf[4]; alt_mode++) {
+               svid = convert_le_u16(buf+i);
+               alt_mode_str = get_dev_string(dev, buf[i+3]);
+               state = ((bmConfigured[alt_mode >> 2]) >> ((alt_mode & 0x3) << 1)) & 0x3;
+               printf(
+                       "    Alternate Mode %d : %s\n"
+                       "      wSVID[%d]                    0x%04X\n"
+                       "      bAlternateMode[%d]       %5u\n"
+                       "      iAlternateModeString[%d] %5u %s\n",
+                       alt_mode, alt_mode_state[state],
+                       alt_mode, svid,
+                       alt_mode, buf[i+2],
+                       alt_mode, buf[i+3], alt_mode_str);
+               free(alt_mode_str);
+               i += 4;
+       }
+
+       free (url);
+}
+
+static void dump_billboard_alt_mode_capability_desc(libusb_device_handle *dev, unsigned char *buf)
+{
+       if (buf[0] != 8) {
+               fprintf(stderr, "  Bad Billboard Alternate Mode Capability descriptor.\n");
+               return;
+       }
+
+       printf("  Billboard Alternate Mode Capability:\n"
+                       "    bLength                 %5u\n"
+                       "    bDescriptorType         %5u\n"
+                       "    bDevCapabilityType      %5u\n"
+                       "    bIndex                  %5u\n"
+                       "    dwAlternateModeVdo          0x%02X%02X%02X%02X\n",
+                       buf[0], buf[1], buf[2], buf[3],
+                       buf[4], buf[5], buf[6], buf[7]);
+}
+
 static void dump_bos_descriptor(libusb_device_handle *fd)
 {
-       /* Total for all known BOS descriptors is 43 bytes:
-        * 6 bytes for Wireless USB, 7 bytes for USB 2.0 extension,
-        * 10 bytes for SuperSpeed, 20 bytes for Container ID.
+       /* Total length of BOS descriptors varies.
+        * Read first static 5 bytes which include the total length before
+        * allocating and reading the full BOS
         */
-       unsigned char bos_desc[43];
+
+       unsigned char bos_desc_static[5];
+       unsigned char *bos_desc;
        unsigned int bos_desc_size;
        int size, ret;
        unsigned char *buf;
@@ -3727,32 +3515,31 @@ static void dump_bos_descriptor(libusb_device_handle *fd)
                        LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_DEVICE,
                        LIBUSB_REQUEST_GET_DESCRIPTOR,
                        USB_DT_BOS << 8, 0,
-                       bos_desc, 5, CTRL_TIMEOUT);
+                       bos_desc_static, 5, CTRL_TIMEOUT);
        if (ret <= 0)
                return;
-       else if (bos_desc[0] != 5 || bos_desc[1] != USB_DT_BOS)
+       else if (bos_desc_static[0] != 5 || bos_desc_static[1] != USB_DT_BOS)
                return;
 
-       bos_desc_size = bos_desc[2] + (bos_desc[3] << 8);
+       bos_desc_size = bos_desc_static[2] + (bos_desc_static[3] << 8);
        printf("Binary Object Store Descriptor:\n"
               "  bLength             %5u\n"
               "  bDescriptorType     %5u\n"
-              "  wTotalLength        %5u\n"
+              "  wTotalLength       0x%04x\n"
               "  bNumDeviceCaps      %5u\n",
-              bos_desc[0], bos_desc[1],
-              bos_desc_size, bos_desc[4]);
+              bos_desc_static[0], bos_desc_static[1],
+              bos_desc_size, bos_desc_static[4]);
 
        if (bos_desc_size <= 5) {
-               if (bos_desc[4] > 0)
+               if (bos_desc_static[4] > 0)
                        fprintf(stderr, "Couldn't get "
                                        "device capability descriptors\n");
                return;
        }
-       if (bos_desc_size > sizeof bos_desc) {
-               fprintf(stderr, "FIXME: alloc bigger buffer for "
-                               "device capability descriptors\n");
+       bos_desc = malloc(bos_desc_size);
+       if (!bos_desc)
                return;
-       }
+       memset(bos_desc, 0, bos_desc_size);
 
        ret = usb_control_msg(fd,
                        LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_DEVICE,
@@ -3761,7 +3548,7 @@ static void dump_bos_descriptor(libusb_device_handle *fd)
                        bos_desc, bos_desc_size, CTRL_TIMEOUT);
        if (ret < 0) {
                fprintf(stderr, "Couldn't get device capability descriptors\n");
-               return;
+               goto out;
        }
 
        size = bos_desc_size - 5;
@@ -3770,7 +3557,7 @@ static void dump_bos_descriptor(libusb_device_handle *fd)
        while (size >= 3) {
                if (buf[0] < 3) {
                        printf("buf[0] = %u\n", buf[0]);
-                       return;
+                       goto out;
                }
                switch (buf[2]) {
                case USB_DC_WIRELESS_USB:
@@ -3782,9 +3569,26 @@ static void dump_bos_descriptor(libusb_device_handle *fd)
                case USB_DC_SUPERSPEED:
                        dump_ss_device_capability_desc(buf);
                        break;
+               case USB_DC_SUPERSPEEDPLUS:
+                       dump_ssp_device_capability_desc(buf);
+                       break;
                case USB_DC_CONTAINER_ID:
                        dump_container_id_device_capability_desc(buf);
                        break;
+               case USB_DC_PLATFORM:
+                       dump_platform_device_capability_desc(fd, buf);
+                       break;
+               case USB_DC_BILLBOARD:
+                       dump_billboard_device_capability_desc(fd, buf);
+                       break;
+               case USB_DC_BILLBOARD_ALT_MODE:
+                       dump_billboard_alt_mode_capability_desc(fd, buf);
+                       break;
+               case USB_DC_CONFIGURATION_SUMMARY:
+                       printf("  Configuration Summary Device Capability:\n");
+                       desc_dump(fd, desc_usb3_dc_configuration_summary,
+                                       buf, DESC_BUF_LEN_FROM_BUF, 2);
+                       break;
                default:
                        printf("  ** UNRECOGNIZED: ");
                        dump_bytes(buf, buf[0]);
@@ -3793,6 +3597,8 @@ static void dump_bos_descriptor(libusb_device_handle *fd)
                size -= buf[0];
                buf += buf[0];
        }
+out:
+       free(bos_desc);
 }
 
 static void dumpdev(libusb_device *dev)
@@ -3811,7 +3617,7 @@ static void dumpdev(libusb_device *dev)
        }
 
        libusb_get_device_descriptor(dev, &desc);
-       dump_device(udev, &desc);
+       dump_device(dev, &desc);
        if (desc.bcdUSB == 0x0250)
                wireless = do_wireless(udev);
        if (desc.bNumConfigurations) {
@@ -3833,7 +3639,7 @@ static void dumpdev(libusb_device *dev)
                                                "descriptor %d, some information will "
                                                "be missing\n", i);
                        } else {
-                               dump_config(udev, config);
+                               dump_config(udev, config, desc.bcdUSB);
                                libusb_free_config_descriptor(config);
                        }
                }
@@ -3856,6 +3662,38 @@ static void dumpdev(libusb_device *dev)
 
 /* ---------------------------------------------------------------------- */
 
+/*
+ * Attempt to get friendly vendor and product names from the udev hwdb. If
+ * either or both are not present, instead populate those from the device's
+ * own string descriptors.
+ */
+static void get_vendor_product_with_fallback(char *vendor, int vendor_len,
+                                            char *product, int product_len,
+                                            libusb_device *dev)
+{
+       struct libusb_device_descriptor desc;
+       char sysfs_name[PATH_MAX];
+       bool have_vendor, have_product;
+
+       libusb_get_device_descriptor(dev, &desc);
+
+       have_vendor = !!get_vendor_string(vendor, vendor_len, desc.idVendor);
+       have_product = !!get_product_string(product, product_len,
+                       desc.idVendor, desc.idProduct);
+
+       if (have_vendor && have_product)
+               return;
+
+       if (get_sysfs_name(sysfs_name, sizeof(sysfs_name), dev) >= 0) {
+               if (!have_vendor)
+                       read_sysfs_prop(vendor, vendor_len, sysfs_name,
+                                       "manufacturer");
+               if (!have_product)
+                       read_sysfs_prop(product, product_len, sysfs_name,
+                                       "product");
+       }
+}
+
 static int dump_one_device(libusb_context *ctx, const char *path)
 {
        libusb_device *dev;
@@ -3868,8 +3706,8 @@ static int dump_one_device(libusb_context *ctx, const char *path)
                return 1;
        }
        libusb_get_device_descriptor(dev, &desc);
-       get_vendor_string(vendor, sizeof(vendor), desc.idVendor);
-       get_product_string(product, sizeof(product), desc.idVendor, desc.idProduct);
+       get_vendor_product_with_fallback(vendor, sizeof(vendor),
+                       product, sizeof(product), dev);
        printf("Device: ID %04x:%04x %s %s\n", desc.idVendor,
                                               desc.idProduct,
                                               vendor,
@@ -3905,9 +3743,10 @@ static int list_devices(libusb_context *ctx, int busnum, int devnum, int vendori
                    (productid != -1 && productid != desc.idProduct))
                        continue;
                status = 0;
-               get_vendor_string(vendor, sizeof(vendor), desc.idVendor);
-               get_product_string(product, sizeof(product),
-                               desc.idVendor, desc.idProduct);
+
+               get_vendor_product_with_fallback(vendor, sizeof(vendor),
+                               product, sizeof(product), dev);
+
                if (verblevel > 0)
                        printf("\n");
                printf("Bus %03u Device %03u: ID %04x:%04x %s %s\n",
@@ -4027,21 +3866,12 @@ int main(int argc, char *argv[])
 
 
        /* by default, print names as well as numbers */
-       err = names_init(DATADIR "/usb.ids");
-#ifdef HAVE_LIBZ
-       if (err != 0)
-               err = names_init(DATADIR "/usb.ids.gz");
-#endif
-       if (err != 0)
-               fprintf(stderr, "%s: cannot open \"%s\", %s\n",
-                               argv[0],
-                               DATADIR "/usb.ids",
-                               strerror(err));
+       if (names_init() < 0)
+               fprintf(stderr, "unable to initialize usb spec");
+
        status = 0;
 
        if (treemode) {
-               /* treemode requires at least verblevel 1 */
-               verblevel += 1 - VERBLEVEL_DEFAULT;
                status = lsusb_t();
                names_exit();
                return status;