root/src/bin/listusb/usb_audio.cpp
/*
 * Originally released under the Be Sample Code License.
 * Copyright 2000, Be Incorporated. All rights reserved.
 *
 * Modified for Haiku by François Revol, Michael Lotz and Greg Crain.
 * Copyright 2007-2024, Haiku Inc. All rights reserved.
 */

#include <MediaDefs.h>
#include <USBKit.h>
#include <stdio.h>

#include <usb/USB_audio.h>
#include <usb/USB_midi.h>

#include "listusb.h"


void
DumpClockSource(uint8 attributes)
{
        switch (attributes & 0x3){
        case 0:
                printf("  External clock.");
                break;
        case 1:
                printf("  Internal fixed clock.");
                break;
        case 2:
                printf("  Internal variable clock.");
                break;
        case 3:
                printf("  Internal programmable clock.");
                break;
        }
        if (attributes & 0x4){
                printf("  Clock synchronized to SOF.");
        }
        printf("\n");
}


void
DumpAudioCSInterfaceDescriptorClockSourceUnit(
        const usb_audio_clocksource_descriptor* descriptor)
{
        printf("                    Length............. 0x%02x\n", descriptor->length);
        printf("                    Type .............. 0x%02x\n", descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (Clock Source)\n",
                descriptor->descriptor_subtype);
        printf("                    Clock ID .......... 0x%02x\n", descriptor->clock_id);
        printf("                    Attributes......... 0x%02x", descriptor->bm_attributes);
        DumpClockSource(descriptor->bm_attributes);
        printf("                    bm controls ....... 0x%02x", descriptor->bm_controls);
        if (descriptor->bm_controls & 0x3)
         printf("  Clock Valid.");
        printf("\n");
        printf("                    Assoc Term ........ 0x%02x\n", descriptor->assoc_terminal);
        printf("                    Clock Src Idx ..... 0x%02x\n", descriptor->clock_source_idx);
}


void
DumpAudioCSInterfaceDescriptorClockSelectorUnit(
        const usb_audio_clockselector_descriptor* descriptor)
{
        printf("                    Length ............ 0x%02x\n", descriptor->length);
        printf("                    Type .............. 0x%02x\n", descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (Clock Selector)\n",
                descriptor->descriptor_subtype);
        printf("                    Clock ID .......... 0x%02x\n", descriptor->clock_id);
        printf("                    Num Of Input Pins .. 0x%02x\n", descriptor->nrinpins);
        printf("                    Clock Src Entity ... 0x%02x\n", descriptor->Csourceid[0]);
        printf("                    Controls ........... 0x%02x\n", descriptor->bm_controls);
        printf("                    Clock Selector...... 0x%02x\n", descriptor->clockselector);
}


void
DumpAudioCSInterfaceDescriptorClockMultiplier(
        const usb_audio_clockmultiplier_descriptor* descriptor)
{
        printf("                    Length ............ 0x%02x\n", descriptor->length);
        printf("                    Type .............. 0x%02x\n", descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (Clock Multiplier)\n",
                descriptor->descriptor_subtype);
        printf("                    Clock ID .......... 0x%02x\n", descriptor->clockid);
        printf("                    Clock Src Entity ... 0x%02x\n", descriptor->clksourceid);
        printf("                    bm_controls ........ 0x%02x\n", descriptor->bm_controls);
        printf("                    Clock Multipler .... 0x%02x\n", descriptor->clockmultiplier);
}

uint16
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("                    Audio codec version .. %d.%d\n",
                descriptor->bcd_release_no >> 8, descriptor->bcd_release_no & 0xFF);

        if (descriptor->bcd_release_no < USB_AUDIO_CLASS_VERSION_2) {
                printf("                    Total Length ...... %u\n",
                        descriptor->r1.total_length);
                printf("                    Interfaces ........ ");

                for (uint8 i = 0; i < descriptor->r1.in_collection; i++)
                        printf("%u, ", descriptor->r1.interface_numbers[i]);
                printf("\n");
        } else {
                // Audio 2.0
                printf("                    Function Category...... %u\n", descriptor->r2.function_category);
                printf("                    Total Length ...........%d\n", descriptor->r2.total_length);
                printf("                    bm Controls ............0x%02x\n", descriptor->r2.bm_controls);
        }

        return descriptor->bcd_release_no;
}


