root/src/add-ons/kernel/drivers/audio/usb/AudioControlInterface.cpp
/*
 *      Driver for USB Audio Device Class devices.
 *      Copyright (c) 2009-13 S.Zharski <imker@gmx.li>
 *      Distributed under the tems of the MIT license.
 *
 */

#include "AudioControlInterface.h"

#include <usb/USB_audio.h>

#include "Device.h"
#include "Driver.h"
#include "Settings.h"


// control id is encoded in following way
//      CS      CN      ID      IF where:
//      CS                              - control selector
//              CN                      - channel
//                      ID              - id of this feature unit
//                              IF      - interface

#define CTL_ID(_CS, _CN, _ID, _IF) \
        (((_CS) << 24) | ((_CN) << 16) | ((_ID) << 8) | (_IF))

#define CS_FROM_CTLID(_ID) (0xff & ((_ID) >> 24))
#define CN_FROM_CTLID(_ID) (0xff & ((_ID) >> 16))
#define ID_FROM_CTLID(_ID) (0xff & ((_ID) >> 8))

#define REQ_VALUE(_ID) (0xffff & ((_ID) >> 16))
#define REQ_INDEX(_ID) (0xffff & (_ID))


struct _Designation {
        uint32 ch;
        uint32 bus;
} gDesignations[AudioControlInterface::kChannels] = {
        { B_CHANNEL_LEFT,                               B_CHANNEL_STEREO_BUS    },
        { B_CHANNEL_RIGHT,                              B_CHANNEL_STEREO_BUS    },
        { B_CHANNEL_CENTER,                             B_CHANNEL_SURROUND_BUS  },
        { B_CHANNEL_SUB,                                B_CHANNEL_SURROUND_BUS  },
        { B_CHANNEL_REARLEFT,                   B_CHANNEL_STEREO_BUS    },
        { B_CHANNEL_REARRIGHT,                  B_CHANNEL_STEREO_BUS    },
        { B_CHANNEL_FRONT_LEFT_CENTER,  B_CHANNEL_STEREO_BUS    },
        { B_CHANNEL_FRONT_RIGHT_CENTER, B_CHANNEL_STEREO_BUS    },
        { B_CHANNEL_BACK_CENTER,                B_CHANNEL_SURROUND_BUS  },
        { B_CHANNEL_SIDE_LEFT,                  B_CHANNEL_STEREO_BUS    },
        { B_CHANNEL_SIDE_RIGHT,                 B_CHANNEL_STEREO_BUS    },
        { B_CHANNEL_TOP_CENTER,                 B_CHANNEL_SURROUND_BUS  },
        { B_CHANNEL_TOP_FRONT_LEFT,             B_CHANNEL_STEREO_BUS    },
        { B_CHANNEL_TOP_FRONT_CENTER,   B_CHANNEL_SURROUND_BUS  },
        { B_CHANNEL_TOP_FRONT_RIGHT,    B_CHANNEL_STEREO_BUS    },
        { B_CHANNEL_TOP_BACK_LEFT,              B_CHANNEL_STEREO_BUS    },
        { B_CHANNEL_TOP_BACK_CENTER,    B_CHANNEL_SURROUND_BUS  },
        { B_CHANNEL_TOP_BACK_RIGHT,             B_CHANNEL_STEREO_BUS    }
};


struct _MixPageCollector : public Vector<multi_mix_control> {
        _MixPageCollector(const char* pageName) {
                multi_mix_control page;
                memset(&page, 0, sizeof(multi_mix_control));
                page.flags = B_MULTI_MIX_GROUP;
                strlcpy(page.name, pageName, sizeof(page.name));
                PushBack(page);
        }
};


_AudioControl::_AudioControl(AudioControlInterface*     interface,
                usb_audiocontrol_header_descriptor* Header)
        :
        fStatus(B_NO_INIT),
        fInterface(interface),
        fSubType(Header->descriptor_subtype),
        fID(0),
        fSourceID(0),
        fStringIndex(0)
{
}


_AudioControl::~_AudioControl()
{
}


AudioChannelCluster*
_AudioControl::OutCluster()
{
        if (SourceID() == 0 || fInterface == NULL)
                return NULL;

        _AudioControl* control = fInterface->Find(SourceID());
        if (control == NULL)
                return NULL;

        return control->OutCluster();
}


AudioChannelCluster::AudioChannelCluster()
        :
        fOutChannelsNumber(0),
        fChannelsConfig(0),
        fChannelNames(0)
{
}


AudioChannelCluster::~AudioChannelCluster()
{
}


bool
AudioChannelCluster::HasChannel(uint32 location)
{
        return (fChannelsConfig & location) == location;
}


_Terminal::_Terminal(AudioControlInterface*     interface,
        usb_audiocontrol_header_descriptor* Header)
        :
        _AudioControl(interface, Header),
        fTerminalType(0),
        fAssociatedTerminal(0),
        fClockSourceId(0),
        fControlsBitmap(0)
{
}


_Terminal::~_Terminal()
{
}


const char*
_Terminal::Name()
{
        return _GetTerminalDescription(fTerminalType);
}


bool
_Terminal::IsUSBIO()
{
        return (fTerminalType & 0xff00) == USB_AUDIO_UNDEFINED_USB_IO;
}


const char*
_Terminal::_GetTerminalDescription(uint16 TerminalType)
{
        static struct _pair {
                uint16 type;
                const char* description;
        } termInfoPairs[] = {
                // USB Terminal Types
                { USB_AUDIO_UNDEFINED_USB_IO,           "USB I/O" },
                { USB_AUDIO_STREAMING_USB_IO,           "USB I/O" },
                { USB_AUDIO_VENDOR_USB_IO,                      "Vendor USB I/O" },
                // Input Terminal Types
                { USB_AUDIO_UNDEFINED_IN,                       "Undefined Input" },
                { USB_AUDIO_MICROPHONE_IN,                      "Microphone" },
                { USB_AUDIO_DESKTOPMIC_IN,                      "Desktop Microphone" },
                { USB_AUDIO_PERSONALMIC_IN,                     "Personal Microphone" },
                { USB_AUDIO_OMNI_MIC_IN,                        "Omni-directional Mic" },
                { USB_AUDIO_MICS_ARRAY_IN,                      "Microphone Array" },
                { USB_AUDIO_PROC_MICS_ARRAY_IN,         "Processing Mic Array" },
                // Output Terminal Types
                { USB_AUDIO_UNDEFINED_OUT,                      "Undefined Output" },
                { USB_AUDIO_SPEAKER_OUT,                        "Speaker" },
                { USB_AUDIO_HEAD_PHONES_OUT,            "Headphones" },
                { USB_AUDIO_HMD_AUDIO_OUT,                      "Head Mounted Disp.Audio" },
                { USB_AUDIO_DESKTOP_SPEAKER,            "Desktop Speaker" },
                { USB_AUDIO_ROOM_SPEAKER,                       "Room Speaker" },
                { USB_AUDIO_COMM_SPEAKER,                       "Communication Speaker" },
                { USB_AUDIO_LFE_SPEAKER,                        "LFE Speaker" },
                // Bi-directional Terminal Types
                { USB_AUDIO_UNDEFINED_IO,                       "Undefined I/O" },
                { USB_AUDIO_HANDSET_IO,                         "Handset" },
                { USB_AUDIO_HEADSET_IO,                         "Headset" },
                { USB_AUDIO_SPEAKER_PHONE_IO,           "Speakerphone" },
                { USB_AUDIO_SPEAKER_PHONEES_IO,         "Echo-supp Speakerphone" },
                { USB_AUDIO_SPEAKER_PHONEEC_IO,         "Echo-cancel Speakerphone" },
                // Telephony Terminal Types
                { USB_AUDIO_UNDEF_TELEPHONY_IO,         "Undefined Telephony" },
                { USB_AUDIO_PHONE_LINE_IO,                      "Phone Line" },
                { USB_AUDIO_TELEPHONE_IO,                       "Telephone" },
                { USB_AUDIO_DOWNLINE_PHONE_IO,          "Down Line Phone" },
                // External Terminal Types
                { USB_AUDIO_UNDEFINEDEXT_IO,            "Undefined External I/O" },
                { USB_AUDIO_ANALOG_CONNECTOR_IO,        "Analog Connector" },
                { USB_AUDIO_DAINTERFACE_IO,                     "Digital Audio Interface" },
                { USB_AUDIO_LINE_CONNECTOR_IO,          "Line Connector" },
                { USB_AUDIO_LEGACY_CONNECTOR_IO,        "LegacyAudioConnector" },
                { USB_AUDIO_SPDIF_INTERFACE_IO,         "S/PDIF Interface" },
                { USB_AUDIO_DA1394_STREAM_IO,           "1394 DA Stream" },
                { USB_AUDIO_DV1394_STREAMSOUND_IO,      "1394 DV Stream Soundtrack" },
                { USB_AUDIO_ADAT_LIGHTPIPE_IO,          "Alesis DAT Stream" },
                { USB_AUDIO_TDIF_IO,                            "Tascam Digital Interface" },
                { USB_AUDIO_MADI_IO,                            "AES Multi-channel interface" },
                // Embedded Terminal Types
                { USB_AUDIO_UNDEF_EMBEDDED_IO,          "Undefined Embedded I/O" },
                { USB_AUDIO_LC_NOISE_SOURCE_OUT,        "Level Calibration Noise Source" },
                { USB_AUDIO_EQUALIZATION_NOISE_OUT,     "Equalization Noise" },
                { USB_AUDIO_CDPLAYER_IN,                        "CD Player" },
                { USB_AUDIO_DAT_IO,                                     "DAT" },
                { USB_AUDIO_DCC_IO,                                     "DCC" },
                { USB_AUDIO_MINI_DISK_IO,                       "Mini Disk" },
                { USB_AUDIO_ANALOG_TAPE_IO,                     "Analog Tape" },
                { USB_AUDIO_PHONOGRAPH_IN,                      "Phonograph" },
                { USB_AUDIO_VCR_AUDIO_IN,                       "VCR Audio" },
                { USB_AUDIO_VIDEO_DISC_AUDIO_IN,        "Video Disc Audio" },
                { USB_AUDIO_DVD_AUDIO_IN,                       "DVD Audio" },
                { USB_AUDIO_TV_TUNER_AUDIO_IN,          "TV Tuner Audio" },
                { USB_AUDIO_SAT_RECEIVER_AUDIO_IN,      "Satellite Receiver Audio" },
                { USB_AUDIO_CABLE_TUNER_AUDIO_IN,       "Cable Tuner Audio" },
                { USB_AUDIO_DSS_AUDIO_IN,                       "DSS Audio" },
                { USB_AUDIO_RADIO_RECEIVER_IN,          "Radio Receiver" },
                { USB_AUDIO_RADIO_TRANSMITTER_IN,       "Radio Transmitter" },
                { USB_AUDIO_MULTI_TRACK_RECORDER_IO,"Multi-track Recorder" },
                { USB_AUDIO_SYNTHESIZER_IO,                     "Synthesizer" },
                { USB_AUDIO_PIANO_IO,                           "Piano" },
                { USB_AUDIO_GUITAR_IO,                          "Guitar" },
                { USB_AUDIO_DRUMS_IO,                           "Percussion Instrument" },
                { USB_AUDIO_INSTRUMENT_IO,                      "Musical Instrument" }
        };

        for (size_t i = 0; i < B_COUNT_OF(termInfoPairs); i++)
                if (termInfoPairs[i].type == TerminalType)
                        return termInfoPairs[i].description;

        TRACE(ERR, "Unknown Terminal Type: %#06x", TerminalType);
        return "Unknown";
}


