listusb: parse UVC devices

- Various cleanups to the USB-audio side, which is similar
- Add in the UVC (USB Video) specific info with parsing of some of the
  descriptors (most of the "control" part).
This commit is contained in:
Adrien Destugues 2016-11-02 12:28:02 +01:00
parent 56f749a63f
commit e64640878a
7 changed files with 565 additions and 21 deletions

View File

@ -14,18 +14,18 @@ enum {
USB_AUDIO_INTERFACE_AUDIO_CLASS = 0x01
};
enum { // Audio Interface Subclasses
USB_AUDIO_INTERFACE_UNDEFINED_SUBCLASS = 0x00,
enum { // Audio Interface Subclasses
USB_AUDIO_INTERFACE_UNDEFINED_SUBCLASS = 0x00,
USB_AUDIO_INTERFACE_AUDIOCONTROL_SUBCLASS = 0x01,
USB_AUDIO_INTERFACE_AUDIOSTREAMING_SUBCLASS, //
USB_AUDIO_INTERFACE_MIDISTREAMING_SUBCLASS //
USB_AUDIO_INTERFACE_AUDIOSTREAMING_SUBCLASS,
USB_AUDIO_INTERFACE_MIDISTREAMING_SUBCLASS
};
enum { // Audio Interface Protocol Codes
USB_AUDIO_PROTOCOL_UNDEFINED = 0x00
USB_AUDIO_PROTOCOL_UNDEFINED = 0x00
};
enum { // Audio Interface Protocol Codes
enum { // Audio Interface Class-Specific Descriptor Types
USB_AUDIO_CS_UNDEFINED = 0x20,
USB_AUDIO_CS_DEVICE = 0x21,
USB_AUDIO_CS_CONFIGURATION = 0x22,
@ -66,7 +66,7 @@ typedef struct {
struct {
uint16 total_length;
uint8 in_collection;
uint8 interface_numbers[1];
uint8 interface_numbers[0];
} _PACKED r1;
struct {

View File

@ -0,0 +1,142 @@
/*
* Copyright 2016, Adrien Destugues, pulkomandy@pulkomandy.tk
* Distributed under terms of the MIT license.
*/
#ifndef USB_VIDEO_H
#define USB_VIDEO_H
// Based on specification for UVC version 1.5.
#include <SupportDefs.h>
#define USB_VIDEO_DEVICE_CLASS 0x0E
enum { // Video Interface Subclasses
USB_VIDEO_INTERFACE_UNDEFINED_SUBCLASS = 0x00,
USB_VIDEO_INTERFACE_VIDEOCONTROL_SUBCLASS = 0x01,
USB_VIDEO_INTERFACE_VIDEOSTREAMING_SUBCLASS,
USB_VIDEO_INTERFACE_COLLECTION_SUBCLASS,
};
enum { // Video Interface Protocol Codes
USB_VIDEO_PROTOCOL_UNDEFINED = 0x00,
USB_VIDEO_PROTOCOL_15 = 0x01,
};
enum { // Video Interface Class-Specific Descriptor Types
USB_VIDEO_CS_UNDEFINED = 0x20,
USB_VIDEO_CS_DEVICE = 0x21,
USB_VIDEO_CS_CONFIGURATION = 0x22,
USB_VIDEO_CS_STRING = 0x23,
USB_VIDEO_CS_INTERFACE = 0x24,
USB_VIDEO_CS_ENDPOINT = 0x25,
};
enum { // Video Class-Specific VideoControl Interface descriptor subtypes
USB_VIDEO_VC_DESCRIPTOR_UNDEFINED = 0x00,
USB_VIDEO_VC_HEADER = 0x01,
USB_VIDEO_VC_INPUT_TERMINAL = 0x02,
USB_VIDEO_VC_OUTPUT_TERMINAL = 0x03,
USB_VIDEO_VC_SELECTOR_UNIT = 0x04,
USB_VIDEO_VC_PROCESSING_UNIT = 0x05,
USB_VIDEO_VC_EXTENSION_UNIT = 0x06,
USB_VIDEO_VC_ENCODING_UNIT = 0x07,
};
enum {
// USB Terminal Types
USB_VIDEO_VENDOR_USB_IO = 0x100,
USB_VIDEO_STREAMING_USB_IO = 0x101,
// Input terminal types
USB_VIDEO_VENDOR_IN = 0x200,
USB_VIDEO_CAMERA_IN = 0x201,
USB_VIDEO_MEDIA_TRANSPORT_IN = 0x202,
// Output terminal types
USB_VIDEO_VENDOR_OUT = 0x300,
USB_VIDEO_DISPLAY_OUT = 0x301,
USB_VIDEO_MEDIA_TRANSPORT_OUT = 0x302,
// External terminal types
USB_VIDEO_VENDOR_EXT = 0x400,
USB_VIDEO_COMPOSITE_EXT = 0x401,
USB_VIDEO_SVIDEO_EXT = 0x402,
USB_VIDEO_COMPONENT_EXT = 0x403,
};
// Class Specific Video Control Interface Header
// 1.5: Table 3-3 p.48
typedef struct {
uint8 length;
uint8 descriptor_type; // USB_AUDIO_CS_INTERFACE
uint8 descriptor_subtype; // USB_AUDIO_AC_HEADER
uint16 bcd_release_no;
uint16 total_length;
uint32 clock_frequency;
uint8 in_collection;
uint8 interface_numbers[0];
} _PACKED usb_videocontrol_header_descriptor;
// Input Terminal Descriptor
// 1.5: Table 3-4, page 50
typedef struct {
uint8 length;
uint8 descriptor_type; // USB_VIDEO_CS_INTERFACE
uint8 descriptor_subtype; // USB_VIDEO_VC_INPUT_TERMINAL
uint8 terminal_id;
uint16 terminal_type;
uint8 assoc_terminal;
uint8 terminal;
union {
struct {
uint16 focal_length_min;
uint16 focal_length_max;
uint16 focal_length;
uint8 control_size;
uint8 controls[3];
} _PACKED camera;
};
} _PACKED usb_video_input_terminal_descriptor;
// Output terminal descriptor
// 1.5: Table 3-5, page 51
typedef struct {
uint8 length;
uint8 descriptor_type; // USB_VIDEO_CS_INTERFACE
uint8 descriptor_subtype; // USB_VIDEO_VC_OUTPUT_TERMINAL
uint8 terminal_id;
uint16 terminal_type;
uint8 assoc_terminal;
uint8 source_id;
uint8 terminal;
} _PACKED usb_video_output_terminal_descriptor;
// Processing unit descriptor
// 1.5: Table 3-8, page 54
typedef struct {
uint8 length;
uint8 descriptor_type;
uint8 descriptor_subtype;
uint8 unit_id;
uint8 source_id;
uint16 max_multiplier;
uint8 control_size;
uint8 controls[3];
uint8 processing;
uint8 video_standards;
} _PACKED usb_video_processing_unit_descriptor;
#endif /* !USB_VIDEO_H */

View File

@ -6,6 +6,7 @@ UseHeaders [ FDirName $(HAIKU_TOP) src add-ons kernel bus_managers usb ] ;
BinCommand listusb :
listusb.cpp
usb_audio.cpp
usb_video.cpp
: be libdevice.so ;
# Manually reference generated headers

View File

@ -10,20 +10,22 @@
#include <Entry.h>
#include <Path.h>
#include <String.h>
#include <USBKit.h>
#include <stdio.h>
#include <usb/USB_audio.h>
#include <usb/USB_video.h>
#include "usbspec_private.h"
#include "usb-utils.h"
#include "listusb.h"
const char*
ClassName(int classNumber) {
switch (classNumber) {
case 0:
return "Per-interface classes";
case 1:
case USB_AUDIO_DEVICE_CLASS:
return "Audio";
case 2:
return "Communication";
@ -45,7 +47,7 @@ ClassName(int classNumber) {
return "Smart card";
case 13:
return "Content security";
case 14:
case USB_VIDEO_DEVICE_CLASS:
return "Video";
case 15:
return "Personal Healthcare";
@ -65,7 +67,58 @@ ClassName(int classNumber) {
}
void DumpAudioDescriptor(const usb_generic_descriptor* descriptor, int subclass);
const char*
SubclassName(int classNumber, int subclass)
{
if (classNumber == 0xEF) {
if (subclass == 0x02)
return " (Common)";
}
if (classNumber == USB_VIDEO_DEVICE_CLASS) {
switch (subclass) {
case USB_VIDEO_INTERFACE_UNDEFINED_SUBCLASS:
return " (Undefined)";
case USB_VIDEO_INTERFACE_VIDEOCONTROL_SUBCLASS:
return " (Control)";
case USB_VIDEO_INTERFACE_VIDEOSTREAMING_SUBCLASS:
return " (Streaming)";
case USB_VIDEO_INTERFACE_COLLECTION_SUBCLASS:
return " (Collection)";
}
}
return "";
}
const char*
ProtocolName(int classNumber, int subclass, int protocol)
{
switch (classNumber) {
case 0x09:
if (subclass == 0x00)
{
switch (protocol) {
case 0x00:
return " (Full speed)";
case 0x01:
return " (Hi-speed, single TT)";
case 0x02:
return " (Hi-speed, multiple TT)";
case 0x03:
return " (Super speed)";
}
}
case 0xE0:
if (subclass == 0x01 && protocol == 0x01)
return " (Bluetooth)";
case 0xEF:
if (subclass == 0x02 && protocol == 0x01)
return " (Interface Association)";
break;
}
return "";
}
void
@ -90,6 +143,9 @@ DumpDescriptor(const usb_generic_descriptor* descriptor,
case USB_AUDIO_DEVICE_CLASS:
DumpAudioDescriptor(descriptor, subclass);
break;
case USB_VIDEO_DEVICE_CLASS:
DumpVideoDescriptor(descriptor, subclass);
break;
default:
DumpDescriptorData(descriptor);
break;
@ -105,10 +161,12 @@ DumpInterface(const BUSBInterface* interface)
printf(" Class .............. 0x%02x (%s)\n",
interface->Class(), ClassName(interface->Class()));
printf(" Subclass ........... 0x%02x\n",
interface->Subclass());
printf(" Protocol ........... 0x%02x\n",
interface->Protocol());
printf(" Subclass ........... 0x%02x%s\n",
interface->Subclass(),
SubclassName(interface->Class(), interface->Subclass()));
printf(" Protocol ........... 0x%02x%s\n",
interface->Protocol(), ProtocolName(interface->Class(),
interface->Subclass(), interface->Protocol()));
printf(" Interface String ... \"%s\"\n",
interface->InterfaceString());
@ -190,10 +248,13 @@ DumpInfo(BUSBDevice& device, bool verbose)
printf("[Device /dev/bus/usb%s]\n", device.Location());
printf(" Class .................. 0x%02x (%s)\n", device.Class(),
ClassName(device.Class()));
printf(" Subclass ............... 0x%02x\n", device.Subclass());
printf(" Protocol ............... 0x%02x\n", device.Protocol());
printf(" Subclass ............... 0x%02x%s\n", device.Subclass(),
SubclassName(device.Class(), device.Subclass()));
printf(" Protocol ............... 0x%02x%s\n", device.Protocol(),
ProtocolName(device.Class(), device.Subclass(), device.Protocol()));
printf(" Max Endpoint 0 Packet .. %d\n", device.MaxEndpoint0PacketSize());
printf(" USB Version ............ 0x%04x\n", device.USBVersion());
uint32_t version = device.USBVersion();
printf(" USB Version ............ %d.%d\n", version >> 8, version & 0xFF);
printf(" Vendor ID .............. 0x%04x", device.VendorID());
if (vendorName != NULL)
printf(" (%s)", vendorName);

View File

@ -11,8 +11,13 @@
#define LISTUSB_H
#include <USBKit.h>
void DumpDescriptorData(const usb_generic_descriptor* descriptor);
void DumpAudioDescriptor(const usb_generic_descriptor* descriptor, int subclass);
void DumpVideoDescriptor(const usb_generic_descriptor* descriptor, int subclass);
#endif /* !LISTUSB_H */

View File

@ -19,10 +19,12 @@ void
DumpAudioCSInterfaceDescriptorHeader(
const usb_audiocontrol_header_descriptor* descriptor)
{
printf(" Type .............. 0x%02x\n",
descriptor->descriptor_type);
printf(" Subtype ........... 0x%02x (Header)\n",
descriptor->descriptor_subtype);
printf(" bcdADC Release date 0x%04x\n",
descriptor->bcd_release_no);
printf(" ADC Release ....... %d.%d\n",
descriptor->bcd_release_no >> 8, descriptor->bcd_release_no & 0xFF);
printf(" Total Length ...... %u\n",
descriptor->r1.total_length);
printf(" Interfaces ........ ");
@ -81,7 +83,7 @@ DumpChannelConfig(uint32 wChannelConfig)
}
const char*
static const char*
TerminalTypeName(uint16 terminalType)
{
switch (terminalType) {
@ -138,6 +140,8 @@ void
DumpAudioCSInterfaceDescriptorInputTerminal(
const usb_audio_input_terminal_descriptor* descriptor)
{
printf(" Type .............. 0x%02x\n",
descriptor->descriptor_type);
printf(" Subtype ........... 0x%02x (Input Terminal)\n",
descriptor->descriptor_subtype);
printf(" Terminal ID ....... %u\n",
@ -164,6 +168,8 @@ void
DumpAudioCSInterfaceDescriptorOutputTerminal(
const usb_audio_output_terminal_descriptor* descriptor)
{
printf(" Type .............. 0x%02x\n",
descriptor->descriptor_type);
printf(" Subtype ........... 0x%02x (Output Terminal)\n",
descriptor->descriptor_subtype);
printf(" Terminal ID ....... %u\n",
@ -184,6 +190,8 @@ void
DumpAudioCSInterfaceDescriptorMixerUnit(
const usb_audio_mixer_unit_descriptor* descriptor)
{
printf(" Type .............. 0x%02x\n",
descriptor->descriptor_type);
printf(" Subtype ........... 0x%02x (Mixer Unit)\n",
descriptor->descriptor_subtype);
printf(" Unit ID ........... %u\n",
@ -222,6 +230,8 @@ void
DumpAudioCSInterfaceDescriptorSelectorUnit(
const usb_audio_selector_unit_descriptor* descriptor)
{
printf(" Type .............. 0x%02x\n",
descriptor->descriptor_type);
printf(" Subtype ........... 0x%02x (Selector Unit)\n",
descriptor->descriptor_subtype);
printf(" Unit ID ........... %u\n",
@ -275,6 +285,8 @@ void
DumpAudioCSInterfaceDescriptorFeatureUnit(
const usb_audio_feature_unit_descriptor* descriptor)
{
printf(" Type .............. 0x%02x\n",
descriptor->descriptor_type);
printf(" Subtype ........... 0x%02x (Feature Unit)\n",
descriptor->descriptor_subtype);
printf(" Unit ID ........... %u\n",
@ -316,6 +328,8 @@ void
DumpAudioCSInterfaceDescriptorAssociated(
const usb_generic_descriptor* descriptor)
{
printf(" Type .............. 0x%02x\n",
descriptor->descriptor_type);
printf(" Subtype ........... 0x%02x (Associate Interface)\n",
(uint8)descriptor->data[0]);
printf(" Interface ......... %u\n",
@ -324,6 +338,7 @@ DumpAudioCSInterfaceDescriptorAssociated(
printf(" Data .............. ");
for (uint8 i = 0; i < descriptor->length - 2; i++)
printf("%02x ", descriptor->data[i]);
printf("\n");
}

View File

@ -0,0 +1,320 @@
/*
* Copyright 2016, Adrien Destugues, pulkomandy@pulkomandy.tk
* Distributed under terms of the MIT license.
*/
#include <stdio.h>
#include <usb/USB_video.h>
#include "listusb.h"
void
DumpVideoCSInterfaceDescriptorHeader(
const usb_videocontrol_header_descriptor* descriptor)
{
printf(" Type .............. 0x%02x\n",
descriptor->descriptor_type);
printf(" Subtype ........... 0x%02x (Header)\n",
descriptor->descriptor_subtype);
printf(" UVC Release ....... %d.%d\n",
descriptor->bcd_release_no >> 8, descriptor->bcd_release_no & 0xFF);
printf(" Total Length ...... %u\n",
descriptor->total_length);
printf(" Clock Frequency ... %" B_PRIu32 "\n",
descriptor->clock_frequency);
printf(" Interfaces ........ ");
for (uint8 i = 0; i < descriptor->in_collection; i++)
printf("%u, ", descriptor->interface_numbers[i]);
printf("\n");
}
static const char*
TerminalTypeName(uint16 terminalType)
{
switch (terminalType) {
case USB_VIDEO_VENDOR_USB_IO:
return "Vendor specific";
case USB_VIDEO_STREAMING_USB_IO:
return "Streaming";
case USB_VIDEO_VENDOR_IN:
return "Vendor specific input";
case USB_VIDEO_CAMERA_IN:
return "Camera";
case USB_VIDEO_MEDIA_TRANSPORT_IN:
return "Media transport input";
case USB_VIDEO_VENDOR_OUT:
return "Vendor specific output";
case USB_VIDEO_DISPLAY_OUT:
return "Display";
case USB_VIDEO_MEDIA_TRANSPORT_OUT:
return "Media transport output";
case USB_VIDEO_VENDOR_EXT:
return "Vendor specific format";
case USB_VIDEO_COMPOSITE_EXT:
return "Composite";
case USB_VIDEO_SVIDEO_EXT:
return "S-Video";
case USB_VIDEO_COMPONENT_EXT:
return "Component";
default:
return "Unknown";
}
}
void
DumpVideoCSInterfaceDescriptorOutputTerminal(
const usb_video_output_terminal_descriptor* descriptor)
{
printf(" Type .............. 0x%02x\n",
descriptor->descriptor_type);
printf(" Subtype ........... 0x%02x (Output Terminal)\n",
descriptor->descriptor_subtype);
printf(" Terminal ID ....... %u\n",
descriptor->terminal_id);
printf(" Terminal Type ..... 0x%04x (%s)\n",
descriptor->terminal_type,
TerminalTypeName(descriptor->terminal_type));
printf(" Associated Terminal %u\n",
descriptor->assoc_terminal);
printf(" Source ID ......... %u\n",
descriptor->source_id);
printf(" Terminal .......... %u\n",
descriptor->terminal);
}
void
DumpVideoCSInterfaceDescriptorInputTerminal(
const usb_video_input_terminal_descriptor* descriptor)
{
printf(" Type .............. 0x%02x\n",
descriptor->descriptor_type);
printf(" Subtype ........... 0x%02x (Input Terminal)\n",
descriptor->descriptor_subtype);
printf(" Terminal ID ....... %u\n",
descriptor->terminal_id);
printf(" Terminal Type ..... 0x%04x (%s)\n",
descriptor->terminal_type,
TerminalTypeName(descriptor->terminal_type));
printf(" Terminal .......... %u\n",
descriptor->terminal);
if (descriptor->terminal_type == USB_VIDEO_CAMERA_IN)
{
printf(" Min. Focal length . %u\n",
descriptor->camera.focal_length_min);
printf(" Max. Focal length . %u\n",
descriptor->camera.focal_length_min);
printf(" Focal length ...... %u\n",
descriptor->camera.focal_length);
printf(" Controls .......... %02x%02x%02x\n",
descriptor->camera.controls[0],
descriptor->camera.controls[1],
descriptor->camera.controls[2]);
}
}
static const char*
ProcessingControlString(int index)
{
switch(index)
{
case 0:
return "Brightness, ";
case 1:
return "Contrast, ";
case 2:
return "Hue, ";
case 3:
return "Saturation, ";
case 4:
return "Sharpness, ";
case 5:
return "Gamma, ";
case 6:
return "White balance temp., ";
case 7:
return "White balance component, ";
case 8:
return "Backlight compensation, ";
case 9:
return "Gain, ";
case 10:
return "Power line frequency, ";
case 11:
return "Automatic hue, ";
case 12:
return "Automatic white balance temp., ";
case 13:
return "Automatic white balance component, ";
case 14:
return "Digital multiplier, ";
case 15:
return "Digital multiplier limit, ";
case 16:
return "Analog video standard, ";
case 17:
return "Analog video lock status, ";
case 18:
return "Automatic contrast, ";
default:
return "Unknown, ";
}
}
void
DumpVideoCSInterfaceDescriptorProcessingUnit(
const usb_video_processing_unit_descriptor* descriptor)
{
printf(" Type .............. 0x%02x\n",
descriptor->descriptor_type);
printf(" Subtype ........... 0x%02x (Processing unit)\n",
descriptor->descriptor_subtype);
printf(" Unit ID ........... %u\n",
descriptor->unit_id);
printf(" Source ID ......... %u\n",
descriptor->source_id);
printf(" Max Multiplier .... %f\n",
descriptor->max_multiplier / 100.f);
printf(" Controls .......... ");
uint32_t controls = (descriptor->controls[0] << 16)
| (descriptor->controls[1] << 8)
| descriptor->controls[2];
for (int i = 0; i < 19; i++)
{
if (controls & (1 << (23 - i))) {
printf(ProcessingControlString(i));
}
}
printf("\n");
printf(" Processing ........ %u\n",
descriptor->processing);
printf(" Video Standards ... 0x%02x\n",
descriptor->video_standards);
}
void
DumpVideoCSInterfaceDescriptorExtensionUnit(
const usb_generic_descriptor* descriptor)
{
uint8 i = 0;
printf(" Type .............. 0x%02x\n",
descriptor->descriptor_type);
printf(" Subtype ........... 0x%02x (Extension unit)\n",
(uint8)descriptor->data[i++]);
printf(" Unit ID ........... %u\n",
(uint8)descriptor->data[i++]);
printf(" GUID .............. ");
for (i = 2; i < 16 + 2; i++)
printf("%02x ", descriptor->data[i]);
printf("\n");
printf(" Control count ..... %u\n",
(uint8)descriptor->data[i++]);
printf(" Input pins ........ ");
i = 20; // Skip the input pin count
for (; i - 20 < descriptor->data[19]; i++)
printf("%u, ", descriptor->data[i]);
printf("\n");
printf(" Controls .......... ");
uint8_t end = descriptor->data[i++];
uint8_t start = i;
for (; i - start < end; i++)
printf("%02x", (uint8)descriptor->data[i]);
printf("\n");
printf(" Extension ......... %u\n",
(uint8)descriptor->data[i++]);
}
void
DumpVideoControlCSInterfaceDescriptor(const usb_generic_descriptor* descriptor)
{
uint8 descriptorSubtype = descriptor->data[0];
switch (descriptorSubtype) {
case USB_VIDEO_VC_HEADER:
DumpVideoCSInterfaceDescriptorHeader(
(usb_videocontrol_header_descriptor*)descriptor);
break;
case USB_VIDEO_VC_INPUT_TERMINAL:
DumpVideoCSInterfaceDescriptorInputTerminal(
(usb_video_input_terminal_descriptor*)descriptor);
break;
case USB_VIDEO_VC_OUTPUT_TERMINAL:
DumpVideoCSInterfaceDescriptorOutputTerminal(
(usb_video_output_terminal_descriptor*)descriptor);
break;
case USB_VIDEO_VC_PROCESSING_UNIT:
DumpVideoCSInterfaceDescriptorProcessingUnit(
(usb_video_processing_unit_descriptor*)descriptor);
break;
case USB_VIDEO_VC_EXTENSION_UNIT:
DumpVideoCSInterfaceDescriptorExtensionUnit(descriptor);
break;
default:
DumpDescriptorData(descriptor);
}
}
void
DumpAudioStreamCSInterfaceDescriptor(const usb_generic_descriptor* descriptor);
void
DumpVideoDescriptor(const usb_generic_descriptor* descriptor, int subclass)
{
switch (subclass) {
case USB_VIDEO_INTERFACE_VIDEOCONTROL_SUBCLASS:
switch (descriptor->descriptor_type) {
case USB_VIDEO_CS_INTERFACE:
DumpVideoControlCSInterfaceDescriptor(descriptor);
break;
default:
DumpDescriptorData(descriptor);
break;
}
break;
case USB_VIDEO_INTERFACE_VIDEOSTREAMING_SUBCLASS:
switch (descriptor->descriptor_type) {
case USB_VIDEO_CS_INTERFACE:
DumpAudioStreamCSInterfaceDescriptor(descriptor);
break;
default:
DumpDescriptorData(descriptor);
break;
}
break;
case USB_VIDEO_INTERFACE_COLLECTION_SUBCLASS:
switch (descriptor->descriptor_type) {
case USB_VIDEO_CS_INTERFACE:
// TODO
DumpDescriptorData(descriptor);
break;
default:
DumpDescriptorData(descriptor);
break;
}
break;
default:
DumpDescriptorData(descriptor);
break;
}
}