void
DumpChannelConfig(uint32 wChannelConfig)
{
        struct _Entry {
                const char* name;
                uint32 mask;
        } aClusters[] = {
                { "Front .......... ", B_CHANNEL_LEFT | B_CHANNEL_RIGHT
                        | B_CHANNEL_CENTER },
                { "L.F.E .......... ", B_CHANNEL_SUB },
                { "Back ........... ", B_CHANNEL_REARLEFT | B_CHANNEL_REARRIGHT
                        | B_CHANNEL_BACK_CENTER },
                { "Center ......... ", B_CHANNEL_FRONT_LEFT_CENTER
                        | B_CHANNEL_FRONT_RIGHT_CENTER },
                { "Side ........... ", B_CHANNEL_SIDE_LEFT | B_CHANNEL_SIDE_RIGHT },
                { "Top ............ ", B_CHANNEL_TOP_CENTER },
                { "Top Front ...... ", B_CHANNEL_TOP_FRONT_LEFT
                        | B_CHANNEL_TOP_FRONT_CENTER | B_CHANNEL_TOP_FRONT_RIGHT },
                { "Top Back ....... ", B_CHANNEL_TOP_BACK_LEFT
                        | B_CHANNEL_TOP_BACK_CENTER | B_CHANNEL_TOP_BACK_RIGHT }
        };

        struct _Entry aChannels[] = {
                { "Left", B_CHANNEL_LEFT | B_CHANNEL_FRONT_LEFT_CENTER
                        | B_CHANNEL_REARLEFT | B_CHANNEL_SIDE_LEFT | B_CHANNEL_TOP_BACK_LEFT
                        | B_CHANNEL_TOP_FRONT_LEFT },
                { "Right", B_CHANNEL_FRONT_RIGHT_CENTER | B_CHANNEL_TOP_FRONT_RIGHT
                        | B_CHANNEL_REARRIGHT | B_CHANNEL_RIGHT | B_CHANNEL_SIDE_RIGHT
                        | B_CHANNEL_TOP_BACK_RIGHT },
                { "Center", B_CHANNEL_BACK_CENTER | B_CHANNEL_CENTER
                        | B_CHANNEL_TOP_BACK_CENTER | B_CHANNEL_TOP_CENTER
                        | B_CHANNEL_TOP_FRONT_CENTER },
                { "L.F.E.", B_CHANNEL_SUB }
        };

        for (size_t i = 0; i < sizeof(aClusters) / sizeof(aClusters[0]); i++) {
                uint32 mask = aClusters[i].mask & wChannelConfig;
                if (mask != 0) {
                        printf("                       %s", aClusters[i].name);
                        for (size_t j = 0; j < sizeof(aChannels) / sizeof(aChannels[0]); j++)
                                if ((aChannels[j].mask & mask) != 0)
                                        printf("%s ", aChannels[j].name);
                        printf("\n");
                }
        }
}


static const char*
TerminalTypeName(uint16 terminalType)
{
        switch (terminalType) {
                case USB_AUDIO_UNDEFINED_USB_IO:
                        return "USB Undefined";
                case USB_AUDIO_STREAMING_USB_IO:
                        return "USB Streaming";
                case USB_AUDIO_VENDOR_USB_IO:
                        return "USB vendor specific";

                case USB_AUDIO_UNDEFINED_IN:
                        return "Undefined";
                case USB_AUDIO_MICROPHONE_IN:
                        return "Microphone";
                case USB_AUDIO_DESKTOPMIC_IN:
                        return "Desktop microphone";
                case USB_AUDIO_PERSONALMIC_IN:
                        return "Personal microphone";
                case USB_AUDIO_OMNI_MIC_IN:
                        return "Omni-directional microphone";
                case USB_AUDIO_MICS_ARRAY_IN:
                        return "Microphone array";
                case USB_AUDIO_PROC_MICS_ARRAY_IN:
                        return "Processing microphone array";
                case USB_AUDIO_LINE_CONNECTOR_IO:
                        return "Line I/O";
                case USB_AUDIO_SPDIF_INTERFACE_IO:
                        return "S/PDIF";

                case USB_AUDIO_UNDEFINED_OUT:
                        return "Undefined";
                case USB_AUDIO_SPEAKER_OUT:
                        return "Speaker";
                case USB_AUDIO_HEAD_PHONES_OUT:
                        return "Headphones";
                case USB_AUDIO_HMD_AUDIO_OUT:
                        return "Head Mounted Display Audio";
                case USB_AUDIO_DESKTOP_SPEAKER:
                        return "Desktop speaker";
                case USB_AUDIO_ROOM_SPEAKER:
                        return "Room speaker";
                case USB_AUDIO_COMM_SPEAKER:
                        return "Communication speaker";
                case USB_AUDIO_LFE_SPEAKER:
                        return "Low frequency effects speaker";

                default:
                        return "Unknown";
        }
}