InputTerminal::InputTerminal(AudioControlInterface*     interface,
                usb_audiocontrol_header_descriptor* Header)
        :
        _AudioChannelCluster<_Terminal>(interface, Header)
{
        usb_audio_input_terminal_descriptor* Terminal
                = (usb_audio_input_terminal_descriptor*) Header;
        fID                                     = Terminal->terminal_id;
        fTerminalType           = Terminal->terminal_type;
        fAssociatedTerminal = Terminal->assoc_terminal;

        TRACE(UAC, "Input Terminal ID:%d >>>\n", fID);
        TRACE(UAC, "Terminal type:%s (%#06x)\n",
                _GetTerminalDescription(fTerminalType), fTerminalType);
        TRACE(UAC, "Assoc.terminal:%d\n",       fAssociatedTerminal);

        if (fInterface->SpecReleaseNumber() < 0x200) {
                fOutChannelsNumber      = Terminal->r1.num_channels;
                fChannelsConfig         = Terminal->r1.channel_config;
                fChannelNames           = Terminal->r1.channel_names;
                fStringIndex            = Terminal->r1.terminal;
        } else {
                fClockSourceId          = Terminal->r2.clock_source_id;
                fOutChannelsNumber      = Terminal->r2.num_channels;
                fChannelsConfig         = Terminal->r2.channel_config;
                fChannelNames           = Terminal->r2.channel_names;
                fControlsBitmap         = Terminal->r2.bm_controls;
                fStringIndex            = Terminal->r2.terminal;

                TRACE(UAC, "Clock Source ID:%d\n", fClockSourceId);
                TRACE(UAC, "Controls Bitmap:%#04x\n", fControlsBitmap);
        }

        TRACE(UAC, "Out.channels num:%d\n",      fOutChannelsNumber);
        TRACE(UAC, "Channels config:%#06x\n", fChannelsConfig);
        TRACE(UAC, "Channels names:%d\n",        fChannelNames);
        TRACE(UAC, "StringIndex:%d\n",           fStringIndex);

        fStatus = B_OK;
}


InputTerminal::~InputTerminal()
{
}


const char*
InputTerminal::Name()
{
        if (fTerminalType == USB_AUDIO_STREAMING_USB_IO)
                return "USB Input";
        return _Terminal::Name();
}


OutputTerminal::OutputTerminal(AudioControlInterface*   interface,
                usb_audiocontrol_header_descriptor* Header)
        :
        _Terminal(interface, Header)
{
        usb_audio_output_terminal_descriptor* Terminal
                = (usb_audio_output_terminal_descriptor*) Header;

        fID                                     = Terminal->terminal_id;
        fTerminalType           = Terminal->terminal_type;
        fAssociatedTerminal     = Terminal->assoc_terminal;
        fSourceID                       = Terminal->source_id;

        TRACE(UAC, "Output Terminal ID:%d >>>\n",       fID);
        TRACE(UAC, "Terminal type:%s (%#06x)\n",
                _GetTerminalDescription(fTerminalType), fTerminalType);
        TRACE(UAC, "Assoc.terminal:%d\n",               fAssociatedTerminal);
        TRACE(UAC, "Source ID:%d\n",                            fSourceID);

        if (fInterface->SpecReleaseNumber() < 0x200) {
                fStringIndex = Terminal->r1.terminal;
        } else {
                fClockSourceId  = Terminal->r2.clock_source_id;
                fControlsBitmap = Terminal->r2.bm_controls;
                fStringIndex    = Terminal->r2.terminal;

                TRACE(UAC, "Clock Source ID:%d\n", fClockSourceId);
                TRACE(UAC, "Controls Bitmap:%#04x\n", fControlsBitmap);
        }

        TRACE(UAC, "StringIndex:%d\n", fStringIndex);

        fStatus = B_OK;
}


OutputTerminal::~OutputTerminal()
{
}


const char*
OutputTerminal::Name()
{
        if (fTerminalType == USB_AUDIO_STREAMING_USB_IO)
                return "USB Output";
        return _Terminal::Name();
}


MixerUnit::MixerUnit(AudioControlInterface*     interface,
                usb_audiocontrol_header_descriptor* Header)
        :
        _AudioChannelCluster<_AudioControl>(interface, Header),
        fBmControlsR2(0)
{
        usb_audio_mixer_unit_descriptor* Mixer
                = (usb_audio_mixer_unit_descriptor*) Header;

        fID = Mixer->unit_id;
        TRACE(UAC, "Mixer ID:%d >>>\n", fID);

        TRACE(UAC, "Number of input pins:%d\n", Mixer->num_input_pins);
        for (size_t i = 0; i < Mixer->num_input_pins; i++) {
                fInputPins.PushBack(Mixer->input_pins[i]);
                TRACE(UAC, "Input pin #%d:%d\n", i, fInputPins[i]);
        }

        uint8* mixerControlsData = NULL;
        uint8 mixerControlsSize = 0;

        if (fInterface->SpecReleaseNumber() < 0x200) {
                usb_audio_output_channels_descriptor_r1* OutChannels
                        = (usb_audio_output_channels_descriptor_r1*)
                        &Mixer->input_pins[Mixer->num_input_pins];

                fOutChannelsNumber      = OutChannels->num_output_pins;
                fChannelsConfig         = OutChannels->channel_config;
                fChannelNames           = OutChannels->channel_names;

                mixerControlsData = (uint8*) ++OutChannels;
                mixerControlsSize = Mixer->length - 10 - Mixer->num_input_pins;
                fStringIndex = *(mixerControlsData + mixerControlsSize);

#if 0 // TEST
                if (fOutChannelsNumber > 2) {
                //      fOutChannelsNumber = 2;
                //      fChannelsConfig = 0x03;
                        mixerControlsData[0] = 0x80;
                        mixerControlsData[1] = 0x40;
                        mixerControlsData[2] = 0x20;
                        mixerControlsData[3] = 0x10;
                        mixerControlsData[4] = 0x08;
                        mixerControlsData[5] = 0x04;
                        mixerControlsData[6] = 0x02;
                        mixerControlsData[7] = 0x01;
                        mixerControlsData[8] = 0x80;
                        mixerControlsData[9] = 0x40;
                        mixerControlsData[10] = 0x02;
                        mixerControlsData[11] = 0x01;
                }
#endif

        } else {
                usb_audio_output_channels_descriptor* OutChannels
                        = (usb_audio_output_channels_descriptor*)
                        &Mixer->input_pins[Mixer->num_input_pins];

                fOutChannelsNumber      = OutChannels->num_output_pins;
                fChannelsConfig         = OutChannels->channel_config;
                fChannelNames           = OutChannels->channel_names;

                mixerControlsData = (uint8*) ++OutChannels;
                mixerControlsSize = Mixer->length - 13 - Mixer->num_input_pins;
                fBmControlsR2 = *(mixerControlsData + mixerControlsSize);
                fStringIndex = *(mixerControlsData + mixerControlsSize + 1);

                TRACE(UAC, "Control Bitmap:%#04x\n", fBmControlsR2);
        }

        TRACE(UAC, "Out channels number:%d\n",          fOutChannelsNumber);
        TRACE(UAC, "Out channels config:%#06x\n",       fChannelsConfig);
        TRACE(UAC, "Out channels names:%d\n",           fChannelNames);
        TRACE(UAC, "Controls Size:%d\n", mixerControlsSize);

        for (size_t i = 0; i < mixerControlsSize; i++) {
                fControlsBitmap.PushBack(mixerControlsData[i]);
                TRACE(UAC, "Controls Data[%d]:%#x\n", i, fControlsBitmap[i]);
        }

        TRACE(UAC, "StringIndex:%d\n", fStringIndex);

        fStatus = B_OK;
}


MixerUnit::~MixerUnit()
{
}


bool
MixerUnit::HasProgrammableControls()
{
        for (int i = 0; i < fControlsBitmap.Count(); i++)
                if (fControlsBitmap[i] != 0)
                        return true;
        return false;
}


bool
MixerUnit::IsControlProgrammable(int inChannel, int outChannel)
{
        AudioChannelCluster* outCluster = OutCluster();
        if (outCluster == NULL) {
                TRACE(ERR, "Output cluster is not valid.\n");
                return false;
        }

        bool result = false;
        if (outChannel < outCluster->ChannelsCount()) {
                int index = inChannel * outCluster->ChannelsCount()+ outChannel;
                result = (fControlsBitmap[index >> 3] & (0x80 >> (index & 7))) != 0;
        }

//      TRACE(UAC, "in:%d out:%d is %s\n",
//              inChannel, outChannel, result ? "on" : "off");
        return result;
}


