blob: 37c43f72989d4aa12cd8788d9fcce912986e6fe9 [file] [log] [blame]
/*****************************************************************************/
/*
* 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.
*
*/
/*****************************************************************************/
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#ifdef HAVE_BYTESWAP_H
#include <byteswap.h>
#endif
#include <libusb.h>
#include <unistd.h>
#include "lsusb.h"
#include "names.h"
#include "devtree.h"
#include "usbmisc.h"
#include <getopt.h>
#define le16_to_cpu(x) libusb_cpu_to_le16(libusb_cpu_to_le16(x))
/* from USB 2.0 spec and updates */
#define USB_DT_DEVICE_QUALIFIER 0x06
#define USB_DT_OTHER_SPEED_CONFIG 0x07
#define USB_DT_OTG 0x09
#define USB_DT_DEBUG 0x0a
#define USB_DT_INTERFACE_ASSOCIATION 0x0b
#define USB_DT_SECURITY 0x0c
#define USB_DT_KEY 0x0d
#define USB_DT_ENCRYPTION_TYPE 0x0e
#define USB_DT_BOS 0x0f
#define USB_DT_DEVICE_CAPABILITY 0x10
#define USB_DT_WIRELESS_ENDPOINT_COMP 0x11
#define USB_DT_WIRE_ADAPTER 0x21
#define USB_DT_RPIPE 0x22
#define USB_DT_RC_INTERFACE 0x23
#define USB_DT_SS_ENDPOINT_COMP 0x30
/* Device Capability Type Codes (Wireless USB spec and USB 3.0 bus spec) */
#define USB_DC_WIRELESS_USB 0x01
#define USB_DC_20_EXTENSION 0x02
#define USB_DC_SUPERSPEED 0x03
#define USB_DC_CONTAINER_ID 0x04
/* Conventional codes for class-specific descriptors. The convention is
* defined in the USB "Common Class" Spec (3.11). Individual class specs
* are authoritative for their usage, not the "common class" writeup.
*/
#define USB_DT_CS_DEVICE (LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_DT_DEVICE)
#define USB_DT_CS_CONFIG (LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_DT_CONFIG)
#define USB_DT_CS_STRING (LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_DT_STRING)
#define USB_DT_CS_INTERFACE (LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_DT_INTERFACE)
#define USB_DT_CS_ENDPOINT (LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_DT_ENDPOINT)
#ifndef USB_CLASS_CCID
#define USB_CLASS_CCID 0x0b
#endif
#ifndef USB_CLASS_VIDEO
#define USB_CLASS_VIDEO 0x0e
#endif
#ifndef USB_CLASS_APPLICATION
#define USB_CLASS_APPLICATION 0xfe
#endif
#ifndef USB_AUDIO_CLASS_1
#define USB_AUDIO_CLASS_1 0x00
#endif
#ifndef USB_AUDIO_CLASS_2
#define USB_AUDIO_CLASS_2 0x20
#endif
#define VERBLEVEL_DEFAULT 0 /* 0 gives lspci behaviour; 1, lsusb-0.9 */
#define CTRL_RETRIES 2
#define CTRL_TIMEOUT (5*1000) /* milliseconds */
#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;
static int do_report_desc = 1;
static const char * const encryption_type[] = {
"UNSECURE",
"WIRED",
"CCM_1",
"RSA_1",
"RESERVED"
};
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_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_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 unsigned int convert_le_u32 (const unsigned char *buf)
{
return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
}
/* ---------------------------------------------------------------------- */
/* workaround libusb API goofs: "byte" should never be sign extended;
* using "char" is trouble. Likewise, sizes should never be negative.
*/
static inline int typesafe_control_msg(libusb_device_handle *dev,
unsigned char requesttype, unsigned char request,
int value, int idx,
unsigned char *bytes, unsigned size, int timeout)
{
int ret = libusb_control_transfer(dev, requesttype, request, value,
idx, bytes, size, timeout);
if (ret < 0)
return -ret;
else
return ret;
}
#define usb_control_msg typesafe_control_msg
/* ---------------------------------------------------------------------- */
int lprintf(unsigned int vl, const char *format, ...)
{
va_list ap;
int r;
if (vl > verblevel)
return 0;
va_start(ap, format);
r = vfprintf(stderr, format, ap);
va_end(ap);
if (!vl)
exit(1);
return r;
}
/* ---------------------------------------------------------------------- */
static int get_string(libusb_device_handle *dev, char *buf, size_t size, u_int8_t id)
{
int ret;
if (!dev) {
buf[0] = 0;
return 0;
}
if (id) {
ret = libusb_get_string_descriptor_ascii(dev, id, (void *)buf, size);
if (ret <= 0) {
buf[0] = 0;
return 0;
} else
return ret;
} else {
buf[0] = 0;
return 0;
}
}
static int get_class_string(char *buf, size_t size, u_int8_t cls)
{
const char *cp;
if (size < 1)
return 0;
*buf = 0;
if (!(cp = names_class(cls)))
return 0;
return snprintf(buf, size, "%s", cp);
}
static int get_subclass_string(char *buf, size_t size, u_int8_t cls, u_int8_t subcls)
{
const char *cp;
if (size < 1)
return 0;
*buf = 0;
if (!(cp = names_subclass(cls, subcls)))
return 0;
return snprintf(buf, size, "%s", cp);
}
static int get_protocol_string(char *buf, size_t size, u_int8_t cls, u_int8_t subcls, u_int8_t proto)
{
const char *cp;
if (size < 1)
return 0;
*buf = 0;
if (!(cp = names_protocol(cls, subcls, proto)))
return 0;
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)
{
const char *cp;
if (size < 1)
return 0;
*buf = 0;
if (!(cp = names_videoterminal(termt)))
return 0;
return snprintf(buf, size, "%s", cp);
}
static const char *get_guid(const unsigned char *buf)
{
static char guid[39];
/* NOTE: see RFC 4122 for more information about GUID/UUID
* structure. The first fields fields are historically big
* endian numbers, dating from Apollo mc68000 workstations.
*/
sprintf(guid, "{%02x%02x%02x%02x"
"-%02x%02x"
"-%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[8], buf[9],
buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
return guid;
}
/* ---------------------------------------------------------------------- */
static void dump_bytes(const unsigned char *buf, unsigned int len)
{
unsigned int i;
for (i = 0; i < len; i++)
printf(" %02x", buf[i]);
printf("\n");
}
static void dump_junk(const unsigned char *buf, const char *indent, unsigned int len)
{
unsigned int i;
if (buf[0] <= len)
return;
printf("%sjunk at descriptor end:", indent);
for (i = len; i < buf[0]; i++)
printf(" %02x", buf[i]);
printf("\n");
}
/*
* General config descriptor dump
*/
static void dump_device(
libusb_device_handle *dev,
struct libusb_device_descriptor *descriptor
)
{
char vendor[128], product[128];
char cls[128], subcls[128], proto[128];
char mfg[128], prod[128], serial[128];
get_vendor_string(vendor, sizeof(vendor), descriptor->idVendor);
get_product_string(product, sizeof(product),
descriptor->idVendor, descriptor->idProduct);
get_class_string(cls, sizeof(cls), descriptor->bDeviceClass);
get_subclass_string(subcls, sizeof(subcls),
descriptor->bDeviceClass, descriptor->bDeviceSubClass);
get_protocol_string(proto, sizeof(proto), descriptor->bDeviceClass,
descriptor->bDeviceSubClass, descriptor->bDeviceProtocol);
get_string(dev, mfg, sizeof(mfg), descriptor->iManufacturer);
get_string(dev, prod, sizeof(prod), descriptor->iProduct);
get_string(dev, serial, sizeof(serial), descriptor->iSerialNumber);
printf("Device Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bcdUSB %2x.%02x\n"
" bDeviceClass %5u %s\n"
" bDeviceSubClass %5u %s\n"
" bDeviceProtocol %5u %s\n"
" bMaxPacketSize0 %5u\n"
" idVendor 0x%04x %s\n"
" idProduct 0x%04x %s\n"
" bcdDevice %2x.%02x\n"
" iManufacturer %5u %s\n"
" iProduct %5u %s\n"
" iSerial %5u %s\n"
" bNumConfigurations %5u\n",
descriptor->bLength, descriptor->bDescriptorType,
descriptor->bcdUSB >> 8, descriptor->bcdUSB & 0xff,
descriptor->bDeviceClass, cls,
descriptor->bDeviceSubClass, subcls,
descriptor->bDeviceProtocol, proto,
descriptor->bMaxPacketSize0,
descriptor->idVendor, vendor, descriptor->idProduct, product,
descriptor->bcdDevice >> 8, descriptor->bcdDevice & 0xff,
descriptor->iManufacturer, mfg,
descriptor->iProduct, prod,
descriptor->iSerialNumber, serial,
descriptor->bNumConfigurations);
}
static void dump_wire_adapter(const unsigned char *buf)
{
printf(" Wire Adapter Class Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bcdWAVersion %2x.%02x\n"
" bNumPorts %5u\n"
" bmAttributes %5u\n"
" wNumRPRipes %5u\n"
" wRPipeMaxBlock %5u\n"
" bRPipeBlockSize %5u\n"
" bPwrOn2PwrGood %5u\n"
" bNumMMCIEs %5u\n"
" DeviceRemovable %5u\n",
buf[0], buf[1], buf[3], buf[2], buf[4], buf[5],
(buf[6] | buf[7] << 8),
(buf[8] | buf[9] << 8),
buf[10], buf[11], buf[12], buf[13]);
}
static void dump_rc_interface(const unsigned char *buf)
{
printf(" Radio Control Interface Class Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bcdRCIVersion %2x.%02x\n",
buf[0], buf[1], buf[3], buf[2]);
}
static void dump_security(const unsigned char *buf)
{
printf(" Security Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" wTotalLength %5u\n"
" bNumEncryptionTypes %5u\n",
buf[0], buf[1], (buf[3] << 8 | buf[2]), buf[4]);
}
static void dump_encryption_type(const unsigned char *buf)
{
int b_encryption_type = buf[2] & 0x4;
printf(" Encryption Type Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bEncryptionType %5u %s\n"
" bEncryptionValue %5u\n"
" bAuthKeyIndex %5u\n",
buf[0], buf[1], buf[2],
encryption_type[b_encryption_type], buf[3], buf[4]);
}
static void dump_association(libusb_device_handle *dev, const unsigned char *buf)
{
char cls[128], subcls[128], proto[128];
char func[128];
get_class_string(cls, sizeof(cls), buf[4]);
get_subclass_string(subcls, sizeof(subcls), buf[4], buf[5]);
get_protocol_string(proto, sizeof(proto), buf[4], buf[5], buf[6]);
get_string(dev, func, sizeof(func), buf[7]);
printf(" Interface Association:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bFirstInterface %5u\n"
" bInterfaceCount %5u\n"
" bFunctionClass %5u %s\n"
" bFunctionSubClass %5u %s\n"
" bFunctionProtocol %5u %s\n"
" iFunction %5u %s\n",
buf[0], buf[1],
buf[2], buf[3],
buf[4], cls,
buf[5], subcls,
buf[6], proto,
buf[7], func);
}
static void dump_config(libusb_device_handle *dev, struct libusb_config_descriptor *config)
{
char cfg[128];
int i;
get_string(dev, cfg, sizeof(cfg), config->iConfiguration);
printf(" Configuration Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" wTotalLength %5u\n"
" bNumInterfaces %5u\n"
" bConfigurationValue %5u\n"
" iConfiguration %5u %s\n"
" bmAttributes 0x%02x\n",
config->bLength, config->bDescriptorType,
le16_to_cpu(config->wTotalLength),
config->bNumInterfaces, config->bConfigurationValue,
config->iConfiguration,
cfg, config->bmAttributes);
if (!(config->bmAttributes & 0x80))
printf(" (Missing must-be-set bit!)\n");
if (config->bmAttributes & 0x40)
printf(" Self Powered\n");
else
printf(" (Bus Powered)\n");
if (config->bmAttributes & 0x20)
printf(" Remote Wakeup\n");
if (config->bmAttributes & 0x10)
printf(" Battery Powered\n");
printf(" MaxPower %5umA\n", config->MaxPower * 2);
/* avoid re-ordering or hiding descriptors for display */
if (config->extra_length) {
int size = config->extra_length;
const unsigned char *buf = config->extra;
while (size >= 2) {
if (buf[0] < 2) {
dump_junk(buf, " ", size);
break;
}
switch (buf[1]) {
case USB_DT_OTG:
/* handled separately */
break;
case USB_DT_INTERFACE_ASSOCIATION:
dump_association(dev, buf);
break;
case USB_DT_SECURITY:
dump_security(buf);
break;
case USB_DT_ENCRYPTION_TYPE:
dump_encryption_type(buf);
break;
default:
/* often a misplaced class descriptor */
printf(" ** UNRECOGNIZED: ");
dump_bytes(buf, buf[0]);
break;
}
size -= buf[0];
buf += buf[0];
}
}
for (i = 0 ; i < config->bNumInterfaces ; i++)
dump_interface(dev, &config->interface[i]);
}
static void dump_altsetting(libusb_device_handle *dev, const struct libusb_interface_descriptor *interface)
{
char cls[128], subcls[128], proto[128];
char ifstr[128];
const unsigned char *buf;
unsigned size, i;
get_class_string(cls, sizeof(cls), interface->bInterfaceClass);
get_subclass_string(subcls, sizeof(subcls), interface->bInterfaceClass, interface->bInterfaceSubClass);
get_protocol_string(proto, sizeof(proto), interface->bInterfaceClass, interface->bInterfaceSubClass, interface->bInterfaceProtocol);
get_string(dev, ifstr, sizeof(ifstr), interface->iInterface);
printf(" Interface Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bInterfaceNumber %5u\n"
" bAlternateSetting %5u\n"
" bNumEndpoints %5u\n"
" bInterfaceClass %5u %s\n"
" bInterfaceSubClass %5u %s\n"
" bInterfaceProtocol %5u %s\n"
" iInterface %5u %s\n",
interface->bLength, interface->bDescriptorType, interface->bInterfaceNumber,
interface->bAlternateSetting, interface->bNumEndpoints, interface->bInterfaceClass, cls,
interface->bInterfaceSubClass, subcls, interface->bInterfaceProtocol, proto,
interface->iInterface, ifstr);
/* avoid re-ordering or hiding descriptors for display */
if (interface->extra_length) {
size = interface->extra_length;
buf = interface->extra;
while (size >= 2 * sizeof(u_int8_t)) {
if (buf[0] < 2) {
dump_junk(buf, " ", size);
break;
}
switch (buf[1]) {
/* This is the polite way to provide class specific
* descriptors: explicitly tagged, using common class
* spec conventions.
*/
case USB_DT_CS_DEVICE:
case USB_DT_CS_INTERFACE:
switch (interface->bInterfaceClass) {
case LIBUSB_CLASS_AUDIO:
switch (interface->bInterfaceSubClass) {
case 1:
dump_audiocontrol_interface(dev, buf, interface->bInterfaceProtocol);
break;
case 2:
dump_audiostreaming_interface(dev, buf, interface->bInterfaceProtocol);
break;
case 3:
dump_midistreaming_interface(dev, buf);
break;
default:
goto dump;
}
break;
case LIBUSB_CLASS_COMM:
dump_comm_descriptor(dev, buf,
" ");
break;
case USB_CLASS_VIDEO:
switch (interface->bInterfaceSubClass) {
case 1:
dump_videocontrol_interface(dev, buf);
break;
case 2:
dump_videostreaming_interface(buf);
break;
default:
goto dump;
}
break;
case USB_CLASS_APPLICATION:
switch (interface->bInterfaceSubClass) {
case 1:
dump_dfu_interface(buf);
break;
default:
goto dump;
}
break;
case LIBUSB_CLASS_HID:
dump_hid_device(dev, interface, buf);
break;
case USB_CLASS_CCID:
dump_ccid_device(buf);
break;
default:
goto dump;
}
break;
/* This is the ugly way: implicitly tagged,
* each class could redefine the type IDs.
*/
default:
switch (interface->bInterfaceClass) {
case LIBUSB_CLASS_HID:
dump_hid_device(dev, interface, buf);
break;
case USB_CLASS_CCID:
dump_ccid_device(buf);
break;
case 0xe0: /* wireless */
switch (interface->bInterfaceSubClass) {
case 1:
switch (interface->bInterfaceProtocol) {
case 2:
dump_rc_interface(buf);
break;
default:
goto dump;
}
break;
case 2:
dump_wire_adapter(buf);
break;
default:
goto dump;
}
break;
default:
/* ... not everything is class-specific */
switch (buf[1]) {
case USB_DT_OTG:
/* handled separately */
break;
case USB_DT_INTERFACE_ASSOCIATION:
dump_association(dev, buf);
break;
default:
dump:
/* often a misplaced class descriptor */
printf(" ** UNRECOGNIZED: ");
dump_bytes(buf, buf[0]);
break;
}
}
}
size -= buf[0];
buf += buf[0];
}
}
for (i = 0 ; i < interface->bNumEndpoints ; i++)
dump_endpoint(dev, interface, &interface->endpoint[i]);
}
static void dump_interface(libusb_device_handle *dev, const struct libusb_interface *interface)
{
int i;
for (i = 0; i < interface->num_altsetting; i++)
dump_altsetting(dev, &interface->altsetting[i]);
}
static void dump_pipe_desc(const unsigned char *buf)
{
static const char *pipe_name[] = {
"Reserved",
"Command pipe",
"Status pipe",
"Data-in pipe",
"Data-out pipe",
[5 ... 0xDF] = "Reserved",
[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 {
printf(" INTERFACE CLASS: ");
dump_bytes(buf, buf[0]);
}
}
static void dump_endpoint(libusb_device_handle *dev, const struct libusb_interface_descriptor *interface, const struct libusb_endpoint_descriptor *endpoint)
{
static const char * const typeattr[] = {
"Control",
"Isochronous",
"Bulk",
"Interrupt"
};
static const char * const syncattr[] = {
"None",
"Asynchronous",
"Adaptive",
"Synchronous"
};
static const char * const usage[] = {
"Data",
"Feedback",
"Implicit feedback Data",
"(reserved)"
};
static const char * const hb[] = { "1x", "2x", "3x", "(?\?)" };
const unsigned char *buf;
unsigned size;
unsigned wmax = le16_to_cpu(endpoint->wMaxPacketSize);
printf(" Endpoint Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bEndpointAddress 0x%02x EP %u %s\n"
" bmAttributes %5u\n"
" Transfer Type %s\n"
" Synch Type %s\n"
" Usage Type %s\n"
" wMaxPacketSize 0x%04x %s %d bytes\n"
" bInterval %5u\n",
endpoint->bLength,
endpoint->bDescriptorType,
endpoint->bEndpointAddress,
endpoint->bEndpointAddress & 0x0f,
(endpoint->bEndpointAddress & 0x80) ? "IN" : "OUT",
endpoint->bmAttributes,
typeattr[endpoint->bmAttributes & 3],
syncattr[(endpoint->bmAttributes >> 2) & 3],
usage[(endpoint->bmAttributes >> 4) & 3],
wmax, hb[(wmax >> 11) & 3], wmax & 0x7ff,
endpoint->bInterval);
/* only for audio endpoints */
if (endpoint->bLength == 9)
printf(" bRefresh %5u\n"
" bSynchAddress %5u\n",
endpoint->bRefresh, endpoint->bSynchAddress);
/* avoid re-ordering or hiding descriptors for display */
if (endpoint->extra_length) {
size = endpoint->extra_length;
buf = endpoint->extra;
while (size >= 2 * sizeof(u_int8_t)) {
if (buf[0] < 2) {
dump_junk(buf, " ", size);
break;
}
switch (buf[1]) {
case USB_DT_CS_ENDPOINT:
if (interface->bInterfaceClass == 1 && interface->bInterfaceSubClass == 2)
dump_audiostreaming_endpoint(buf, interface->bInterfaceProtocol);
else if (interface->bInterfaceClass == 1 && interface->bInterfaceSubClass == 3)
dump_midistreaming_endpoint(buf);
break;
case USB_DT_CS_INTERFACE:
/* MISPLACED DESCRIPTOR ... less indent */
switch (interface->bInterfaceClass) {
case LIBUSB_CLASS_COMM:
case LIBUSB_CLASS_DATA: /* comm data */
dump_comm_descriptor(dev, buf,
" ");
break;
case LIBUSB_CLASS_MASS_STORAGE:
dump_pipe_desc(buf);
break;
default:
printf(" INTERFACE CLASS: ");
dump_bytes(buf, buf[0]);
}
break;
case USB_DT_CS_DEVICE:
/* MISPLACED DESCRIPTOR ... less indent */
switch (interface->bInterfaceClass) {
case USB_CLASS_CCID:
dump_ccid_device(buf);
break;
default:
printf(" DEVICE CLASS: ");
dump_bytes(buf, buf[0]);
}
break;
case USB_DT_OTG:
/* handled separately */
break;
case USB_DT_INTERFACE_ASSOCIATION:
dump_association(dev, buf);
break;
case USB_DT_SS_ENDPOINT_COMP:
printf(" bMaxBurst %15u\n", buf[2]);
/* Print bulk streams info or isoc "Mult" */
if ((endpoint->bmAttributes & 3) == 2 &&
(buf[3] & 0x1f))
printf(" MaxStreams %14u\n",
(unsigned) 1 << buf[3]);
if ((endpoint->bmAttributes & 3) == 1 &&
(buf[3] & 0x3))
printf(" Mult %20u\n",
buf[3] & 0x3);
break;
default:
/* often a misplaced class descriptor */
printf(" ** UNRECOGNIZED: ");
dump_bytes(buf, buf[0]);
break;
}
size -= buf[0];
buf += buf[0];
}
}
}
static void dump_unit(unsigned int data, unsigned int len)
{
char *systems[5] = { "None", "SI Linear", "SI Rotation",
"English Linear", "English Rotation" };
char *units[5][8] = {
{ "None", "None", "None", "None", "None",
"None", "None", "None" },
{ "None", "Centimeter", "Gram", "Seconds", "Kelvin",
"Ampere", "Candela", "None" },
{ "None", "Radians", "Gram", "Seconds", "Kelvin",
"Ampere", "Candela", "None" },
{ "None", "Inch", "Slug", "Seconds", "Fahrenheit",
"Ampere", "Candela", "None" },
{ "None", "Degrees", "Slug", "Seconds", "Fahrenheit",
"Ampere", "Candela", "None" },
};
unsigned int i;
unsigned int sys;
int earlier_unit = 0;
/* First nibble tells us which system we're in. */
sys = data & 0xf;
data >>= 4;
if (sys > 4) {
if (sys == 0xf)
printf("System: Vendor defined, Unit: (unknown)\n");
else
printf("System: Reserved, Unit: (unknown)\n");
return;
} else {
printf("System: %s, Unit: ", systems[sys]);
}
for (i = 1 ; i < len * 2 ; i++) {
char nibble = data & 0xf;
data >>= 4;
if (nibble != 0) {
if (earlier_unit++ > 0)
printf("*");
printf("%s", units[sys][i]);
if (nibble != 1) {
/* This is a _signed_ nibble(!) */
int val = nibble & 0x7;
if (nibble & 0x08)
val = -((0x7 & ~val) + 1);
printf("^%d", val);
}
}
}
if (earlier_unit == 0)
printf("(None)");
printf("\n");
}
/* ---------------------------------------------------------------------- */
/*
* 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 const struct bmcontrol uac2_extension_unit_bmcontrols[] = {
{ "Enable", 0 },
{ "Cluster", 1 },
{ "Underflow", 2 },
{ "Overflow", 3 },
{ NULL }
};
static const struct bmcontrol uac2_clock_source_bmcontrols[] = {
{ "Clock Frequency", 0 },
{ "Clock Validity", 1 },
{ NULL }
};
static const struct bmcontrol uac2_clock_selector_bmcontrols[] = {
{ "Clock Selector", 0 },
{ NULL }
};
static const struct bmcontrol uac2_clock_multiplier_bmcontrols[] = {
{ "Clock Numerator", 0 },
{ "Clock Denominator", 1 },
{ NULL }
};
static const struct bmcontrol uac2_selector_bmcontrols[] = {
{ "Selector", 0 },
{ NULL }
};
static void dump_audio_bmcontrols(const char *prefix, int bmcontrols, const struct bmcontrol *list, 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 */
list++;
}
}
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)"
};
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[128], term[128], termts[128];
if (buf[1] != USB_DT_CS_INTERFACE)
printf(" Warning: Invalid descriptor\n");
else if (buf[0] < 3)
printf(" Warning: Descriptor too short\n");
printf(" AudioControl Interface Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" 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];
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;
}
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:
get_string(dev, chnames, sizeof(chnames), buf[10]);
get_string(dev, term, sizeof(term), 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:
get_string(dev, chnames, sizeof(chnames), buf[13]);
get_string(dev, term, sizeof(term), 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) */
break;
case 0x03: /* OUTPUT_TERMINAL */
printf("(OUTPUT_TERMINAL)\n");
switch (protocol) {
case USB_AUDIO_CLASS_1:
get_string(dev, term, sizeof(term), 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:
get_string(dev, term, sizeof(term), 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) */
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;
}
get_string(dev, chnames, sizeof(chnames), buf[8+j]);
get_string(dev, term, sizeof(term), 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;
get_string(dev, chnames, sizeof(chnames), buf[10+j]);
get_string(dev, term, sizeof(term), 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) */
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");
get_string(dev, term, sizeof(term), 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");
get_string(dev, term, sizeof(term), 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) */
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");
get_string(dev, term, sizeof(term), 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);
}
get_string(dev, term, sizeof(term), buf[5+(k*4)]);
printf(" iFeature %5u %s\n", buf[5+(k*4)], term);
dump_junk(buf, " ", 6+(k*4));
break;
} /* switch (protocol) */
break;
case 0x07: /* PROCESSING_UNIT */
printf("(PROCESSING_UNIT)\n");
switch (protocol) {
case USB_AUDIO_CLASS_1:
j = buf[6];
k = buf[11+j];
get_string(dev, chnames, sizeof(chnames), buf[10+j]);
get_string(dev, term, sizeof(term), 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;
get_string(dev, chnames, sizeof(chnames), buf[12+j]);
get_string(dev, term, sizeof(term), 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) */
break;
case 0x08: /* EXTENSION_UNIT */
printf("(EXTENSION_UNIT)\n");
switch (protocol) {
case USB_AUDIO_CLASS_1:
j = buf[6];
k = buf[11+j];
get_string(dev, chnames, sizeof(chnames), buf[10+j]);
get_string(dev, term, sizeof(term), 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];
get_string(dev, chnames, sizeof(chnames), buf[13+j]);
get_string(dev, term, sizeof(term), 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 < 26; 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+k], term);
dump_junk(buf, " ", 16+j);
break;
} /* switch (protocol) */
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);
get_string(dev, term, sizeof(term), buf[7]);
printf(" bAssocTerminal %5u\n", buf[6]);
printf(" iClockSource %5u %s\n", buf[7], term);
dump_junk(buf, " ", 8);
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");
get_string(dev, term, sizeof(term), 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]);
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);
get_string(dev, term, sizeof(term), buf[6]);
printf(" iClockMultiplier %5u %s\n", buf[6], term);
dump_junk(buf, " ", 7);
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");
get_string(dev, term, sizeof(term), 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);
break;
case 0xf0: /* EFFECT_UNIT - the real value is 0x07, see above for the reason for remapping */
printf("(EFFECT_UNIT)\n");
if (buf[0] < 16)
printf(" Warning: Descriptor too short\n");
k = (buf[0] - 16) / 4;
get_string(dev, term, sizeof(term), 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));
break;
default:
printf("(unknown)\n"
" Invalid desc subtype:");
dump_bytes(buf+3, buf[0]-3);
break;
}
}
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)
{
static const char * const fmtItag[] = {
"TYPE_I_UNDEFINED", "PCM", "PCM8", "IEEE_FLOAT", "ALAW", "MULAW" };
static const char * const fmtIItag[] = { "TYPE_II_UNDEFINED", "MPEG", "AC-3" };
static const char * const fmtIIItag[] = {
"TYPE_III_UNDEFINED", "IEC1937_AC-3", "IEC1937_MPEG-1_Layer1",
"IEC1937_MPEG-Layer2/3/NOEXT", "IEC1937_MPEG-2_EXT",
"IEC1937_MPEG-2_Layer1_LS", "IEC1937_MPEG-2_Layer2/3_LS" };
unsigned int i, j, fmttag;
const char *fmtptr = "undefined";
char name[128];
if (buf[1] != USB_DT_CS_INTERFACE)
printf(" Warning: Invalid descriptor\n");
else if (buf[0] < 3)
printf(" Warning: Descriptor too short\n");
printf(" AudioStreaming Interface Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bDescriptorSubtype %5u ",
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 %5u\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]);
get_string(dev, name, sizeof(name), buf[15]);
printf(" iChannelNames %5u %s\n", buf[15], name);
dump_junk(buf, " ", 16);
break;
} /* switch (protocol) */
break;
case 0x02: /* FORMAT_TYPE */
printf("(FORMAT_TYPE)\n");
switch (protocol) {
case USB_AUDIO_CLASS_1:
if (buf[0] < 8)
printf(" Warning: Descriptor too short\n");
printf(" bFormatType %5u ", buf[3]);
switch (buf[3]) {
case 0x01: /* FORMAT_TYPE_I */
printf("(FORMAT_TYPE_I)\n");
j = buf[7] ? (buf[7]*3+8) : 14;
if (buf[0] < j)
printf(" Warning: Descriptor too short\n");
printf(" bNrChannels %5u\n"
" bSubframeSize %5u\n"
" bBitResolution %5u\n"
" bSamFreqType %5u %s\n",
buf[4], buf[5], buf[6], buf[7], buf[7] ? "Discrete" : "Continuous");
if (!buf[7])
printf(" tLowerSamFreq %7u\n"
" tUpperSamFreq %7u\n",
buf[8] | (buf[9] << 8) | (buf[10] << 16), buf[11] | (buf[12] << 8) | (buf[13] << 16));
else
for (i = 0; i < buf[7]; i++)
printf(" tSamFreq[%2u] %7u\n", i,
buf[8+3*i] | (buf[9+3*i] << 8) | (buf[10+3*i] << 16));
dump_junk(buf, " ", j);
break;
case 0x02: /* FORMAT_TYPE_II */
printf("(FORMAT_TYPE_II)\n");
j = buf[8] ? (buf[7]*3+9) : 15;
if (buf[0] < j)
printf(" Warning: Descriptor too short\n");
printf(" wMaxBitRate %5u\n"
" wSamplesPerFrame %5u\n"
" bSamFreqType %5u %s\n",
buf[4] | (buf[5] << 8), buf[6] | (buf[7] << 8), buf[8], buf[8] ? "Discrete" : "Continuous");
if (!buf[8])
printf(" tLowerSamFreq %7u\n"
" tUpperSamFreq %7u\n",
buf[9] | (buf[10] << 8) | (buf[11] << 16), buf[12] | (buf[13] << 8) | (buf[14] << 16));
else
for (i = 0; i < buf[8]; i++)
printf(" tSamFreq[%2u] %7u\n", i,
buf[9+3*i] | (buf[10+3*i] << 8) | (buf[11+3*i] << 16));
dump_junk(buf, " ", j);
break;
case 0x03: /* FORMAT_TYPE_III */
printf("(FORMAT_TYPE_III)\n");
j = buf[7] ? (buf[7]*3+8) : 14;
if (buf[0] < j)
printf(" Warning: Descriptor too short\n");
printf(" bNrChannels %5u\n"
" bSubframeSize %5u\n"
" bBitResolution %5u\n"
" bSamFreqType %5u %s\n",
buf[4], buf[5], buf[6], buf[7], buf[7] ? "Discrete" : "Continuous");
if (!buf[7])
printf(" tLowerSamFreq %7u\n"
" tUpperSamFreq %7u\n",
buf[8] | (buf[9] << 8) | (buf[10] << 16), buf[11] | (buf[12] << 8) | (buf[13] << 16));
else
for (i = 0; i < buf[7]; i++)
printf(" tSamFreq[%2u] %7u\n", i,
buf[8+3*i] | (buf[9+3*i] << 8) | (buf[10+3*i] << 16));
dump_junk(buf, " ", j);
break;
default:
printf("(unknown)\n"
" Invalid desc format type:");
dump_bytes(buf+4, buf[0]-4);
}
break;
case USB_AUDIO_CLASS_2:
printf(" bFormatType %5u ", buf[3]);
switch (buf[3]) {
case 0x01: /* FORMAT_TYPE_I */
printf("(FORMAT_TYPE_I)\n");
if (buf[0] < 6)
printf(" Warning: Descriptor too short\n");
printf(" bSubslotSize %5u\n"
" bBitResolution %5u\n",
buf[4], buf[5]);
dump_junk(buf, " ", 6);
break;
case 0x02: /* FORMAT_TYPE_II */
printf("(FORMAT_TYPE_II)\n");
if (buf[0] < 8)
printf(" Warning: Descriptor too short\n");
printf(" wMaxBitRate %5u\n"
" wSlotsPerFrame %5u\n",
buf[4] | (buf[5] << 8),
buf[6] | (buf[7] << 8));
dump_junk(buf, " ", 8);
break;
case 0x03: /* FORMAT_TYPE_III */
printf("(FORMAT_TYPE_III)\n");
if (buf[0] < 6)
printf(" Warning: Descriptor too short\n");
printf(" bSubslotSize %5u\n"
" bBitResolution %5u\n",
buf[4], buf[5]);
dump_junk(buf, " ", 6);
break;
case 0x04: /* FORMAT_TYPE_IV */
printf("(FORMAT_TYPE_IV)\n");
if (buf[0] < 4)
printf(" Warning: Descriptor too short\n");
printf(" bFormatType %5u\n", buf[3]);
dump_junk(buf, " ", 4);
break;
default:
printf("(unknown)\n"
" Invalid desc format type:");
dump_bytes(buf+4, buf[0]-4);
}
break;
} /* switch (protocol) */
break;
case 0x03: /* FORMAT_SPECIFIC */
printf("(FORMAT_SPECIFIC)\n");
if (buf[0] < 5)
printf(" Warning: Descriptor too short\n");
fmttag = buf[3] | (buf[4] << 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(" wFormatTag %5u %s\n", fmttag, fmtptr);
switch (fmttag) {
case 0x1001: /* MPEG */
if (buf[0] < 8)
printf(" Warning: Descriptor too short\n");
printf(" bmMPEGCapabilities 0x%04x\n",
buf[5] | (buf[6] << 8));
if (buf[5] & 0x01)
printf(" Layer I\n");
if (buf[5] & 0x02)
printf(" Layer II\n");
if (buf[5] & 0x04)
printf(" Layer III\n");
if (buf[5] & 0x08)
printf(" MPEG-1 only\n");
if (buf[5] & 0x10)
printf(" MPEG-1 dual-channel\n");
if (buf[5] & 0x20)
printf(" MPEG-2 second stereo\n");
if (buf[5] & 0x40)
printf(" MPEG-2 7.1 channel augmentation\n");
if (buf[5] & 0x80)
printf(" Adaptive multi-channel prediction\n");
printf(" MPEG-2 multilingual support: ");
switch (buf[6] & 3) {
case 0:
printf("Not supported\n");
break;
case 1:
printf("Supported at Fs\n");
break;
case 2:
printf("Reserved\n");
break;
default:
printf("Supported at Fs and 1/2Fs\n");
break;
}
printf(" bmMPEGFeatures 0x%02x\n", buf[7]);
printf(" Internal Dynamic Range Control: ");
switch ((buf[7] << 4) & 3) {
case 0:
printf("not supported\n");
break;
case 1:
printf("supported but not scalable\n");
break;
case 2:
printf("scalable, common boost and cut scaling value\n");
break;
default:
printf("scalable, separate boost and cut scaling value\n");
break;
}
dump_junk(buf, " ", 8);
break;
case 0x1002: /* AC-3 */
if (buf[0] < 10)
printf(" Warning: Descriptor too short\n");
printf(" bmBSID 0x%08x\n"
" bmAC3Features 0x%02x\n",
buf[5] | (buf[6] << 8) | (buf[7] << 16) | (buf[8] << 24), buf[9]);
if (buf[9] & 0x01)
printf(" RF mode\n");
if (buf[9] & 0x02)
printf(" Line mode\n");
if (buf[9] & 0x04)
printf(" Custom0 mode\n");
if (buf[9] & 0x08)
printf(" Custom1 mode\n");
printf(" Internal Dynamic Range Control: ");
switch ((buf[9] >> 4) & 3) {
case 0:
printf("not supported\n");
break;
case 1:
printf("supported but not scalable\n");
break;
case 2:
printf("scalable, common boost and cut scaling value\n");
break;
default:
printf("scalable, separate boost and cut scaling value\n");
break;
}
dump_junk(buf, " ", 8);
break;
default:
printf("(unknown)\n"
" Invalid desc format type:");
dump_bytes(buf+4, buf[0]-4);
}
break;
default:
printf(" Invalid desc subtype:");
dump_bytes(buf+3, buf[0]-3);
break;
}
}
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 const char * const lockdelunits[] = { "Undefined", "Milliseconds", "Decoded PCM samples", "Reserved" };
unsigned int lckdelidx;
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"
" 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);
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 */
}
static void dump_midistreaming_interface(libusb_device_handle *dev, const unsigned char *buf)
{
static const char * const jacktypes[] = {"Undefined", "Embedded", "External"};
char jackstr[128];
unsigned int j, tlength, capssize;
unsigned long caps;
if (buf[1] != USB_DT_CS_INTERFACE)
printf(" Warning: Invalid descriptor\n");
else if (buf[0] < 3)
printf(" Warning: Descriptor too short\n");
printf(" MIDIStreaming Interface Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bDescriptorSubtype %5u ",
buf[0], buf[1], buf[2]);
switch (buf[2]) {
case 0x01:
printf("(HEADER)\n");
if (buf[0] < 7)
printf(" Warning: Descriptor too short\n");
tlength = buf[5] | (buf[6] << 8);
printf(" bcdADC %2x.%02x\n"
" wTotalLength %5u\n",
buf[4], buf[3], tlength);
dump_junk(buf, " ", 7);
break;
case 0x02:
printf("(MIDI_IN_JACK)\n");
if (buf[0] < 6)
printf(" Warning: Descriptor too short\n");
get_string(dev, jackstr, sizeof(jackstr), buf[5]);
printf(" bJackType %5u %s\n"
" bJackID %5u\n"
" iJack %5u %s\n",
buf[3], buf[3] < 3 ? jacktypes[buf[3]] : "Invalid",
buf[4], buf[5], jackstr);
dump_junk(buf, " ", 6);
break;
case 0x03:
printf("(MIDI_OUT_JACK)\n");
if (buf[0] < 9)
printf(" Warning: Descriptor too short\n");
printf(" bJackType %5u %s\n"
" bJackID %5u\n"
" bNrInputPins %5u\n",
buf[3], buf[3] < 3 ? jacktypes[buf[3]] : "Invalid",
buf[4], buf[5]);
for (j = 0; j < buf[5]; j++) {
printf(" baSourceID(%2u) %5u\n"
" BaSourcePin(%2u) %5u\n",
j, buf[2*j+6], j, buf[2*j+7]);
}
j = 6+buf[5]*2; /* midi10.pdf says, incorrectly: 5+2*p */
get_string(dev, jackstr, sizeof(jackstr), buf[j]);
printf(" iJack %5u %s\n",
buf[j], jackstr);
dump_junk(buf, " ", j+1);
break;
case 0x04:
printf("(ELEMENT)\n");
if (buf[0] < 12)
printf(" Warning: Descriptor too short\n");
printf(" bElementID %5u\n"
" bNrInputPins %5u\n",
buf[3], buf[4]);
for (j = 0; j < buf[4]; j++) {
printf(" baSourceID(%2u) %5u\n"
" BaSourcePin(%2u) %5u\n",
j, buf[2*j+5], j, buf[2*j+6]);
}
j = 5+buf[4]*2;
printf(" bNrOutputPins %5u\n"
" bInTerminalLink %5u\n"
" bOutTerminalLink %5u\n"
" bElCapsSize %5u\n",
buf[j], buf[j+1], buf[j+2], buf[j+3]);
capssize = buf[j+3];
caps = 0;
for (j = 0; j < capssize; j++)
caps |= (buf[j+9+buf[4]*2] << (8*j));
printf(" bmElementCaps 0x%08lx\n", caps);
if (caps & 0x01)
printf(" Undefined\n");
if (caps & 0x02)
printf(" MIDI Clock\n");
if (caps & 0x04)
printf(" MTC (MIDI Time Code)\n");
if (caps & 0x08)
printf(" MMC (MIDI Machine Control)\n");
if (caps & 0x10)
printf(" GM1 (General MIDI v.1)\n");
if (caps & 0x20)
printf(" GM2 (General MIDI v.2)\n");
if (caps & 0x40)
printf(" GS MIDI Extension\n");
if (caps & 0x80)
printf(" XG MIDI Extension\n");
if (caps & 0x100)
printf(" EFX\n");
if (caps & 0x200)
printf(" MIDI Patch Bay\n");
if (caps & 0x400)
printf(" DLS1 (Downloadable Sounds Level 1)\n");
if (caps & 0x800)
printf(" DLS2 (Downloadable Sounds Level 2)\n");
j = 9+2*buf[4]+capssize;
get_string(dev, jackstr, sizeof(jackstr), buf[j]);
printf(" iElement %5u %s\n", buf[j], jackstr);
dump_junk(buf, " ", j+1);
break;
default:
printf("\n Invalid desc subtype: ");
dump_bytes(buf+3, buf[0]-3);
break;
}
}
static void dump_midistreaming_endpoint(const unsigned char *buf)
{
unsigned int j;
if (buf[1] != USB_DT_CS_ENDPOINT)
printf(" Warning: Invalid descriptor\n");
else if (buf[0] < 5)
printf(" Warning: Descriptor too short\n");
printf(" MIDIStreaming Endpoint Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bDescriptorSubtype %5u (%s)\n"
" bNumEmbMIDIJack %5u\n",
buf[0], buf[1], buf[2], buf[2] == 1 ? "GENERAL" : "Invalid", buf[3]);
for (j = 0; j < buf[3]; j++)
printf(" baAssocJackID(%2u) %5u\n", j, buf[4+j]);
dump_junk(buf, " ", 4+buf[3]);
}
/*
* Video Class descriptor dump
*/
static void dump_videocontrol_interface(libusb_device_handle *dev, const unsigned char *buf)
{
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"
};
static const char * const camctrlnames[] = {
"Scanning Mode", "Auto-Exposure Mode", "Auto-Exposure Priority",
"Exposure Time (Absolute)", "Exposure Time (Relative)", "Focus (Absolute)",
"Focus (Relative)", "Iris (Absolute)", "Iris (Relative)", "Zoom (Absolute)",
"Zoom (Relative)", "PanTilt (Absolute)", "PanTilt (Relative)",
"Roll (Absolute)", "Roll (Relative)", "Reserved", "Reserved", "Focus, Auto",
"Privacy"
};
static const char * const stdnames[] = {
"None", "NTSC - 525/60", "PAL - 625/50", "SECAM - 625/50",
"NTSC - 625/50", "PAL - 525/60" };
unsigned int i, ctrls, stds, n, p, termt, freq;
char term[128], termts[128];
if (buf[1] != USB_DT_CS_INTERFACE)
printf(" Warning: Invalid descriptor\n");
else if (buf[0] < 3)
printf(" Warning: Descriptor too short\n");
printf(" VideoControl Interface Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bDescriptorSubtype %5u ",
buf[0], buf[1], buf[2]);
switch (buf[2]) {
case 0x01: /* HEADER */
printf("(HEADER)\n");
n = buf[11];
if (buf[0] < 12+n)
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"
" dwClockFrequency %5u.%06uMHz\n"
" bInCollection %5u\n",
buf[4], buf[3], buf[5] | (buf[6] << 8), freq / 1000000,
freq % 1000000, n);
for (i = 0; i < n; i++)
printf(" baInterfaceNr(%2u) %5u\n", i, buf[12+i]);
dump_junk(buf, " ", 12+n);
break;
case 0x02: /* INPUT_TERMINAL */
printf("(INPUT_TERMINAL)\n");
get_string(dev, term, sizeof(term), buf[7]);
termt = buf[4] | (buf[5] << 8);
n = termt == 0x0201 ? 7 : 0;
get_videoterminal_string(termts, sizeof(termts), termt);
if (buf[0] < 8 + n)
printf(" Warning: Descriptor too short\n");
printf(" bTerminalID %5u\n"
" wTerminalType 0x%04x %s\n"
" bAssocTerminal %5u\n",
buf[3], termt, termts, buf[6]);
printf(" iTerminal %5u %s\n",
buf[7], term);
if (termt == 0x0201) {
n += buf[14];
printf(" wObjectiveFocalLengthMin %5u\n"
" wObjectiveFocalLengthMax %5u\n"
" wOcularFocalLength %5u\n"
" bControlSize %5u\n",
buf[8] | (buf[9] << 8), buf[10] | (buf[11] << 8),
buf[12] | (buf[13] << 8), buf[14]);
ctrls = 0;
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]);
}
dump_junk(buf, " ", 8+n);
break;
case 0x03: /* OUTPUT_TERMINAL */
printf("(OUTPUT_TERMINAL)\n");
get_string(dev, term, sizeof(term), 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 0x04: /* SELECTOR_UNIT */
printf("(SELECTOR_UNIT)\n");
p = buf[4];
if (buf[0] < 6+p)
printf(" Warning: Descriptor too short\n");
get_string(dev, term, sizeof(term), buf[5+p]);
printf(" bUnitID %5u\n"
" bNrInPins %5u\n",
buf[3], p);
for (i = 0; i < p; i++)
printf(" baSource(%2u) %5u\n", i, buf[5+i]);
printf(" iSelector %5u %s\n",
buf[5+p], term);
dump_junk(buf, " ", 6+p);
break;
case 0x05: /* PROCESSING_UNIT */
printf("(PROCESSING_UNIT)\n");
n = buf[7];
get_string(dev, term, sizeof(term), buf[8+n]);
if (buf[0] < 10+n)
printf(" Warning: Descriptor too short\n");
printf(" bUnitID %5u\n"
" bSourceID %5u\n"
" wMaxMultiplier %5u\n"
" bControlSize %5u\n",
buf[3], buf[4], buf[5] | (buf[6] << 8), n);
ctrls = 0;
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]);
stds = buf[9+n];
printf(" iProcessing %5u %s\n"
" bmVideoStandards 0x%2x\n", buf[8+n], term, stds);
for (i = 0; i < 6; i++)
if ((stds >> i) & 1)
printf(" %s\n", stdnames[i]);
break;
case 0x06: /* EXTENSION_UNIT */
printf("(EXTENSION_UNIT)\n");
p = buf[21];
n = buf[22+p];
get_string(dev, term, sizeof(term), buf[23+p+n]);
if (buf[0] < 24+p+n)
printf(" Warning: Descriptor too short\n");
printf(" bUnitID %5u\n"
" guidExtensionCode %s\n"
" bNumControl %5u\n"
" bNrPins %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]);
printf(" bControlSize %5u\n", buf[22+p]);
for (i = 0; i < n; i++)
printf(" bmControls(%2u) 0x%02x\n", i, buf[23+p+i]);
printf(" iExtension %5u %s\n",
buf[23+p+n], term);
dump_junk(buf, " ", 24+p+n);
break;
default:
printf("(unknown)\n"
" Invalid desc subtype:");
dump_bytes(buf+3, buf[0]-3);
break;
}
}
static void dump_videostreaming_interface(const unsigned char *buf)
{
static const char * const colorPrims[] = { "Unspecified", "BT.709,sRGB",
"BT.470-2 (M)", "BT.470-2 (B,G)", "SMPTE 170M", "SMPTE 240M" };
static const char * const transferChars[] = { "Unspecified", "BT.709",
"BT.470-2 (M)", "BT.470-2 (B,G)", "SMPTE 170M", "SMPTE 240M",
"Linear", "sRGB"};
static const char * const matrixCoeffs[] = { "Unspecified", "BT.709",
"FCC", "BT.470-2 (B,G)", "SMPTE 170M (BT.601)", "SMPTE 240M" };
unsigned int i, m, n, p, flags, len;
if (buf[1] != USB_DT_CS_INTERFACE)
printf(" Warning: Invalid descriptor\n");
else if (buf[0] < 3)
printf(" Warning: Descriptor too short\n");
printf(" VideoStreaming Interface Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bDescriptorSubtype %5u ",
buf[0], buf[1], buf[2]);
switch (buf[2]) {
case 0x01: /* INPUT_HEADER */
printf("(INPUT_HEADER)\n");
p = buf[3];
n = buf[12];
if (buf[0] < 13+p*n)
printf(" Warning: Descriptor too short\n");
printf(" bNumFormats %5u\n"
" wTotalLength %5u\n"
" bEndPointAddress %5u\n"
" bmInfo %5u\n"
" bTerminalLink %5u\n"
" bStillCaptureMethod %5u\n"
" bTriggerSupport %5u\n"
" bTriggerUsage %5u\n"
" bControlSize %5u\n",
p, buf[4] | (buf[5] << 8), buf[6], buf[7], buf[8],
buf[9], buf[10], buf[11], n);
for (i = 0; i < p; i++)
printf(
" bmaControls(%2u) %5u\n",
i, buf[13+p*n]);
dump_junk(buf, " ", 13+p*n);
break;
case 0x02: /* OUTPUT_HEADER */
printf("(OUTPUT_HEADER)\n");
p = buf[3];
n = buf[8];
if (buf[0] < 9+p*n)
printf(" Warning: Descriptor too short\n");
printf(" bNumFormats %5u\n"
" wTotalLength %5u\n"
" bEndpointAddress %5u\n"
" bTerminalLink %5u\n"
" bControlSize %5u\n",
p, buf[4] | (buf[5] << 8), buf[6], buf[7], n);
for (i = 0; i < p; i++)
printf(
" bmaControls(%2u) %5u\n",
i, buf[9+p*n]);
dump_junk(buf, " ", 9+p*n);
break;
case 0x03: /* STILL_IMAGE_FRAME */
printf("(STILL_IMAGE_FRAME)\n");
n = buf[4];
m = buf[5+4*n];
if (buf[0] < 6+4*n+m)
printf(" Warning: Descriptor too short\n");
printf(" bEndpointAddress %5u\n"
" bNumImageSizePatterns %3u\n",
buf[3], n);
for (i = 0; i < n; i++)
printf(" wWidth(%2u) %5u\n"
" 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);
for (i = 0; i < m; i++)
printf(" bCompression(%2u) %5u\n",
i, buf[6+4*n+i]);
dump_junk(buf, " ", 6+4*n+m);
break;
case 0x04: /* FORMAT_UNCOMPRESSED */
case 0x10: /* FORMAT_FRAME_BASED */
if (buf[2] == 0x04) {
printf("(FORMAT_UNCOMPRESSED)\n");
len = 27;
} else {
printf("(FORMAT_FRAME_BASED)\n");
len = 28;
}
if (buf[0] < len)
printf(" Warning: Descriptor too short\n");
flags = buf[25];
printf(" bFormatIndex %5u\n"
" bNumFrameDescriptors %5u\n"
" guidFormat %s\n"
" bBitsPerPixel %5u\n"
" bDefaultFrameIndex %5u\n"
" bAspectRatioX %5u\n"
" bAspectRatioY %5u\n"
" bmInterlaceFlags 0x%02x\n",
buf[3], buf[4], get_guid(&buf[5]), buf[21], buf[22],
buf[23], buf[24], flags);
printf(" Interlaced stream or variable: %s\n",
(flags & (1 << 0)) ? "Yes" : "No");
printf(" Fields per frame: %u fields\n",
(flags & (1 << 1)) ? 1 : 2);
printf(" Field 1 first: %s\n",
(flags