void
DumpAudioCSInterfaceDescriptorInputTerminal(
        const usb_audio_input_terminal_descriptor* descriptor,
         uint16 bcd_release_no)
{
        printf("                    Length............. %u\n", descriptor->length);
        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("                    Associated Terminal %u\n",
                descriptor->assoc_terminal);

        if (bcd_release_no < USB_AUDIO_CLASS_VERSION_2){
                printf("                    Nr Channels ....... %u\n", descriptor->r1.num_channels);
                printf("                    Channel Config .... 0x%x\n", descriptor->r1.channel_config);
                DumpChannelConfig(descriptor->r1.channel_config);
                printf("                    Channel Names ..... %u\n", descriptor->r1.channel_names);
                printf("                    Terminal .......... %u\n", descriptor->r1.terminal);
        } else {
                // Audio 2.0
                printf("                    Clock Source ID.....0x%02x\n", descriptor->r2.clock_source_id);
                printf("                    Nr Channels ....... %u\n", descriptor->r2.num_channels);
                printf("                    Channel Config .... 0x%08" B_PRIx32 "\n",
                        descriptor->r2.channel_config);
                DumpChannelConfig(descriptor->r2.channel_config);
                printf("                    Channel Names ..... %u\n", descriptor->r2.channel_names);
                printf("                    bm_controls.........0x%04x\n", descriptor->r2.bm_controls);
                printf("                    Terminal .......... %u\n", descriptor->r2.terminal);
        }
}


void
DumpAudioCSInterfaceDescriptorOutputTerminal(
        const usb_audio_output_terminal_descriptor* descriptor,
         uint16 bcd_release_no)
{
        printf("                    Length............. %u\n", descriptor->length);
        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 ......... 0x%02x\n", descriptor->source_id);

        if (bcd_release_no < USB_AUDIO_CLASS_VERSION_2) {
                printf("                    Terminal .......... %u\n", descriptor->r1.terminal);
        } else {
                // Audio 2.0
                printf("                    Clock Source ID.....0x%02x\n", descriptor->r2.clock_source_id);
                printf("                    bm_controls.........0x%04x\n", descriptor->r2.bm_controls);
                printf("                    Terminal .......... %u\n", descriptor->r2.terminal);
        }
}


void
DumpAudioCSInterfaceDescriptorMixerUnit(
        const usb_audio_mixer_unit_descriptor* descriptor)
{
        printf("                    Length............. %u\n", descriptor->length);
        printf("                    Type .............. 0x%02x\n",
                descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (Mixer Unit)\n",
                descriptor->descriptor_subtype);
        printf("                    Unit ID ........... %u\n",
                descriptor->unit_id);

        printf("                    Source IDs ........ ");
        for (uint8 i = 0; i < descriptor->num_input_pins; i++)
                printf("%u, ", descriptor->input_pins[i]);
        printf("\n");

        usb_audio_output_channels_descriptor_r1* channels
                = (usb_audio_output_channels_descriptor_r1*)
                        &descriptor->input_pins[descriptor->num_input_pins];

        printf("                    Channels .......... %u\n",
                channels->num_output_pins);
        printf("                    Channel Config .... 0x%x\n",
                        channels->channel_config);
        DumpChannelConfig(channels->channel_config);
        printf("                    Channel Names ..... %u\n",
                channels->channel_names);

        usb_generic_descriptor* generic = (usb_generic_descriptor*)descriptor;
        uint8 idx = 7 + descriptor->num_input_pins;
                printf("                    Bitmap Control .... 0x");
        for (uint i = 1; idx < descriptor->length - 3; idx++, i++)
                printf("%02x ", (uint8)generic->data[idx]);
        printf("\n");

        printf("                    Mixer ............. %u\n",
                        generic->data[generic->length - 3]);
}


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",
                descriptor->unit_id);

        printf("                    Source IDs ........ ");
        for (uint8 i = 0; i < descriptor->num_input_pins; i++)
                printf("%u, ", descriptor->input_pins[i]);
        printf("\n");

        usb_generic_descriptor* generic = (usb_generic_descriptor*)descriptor;
        printf("                    Selector .......... %u\n",
                (uint8)generic->data[descriptor->num_input_pins + 2]);
}


void
DumpBMAControl(uint8 channel, uint32 bma, uint16 bcd_release_no)
{
        const char* BMAControls[] = {
                "Mute",
                "Volume",
                "Bass",
                "Mid",
                "Treble",
                "Graphic Equalizer",
                "Automatic Gain",
                "Delay",
                "Bass Boost",
                "Loudness"
        };

        if (bma == 0)
                return;

        if (channel == 0)
                printf("                       Master Channel . ");
        else
                printf("                       Channel %u ...... ", channel);

        int mask = 1;
        if (bcd_release_no < USB_AUDIO_CLASS_VERSION_2) {
        for (uint8 i = 0;
                        i < sizeof(BMAControls) / sizeof(BMAControls[0]); i++, mask <<= 1)
                if (bma & mask)
                        printf("%s ", BMAControls[i]);
        } else {
                // Audio 2.0
                mask = 0x3;
                for (uint8 i = 0; i < sizeof(BMAControls) / sizeof(BMAControls[0]); i++, mask <<= 2) {
                        if (bma & mask) {
                                printf("%s ", BMAControls[i]);
                        }
                }
        }
        printf("\n");
}