SelectorUnit::SelectorUnit(AudioControlInterface*       interface,
                usb_audiocontrol_header_descriptor* Header)
        :
        _AudioControl(interface, Header),
        fControlsBitmap(0)
{
        usb_audio_selector_unit_descriptor* Selector
                = (usb_audio_selector_unit_descriptor*) Header;

        fID = Selector->unit_id;
        TRACE(UAC, "Selector ID:%d >>>\n", fID);

        TRACE(UAC, "Number of input pins:%d\n", Selector->num_input_pins);
        for (size_t i = 0; i < Selector->num_input_pins; i++) {
                fInputPins.PushBack(Selector->input_pins[i]);
                TRACE(UAC, "Input pin #%d:%d\n", i, fInputPins[i]);
        }

        if (fInterface->SpecReleaseNumber() < 0x200) {
                fStringIndex = Selector->input_pins[Selector->num_input_pins];
        } else {
                fControlsBitmap = Selector->input_pins[Selector->num_input_pins];
                fStringIndex = Selector->input_pins[Selector->num_input_pins + 1];

                TRACE(UAC, "Controls Bitmap:%d\n", fControlsBitmap);
        }

        TRACE(UAC, "StringIndex:%d\n", fStringIndex);

        fStatus = B_OK;
}


SelectorUnit::~SelectorUnit()
{
}


AudioChannelCluster*
SelectorUnit::OutCluster()
{
        if (fInterface == NULL)
                return NULL;

        for (int i = 0; i < fInputPins.Count(); i++) {
                _AudioControl* control = fInterface->Find(fInputPins[i]);
                if (control == NULL)
                        continue;
                // selector has the same channels number in the
                // out cluster as anyone of his inputs
                if (control->OutCluster() != NULL)
                        return control->OutCluster();
        }

        return NULL;
}


FeatureUnit::FeatureUnit(AudioControlInterface* interface,
                usb_audiocontrol_header_descriptor* Header)
        :
        _AudioControl(interface, Header)
{
        usb_audio_feature_unit_descriptor* Feature
                = (usb_audio_feature_unit_descriptor*) Header;

        fID = Feature->unit_id;
        TRACE(UAC, "Feature ID:%d >>>\n", fID);

        fSourceID = Feature->source_id;
        TRACE(UAC, "Source ID:%d\n", fSourceID);

        uint8 controlSize = 4;
        uint8 channelsCount = (Feature->length - 6) / controlSize;
        uint8* ControlsBitmapPointer = (uint8*)&Feature->r2.bma_controls[0];

        if (fInterface->SpecReleaseNumber() < 0x200) {
                controlSize = Feature->r1.control_size;
                channelsCount = (Feature->length - 7) / Feature->r1.control_size;
                ControlsBitmapPointer = &Feature->r1.bma_controls[0];
        }

        TRACE(UAC, "Channel bitmap size:%d\n", controlSize);
        TRACE(UAC, "Channels number:%d\n", channelsCount - 1); // not add master!

        for (size_t i = 0; i < channelsCount; i++) {
                uint8* controlPointer = &ControlsBitmapPointer[i* controlSize];
                switch(controlSize) {
                        case 1: fControlBitmaps.PushBack(*controlPointer); break;
                        case 2: fControlBitmaps.PushBack(*(uint16*)controlPointer); break;
                        case 4: fControlBitmaps.PushBack(*(uint32*)controlPointer); break;
                        default:
                                TRACE(ERR, "Feature control of unsupported size %d ignored\n",
                                                                                                                controlSize);
                                continue;
                }

                NormalizeAndTraceChannel(i);
        }

        fStringIndex = ControlsBitmapPointer[channelsCount* controlSize];
        TRACE(UAC, "StringIndex:%d\n", fStringIndex);

        fStatus = B_OK;
}


FeatureUnit::~FeatureUnit()
{
}


const char*
FeatureUnit::Name()
{
        // first check if source of this FU is an input terminal
        _AudioControl* control = fInterface->Find(fSourceID);
        while (control != NULL) {
                if (control->SubType() != USB_AUDIO_AC_INPUT_TERMINAL)
                        break;

                // USB I/O terminal is a not good candidate to use it's name
                if (static_cast<_Terminal*>(control)->IsUSBIO())
                        break;

                // use the name of source input terminal as name of this FU
                return control->Name();
        }

        // check if output of this FU is connected to output terminal
        control = fInterface->FindOutputTerminal(fID);
        while (control != NULL) {
                if (control->SubType() != USB_AUDIO_AC_OUTPUT_TERMINAL)
                        break;

                // USB I/O terminal is a not good candidate to use it's name
                if (static_cast<_Terminal*>(control)->IsUSBIO())
                        break;

                // use the name of this output terminal as name of this FU
                return control->Name();
        }

        // otherwise get the generic name of this FU's source
        control = fInterface->Find(fSourceID);
        if (control != NULL && control->Name() != NULL
                        && strlen(control->Name()) > 0)
                return control->Name();

        // I have no more ideas, have you one?
        return "Unknown";
}


bool
FeatureUnit::HasControl(int32 Channel, uint32 Control)
{
        if (Channel >= fControlBitmaps.Count()) {
                TRACE(ERR, "Out of limits error of retrieving control %#010x "
                        "for channel %d\n", Control, Channel);
                return false;
        }

        return (Control & fControlBitmaps[Channel]) != 0;
}


void
FeatureUnit::NormalizeAndTraceChannel(int32 Channel)
{
        if (Channel >= fControlBitmaps.Count()) {
                TRACE(ERR, "Out of limits error of tracing channel %d\n", Channel);
                return;
        }

        struct _RemapInfo {
                uint32  rev1Bits;
                uint32  rev2Bits;
                const char* name;
        } remapInfos[] = {
                { BMA_CTL_MUTE_R1,              BMA_CTL_MUTE,                   "Mute"          },
                { BMA_CTL_VOLUME_R1,    BMA_CTL_VOLUME,                 "Volume"        },
                { BMA_CTL_BASS_R1,              BMA_CTL_BASS,                   "Bass"          },
                { BMA_CTL_MID_R1,               BMA_CTL_MID,                    "Mid"           },
                { BMA_CTL_TREBLE_R1,    BMA_CTL_TREBLE,                 "Treble"        },
                { BMA_CTL_GRAPHEQ_R1,   BMA_CTL_GRAPHEQ,                "Graphic Equalizer"     },
                { BMA_CTL_AUTOGAIN_R1,  BMA_CTL_AUTOGAIN,               "Automatic Gain"},
                { BMA_CTL_DELAY_R1,             BMA_CTL_DELAY,                  "Delay"                 },
                { BMA_CTL_BASSBOOST_R1, BMA_CTL_BASSBOOST,              "Bass Boost"    },
                { BMA_CTL_LOUDNESS_R1,  BMA_CTL_LOUDNESS,               "Loudness"              },
                { 0,                                    BMA_CTL_INPUTGAIN,              "InputGain"             },
                { 0,                                    BMA_CTL_INPUTGAINPAD,   "InputGainPad"  },
                { 0,                                    BMA_CTL_PHASEINVERTER,  "PhaseInverter" },
                { 0,                                    BMA_CTL_UNDERFLOW,              "Underflow"             },
                { 0,                                    BMA_CTL_OVERFLOW,               "Overflow"              }
        };

        if (Channel == 0)
                TRACE(UAC, "Master channel bitmap:%#x\n", fControlBitmaps[Channel]);
        else
                TRACE(UAC, "Channel %d bitmap:%#x\n", Channel, fControlBitmaps[Channel]);

        bool isRev1 = (fInterface->SpecReleaseNumber() < 0x200);

        uint32 remappedBitmap = 0;
        for (size_t i = 0; i < B_COUNT_OF(remapInfos); i++) {
                uint32 bits = isRev1 ? remapInfos[i].rev1Bits : remapInfos[i].rev2Bits;
                if ((fControlBitmaps[Channel] & bits) > 0) {
                        if (isRev1)
                                remappedBitmap |= remapInfos[i].rev2Bits;
                        TRACE(UAC, "\t%s\n", remapInfos[i].name);
                }
        }

        if (isRev1) {
                TRACE(UAC, "\t%#08x -> %#08x.\n",
                        fControlBitmaps[Channel], remappedBitmap);
                fControlBitmaps[Channel] = remappedBitmap;
        }
}


EffectUnit::EffectUnit(AudioControlInterface*   interface,
                usb_audiocontrol_header_descriptor* Header)
        :
        _AudioControl(interface, Header)
{
        usb_audio_input_terminal_descriptor* descriptor
                = (usb_audio_input_terminal_descriptor*) Header;
        TRACE(UAC, "Effect Unit:%d >>>\n", descriptor->terminal_id);
}


EffectUnit::~EffectUnit()
{
}


