hrev44099 adds 1 changeset to branch 'master' old head: b2070d2855626e2faa955aeb97082feb07089455 new head: 408c7ab11d7dbbbce4c098d83e40db1a3da1c873 ---------------------------------------------------------------------------- 408c7ab: Publishing USB Audio driver from my dev.branch on the old SVN repo. [ Siarzhuk Zharski <zharik@xxxxxx> ] ---------------------------------------------------------------------------- Revision: hrev44099 Commit: 408c7ab11d7dbbbce4c098d83e40db1a3da1c873 URL: http://cgit.haiku-os.org/haiku/commit/?id=408c7ab Author: Siarzhuk Zharski <zharik@xxxxxx> Date: Thu May 3 18:37:59 2012 UTC ---------------------------------------------------------------------------- 22 files changed, 6513 insertions(+), 1464 deletions(-) .../audio/usb_audio/AudioControlInterface.cpp | 1711 ++++++++++++++++ .../audio/usb_audio/AudioControlInterface.h | 319 +++ .../drivers/audio/usb_audio/AudioFunction.cpp | 1092 ++++++++++ .../kernel/drivers/audio/usb_audio/AudioFunction.h | 349 ++++ .../audio/usb_audio/AudioStreamingInterface.cpp | 406 ++++ .../audio/usb_audio/AudioStreamingInterface.h | 156 ++ .../kernel/drivers/audio/usb_audio/Device.cpp | 778 +++++++ .../kernel/drivers/audio/usb_audio/Device.h | 139 ++ .../kernel/drivers/audio/usb_audio/Driver.cpp | 303 +++ .../kernel/drivers/audio/usb_audio/Driver.h | 53 + src/add-ons/kernel/drivers/audio/usb_audio/Jamfile | 17 +- .../kernel/drivers/audio/usb_audio/Settings.cpp | 113 + .../kernel/drivers/audio/usb_audio/Settings.h | 34 + .../kernel/drivers/audio/usb_audio/Stream.cpp | 528 +++++ .../kernel/drivers/audio/usb_audio/Stream.h | 96 + .../drivers/audio/usb_audio/USB_audio_spec.h | 297 ++- .../drivers/audio/usb_audio/USB_audio_utils.c | 395 ---- .../drivers/audio/usb_audio/USB_audio_utils.h | 43 - .../kernel/drivers/audio/usb_audio/makefile | 127 ++ src/add-ons/kernel/drivers/audio/usb_audio/sound.h | 87 - .../kernel/drivers/audio/usb_audio/usb_audio.c | 906 -------- .../drivers/audio/usb_audio/usb_audio.settings | 28 + ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/AudioControlInterface.cpp b/src/add-ons/kernel/drivers/audio/usb_audio/AudioControlInterface.cpp new file mode 100644 index 0000000..a58bdd2 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/AudioControlInterface.cpp @@ -0,0 +1,1711 @@ +/* + * Driver for USB Audio Device Class devices. + * Copyright (c) 2009,10,12 S.Zharski <imker@xxxxxx> + * Distributed under the tems of the MIT license. + * + */ + +#include "AudioControlInterface.h" +#include "Settings.h" +#include "Device.h" +#include "audio.h" + + +enum TerminalTypes { + // USB Terminal Types + UndefinedUSB_IO = 0x100, + StreamingUSB_IO = 0x101, + VendorUSB_IO = 0x1ff, + // Input Terminal Types + Undefined_In = 0x200, + Microphone_In = 0x201, + DesktopMic_In = 0x202, + PersonalMic_In = 0x203, + OmniMic_In = 0x204, + MicsArray_In = 0x205, + ProcMicsArray_In = 0x206, + // Output Terminal Types + Undefined_Out = 0x300, + Speaker_Out = 0x301, + HeadPhones_Out = 0x302, + HMDAudio_Out = 0x303, + DesktopSpeaker = 0x304, + RoomSpeaker = 0x305, + CommSpeaker = 0x306, + LFESpeaker = 0x307, + // Bi-directional Terminal Types + Undefined_IO = 0x400, + Handset_IO = 0x401, + Headset_IO = 0x402, + SpeakerPhone_IO = 0x403, + SpeakerPhoneES_IO = 0x404, + SpeakerPhoneEC_IO = 0x405, + // Telephony Terminal Types + UndefTelephony_IO = 0x500, + PhoneLine_IO = 0x501, + Telephone_IO = 0x502, + DownLinePhone_IO = 0x503, + // External Terminal Types + UndefinedExt_IO = 0x600, + AnalogConnector_IO = 0x601, + DAInterface_IO = 0x602, + LineConnector_IO = 0x603, + LegacyConnector_IO = 0x604, + SPDIFInterface_IO = 0x605, + DA1394Stream_IO = 0x606, + DV1394StreamSound_IO= 0x607, + ADATLightpipe_IO = 0x608, + TDIF_IO = 0x609, + MADI_IO = 0x60a, + // Embedded Terminal Types + UndefEmbedded_IO = 0x700, + LCNoiseSource_Out = 0x701, + EqualizationNoise_Out = 0x702, + CDPlayer_In = 0x703, + DAT_IO = 0x704, + DCC_IO = 0x705, + MiniDisk_IO = 0x706, + AnalogTape_IO = 0x707, + Phonograph_In = 0x708, + VCRAudio_In = 0x709, + VideoDiscAudio_In = 0x70a, + DVDAudio_In = 0x70b, + TVTunerAudio_In = 0x70c, + SatReceiverAudio_In = 0x70d, + CableTunerAudio_In = 0x70e, + DSSAudio_In = 0x70f, + Radio_Receiver_In = 0x710, + RadioTransmitter_In = 0x711, + MultiTrackRecorder_IO = 0x712, + Synthesizer_IO = 0x713, + Piano_IO = 0x714, + Guitar_IO = 0x715, + Drums_IO = 0x716, + Instrument_IO = 0x717 +}; + + +const char* +GetTerminalDescription(uint16 TerminalType) +{ + switch(TerminalType) { + // USB Terminal Types + case UndefinedUSB_IO: return "USB I/O"; + case StreamingUSB_IO: return "USB Streaming I/O"; + case VendorUSB_IO: return "Vendor USB I/O"; + // Input Terminal Types + case Undefined_In: return "Undefined Input"; + case Microphone_In: return "Microphone"; + case DesktopMic_In: return "Desktop Microphone"; + case PersonalMic_In: return "Personal Microphone"; + case OmniMic_In: return "Omni-directional Mic"; + case MicsArray_In: return "Microphone Array"; + case ProcMicsArray_In: return "Processing Mic Array"; + // Output Terminal Types + case Undefined_Out: return "Undefined Output"; + case Speaker_Out: return "Speaker"; + case HeadPhones_Out: return "Headphones"; + case HMDAudio_Out: return "Head Mounted Disp.Audio"; + case DesktopSpeaker: return "Desktop Speaker"; + case RoomSpeaker: return "Room Speaker"; + case CommSpeaker: return "Communication Speaker"; + case LFESpeaker: return "LFE Speaker"; + // Bi-directional Terminal Types + case Undefined_IO: return "Undefined I/O"; + case Handset_IO: return "Handset"; + case Headset_IO: return "Headset"; + case SpeakerPhone_IO: return "Speakerphone"; + case SpeakerPhoneES_IO: return "Echo-supp Speakerphone"; + case SpeakerPhoneEC_IO: return "Echo-cancel Speakerphone"; + // Telephony Terminal Types + case UndefTelephony_IO: return "Undefined Telephony"; + case PhoneLine_IO: return "Phone Line"; + case Telephone_IO: return "Telephone"; + case DownLinePhone_IO: return "Down Line Phone"; + // External Terminal Types + case UndefinedExt_IO: return "Undefined External I/O"; + case AnalogConnector_IO: return "Analog Connector"; + case DAInterface_IO: return "Digital Audio Interface"; + case LineConnector_IO: return "Line Connector"; + case LegacyConnector_IO: return "LegacyAudioConnector"; + case SPDIFInterface_IO: return "S/PDIF Interface"; + case DA1394Stream_IO: return "1394 DA Stream"; + case DV1394StreamSound_IO: return "1394 DV Stream Soundtrack"; + case ADATLightpipe_IO: return "Alesis DAT Stream"; + case TDIF_IO: return "Tascam Digital Interface"; + case MADI_IO: return "AES Multi-channel interface"; + // Embedded Terminal Types + case UndefEmbedded_IO: return "Undefined Embedded I/O"; + case LCNoiseSource_Out: return "Level Calibration Noise Source"; + case EqualizationNoise_Out: return "Equalization Noise"; + case CDPlayer_In: return "CD Player"; + case DAT_IO: return "DAT"; + case DCC_IO: return "DCC"; + case MiniDisk_IO: return "Mini Disk"; + case AnalogTape_IO: return "Analog Tape"; + case Phonograph_In: return "Phonograph"; + case VCRAudio_In: return "VCR Audio"; + case VideoDiscAudio_In: return "Video Disc Audio"; + case DVDAudio_In: return "DVD Audio"; + case TVTunerAudio_In: return "TV Tuner Audio"; + case SatReceiverAudio_In: return "Satellite Receiver Audio"; + case CableTunerAudio_In: return "Cable Tuner Audio"; + case DSSAudio_In: return "DSS Audio"; + case Radio_Receiver_In: return "Radio Receiver"; + case RadioTransmitter_In: return "Radio Transmitter"; + case MultiTrackRecorder_IO: return "Multi-track Recorder"; + case Synthesizer_IO: return "Synthesizer"; + case Piano_IO: return "Piano"; + case Guitar_IO: return "Guitar"; + case Drums_IO: return "Percussion Instrument"; + case Instrument_IO: return "Musical Instrument"; + } + + TRACE_ALWAYS("Unknown Terminal Type: %#06x", TerminalType); + return "Unknown"; +} + + +// 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)) + + +_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() +{ + +} + + +_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) == UndefinedUSB_IO; +} + + +InputTerminal::InputTerminal(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioChannelCluster<_Terminal>(interface, Header) +{ + usb_input_terminal_descriptor_r1* Terminal + = (usb_input_terminal_descriptor_r1*) Header; + fID = Terminal->terminal_id; + fTerminalType = Terminal->terminal_type; + fAssociatedTerminal = Terminal->assoc_terminal; + + TRACE("Input Terminal ID:%d >>>\n", fID); + TRACE("Terminal type:%s (%#06x)\n", + GetTerminalDescription(fTerminalType), fTerminalType); + TRACE("Assoc.terminal:%d\n", fAssociatedTerminal); + + if (fInterface->SpecReleaseNumber() < 0x200) { + fOutChannelsNumber = Terminal->num_channels; + fChannelsConfig = Terminal->channel_config; + fChannelNames = Terminal->channel_names; + fStringIndex = Terminal->terminal; + } else { + usb_input_terminal_descriptor* Terminal + = (usb_input_terminal_descriptor*) Header; + fClockSourceId = Terminal->clock_source_id; + fOutChannelsNumber = Terminal->num_channels; + fChannelsConfig = Terminal->channel_config; + fChannelNames = Terminal->channel_names; + fControlsBitmap = Terminal->bm_controls; + fStringIndex = Terminal->terminal; + + TRACE("Clock Source ID:%d\n", fClockSourceId); + TRACE("Controls Bitmap:%#04x\n", fControlsBitmap); + } + + TRACE("Out.channels num:%d\n", fOutChannelsNumber); + TRACE("Channels config:%#06x\n", fChannelsConfig); + TRACE("Channels names:%d\n", fChannelNames); + TRACE("StringIndex:%d\n", fStringIndex); + + fStatus = B_OK; +} + + +InputTerminal::~InputTerminal() +{ +} + + +OutputTerminal::OutputTerminal(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header) + : + _Terminal(interface, Header) +{ + usb_output_terminal_descriptor_r1* Terminal + = (usb_output_terminal_descriptor_r1*) Header; + + fID = Terminal->terminal_id; + fTerminalType = Terminal->terminal_type; + fAssociatedTerminal = Terminal->assoc_terminal; + fSourceID = Terminal->source_id; + + TRACE("Output Terminal ID:%d >>>\n", fID); + TRACE("Terminal type:%s (%#06x)\n", + GetTerminalDescription(fTerminalType), fTerminalType); + TRACE("Assoc.terminal:%d\n", fAssociatedTerminal); + TRACE("Source ID:%d\n", fSourceID); + + if (fInterface->SpecReleaseNumber() < 0x200) { + fStringIndex = Terminal->terminal; + } else { + usb_output_terminal_descriptor* Terminal + = (usb_output_terminal_descriptor*) Header; + + fClockSourceId = Terminal->clock_source_id; + fControlsBitmap = Terminal->bm_controls; + fStringIndex = Terminal->terminal; + + TRACE("Clock Source ID:%d\n", fClockSourceId); + TRACE("Controls Bitmap:%#04x\n", fControlsBitmap); + } + + TRACE("StringIndex:%d\n", fStringIndex); + + fStatus = B_OK; +} + + +OutputTerminal::~OutputTerminal() +{ +} + + +MixerUnit::MixerUnit(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioChannelCluster<_AudioControl>(interface, Header), + fControlsBitmap(0) +{ + usb_mixer_unit_descriptor* Mixer + = (usb_mixer_unit_descriptor*) Header; + + fID = Mixer->unit_id; + TRACE("Mixer ID:%d >>>\n", fID); + + TRACE("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("Input pin #%d:%d\n", i, fInputPins[i]); + } + + uint8* mixerControlsData = NULL; + uint8 mixerControlsSize = 0; + + if (fInterface->SpecReleaseNumber() < 0x200) { + usb_output_channels_descriptor_r1* OutChannels + = (usb_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); + } else { + usb_output_channels_descriptor* OutChannels + = (usb_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; + fControlsBitmap = *(mixerControlsData + mixerControlsSize); + fStringIndex = *(mixerControlsData + mixerControlsSize + 1); + + TRACE("Control Bitmap:%#04x\n", fControlsBitmap); + } + + TRACE("Out channels number:%d\n", fOutChannelsNumber); + TRACE("Out channels config:%#06x\n", fChannelsConfig); + TRACE("Out channels names:%d\n", fChannelNames); + TRACE("Controls Size:%d\n", mixerControlsSize); + + for (size_t i = 0; i < mixerControlsSize; i++) { + fProgrammableControls.PushBack(mixerControlsData[i]); + TRACE("Controls Data[%d]:%#x\n", i, fProgrammableControls[i]); + } + + TRACE("StringIndex:%d\n", fStringIndex); + + fStatus = B_OK; +} + + +MixerUnit::~MixerUnit() +{ +} + + +SelectorUnit::SelectorUnit(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(interface, Header), + fControlsBitmap(0) +{ + usb_selector_unit_descriptor* Selector + = (usb_selector_unit_descriptor*) Header; + + fID = Selector->unit_id; + TRACE("Selector ID:%d >>>\n", fID); + + TRACE("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("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("Controls Bitmap:%d\n", fControlsBitmap); + } + + TRACE("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; + } + + if (control->OutCluster() != NULL) { + return control->OutCluster(); + } + } + + return NULL; +} + + +FeatureUnit::FeatureUnit(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(interface, Header) +{ + usb_feature_unit_descriptor* Feature + = (usb_feature_unit_descriptor*) Header; + + fID = Feature->unit_id; + TRACE("Feature ID:%d >>>\n", fID); + + fSourceID = Feature->source_id; + TRACE("Source ID:%d\n", fSourceID); + + uint8 controlSize = 4; + uint8 channelsCount = (Feature->length - 6) / controlSize; + uint8 *ControlsBitmapPointer = (uint8*)&Feature->bma_controls[0]; + + if (fInterface->SpecReleaseNumber() < 0x200) { + usb_feature_unit_descriptor_r1* Feature + = (usb_feature_unit_descriptor_r1*) Header; + controlSize = Feature->control_size; + channelsCount = (Feature->length - 7) / Feature->control_size; + ControlsBitmapPointer = &Feature->bma_controls[0]; + } + + TRACE("Channel bitmap size:%d\n", controlSize); + TRACE("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_ALWAYS("Feature control of unsupported size %d ignored\n", + controlSize); + continue; + } + + NormalizeAndTraceChannel(i); + } + + fStringIndex = ControlsBitmapPointer[channelsCount * controlSize]; + TRACE("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 != 0) { + + if (control->SubType() != IDSInputTerminal) + 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 != 0) { + + if (control->SubType() != IDSOutputTerminal) + 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(); + } + + return "Unknown"; +} + + +bool +FeatureUnit::HasControl(int32 Channel, uint32 Control) +{ + if (Channel >= fControlBitmaps.Count()) { + TRACE_ALWAYS("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_ALWAYS("Out of limits error of tracing channel %d\n", Channel); + return; + } + + struct _RemapInfo { + uint32 rev1Bits; + uint32 rev2Bits; + const char* name; + } remapInfos[] = { + { MuteControl1, MuteControl, "Mute" }, + { VolumeControl1, VolumeControl, "Volume" }, + { BassControl1, BassControl, "Bass" }, + { MidControl1, MidControl, "Mid" }, + { TrebleControl1, TrebleControl, "Treble" }, + { GraphEqControl1, GraphEqControl, "Graphic Equalizer" }, + { AutoGainControl1, AutoGainControl, "Automatic Gain" }, + { DelayControl1, DelayControl, "Delay" }, + { BassBoostControl1, BassBoostControl, "Bass Boost" }, + { LoudnessControl1, LoudnessControl, "Loudness" }, + { 0, InputGainControl, "InputGain" }, + { 0, InputGainPadControl, "InputGainPad" }, + { 0, PhaseInverterControl, "PhaseInverter" }, + { 0, UnderflowControl, "Underflow" }, + { 0, OverflowControl, "Overflow" } + }; + + if (Channel == 0) + TRACE("Master channel bitmap:%#x\n", fControlBitmaps[Channel]); + else + TRACE("Channel %d bitmap:%#x\n", Channel, fControlBitmaps[Channel]); + + bool isRev1 = (fInterface->SpecReleaseNumber() < 0x200); + + uint32 remappedBitmap = 0; + for (size_t i = 0; i < _countof(remapInfos); i++) { + uint32 bits = isRev1 ? remapInfos[i].rev1Bits : remapInfos[i].rev2Bits; + if ((fControlBitmaps[Channel] & bits) > 0) { + if (isRev1) { + remappedBitmap |= remapInfos[i].rev2Bits; + } + TRACE("\t%s\n", remapInfos[i].name); + } + } + + if (isRev1) { + TRACE("\t%#08x -> %#08x.\n", fControlBitmaps[Channel], remappedBitmap); + fControlBitmaps[Channel] = remappedBitmap; + } +} + + +EffectUnit::EffectUnit(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(interface, Header) +{ + usb_input_terminal_descriptor* D + = (usb_input_terminal_descriptor*) Header; + TRACE("Effect Unit:%d >>>\n", D->terminal_id); +} + + +EffectUnit::~EffectUnit() +{ +} + + +ProcessingUnit::ProcessingUnit(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioChannelCluster<_AudioControl>(interface, Header), + fProcessType(0), + fControlsBitmap(0) +{ + usb_processing_unit_descriptor* Processing + = (usb_processing_unit_descriptor*) Header; + + fID = Processing->unit_id; + TRACE("Processing ID:%d >>>\n", fID); + + fProcessType = Processing->process_type; + TRACE("Processing Type:%d\n", fProcessType); + + TRACE("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("Input pin #%d:%d\n", i, fInputPins[i]); + } + + if (fInterface->SpecReleaseNumber() < 0x200) { + usb_output_channels_descriptor_r1* OutChannels + = (usb_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_output_channels_descriptor* OutChannels + = (usb_output_channels_descriptor*) + &Processing->input_pins[Processing->num_input_pins]; + + fOutChannelsNumber = OutChannels->num_output_pins; + fChannelsConfig = OutChannels->channel_config; + fChannelNames = OutChannels->channel_names; + } + + TRACE("Out channels number:%d\n", fOutChannelsNumber); + TRACE("Out channels config:%#06x\n", fChannelsConfig); + TRACE("Out channels names:%d\n", fChannelNames); + /* + uint8 controlsSize = Processing->length - 10 - Processing->num_input_pins; + TRACE("Controls Size:%d\n", controlsSize); + + uint8* controlsData = (uint8*) ++OutChannels; + + for (size_t i = 0; i < controlsSize; i++) { + fProgrammableControls.PushBack(controlsData[i]); + TRACE("Controls Data[%d]:%#x\n", i, controlsData[i]); + } + + fStringIndex = *(controlsData + controlsSize); + + TRACE("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_extension_unit_descriptor* Extension + = (usb_extension_unit_descriptor*) Header; + + fID = Extension->unit_id; + TRACE("Extension ID:%d >>>\n", fID); + + fExtensionCode = Extension->extension_code; + TRACE("Extension Type:%d\n", fExtensionCode); + + TRACE("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("Input pin #%d:%d\n", i, fInputPins[i]); + } + + if (fInterface->SpecReleaseNumber() < 0x200) { + usb_output_channels_descriptor_r1* OutChannels + = (usb_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_output_channels_descriptor* OutChannels + = (usb_output_channels_descriptor*) + &Extension->input_pins[Extension->num_input_pins]; + + fOutChannelsNumber = OutChannels->num_output_pins; + fChannelsConfig = OutChannels->channel_config; + fChannelNames = OutChannels->channel_names; + } + + TRACE("Out channels number:%d\n", fOutChannelsNumber); + TRACE("Out channels config:%#06x\n", fChannelsConfig); + TRACE("Out channels names:%d\n", fChannelNames); + /* + uint8 controlsSize = Processing->length - 10 - Processing->num_input_pins; + TRACE("Controls Size:%d\n", controlsSize); + + uint8* controlsData = (uint8*) ++OutChannels; + + for (size_t i = 0; i < controlsSize; i++) { + fProgrammableControls.PushBack(controlsData[i]); + TRACE("Controls Data[%d]:%#x\n", i, controlsData[i]); + } + + fStringIndex = *(controlsData + controlsSize); + + TRACE("StringIndex:%d\n", fStringIndex); +*/ + fStatus = B_OK; +} + + +ExtensionUnit::~ExtensionUnit() +{ +} + + +ClockSource::ClockSource(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(interface, Header) +{ + usb_input_terminal_descriptor* D + = (usb_input_terminal_descriptor*) Header; + TRACE("Clock Source:%d >>>\n", D->terminal_id); +} + + +ClockSource::~ClockSource() +{ +} + + +ClockSelector::ClockSelector(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(interface, Header) +{ + usb_input_terminal_descriptor* D + = (usb_input_terminal_descriptor*) Header; + TRACE("Clock Selector:%d >>>\n", D->terminal_id); +} + + +ClockSelector::~ClockSelector() +{ +} + + +ClockMultiplier::ClockMultiplier(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(interface, Header) +{ + usb_input_terminal_descriptor* D + = (usb_input_terminal_descriptor*) Header; + TRACE("Clock Multiplier:%d >>>\n", D->terminal_id); +} + + +ClockMultiplier::~ClockMultiplier() +{ +} + + +SampleRateConverter::SampleRateConverter(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(interface, Header) +{ + usb_input_terminal_descriptor* D + = (usb_input_terminal_descriptor*) Header; + TRACE("Sample Rate Converter:%d >>>\n", D->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 != AC_CS_INTERFACE) { + TRACE_ALWAYS("Ignore Audio Control of " + "unknown descriptor type %#04x.\n", Header->descriptor_type); + continue; + } + + _AudioControl *control = NULL; + + switch(Header->descriptor_subtype) { + default: + TRACE_ALWAYS("Ignore Audio Control of unknown " + "descriptor sub-type %#04x\n", Header->descriptor_subtype); + break; + case IDSUndefined: + TRACE_ALWAYS("Ignore Audio Control of undefined sub-type\n"); + break; + case IDSHeader: + InitACHeader(interface, Header); + break; + case IDSInputTerminal: + control = new InputTerminal(this, Header); + break; + case IDSOutputTerminal: + control = new OutputTerminal(this, Header); + break; + case IDSMixerUnit: + control = new MixerUnit(this, Header); + break; + case IDSSelectorUnit: + control = new SelectorUnit(this, Header); + break; + case IDSFeatureUnit: + control = new FeatureUnit(this, Header); + break; + case IDSEffectUnit: + if (SpecReleaseNumber() < 200) + control = new ProcessingUnit(this, Header); + else + control = new EffectUnit(this, Header); + break; + case IDSProcessingUnit: + if (SpecReleaseNumber() < 200) + control = new ExtensionUnit(this, Header); + else + control = new ProcessingUnit(this, Header); + break; + case IDSExtensionUnit: + control = new ExtensionUnit(this, Header); + break; + case IDSClockSource: + control = new ClockSource(this, Header); + break; + case IDSClockSelector: + control = new ClockSelector(this, Header); + break; + case IDSClockMultiplier: + control = new ClockMultiplier(this, Header); + break; + case IDSSampleRateConverter: + control = new SampleRateConverter(this, Header); + break; + } + + if (control != 0 && control->InitCheck() == B_OK) { + switch(control->SubType()) { + case IDSOutputTerminal: + fOutputTerminals.Put(control->SourceID(), control); + break; + case IDSInputTerminal: + 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("ADCSpecification:%#06x\n", fADCSpecification); + + if (fADCSpecification < 0x200) { + usb_audiocontrol_header_descriptor_r1* Header1 + = (usb_audiocontrol_header_descriptor_r1*) Header; + + TRACE("InterfacesCount:%d\n", Header1->in_collection); + for (size_t i = 0; i < Header1->in_collection; i++) { + fStreams.PushBack(Header1->interface_numbers[i]); + TRACE("Interface[%d] number is %d\n", i, fStreams[i]); + } + } else { + fFunctionCategory = Header->function_category; + fControlsBitmap = Header->bm_controls; + TRACE("Function Category:%#04x\n", fFunctionCategory); + TRACE("Controls Bitmap:%#04x\n", fControlsBitmap); + } + + return /*fStatus =*/ B_OK; +} + + + uint32 +AudioControlInterface::GetChannelsDescription( + Vector<multi_channel_info>& Channels, + multi_description *Description, + AudioControlsVector &Terminals) +{ + uint32 addedChannels = 0; +// multi_channel_info* Channels = Description->channels; + + for (int32 i = 0; i < Terminals.Count(); i++) { + bool bIsInputTerminal = Terminals[i]->SubType() == IDSInputTerminal; + + AudioChannelCluster* cluster = Terminals[i]->OutCluster(); + if (cluster == 0 || cluster->ChannelsCount() <= 0) { + TRACE_ALWAYS("Terminal #%d ignored due null " + "channels cluster (%08x)\n", Terminals[i]->ID(), cluster); + continue; + } + + uint32 channels = GetTerminalChannels(Channels, cluster, // index, + bIsInputTerminal ? B_MULTI_INPUT_CHANNEL : B_MULTI_OUTPUT_CHANNEL); + + if (bIsInputTerminal) + 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(); + + const uint32 locations = 18; + + struct _Designation { + uint32 ch; + uint32 bus; + } Designations[locations] = { + { 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_STEREO_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 } + }; + + // Haiku multi-aduio designations have the same bits + // as USB Audio 2.0 cluster spatial locations :-) + for (size_t i = 0; i < locations; i++) { + uint32 designation = 1 << i; + if ((cluster->ChannelsConfig() & designation) == designation) { + multi_channel_info info; + info.channel_id = Channels.Count(); + info.kind = kind; + info.designations= Designations[i].ch | Designations[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; +// multi_channel_info* Channels = Description->channels; + + // 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_ALWAYS("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; + } + + // output 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 == 0 || cluster->ChannelsCount() <= 0) { + TRACE_ALWAYS("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_ALWAYS("Not processing due NULL root control.\n"); + return; + } + + switch(rootControl->SubType()) { + case IDSOutputTerminal: + // _HarvestRecordFeatureUnits(Find(rootControl->SourceID()), Map); + break; + + case IDSSelectorUnit: + { + 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 IDSFeatureUnit: + Map.Put(rootControl->ID(), rootControl); + break; + } +} + + +void +AudioControlInterface::_InitGainLimits(multi_mix_control& Control) +{ + struct _GainInfo { + uint8 request; + int16 data; + float& value; + } gainInfos[] = { + { UAS_GET_MIN, 0, Control.gain.min_gain }, + { UAS_GET_MAX, 0, Control.gain.max_gain }, + { UAS_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 < _countof(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_ALWAYS("Request %d failed:%#08x; received %d of %d\n", + i, status, actualLength, sizeof(gainInfos[i].data)); + continue; + } + + gainInfos[i].value = static_cast<float>(gainInfos[i].data) / 256.; + } + + TRACE_ALWAYS("Control %s: from %f to %f dB, step %f dB.\n", Control.name, + Control.gain.min_gain, Control.gain.max_gain, + Control.gain.granularity); +} + + +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 MuteControl: + id = UAS_MUTE_CONTROL; + flags = B_MULTI_MIX_ENABLE; + string = S_MUTE; + break; + case VolumeControl: + id = UAS_VOLUME_CONTROL; + flags = B_MULTI_MIX_GAIN; + string = S_GAIN; + initGainLimits = true; + break; + case AutoGainControl: + id = UAS_AUTOMATIC_GAIN_CONTROL; + flags = B_MULTI_MIX_ENABLE; + name = "Auto Gain"; + break; + default: + TRACE_ALWAYS("Unsupported type %#08x ignored.\n", controlType); + return 0; + } + + multi_mix_control* Controls = Info->controls; + + if (unit->HasControl(channel, controlType)) { + uint32 masterIndex + = Controls[index].id = CTL_ID(id, channel, unit->ID(), fInterface); + Controls[index].flags = flags; + Controls[index].parent = parentIndex; + Controls[index].string = string; + if (name != NULL) + strncpy(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) + strncpy(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_ALWAYS("Feature Unit for null control ignored.\n"); + return 0; + } + + AudioChannelCluster* cluster = unit->OutCluster(); + if (cluster == 0) { + TRACE_ALWAYS("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 < _countof(channelInfos) /*&& channelsConfig > 0*/; 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 + = Controls[index].id = index; + 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(MuteControl, index, groupIndex, Info, + unit, channel, channelInfos[i].channels); + + // Gain controls may be usefull too + if (_ListFeatureUnitOption(VolumeControl, index, groupIndex, Info, + unit, channel, channelInfos[i].channels) == 0) + { + masterIndex = (i == 0) ? groupIndex : 0 ; + TRACE("channel:%d set master index to %d\n", channel, masterIndex); + } + + // Auto Gain checkbox will be listed too + _ListFeatureUnitOption(AutoGainControl, 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_ALWAYS("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() != IDSSelectorUnit) { + return; + } + + multi_mix_control* Controls = Info->controls; + + int32 recordMUX + = Controls[index].id = CTL_ID(0, 0, selector->ID(), fInterface); + Controls[index].flags = B_MULTI_MIX_MUX; + Controls[index].parent = parentGroup; + Controls[index].string = S_null; + strncpy(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) { + strncpy(Controls[index].name, + control->Name(), sizeof(Controls[index].name)); + } else { + snprintf(Controls[index].name, + sizeof(Controls[index].name), "Input #%d", i + 1); + } + index++; + } + +} + + +void +AudioControlInterface::_ListMixControlsPage(int32& index, + multi_mix_control_info* Info, + AudioControlsMap& Map, const char* Name) +{ + multi_mix_control* Controls = Info->controls; + int32 groupIndex + = Controls[index].id = index | 0x10000; + Controls[index].flags = B_MULTI_MIX_GROUP; + Controls[index].parent = 0; + strncpy(Controls[index].name, Name, sizeof(Controls[index].name)); + index++; + + int32 group = groupIndex; + for (AudioControlsIterator I = Map.Begin(); I != Map.End(); I++) { + TRACE("%s control %d listed.\n", Name, I->Value()->ID()); + switch(I->Value()->SubType()) { + case IDSFeatureUnit: + group = _ListFeatureUnitControl(index, groupIndex, + Info, I->Value()); + break; + case IDSSelectorUnit: + _ListSelectorUnitControl(index, group, Info, I->Value()); + break; + } + } +} + + +status_t +AudioControlInterface::ListMixControls(multi_mix_control_info* Info) +{ + // first harvest feature units that assigned to output USB I/O terminal(s) + AudioControlsMap RecordControlsMap; + + for (AudioControlsIterator I = fOutputTerminals.Begin(); + I != fOutputTerminals.End(); I++) + { + _Terminal* terminal = static_cast<_Terminal*>(I->Value()); + if (terminal->IsUSBIO()) { + _HarvestRecordFeatureUnits(terminal, RecordControlsMap); + } + } + + AudioControlsMap InputControlsMap; + AudioControlsMap OutputControlsMap; + + for (AudioControlsIterator I = fAudioControls.Begin(); + I != fAudioControls.End(); I++) + { + _AudioControl* control = I->Value(); + // filter out feature units + if (control->SubType() != IDSFeatureUnit) { + continue; + } + + // ignore controls that are already in the record controls map + if (RecordControlsMap.Find(control->ID()) != RecordControlsMap.End()) { + continue; + } + + _AudioControl* sourceControl = Find(control->SourceID()); + if (sourceControl != 0 && sourceControl->SubType() == IDSInputTerminal) { + 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"); + } + + 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; + switch(CS_FROM_CTLID(Info->values[i].id)) { + case UAS_VOLUME_CONTROL: + length = 2; + break; + case 0: // Selector Unit + case UAS_MUTE_CONTROL: + case UAS_AUTOMATIC_GAIN_CONTROL: + length = 1; + break; + default: + TRACE_ALWAYS("Unsupported control type %#02x ignored.\n", + CS_FROM_CTLID(Info->values[i].id)); + continue; + } + + size_t actualLength = 0; + status_t status = gUSBModule->send_request(fDevice->USBDevice(), + USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_CLASS, UAS_GET_CUR, + REQ_VALUE(Info->values[i].id), REQ_INDEX(Info->values[i].id), + length, &data, &actualLength); + + if (status != B_OK || actualLength != length) { + TRACE_ALWAYS("Request failed:%#08x; received %d of %d\n", + status, actualLength, length); + continue; + } + + switch(CS_FROM_CTLID(Info->values[i].id)) { + case UAS_VOLUME_CONTROL: + Info->values[i].gain = static_cast<float>(data) / 256.; + TRACE("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 UAS_MUTE_CONTROL: + Info->values[i].enable = data > 0; + TRACE("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 UAS_AUTOMATIC_GAIN_CONTROL: + Info->values[i].enable = data > 0; + TRACE("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; + case 0: // Selector Unit + Info->values[i].mux = data; + TRACE("Selector control %d; is %d.\n", + ID_FROM_CTLID(Info->values[i].id), + Info->values[i].mux); + break; + default: + 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; + + switch(CS_FROM_CTLID(Info->values[i].id)) { + case UAS_VOLUME_CONTROL: + data = static_cast<int16>(Info->values[i].gain * 256.); + length = 2; + TRACE("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 UAS_MUTE_CONTROL: + data = (Info->values[i].enable ? 1 : 0); + length = 1; + TRACE("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 UAS_AUTOMATIC_GAIN_CONTROL: + data = (Info->values[i].enable ? 1 : 0); + length = 1; + TRACE("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; + case 0: // Selector Unit + data = Info->values[i].mux; + length = 1; + TRACE("Selector Control %d about to set to %d.\n", + ID_FROM_CTLID(Info->values[i].id), + Info->values[i].mux); + break; + default: + TRACE_ALWAYS("Unsupported control type %#02x ignored.\n", + CS_FROM_CTLID(Info->values[i].id)); + continue; + } + + size_t actualLength = 0; + status_t status = gUSBModule->send_request(fDevice->USBDevice(), + USB_REQTYPE_INTERFACE_OUT | USB_REQTYPE_CLASS, UAS_SET_CUR, + REQ_VALUE(Info->values[i].id), REQ_INDEX(Info->values[i].id), + length, &data, &actualLength); + + if (status != B_OK || actualLength != length) { + TRACE_ALWAYS("Request failed:%#08x; send %d of %d\n", + status, actualLength, length); + continue; + } + + TRACE("Value set OK\n"); + } + + return B_OK; +} + diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/AudioControlInterface.h b/src/add-ons/kernel/drivers/audio/usb_audio/AudioControlInterface.h new file mode 100644 index 0000000..967351e --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/AudioControlInterface.h @@ -0,0 +1,319 @@ +/* + * Driver for USB Audio Device Class devices. + * Copyright (c) 2009,10,12 S.Zharski <imker@xxxxxx> + * Distributed under the terms of the MIT license. + * + */ +#ifndef _AUDIO_CONTROL_INTERFACE_H_ +#define _AUDIO_CONTROL_INTERFACE_H_ + + +#include <util/VectorMap.h> + +#include "Driver.h" +#include "USB_audio_spec.h" + + +class AudioControlInterface; + + +class AudioChannelCluster { +public: + AudioChannelCluster(); + virtual ~AudioChannelCluster(); + + uint8 ChannelsCount() { return fOutChannelsNumber; } + uint32 ChannelsConfig() { return fChannelsConfig; } + +protected: + uint8 fOutChannelsNumber; + uint32 fChannelsConfig; + uint8 fChannelNames; +}; + + +template< class __base_class_name > +class _AudioChannelCluster + : public __base_class_name, public AudioChannelCluster { +public: + _AudioChannelCluster(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header) + : + __base_class_name(interface, Header) {} + virtual ~_AudioChannelCluster() {} + + virtual AudioChannelCluster* + OutCluster() { return this; } + +}; + + +// +// base class for Audio Controls (Units and Terminals) +// +// +class _AudioControl { +public: + _AudioControl(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~_AudioControl(); + + uint8 ID() { return fID; } + uint8 SourceID() { return fSourceID; } + uint8 SubType() { return fSubType; } + status_t InitCheck() { return fStatus; }; +virtual const char* Name() { return ""; } +virtual AudioChannelCluster* OutCluster(); + +protected: + // state tracking + status_t fStatus; + AudioControlInterface* fInterface; + uint8 fSubType; + uint8 fID; + uint8 fSourceID; + uint8 fStringIndex; +}; + + + +class _Terminal : public _AudioControl { +public: + _Terminal(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~_Terminal(); + + uint16 TerminalType() { return fTerminalType; } + bool IsUSBIO(); +virtual const char* Name(); + +protected: + uint16 fTerminalType; + uint8 fAssociatedTerminal; + uint8 fClockSourceId; + uint16 fControlsBitmap; +}; + +class InputTerminal : public _AudioChannelCluster<_Terminal> { +public: + InputTerminal(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~InputTerminal(); + +protected: +}; + + +class OutputTerminal : public _Terminal { +public: + OutputTerminal(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~OutputTerminal(); + +protected: +}; + + +class MixerUnit : public _AudioChannelCluster<_AudioControl> { +public: + MixerUnit(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~MixerUnit(); + +protected: + Vector<uint8> fInputPins; + Vector<uint8> fProgrammableControls; + uint8 fControlsBitmap; +}; + + +class SelectorUnit : public _AudioControl { +public: + SelectorUnit(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~SelectorUnit(); + +virtual AudioChannelCluster* OutCluster(); +// protected: + Vector<uint8> fInputPins; + uint8 fControlsBitmap; +}; + + +class FeatureUnit : public _AudioControl { +public: + FeatureUnit(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~FeatureUnit(); + +virtual const char* Name(); + bool HasControl(int32 Channel, uint32 Control); + +// protected: + void NormalizeAndTraceChannel(int32 Channel); + + Vector<uint32> fControlBitmaps; +}; + + +class EffectUnit : public _AudioControl/*, public _AudioChannelsCluster*/ { +public: + EffectUnit(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~EffectUnit(); + +protected: +/* uint16 fProcessType; + Vector<uint8> fInputPins; + uint8 fControlsBitmap; + Vector<uint16> fModes; +*/}; + + +class ProcessingUnit : public _AudioChannelCluster<_AudioControl> { +public: + ProcessingUnit(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~ProcessingUnit(); + +protected: + uint16 fProcessType; + Vector<uint8> fInputPins; + uint8 fControlsBitmap; + Vector<uint16> fModes; +}; + + +class ExtensionUnit : public _AudioChannelCluster<_AudioControl> { +public: + ExtensionUnit(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~ExtensionUnit(); + +protected: + uint16 fExtensionCode; + Vector<uint8> fInputPins; + uint8 fControlsBitmap; +}; + + +class ClockSource : public _AudioControl { +public: + ClockSource(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~ClockSource(); + +protected: +}; + + +class ClockSelector : public _AudioControl { +public: + ClockSelector(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~ClockSelector(); + +protected: +}; + + +class ClockMultiplier : public _AudioControl { +public: + ClockMultiplier(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~ClockMultiplier(); + +protected: +}; + + +class SampleRateConverter : public _AudioControl { +public: + SampleRateConverter( + AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~SampleRateConverter(); + +protected: +}; + + +typedef VectorMap<uint32, _AudioControl*> AudioControlsMap; +typedef VectorMap<uint32, _AudioControl*>::Iterator AudioControlsIterator; + +typedef Vector<_AudioControl*> AudioControlsVector; +// typedef Vector<_AudioControl*>::Iterator AudioControlsIterator; + +class Device; + +class AudioControlInterface { +public: + AudioControlInterface(Device* device); + ~AudioControlInterface(); + + status_t InitCheck() { return fStatus; } + status_t Init(size_t interface, usb_interface_info *Interface); + + _AudioControl* Find(uint8 id); + _AudioControl* FindOutputTerminal(uint8 id); + uint16 SpecReleaseNumber() { return fADCSpecification; } + + AudioControlsMap& Controls() { return fAudioControls; } + + uint32 GetChannelsDescription( + Vector<multi_channel_info>& Channels, + multi_description *Description, + AudioControlsVector &USBTerminals); + uint32 GetBusChannelsDescription( + Vector<multi_channel_info>& Channels, + multi_description *Description); + + status_t GetMix(multi_mix_value_info *Info); + status_t SetMix(multi_mix_value_info *Info); + status_t ListMixControls(multi_mix_control_info* Info); + +protected: + status_t InitACHeader(size_t interface, + usb_audiocontrol_header_descriptor* Header); + + uint32 GetTerminalChannels( + Vector<multi_channel_info>& Channels, + AudioChannelCluster* cluster, + channel_kind kind, uint32 connectors = 0); + + void _HarvestRecordFeatureUnits(_AudioControl* rootControl, + AudioControlsMap& Map); + void _ListMixControlsPage(int32& index, + multi_mix_control_info* Info, + AudioControlsMap& Map, const char* Name); + int32 _ListFeatureUnitControl(int32& index, int32 parentId, + multi_mix_control_info* Info, + _AudioControl* control); + uint32 _ListFeatureUnitOption(uint32 controlType, + int32& index, int32 parentIndex, + multi_mix_control_info* Info, FeatureUnit* unit, + uint32 channel, uint32 channels); + void _ListSelectorUnitControl(int32& index, int32 parentGroup, + multi_mix_control_info* Info, + _AudioControl* control); + void _InitGainLimits(multi_mix_control& Control); + + size_t fInterface; + status_t fStatus; + // part of AudioControl Header description + uint16 fADCSpecification; + Vector<uint8> fStreams; + uint8 fFunctionCategory; + uint8 fControlsBitmap; + Device* fDevice; + + // map to store all controls and lookup by control ID + AudioControlsMap fAudioControls; + // map to store output terminal and lookup them by source ID + AudioControlsMap fOutputTerminals; + // map to store output terminal and lookup them by control ID + AudioControlsMap fInputTerminals; +}; + +#endif // _AUDIO_CONTROL_INTERFACE_H_ + diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/AudioFunction.cpp b/src/add-ons/kernel/drivers/audio/usb_audio/AudioFunction.cpp new file mode 100644 index 0000000..fcded98 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/AudioFunction.cpp @@ -0,0 +1,1092 @@ +/* + * Driver for USB Audio Device Class devices. + * Copyright (c) 2009,10,12 S.Zharski <imker@xxxxxx> + * Distributed under the tems of the MIT license. + * + */ + +#include "AudioFunction.h" +#include "Settings.h" +#include "Device.h" +#include "audio.h" + + +enum TerminalTypes { + // USB Terminal Types + UndefinedUSB_IO = 0x100, + StreamingUSB_IO = 0x101, + VendorUSB_IO = 0x1ff, + // Input Terminal Types + Undefined_In = 0x200, + Microphone_In = 0x201, + DesktopMic_In = 0x202, + PersonalMic_In = 0x203, + OmniMic_In = 0x204, + MicsArray_In = 0x205, + ProcMicsArray_In = 0x206, + // Output Terminal Types + Undefined_Out = 0x300, + Speaker_Out = 0x301, + HeadPhones_Out = 0x302, + HMDAudio_Out = 0x303, + DesktopSpeaker = 0x304, + RoomSpeaker = 0x305, + CommSpeaker = 0x306, + LFESpeaker = 0x307, + // Bi-directional Terminal Types + Undefined_IO = 0x400, + Handset_IO = 0x401, + Headset_IO = 0x402, + SpeakerPhone_IO = 0x403, + SpeakerPhoneES_IO = 0x404, + SpeakerPhoneEC_IO = 0x405, + // Telephony Terminal Types + UndefTelephony_IO = 0x500, + PhoneLine_IO = 0x501, + Telephone_IO = 0x502, + DownLinePhone_IO = 0x503, + // External Terminal Types + UndefinedExt_IO = 0x600, + AnalogConnector_IO = 0x601, + DAInterface_IO = 0x602, + LineConnector_IO = 0x603, + LegacyConnector_IO = 0x604, + SPDIFInterface_IO = 0x605, + DA1394Stream_IO = 0x606, + DV1394StreamSound_IO= 0x607, + ADATLightpipe_IO = 0x608, + TDIF_IO = 0x609, + MADI_IO = 0x60a, + // Embedded Terminal Types + UndefEmbedded_IO = 0x700, + LCNoiseSource_Out = 0x701, + EqualizationNoise_Out = 0x702, + CDPlayer_In = 0x703, + DAT_IO = 0x704, + DCC_IO = 0x705, + MiniDisk_IO = 0x706, + AnalogTape_IO = 0x707, + Phonograph_In = 0x708, + VCRAudio_In = 0x709, + VideoDiscAudio_In = 0x70a, + DVDAudio_In = 0x70b, + TVTunerAudio_In = 0x70c, + SatReceiverAudio_In = 0x70d, + CableTunerAudio_In = 0x70e, + DSSAudio_In = 0x70f, + Radio_Receiver_In = 0x710, + RadioTransmitter_In = 0x711, + MultiTrackRecorder_IO = 0x712, + Synthesizer_IO = 0x713, + Piano_IO = 0x714, + Guitar_IO = 0x715, + Drums_IO = 0x716, + Instrument_IO = 0x717 +}; + + +const char* +GetTerminalDescription(uint16 TerminalType) +{ + switch(TerminalType) { + // USB Terminal Types + case UndefinedUSB_IO: return "USB I/O"; + case StreamingUSB_IO: return "USB Streaming I/O"; + case VendorUSB_IO: return "Vendor USB I/O"; + // Input Terminal Types + case Undefined_In: return "Undefined Input"; + case Microphone_In: return "Microphone"; + case DesktopMic_In: return "Desktop Microphone"; + case PersonalMic_In: return "Personal Microphone"; + case OmniMic_In: return "Omni-directional Mic"; + case MicsArray_In: return "Microphone Array"; + case ProcMicsArray_In: return "Processing Mic Array"; + // Output Terminal Types + case Undefined_Out: return "Undefined Output"; + case Speaker_Out: return "Speaker"; + case HeadPhones_Out: return "Headphones"; + case HMDAudio_Out: return "Head Mounted Disp.Audio"; + case DesktopSpeaker: return "Desktop Speaker"; + case RoomSpeaker: return "Room Speaker"; + case CommSpeaker: return "Communication Speaker"; + case LFESpeaker: return "LFE Speaker"; + // Bi-directional Terminal Types + case Undefined_IO: return "Undefined I/O"; + case Handset_IO: return "Handset"; + case Headset_IO: return "Headset"; + case SpeakerPhone_IO: return "Speakerphone"; + case SpeakerPhoneES_IO: return "Echo-supp Speakerphone"; + case SpeakerPhoneEC_IO: return "Echo-cancel Speakerphone"; + // Telephony Terminal Types + case UndefTelephony_IO: return "Undefined Telephony"; + case PhoneLine_IO: return "Phone Line"; + case Telephone_IO: return "Telephone"; + case DownLinePhone_IO: return "Down Line Phone"; + // External Terminal Types + case UndefinedExt_IO: return "Undefined External I/O"; + case AnalogConnector_IO: return "Analog Connector"; + case DAInterface_IO: return "Digital Audio Interface"; + case LineConnector_IO: return "Line Connector"; + case LegacyConnector_IO: return "LegacyAudioConnector"; + case SPDIFInterface_IO: return "S/PDIF Interface"; + case DA1394Stream_IO: return "1394 DA Stream"; + case DV1394StreamSound_IO: return "1394 DV Stream Soundtrack"; + case ADATLightpipe_IO: return "Alesis DAT Stream"; + case TDIF_IO: return "Tascam Digital Interface"; + case MADI_IO: return "AES Multi-channel interface"; + // Embedded Terminal Types + case UndefEmbedded_IO: return "Undefined Embedded I/O"; + case LCNoiseSource_Out: return "Level Calibration Noise Source"; + case EqualizationNoise_Out: return "Equalization Noise"; + case CDPlayer_In: return "CD Player"; + case DAT_IO: return "DAT"; + case DCC_IO: return "DCC"; + case MiniDisk_IO: return "Mini Disk"; + case AnalogTape_IO: return "Analog Tape"; + case Phonograph_In: return "Phonograph"; + case VCRAudio_In: return "VCR Audio"; + case VideoDiscAudio_In: return "Video Disc Audio"; + case DVDAudio_In: return "DVD Audio"; + case TVTunerAudio_In: return "TV Tuner Audio"; + case SatReceiverAudio_In: return "Satellite Receiver Audio"; + case CableTunerAudio_In: return "Cable Tuner Audio"; + case DSSAudio_In: return "DSS Audio"; + case Radio_Receiver_In: return "Radio Receiver"; + case RadioTransmitter_In: return "Radio Transmitter"; + case MultiTrackRecorder_IO: return "Multi-track Recorder"; + case Synthesizer_IO: return "Synthesizer"; + case Piano_IO: return "Piano"; + case Guitar_IO: return "Guitar"; + case Drums_IO: return "Percussion Instrument"; + case Instrument_IO: return "Musical Instrument"; + } + + TRACE_ALWAYS("Unknown Terminal Type: %#06x", TerminalType); + return "Unknown"; +} + + +_AudioFunctionEntity::_AudioFunctionEntity(Device* device, size_t interface) + : + fStatus(B_NO_INIT), + fDevice(device), + fInterface(interface) +{ +} + + +_AudioFunctionEntity::~_AudioFunctionEntity() +{ + +} + + +_AudioControl::_AudioControl(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioFunctionEntity(device, interface), + fSubType(Header->descriptor_subtype), + fID(0), + fSourceID(0), + fStringIndex(0) +{ +} + + +_AudioControl::~_AudioControl() +{ +} + + +_AudioChannelsCluster* +_AudioControl::OutputCluster() +{ + if (SourceID() == 0 || fDevice == 0) { + return NULL; + } + + _AudioControl* control = fDevice->FindAudioControl(SourceID()); + if (control == 0) { + return NULL; + } + + return control->OutputCluster(); +} + + +AudioControlHeader::AudioControlHeader(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(device, interface, Header), + fADCSpecification(0), + fFunctionCategory(0), + fControlsBitmap(0) +{ + if (Header == NULL) { + return; + } + + fADCSpecification = Header->bcd_release_no; + TRACE("ADCSpecification:%#06x\n", fADCSpecification); + + if (fADCSpecification < 0x200) { + usb_audiocontrol_header_descriptor_r1* Header1 + = (usb_audiocontrol_header_descriptor_r1*) Header; + + TRACE("InterfacesCount:%d\n", Header1->in_collection); + for (size_t i = 0; i < Header1->in_collection; i++) { + fStreams.PushBack(Header1->interface_numbers[i]); + TRACE("Interface[%d] number is %d\n", i, fStreams[i]); + } + } else { + fFunctionCategory = Header->function_category; + fControlsBitmap = Header->bm_controls; + TRACE("Function Category:%#04x\n", fFunctionCategory); + TRACE("Controls Bitmap:%#04x\n", fControlsBitmap); + } + + fStatus = B_OK; +} + + +AudioControlHeader::~AudioControlHeader() +{ + +} + + +_AudioChannelsCluster::_AudioChannelsCluster() + : + fOutChannelsNumber(0), + fChannelsConfig(0), + fChannelNames(0) +{ + +} + + +_AudioChannelsCluster::~_AudioChannelsCluster() +{ + +} + + +_AudioChannelsCluster* +_AudioChannelsCluster::OutputCluster() +{ + return this; +} + + +_Terminal::_Terminal(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(device, 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) == UndefinedUSB_IO; +} + + +InputTerminal::InputTerminal(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header) + : + _Terminal(device, interface, Header) +{ + usb_input_terminal_descriptor_r1* Terminal + = (usb_input_terminal_descriptor_r1*) Header; + fID = Terminal->terminal_id; + fTerminalType = Terminal->terminal_type; + fAssociatedTerminal = Terminal->assoc_terminal; + + TRACE("Input Terminal ID:%d\n", fID); + TRACE("Terminal type:%s (%#06x)\n", + GetTerminalDescription(fTerminalType), fTerminalType); + TRACE("Assoc.terminal:%d\n", fAssociatedTerminal); + + if (device->SpecReleaseNumber() < 0x200) { + fOutChannelsNumber = Terminal->num_channels; + fChannelsConfig = Terminal->channel_config; + fChannelNames = Terminal->channel_names; + fStringIndex = Terminal->terminal; + } else { + usb_input_terminal_descriptor* Terminal + = (usb_input_terminal_descriptor*) Header; + fClockSourceId = Terminal->clock_source_id; + fOutChannelsNumber = Terminal->num_channels; + fChannelsConfig = Terminal->channel_config; + fChannelNames = Terminal->channel_names; + fControlsBitmap = Terminal->bm_controls; + fStringIndex = Terminal->terminal; + + TRACE("Clock Source ID:%d\n", fClockSourceId); + TRACE("Controls Bitmap:%#04x\n", fControlsBitmap); + } + + TRACE("Out.channels num:%d\n", fOutChannelsNumber); + TRACE("Channels config:%#06x\n", fChannelsConfig); + TRACE("Channels names:%d\n", fChannelNames); + TRACE("StringIndex:%d\n", fStringIndex); + + fStatus = B_OK; +} + + +InputTerminal::~InputTerminal() +{ +} + + +OutputTerminal::OutputTerminal(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header) + : + _Terminal(device, interface, Header) +{ + usb_output_terminal_descriptor_r1* Terminal + = (usb_output_terminal_descriptor_r1*) Header; + + fID = Terminal->terminal_id; + fTerminalType = Terminal->terminal_type; + fAssociatedTerminal = Terminal->assoc_terminal; + fSourceID = Terminal->source_id; + + TRACE("Output Terminal ID:%d\n", fID); + TRACE("Terminal type:%s (%#06x)\n", + GetTerminalDescription(fTerminalType), fTerminalType); + TRACE("Assoc.terminal:%d\n", fAssociatedTerminal); + TRACE("Source ID:%d\n", fSourceID); + + if (device->SpecReleaseNumber() < 0x200) { + fStringIndex = Terminal->terminal; + } else { + usb_output_terminal_descriptor* Terminal + = (usb_output_terminal_descriptor*) Header; + + fClockSourceId = Terminal->clock_source_id; + fControlsBitmap = Terminal->bm_controls; + fStringIndex = Terminal->terminal; + + TRACE("Clock Source ID:%d\n", fClockSourceId); + TRACE("Controls Bitmap:%#04x\n", fControlsBitmap); + } + + TRACE("StringIndex:%d\n", fStringIndex); + + fStatus = B_OK; +} + + +OutputTerminal::~OutputTerminal() +{ +} + + +MixerUnit::MixerUnit(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(device, interface, Header), + fControlsBitmap(0) +{ + usb_mixer_unit_descriptor* Mixer + = (usb_mixer_unit_descriptor*) Header; + + fID = Mixer->unit_id; + TRACE("Mixer ID:%d\n", fID); + + TRACE("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("Input pin #%d:%d\n", i, fInputPins[i]); + } + + uint8* mixerControlsData = NULL; + uint8 mixerControlsSize = 0; + + if (device->SpecReleaseNumber() < 0x200) { + usb_output_channels_descriptor_r1* OutChannels + = (usb_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); + } else { + usb_output_channels_descriptor* OutChannels + = (usb_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; + fControlsBitmap = *(mixerControlsData + mixerControlsSize); + fStringIndex = *(mixerControlsData + mixerControlsSize + 1); [ *** diff truncated: 5723 lines dropped *** ]