void
DumpAudioCSInterfaceDescriptorFeatureUnit(
        const usb_audio_feature_unit_descriptor* descriptor,
        uint16 bcd_release_no)
{
        printf("                    Length............. %u\n", descriptor->length);
        printf("                    Type .............. 0x%02x\n",
                descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (Feature Unit)\n",
                descriptor->descriptor_subtype);
        printf("                    Unit ID ........... %u\n",
                        descriptor->unit_id);
        printf("                    Source ID ......... 0x%02x\n", descriptor->source_id);

        if (bcd_release_no < USB_AUDIO_CLASS_VERSION_2) {
                printf("                    Control Size ...... %u\n", descriptor->r1.control_size);
                uint8 channels = 0;
                if (descriptor->r1.control_size > 0)
                        channels = (descriptor->length - 6) / descriptor->r1.control_size;
                for (uint8 i = 0; i < channels; i++) {
                        switch (descriptor->r1.control_size) {
                                case 1:
                                        DumpBMAControl(i, descriptor->r1.bma_controls[i], bcd_release_no);
                                        break;
                                case 2:
                                        DumpBMAControl(i, *(uint16*)&descriptor->r1.bma_controls[i * 2],
                                                bcd_release_no);
                                        break;
                                case 4:
                                        DumpBMAControl(i, *(uint32*)&descriptor->r1.bma_controls[i * 4],
                                                bcd_release_no);
                                        break;
                                default:
                                        printf("                    BMA Channel %u ... ", i);
                                        for (uint8 j = 0; j < descriptor->r1.control_size; j++)
                                                printf("%02x ", descriptor->r1.bma_controls[i + j]);
                                        printf("\n");
                                        break;
                        }
                        //      usb_generic_descriptor* generic = (usb_generic_descriptor*)descriptor;
                        //      printf("                    Feature ........... %u\n",
                        //                      (uint8)generic->data[descriptor->length - 3]);
                }
        } else {
                        // Audio 2.0
                        printf("                    BMA Controls ....... 0x%08" B_PRIx32 "\n",
                                descriptor->r2.bma_controls[0]);
                        DumpBMAControl(0, descriptor->r2.bma_controls[0], bcd_release_no);
                }
}

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",
                (uint8)descriptor->data[1]);

        printf("                    Data .............. ");
        for (uint8 i = 0; i < descriptor->length - 2; i++)
                printf("%02x ", descriptor->data[i]);
        printf("\n");
}


void
DumpAudioControlCSInterfaceDescriptor(const usb_generic_descriptor* descriptor)
{
        uint8 descriptorSubtype = descriptor->data[0];
        static uint16 bcd_release_no;

        switch (descriptorSubtype) {
                case USB_AUDIO_AC_HEADER:
                        bcd_release_no = DumpAudioCSInterfaceDescriptorHeader(
                                (usb_audiocontrol_header_descriptor*)descriptor);
                        break;
                case USB_AUDIO_AC_INPUT_TERMINAL:
                        DumpAudioCSInterfaceDescriptorInputTerminal(
                                (usb_audio_input_terminal_descriptor*)descriptor, bcd_release_no);
                        break;
                case USB_AUDIO_AC_OUTPUT_TERMINAL:
                        DumpAudioCSInterfaceDescriptorOutputTerminal(
                                (usb_audio_output_terminal_descriptor*)descriptor, bcd_release_no);
                        break;
                case USB_AUDIO_AC_MIXER_UNIT:
                        DumpAudioCSInterfaceDescriptorMixerUnit(
                                (usb_audio_mixer_unit_descriptor*)descriptor);
                        break;
                case USB_AUDIO_AC_SELECTOR_UNIT:
                        DumpAudioCSInterfaceDescriptorSelectorUnit(
                                (usb_audio_selector_unit_descriptor*)descriptor);
                        break;
                case USB_AUDIO_AC_FEATURE_UNIT:
                        DumpAudioCSInterfaceDescriptorFeatureUnit(
                                (usb_audio_feature_unit_descriptor*)descriptor, bcd_release_no);
                        break;
                case USB_AUDIO_AC_EXTENSION_UNIT:
                        // USB_AUDIO_AC_PROCESSING_UNIT_R2 == USB_AUDIO_AC_EXTENSION_UNIT
                        if (bcd_release_no < USB_AUDIO_CLASS_VERSION_2)
                                DumpAudioCSInterfaceDescriptorAssociated(descriptor);
                        break;
                case USB_AUDIO_AC_PROCESSING_UNIT:
                        // USB_AUDIO_AC_EFFECT_UNIT_R2 == USB_AUDIO_AC_PROCESSING_UNIT
                        break;
                case USB_AUDIO_AC_EXTENSION_UNIT_R2:
                        break;
                case USB_AUDIO_AC_CLOCK_SOURCE_R2:
                        DumpAudioCSInterfaceDescriptorClockSourceUnit(
                                (usb_audio_clocksource_descriptor*)descriptor);
                        break;
                case USB_AUDIO_AC_CLOCK_SELECTOR_R2:
                        DumpAudioCSInterfaceDescriptorClockSelectorUnit(
                                (usb_audio_clockselector_descriptor*)descriptor);
                        break;
                case USB_AUDIO_AC_CLOCK_MULTIPLIER_R2:
                        DumpAudioCSInterfaceDescriptorClockMultiplier(
                        (usb_audio_clockmultiplier_descriptor*)descriptor);
                        break;
                case USB_AUDIO_AC_SAMPLE_RATE_CONVERTER_R2:
                        break;

                default:
                        DumpDescriptorData(descriptor);
        }
}