ProcessingUnit::ProcessingUnit(AudioControlInterface*   interface,
                usb_audiocontrol_header_descriptor* Header)
        :
        _AudioChannelCluster<_AudioControl>(interface, Header),
        fProcessType(0),
        fControlsBitmap(0)
{
        usb_audio_processing_unit_descriptor* Processing
                = (usb_audio_processing_unit_descriptor*) Header;

        fID = Processing->unit_id;
        TRACE(UAC, "Processing ID:%d >>>\n", fID);

        fProcessType = Processing->process_type;
        TRACE(UAC, "Processing Type:%d\n", fProcessType);

        TRACE(UAC, "Number of input pins:%d\n", Processing->num_input_pins);
        for (size_t i = 0; i < Processing->num_input_pins; i++) {
                fInputPins.PushBack(Processing->input_pins[i]);
                TRACE(UAC, "Input pin #%d:%d\n", i, fInputPins[i]);
        }

        if (fInterface->SpecReleaseNumber() < 0x200) {
                usb_audio_output_channels_descriptor_r1* OutChannels
                        = (usb_audio_output_channels_descriptor_r1*)
                        &Processing->input_pins[Processing->num_input_pins];

                fOutChannelsNumber      = OutChannels->num_output_pins;
                fChannelsConfig         = OutChannels->channel_config;
                fChannelNames           = OutChannels->channel_names;
        } else {
                usb_audio_output_channels_descriptor* OutChannels
                        = (usb_audio_output_channels_descriptor*)
                        &Processing->input_pins[Processing->num_input_pins];

                fOutChannelsNumber      = OutChannels->num_output_pins;
                fChannelsConfig         = OutChannels->channel_config;
                fChannelNames           = OutChannels->channel_names;
        }

        TRACE(UAC, "Out channels number:%d\n",          fOutChannelsNumber);
        TRACE(UAC, "Out channels config:%#06x\n",       fChannelsConfig);
        TRACE(UAC, "Out channels names:%d\n",           fChannelNames);
        /*
        uint8 controlsSize = Processing->length - 10 - Processing->num_input_pins;
        TRACE(UAC, "Controls Size:%d\n", controlsSize);

        uint8* controlsData = (uint8*) ++OutChannels;

        for (size_t i = 0; i < controlsSize; i++) {
                fProgrammableControls.PushBack(controlsData[i]);
                TRACE(UAC, "Controls Data[%d]:%#x\n", i, controlsData[i]);
        }

        fStringIndex = *(controlsData + controlsSize);

        TRACE(UAC, "StringIndex:%d\n", fStringIndex);
*/
        fStatus = B_OK;
}


ProcessingUnit::~ProcessingUnit()
{
}


ExtensionUnit::ExtensionUnit(AudioControlInterface*     interface,
                usb_audiocontrol_header_descriptor* Header)
        :
        _AudioChannelCluster<_AudioControl>(interface, Header),
        fExtensionCode(0),
        fControlsBitmap(0)
{
        usb_audio_extension_unit_descriptor* Extension
                = (usb_audio_extension_unit_descriptor*) Header;

        fID = Extension->unit_id;
        TRACE(UAC, "Extension ID:%d >>>\n", fID);

        fExtensionCode = Extension->extension_code;
        TRACE(UAC, "Extension Type:%d\n", fExtensionCode);

        TRACE(UAC, "Number of input pins:%d\n", Extension->num_input_pins);
        for (size_t i = 0; i < Extension->num_input_pins; i++) {
                fInputPins.PushBack(Extension->input_pins[i]);
                TRACE(UAC, "Input pin #%d:%d\n", i, fInputPins[i]);
        }

        if (fInterface->SpecReleaseNumber() < 0x200) {
                usb_audio_output_channels_descriptor_r1* OutChannels
                        = (usb_audio_output_channels_descriptor_r1*)
                        &Extension->input_pins[Extension->num_input_pins];

                fOutChannelsNumber      = OutChannels->num_output_pins;
                fChannelsConfig         = OutChannels->channel_config;
                fChannelNames           = OutChannels->channel_names;
        } else {
                usb_audio_output_channels_descriptor* OutChannels
                        = (usb_audio_output_channels_descriptor*)
                        &Extension->input_pins[Extension->num_input_pins];

                fOutChannelsNumber      = OutChannels->num_output_pins;
                fChannelsConfig         = OutChannels->channel_config;
                fChannelNames           = OutChannels->channel_names;
        }

        TRACE(UAC, "Out channels number:%d\n",          fOutChannelsNumber);
        TRACE(UAC, "Out channels config:%#06x\n",       fChannelsConfig);
        TRACE(UAC, "Out channels names:%d\n",           fChannelNames);
        /*
        uint8 controlsSize = Processing->length - 10 - Processing->num_input_pins;
        TRACE(UAC, "Controls Size:%d\n", controlsSize);

        uint8* controlsData = (uint8*) ++OutChannels;

        for (size_t i = 0; i < controlsSize; i++) {
                fProgrammableControls.PushBack(controlsData[i]);
                TRACE(UAC, "Controls Data[%d]:%#x\n", i, controlsData[i]);
        }

        fStringIndex = *(controlsData + controlsSize);

        TRACE(UAC, "StringIndex:%d\n", fStringIndex);
*/
        fStatus = B_OK;
}


ExtensionUnit::~ExtensionUnit()
{
}


ClockSource::ClockSource(AudioControlInterface* interface,
                usb_audiocontrol_header_descriptor* Header)
        :
        _AudioControl(interface, Header)
{
        usb_audio_input_terminal_descriptor* descriptor
                = (usb_audio_input_terminal_descriptor*) Header;
        TRACE(UAC, "Clock Source:%d >>>\n",     descriptor->terminal_id);
}


ClockSource::~ClockSource()
{
}


ClockSelector::ClockSelector(AudioControlInterface*     interface,
                usb_audiocontrol_header_descriptor* Header)
        :
        _AudioControl(interface, Header)
{
        usb_audio_input_terminal_descriptor* descriptor
                = (usb_audio_input_terminal_descriptor*) Header;
        TRACE(UAC, "Clock Selector:%d >>>\n", descriptor->terminal_id);
}


ClockSelector::~ClockSelector()
{
}


ClockMultiplier::ClockMultiplier(AudioControlInterface* interface,
                usb_audiocontrol_header_descriptor* Header)
        :
        _AudioControl(interface, Header)
{
        usb_audio_input_terminal_descriptor* descriptor
                = (usb_audio_input_terminal_descriptor*) Header;
        TRACE(UAC, "Clock Multiplier:%d >>>\n", descriptor->terminal_id);
}


ClockMultiplier::~ClockMultiplier()
{
}


SampleRateConverter::SampleRateConverter(AudioControlInterface* interface,
                usb_audiocontrol_header_descriptor* Header)
        :
        _AudioControl(interface, Header)
{
        usb_audio_input_terminal_descriptor* descriptor
                = (usb_audio_input_terminal_descriptor*) Header;
        TRACE(UAC, "Sample Rate Converter:%d >>>\n", descriptor->terminal_id);
}


SampleRateConverter::~SampleRateConverter()
{
}


AudioControlInterface::AudioControlInterface(Device* device)
        :
        fInterface(0),
        fStatus(B_NO_INIT),
        fADCSpecification(0),
        fFunctionCategory(0),
        fControlsBitmap(0),
        fDevice(device)
{
}


AudioControlInterface::~AudioControlInterface()
{
        for (AudioControlsIterator I = fAudioControls.Begin();
                        I != fAudioControls.End(); I++)
                delete I->Value();

        fAudioControls.MakeEmpty();

        // object already freed. just purge the map
        fOutputTerminals.MakeEmpty();

        // object already freed. just purge the map
        fInputTerminals.MakeEmpty();
}


status_t
AudioControlInterface::Init(size_t interface, usb_interface_info* Interface)
{
        for (size_t i = 0; i < Interface->generic_count; i++) {
                usb_audiocontrol_header_descriptor* Header
                        = (usb_audiocontrol_header_descriptor* )Interface->generic[i];

                if (Header->descriptor_type != USB_AUDIO_CS_INTERFACE) {
                        TRACE(ERR, "Ignore Audio Control of "
                                "unknown descriptor type %#04x.\n",     Header->descriptor_type);
                        continue;
                }

                _AudioControl* control = NULL;

                switch(Header->descriptor_subtype) {
                        default:
                                TRACE(ERR, "Ignore Audio Control of unknown "
                                        "descriptor sub-type %#04x\n", Header->descriptor_subtype);
                                break;
                        case USB_AUDIO_AC_DESCRIPTOR_UNDEFINED:
                                TRACE(ERR, "Ignore Audio Control of undefined sub-type\n");
                                break;
                        case USB_AUDIO_AC_HEADER:
                                InitACHeader(interface, Header);
                                break;
                        case USB_AUDIO_AC_INPUT_TERMINAL:
                                control = new(std::nothrow) InputTerminal(this, Header);
                                break;
                        case USB_AUDIO_AC_OUTPUT_TERMINAL:
                                control = new(std::nothrow) OutputTerminal(this, Header);
                                break;
                        case USB_AUDIO_AC_MIXER_UNIT:
                                control = new(std::nothrow) MixerUnit(this, Header);
                                break;
                        case USB_AUDIO_AC_SELECTOR_UNIT:
                                control = new(std::nothrow) SelectorUnit(this, Header);
                                break;
                        case USB_AUDIO_AC_FEATURE_UNIT:
                                control = new(std::nothrow) FeatureUnit(this, Header);
                                break;
                        case USB_AUDIO_AC_PROCESSING_UNIT:
                                if (SpecReleaseNumber() < 200)
                                        control = new(std::nothrow) ProcessingUnit(this, Header);
                                else
                                        control = new(std::nothrow) EffectUnit(this, Header);
                                break;
                        case USB_AUDIO_AC_EXTENSION_UNIT:
                                if (SpecReleaseNumber() < 200)
                                        control = new(std::nothrow) ExtensionUnit(this, Header);
                                else
                                        control = new(std::nothrow) ProcessingUnit(this, Header);
                                break;
                        case USB_AUDIO_AC_EXTENSION_UNIT_R2:
                                control = new(std::nothrow) ExtensionUnit(this, Header);
                                break;
                        case USB_AUDIO_AC_CLOCK_SOURCE_R2:
                                control = new(std::nothrow) ClockSource(this, Header);
                                break;
                        case USB_AUDIO_AC_CLOCK_SELECTOR_R2:
                                control = new(std::nothrow) ClockSelector(this, Header);
                                break;
                        case USB_AUDIO_AC_CLOCK_MULTIPLIER_R2:
                                control = new(std::nothrow) ClockMultiplier(this, Header);
                                break;
                        case USB_AUDIO_AC_SAMPLE_RATE_CONVERTER_R2:
                                control = new(std::nothrow) SampleRateConverter(this, Header);
                                break;
                }

                if (control != 0 && control->InitCheck() == B_OK) {
                        switch(control->SubType()) {
                                case USB_AUDIO_AC_OUTPUT_TERMINAL:
                                        fOutputTerminals.Put(control->SourceID(), control);
                                        break;
                                case USB_AUDIO_AC_INPUT_TERMINAL:
                                        fInputTerminals.Put(control->ID(), control);
                                        break;
                        }
                        fAudioControls.Put(control->ID(), control);

                } else
                        delete control;
        }

        return fStatus = B_OK;
}


_AudioControl*
AudioControlInterface::Find(uint8 id)
{
        return fAudioControls.Get(id);
}


_AudioControl*
AudioControlInterface::FindOutputTerminal(uint8 id)
{
        return fOutputTerminals.Get(id);
}


status_t
AudioControlInterface::InitACHeader(size_t interface,
                usb_audiocontrol_header_descriptor* Header)
{
        if (Header == NULL)
                return fStatus = B_NO_INIT;

        fInterface = interface;

        fADCSpecification = Header->bcd_release_no;
        TRACE(UAC, "ADCSpecification:%#06x\n", fADCSpecification);

        if (fADCSpecification < 0x200) {
                TRACE(UAC, "InterfacesCount:%d\n",      Header->r1.in_collection);
                for (size_t i = 0; i < Header->r1.in_collection; i++) {
                        fStreams.PushBack(Header->r1.interface_numbers[i]);
                        TRACE(UAC, "Interface[%d] number is %d\n", i, fStreams[i]);
                }
        } else {
                fFunctionCategory = Header->r2.function_category;
                fControlsBitmap = Header->r2.bm_controls;
                TRACE(UAC, "Function Category:%#04x\n", fFunctionCategory);
                TRACE(UAC, "Controls Bitmap:%#04x\n", fControlsBitmap);
        }

        return B_OK;
}


uint32
AudioControlInterface::GetChannelsDescription(
        Vector<multi_channel_info>& Channels, multi_description* Description,
        Vector<_AudioControl*>& Terminals, bool isForInput)
{
        uint32 addedChannels = 0;

        for (int32 i = 0; i < Terminals.Count(); i++) {
                AudioChannelCluster* cluster = Terminals[i]->OutCluster();
                if (cluster == NULL || cluster->ChannelsCount() <= 0) {
                        TRACE(ERR, "Terminal #%d ignored due null "
                                "channels cluster (%08x)\n", Terminals[i]->ID(), cluster);
                        continue;
                }

                uint32 channels = GetTerminalChannels(Channels, cluster,
                        isForInput ? B_MULTI_INPUT_CHANNEL : B_MULTI_OUTPUT_CHANNEL);

                if (isForInput)
                        Description->input_channel_count += channels;
                else
                        Description->output_channel_count += channels;

                addedChannels += channels;
        }

        return addedChannels;
}


uint32
AudioControlInterface::GetTerminalChannels(Vector<multi_channel_info>& Channels,
                AudioChannelCluster* cluster, channel_kind kind, uint32 connectors)
{
        if (cluster->ChannelsCount() < 2) { // mono channel
                multi_channel_info info;
                info.channel_id = Channels.Count();
                info.kind               = kind;
                info.designations= B_CHANNEL_MONO_BUS;
                info.connectors = connectors;
                Channels.PushBack(info);

                return 1;
        }

        uint32 startCount = Channels.Count();

        // Haiku multi-aduio designations have the same bits
        // as USB Audio 2.0 cluster spatial locations :-)
        for (size_t i = 0; i < kChannels; i++) {
                uint32 designation = 1 << i;
                if ((cluster->ChannelsConfig() & designation) == designation) {
                        multi_channel_info info;
                        info.channel_id = Channels.Count();
                        info.kind               = kind;
                        info.designations= gDesignations[i].ch | gDesignations[i].bus;
                        info.connectors = connectors;
                        Channels.PushBack(info);
                }
        }

        return Channels.Count() - startCount;
}


uint32
AudioControlInterface::GetBusChannelsDescription(
        Vector<multi_channel_info>& Channels, multi_description* Description)
{
        uint32 addedChannels = 0;

        // first iterate output channels
        for (AudioControlsIterator I = fOutputTerminals.Begin();
                        I != fOutputTerminals.End(); I++) {
                _AudioControl* control = I->Value();
                if (static_cast<_Terminal*>(control)->IsUSBIO())
                        continue;

                AudioChannelCluster* cluster = control->OutCluster();
                if (cluster == 0 || cluster->ChannelsCount() <= 0) {
                        TRACE(ERR, "Terminal #%d ignored due null "
                                "channels cluster (%08x)\n", control->ID(), cluster);
                        continue;
                }

                uint32 channels = GetTerminalChannels(Channels,
                        cluster, B_MULTI_OUTPUT_BUS);

                Description->output_bus_channel_count += channels;
                addedChannels += channels;
        }

        // input channels should follow too
        for (AudioControlsIterator I = fInputTerminals.Begin();
                        I != fInputTerminals.End(); I++) {
                _AudioControl* control = I->Value();
                if (static_cast<_Terminal*>(control)->IsUSBIO())
                        continue;

                AudioChannelCluster* cluster = control->OutCluster();
                if (cluster == NULL || cluster->ChannelsCount() <= 0) {
                        TRACE(ERR, "Terminal #%d ignored due null "
                                "channels cluster (%08x)\n", control->ID(), cluster);
                        continue;
                }

                uint32 channels = GetTerminalChannels(Channels,
                        cluster, B_MULTI_INPUT_BUS);

                Description->input_bus_channel_count += channels;
                addedChannels += channels;
        }

        return addedChannels;
}


void
AudioControlInterface::_HarvestRecordFeatureUnits(_AudioControl* rootControl,
                AudioControlsMap& Map)
{
        if (rootControl == 0) {
                TRACE(ERR, "Not processing due NULL root control.\n");
                return;
        }

        switch(rootControl->SubType()) {
                case USB_AUDIO_AC_SELECTOR_UNIT:
                        {
                                SelectorUnit* unit = static_cast<SelectorUnit*>(rootControl);
                                for (int i = 0; i < unit->fInputPins.Count(); i++)
                                        _HarvestRecordFeatureUnits(Find(unit->fInputPins[i]), Map);
                                Map.Put(rootControl->ID(), rootControl);
                        }
                        break;

                case USB_AUDIO_AC_FEATURE_UNIT:
                        Map.Put(rootControl->ID(), rootControl);
                        break;
        }
}


void
AudioControlInterface::_HarvestOutputFeatureUnits(_AudioControl* rootControl,
                AudioControlsMap& Map)
{
        if (rootControl == 0) {
                TRACE(ERR, "Not processing due NULL root control.\n");
                return;
        }

        switch(rootControl->SubType()) {
                case USB_AUDIO_AC_MIXER_UNIT:
                        {
                                MixerUnit* unit = static_cast<MixerUnit*>(rootControl);
                                for (int i = 0; i < unit->fInputPins.Count(); i++)
                                        _HarvestOutputFeatureUnits(Find(unit->fInputPins[i]), Map);
                                Map.Put(rootControl->ID(), rootControl);
                        }
                        break;

                case USB_AUDIO_AC_FEATURE_UNIT:
                        Map.Put(rootControl->ID(), rootControl);
                        break;
        }
}


bool
AudioControlInterface::_InitGainLimits(multi_mix_control& Control)
{
        bool canControl = false;
        float current = 0.;
        struct _GainInfo {
                uint8   request;
                int16   data;
                float&  value;
        } gainInfos[] = {
                { USB_AUDIO_GET_CUR, 0, current },
                { USB_AUDIO_GET_MIN, 0, Control.gain.min_gain },
                { USB_AUDIO_GET_MAX, 0, Control.gain.max_gain },
                { USB_AUDIO_GET_RES, 0, Control.gain.granularity }
        };

        Control.gain.min_gain = 0.;
        Control.gain.max_gain = 100.;
        Control.gain.granularity = 1.;

        size_t actualLength = 0;
        for (size_t i = 0; i < B_COUNT_OF(gainInfos); i++) {
                status_t status = gUSBModule->send_request(fDevice->USBDevice(),
                        USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_CLASS,
                        gainInfos[i].request, REQ_VALUE(Control.id),
                        REQ_INDEX(Control.id), sizeof(gainInfos[i].data),
                        &gainInfos[i].data, &actualLength);

                if (status != B_OK || actualLength != sizeof(gainInfos[i].data)) {
                        TRACE(ERR, "Request %d (%04x:%04x) fail:%#08x; received %d of %d\n",
                                i, REQ_VALUE(Control.id), REQ_INDEX(Control.id), status,
                                actualLength, sizeof(gainInfos[i].data));
                        continue;
                }

                if (i == 0)
                        canControl = true;

                gainInfos[i].value = static_cast<float>(gainInfos[i].data) / 256.;
        }

        TRACE(ERR, "Control %s: %f dB, from %f to %f dB, step %f dB.\n",
                Control.name, current, Control.gain.min_gain, Control.gain.max_gain,
                Control.gain.granularity);
        return canControl;
}