void
DumpGeneralASInterfaceDescriptor(
        const usb_audio_streaming_interface_descriptor* descriptor)
{
        printf("                    Subtype ........... %u (AS_GENERAL)\n",
                descriptor->descriptor_subtype);
        printf("                    Terminal link ..... %u\n",
                descriptor->terminal_link);
        printf("                    Delay ............. %u\n",
                descriptor->r1.delay);
        printf("                    Format tag ........ %u\n",
                descriptor->r1.format_tag);
}


uint32
GetSamplingFrequency(const usb_audio_sampling_freq& freq)
{
        return freq.bytes[0] | freq.bytes[1] << 8 | freq.bytes[2] << 16;
}


void
DumpSamplingFrequencies(uint8 type, const usb_audio_sampling_freq* freqs)
{
        if (type > 0) {
                printf("                    Sampling Freq ..... ");
                for (uint8 i = 0; i < type; i++)
                        printf("%" B_PRIu32 ", ", GetSamplingFrequency(freqs[i]));
                printf("\n");
        } else {
                printf("                    Sampling Freq ..... %" B_PRIu32 " to %"
                        B_PRIu32 "\n", GetSamplingFrequency(freqs[0]),
                        GetSamplingFrequency(freqs[1]));
        }
}


void
DumpASFormatTypeI(const usb_audio_format_descriptor* descriptor)
{
        printf("                    Subtype ........... %u (FORMAT_TYPE)\n",
                descriptor->descriptor_subtype);
        printf("                    Format Type ....... %u (FORMAT_TYPE_I)\n",
                descriptor->format_type);
        printf("                    Channels .......... %u\n",
                descriptor->typeI.nr_channels);
        printf("                    Subframe size ..... %u\n",
                descriptor->typeI.subframe_size);
        printf("                    Bit resolution .... %u\n",
                descriptor->typeI.bit_resolution);

        DumpSamplingFrequencies(descriptor->typeI.sam_freq_type,
                        descriptor->typeI.sam_freqs);
}


void
DumpASFormatTypeIII(const usb_audio_format_descriptor* descriptor)
{
        printf("                    Subtype ........... %u (FORMAT_TYPE)\n",
                descriptor->descriptor_subtype);
        printf("                    Format Type ....... %u (FORMAT_TYPE_III)\n",
                descriptor->format_type);
        printf("                    Channels .......... %u\n",
                descriptor->typeIII.nr_channels);
        printf("                    Subframe size ..... %u\n",
                descriptor->typeIII.subframe_size);
        printf("                    Bit resolution .... %u\n",
                descriptor->typeIII.bit_resolution);

        DumpSamplingFrequencies(descriptor->typeIII.sam_freq_type,
                        descriptor->typeIII.sam_freqs);
}


void
DumpASFormatTypeII(const usb_audio_format_descriptor* descriptor)
{
        printf("                    Subtype ........... %u (FORMAT_TYPE)\n",
                descriptor->descriptor_subtype);
        printf("                    Format Type ....... %u (FORMAT_TYPE_II)\n",
                descriptor->format_type);
        printf("                    Max Bitrate ....... %u\n",
                descriptor->typeII.max_bit_rate);
        printf("                    Samples per Frame . %u\n",
                descriptor->typeII.samples_per_frame);

        DumpSamplingFrequencies(descriptor->typeII.sam_freq_type,
                        descriptor->typeII.sam_freqs);
}