uint32
AudioControlInterface::_ListFeatureUnitOption(uint32 controlType,
                int32& index, int32 parentIndex, multi_mix_control_info* Info,
                FeatureUnit* unit, uint32 channel, uint32 channels)
{
        int32 startIndex = index;
        uint32 id = 0;
        uint32 flags = 0;
        strind_id string = S_null;
        const char* name = NULL;
        bool initGainLimits = false;

        switch(controlType) {
                case BMA_CTL_MUTE:
                        id = USB_AUDIO_MUTE_CONTROL;
                        flags = B_MULTI_MIX_ENABLE;
                        string = S_MUTE;
                        break;
                case BMA_CTL_VOLUME:
                        id = USB_AUDIO_VOLUME_CONTROL;
                        flags = B_MULTI_MIX_GAIN;
                        string = S_GAIN;
                        initGainLimits = true;
                        break;
                case BMA_CTL_AUTOGAIN:
                        id = USB_AUDIO_AUTOMATIC_GAIN_CONTROL;
                        flags = B_MULTI_MIX_ENABLE;
                        name = "Auto Gain";
                        break;
                default:
                        TRACE(ERR, "Unsupported type %#08x ignored.\n", controlType);
                        return 0;
        }

        multi_mix_control* Controls = Info->controls;

        if (unit->HasControl(channel, controlType)) {
                uint32 masterIndex = CTL_ID(id, channel, unit->ID(), fInterface);
                Controls[index].id  = masterIndex;
                Controls[index].flags = flags;
                Controls[index].parent = parentIndex;
                Controls[index].string = string;
                if (name != NULL)
                        strlcpy(Controls[index].name, name, sizeof(Controls[index].name));
                if (initGainLimits)
                        _InitGainLimits(Controls[index]);

                index++;

                if (channels == 2) {
                        Controls[index].id = CTL_ID(id, channel + 1, unit->ID(), fInterface);
                        Controls[index].flags = flags;
                        Controls[index].parent = parentIndex;
                        Controls[index].master = masterIndex;
                        Controls[index].string = string;
                        if (name != NULL)
                                strlcpy(Controls[index].name, name, sizeof(Controls[index].name));
                        if (initGainLimits)
                                _InitGainLimits(Controls[index]);
                        index++;
                }
        }

        return index - startIndex;
}


int32
AudioControlInterface::_ListFeatureUnitControl(int32& index, int32 parentIndex,
                multi_mix_control_info* Info, _AudioControl* control)
{
        FeatureUnit* unit = static_cast<FeatureUnit*>(control);
        if (unit == 0) {
                TRACE(ERR, "Feature Unit for null control ignored.\n");
                return 0;
        }

        if (index + 4 > Info->control_count) {
                TRACE(ERR, "Could not list feature control group."
                        " Limit %d of %d has been reached.\n",
                        index, Info->control_count);
                return 0;
        }

        AudioChannelCluster* cluster = unit->OutCluster();
        if (cluster == 0) {
                TRACE(ERR, "Control %s with null cluster ignored.\n", unit->Name());
                return 0;
        }

        struct _ChannelInfo {
                const char*     Name;
                uint8           channels;
                uint32          Mask;
        } channelInfos[] = {
                { "",                   1, 0 }, // Master channel entry - no bitmask
                { "",                   2, B_CHANNEL_LEFT | B_CHANNEL_RIGHT },
                { "Left",               1, B_CHANNEL_LEFT       },
                { "Right",              1, B_CHANNEL_RIGHT      },
                { "Center",             1, B_CHANNEL_CENTER },
                { "L.F.E.",             1, B_CHANNEL_SUB        },
                { "Back",               2, B_CHANNEL_REARLEFT | B_CHANNEL_REARRIGHT },
                { "Back Left",  1, B_CHANNEL_REARLEFT   },
                { "Back Right", 1, B_CHANNEL_REARRIGHT  },
                { "Front of Center",            2, B_CHANNEL_FRONT_LEFT_CENTER
                                                                                | B_CHANNEL_FRONT_RIGHT_CENTER },
                { "Front Left of Center",       1, B_CHANNEL_FRONT_LEFT_CENTER  },
                { "Front Right of Center",      1, B_CHANNEL_FRONT_RIGHT_CENTER },
                { "Back Center",                1, B_CHANNEL_BACK_CENTER },
                { "Side",                               2, B_CHANNEL_SIDE_LEFT | B_CHANNEL_SIDE_RIGHT },
                { "Side Left",                  1, B_CHANNEL_SIDE_LEFT },
                { "Side Right",                 1, B_CHANNEL_SIDE_RIGHT },
                { "Top Center",                 1, B_CHANNEL_TOP_CENTER },
                { "Top Front Left",             1, B_CHANNEL_TOP_FRONT_LEFT },
                { "Top Front Center",   1, B_CHANNEL_TOP_FRONT_CENTER },
                { "Top Front Right",    1, B_CHANNEL_TOP_FRONT_RIGHT },
                { "Top Back Left",              1, B_CHANNEL_TOP_BACK_LEFT },
                { "Top Back Center",    1, B_CHANNEL_TOP_BACK_CENTER },
                { "Top Back Right",             1, B_CHANNEL_TOP_BACK_RIGHT }
        };

        multi_mix_control* Controls = Info->controls;

        uint32 channelsConfig = cluster->ChannelsConfig();
        int32 groupIndex = 0;
        int32 channel = 0;
        int32 masterIndex = 0;  // in case master channel has no volume
                                                        // control - add following "L+R" channels into it

        for (size_t i = 0; i < B_COUNT_OF(channelInfos); i++) {
                if ((channelsConfig & channelInfos[i].Mask) != channelInfos[i].Mask) {
                        // ignore non-listed and possibly non-paired stereo channels.
                        //      note that master channel with zero mask pass this check! ;-)
                        continue;
                }

                if (masterIndex == 0) {
                        groupIndex = index;
                        Controls[index].id = groupIndex;
                        Controls[index].flags = B_MULTI_MIX_GROUP;
                        Controls[index].parent = parentIndex;
                        snprintf(Controls[index].name, sizeof(Controls[index].name),
                                "%s %s", unit->Name(), channelInfos[i].Name);
                        index++;
                } else {
                        groupIndex = masterIndex;
                        masterIndex = 0;
                }

                // First list possible Mute controls
                _ListFeatureUnitOption(BMA_CTL_MUTE, index, groupIndex, Info,
                                unit, channel, channelInfos[i].channels);

                // Gain controls may be usefull too
                if (_ListFeatureUnitOption(BMA_CTL_VOLUME, index, groupIndex, Info,
                                unit, channel, channelInfos[i].channels) == 0) {
                        masterIndex = (i == 0) ? groupIndex : 0 ;
                        TRACE(UAC, "channel:%d set master index to %d\n",
                                channel, masterIndex);
                }

                // Auto Gain checkbox will be listed too
                _ListFeatureUnitOption(BMA_CTL_AUTOGAIN, index, groupIndex, Info,
                        unit, channel, channelInfos[i].channels);

                // Now check if the group filled with something usefull.
                // In case no controls were added into it - "remove" it
                if (Controls[index - 1].flags == B_MULTI_MIX_GROUP) {
                        Controls[index - 1].id = 0;
                        index--;

                        masterIndex = 0;
                }

                channel += channelInfos[i].channels;

                // remove bits for already processed channels - this prevent from
                // duplication of the stereo channels as "orphaned" ones and optimize
                // exit from this iterations after all channels are processed.
                channelsConfig &= ~channelInfos[i].Mask;

                if (0 == channelsConfig)
                        break;
        }

        if (channelsConfig > 0)
                TRACE(ERR, "Following channels were not processed: %#08x.\n",
                        channelsConfig);

        // return last group index to stick possible selector unit to it. ;-)
        return groupIndex;
}


void
AudioControlInterface::_ListSelectorUnitControl(int32& index, int32 parentGroup,
                multi_mix_control_info* Info, _AudioControl* control)
{
        SelectorUnit* selector = static_cast<SelectorUnit*>(control);
        if (selector == 0 || selector->SubType() != USB_AUDIO_AC_SELECTOR_UNIT)
                return;

        if ((index + 1 + selector->fInputPins.Count()) > Info->control_count) {
                TRACE(ERR, "Could not list selector control."
                        " Limit %d of %d has been reached.\n",
                        index, Info->control_count);
                return;
        }

        multi_mix_control* Controls = Info->controls;

        int32 recordMUX = CTL_ID(0, 0, selector->ID(), fInterface);
        Controls[index].id      = recordMUX;
        Controls[index].flags = B_MULTI_MIX_MUX;
        Controls[index].parent = parentGroup;
        Controls[index].string = S_null;
        strlcpy(Controls[index].name, "Source", sizeof(Controls[index].name));
        index++;

        for (int i = 0; i < selector->fInputPins.Count(); i++) {
                Controls[index].id = CTL_ID(0, 1, selector->ID(), fInterface);
                Controls[index].flags = B_MULTI_MIX_MUX_VALUE;
                Controls[index].master = 0;
                Controls[index].string = S_null;
                Controls[index].parent = recordMUX;
                _AudioControl* control = Find(selector->fInputPins[i]);
                if (control != NULL)
                        strlcpy(Controls[index].name,
                                control->Name(), sizeof(Controls[index].name));
                else
                        snprintf(Controls[index].name,
                                sizeof(Controls[index].name), "Input #%d", i + 1);
                index++;
        }
}


size_t
AudioControlInterface::_CollectMixerUnitControls(
                const uint32 controlIds[kChannels][kChannels],
                size_t inLeft, size_t outLeft, size_t inRight, size_t outRight,
                const char* inputName, const char* name,
                Vector<multi_mix_control>& Controls)
{
        size_t count = 0;
        uint32 leftId = controlIds[inLeft][outLeft];
        uint32 rightId = controlIds[inRight][outRight];

//      TRACE(UAC, "left:%d %d: %08x; right:%d %d: %08x\n",
//                      inLeft, outLeft, leftId, inRight, outRight, rightId);

        multi_mix_control control;
        memset(&control, 0, sizeof(multi_mix_control));
        snprintf(control.name, sizeof(control.name), "%s %s", inputName, name);

        for (size_t i = 0; i < 2; i++) {
                if (leftId != 0 || rightId != 0) {
                        control.flags = B_MULTI_MIX_GROUP;
                        control.string = S_null;
                        Controls.PushBack(control);

                        int gainControls = 0;
                        if (leftId != 0) {
                                control.id = leftId;
                                control.flags = B_MULTI_MIX_GAIN;
                                control.string = S_GAIN;
                                if (_InitGainLimits(control)) {
                                        gainControls++;
                                        Controls.PushBack(control);
                                }
                        }

                        if (rightId != 0) {
                                control.id = rightId;
                                control.flags = B_MULTI_MIX_GAIN;
                                control.string = S_GAIN;
                                control.master = leftId;
                                if (_InitGainLimits(control)) {
                                        gainControls++;
                                        Controls.PushBack(control);
                                }
                        }

                        // remove empty mix group
                        if (gainControls == 0)
                                Controls.PopBack();
                        else
                                count++;
                }

                // take care about surround bus
                if (inLeft == inRight)
                        break;
                // handle possible reverse controls
                leftId = controlIds[inLeft][outRight];
                rightId = controlIds[inRight][outLeft];
                snprintf(control.name, sizeof(control.name),
                        "%s %s (Reverse)", inputName, name);
        }

        return count;
}


void
AudioControlInterface::_ListMixerUnitControls(int32& index,
                multi_mix_control_info* Info, Vector<multi_mix_control>& controls)
{
        multi_mix_control* Controls = Info->controls;
        uint32 groupParent = 0;
        uint32 gainParent = 0;
        for (Vector<multi_mix_control>::Iterator I = controls.Begin();
                        I != controls.End() && index < Info->control_count; I++) {
                memcpy(Controls + index, &*I, sizeof(multi_mix_control));
                switch (I->flags) {
                        case B_MULTI_MIX_GROUP:
                                Controls[index].id = index;
                                Controls[index].parent = groupParent;
                                if (groupParent == 0) {
                                        Controls[index].id |= 0x10000;
                                        groupParent = Controls[index].id;
                                }
                                gainParent = Controls[index].id;
                                break;
                        case B_MULTI_MIX_GAIN:
                                Controls[index].parent = gainParent;
                                break;
                        default:
                                TRACE(ERR, "Control type %d ignored\n", I->flags);
                                continue;
                }

                index++;
        }

        if (index == Info->control_count)
                TRACE(ERR, "Control count limit %d has been reached.\n", index);
}


void
AudioControlInterface::_ListMixControlsForMixerUnit(int32& index,
                multi_mix_control_info* Info, _AudioControl* control)
{
        MixerUnit* mixer = static_cast<MixerUnit*>(control);
        if (mixer == 0 || mixer->SubType() != USB_AUDIO_AC_MIXER_UNIT)
                return;

        struct _ChannelPair {
                size_t inLeft;
                size_t inRight;
                const char* name;
        } channelPairs[] = {
                { 0, 1, "" },
                { 2, 2, "Center" },
                { 3, 3, "L.F.E" },
                { 4, 5, "Back" },
                { 6, 7, "Front of Center" },
                { 8, 8, "Back Center" },
                { 9, 10, "Side" },
                { 11, 11, "Top Center" },
                { 12, 14, "Top Front" },
                { 13, 13, "Top Front Center" },
                { 15, 17, "Top Back" },
                { 16, 16, "Top Back Center" }
        };

        Vector<_MixPageCollector*> mixControls;

        _MixPageCollector* genericPage = new(std::nothrow) _MixPageCollector("Mixer");
        mixControls.PushBack(genericPage);

        // page for extended in (>2) and out (>2) mixer controls
        size_t controlsOnExMixerPage = 0;
        _MixPageCollector* exMixerPage = new(std::nothrow) _MixPageCollector("Mixer");

        AudioChannelCluster* outCluster = mixer->OutCluster();

        int inOffset = 0;
        for (int iPin = 0; iPin < mixer->fInputPins.Count(); iPin++) {
                _AudioControl* control = Find(mixer->fInputPins[iPin]);
                AudioChannelCluster* inCluster = NULL;
                if (control != NULL)
                        inCluster = control->OutCluster();
                if (inCluster == NULL) {
                        TRACE(ERR, "control %p cluster %p failed!\n", control, inCluster);
                        break;
                }

                // at first - collect programmable control ids
                uint32 controlIds[kChannels][kChannels] = { { 0 } };

                int inChannel = 0;
                for (size_t in = 0; in < kChannels
                                && inChannel < inCluster->ChannelsCount(); in++) {
                        if ((inCluster->ChannelsConfig() & (1 << in)) == 0)
                                continue;

                        for (size_t out = 0, outChannel = 0; out < kChannels
                                        && outChannel < outCluster->ChannelsCount(); out++) {
                                if ((outCluster->ChannelsConfig() & (1 << out)) == 0)
                                        continue;

                                if (mixer->IsControlProgrammable(
                                                inOffset + inChannel, outChannel)) {
                                        if (SpecReleaseNumber() < 0x200)
                                                // USB Audio 1.0 uses ICN/OCN for request
                                                controlIds[in][out] = CTL_ID(inOffset + inChannel + 1,
                                                        outChannel + 1, mixer->ID(), fInterface);
                                        else
                                                // USB Audio 2.0 uses CS/MCN for request
                                                controlIds[in][out] = CTL_ID(USB_AUDIO_MIXER_CONTROL,
                                                        (inOffset + inChannel) * outCluster->ChannelsCount()
                                                        + outChannel, mixer->ID(), fInterface);
                                }

                                outChannel++;
                        }

                        inChannel++;
                }

                inOffset += inChannel;

                for (size_t in = 0; in < kChannels; in++)
                        for (size_t out = 0; out < kChannels; out++)
                                if (controlIds[in][out] != 0)
                                        TRACE(UAC, "ctrl:%08x for in %d; out %d;\n",
                                                controlIds[in][out], in, out);

                // second step - distribute controls on
                // mixer pages in logical groups
                uint32 exChannelsMask = ~(B_CHANNEL_LEFT | B_CHANNEL_RIGHT);
                bool inIsEx = (inCluster->ChannelsConfig() & exChannelsMask) != 0;
                bool outIsEx = (outCluster->ChannelsConfig() & exChannelsMask) != 0;

                if (!inIsEx && !outIsEx) {
                        // heap up all mono and stereo controls into single "Mixer" page
                        for (size_t i = 0; i < 2; i++)
                                _CollectMixerUnitControls(controlIds,
                                        kLeftChannel, channelPairs[i].inLeft,
                                        kRightChannel, channelPairs[i].inRight,
                                        control->Name(), channelPairs[i].name,
                                        *mixControls[0]);
                        continue; // go next input cluster
                }

                if (!outIsEx) {
                        // special case - extended (>2 channels) input cluster
                        // connected to 2-channels output - add into generic "Mixer" page
                        for (size_t i = 0; i < B_COUNT_OF(channelPairs); i++)
                                _CollectMixerUnitControls(controlIds,
                                        channelPairs[i].inLeft, kLeftChannel,
                                        channelPairs[i].inRight, kRightChannel,
                                        control->Name(), channelPairs[i].name,
                                        *mixControls[0]);
                        continue; // go next input cluster
                }

                // make separate mixer pages for set of extended (>2) input
                // channels connected to extended (>2 channels) output
                for (size_t in = 0; in < B_COUNT_OF(channelPairs); in++) {
                        for (size_t out = 0; out < B_COUNT_OF(channelPairs); out++) {
                                char outName[sizeof(Info->controls->name)] = { 0 };
                                if (in == out)
                                        strlcpy(outName, channelPairs[out].name, sizeof(outName));
                                else
                                        snprintf(outName, sizeof(outName), "%s to %s",
                                                channelPairs[in].name, channelPairs[out].name);

                                controlsOnExMixerPage += _CollectMixerUnitControls(controlIds,
                                        channelPairs[in].inLeft, channelPairs[out].inLeft,
                                        channelPairs[in].inRight, channelPairs[out].inRight,
                                        control->Name(), outName, *exMixerPage);
                        }

                        if (controlsOnExMixerPage >= 6) {
                                mixControls.PushBack(exMixerPage);
                                exMixerPage = new(std::nothrow) _MixPageCollector("Mixer");
                                controlsOnExMixerPage = 0;
                        }
                }
        }

        if (exMixerPage->Count() > 1)
                mixControls.PushBack(exMixerPage);
        else
                delete exMixerPage;

        // final step - fill multiaudio controls info with
        // already structured pages/controls info arrays
        for (Vector<_MixPageCollector*>::Iterator I = mixControls.Begin();
                        I != mixControls.End(); I++) {
                Vector<multi_mix_control>* controls = *I;
                        TRACE(UAC, "controls count: %d\n", controls->Count());
                if (controls->Count() > 1)
                        _ListMixerUnitControls(index, Info, *controls);
                delete controls;
        }
}


void
AudioControlInterface::_ListMixControlsPage(int32& index,
                multi_mix_control_info* Info, AudioControlsMap& Map, const char* Name)
{
        multi_mix_control* Controls = Info->controls;
        int32 groupIndex = index | 0x10000;
        Controls[index].id      = groupIndex;
        Controls[index].flags = B_MULTI_MIX_GROUP;
        Controls[index].parent = 0;
        strlcpy(Controls[index].name, Name, sizeof(Controls[index].name));
        index++;

        int32 group = groupIndex;
        for (AudioControlsIterator I = Map.Begin(); I != Map.End(); I++) {
                TRACE(UAC, "%s control %d listed.\n", Name, I->Value()->ID());
                switch(I->Value()->SubType()) {
                        case USB_AUDIO_AC_FEATURE_UNIT:
                                group = _ListFeatureUnitControl(index, groupIndex,
                                        Info, I->Value());
                                break;
                        case USB_AUDIO_AC_SELECTOR_UNIT:
                                _ListSelectorUnitControl(index, group, Info, I->Value());
                                break;
                        default:
                                break;
                }
        }
}