void
DumpASFmtType(const usb_audio_format_descriptor* descriptor)
{
        uint8 format = descriptor->format_type;
        switch (format) {
                case USB_AUDIO_FORMAT_TYPE_I:
                        DumpASFormatTypeI(descriptor);
                        break;
                case USB_AUDIO_FORMAT_TYPE_II:
                        DumpASFormatTypeII(descriptor);
                        break;
                case USB_AUDIO_FORMAT_TYPE_III:
                        DumpASFormatTypeIII(descriptor);
                        break;
                default:
                        DumpDescriptorData((usb_generic_descriptor*)descriptor);
                        break;
        }
}


void
DumpMPEGCapabilities(uint16 capabilities)
{
        const char* MPEGCapabilities[] = {
                "Layer I",
                "Layer II",
                "Layer III",

                "MPEG-1 only",
                "MPEG-1 dual-channel",
                "MPEG-2 second stereo",
                "MPEG-2 7.1 channel augumentation",
                "Adaptive multi-channel predicion"
        };

        uint16 mask = 1;
        for (uint8 i = 0;
                        i < sizeof(MPEGCapabilities) / sizeof(MPEGCapabilities[0]); i++) {
                if (capabilities & mask)
                        printf("                         %s\n", MPEGCapabilities[i]);
                mask <<= 1;
        }

        mask = 0x300; // bits 8 and 9
        uint16 multilingualSupport = (capabilities & mask) >> 8;
        switch (multilingualSupport) {
                case 0:
                        printf("                         No Multilingual support\n");
                        break;
                case 1:
                        printf("                         Supported at Fs\n");
                        break;
                case 3:
                        printf("                         Supported at Fs and 1/2Fs\n");
                        break;
                default:
                        break;
        }
}


void
DumpMPEGFeatures(uint8 features)
{
        uint8 mask = 0x30; // bits 4 and 5
        uint8 dynRangeControl = (features & mask) >> 4;
        switch (dynRangeControl) {
                case 0:
                        printf("                         Not supported\n");
                        break;
                case 1:
                        printf("                         Supported, not scalable\n");
                        break;
                case 2:
                        printf("                         Scalable, common boost, "
                                "cut scaling value\n");
                        break;
                case 3:
                        printf("                         Scalable, separate boost, "
                                "cut scaling value\n");
                default:
                        break;
        }
}


void
DumpASFmtSpecificMPEG(const usb_generic_descriptor* descriptor)
{
        printf("                    Subtype ........... %u (FORMAT_SPECIFIC)\n",
                descriptor->data[0]);
        printf("                    Format Tag ........ %u\n",
                *(uint16*)&descriptor->data[1]);
        printf("                    MPEG Capabilities . %u\n",
                *(uint16*)&descriptor->data[3]);
        DumpMPEGCapabilities(*(uint16*)&descriptor->data[3]);
        printf("                    MPEG Features ..... %u\n",
                descriptor->data[5]);
        DumpMPEGFeatures(descriptor->data[5]);
}


void
DumpAC_3Features(uint8 features)
{
        const char* featuresStr[] = {
                "RF mode",
                "Line mode",
                "Custom0 mode",
                "Custom1 mode"
        };

        uint8 mask = 1;
        for (uint8 i = 0; i < sizeof(featuresStr) / sizeof(const char*); i++) {
                if (features & mask)
                        printf("                         %s\n", featuresStr[i]);
                mask <<= 1;
        }

        mask = 0x30; // bits 4 and 5
        uint8 dynRangeControl = (features & mask) >> 4;
        switch (dynRangeControl) {
                case 0:
                        printf("                         Not supported\n");
                        break;
                case 1:
                        printf("                         Supported, not scalable\n");
                        break;
                case 2:
                        printf("                         Scalable, common boost, "
                                "cut scaling value\n");
                        break;
                case 3:
                        printf("                         Scalable, separate boost, "
                                "cut scaling value\n");
                default:
                        break;
        }
}


void
DumpASFmtSpecificAC_3(const usb_generic_descriptor* descriptor)
{
        printf("                    Subtype ........... %u (FORMAT_TYPE)\n",
                descriptor->data[0]);
        printf("                    Format Tag ........ %u\n",
                *(uint16*)&descriptor->data[1]);
        printf("                    BSID .............. %" B_PRIx32 "\n",
                *(uint32*)&descriptor->data[2]);
        printf("                    AC3 Features ...... %u\n",
                descriptor->data[6]);
        DumpAC_3Features(descriptor->data[6]);
}


void
DumpASFmtSpecific(const usb_generic_descriptor* descriptor)
{
        enum {
                TYPE_II_UNDEFINED = 0x1000,
                MPEG =                          0x1001,
                AC_3 =                          0x1002
        };

        uint16 formatTag = *(uint16*)&descriptor->data[1];
        switch (formatTag) {
                case MPEG:
                        DumpASFmtSpecificMPEG(descriptor);
                        break;
                case AC_3:
                        DumpASFmtSpecificAC_3(descriptor);
                        break;
                default:
                        DumpDescriptorData(descriptor);
                        break;
        }
}


void
DumpAudioStreamCSInterfaceDescriptor(const usb_generic_descriptor* descriptor)
{
        uint8 subtype = descriptor->data[0];
        switch (subtype) {
                case USB_AUDIO_AS_GENERAL:
                        DumpGeneralASInterfaceDescriptor(
                                (usb_audio_streaming_interface_descriptor*)descriptor);
                        break;
                case USB_AUDIO_AS_FORMAT_TYPE:
                        DumpASFmtType(
                                (usb_audio_format_descriptor*)descriptor);
                        break;
                case USB_AUDIO_AS_FORMAT_SPECIFIC:
                        DumpASFmtSpecific(descriptor);
                        break;
                default:
                        DumpDescriptorData(descriptor);
                        break;
        }
}


void
DumpAudioStreamCSEndpointDescriptor(
        const usb_audio_streaming_endpoint_descriptor* descriptor)
{
        printf("                    Type .............. 0x%02x (CS_ENDPOINT)\n",
                descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (EP_GENERAL)\n",
                descriptor->descriptor_subtype);
        printf("                    Attributes ........ 0x%02x ",
                descriptor->attributes);

        const char* attributes[] = {
                "Sampling Frequency",
                "Pitch",
                "", "", "", "", "",
                "Max Packet Only"
        };

        uint8 mask = 1;
        for (uint8 i = 0; i < sizeof(attributes) / sizeof(attributes[0]); i++) {
                if ((descriptor->attributes & mask) != 0)
                        printf("%s ", attributes[i]);
                mask <<= 1;
        }
        printf("\n");

        const char* aUnits[] = {
                "Undefined",
                "Milliseconds",
                "Decoded PCM samples",
                "Unknown (%u)"
        };

        const char* units = descriptor->lock_delay_units >= 4
                ? aUnits[3] : aUnits[descriptor->lock_delay_units];

        printf("                    Lock Delay Units .. %u (%s)\n",
                descriptor->lock_delay_units, units);
        printf("                    Lock Delay ........ %u\n",
                descriptor->lock_delay);
}


void
DumpMidiInterfaceHeaderDescriptor(
        const usb_midi_interface_header_descriptor* descriptor)
{
        printf("                    Type .............. 0x%02x (CS_ENDPOINT)\n",
                descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (MS_HEADER)\n",
                descriptor->descriptor_subtype);
        printf("                    MSC Version ....... 0x%04x\n",
                descriptor->ms_version);
        printf("                    Length ............ 0x%04x\n",
                descriptor->total_length);
}


void
DumpMidiInJackDescriptor(
        const usb_midi_in_jack_descriptor* descriptor)
{
        printf("                    Type .............. 0x%02x (CS_INTERFACE)\n",
                descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (MIDI_IN_JACK)\n",
                descriptor->descriptor_subtype);
        printf("                    Jack ID ........... 0x%02x\n",
                descriptor->id);
        // TODO can we get the string?
        printf("                    String ............ 0x%02x\n",
                descriptor->string_descriptor);

        switch (descriptor->type) {
                case USB_MIDI_EMBEDDED_JACK:
                        printf("                    Jack Type ......... Embedded\n");
                        break;
                case USB_MIDI_EXTERNAL_JACK:
                        printf("                    Jack Type ......... External\n");
                        break;
                default:
                        printf("                    Jack Type ......... 0x%02x (unknown)\n",
                                descriptor->type);
                        break;
        }
}


void
DumpMidiOutJackDescriptor(
        const usb_midi_out_jack_descriptor* descriptor)
{
        printf("                    Type .............. 0x%02x (CS_INTERFACE)\n",
                descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (MIDI_OUT_JACK)\n",
                descriptor->descriptor_subtype);
        printf("                    Jack ID ........... 0x%02x\n",
                descriptor->id);

        switch (descriptor->type) {
                case USB_MIDI_EMBEDDED_JACK:
                        printf("                    Jack Type ......... Embedded\n");
                        break;
                case USB_MIDI_EXTERNAL_JACK:
                        printf("                    Jack Type ......... External\n");
                        break;
                default:
                        printf("                    Jack Type ......... 0x%02x (unknown)\n",
                                descriptor->type);
                        break;
        }

        for (int i = 0; i < descriptor->inputs_count; i++) {
                printf("                    Pin %02d ............ (%d,%d)\n", i,
                        descriptor->input_source[i].source_id,
                        descriptor->input_source[i].source_pin);
        }
}