status_t
AudioControlInterface::ListMixControls(multi_mix_control_info* Info)
{
        // first harvest feature units that assigned to output terminal(s)
        AudioControlsMap RecordControlsMap;
        AudioControlsMap OutputControlsMap;

        for (AudioControlsIterator I = fOutputTerminals.Begin();
                        I != fOutputTerminals.End(); I++) {
                _Terminal* terminal = static_cast<_Terminal*>(I->Value());
                if (terminal->IsUSBIO())
                        _HarvestRecordFeatureUnits(terminal, RecordControlsMap);
                else
                        _HarvestOutputFeatureUnits(terminal, OutputControlsMap);
        }

        // separate input and output Feature units
        // and collect mixer units that can be controlled
        AudioControlsMap InputControlsMap;
        AudioControlsMap MixerControlsMap;

        for (AudioControlsIterator I = fAudioControls.Begin();
                        I != fAudioControls.End(); I++) {
                _AudioControl* control = I->Value();

                if (control->SubType() == USB_AUDIO_AC_MIXER_UNIT) {
                        MixerUnit* mixerControl = static_cast<MixerUnit*>(control);
                        if (mixerControl->HasProgrammableControls())
                                MixerControlsMap.Put(control->ID(), control);
                        continue;
                }

                // filter out feature units
                if (control->SubType() != USB_AUDIO_AC_FEATURE_UNIT)
                        continue;

                // ignore controls that are already in the output controls maps
                if (RecordControlsMap.Find(control->ID()) != RecordControlsMap.End()
                        || OutputControlsMap.Find(control->ID()) != OutputControlsMap.End())
                        continue;

                _AudioControl* sourceControl = Find(control->SourceID());
                if (sourceControl != 0
                                && sourceControl->SubType() == USB_AUDIO_AC_INPUT_TERMINAL)
                        InputControlsMap.Put(control->ID(), control);
                else
                        OutputControlsMap.Put(control->ID(), control);
        }

        int32 index = 0;
        if (InputControlsMap.Count() > 0)
                _ListMixControlsPage(index, Info, InputControlsMap, "Input");

        if (OutputControlsMap.Count() > 0)
                _ListMixControlsPage(index, Info, OutputControlsMap, "Output");

        if (RecordControlsMap.Count() > 0)
                _ListMixControlsPage(index, Info, RecordControlsMap, "Record");


        for (AudioControlsIterator I = MixerControlsMap.Begin();
                        I != MixerControlsMap.End(); I++)
                _ListMixControlsForMixerUnit(index, Info, I->Value());

        Info->control_count = index;

        return B_OK;
}


status_t
AudioControlInterface::GetMix(multi_mix_value_info* Info)
{
        for (int32 i = 0; i < Info->item_count; i++) {
                uint16 length = 0;
                int16 data = 0;

                _AudioControl* control = Find(ID_FROM_CTLID(Info->values[i].id));
                if (control == NULL) {
                        TRACE(ERR, "No control found for unit id %#02x. Ignore it.\n",
                                ID_FROM_CTLID(Info->values[i].id));
                        continue;
                }

                switch (control->SubType()) {
                        case USB_AUDIO_AC_FEATURE_UNIT:
                                switch(CS_FROM_CTLID(Info->values[i].id)) {
                                        case USB_AUDIO_VOLUME_CONTROL:
                                                length = 2;
                                                break;
                                        case USB_AUDIO_MUTE_CONTROL:
                                        case USB_AUDIO_AUTOMATIC_GAIN_CONTROL:
                                                length = 1;
                                                break;
                                        default:
                                                TRACE(ERR, "Unsupported control id:%08x of type %#02x "
                                                        "ignored.\n", ID_FROM_CTLID(Info->values[i].id),
                                                        CS_FROM_CTLID(Info->values[i].id));
                                                continue;
                                }
                                break;
                        case USB_AUDIO_AC_SELECTOR_UNIT:
                                length = 1;
                                break;
                        case USB_AUDIO_AC_MIXER_UNIT:
                                length = 2;
                                break;
                        default:
                                TRACE(ERR, "Control id:%08x of type %d is not supported\n",
                                        ID_FROM_CTLID(Info->values[i].id), control->SubType());
                                continue;
                }

                size_t actualLength = 0;
                status_t status = gUSBModule->send_request(fDevice->USBDevice(),
                        USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_CLASS, USB_AUDIO_GET_CUR,
                        REQ_VALUE(Info->values[i].id), REQ_INDEX(Info->values[i].id),
                        length, &data, &actualLength);

                if (status != B_OK || actualLength != length) {
                        TRACE(ERR, "Request (%04x:%04x) failed:%#08x; received %d of %d\n",
                                REQ_VALUE(Info->values[i].id), REQ_INDEX(Info->values[i].id),
                                status, actualLength, length);
                        continue;
                }

                switch (control->SubType()) {
                        case USB_AUDIO_AC_FEATURE_UNIT:
                                switch(CS_FROM_CTLID(Info->values[i].id)) {
                                        case USB_AUDIO_VOLUME_CONTROL:
                                                Info->values[i].gain = static_cast<float>(data) / 256.;
                                                TRACE(MIX, "Gain control %d; channel: %d; is %f dB.\n",
                                                        ID_FROM_CTLID(Info->values[i].id),
                                                        CN_FROM_CTLID(Info->values[i].id),
                                                        Info->values[i].gain);
                                                break;
                                        case USB_AUDIO_MUTE_CONTROL:
                                                Info->values[i].enable = data > 0;
                                                TRACE(MIX, "Mute control %d; channel: %d; is %d.\n",
                                                        ID_FROM_CTLID(Info->values[i].id),
                                                        CN_FROM_CTLID(Info->values[i].id),
                                                        Info->values[i].enable);
                                                break;
                                        case USB_AUDIO_AUTOMATIC_GAIN_CONTROL:
                                                Info->values[i].enable = data > 0;
                                                TRACE(MIX, "AGain control %d; channel: %d; is %d.\n",
                                                        ID_FROM_CTLID(Info->values[i].id),
                                                        CN_FROM_CTLID(Info->values[i].id),
                                                        Info->values[i].enable);
                                                break;
                                        default:
                                                break;
                                }
                                break;
                        case USB_AUDIO_AC_SELECTOR_UNIT:
                                Info->values[i].mux = data - 1;
                                TRACE(MIX, "Selector control %d; is %d.\n",
                                        ID_FROM_CTLID(Info->values[i].id),
                                        Info->values[i].mux);
                                break;
                        case USB_AUDIO_AC_MIXER_UNIT:
                                Info->values[i].gain = static_cast<float>(data) / 256.;
                                TRACE(MIX, "Mixer #%d channels in: %d; out: %d; is %f dB.\n",
                                        ID_FROM_CTLID(Info->values[i].id),
                                        CS_FROM_CTLID(Info->values[i].id),
                                        CN_FROM_CTLID(Info->values[i].id),
                                        Info->values[i].gain);
                                break;
                }
        }

        return B_OK;
}


status_t
AudioControlInterface::SetMix(multi_mix_value_info* Info)
{
        for (int32 i = 0; i < Info->item_count; i++) {
                uint16 length = 0;
                int16 data = 0;

                _AudioControl* control = Find(ID_FROM_CTLID(Info->values[i].id));
                if (control == NULL) {
                        TRACE(ERR, "No control found for unit id %#02x. Ignore it.\n",
                                ID_FROM_CTLID(Info->values[i].id));
                        continue;
                }

                switch (control->SubType()) {
                        case USB_AUDIO_AC_FEATURE_UNIT:
                                switch(CS_FROM_CTLID(Info->values[i].id)) {
                                        case USB_AUDIO_VOLUME_CONTROL:
                                                data = static_cast<int16>(Info->values[i].gain * 256.);
                                                length = 2;
                                                TRACE(MIX, "Gain control %d; channel: %d; "
                                                        "about to set to %f dB.\n",
                                                        ID_FROM_CTLID(Info->values[i].id),
                                                        CN_FROM_CTLID(Info->values[i].id),
                                                        Info->values[i].gain);
                                                break;
                                        case USB_AUDIO_MUTE_CONTROL:
                                                data = (Info->values[i].enable ? 1 : 0);
                                                length = 1;
                                                TRACE(MIX, "Mute control %d; channel: %d; "
                                                        "about to set to %d.\n",
                                                        ID_FROM_CTLID(Info->values[i].id),
                                                        CN_FROM_CTLID(Info->values[i].id),
                                                        Info->values[i].enable);
                                                break;
                                        case USB_AUDIO_AUTOMATIC_GAIN_CONTROL:
                                                data = (Info->values[i].enable ? 1 : 0);
                                                length = 1;
                                                TRACE(MIX, "AGain control %d; channel: %d; "
                                                        "about to set to %d.\n",
                                                        ID_FROM_CTLID(Info->values[i].id),
                                                        CN_FROM_CTLID(Info->values[i].id),
                                                        Info->values[i].enable);
                                                break;
                                        default:
                                                TRACE(ERR, "Unsupported control id:%08x of type %#02x "
                                                        "ignored.\n", ID_FROM_CTLID(Info->values[i].id),
                                                        CS_FROM_CTLID(Info->values[i].id));
                                                continue;
                                }
                                break;
                        case USB_AUDIO_AC_SELECTOR_UNIT:
                                data = Info->values[i].mux + 1;
                                length = 1;
                                TRACE(MIX, "Selector Control %d about to set to %d.\n",
                                        ID_FROM_CTLID(Info->values[i].id),
                                        Info->values[i].mux);
                                break;
                        case USB_AUDIO_AC_MIXER_UNIT:
                                data = static_cast<int16>(Info->values[i].gain * 256.);
                                length = 2;
                                TRACE(MIX, "Mixer %d channels in: %d; out: %d; "
                                        "about to set to %f dB.\n",
                                        ID_FROM_CTLID(Info->values[i].id),
                                        CS_FROM_CTLID(Info->values[i].id),
                                        CN_FROM_CTLID(Info->values[i].id),
                                        Info->values[i].gain);
                                break;
                        default:
                                TRACE(ERR, "Control id:%08x of type %d is not supported\n",
                                        Info->values[i].id, control->SubType());
                                continue;
                }

                size_t actualLength = 0;
                status_t status = gUSBModule->send_request(fDevice->USBDevice(),
                        USB_REQTYPE_INTERFACE_OUT | USB_REQTYPE_CLASS, USB_AUDIO_SET_CUR,
                        REQ_VALUE(Info->values[i].id), REQ_INDEX(Info->values[i].id),
                        length, &data, &actualLength);

                if (status != B_OK || actualLength != length) {
                        TRACE(ERR, "Request (%04x:%04x) failed:%#08x; send %d of %d\n",
                                REQ_VALUE(Info->values[i].id), REQ_INDEX(Info->values[i].id),
                                status, actualLength, length);
                        continue;
                }

                TRACE(MIX, "Value set OK\n");
        }

        return B_OK;
}