void
DumpMidiStreamCSInterfaceDescriptor(const usb_generic_descriptor* descriptor)
{
        uint8 subtype = descriptor->data[0];
        switch (subtype) {
                case USB_MS_HEADER_DESCRIPTOR:
                        DumpMidiInterfaceHeaderDescriptor(
                                (usb_midi_interface_header_descriptor*)descriptor);
                        break;
                case USB_MS_MIDI_IN_JACK_DESCRIPTOR:
                        DumpMidiInJackDescriptor(
                                (usb_midi_in_jack_descriptor*)descriptor);
                        break;
                case USB_MS_MIDI_OUT_JACK_DESCRIPTOR:
                        DumpMidiOutJackDescriptor(
                                (usb_midi_out_jack_descriptor*)descriptor);
                        break;
                case USB_MS_ELEMENT_DESCRIPTOR:
                        // TODO
                        DumpDescriptorData(descriptor);
                        break;
                default:
                        DumpDescriptorData(descriptor);
                        break;
        }
}


void
DumpMidiStreamCSEndpointDescriptor(
        const usb_midi_endpoint_descriptor* descriptor)
{
        printf("                    Type .............. 0x%02x (CS_ENDPOINT)\n",
                descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (MS_GENERAL)\n",
                descriptor->descriptor_subtype);
        printf("                    Jacks ............. ");

        for (int i = 0; i < descriptor->jacks_count; i++)
                printf("%d, ", descriptor->jacks_id[i]);

        printf("\n");
}


void
DumpAudioStreamInterfaceDescriptor(const usb_interface_descriptor* descriptor)
{
        printf("                    Type .............. %u (INTERFACE)\n",
                descriptor->descriptor_type);
        printf("                    Interface ........... %u\n",
                descriptor->interface_number);
        printf("                    Alternate setting ... %u\n",
                descriptor->alternate_setting);
        printf("                    Endpoints ........... %u\n",
                descriptor->num_endpoints);
        printf("                    Interface class ..... %u (AUDIO)\n",
                descriptor->interface_class);
        printf("                    Interface subclass .. %u (AUDIO_STREAMING)\n",
                descriptor->interface_subclass);
        printf("                    Interface ........... %u\n",
                descriptor->interface);
}


void
DumpAudioDescriptor(const usb_generic_descriptor* descriptor, int subclass)
{
        const uint8 USB_AUDIO_INTERFACE = 0x04;

        switch (subclass) {
                case USB_AUDIO_INTERFACE_AUDIOCONTROL_SUBCLASS:
                        switch (descriptor->descriptor_type) {
                                case USB_AUDIO_CS_INTERFACE:
                                        DumpAudioControlCSInterfaceDescriptor(descriptor);
                                        break;
                                default:
                                        DumpDescriptorData(descriptor);
                                        break;
                        }
                        break;
                case USB_AUDIO_INTERFACE_AUDIOSTREAMING_SUBCLASS:
                        switch (descriptor->descriptor_type) {
                                case USB_AUDIO_INTERFACE:
                                        DumpAudioStreamInterfaceDescriptor(
                                                (const usb_interface_descriptor*)descriptor);
                                        break;
                                case USB_AUDIO_CS_INTERFACE:
                                        DumpAudioStreamCSInterfaceDescriptor(descriptor);
                                        break;
                                case USB_AUDIO_CS_ENDPOINT:
                                        DumpAudioStreamCSEndpointDescriptor(
                                                (const usb_audio_streaming_endpoint_descriptor*)descriptor);
                                        break;
                                default:
                                        DumpDescriptorData(descriptor);
                                        break;
                        }
                        break;
                case USB_AUDIO_INTERFACE_MIDISTREAMING_SUBCLASS:
                        switch (descriptor->descriptor_type) {
                                case USB_AUDIO_CS_INTERFACE:
                                        DumpMidiStreamCSInterfaceDescriptor(descriptor);
                                        break;
                                case USB_AUDIO_CS_ENDPOINT:
                                        DumpMidiStreamCSEndpointDescriptor(
                                                (const usb_midi_endpoint_descriptor*)descriptor);
                                        break;
                                default:
                                        DumpDescriptorData(descriptor);
                                        break;
                        }
                        break;
                default:
                        DumpDescriptorData(descriptor);
                        break;
        }
}