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 0000000000..a58bdd2966 --- /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 + * 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& 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& 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& 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(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(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(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(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(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(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 0000000000..967351ebd9 --- /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 + * Distributed under the terms of the MIT license. + * + */ +#ifndef _AUDIO_CONTROL_INTERFACE_H_ +#define _AUDIO_CONTROL_INTERFACE_H_ + + +#include + +#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 fInputPins; + Vector fProgrammableControls; + uint8 fControlsBitmap; +}; + + +class SelectorUnit : public _AudioControl { +public: + SelectorUnit(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~SelectorUnit(); + +virtual AudioChannelCluster* OutCluster(); +// protected: + Vector 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 fControlBitmaps; +}; + + +class EffectUnit : public _AudioControl/*, public _AudioChannelsCluster*/ { +public: + EffectUnit(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~EffectUnit(); + +protected: +/* uint16 fProcessType; + Vector fInputPins; + uint8 fControlsBitmap; + Vector fModes; +*/}; + + +class ProcessingUnit : public _AudioChannelCluster<_AudioControl> { +public: + ProcessingUnit(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~ProcessingUnit(); + +protected: + uint16 fProcessType; + Vector fInputPins; + uint8 fControlsBitmap; + Vector fModes; +}; + + +class ExtensionUnit : public _AudioChannelCluster<_AudioControl> { +public: + ExtensionUnit(AudioControlInterface* interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~ExtensionUnit(); + +protected: + uint16 fExtensionCode; + Vector 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 AudioControlsMap; +typedef VectorMap::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& Channels, + multi_description *Description, + AudioControlsVector &USBTerminals); + uint32 GetBusChannelsDescription( + Vector& 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& 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 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 0000000000..fcded987e2 --- /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 + * 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); + + 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(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(device, 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 (device->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() +{ +} + + +FeatureUnit::FeatureUnit(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(device, 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 (device->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; + } + + TraceChannel(device, 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 + AudioControlsIterator it = fDevice->fAudioControls.Find(fSourceID); + while (it != fDevice->fAudioControls.End()) { + _AudioControl *control = it->Value(); + if (control != 0) + break; + + 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 + it = fDevice->fOutputTerminals.Find(fID); + while (it != fDevice->fOutputTerminals.End()) { + _AudioControl *control = it->Value(); + if (control != 0) + break; + + if (control->SubType() != IDSOutputTerminal) + break; + + // USB I/O terminal is a not good candidate to use it's name + uint16 Type = static_cast(control)->TerminalType(); + if ((Type && 0xff00) == UndefinedUSB_IO) + 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::TraceChannel(Device* device, int32 Channel) +{ + if (Channel >= fControlBitmaps.Count()) { + TRACE_ALWAYS("Out of limits error of tracing channel %d\n", Channel); + return; + } + + if (Channel == 0) + TRACE("Master channel bitmap:%#x\n", fControlBitmaps[Channel]); + else + TRACE("Channel %d bitmap:%#x\n", Channel, fControlBitmaps[Channel]); + + if (device->SpecReleaseNumber() < 0x200) { + if (HasControl(Channel, MuteControl1)) + TRACE("\tMute\n"); + if (HasControl(Channel, VolumeControl1)) + TRACE("\tVolume\n"); + if (HasControl(Channel, BassControl1)) + TRACE("\tBass\n"); + if (HasControl(Channel, MidControl1)) + TRACE("\tMid\n"); + if (HasControl(Channel, TrebleControl1)) + TRACE("\tTreble\n"); + if (HasControl(Channel, GraphEqControl1)) + TRACE("\tGraphic Equalizer\n"); + if (HasControl(Channel, AutoGainControl1)) + TRACE("\tAutomatic Gain\n"); + if (HasControl(Channel, DelayControl1)) + TRACE("\tDelay\n"); + if (HasControl(Channel, BassBoostControl1)) + TRACE("\tBass Boost\n"); + if (HasControl(Channel, LoudnessControl1)) + TRACE("\tLoudness\n"); + } else { + if (HasControl(Channel, MuteControl)) + TRACE("\tMute\n"); + if (HasControl(Channel, VolumeControl)) + TRACE("\tVolume\n"); + if (HasControl(Channel, BassControl)) + TRACE("\tBass\n"); + if (HasControl(Channel, MidControl)) + TRACE("\tMid\n"); + if (HasControl(Channel, TrebleControl)) + TRACE("\tTreble\n"); + if (HasControl(Channel, GraphEqControl)) + TRACE("\tGraphic Equalizer\n"); + if (HasControl(Channel, AutoGainControl)) + TRACE("\tAutomatic Gain\n"); + if (HasControl(Channel, DelayControl)) + TRACE("\tDelay\n"); + if (HasControl(Channel, BassBoostControl)) + TRACE("\tBass Boost\n"); + if (HasControl(Channel, LoudnessControl)) + TRACE("\tLoudness\n"); + if (HasControl(Channel, InputGainControl)) + TRACE("\tInputGain\n"); + if (HasControl(Channel, InputGainPadControl)) + TRACE("\tInputGainPad\n"); + if (HasControl(Channel, PhaseInverterControl)) + TRACE("\tPhaseInverter\n"); + if (HasControl(Channel, UnderflowControl)) + TRACE("\tUnderflow\n"); + if (HasControl(Channel, OverflowControl)) + TRACE("\tOverflow\n"); + } +} + + +EffectUnit::EffectUnit(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(device, interface, Header) +{ + usb_input_terminal_descriptor* D + = (usb_input_terminal_descriptor*) Header; + TRACE("Effect Unit:%d\n", D->terminal_id); +} + + +EffectUnit::~EffectUnit() +{ +} + + +ProcessingUnit::ProcessingUnit(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(device, 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 (device->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(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(device, 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 (device->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(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(device, interface, Header) +{ + usb_input_terminal_descriptor* D + = (usb_input_terminal_descriptor*) Header; + TRACE("Clock Source:%d\n", D->terminal_id); +} + + +ClockSource::~ClockSource() +{ +} + + +ClockSelector::ClockSelector(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(device, interface, Header) +{ + usb_input_terminal_descriptor* D + = (usb_input_terminal_descriptor*) Header; + TRACE("Clock Selector:%d\n", D->terminal_id); +} + + +ClockSelector::~ClockSelector() +{ +} + + +ClockMultiplier::ClockMultiplier(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(device, interface, Header) +{ + usb_input_terminal_descriptor* D + = (usb_input_terminal_descriptor*) Header; + TRACE("Clock Multiplier:%d\n", D->terminal_id); +} + + +ClockMultiplier::~ClockMultiplier() +{ +} + + +SampleRateConverter::SampleRateConverter(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header) + : + _AudioControl(device, interface, Header) +{ + usb_input_terminal_descriptor* D + = (usb_input_terminal_descriptor*) Header; + TRACE("Sample Rate Converter:%d\n", D->terminal_id); +} + + +SampleRateConverter::~SampleRateConverter() +{ +} + + +// +// Audio Stream information entities +// +// +ASInterfaceDescriptor::ASInterfaceDescriptor(Device* device, size_t interface, + usb_as_interface_descriptor_r1* Descriptor) + : + _AudioFunctionEntity(device, interface), + fTerminalLink(0), + fDelay(0), + fFormatTag(0) +{ + fTerminalLink = Descriptor->terminal_link; + fDelay = Descriptor->delay; + fFormatTag = Descriptor->format_tag; + + TRACE("fTerminalLink:%d\n", fTerminalLink); + TRACE("fDelay:%d\n", fDelay); + TRACE("fFormatTag:%#06x\n", fFormatTag); + + fStatus = B_OK; +} + + +ASInterfaceDescriptor::~ASInterfaceDescriptor() +{ + +} + + +ASEndpointDescriptor::ASEndpointDescriptor(Device* device, size_t interface, + usb_as_cs_endpoint_descriptor* Descriptor) + : + _AudioFunctionEntity(device, interface), + fAttributes(0), + fLockDelayUnits(0), + fLockDelay(0) +{ + + fAttributes = Descriptor->attributes; + fLockDelayUnits = Descriptor->lock_delay_units; + fLockDelay = Descriptor->lock_delay; + + TRACE("fAttributes:%d\n", fAttributes); + TRACE("fLockDelayUnits:%d\n", fLockDelayUnits); + TRACE("fLockDelay:%d\n", fLockDelay); + + fStatus = B_OK; +} + + +ASEndpointDescriptor::~ASEndpointDescriptor() +{ +} + + +_ASFormatDescriptor::_ASFormatDescriptor(Device* device, size_t interface) + : + _AudioFunctionEntity(device, interface) +{ +} + + +_ASFormatDescriptor::~_ASFormatDescriptor() +{ +} + + +uint32 +_ASFormatDescriptor::GetSamFreq(uint8* freq) +{ + return freq[0] | freq[1] << 8 | freq[2] << 16; +} + + +TypeIFormatDescriptor::TypeIFormatDescriptor(Device* device, size_t interface, + usb_type_I_format_descriptor* Descriptor) + : + _ASFormatDescriptor(device, interface), + fNumChannels(0), + fSubframeSize(0), + fBitResolution(0), + fSampleFrequencyType(0) +{ + fStatus = Init(Descriptor); +} + + +TypeIFormatDescriptor::~TypeIFormatDescriptor() +{ +} + + +status_t +TypeIFormatDescriptor::Init(usb_type_I_format_descriptor* Descriptor) +{ + fNumChannels = Descriptor->nr_channels; + fSubframeSize = Descriptor->subframe_size; + fBitResolution = Descriptor->bit_resolution; + fSampleFrequencyType = Descriptor->sam_freq_type; + + if (fSampleFrequencyType == 0) { + fSampleFrequencies.PushBack( + GetSamFreq(Descriptor->sf.cont.lower_sam_freq)); + fSampleFrequencies.PushBack( + GetSamFreq(Descriptor->sf.cont.upper_sam_freq)); + } else { + for (size_t i = 0; i < fSampleFrequencyType; i++) { + fSampleFrequencies.PushBack( + GetSamFreq(Descriptor->sf.discr.sam_freq[i])); + } + } + + TRACE("fNumChannels:%d\n", fNumChannels); + TRACE("fSubframeSize:%d\n", fSubframeSize); + TRACE("fBitResolution:%d\n", fBitResolution); + TRACE("fSampleFrequencyType:%d\n", fSampleFrequencyType); + + for (int32 i = 0; i < fSampleFrequencies.Count(); i++) { + TRACE("Frequency #%d: %d\n", i, fSampleFrequencies[i]); + } + + return B_OK; +} + + +TypeIIFormatDescriptor::TypeIIFormatDescriptor(Device* device, size_t interface, + usb_type_II_format_descriptor* Descriptor) + : + _ASFormatDescriptor(device, interface), + fMaxBitRate(0), + fSamplesPerFrame(0), + fSampleFrequencyType(0), + fSampleFrequencies(0) +{ +} + + +TypeIIFormatDescriptor::~TypeIIFormatDescriptor() +{ +} + + +TypeIIIFormatDescriptor::TypeIIIFormatDescriptor(Device* device, size_t interface, + usb_type_III_format_descriptor* Descriptor) + : + TypeIFormatDescriptor(device, interface, + (usb_type_I_format_descriptor*) Descriptor) +{ + +} + + +TypeIIIFormatDescriptor::~TypeIIIFormatDescriptor() +{ +} + + +AudioStreamAlternate::AudioStreamAlternate(size_t alternate, + ASInterfaceDescriptor* interface, + ASEndpointDescriptor* endpoint, + _ASFormatDescriptor* format) + : + fAlternate(alternate), + fInterface(interface), + fEndpoint(endpoint), + fFormat(format) +{ +} + + +AudioStreamAlternate::~AudioStreamAlternate() +{ + delete fInterface; + delete fEndpoint; + delete fFormat; +} + diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/AudioFunction.h b/src/add-ons/kernel/drivers/audio/usb_audio/AudioFunction.h new file mode 100644 index 0000000000..187795f9e5 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/AudioFunction.h @@ -0,0 +1,349 @@ +/* + * Driver for USB Audio Device Class devices. + * Copyright (c) 2009,10,12 S.Zharski + * Distributed under the terms of the MIT license. + * + */ +#ifndef _AUDIO_FUNCTION_H_ +#define _AUDIO_FUNCTION_H_ + + +#include + +#include "Driver.h" +#include "USB_audio_spec.h" + + +class Device; + +// +// base class for all entities in Audio Function +// +// +class _AudioFunctionEntity { +public: + _AudioFunctionEntity(Device* device, size_t interface); + ~_AudioFunctionEntity(); + + status_t InitCheck() { return fStatus; }; + +protected: + // state tracking + status_t fStatus; + Device* fDevice; + size_t fInterface; +}; + + +class _AudioChannelsCluster { +public: + _AudioChannelsCluster(); + virtual ~_AudioChannelsCluster(); + +virtual _AudioChannelsCluster* OutputCluster(); + +protected: + uint8 fOutChannelsNumber; + uint32 fChannelsConfig; + uint8 fChannelNames; +}; + + +// +// base class for Audio Controls (Units and Terminals) +// +// +class _AudioControl : public _AudioFunctionEntity { +public: + _AudioControl(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~_AudioControl(); + + uint8 ID() { return fID; } + uint8 SourceID() { return fSourceID; } + uint8 SubType() { return fSubType; } +virtual const char* Name() { return ""; } +virtual _AudioChannelsCluster* OutputCluster(); + +protected: + // state tracking + uint8 fSubType; + uint8 fID; + uint8 fSourceID; + uint8 fStringIndex; +}; + + +class AudioControlHeader : public _AudioControl { +public: + AudioControlHeader(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~AudioControlHeader(); + +// protected: + uint16 fADCSpecification; + Vector fStreams; + uint8 fFunctionCategory; + uint8 fControlsBitmap; +}; + + +class _Terminal : public _AudioControl { +public: + _Terminal(Device* device, size_t 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 _Terminal, public _AudioChannelsCluster { +public: + InputTerminal(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~InputTerminal(); + +protected: +}; + + +class OutputTerminal : public _Terminal { +public: + OutputTerminal(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~OutputTerminal(); + +protected: +}; + + +class MixerUnit : public _AudioControl, public _AudioChannelsCluster { +public: + MixerUnit(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~MixerUnit(); + +protected: + Vector fInputPins; + Vector fProgrammableControls; + uint8 fControlsBitmap; +}; + + +class SelectorUnit : public _AudioControl { +public: + SelectorUnit(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~SelectorUnit(); + +protected: + Vector fInputPins; + uint8 fControlsBitmap; +}; + + +class FeatureUnit : public _AudioControl { +public: + FeatureUnit(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~FeatureUnit(); + +virtual const char* Name(); + bool HasControl(int32 Channel, uint32 Control); + +protected: + void TraceChannel(Device* device, int32 Channel); + + Vector fControlBitmaps; +}; + + +class EffectUnit : public _AudioControl/*, public _AudioChannelsCluster*/ { +public: + EffectUnit(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~EffectUnit(); + +protected: +/* uint16 fProcessType; + Vector fInputPins; + uint8 fControlsBitmap; + Vector fModes; +*/}; + + +class ProcessingUnit : public _AudioControl, public _AudioChannelsCluster { +public: + ProcessingUnit(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~ProcessingUnit(); + +protected: + uint16 fProcessType; + Vector fInputPins; + uint8 fControlsBitmap; + Vector fModes; +}; + + +class ExtensionUnit : public _AudioControl, public _AudioChannelsCluster { +public: + ExtensionUnit(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~ExtensionUnit(); + +protected: + uint16 fExtensionCode; + Vector fInputPins; + uint8 fControlsBitmap; +}; + + +class ClockSource : public _AudioControl { +public: + ClockSource(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~ClockSource(); + +protected: +}; + + +class ClockSelector : public _AudioControl { +public: + ClockSelector(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~ClockSelector(); + +protected: +}; + + +class ClockMultiplier : public _AudioControl { +public: + ClockMultiplier(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~ClockMultiplier(); + +protected: +}; + + +class SampleRateConverter : public _AudioControl { +public: + SampleRateConverter(Device* device, size_t interface, + usb_audiocontrol_header_descriptor* Header); + virtual ~SampleRateConverter(); +protected: +}; + + +// +// Audio Streaming Interface information entities +// +// +class ASInterfaceDescriptor : public _AudioFunctionEntity { +public: + ASInterfaceDescriptor(Device* device, size_t interface, + usb_as_interface_descriptor_r1* Descriptor); + ~ASInterfaceDescriptor(); +// protected: + uint8 fTerminalLink; + uint8 fDelay; + uint16 fFormatTag; +}; + + +class ASEndpointDescriptor : public _AudioFunctionEntity { +public: + ASEndpointDescriptor(Device* device, size_t interface, + usb_as_cs_endpoint_descriptor* Descriptor); + ~ASEndpointDescriptor(); +// protected: + uint8 fAttributes; + uint8 fLockDelayUnits; + uint16 fLockDelay; +}; + + +class _ASFormatDescriptor : public _AudioFunctionEntity { +public: + _ASFormatDescriptor(Device* device, size_t interface); + virtual ~_ASFormatDescriptor(); + +// protected: + uint32 GetSamFreq(uint8* freq); +}; + + +class TypeIFormatDescriptor : public _ASFormatDescriptor { +public: + TypeIFormatDescriptor(Device* device, size_t interface, + usb_type_I_format_descriptor* Descriptor); + virtual ~TypeIFormatDescriptor(); + + status_t Init(usb_type_I_format_descriptor* Descriptor); + +// protected: + uint8 fNumChannels; + uint8 fSubframeSize; + uint8 fBitResolution; + uint8 fSampleFrequencyType; + Vector fSampleFrequencies; +}; + + +class TypeIIFormatDescriptor : public _ASFormatDescriptor { +public: + TypeIIFormatDescriptor(Device* device, size_t interface, + usb_type_II_format_descriptor* Descriptor); + virtual ~TypeIIFormatDescriptor(); + +// protected: + uint16 fMaxBitRate; + uint16 fSamplesPerFrame; + uint8 fSampleFrequencyType; + Vector fSampleFrequencies; +}; + + +class TypeIIIFormatDescriptor : public TypeIFormatDescriptor { +public: + TypeIIIFormatDescriptor(Device* device, size_t interface, + usb_type_III_format_descriptor* Descriptor); + virtual ~TypeIIIFormatDescriptor(); + +// protected: +}; + + +class AudioStreamAlternate { +public: + AudioStreamAlternate(size_t alternate, + ASInterfaceDescriptor* interface, + ASEndpointDescriptor* endpoint, + _ASFormatDescriptor* format); + ~AudioStreamAlternate(); + + ASInterfaceDescriptor* Interface() { return fInterface; } + ASEndpointDescriptor* Endpoint() { return fEndpoint; } + _ASFormatDescriptor* Format() { return fFormat; } + +protected: + size_t fAlternate; + ASInterfaceDescriptor* fInterface; + ASEndpointDescriptor* fEndpoint; + _ASFormatDescriptor* fFormat; +}; + +#endif // _AUDIO_FUNCTION_H_ + diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/AudioStreamingInterface.cpp b/src/add-ons/kernel/drivers/audio/usb_audio/AudioStreamingInterface.cpp new file mode 100644 index 0000000000..0c9114003a --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/AudioStreamingInterface.cpp @@ -0,0 +1,406 @@ +/* + * Driver for USB Audio Device Class devices. + * Copyright (c) 2009,10,12 S.Zharski + * Distributed under the tems of the MIT license. + * + */ + +#include "AudioStreamingInterface.h" +#include "Settings.h" +#include "Device.h" +#include "audio.h" + + +// +// Audio Stream information entities +// +// +ASInterfaceDescriptor::ASInterfaceDescriptor(/*Device* device, size_t interface,*/ + usb_as_interface_descriptor_r1* Descriptor) + : +// _AudioFunctionEntity(device, interface), + fTerminalLink(0), + fDelay(0), + fFormatTag(0) +{ + fTerminalLink = Descriptor->terminal_link; + fDelay = Descriptor->delay; + fFormatTag = Descriptor->format_tag; + + TRACE("fTerminalLink:%d\n", fTerminalLink); + TRACE("fDelay:%d\n", fDelay); + TRACE("fFormatTag:%#06x\n", fFormatTag); + + // fStatus = B_OK; +} + + +ASInterfaceDescriptor::~ASInterfaceDescriptor() +{ + +} + + +ASEndpointDescriptor::ASEndpointDescriptor(usb_endpoint_descriptor* Endpoint, + usb_as_cs_endpoint_descriptor* Descriptor) + : + fAttributes(0), + fLockDelayUnits(0), + fLockDelay(0), + fMaxPacketSize(0), + fEndpointAddress(0) +{ +// usb_audiocontrol_header_descriptor *Header +// = (usb_audiocontrol_header_descriptor *)Interface->generic[i]; + + fAttributes = Descriptor->attributes; + fLockDelayUnits = Descriptor->lock_delay_units; + fLockDelay = Descriptor->lock_delay; + +// usb_endpoint_descriptor* endpoint = Interface->endpoint[0]->descr; + fEndpointAddress = Endpoint->endpoint_address; + fMaxPacketSize = Endpoint->max_packet_size; + + TRACE("fAttributes:%d\n", fAttributes); + TRACE("fLockDelayUnits:%d\n", fLockDelayUnits); + TRACE("fLockDelay:%d\n", fLockDelay); + TRACE("fMaxPacketSize:%d\n", fMaxPacketSize); + TRACE("fEndpointAddress:%#02x\n", fEndpointAddress); +} + + +ASEndpointDescriptor::~ASEndpointDescriptor() +{ +} + + +_ASFormatDescriptor::_ASFormatDescriptor(usb_type_I_format_descriptor* Descriptor) + : + /*_AudioFunctionEntity(device, interface)*/ + fFormatType(UAF_FORMAT_TYPE_UNDEFINED) +{ + fFormatType = Descriptor->format_type; +} + + +_ASFormatDescriptor::~_ASFormatDescriptor() +{ +} + + +uint32 +_ASFormatDescriptor::GetSamFreq(uint8* freq) +{ + return freq[0] | freq[1] << 8 | freq[2] << 16; +} + + +TypeIFormatDescriptor::TypeIFormatDescriptor(/*Device* device, size_t interface,*/ + usb_type_I_format_descriptor* Descriptor) + : + _ASFormatDescriptor(Descriptor), + fNumChannels(0), + fSubframeSize(0), + fBitResolution(0), + fSampleFrequencyType(0) +{ + /*fStatus =*/ Init(Descriptor); +} + + +TypeIFormatDescriptor::~TypeIFormatDescriptor() +{ +} + + +status_t +TypeIFormatDescriptor::Init(usb_type_I_format_descriptor* Descriptor) +{ + fNumChannels = Descriptor->nr_channels; + fSubframeSize = Descriptor->subframe_size; + fBitResolution = Descriptor->bit_resolution; + fSampleFrequencyType = Descriptor->sam_freq_type; + + if (fSampleFrequencyType == 0) { + fSampleFrequencies.PushBack( + GetSamFreq(Descriptor->sf.cont.lower_sam_freq)); + fSampleFrequencies.PushBack( + GetSamFreq(Descriptor->sf.cont.upper_sam_freq)); + } else { + for (size_t i = 0; i < fSampleFrequencyType; i++) { + fSampleFrequencies.PushBack( + GetSamFreq(Descriptor->sf.discr.sam_freq[i])); + } + } + + TRACE("fNumChannels:%d\n", fNumChannels); + TRACE("fSubframeSize:%d\n", fSubframeSize); + TRACE("fBitResolution:%d\n", fBitResolution); + TRACE("fSampleFrequencyType:%d\n", fSampleFrequencyType); + + for (int32 i = 0; i < fSampleFrequencies.Count(); i++) { + TRACE("Frequency #%d: %d\n", i, fSampleFrequencies[i]); + } + + return B_OK; +} + + +TypeIIFormatDescriptor::TypeIIFormatDescriptor(/*Device* device, size_t interface,*/ + usb_type_II_format_descriptor* Descriptor) + : + _ASFormatDescriptor((usb_type_I_format_descriptor*)Descriptor), + fMaxBitRate(0), + fSamplesPerFrame(0), + fSampleFrequencyType(0), + fSampleFrequencies(0) +{ +} + + +TypeIIFormatDescriptor::~TypeIIFormatDescriptor() +{ +} + + +TypeIIIFormatDescriptor::TypeIIIFormatDescriptor(/*Device* device, size_t interface,*/ + usb_type_III_format_descriptor* Descriptor) + : + TypeIFormatDescriptor(/*device, interface, */ + (usb_type_I_format_descriptor*) Descriptor) +{ + +} + + +TypeIIIFormatDescriptor::~TypeIIIFormatDescriptor() +{ +} + + +AudioStreamAlternate::AudioStreamAlternate(size_t alternate, + ASInterfaceDescriptor* interface, + ASEndpointDescriptor* endpoint, + _ASFormatDescriptor* format) + : + fAlternate(alternate), + fInterface(interface), + fEndpoint(endpoint), + fFormat(format) +{ +} + + +AudioStreamAlternate::~AudioStreamAlternate() +{ + delete fInterface; + delete fEndpoint; + delete fFormat; +} + + +AudioStreamingInterface::AudioStreamingInterface( + AudioControlInterface* controlInterface, + size_t interface, usb_interface_list *List) + : + fInterface(interface), + fControlInterface(controlInterface), + fIsInput(false), + fActiveAlternate(0) +{ + TRACE_ALWAYS("if[%d]:alt_count:%d\n", interface, List->alt_count); + + for (size_t alt = 0; alt < List->alt_count; alt++) { + ASInterfaceDescriptor* ASInterface = NULL; + ASEndpointDescriptor* ASEndpoint = NULL; + _ASFormatDescriptor* ASFormat = NULL; + + usb_interface_info *Interface = &List->alt[alt]; + + TRACE_ALWAYS("if[%d]:alt[%d]:descrs_count:%d\n", + interface, alt, Interface->generic_count); + 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) { + switch(Header->descriptor_subtype) { + case UAS_AS_GENERAL: + if (ASInterface == 0) { + ASInterface = new ASInterfaceDescriptor( + /*this, interface, */ + (usb_as_interface_descriptor_r1*) Header); + } else + TRACE_ALWAYS("Duplicate AStream interface ignored.\n"); + break; + case UAS_FORMAT_TYPE: + if (ASFormat == 0) { + ASFormat = new TypeIFormatDescriptor( + (usb_type_I_format_descriptor*) Header); + } else + TRACE_ALWAYS("Duplicate AStream format ignored.\n"); + break; + default: + TRACE_ALWAYS("Ignore AStream descr subtype %#04x\n", + Header->descriptor_subtype); + break; + } + continue; + } + + if (Header->descriptor_type == AC_CS_ENDPOINT) { + if (ASEndpoint == 0) { + usb_endpoint_descriptor* Endpoint + = Interface->endpoint[0].descr; + ASEndpoint = new ASEndpointDescriptor(Endpoint, + (usb_as_cs_endpoint_descriptor*)Header); + } else + TRACE_ALWAYS("Duplicate AStream endpoint ignored.\n"); + continue; + } + + TRACE_ALWAYS("Ignore Audio Stream of " + "unknown descriptor type %#04x.\n", Header->descriptor_type); + } + + fAlternates.Add(new AudioStreamAlternate(alt, ASInterface, + ASEndpoint, ASFormat)); + } +} + + +AudioStreamingInterface::~AudioStreamingInterface() +{ + // alternates of the streams +// StreamAlternatesVector fAlternates; +// size_t fActiveAlternate; + // we own stream header objects too, so free them + for (StreamAlternatesIterator I = fAlternates.Begin(); + I != fAlternates.End(); I++) { + delete *I; + } + fAlternates.MakeEmpty(); +} + + +uint8 +AudioStreamingInterface::TerminalLink() +{ + if (fAlternates[fActiveAlternate]->Interface() != 0) { + return fAlternates[fActiveAlternate]->Interface()->fTerminalLink; + } + + return 0; +} + + +AudioChannelCluster* +AudioStreamingInterface::ChannelCluster() +{ + _AudioControl* control = fControlInterface->Find(TerminalLink()); + if (control == 0) { + TRACE_ALWAYS("Control was not found for terminal id:%d.\n", + TerminalLink()); + return NULL; + } + + return control->OutCluster(); +} + + +/* +ASInterfaceDescriptor* +Stream::ASInterface() +{ + return fAlternates[fActiveAlternate]->Interface(); +} + + +_ASFormatDescriptor* +Stream::ASFormat() +{ + return fAlternates[fActiveAlternate]->Format(); +} */ + +void +AudioStreamingInterface::GetFormatsAndRates(multi_description *Description) +{ + // TODO: fIsInput ?????? + Description->interface_flags + |= fIsInput ? B_MULTI_INTERFACE_RECORD : B_MULTI_INTERFACE_PLAYBACK; + + uint32 rates = 0; + uint32 formats = 0; + + TypeIFormatDescriptor* format + = static_cast( + fAlternates[fActiveAlternate]->Format()); + + if (format == NULL || fAlternates[fActiveAlternate]->Interface() == NULL) { + TRACE_ALWAYS("Ignore alternate %d due format " + "%#08x or interface %#08x null.\n", fActiveAlternate, format, + fAlternates[fActiveAlternate]->Interface()); + } + + if (format->fSampleFrequencyType == 0) { // continuous frequencies + rates = B_SR_CVSR; + Description->min_cvsr_rate = float(format->fSampleFrequencies[0]); + Description->max_cvsr_rate = float(format->fSampleFrequencies[1]); + } else { + for (int i = 0; i < format->fSampleFrequencies.Count(); i++) { + switch(format->fSampleFrequencies[i]) { + case 8000: rates |= B_SR_8000; break; + case 11025: rates |= B_SR_11025; break; + case 12000: rates |= B_SR_12000; break; + case 16000: rates |= B_SR_16000; break; + case 22050: rates |= B_SR_22050; break; + case 24000: rates |= B_SR_24000; break; + case 32000: rates |= B_SR_32000; break; + case 44100: rates |= B_SR_44100; break; + case 48000: rates |= B_SR_48000; break; + case 64000: rates |= B_SR_64000; break; + case 88200: rates |= B_SR_88200; break; + case 96000: rates |= B_SR_96000; break; + case 176400: rates |= B_SR_176400; break; + case 192000: rates |= B_SR_192000; break; + case 384000: rates |= B_SR_384000; break; + case 1536000: rates |= B_SR_1536000; break; + default: + TRACE_ALWAYS("Ignore unsupported " + "sample rate %d for alternate %d.\n", + format->fSampleFrequencies[i], fActiveAlternate); + break; + } + } + } + + switch (fAlternates[fActiveAlternate]->Interface()->fFormatTag) { + case UAF_PCM8: formats = B_FMT_8BIT_U; break; + case UAF_IEEE_FLOAT: formats = B_FMT_FLOAT; break; + case UAF_PCM: + switch(format->fBitResolution) { + case 8: formats = B_FMT_8BIT_S; break; + case 16: formats = B_FMT_16BIT; break; + case 18: formats = B_FMT_18BIT; break; + case 20: formats = B_FMT_20BIT; break; + case 24: formats = B_FMT_24BIT; break; + case 32: formats = B_FMT_32BIT; break; + break; + default: + TRACE_ALWAYS("Ignore unsupported " + "bit resolution %d for alternate %d.\n", + format->fBitResolution, fActiveAlternate); + break; + } + break; + } + + if (fIsInput) { + Description->input_rates = rates; + Description->input_formats = formats; + } else { + Description->output_rates = rates; + Description->output_formats = formats; + } +} + diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/AudioStreamingInterface.h b/src/add-ons/kernel/drivers/audio/usb_audio/AudioStreamingInterface.h new file mode 100644 index 0000000000..5e89f5ba51 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/AudioStreamingInterface.h @@ -0,0 +1,156 @@ +/* + * Driver for USB Audio Device Class devices. + * Copyright (c) 2009,10,12 S.Zharski + * Distributed under the terms of the MIT license. + * + */ +#ifndef _AUDIO_STREAMING_INTERFACE_H_ +#define _AUDIO_STREAMING_INTERFACE_H_ + + +#include "Driver.h" +#include "AudioControlInterface.h" + +#include +#include "USB_audio_spec.h" + + +// +// Audio Streaming Interface information entities +// +// +class ASInterfaceDescriptor /*: public _AudioFunctionEntity*/ { +public: + ASInterfaceDescriptor(/*Device* device, size_t interface,*/ + usb_as_interface_descriptor_r1* Descriptor); + ~ASInterfaceDescriptor(); + +// protected: + uint8 fTerminalLink; + uint8 fDelay; + uint16 fFormatTag; +}; + + +class ASEndpointDescriptor /*: public _AudioFunctionEntity*/ { +public: + ASEndpointDescriptor( + usb_endpoint_descriptor* Endpoint, + usb_as_cs_endpoint_descriptor* Descriptor); + ~ASEndpointDescriptor(); + +// protected: + uint8 fAttributes; + uint8 fLockDelayUnits; + uint16 fLockDelay; + uint16 fMaxPacketSize; + uint8 fEndpointAddress; +}; + + +class _ASFormatDescriptor /*: public _AudioFunctionEntity*/ { +public: + _ASFormatDescriptor( + usb_type_I_format_descriptor* Descriptor); + virtual ~_ASFormatDescriptor(); + +// protected: + uint8 fFormatType; + uint32 GetSamFreq(uint8* freq); +}; + + +class TypeIFormatDescriptor : public _ASFormatDescriptor { +public: + TypeIFormatDescriptor(/*Device* device, size_t interface,*/ + usb_type_I_format_descriptor* Descriptor); + virtual ~TypeIFormatDescriptor(); + + status_t Init(usb_type_I_format_descriptor* Descriptor); + +// protected: + uint8 fNumChannels; + uint8 fSubframeSize; + uint8 fBitResolution; + uint8 fSampleFrequencyType; + Vector fSampleFrequencies; +}; + + +class TypeIIFormatDescriptor : public _ASFormatDescriptor { +public: + TypeIIFormatDescriptor(/*Device* device, size_t interface,*/ + usb_type_II_format_descriptor* Descriptor); + virtual ~TypeIIFormatDescriptor(); + +// protected: + uint16 fMaxBitRate; + uint16 fSamplesPerFrame; + uint8 fSampleFrequencyType; + Vector fSampleFrequencies; +}; + + +class TypeIIIFormatDescriptor : public TypeIFormatDescriptor { +public: + TypeIIIFormatDescriptor(/*Device* device, size_t interface, */ + usb_type_III_format_descriptor* Descriptor); + virtual ~TypeIIIFormatDescriptor(); + +// protected: +}; + + +class AudioStreamAlternate { +public: + AudioStreamAlternate(size_t alternate, + ASInterfaceDescriptor* interface, + ASEndpointDescriptor* endpoint, + _ASFormatDescriptor* format); + ~AudioStreamAlternate(); + + ASInterfaceDescriptor* Interface() { return fInterface; } + ASEndpointDescriptor* Endpoint() { return fEndpoint; } + _ASFormatDescriptor* Format() { return fFormat; } + +protected: + size_t fAlternate; + ASInterfaceDescriptor* fInterface; + ASEndpointDescriptor* fEndpoint; + _ASFormatDescriptor* fFormat; +}; + + +typedef Vector StreamAlternatesVector; +typedef Vector::Iterator StreamAlternatesIterator; + + +class AudioStreamingInterface { +public: + AudioStreamingInterface( + AudioControlInterface* controlInterface, + size_t interface, usb_interface_list *List); + ~AudioStreamingInterface(); + + // status_t InitCheck() { return fStatus; } + uint8 TerminalLink(); + bool IsInput() { return fIsInput; } + + AudioChannelCluster* ChannelCluster(); + + void GetFormatsAndRates(multi_description *Description); + +protected: + size_t fInterface; + AudioControlInterface* fControlInterface; + + // status_t fStatus; + bool fIsInput; + // alternates of the streams + StreamAlternatesVector fAlternates; + size_t fActiveAlternate; +}; + + +#endif // _AUDIO_STREAMING_INTERFACE_H_ + diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/Device.cpp b/src/add-ons/kernel/drivers/audio/usb_audio/Device.cpp new file mode 100644 index 0000000000..271922432f --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/Device.cpp @@ -0,0 +1,778 @@ +/* + * Driver for USB Audio Device Class devices. + * Copyright (c) 2009,10,12 S.Zharski + * Distributed under the terms of the MIT license. + * + */ + +#include "Driver.h" +#include "Device.h" +#include "Settings.h" +//#include "audio.h" +#include "AudioStreamingInterface.h" +// #include "StreamFormats.h" + +#include + +Device::Device(usb_device device) + : + fStatus(B_ERROR), + fOpen(false), + fRemoved(false), + fInsideNotify(0), + fDevice(device), + fNonBlocking(false), + fAudioControl(this), + fControlEndpoint(0), + fInStreamEndpoint(0), + fOutStreamEndpoint(0), + fNotifyReadSem(-1), + fNotifyWriteSem(-1), + fNotifyBuffer(NULL), + fNotifyBufferLength(0), + fBuffersReadySem(-1) +{ + const usb_device_descriptor* deviceDescriptor + = gUSBModule->get_device_descriptor(device); + + if (deviceDescriptor == NULL) { + TRACE_ALWAYS("Error of getting USB device descriptor.\n"); + return; + } + + fVendorID = deviceDescriptor->vendor_id; + fProductID = deviceDescriptor->product_id; + + fNotifyReadSem = create_sem(0, DRIVER_NAME"_notify_read"); + if (fNotifyReadSem < B_OK) { + TRACE_ALWAYS("Error of creating read notify semaphore:%#010x\n", + fNotifyReadSem); + return; + } + + fNotifyWriteSem = create_sem(0, DRIVER_NAME"_notify_write"); + if (fNotifyWriteSem < B_OK) { + TRACE_ALWAYS("Error of creating write notify semaphore:%#010x\n", + fNotifyWriteSem); + return; + } + + fBuffersReadySem = create_sem(0, DRIVER_NAME "_buffers_ready"); + if (fBuffersReadySem < B_OK) { + TRACE_ALWAYS("Error of creating ready buffers semaphore:%#010x\n", + fBuffersReadySem); + return; + } + + if (_SetupEndpoints() != B_OK) { + return; + } + + // must be set in derived class constructor + fStatus = B_OK; +} + + +Device::~Device() +{ + // we have to clear because we own those objects here +/* + 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(); +*/ + // free stream objects too. + for (AudioStreamsIterator I = fStreams.Begin(); + I != fStreams.End(); I++) { + delete *I; + } + fStreams.MakeEmpty(); + + if (fNotifyReadSem >= B_OK) + delete_sem(fNotifyReadSem); + if (fNotifyWriteSem >= B_OK) + delete_sem(fNotifyWriteSem); + + if (fBuffersReadySem > B_OK) + delete_sem(fBuffersReadySem); + +// if (!fRemoved) // ??? +// gUSBModule->cancel_queued_transfers(fNotifyEndpoint); + + if (fNotifyBuffer) + free(fNotifyBuffer); +} + + +status_t +Device::Open(uint32 flags) +{ + if (fOpen) + return B_BUSY; + if (fRemoved) + return B_ERROR; + + status_t result = StartDevice(); + if (result != B_OK) { + return result; + } + + // setup state notifications +/* result = gUSBModule->queue_interrupt(fNotifyEndpoint, fNotifyBuffer, + fNotifyBufferLength, _NotifyCallback, this); + if (result != B_OK) { + TRACE_ALWAYS("Error of requesting notify interrupt:%#010x\n", result); + return result; + } +*/ + + fNonBlocking = (flags & O_NONBLOCK) == O_NONBLOCK; + fOpen = true; + return result; +} + + +status_t +Device::Close() +{ + if (fRemoved) { + fOpen = false; + return B_OK; + } + + for (int i = 0; i < fStreams.Count(); i++) { + fStreams[i]->Stop(); + } + + // wait until possible notification handling finished... + while (atomic_add(&fInsideNotify, 0) != 0) + snooze(100); + gUSBModule->cancel_queued_transfers(fControlEndpoint); + gUSBModule->cancel_queued_transfers(fInStreamEndpoint); + gUSBModule->cancel_queued_transfers(fOutStreamEndpoint); + + fOpen = false; + + return StopDevice(); +} + + +status_t +Device::Free() +{ + return B_OK; +} + + +status_t +Device::Read(uint8 *buffer, size_t *numBytes) +{ + *numBytes = 0; + return B_IO_ERROR; +} + + +status_t +Device::Write(const uint8 *buffer, size_t *numBytes) +{ + *numBytes = 0; + return B_IO_ERROR; +} + + +status_t +Device::Control(uint32 op, void *buffer, size_t length) +{ + switch (op) { + case B_MULTI_GET_DESCRIPTION: + return _MultiGetDescription((multi_description*)buffer); + + case B_MULTI_GET_EVENT_INFO: + TRACE(("B_MULTI_GET_EVENT_INFO\n")); + return B_ERROR; + + case B_MULTI_SET_EVENT_INFO: + TRACE(("B_MULTI_SET_EVENT_INFO\n")); + return B_ERROR; + + case B_MULTI_GET_EVENT: + TRACE(("B_MULTI_GET_EVENT\n")); + return B_ERROR; + + case B_MULTI_GET_ENABLED_CHANNELS: + return _MultiGetEnabledChannels((multi_channel_enable*)buffer); + + case B_MULTI_SET_ENABLED_CHANNELS: + return _MultiSetEnabledChannels((multi_channel_enable*)buffer); + + case B_MULTI_GET_GLOBAL_FORMAT: + return _MultiGetGlobalFormat((multi_format_info*)buffer); + + case B_MULTI_SET_GLOBAL_FORMAT: + return _MultiSetGlobalFormat((multi_format_info*)buffer); + + case B_MULTI_GET_CHANNEL_FORMATS: + TRACE(("B_MULTI_GET_CHANNEL_FORMATS\n")); + return B_ERROR; + + case B_MULTI_SET_CHANNEL_FORMATS: /* only implemented if possible */ + TRACE(("B_MULTI_SET_CHANNEL_FORMATS\n")); + return B_ERROR; + + case B_MULTI_GET_MIX: + return _MultiGetMix((multi_mix_value_info *)buffer); + + case B_MULTI_SET_MIX: + return _MultiSetMix((multi_mix_value_info *)buffer); + + case B_MULTI_LIST_MIX_CHANNELS: + TRACE(("B_MULTI_LIST_MIX_CHANNELS\n")); + return B_ERROR; + + case B_MULTI_LIST_MIX_CONTROLS: + return _MultiListMixControls((multi_mix_control_info*)buffer); + + case B_MULTI_LIST_MIX_CONNECTIONS: + TRACE(("B_MULTI_LIST_MIX_CONNECTIONS\n")); + return B_ERROR; + + case B_MULTI_GET_BUFFERS: + // Fill out the struct for the first time; doesn't start anything. + return _MultiGetBuffers((multi_buffer_list*)buffer); + + case B_MULTI_SET_BUFFERS: + // Set what buffers to use, if the driver supports soft buffers. + TRACE(("B_MULTI_SET_BUFFERS\n")); + return B_ERROR; /* we do not support soft buffers */ + + case B_MULTI_SET_START_TIME: + // When to actually start + TRACE(("B_MULTI_SET_START_TIME\n")); + return B_ERROR; + + case B_MULTI_BUFFER_EXCHANGE: + // stop and go are derived from this being called + return _MultiBufferExchange((multi_buffer_info*)buffer); + + case B_MULTI_BUFFER_FORCE_STOP: + // force stop of playback, nothing in data + return _MultiBufferForceStop(); + + default: + TRACE_ALWAYS("Unhandled IOCTL catched: %#010x\n", op); + } + + return B_DEV_INVALID_IOCTL; +} + + +void +Device::Removed() +{ + fRemoved = true; +// fHasConnection = false; + + // the notify hook is different from the read and write hooks as it does + // itself schedule traffic (while the other hooks only release a semaphore + // to notify another thread which in turn safly checks for the removed + // case) - so we must ensure that we are not inside the notify hook anymore + // before returning, as we would otherwise violate the promise not to use + // any of the pipes after returning from the removed hook + while (atomic_add(&fInsideNotify, 0) != 0) + snooze(100); + + gUSBModule->cancel_queued_transfers(fControlEndpoint); + gUSBModule->cancel_queued_transfers(fInStreamEndpoint); + gUSBModule->cancel_queued_transfers(fOutStreamEndpoint); +/* + if (fLinkStateChangeSem >= B_OK) + release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE); +*/ +} + + +status_t +Device::SetupDevice(bool deviceReplugged) +{ + return B_OK; +} + + +status_t +Device::CompareAndReattach(usb_device device) +{ + const usb_device_descriptor *deviceDescriptor + = gUSBModule->get_device_descriptor(device); + + if (deviceDescriptor == NULL) { + TRACE_ALWAYS("Error of getting USB device descriptor.\n"); + return B_ERROR; + } + + if (deviceDescriptor->vendor_id != fVendorID + && deviceDescriptor->product_id != fProductID) { + // this certainly isn't the same device + return B_BAD_VALUE; + } + + // this is the same device that was replugged - clear the removed state, + // re- setup the endpoints and transfers and open the device if it was + // previously opened + fDevice = device; + fRemoved = false; + status_t result = _SetupEndpoints(); + if (result != B_OK) { + fRemoved = true; + return result; + } + + // we need to setup hardware on device replug + result = SetupDevice(true); + if (result != B_OK) { + return result; + } + + if (fOpen) { + fOpen = false; + result = Open(fNonBlocking ? O_NONBLOCK : 0); + } + + return result; +} + + +status_t +Device::_MultiGetDescription(multi_description *multiDescription) +{ + multi_description Description; + if (user_memcpy(&Description, multiDescription, + sizeof(multi_description)) != B_OK) { + return B_BAD_ADDRESS; + } + + Description.interface_version = B_CURRENT_INTERFACE_VERSION; + Description.interface_minimum = B_CURRENT_INTERFACE_VERSION; + + strncpy(Description.friendly_name, "USB Audio", // TODO: ???? + sizeof(Description.friendly_name)); + + strncpy(Description.vendor_info, "S.Zharski", + sizeof(Description.vendor_info)); + + Description.output_channel_count = 0; + Description.input_channel_count = 0; + Description.output_bus_channel_count = 0; + Description.input_bus_channel_count = 0; + Description.aux_bus_channel_count = 0; + + Description.output_rates = 0; + Description.input_rates = 0; + + Description.min_cvsr_rate = 0; + Description.max_cvsr_rate = 0; + + Description.output_formats = 0; + Description.input_formats = 0; + Description.lock_sources = B_MULTI_LOCK_INTERNAL; + Description.timecode_sources = 0; + Description.interface_flags = 0; + Description.start_latency = 3000; + + Description.control_panel[0] = '\0'; + + AudioControlsVector USBTerminals; + + // channels (USB I/O terminals) are already in fStreams + // in outputs->inputs order, use them. + for (int i = 0; i < fStreams.Count(); i++) { + uint8 id = fStreams[i]->TerminalLink(); + _AudioControl *control = fAudioControl.Find(id); + // if (control->SubType() == IDSOutputTerminal) { + // USBTerminals.PushFront(control); + // fStreams[i]->GetFormatsAndRates(Description); + // } else + // if (control->SubType() == IDSInputTerminal) { + USBTerminals.PushBack(control); + fStreams[i]->GetFormatsAndRates(&Description); + // } + } + + // int32 index = 0; + Vector Channels; + /*uint32 channels =*/ fAudioControl.GetChannelsDescription(Channels, &Description, USBTerminals); + /*uint32 bus_channels =*/ fAudioControl.GetBusChannelsDescription(Channels, &Description ); + + // Description.request_channel_count = channels + bus_channels; + + TraceMultiDescription(&Description, Channels); + + if (user_memcpy(multiDescription, &Description, + sizeof(multi_description)) != B_OK) { + return B_BAD_ADDRESS; + } + + // if (Description.request_channel_count >= + // (int)(sizeof(channel_descriptions) / sizeof(channel_descriptions[0]))) + // { + if (user_memcpy(multiDescription->channels, + &Channels[0], min_c(Channels.Count(), + Description.request_channel_count)) != B_OK) + return B_BAD_ADDRESS; + // } + + return B_OK; +} + + +void +Device::TraceMultiDescription(multi_description *Description, + Vector& Channels) +{ + TRACE("interface_version:%d\n", Description->interface_version); + TRACE("interface_minimum:%d\n", Description->interface_minimum); + TRACE("friendly_name:%s\n", Description->friendly_name); + TRACE("vendor_info:%s\n", Description->vendor_info); + TRACE("output_channel_count:%d\n", Description->output_channel_count); + TRACE("input_channel_count:%d\n", Description->input_channel_count); + TRACE("output_bus_channel_count:%d\n", Description->output_bus_channel_count); + TRACE("input_bus_channel_count:%d\n", Description->input_bus_channel_count); + TRACE("aux_bus_channel_count:%d\n", Description->aux_bus_channel_count); + TRACE("output_rates:%#08x\n", Description->output_rates); + TRACE("input_rates:%#08x\n", Description->input_rates); + TRACE("min_cvsr_rate:%f\n", Description->min_cvsr_rate); + TRACE("max_cvsr_rate:%f\n", Description->max_cvsr_rate); + TRACE("output_formats:%#08x\n", Description->output_formats); + TRACE("input_formats:%#08x\n", Description->input_formats); + TRACE("lock_sources:%d\n", Description->lock_sources); + TRACE("timecode_sources:%d\n", Description->timecode_sources); + TRACE("interface_flags:%#08x\n", Description->interface_flags); + TRACE("start_latency:%d\n", Description->start_latency); + TRACE("control_panel:%s\n", Description->control_panel); + + // multi_channel_info* Channels = Description->channels; + // for (int i = 0; i < Description->request_channel_count; i++) { + for (int i = 0; i < Channels.Count(); i++) { + TRACE(" channel_id:%d\n", Channels[i].channel_id); + TRACE(" kind:%#02x\n", Channels[i].kind); + TRACE(" designations:%#08x\n", Channels[i].designations); + TRACE(" connectors:%#08x\n", Channels[i].connectors); + } + + TRACE("request_channel_count:%d\n\n", Description->request_channel_count); +} + + +status_t +Device::_MultiGetEnabledChannels(multi_channel_enable *Enable) +{ + status_t status = B_OK; + + Enable->lock_source = B_MULTI_LOCK_INTERNAL; + + uint32 offset = 0; + for (int i = 0; i < fStreams.Count() && status == B_OK; i++) { + status = fStreams[i]->GetEnabledChannels(offset, Enable); + } + + return status; +} + + +status_t +Device::_MultiSetEnabledChannels(multi_channel_enable *Enable) +{ + status_t status = B_OK; + uint32 offset = 0; + for (int i = 0; i < fStreams.Count() && status == B_OK; i++) { + status = fStreams[i]->SetEnabledChannels(offset, Enable); + } + + return status; +} + + +status_t +Device::_MultiGetGlobalFormat(multi_format_info *Format) +{ + status_t status = B_OK; + + Format->output_latency = 0; + Format->input_latency = 0; + Format->timecode_kind = 0; + + // uint32 offset = 0; + for (int i = 0; i < fStreams.Count() && status == B_OK; i++) { + status = fStreams[i]->GetGlobalFormat(Format); + } + + return status; +} + + +status_t +Device::_MultiSetGlobalFormat(multi_format_info *Format) +{ + status_t status = B_OK; + + TRACE("output_latency:%lld\n", Format->output_latency); + TRACE("input_latency:%lld\n", Format->input_latency); + TRACE("timecode_kind:%#08x\n", Format->timecode_kind); + + // uint32 offset = 0; + for (int i = 0; i < fStreams.Count() && status == B_OK; i++) { + status = fStreams[i]->SetGlobalFormat(Format); + } + + return status; +} + + +status_t +Device::_MultiGetBuffers(multi_buffer_list* List) +{ + status_t status = B_OK; + + TRACE("info_size:%d\n" + "request_playback_buffers:%d\n" + "request_playback_channels:%d\n" + "request_playback_buffer_size:%d\n" + "request_record_buffers:%d\n" + "request_record_channels:%d\n" + "request_record_buffer_size:%d\n", + List->info_size, + List->request_playback_buffers, + List->request_playback_channels, + List->request_playback_buffer_size, + List->request_record_buffers, + List->request_record_channels, + List->request_record_buffer_size); + + for (int i = 0; i < fStreams.Count() && status == B_OK; i++) { + status = fStreams[i]->GetBuffers(List); + } + + return B_OK; +} + + +status_t +Device::_MultiBufferExchange(multi_buffer_info* Info) +{ + for (int i = 0; i < fStreams.Count(); i++) { + if (!fStreams[i]->IsRunning()) { + fStreams[i]->Start(); + } + } + + TRACE_ALWAYS("Exchange!\n"); + snooze(1000000); + return B_OK; + + status_t status = B_ERROR; + bool anyBufferProcessed = false; + for (int i = 0; i < fStreams.Count() && !anyBufferProcessed; i++) { + status = acquire_sem_etc(fBuffersReadySem, 1, + B_RELATIVE_TIMEOUT | B_CAN_INTERRUPT, 50000); + if (status == B_TIMED_OUT) { + TRACE_ALWAYS("Timeout during buffers exchange.\n"); + break; + } + + anyBufferProcessed = fStreams[i]->ExchangeBuffer(Info); + status = anyBufferProcessed ? B_OK : B_ERROR; + } + + return status; +} + + +status_t +Device::_MultiBufferForceStop() +{ + for (int i = 0; i < fStreams.Count(); i++) { + fStreams[i]->Stop(); + } + return B_OK; +} + + +status_t +Device::_MultiGetMix(multi_mix_value_info *Info) +{ + return fAudioControl.GetMix(Info); +} + + +status_t +Device::_MultiSetMix(multi_mix_value_info *Info) +{ + return fAudioControl.SetMix(Info); +} + + +status_t +Device::_MultiListMixControls(multi_mix_control_info* Info) +{ + status_t status = fAudioControl.ListMixControls(Info); + TraceListMixControls(Info); + return status; +} + + +void +Device::TraceListMixControls(multi_mix_control_info *Info) +{ + TRACE("control_count:%d\n.", Info->control_count); + + int32 i = 0; + while (Info->controls[i].id > 0) { + multi_mix_control &c = Info->controls[i]; + TRACE("id:%#08x\n", c.id); + TRACE("flags:%#08x\n", c.flags); + TRACE("master:%#08x\n", c.master); + TRACE("parent:%#08x\n", c.parent); + TRACE("string:%d\n", c.string); + TRACE("name:%s\n", c.name); + i++; + } +} + + +status_t +Device::_SetupEndpoints() +{ + const usb_configuration_info *config + = gUSBModule->get_nth_configuration(fDevice, 0); + + if (config == NULL) { + TRACE_ALWAYS("Error of getting USB device configuration.\n"); + return B_ERROR; + } + + if (config->interface_count <= 0) { + TRACE_ALWAYS("Error:no interfaces found in USB device configuration\n"); + return B_ERROR; + } + + for (size_t i = 0; i < config->interface_count; i++) { + usb_interface_info *Interface = config->interface[i].active; + if (Interface->descr->interface_class != UAS_AUDIO) { + continue; + } + + switch (Interface->descr->interface_subclass) { + case UAS_AUDIOCONTROL: + fAudioControl.Init(i, Interface); + break; + case UAS_AUDIOSTREAMING: + { + Stream *stream = new Stream(this, i, &config->interface[i]); + if (B_OK == stream->Init()) { + // put the stream in the correct order: + // first output that input ones. + if (stream->IsInput()) { + fStreams.PushBack(stream); + } else { + fStreams.PushFront(stream); + } + } else { + delete stream; + } + } + break; + default: + TRACE_ALWAYS("Ignore interface of unsupported subclass %#x.\n", + Interface->descr->interface_subclass); + break; + } + } + + if (fAudioControl.InitCheck() == B_OK && fStreams.Count() > 0) { + TRACE("Found device %#06x:%#06x\n", fVendorID, fProductID); + gUSBModule->set_configuration(fDevice, config); + + for (int i = 0; i < fStreams.Count(); i++) { + fStreams[i]->OnSetConfiguration(fDevice, config); + } + + return B_OK; + } + + return B_NO_INIT; +} + + +status_t +Device::StopDevice() +{ + status_t result = B_OK; // WriteRXControlRegister(0); + + if (result != B_OK) { + TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n", 0, result); + } + + TRACE_RET(result); + return result; +} + + +void +Device::_ReadCallback(void *cookie, int32 status, void *data, + uint32 actualLength) +{ + TRACE_FLOW("ReadCB: %d bytes; status:%#010x\n", actualLength, status); + Device *device = (Device *)cookie; + device->fActualLengthRead = actualLength; + device->fStatusRead = status; + release_sem_etc(device->fNotifyReadSem, 1, B_DO_NOT_RESCHEDULE); +} + + +void +Device::_WriteCallback(void *cookie, int32 status, void *data, + uint32 actualLength) +{ + TRACE_FLOW("WriteCB: %d bytes; status:%#010x\n", actualLength, status); + Device *device = (Device *)cookie; + device->fActualLengthWrite = actualLength; + device->fStatusWrite = status; + release_sem_etc(device->fNotifyWriteSem, 1, B_DO_NOT_RESCHEDULE); +} + + +void +Device::_NotifyCallback(void *cookie, int32 status, void *data, + uint32 actualLength) +{ + Device *device = (Device *)cookie; + atomic_add(&device->fInsideNotify, 1); + if (status == B_CANCELED || device->fRemoved) { + atomic_add(&device->fInsideNotify, -1); + return; + } + +/* if (status != B_OK) { + TRACE_ALWAYS("Device status error:%#010x\n", status); + status_t result = gUSBModule->clear_feature(device->fControLeNDPOint, + USB_FEATURE_ENDPOINT_HALT); + if (result != B_OK) + TRACE_ALWAYS("Error during clearing of HALT state:%#010x.\n", result); + } +*/ + // parse data in overriden class +// device->OnNotify(actualLength); + + // schedule next notification buffer +// gUSBModule->queue_interrupt(device->fNotifyEndpoint, device->fNotifyBuffer, +// device->fNotifyBufferLength, _NotifyCallback, device); + atomic_add(&device->fInsideNotify, -1); +} + diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/Device.h b/src/add-ons/kernel/drivers/audio/usb_audio/Device.h new file mode 100644 index 0000000000..e40991d184 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/Device.h @@ -0,0 +1,139 @@ +/* + * Driver for USB Audio Device Class devices. + * Copyright (c) 2009,10,12 S.Zharski + * Distributed under the terms of the MIT license. + * + */ +#ifndef _USB_AUDIO_DEVICE_H_ +#define _USB_AUDIO_DEVICE_H_ + + +#include "Driver.h" +#include "USB_audio_spec.h" +#include "AudioControlInterface.h" +#include "AudioStreamingInterface.h" +#include "Stream.h" + +// typedef VectorMap AudioControlsMap; +// typedef VectorMap::Iterator AudioControlsIterator; + +typedef Vector AudioStreamsVector; +typedef Vector::Iterator AudioStreamsIterator; + + +class FeatureUnit; + +class Device { + friend class FeatureUnit; + friend class Stream; +public: + Device(usb_device device); + virtual ~Device(); + + status_t InitCheck() { return fStatus; }; + + status_t Open(uint32 flags); + bool IsOpen() { return fOpen; }; + + status_t Close(); + status_t Free(); + + status_t Read(uint8 *buffer, size_t *numBytes); + status_t Write(const uint8 *buffer, size_t *numBytes); + status_t Control(uint32 op, void *buffer, size_t length); + + void Removed(); + bool IsRemoved() { return fRemoved; }; + + status_t CompareAndReattach(usb_device device); +virtual status_t SetupDevice(bool deviceReplugged); +// uint16 SpecReleaseNumber(); + +// _AudioControl* FindAudioControl(uint8 ID); + usb_device USBDevice() { return fDevice; } + + AudioControlInterface& AudioControl() { return fAudioControl; } + +private: +static void _ReadCallback(void *cookie, int32 status, + void *data, uint32 actualLength); +static void _WriteCallback(void *cookie, int32 status, + void *data, uint32 actualLength); +static void _NotifyCallback(void *cookie, int32 status, + void *data, uint32 actualLength); + + status_t _SetupEndpoints(); +// void ParseAudioControlInterface(usb_device device, +// size_t interface, usb_interface_info *Interface); +// void ParseAudioStreamingInterface(usb_device device, +// size_t interface, usb_interface_list *List); + +protected: +virtual status_t StartDevice() { return B_OK; } +virtual status_t StopDevice(); + + void TraceMultiDescription(multi_description *Description, + Vector& Channels); + void TraceListMixControls(multi_mix_control_info *Info); + // state tracking + status_t fStatus; + bool fOpen; + bool fRemoved; + vint32 fInsideNotify; + usb_device fDevice; + uint16 fVendorID; + uint16 fProductID; +const char * fDescription; + bool fNonBlocking; + + AudioControlInterface fAudioControl; +/* + AudioControlHeader* fAudioControlHeader; + // 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; +*/ + // vector of audio streams + AudioStreamsVector fStreams; + +protected: + status_t _MultiGetDescription(multi_description *Description); + status_t _MultiGetEnabledChannels(multi_channel_enable *Enable); + status_t _MultiSetEnabledChannels(multi_channel_enable *Enable); + status_t _MultiGetBuffers(multi_buffer_list* List); + status_t _MultiGetGlobalFormat(multi_format_info *Format); + status_t _MultiSetGlobalFormat(multi_format_info *Format); + status_t _MultiGetMix(multi_mix_value_info *Info); + status_t _MultiSetMix(multi_mix_value_info *Info); + status_t _MultiListMixControls(multi_mix_control_info* Info); + status_t _MultiBufferExchange(multi_buffer_info* Info); + status_t _MultiBufferForceStop(); + + // interface and device infos +// uint16 fFrameSize; + + // pipes for notifications and data io + usb_pipe fControlEndpoint; + usb_pipe fInStreamEndpoint; + usb_pipe fOutStreamEndpoint; + + // data stores for async usb transfers + uint32 fActualLengthRead; + uint32 fActualLengthWrite; + int32 fStatusRead; + int32 fStatusWrite; + sem_id fNotifyReadSem; + sem_id fNotifyWriteSem; + + uint8 * fNotifyBuffer; + uint32 fNotifyBufferLength; + + sem_id fBuffersReadySem; +}; + + +#endif // _USB_AUDIO_DEVICE_H_ + diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/Driver.cpp b/src/add-ons/kernel/drivers/audio/usb_audio/Driver.cpp new file mode 100644 index 0000000000..326cfc8d1b --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/Driver.cpp @@ -0,0 +1,303 @@ +/* + * Driver for USB Audio Device Class devices. + * Copyright (c) 2009,10,12 S.Zharski + * Distributed under the terms of the MIT license. + * + */ + +#include +#include +#include + +#include // for mutex + +#include "Driver.h" +#include "Settings.h" +#include "Device.h" +#include "USB_audio_spec.h" + + +int32 api_version = B_CUR_DRIVER_API_VERSION; + + +static const char *sDeviceBaseName = "audio/hmulti/USB Audio/"; +Device *gDevices[MAX_DEVICES]; +char *gDeviceNames[MAX_DEVICES + 1]; + +usb_module_info *gUSBModule = NULL; + + +mutex gDriverLock; +// auto - release helper class +class DriverSmartLock { +public: + DriverSmartLock() { mutex_lock(&gDriverLock); } + ~DriverSmartLock() { mutex_unlock(&gDriverLock); } +}; + + +status_t +usb_audio_device_added(usb_device device, void **cookie) +{ + *cookie = NULL; + + DriverSmartLock driverLock; // released on exit + + // check if this is a replug of an existing device first + for (int32 i = 0; i < MAX_DEVICES; i++) { + if (gDevices[i] == NULL) + continue; + + if (gDevices[i]->CompareAndReattach(device) != B_OK) + continue; + + TRACE("The device is plugged back. Use entry at %ld.\n", i); + *cookie = gDevices[i]; + return B_OK; + } + + // no such device yet, create a new one + Device *audioDevice = new Device(device); + if (audioDevice == 0) { + return ENODEV; + } + + status_t status = audioDevice->InitCheck(); + if (status < B_OK) { + delete audioDevice; + return status; + } + + status = audioDevice->SetupDevice(false); + if (status < B_OK) { + delete audioDevice; + return status; + } + + for (int32 i = 0; i < MAX_DEVICES; i++) { + if (gDevices[i] != NULL) + continue; + + gDevices[i] = audioDevice; + *cookie = audioDevice; + + TRACE("New device is added at %ld.\n", i); + return B_OK; + } + + // no space for the device + TRACE_ALWAYS("Error: no more device entries availble.\n"); + + delete audioDevice; + return B_ERROR; +} + + +status_t +usb_audio_device_removed(void *cookie) +{ + DriverSmartLock driverLock; // released on exit + + Device *device = (Device *)cookie; + for (int32 i = 0; i < MAX_DEVICES; i++) { + if (gDevices[i] == device) { + if (device->IsOpen()) { + // the device will be deleted upon being freed + device->Removed(); + } else { + gDevices[i] = NULL; + delete device; + TRACE("Device at %ld deleted.\n", i); + } + break; + } + } + + return B_OK; +} + + +status_t +init_hardware() +{ + return B_OK; +} + + +status_t +init_driver() +{ + status_t status = get_module(B_USB_MODULE_NAME, + (module_info **)&gUSBModule); + if (status < B_OK) + return status; + + load_settings(); + + TRACE_ALWAYS("%s\n", kVersion); + + for (int32 i = 0; i < MAX_DEVICES; i++) + gDevices[i] = NULL; + + gDeviceNames[0] = NULL; + mutex_init(&gDriverLock, DRIVER_NAME"_devices"); + + static usb_notify_hooks notifyHooks = { + &usb_audio_device_added, + &usb_audio_device_removed + }; + + static usb_support_descriptor supportedDevices[] = { + {UAS_AUDIO, 0, 0, 0, 0 } + }; + + gUSBModule->register_driver(DRIVER_NAME, supportedDevices, 0, NULL); + gUSBModule->install_notify(DRIVER_NAME, ¬ifyHooks); + return B_OK; +} + + +void +uninit_driver() +{ + gUSBModule->uninstall_notify(DRIVER_NAME); + mutex_lock(&gDriverLock); + + for (int32 i = 0; i < MAX_DEVICES; i++) { + if (gDevices[i]) { + delete gDevices[i]; + gDevices[i] = NULL; + } + } + + for (int32 i = 0; gDeviceNames[i]; i++) { + free(gDeviceNames[i]); + gDeviceNames[i] = NULL; + } + + mutex_destroy(&gDriverLock); + put_module(B_USB_MODULE_NAME); + + release_settings(); +} + + +static status_t +usb_audio_open(const char *name, uint32 flags, void **cookie) +{ + DriverSmartLock driverLock; // released on exit + + *cookie = NULL; + status_t status = ENODEV; + int32 index = strtol(name + strlen(sDeviceBaseName), NULL, 10); + if (index >= 0 && index < MAX_DEVICES && gDevices[index]) { + status = gDevices[index]->Open(flags); + *cookie = gDevices[index]; + } + + return status; +} + + +static status_t +usb_audio_read(void *cookie, off_t position, void *buffer, size_t *numBytes) +{ + Device *device = (Device *)cookie; + return device->Read((uint8 *)buffer, numBytes); +} + + +static status_t +usb_audio_write(void *cookie, off_t position, const void *buffer, + size_t *numBytes) +{ + Device *device = (Device *)cookie; + return device->Write((const uint8 *)buffer, numBytes); +} + + +static status_t +usb_audio_control(void *cookie, uint32 op, void *buffer, size_t length) +{ + Device *device = (Device *)cookie; + return device->Control(op, buffer, length); +} + + +static status_t +usb_audio_close(void *cookie) +{ + Device *device = (Device *)cookie; + return device->Close(); +} + + +static status_t +usb_audio_free(void *cookie) +{ + Device *device = (Device *)cookie; + + DriverSmartLock driverLock; // released on exit + + status_t status = device->Free(); + for (int32 i = 0; i < MAX_DEVICES; i++) { + if (gDevices[i] == device) { + // the device is removed already but as it was open the + // removed hook has not deleted the object + gDevices[i] = NULL; + delete device; + TRACE("Device at %ld deleted.\n", i); + break; + } + } + + return status; +} + + +const char ** +publish_devices() +{ + for (int32 i = 0; gDeviceNames[i]; i++) { + free(gDeviceNames[i]); + gDeviceNames[i] = NULL; + } + + DriverSmartLock driverLock; // released on exit + + int32 deviceCount = 0; + for (int32 i = 0; i < MAX_DEVICES; i++) { + if (gDevices[i] == NULL) + continue; + + gDeviceNames[deviceCount] = (char *)malloc(strlen(sDeviceBaseName) + 4); + if (gDeviceNames[deviceCount]) { + sprintf(gDeviceNames[deviceCount], "%s%ld", sDeviceBaseName, i); + TRACE("publishing %s\n", gDeviceNames[deviceCount]); + deviceCount++; + } else + TRACE_ALWAYS("Error: out of memory during allocating device name.\n"); + } + + gDeviceNames[deviceCount] = NULL; + return (const char **)&gDeviceNames[0]; +} + + +device_hooks * +find_device(const char *name) +{ + static device_hooks deviceHooks = { + usb_audio_open, + usb_audio_close, + usb_audio_free, + usb_audio_control, + usb_audio_read, + usb_audio_write, + NULL, /* select */ + NULL /* deselect */ + }; + + return &deviceHooks; +} + diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/Driver.h b/src/add-ons/kernel/drivers/audio/usb_audio/Driver.h new file mode 100644 index 0000000000..07acd4d0e4 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/Driver.h @@ -0,0 +1,53 @@ +/* + * Driver for USB Audio Device Class devices. + * Copyright (c) 2009,10,12 S.Zharski + * Distributed under the terms of the MIT license. + * + */ +#ifndef _USB_AUDIO_DRIVER_H_ +#define _USB_AUDIO_DRIVER_H_ + + +#include +#include +#include +#include +#include + + +#define DRIVER_NAME "usb_audio" +#define MAX_DEVICES 8 + +const char* const kVersion = "ver.0.0.4"; + +const uint32 kSamplesBufferSize = 1024; +const uint32 kSamplesBufferCount = 2; + + +// calculate count of array members +#ifdef _countof + #warning "countof(...) WAS ALREADY DEFINED!!! Remove local definition!" + #undef countof +#endif +#define _countof(array)(sizeof(array) / sizeof(array[0])) + + +extern usb_module_info *gUSBModule; + + +extern "C" { + + status_t usb_audio_device_added(usb_device device, void **cookie); + status_t usb_audio_device_removed(void *cookie); + + status_t init_hardware(); + void uninit_driver(); + + const char **publish_devices(); + device_hooks *find_device(const char *name); + +} + + +#endif // _USB_AUDIO_DRIVER_H_ + diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/Jamfile b/src/add-ons/kernel/drivers/audio/usb_audio/Jamfile index ccb2caa41d..0ca7052f1b 100644 --- a/src/add-ons/kernel/drivers/audio/usb_audio/Jamfile +++ b/src/add-ons/kernel/drivers/audio/usb_audio/Jamfile @@ -2,10 +2,17 @@ SubDir HAIKU_TOP src add-ons kernel drivers audio usb_audio ; SetSubDirSupportedPlatformsBeOSCompatible ; -UsePrivateHeaders audio ; +UsePrivateHeaders kernel media ; + +UsePrivateHeaders kernel net ; + +UsePrivateHeaders [ FDirName kernel util ] ; KernelAddon usb_audio : - usb_audio.c - USB_audio_utils.c -; - + Driver.cpp + Device.cpp + AudioControlInterface.cpp + AudioStreamingInterface.cpp + Stream.cpp + Settings.cpp + ; diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/Settings.cpp b/src/add-ons/kernel/drivers/audio/usb_audio/Settings.cpp new file mode 100644 index 0000000000..55bba45b2d --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/Settings.cpp @@ -0,0 +1,113 @@ +/* + * Driver for USB Audio Device Class devices. + * Copyright (c) 2009,10,12 S.Zharski + * Distributed under the terms of the MIT license. + * + */ + +#include // for mutex +#include // for file operation +#include // for file operation +#include // for file operation + +#include "Settings.h" + +bool gTraceOn = false; +bool gTruncateLogFile = false; +bool gAddTimeStamp = true; +bool gTraceFlow = false; +static char *gLogFilePath = NULL; +mutex gLogLock; + +static +void create_log() +{ + if (gLogFilePath == NULL) + return; + + int flags = O_WRONLY | O_CREAT | ((gTruncateLogFile) ? O_TRUNC : 0); + close(open(gLogFilePath, flags, 0666)); + + mutex_init(&gLogLock, DRIVER_NAME"-logging"); +} + + +void load_settings() +{ + void *handle = load_driver_settings(DRIVER_NAME); + if (handle == 0) + return; + + gTraceOn = get_driver_boolean_parameter(handle, "trace", gTraceOn, true); + gTraceFlow = get_driver_boolean_parameter(handle, "trace_flow", + gTraceFlow, true); + gTruncateLogFile = get_driver_boolean_parameter(handle, "truncate_logfile", + gTruncateLogFile, true); + gAddTimeStamp = get_driver_boolean_parameter(handle, "add_timestamp", + gAddTimeStamp, true); + const char * logFilePath = get_driver_parameter(handle, "logfile", + NULL, "/var/log/"DRIVER_NAME".log"); + if (logFilePath != NULL) { + gLogFilePath = strdup(logFilePath); + } + + unload_driver_settings(handle); + + create_log(); +} + + +void release_settings() +{ + if (gLogFilePath != NULL) { + mutex_destroy(&gLogLock); + free(gLogFilePath); + } +} + + +void usb_audio_trace(bool force, const char* func, const char *fmt, ...) +{ + if (!(force || gTraceOn)) { + return; + } + + va_list arg_list; + static const char *prefix = DRIVER_NAME":"; + static char buffer[1024]; + char *buf_ptr = buffer; + if (gLogFilePath == NULL) { + strcpy(buffer, prefix); + buf_ptr += strlen(prefix); + } + + if (gAddTimeStamp) { + bigtime_t time = system_time(); + uint32 msec = time / 1000; + uint32 sec = msec / 1000; + sprintf(buf_ptr, "%02ld.%02ld.%03ld:", + sec / 60, sec % 60, msec % 1000); + buf_ptr += strlen(buf_ptr); + } + + if (func != NULL) { + sprintf(buf_ptr, "%s::", func); + buf_ptr += strlen(buf_ptr); + } + + va_start(arg_list, fmt); + vsprintf(buf_ptr, fmt, arg_list); + va_end(arg_list); + + if (gLogFilePath == NULL) { + dprintf(buffer); + return; + } + + mutex_lock(&gLogLock); + int fd = open(gLogFilePath, O_WRONLY | O_APPEND); + write(fd, buffer, strlen(buffer)); + close(fd); + mutex_unlock(&gLogLock); +} + diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/Settings.h b/src/add-ons/kernel/drivers/audio/usb_audio/Settings.h new file mode 100644 index 0000000000..692fa4d0af --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/Settings.h @@ -0,0 +1,34 @@ +/* + * Driver for USB Audio Device Class devices. + * Copyright (c) 2009,10,12 S.Zharski + * Distributed under the terms of the MIT license. + * + */ +#ifndef _USB_AUDIO_SETTINGS_H_ +#define _USB_AUDIO_SETTINGS_H_ + + +#include + +#include "Driver.h" + +void load_settings(); +void release_settings(); + +void usb_audio_trace(bool force, const char *func, const char *fmt, ...); + +#ifdef TRACE +#undef TRACE +#endif + +#define TRACE(x...) usb_audio_trace(false, __func__, x) +#define TRACE_ALWAYS(x...) usb_audio_trace(true, __func__, x) + +extern bool gTraceFlow; +#define TRACE_FLOW(x...) usb_audio_trace(gTraceFlow, NULL, x) + +#define TRACE_RET(result) usb_audio_trace(false, __func__, \ + "Returns:%#010x\n", result); + +#endif /*_USB_AUDIO_SETTINGS_H_*/ + diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/Stream.cpp b/src/add-ons/kernel/drivers/audio/usb_audio/Stream.cpp new file mode 100644 index 0000000000..9ea21dcb4c --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/Stream.cpp @@ -0,0 +1,528 @@ +/* + * Driver for USB Audio Device Class devices. + * Copyright (c) 2009,10,12 S.Zharski + * Distributed under the terms of the MIT license. + * + */ + + +#include "Stream.h" +#include "Device.h" +#include "Driver.h" +#include "Settings.h" + + +Stream::Stream(Device *device, size_t interface, usb_interface_list *List + /*, bool isInput, uint32 HWChannel*/) + : + AudioStreamingInterface(&device->AudioControl(), interface, List), + fDevice(device), + fStatus(B_NO_INIT), + fStreamEndpoint(0), + fIsRunning(false), + fArea(-1), + fDescriptors(0), + fDescriptorsCount(0), + fCurrentBuffer(0), + fStartingFrame(0), + fSamplesCount(0), + fProcessedBuffers(0)/*, + fBuffersPhysAddress(0)/ *, + fRealTime(0), + fFramesCount(0), + fBufferCycle(0)*/ +{ + +} + + +Stream::~Stream() +{ + delete_area(fArea); +} + + +status_t +Stream::Init() +{ + // lookup alternate with maximal (ch * 100 + resolution) + uint16 maxChxRes = 0; + for (int i = 0; i < fAlternates.Count(); i++) { + if (fAlternates[i]->Interface() == 0) { + TRACE("Ignore alternate %d - zero interface description.\n", i); + continue; + } + + if (fAlternates[i]->Format() == 0) { + TRACE("Ignore alternate %d - zero format description.\n", i); + continue; + } + + if (fAlternates[i]->Format()->fFormatType != UAF_FORMAT_TYPE_I) { + TRACE("Ignore alternate %d - format type %#02x is not supported.\n", + i, fAlternates[i]->Format()->fFormatType); + continue; + } + + switch (fAlternates[i]->Interface()->fFormatTag) { + case UAF_PCM: + case UAF_PCM8: + case UAF_IEEE_FLOAT: + // case UAF_ALAW: + // case UAF_MULAW: + break; + default: + TRACE("Ignore alternate %d - format %#04x is not supported.\n", + i, fAlternates[i]->Interface()->fFormatTag); + continue; + } + + TypeIFormatDescriptor* format + = static_cast(fAlternates[i]->Format()); + + if (fAlternates[i]->Interface()->fFormatTag == UAF_PCM) { + switch(format->fBitResolution) { + default: + TRACE("Ignore alternate %d - bit resolution %d " + "is not supported.\n", i, format->fBitResolution); + continue; + case 8: case 16: case 18: case 20: case 24: case 32: + break; + } + } + + uint16 chxRes = format->fNumChannels * 100 + format->fBitResolution; + if (chxRes > maxChxRes) { + maxChxRes = chxRes; + fActiveAlternate = i; + } + } + + if (maxChxRes <= 0) { + TRACE("No compatible alternate found. Stream initialization failed.\n"); + return fStatus; + } + const ASEndpointDescriptor* endpoint = fAlternates[ + fActiveAlternate]->Endpoint(); + fIsInput = (endpoint->fEndpointAddress & USB_ENDPOINT_ADDR_DIR_IN) + == USB_ENDPOINT_ADDR_DIR_IN; + TRACE("Alternate %d selected!\n", fActiveAlternate); + + TypeIFormatDescriptor* format + = static_cast(fAlternates[ + fActiveAlternate]->Format()); + + size_t bufferSize = format->fNumChannels * format->fSubframeSize; + TRACE("bufferSize:%d\n", bufferSize); + + bufferSize *= kSamplesBufferSize; + TRACE("bufferSize:%d\n", bufferSize); + + bufferSize *= (sizeof(usb_iso_packet_descriptor) + endpoint->fMaxPacketSize); + TRACE("bufferSize:%d\n", bufferSize); + + bufferSize /= endpoint->fMaxPacketSize; + TRACE("bufferSize:%d\n", bufferSize); + + bufferSize = (bufferSize + (B_PAGE_SIZE - 1)) &~ (B_PAGE_SIZE - 1); + TRACE("bufferSize:%d\n", bufferSize); + + fArea = create_area( (fIsInput) ? DRIVER_NAME "_record_area" : + DRIVER_NAME "_playback_area", + (void**)&fDescriptors, B_ANY_KERNEL_ADDRESS, + bufferSize, B_CONTIGUOUS, + B_READ_AREA | B_WRITE_AREA); + if (fArea < 0) { + TRACE_ALWAYS("Error of creating %#x - bytes size buffer area:%#010x\n", + bufferSize, fArea); + fStatus = fArea; + return fStatus; + } + + physical_entry PhysEntry; + get_memory_map(fDescriptors, bufferSize, &PhysEntry, 1); + + TRACE_ALWAYS("Created area id: " + "%d\naddress:%#010x[phys:%#010x]\nsize:%#010x\n", + fArea, fDescriptors, PhysEntry.address, bufferSize); + + fDescriptorsCount = bufferSize; + fDescriptorsCount /= (sizeof(usb_iso_packet_descriptor) + + endpoint->fMaxPacketSize); + fDescriptorsCount /= kSamplesBufferCount; + // we need same size buffers. round it! + fDescriptorsCount *= kSamplesBufferCount; + + fSamplesCount = fDescriptorsCount * endpoint->fMaxPacketSize; + TRACE("samplesCount:%d\n", fSamplesCount); + + fSamplesCount /= format->fNumChannels * format->fSubframeSize; + TRACE("samplesCount:%d\n", fSamplesCount); + + for (size_t i = 0; i < fDescriptorsCount; i++) { + fDescriptors[i].request_length = endpoint->fMaxPacketSize; + fDescriptors[i].actual_length = 0; + fDescriptors[i].status = B_OK; + } + +/* uint32* b = (uint32*)(fDescriptors + fDescriptorsCount); + for (size_t i = 0; i < fSamplesCount; i++) { + b[i] = i * 10; + }*/ + + TRACE_ALWAYS("Descriptors count:%d\nsample size:%d\nchannels:%d:%d\n", + fDescriptorsCount, format->fSubframeSize, format->fNumChannels, + sizeof(usb_iso_packet_descriptor)); + return fStatus = B_OK; +} + + +status_t +Stream::OnSetConfiguration(usb_device device, + const usb_configuration_info *config) +{ + if (config == NULL) { + TRACE_ALWAYS("NULL configuration. Not set.\n"); + return B_ERROR; + } + + usb_interface_info* interface + = &config->interface[fInterface].alt[fActiveAlternate]; + if (interface == NULL) { + TRACE_ALWAYS("NULL interface. Not set.\n"); + return B_ERROR; + } + + /*status_t status =*/ gUSBModule->set_alt_interface(device, interface); + uint8 address = fAlternates[fActiveAlternate]->Endpoint()->fEndpointAddress; + + for (size_t i = 0; i < interface->endpoint_count; i++) { + if (address == interface->endpoint[i].descr->endpoint_address) { + fStreamEndpoint = interface->endpoint[i].handle; + TRACE("%s Stream Endpoint [address %#04x] handle is: %#010x.\n", + fIsInput ? "Input" : "Output", address, fStreamEndpoint); + return B_OK; + } + } + + TRACE("%s Stream Endpoint [address %#04x] was not found.\n", + fIsInput ? "Input" : "Output", address); + return B_ERROR; +} + + +status_t +Stream::Start() +{ + status_t result = B_BUSY; + if (!fIsRunning) { + if (!fIsInput) { + // for (size_t i = 0; i < kSamplesBufferCount; i++) + // result = _QueueNextTransfer(i); + // TODO + // result = _QueueNextTransfer(0); + result = B_OK; + } else + result = B_OK; + fIsRunning = result == B_OK; + } + return result; +} + + +status_t +Stream::Stop() +{ + if (fIsRunning) { + gUSBModule->cancel_queued_transfers(fStreamEndpoint); + fIsRunning = false; + } + + return B_OK; +} + + +status_t +Stream::_QueueNextTransfer(size_t queuedBuffer) +{ + TypeIFormatDescriptor* format + = static_cast(fAlternates[ + fActiveAlternate]->Format()); + + size_t bufferSize = format->fNumChannels * format->fSubframeSize; + bufferSize *= fSamplesCount / kSamplesBufferCount; + + uint8* buffers = (uint8*)(fDescriptors + fDescriptorsCount); + + size_t packetsCount = fDescriptorsCount / kSamplesBufferCount; + + TRACE("buffers:%#010x[%#x]\ndescrs:%#010x[%#x]\n", + buffers + bufferSize * queuedBuffer, bufferSize, + fDescriptors + queuedBuffer * packetsCount, packetsCount); + + return gUSBModule->queue_isochronous(fStreamEndpoint, + buffers + bufferSize * queuedBuffer, bufferSize, + fDescriptors + queuedBuffer * packetsCount, packetsCount, + NULL/*&fStartingFrame*/, USB_ISO_ASAP, + Stream::_TransferCallback, this); + + return B_OK; +} + + +void +Stream::_TransferCallback(void *cookie, int32 status, void *data, + uint32 actualLength) +{ + Stream *stream = (Stream *)cookie; + + stream->fCurrentBuffer++; + if (stream->fCurrentBuffer >= kSamplesBufferCount) { + stream->fCurrentBuffer = 0; + } + + stream->_DumpDescriptors(); + + stream->_DumpDescriptors(); + + /* + status_t result = stream->_QueueNextTransfer(stream->fCurrentBuffer); + + if (atomic_add(&stream->fProcessedBuffers, 1) > (int32)kSamplesBufferCount) { + TRACE_ALWAYS("Processed buffers overflow:%d\n", stream->fProcessedBuffers); + } +*/ + release_sem_etc(stream->fDevice->fBuffersReadySem, 1, B_DO_NOT_RESCHEDULE); + + // TRACE_ALWAYS("st:%#010x, len:%d -> %#010x\n", status, actualLength, result); + TRACE_ALWAYS("st:%#010x, data:%#010x, len:%d\n", status, data, actualLength); + +/* if (status != B_OK) { + TRACE_ALWAYS("Device status error:%#010x\n", status); + status_t result = gUSBModule->clear_feature(device->fControLeNDPOint, + USB_FEATURE_ENDPOINT_HALT); + if (result != B_OK) + TRACE_ALWAYS("Error during clearing of HALT state:%#010x.\n", result); + } +*/ +} + + +void +Stream::_DumpDescriptors() +{ + size_t packetsCount = fDescriptorsCount / kSamplesBufferCount; + size_t from = /*fCurrentBuffer > 0 ? packetsCount :*/ 0 ; + size_t to = /*fCurrentBuffer > 0 ?*/ fDescriptorsCount /*: packetsCount*/ ; + for (size_t i = from; i < to; i++) { + TRACE_ALWAYS("%d:req_len:%d; act_len:%d; stat:%#010x\n", i, + fDescriptors[i].request_length, fDescriptors[i].actual_length, + fDescriptors[i].status); + } +} + + +status_t +Stream::GetEnabledChannels(uint32& offset, multi_channel_enable *Enable) +{ + AudioChannelCluster* cluster = ChannelCluster(); + if (cluster == 0) { + return B_ERROR; + } + + for (size_t i = 0; i < cluster->ChannelsCount(); i++) { + B_SET_CHANNEL(Enable->enable_bits, offset++, true); + TRACE("Report channel %d as enabled.\n", offset); + } + + return B_OK; +} + + +status_t +Stream::SetEnabledChannels(uint32& offset, multi_channel_enable *Enable) +{ + AudioChannelCluster* cluster = ChannelCluster(); + if (cluster == 0) { + return B_ERROR; + } + + for (size_t i = 0; i < cluster->ChannelsCount(); i++) { + TRACE("%s channel %d.\n", (B_TEST_CHANNEL(Enable->enable_bits, offset++) + ? "Enable" : "Disable"), offset + 1); + } + + return B_OK; +} + + +status_t +Stream::GetGlobalFormat(multi_format_info *Format) +{ + if (IsInput()) { + // TODO + Format->input.rate = B_SR_48000; + Format->input.cvsr = 48000; + Format->input.format = B_FMT_16BIT; + } else { + // TODO + Format->output.rate = B_SR_48000; + Format->output.cvsr = 48000; + Format->output.format = B_FMT_16BIT; + } + + return B_OK; +} + + +status_t +Stream::SetGlobalFormat(multi_format_info *Format) +{ + if (IsInput()) { + // TODO + TRACE("input.rate:%d\n", Format->input.rate); + TRACE("input.cvsr:%f\n", Format->input.cvsr); + TRACE("input.format:%#08x\n", Format->input.format); + } else { + // TODO + TRACE("output.rate:%d\n", Format->output.rate); + TRACE("output.cvsr:%f\n", Format->output.cvsr); + TRACE("output.format:%#08x\n", Format->output.format); + } + + return B_OK; +} + + +status_t +Stream::GetBuffers(multi_buffer_list* List) +{ +// TODO: check the available buffers count! + + int32 startChannel = List->return_playback_channels; + buffer_desc** Buffers = List->playback_buffers; + + if (fIsInput) { + List->flags |= B_MULTI_BUFFER_RECORD; + List->return_record_buffer_size = fSamplesCount / kSamplesBufferCount; + List->return_record_buffers = kSamplesBufferCount; + startChannel = List->return_record_channels; + Buffers = List->record_buffers; + + TRACE("flags:%#10x\nreturn_record_buffer_size:%#010x\n" + "return_record_buffers:%#010x\n", List->flags, + List->return_record_buffer_size, List->return_record_buffers); + } else { + List->flags |= B_MULTI_BUFFER_PLAYBACK; + List->return_playback_buffer_size = fSamplesCount / kSamplesBufferCount; + List->return_playback_buffers = kSamplesBufferCount; + + TRACE("flags:%#10x\nreturn_playback_buffer_size:%#010x\n" + "return_playback_buffers:%#010x\n", List->flags, + List->return_playback_buffer_size, List->return_playback_buffers); + } + + TypeIFormatDescriptor* format + = static_cast( + fAlternates[fActiveAlternate]->Format()); + const ASEndpointDescriptor* endpoint + = fAlternates[fActiveAlternate]->Endpoint(); + + // [buffer][channel] init buffers + for (size_t buffer = 0; buffer < kSamplesBufferCount; buffer++) { + TRACE("%s buffer #%d:\n", fIsInput ? "input" : "output", buffer + 1); + + for (size_t channel = startChannel; + channel < format->fNumChannels; channel++) + { + // init stride to the same for all buffers + uint32 stride = format->fSubframeSize * format->fNumChannels; + Buffers[buffer][channel].stride = stride; + + // init to buffers area begin + Buffers[buffer][channel].base + = (char*)(fDescriptors + fDescriptorsCount); + // shift for whole buffer if required + size_t bufferSize = endpoint->fMaxPacketSize + * (fDescriptorsCount / kSamplesBufferCount); + Buffers[buffer][channel].base += buffer * bufferSize; + // shift for channel if required + Buffers[buffer][channel].base += channel * format->fSubframeSize; + + TRACE("%d:%d: base:%#010x; stride:%#010x\n", buffer, channel, + Buffers[buffer][channel].base, Buffers[buffer][channel].stride); + } + } + + if (fIsInput) { + List->return_record_channels += format->fNumChannels; + TRACE("return_record_channels:%#010x\n", List->return_record_channels); + } else { + List->return_playback_channels += format->fNumChannels; + TRACE("return_playback_channels:%#010x\n", + List->return_playback_channels); + } + + return B_OK; +} + + +/* +int32 +Stream::InterruptHandler(uint32 SignaledChannelsMask) +{ + uint32 ChannelMask = 1 << fHWChannel; + if ((SignaledChannelsMask & ChannelMask) == 0) { + return B_UNHANDLED_INTERRUPT; + } + + uint32 CurrentSamplePositionFlag = fDevice->ReadPCI32(TrCurSPFBReg); + + fRealTime = system_time(); + fFramesCount += fBufferSize; + fBufferCycle = ((CurrentSamplePositionFlag & ChannelMask) == ChannelMask) ? 1 : 0; + +fCSP = CurrentSamplePositionFlag; + + release_sem_etc(fDevice->fBuffersReadySem, 1, B_DO_NOT_RESCHEDULE); + + return B_HANDLED_INTERRUPT; +}*/ + + +bool +Stream::ExchangeBuffer(multi_buffer_info* Info) +{ + if (fProcessedBuffers <= 0) { + // looks like somebody else has processed buffers but this stream + release_sem_etc(fDevice->fBuffersReadySem, 1, B_DO_NOT_RESCHEDULE); + return false; + } + + Info->played_real_time = system_time();// TODO fRealTime; + Info->played_frames_count += fSamplesCount / kSamplesBufferCount; + Info->playback_buffer_cycle = fCurrentBuffer; + + fCurrentBuffer++; + fCurrentBuffer %= kSamplesBufferCount; + + atomic_add(&fProcessedBuffers, -1); + + return true; +} + + +/* +ASInterfaceDescriptor* +Stream::ASInterface() +{ + return fAlternates[fActiveAlternate]->Interface(); +} + + +_ASFormatDescriptor* +Stream::ASFormat() +{ + return fAlternates[fActiveAlternate]->Format(); +}*/ + diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/Stream.h b/src/add-ons/kernel/drivers/audio/usb_audio/Stream.h new file mode 100644 index 0000000000..960ef9cac8 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/Stream.h @@ -0,0 +1,96 @@ +/* + * Driver for USB Audio Device Class devices. + * Copyright (c) 2009,10,12 S.Zharski + * Distributed under the terms of the MIT license. + * + */ +#ifndef _STREAM_H_ +#define _STREAM_H_ + + +#include + +#include "AudioStreamingInterface.h" + +class Device; + +// typedef Vector StreamAlternatesVector; +// typedef Vector::Iterator StreamAlternatesIterator; + +class Stream : public AudioStreamingInterface { + friend class Device; +public: + Stream(Device* device, size_t interface, + usb_interface_list *List + /*, bool isInput, uint32 HWChannel*/); + ~Stream(); + status_t Init(); + status_t InitCheck() { return fStatus; } + + status_t Start(); + status_t Stop(); + bool IsRunning() { return fIsRunning; } + + status_t GetBuffers(multi_buffer_list* List); + + status_t OnSetConfiguration(usb_device device, + const usb_configuration_info *config); + + bool ExchangeBuffer(multi_buffer_info* Info); +/* + int32 InterruptHandler(uint32 SignaledChannelsMask); +*/ +// ASInterfaceDescriptor* ASInterface(); +// _ASFormatDescriptor* ASFormat(); + status_t GetEnabledChannels(uint32& offset, + multi_channel_enable *Enable); + status_t SetEnabledChannels(uint32& offset, + multi_channel_enable *Enable); + status_t GetGlobalFormat(multi_format_info *Format); + status_t SetGlobalFormat(multi_format_info *Format); + + +protected: + + Device* fDevice; + status_t fStatus; + + // alternates of the streams +// StreamAlternatesVector fAlternates; +// size_t fActiveAlternate; + + uint8 fTerminalID; + usb_pipe fStreamEndpoint; + bool fIsRunning; +/* uint32 fHWChannel;*/ + area_id fArea; + usb_iso_packet_descriptor* fDescriptors; + size_t fDescriptorsCount; + size_t fCurrentBuffer; + uint32 fStartingFrame; + size_t fSamplesCount; + int32 fProcessedBuffers; +// void* fBuffersPhysAddress; +/* bigtime_t fRealTime; + bigtime_t fFramesCount; + int32 fBufferCycle; +public: + uint32 fCSP; */ +private: + status_t _QueueNextTransfer(size_t buffer); +static void _TransferCallback(void *cookie, int32 status, + void *data, uint32 actualLength); + void _DumpDescriptors(); +}; + +/* +class RecordStream : public Stream { +public: + RecordStream(Device* device, uint32 HWChannel); + ~RecordStream(); + status_t Start(); +}; +*/ + +#endif // _STREAM_H_ + diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/USB_audio_spec.h b/src/add-ons/kernel/drivers/audio/usb_audio/USB_audio_spec.h index 1d31175409..47c3876fbf 100644 --- a/src/add-ons/kernel/drivers/audio/usb_audio/USB_audio_spec.h +++ b/src/add-ons/kernel/drivers/audio/usb_audio/USB_audio_spec.h @@ -1,9 +1,9 @@ /* * USB audio spec stuctures - * + * * Based on the USB Device Class Definition for Audio Devices Release 1.0 * (March 18, 1998) - * + * * And the USB Device Class Definition for Audio Formats Release 1.0 * (March 18, 1998) */ @@ -39,6 +39,28 @@ extern "C" { #define UAS_CS_INTERFACE 0x24 #define UAS_CS_ENDPOINT 0x25 + +// Audio Function Categories +// Table A-7 page 132 + +enum AudioFunctionCategory { + AFCUndefined = 0x00, + AFCDesktopSpeaker = 0x01, + AFCHomeTheater = 0x02, + AFCMicrophone = 0x03, + AFCHeadset = 0x04, + AFCTelephone = 0x05, + AFCConverter = 0x06, + AFCVoiceRecorder = 0x07, + AFCIOBox = 0x08, + AFCInstrument = 0x09, + AFCProAudio = 0x0a, + AFCAudioVideo = 0x0b, + AFCControlPanel = 0x0c, + AFCOther = 0xff +}; + +/* // Table A-5, page 100 #define UAS_AC_DESCRIPTOR_UNDEFINED 0x00 #define UAS_HEADER 0x01 @@ -49,6 +71,25 @@ extern "C" { #define UAS_FEATURE_UNIT 0x06 #define UAS_PROCESSING_UNIT 0x07 #define UAS_EXTENSION_UNIT 0x08 +*/ +// Specification Release 2.0 +// Table A-9 page 131 +enum ACInterfaceDescriptorSubtype { + IDSUndefined = 0x00, + IDSHeader = 0x01, + IDSInputTerminal = 0x02, + IDSOutputTerminal = 0x03, + IDSMixerUnit = 0x04, + IDSSelectorUnit = 0x05, + IDSFeatureUnit = 0x06, + IDSEffectUnit = 0x07, + IDSProcessingUnit = 0x08, + IDSExtensionUnit = 0x09, + IDSClockSource = 0x0a, + IDSClockSelector = 0x0b, + IDSClockMultiplier = 0x0c, + IDSSampleRateConverter = 0x0d +}; // Table A-6, page 100 #define UAS_AS_DESCRIPTOR_UNDEFINED 0x00 @@ -99,45 +140,123 @@ extern "C" { #define UAS_SAMPLING_FREQ_CONTROL 0x01 #define UAS_PITCH_CONTROL 0x02 + // header descriptor typedef struct { - uint8 length; + uint8 length; uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24) uint8 descriptor_subtype; // HEADER uint16 bcd_release_no; // Audio Device Class Specification relno - uint16 total_length; // + uint16 total_length; // uint8 in_collection; // # of audiostreaming units uint8 interface_numbers[1]; // or more +} _PACKED usb_audiocontrol_header_descriptor_r1; + +typedef struct { + uint8 length; + uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24) + uint8 descriptor_subtype; // HEADER + uint16 bcd_release_no; // Audio Device Class Specification relno + uint8 function_category; // this Audio function Category + uint16 total_length; // + uint8 bm_controls; // bitmap of controls } _PACKED usb_audiocontrol_header_descriptor; // input terminal descriptor -// Table 4-3, page 39 +// Table 4-3, page 39 typedef struct { uint8 length; uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24) uint8 descriptor_subtype; // INPUT_TERMINAL - uint8 terminal_id; // + uint8 terminal_id; // uint16 terminal_type; // 0x0101 ? uint8 assoc_terminal; // OT terminal ID of corresponding outp uint8 num_channels; // stereo = 2 - uint8 channel_config; // spatial location of two channels (bitmap 0x03 is plain stereo) - uint8 channel_names; // index of string descr, name of first logical channel - uint8 terminal; // index of string descr, name of Input Terminal + uint8 channel_config; // spatial location of two channels + // (bitmap 0x03 is plain stereo) + uint8 channel_names; // index of string descr, + // name of first logical channel + uint8 terminal; // index of string descr, + // name of Input Terminal +} _PACKED usb_input_terminal_descriptor_r1; + +// Table 4-9, page 53 +typedef struct { + uint8 length; + uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24) + uint8 descriptor_subtype; // INPUT_TERMINAL + uint8 terminal_id; // + uint16 terminal_type; // 0x0101 ? + uint8 assoc_terminal; // OT terminal ID of corresponding outp + uint8 clock_source_id; // + uint8 num_channels; // stereo = 2 + uint32 channel_config; // spatial location of two channels + // (bitmap 0x03 is plain stereo) + uint8 channel_names; // index of string descr, + // name of first logical channel + uint16 bm_controls; // + uint8 terminal; // index of string descr, + // name of Input Terminal } _PACKED usb_input_terminal_descriptor; // output terminal descriptor -// Table 4-4, page 40 +// Table 4-4, page 40 typedef struct { uint8 length; uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24) uint8 descriptor_subtype; // OUTPUT_TERMINAL - uint8 terminal_id; // + uint8 terminal_id; // uint16 terminal_type; // 0x0101 ? uint8 assoc_terminal; // OT terminal ID of corresponding outp - uint8 source_id; // ID of the unit or terminal to which this terminal is connected - uint8 terminal; // index of string descr, name of Input Terminal + uint8 source_id; // ID of the unit or terminal to which + // this terminal is connected + uint8 terminal; // index of string descr, + // name of Input Terminal +} _PACKED usb_output_terminal_descriptor_r1; + +typedef struct { + uint8 length; + uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24) + uint8 descriptor_subtype; // OUTPUT_TERMINAL + uint8 terminal_id; // + uint16 terminal_type; // 0x0101 ? + uint8 assoc_terminal; // OT terminal ID of corresponding outp + uint8 source_id; // ID of the unit or terminal to which + // this terminal is connected + uint8 clock_source_id; // + uint16 bm_controls; // + uint8 terminal; // index of string descr, + // name of Input Terminal } _PACKED usb_output_terminal_descriptor; + +// mixer unit descriptor +// Table 4-5, page 41 +typedef struct { + uint8 length; + uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24) + uint8 descriptor_subtype; // MIXER_UNIT + uint8 unit_id; // unique within audio function + uint8 num_input_pins; // + uint8 input_pins[1]; // array of source ids for the mixer + // use usb_output_channels_descriptor + // to parse the rest +} _PACKED usb_mixer_unit_descriptor; + +// pseudo-descriptor for a section corresponding to logical output channels +// used in mixer, processing and extension descriptions. +typedef struct { + uint8 num_output_pins; // number of mixer output pins + uint16 channel_config; // location of logical channels + uint8 channel_names; // id of name string of first logical channel +} _PACKED usb_output_channels_descriptor_r1; + +typedef struct { + uint8 num_output_pins; // number of mixer output pins + uint32 channel_config; // location of logical channels + uint8 channel_names; // id of name string of first logical channel +} _PACKED usb_output_channels_descriptor; + // selector unit descriptor // Table 4-6, page 43 typedef struct { @@ -145,11 +264,13 @@ typedef struct { uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24) uint8 descriptor_subtype; // SELECTOR_UNIT uint8 unit_id; // unique within audio function - uint8 num_input_pins; // - uint8 input_pins[2]; // - uint8 selector_string[1]; // YUK - doesn't work if num_input_pins!=2 - in that case, add (num_input_pins-2) to the index of this array... + uint8 num_input_pins; // + uint8 input_pins[1]; // id of the unit or terminal + // this pin is connected to +/* uint8 selector_string; */ // be afraid of the variable + // size of input_pins array! } _PACKED usb_selector_unit_descriptor; - + // feature unit descriptor // Table 4-7, page 43 typedef struct { @@ -157,13 +278,88 @@ typedef struct { uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24) uint8 descriptor_subtype; // FEATURE_UNIT uint8 unit_id; // unique within audio function - uint8 source_id; // id of the unit or terminal to which this unit is connected - uint8 control_size; // bma_controls is 2 bytes per element - uint16 master_bma_control; // only if control_size is 2 - uint16 bma_controls[2]; // if control_size = 2 and two channels - uint8 feature_string; // if control_size = 2 and ch = 2 + uint8 source_id; // id of the unit or terminal to + // which this unit is connected + uint8 control_size; // size of element in bma_controls array + uint8 bma_controls[1]; // the size of element must be equal + // to control_size!! + // the channel 0 is master one! +/* uint8 feature_string; */ // be afraid of the variable size + // of bma_controls array! +} _PACKED usb_feature_unit_descriptor_r1; + +typedef struct { + uint8 length; + uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24) + uint8 descriptor_subtype; // FEATURE_UNIT + uint8 unit_id; // unique within audio function + uint8 source_id; // id of the unit or terminal to + // which this unit is connected + uint32 bma_controls[1]; // the channel 0 is master one! +/* uint8 feature_string; */ // be afraid of the variable size of + // bma_controls array! } _PACKED usb_feature_unit_descriptor; +// bitset for feature control bitmap +// Table 4-7, page 44, bmaControls field +enum FeatureControls { + MuteControl1 = 0x0001, + VolumeControl1 = 0x0002, + BassControl1 = 0x0004, + MidControl1 = 0x0008, + TrebleControl1 = 0x0010, + GraphEqControl1 = 0x0020, + AutoGainControl1 = 0x0040, + DelayControl1 = 0x0080, + BassBoostControl1 = 0x0100, + LoudnessControl1 = 0x0200, + // Release 2.0 + MuteControl = 0x00000003, + VolumeControl = 0x0000000c, + BassControl = 0x00000030, + MidControl = 0x000000c0, + TrebleControl = 0x00000300, + GraphEqControl = 0x00000c00, + AutoGainControl = 0x00003000, + DelayControl = 0x0000c000, + BassBoostControl = 0x00030000, + LoudnessControl = 0x000c0000, + InputGainControl = 0x00300000, + InputGainPadControl = 0x00c00000, + PhaseInverterControl= 0x03000000, + UnderflowControl = 0x0c000000, + OverflowControl = 0x30000000 +}; + +// processing unit descriptor +// Table 4-8, page 45 +typedef struct { + uint8 length; + uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24) + uint8 descriptor_subtype; // PROCESSING_UNIT + uint8 unit_id; // unique within audio function + uint16 process_type; // type of processing this unit is performing + uint8 num_input_pins; // number of input pins of this unit + uint8 input_pins[1]; // array of source ids for the processing unit + // use usb_output_channels_descriptor + // to parse the rest +// TODO - the bmControl!!!! +} _PACKED usb_processing_unit_descriptor; + +// extension unit descriptor +// Table 4-15, page 56 +typedef struct { + uint8 length; + uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24) + uint8 descriptor_subtype; // EXTENSION_UNIT + uint8 unit_id; // unique within audio function + uint16 extension_code; // vendor-specific code identifying this unit + uint8 num_input_pins; // number of input pins + uint8 input_pins[1]; // array of source ids for the processing unit + // use usb_output_channels_descriptor + // to parse the rest +} _PACKED usb_extension_unit_descriptor; + // Audio Streaming (as) descriptors @@ -177,6 +373,20 @@ typedef struct { uint8 terminal_link; // terminal ID to which this endp is connected uint8 delay; // delay in # of frames uint16 format_tag; // wFormatTag, 0x0001 = PCM +} _PACKED usb_as_interface_descriptor_r1; + +typedef struct { + uint8 length; // 7 + uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24) + uint8 descriptor_subtype; // UAS_AS_GENERAL + uint8 terminal_link; // terminal ID to which this endp is connected + uint8 bm_controls; // controls bitmap + uint8 format_type; // type of audio streaming use + uint32 bm_formats; // audio data formats to be used with + // this interface + uint8 num_output_pins; // number of physical channels in the claster + uint32 channel_config; // spatial location of channels + uint8 channel_names; // id of name string of first physical channel } _PACKED usb_as_interface_descriptor; // Class-specific As Isochronous Audio Data Endpoint descriptor @@ -185,9 +395,10 @@ typedef struct { uint8 length; // 7 uint8 descriptor_type; // UAS_CS_ENDPOINT descriptor type (0x25) uint8 descriptor_subtype; // UAS_EP_GENERAL - uint8 attributes; // d0=samfq d1=pitch d7=maxpacketsonly - uint8 lock_delay_units; // 1=ms 2=decpcmsampl - uint16 lock_delay; // time for endp to lock internal clock recovery circuitry + uint8 attributes; // d0 = samfq d1 = pitch d7 = maxpacketsonly + uint8 lock_delay_units; // 1 = ms 2 = decpcmsampl + uint16 lock_delay; // time for endp to lock internal + // clock recovery circuitry } _PACKED usb_as_cs_endpoint_descriptor; @@ -197,7 +408,7 @@ typedef struct { uint8 data[3]; } _PACKED usb_triplet; -// and +// and /* * Audio data formats spec @@ -237,18 +448,48 @@ typedef union { // Table 2-1, page 10 typedef struct { - uint8 length; // 0e for + uint8 length; // 0e for uint8 descriptor_type; // UAS_CS_INTERFACE (0x24) uint8 descriptor_subtype; // UAS_FORMAT_TYPE (0x02) uint8 format_type; // UAF_FORMAT_TYPE_I (0x01) uint8 nr_channels; // hopefully 2 uint8 subframe_size; // 1, 2, or 4 bytes uint8 bit_resolution; // 8, 16 or 20 bits - uint8 sam_freq_type; // 0 == continuous, 1 == a fixed number of discrete sam freqs + uint8 sam_freq_type; // 0 == continuous, 1 == a fixed + // number of discrete sam freqs usb_audio_sam_freq_descr sf; // union // uint8 sam_freq[12 * 3]; } _PACKED usb_type_I_format_descriptor; +// Table 2-4, page 13 +typedef struct { + uint8 length; // 0e for + uint8 descriptor_type; // UAS_CS_INTERFACE (0x24) + uint8 descriptor_subtype; // UAS_FORMAT_TYPE (0x02) + uint8 format_type; // UAF_FORMAT_TYPE_II (0x02) + uint16 max_bit_rate; // max bit rate in kbits/sec + uint16 samples_per_frame; // samples per frame + uint8 sam_freq_type; // 0 == continuous, 1 == a fixed + // number of discrete sam freqs + usb_audio_sam_freq_descr sf; // union +// uint8 sam_freq[12 * 3]; +} _PACKED usb_type_II_format_descriptor; + +// Table 2-23, page 26 (the same as Type I) +typedef struct { + uint8 length; // 0e for + uint8 descriptor_type; // UAS_CS_INTERFACE (0x24) + uint8 descriptor_subtype; // UAS_FORMAT_TYPE (0x02) + uint8 format_type; // UAF_FORMAT_TYPE_III (0x03) + uint8 nr_channels; // hopefully 2 + uint8 subframe_size; // 1, 2, or 4 bytes + uint8 bit_resolution; // 8, 16 or 20 bits + uint8 sam_freq_type; // 0 == continuous, 1 == a fixed + // number of discrete sam freqs + usb_audio_sam_freq_descr sf; // union +// uint8 sam_freq[12 * 3]; +} _PACKED usb_type_III_format_descriptor; + #ifdef __cplusplus } #endif diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/USB_audio_utils.c b/src/add-ons/kernel/drivers/audio/usb_audio/USB_audio_utils.c deleted file mode 100644 index 8c29417861..0000000000 --- a/src/add-ons/kernel/drivers/audio/usb_audio/USB_audio_utils.c +++ /dev/null @@ -1,395 +0,0 @@ -/* - * USB audio spec utils - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include "USB_audio_spec.h" -#include "USB_audio_utils.h" - -extern void set_triplet(int8* ptr, uint32 param); - -uint32 -get_triplet(int8 *ptr) -{ - uint32 lsb = ptr[0] & 0xff; - uint32 hsb = ptr[1] & 0xff; - uint32 msb = ptr[2] & 0xff; - - return (msb << 16) | (hsb << 8) | (lsb); -} - - -/* - * dump a whole configuration - */ - -void -dump_usb_configuration_info(const usb_configuration_info *conf) -{ - usb_interface_info *inf; - int i, j, k; - bool isactive; - - dprintf("Dumping usb_configuration_info... %d interfaces\n", conf->interface_count); - for (i = 0; i < conf->interface_count; i++) { - dprintf("INTERFACE %d, %d alternatitives:\n", i, conf->interface[i].alt_count); - for (j=0; jinterface[i].alt_count; j++) { - isactive = &conf->interface[i].alt[j] == conf->interface[i].active; - dprintf(" altconf #%d (%s):\n", j, isactive?"*":"-"); - if (isactive) { - inf = conf->interface[i].active; - dump_interface(inf); - } else { - inf = &conf->interface[i].alt[j]; - dump_interface(inf); -// dprintf(" #endp: %d\n",conf->interface[i].alt[j].endpoint_count); - } - } - } -} - - -void -dump_interface_descriptor(usb_interface_descriptor *intf_des) -{ - dprintf(" interface descriptor: %d bytes\n", intf_des->length); - dprintf(" interface_number: %d\n", intf_des->interface_number); - dprintf(" alternate_setting: %d\n", intf_des->alternate_setting); - dprintf(" num_endpoints: %d\n", intf_des->num_endpoints); - dprintf(" class/sub/prot: %d/%d/%d\n", intf_des->interface_class, - intf_des->interface_subclass, intf_des->interface_protocol); - dprintf(" interface str: %d\n", intf_des->interface); -} - - -void -dump_interface(usb_interface_info *inf) -{ - int i; - usb_interface_descriptor *intf_des = inf->descr; - - dump_interface_descriptor(intf_des); - if (inf->descr->interface_class == 1) { - if (inf->descr->interface_subclass == 1) { - dump_usb_class_110(inf->endpoint, inf->endpoint_count, - (int8**)inf->generic, inf->generic_count); - } - if (inf->descr->interface_subclass == 2) { - dump_usb_class_120(inf->endpoint, inf->endpoint_count, - (int8**)inf->generic, inf->generic_count); - } - } - //if (inf->descr->interface_class == 3) { - // if (inf->descr->interface_subclass == 0) { - // dump_usb_class_300(inf->endpoint, inf->endpoint_count, - // (int8**)inf->generic, inf->generic_count); -// // dprintf(" \n"); - // } - //} -} - - -char * -attr(int8 attrib) -{ - switch (attrib) { - case 0x00: return "Control"; - case 0x01: return "Isochronous"; - case 0x02: return "Bulk"; - case 0x03: return "Interrupt"; - } - return "?"; -} - - -void -dump_endpoint_info(int i, usb_endpoint_info *ep_inf) -{ - dprintf(" endpoint %d:\n",i); - dprintf(" address: %d, %s\n",ep_inf->descr->endpoint_address & 0x07, (ep_inf->descr->endpoint_address & 0x80)?"IN":"OUT"); - dprintf(" transfer type: %s\n",attr(ep_inf->descr->attributes)); - dprintf(" max_packet_size: %d\n",ep_inf->descr->max_packet_size); - dprintf(" interval: %d\n",ep_inf->descr->interval); - dprintf(" usb_pipe handle: %p\n",ep_inf->handle); -} - -/* - * dump class 1/1/0 - */ - -void -dump_usb_class_110(usb_endpoint_info *ep_inf, size_t endpoint_count, - int8 **ptr, size_t count) -{ - int i, length; - int8* data; - int descr; - - dprintf(" Class 1/1/0 - Audio Control\n"); - dprintf(" With %d endpoints:\n", endpoint_count); - for (i=0; ibcd_release_no); - dprintf(" total_length: %d bytes\n", h->total_length); - dprintf(" in_collection: %d\n", h->in_collection); - for (i = 0; i < h->in_collection; i++) { - dprintf(" %d\n", h->interface_numbers[i]); - } -} - - -void -dump_usb_input_terminal_descriptor(int8 *data) -{ - usb_input_terminal_descriptor* it = (usb_input_terminal_descriptor*)data; - - dump_descr(data); - dprintf(" INPUT_TERMINAL\n"); - dprintf(" terminal id: %d\n", it->terminal_id); - dprintf(" terminal_type: 0x%x\n", it->terminal_type); - dprintf(" assoc_terminal: %d\n", it->assoc_terminal); - dprintf(" num_channels: %d\n", it->num_channels); - dprintf(" channel_config: %d\n", it->channel_config); - dprintf(" channel_names: %d\n", it->channel_names); - dprintf(" terminal str: %d\n", it->terminal); -} - - -void -dump_usb_output_terminal_descriptor(int8 *data) -{ - usb_output_terminal_descriptor* ot = (usb_output_terminal_descriptor*)data; - - dump_descr(data); - dprintf(" OUTPUT_TERMINAL\n"); - dprintf(" terminal id: %d\n", ot->terminal_id); - dprintf(" terminal_type: 0x%x\n", ot->terminal_type); - dprintf(" assoc_terminal: %d\n", ot->assoc_terminal); - dprintf(" source_id: %d\n", ot->source_id); - dprintf(" terminal str: %d\n", ot->terminal); -} - - -void -dump_usb_selector_unit_descriptor(int8 *data) -{ - int i; - usb_selector_unit_descriptor* su = (usb_selector_unit_descriptor*)data; - - dump_descr(data); - dprintf(" SELECTOR_UNIT\n"); - dprintf(" unit_id: %d\n", su->unit_id); - dprintf(" num_input_pins: %d\n", su->num_input_pins); - for (i=0; i < su->num_input_pins; i++) { - dprintf(" %d\n", su->input_pins[i]); - } - dprintf(" selector str: %d\n", su->input_pins[i]); // or selector_string -} - - -void -bma_dec(int16 bitmap) -{ - if (bitmap & 0x0001) dprintf("Mute "); - if (bitmap & 0x0002) dprintf("Volume "); - if (bitmap & 0x0004) dprintf("Bass "); - if (bitmap & 0x0008) dprintf("Mid "); - if (bitmap & 0x0010) dprintf("Treble "); - if (bitmap & 0x0020) dprintf("Graphic EQ "); - if (bitmap & 0x0040) dprintf("Automatic Gain "); - if (bitmap & 0x0080) dprintf("Delay "); - if (bitmap & 0x0100) dprintf("Bass Boost "); - if (bitmap & 0x0200) dprintf("Loudness "); - dprintf("\n"); -} - - -void -dump_usb_feature_unit_descriptor(int8 *data) -{ - // warning: either doc is out of date, or this & 1325 implementation is wrong on control_size vs bma_controls[] length - int i, length; - usb_feature_unit_descriptor* fu = (usb_feature_unit_descriptor*)data; - - dump_descr(data); - length = data[0]; - dprintf(" FEATURE_UNIT\n"); - dprintf(" unit_id: %d\n", fu->unit_id); - dprintf(" source_id: %d\n", fu->source_id); - dprintf(" control_size: %d\n", fu->control_size); - if (fu->control_size == 2) { - dprintf(" master bma: "); - bma_dec(fu->master_bma_control); - for (i=0; i < fu->control_size; i++) { - dprintf(" ch %d bma: ", i); - bma_dec(fu->bma_controls[i]); - } - dprintf(" featurestr: %d\n", fu->feature_string); - } else { - dprintf(" err: unsupported control_size\n"); - } -} - -/* - * dump class 1/2/0 - */ - -void -dump_usb_class_120(usb_endpoint_info *ep_inf, - size_t endpoint_count, int8 **ptr, size_t count) -{ - int i, length; - int8* data; - dprintf(" Class 1/2/0 - Audio Streaming\n"); - - dprintf(" With %d endpoints:\n", endpoint_count); - for (i=0; iattributes); - dprintf(" lock_delay_units: %d\n", ep->lock_delay_units); - dprintf(" lock_delay: %d\n", ep->lock_delay); -} - - -/* - * Last resort... - */ - -void -dump_descr(int8 *data) -{ - dump_data(data + 1, *data); -} - - -void -dump_data(int8 *data, int length) -{ - int i; - for (i=0; i - -#ifdef __cplusplus -extern "C" { -#endif - -// A whole config -void dump_usb_configuration_info(const usb_configuration_info *conf); -void dump_interface_descriptor(usb_interface_descriptor* intf_des); -void dump_interface(usb_interface_info* inf); -void dump_endpoint_info(int i, usb_endpoint_info* ep_inf); - -// Class 1/1/0 (Audio Control) -void dump_usb_class_110(usb_endpoint_info* ep_inf, size_t endpoint_count, int8** ptr, size_t count); -void dump_usb_audiocontrol_header_descriptor(int8* data); -void dump_usb_input_terminal_descriptor(int8* data); -void dump_usb_output_terminal_descriptor(int8* data); -void dump_usb_selector_unit_descriptor(int8* data); -void dump_usb_feature_unit_descriptor(int8* data); - -// Class 1/2/0 (Audio Streaming) -void dump_usb_class_120(usb_endpoint_info* ep_inf, size_t endpoint_count, int8** ptr, size_t count); - -void dump_usb_as_interface_descriptor(int8* data); -void dump_usb_type_I_format_descriptor(int8* data); -void dump_usb_as_cs_endpoint_descriptor(int8* data); - -// utils -void dump_descr(int8* data); -void dump_data(int8* data, int length); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/makefile b/src/add-ons/kernel/drivers/audio/usb_audio/makefile new file mode 100644 index 0000000000..a47a5d2554 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/makefile @@ -0,0 +1,127 @@ +## BeOS Generic Makefile v2.2 ## + +## Fill in this file to specify the project being created, and the referenced +## makefile-engine will do all of the hard work for you. This handles both +## Intel and PowerPC builds of the BeOS. + +## Application Specific Settings --------------------------------------------- + +# specify the name of the binary +NAME=usb_audio + +# specify the type of binary +# APP: Application +# SHARED: Shared library or add-on +# STATIC: Static library archive +# DRIVER: Kernel Driver +TYPE=DRIVER + +# add support for new Pe and Eddie features +# to fill in generic makefile + +#%{ +# @src->@ + +# specify the source files to use +# full paths or paths relative to the makefile can be included +# all files, regardless of directory, will have their object +# files created in the common object directory. +# Note that this means this makefile will not work correctly +# if two source files with the same name (source.c or source.cpp) +# are included from different directories. Also note that spaces +# in folder names do not work well with this makefile. +SRCS=Driver.cpp\ + Device.cpp\ + AudioControlInterface.cpp\ + AudioStreamingInterface.cpp\ + Stream.cpp\ + Settings.cpp + +# specify the resource files to use +# full path or a relative path to the resource file can be used. +RSRCS= + +# @<-src@ +#%} + +# end support for Pe and Eddie + +# specify additional libraries to link against +# there are two acceptable forms of library specifications +# - if your library follows the naming pattern of: +# libXXX.so or libXXX.a you can simply specify XXX +# library: libbe.so entry: be +# +# - if your library does not follow the standard library +# naming scheme you need to specify the path to the library +# and it's name +# library: my_lib.a entry: my_lib.a or path/my_lib.a +LIBS= + +# specify additional paths to directories following the standard +# libXXX.so or libXXX.a naming scheme. You can specify full paths +# or paths relative to the makefile. The paths included may not +# be recursive, so include all of the paths where libraries can +# be found. Directories where source files are found are +# automatically included. +LIBPATHS= + +# additional paths to look for system headers +# thes use the form: #include
+# source file directories are NOT auto-included here +SYSTEM_INCLUDE_PATHS=../../../../../../headers/private/kernel \ + ../../../../../../headers/private/kernel/util \ + ../../../../../../headers/private/media + +# additional paths to look for local headers +# thes use the form: #include "header" +# source file directories are automatically included +LOCAL_INCLUDE_PATHS=../../../../../../build/config_headers + +# specify the level of optimization that you desire +# NONE, SOME, FULL +OPTIMIZE= + +# specify any preprocessor symbols to be defined. The symbols will not +# have their values set automatically; you must supply the value (if any) +# to use. For example, setting DEFINES to "DEBUG=1" will cause the +# compiler option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG" +# would pass "-DDEBUG" on the compiler's command line. +DEFINES= + +# specify special warning levels +# if unspecified default warnings will be used +# NONE = supress all warnings +# ALL = enable all warnings +WARNINGS=ALL + +# specify whether image symbols will be created +# so that stack crawls in the debugger are meaningful +# if TRUE symbols will be created +SYMBOLS= + +# specify debug settings +# if TRUE will allow application to be run from a source-level +# debugger. Note that this will disable all optimzation. +DEBUGGER= + +# specify additional compiler flags for all files +COMPILER_FLAGS= + +# specify additional linker flags +LINKER_FLAGS= + +# specify the version of this particular item +# (for example, -app 3 4 0 d 0 -short 340 -long "340 "`echo -n -e '\302\251'`"1999 GNU GPL") +# This may also be specified in a resource. +APP_VERSION= + +# (for TYPE == DRIVER only) Specify desired location of driver in the /dev +# hierarchy. Used by the driverinstall rule. E.g., DRIVER_PATH = video/usb will +# instruct the driverinstall rule to place a symlink to your driver's binary in +# ~/add-ons/kernel/drivers/dev/video/usb, so that your driver will appear at +# /dev/video/usb when loaded. Default is "misc". +DRIVER_PATH=audio/hmulti + +## include the makefile-engine +include $(BUILDHOME)/etc/makefile-engine diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/sound.h b/src/add-ons/kernel/drivers/audio/usb_audio/sound.h deleted file mode 100644 index 0e64ee3559..0000000000 --- a/src/add-ons/kernel/drivers/audio/usb_audio/sound.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - Copyright 1999, Be Incorporated. All Rights Reserved. - This file may be used under the terms of the Be Sample Code License. - - Data structures and control calls for using the sound driver -*/ -#ifndef _SOUND_H -#define _SOUND_H - - -#include - - -enum adc_source { - line=0, aux1, mic, loopback -}; - -enum sample_rate { - kHz_8_0 = 0, kHz_5_51, kHz_16_0, kHz_11_025, kHz_27_42, kHz_18_9, - kHz_32_0, kHz_22_05, kHz_37_8 = 9, kHz_44_1 = 11, kHz_48_0, kHz_33_075, - kHz_9_6, kHz_6_62 -}; - -enum sample_format { - linear_8bit_unsigned_mono = 0, linear_8bit_unsigned_stereo, - ulaw_8bit_companded_mono, ulaw_8bit_companded_stereo, - linear_16bit_little_endian_mono, linear_16bit_little_endian_stereo, - alaw_8bit_companded_mono, alaw_8bit_companded_stereo, - sample_format_reserved_1, sample_format_reserved_2, - adpcm_4bit_mono, adpcm_4bit_stereo, - linear_16bit_big_endian_mono, linear_16bit_big_endian_stereo, - sample_format_reserved_3, sample_format_reserved_4 -}; - -struct channel { - enum adc_source adc_source; /* adc input source */ - char adc_gain; /* 0..15 adc gain, in 1.5 dB steps */ - char mic_gain_enable; /* non-zero enables 20 dB MIC input gain */ - char aux1_mix_gain; /* 0..31 aux1 mix to output gain. 12.0 to -34.5 dB in 1.5dB steps */ - char aux1_mix_mute; /* non-zero mutes aux1 mix */ - char aux2_mix_gain; /* 0..31 aux2 mix to output gain. 12.0 to -34.5 dB in 1.5dB steps */ - char aux2_mix_mute; /* non-zero mutes aux2 mix */ - char line_mix_gain; /* 0..31 line mix to output gain. 12.0 to -34.5 dB in 1.5dB steps */ - char line_mix_mute; /* non-zero mutes line mix */ - char dac_attn; /* 0..61 dac attenuation, in -1.5 dB steps */ - char dac_mute; /* non-zero mutes dac output */ -}; - -typedef struct sound_setup { - struct channel left; /* left channel setup */ - struct channel right; /* right channel setup */ - enum sample_rate sample_rate; /* sample rate */ - enum sample_format playback_format;/* sample format for playback */ - enum sample_format capture_format; /* sample format for capture */ - char dither_enable; /* non-zero enables dither on 16 => 8 bit */ - char loop_attn; /* 0..64 adc to dac loopback attenuation, in -1.5 dB steps */ - char loop_enable; /* non-zero enables loopback */ - char output_boost; /* zero (2.0 Vpp) non-zero (2.8 Vpp) output level boost */ - char highpass_enable;/* non-zero enables highpass filter in adc */ - char mono_gain; /* 0..64 mono speaker gain */ - char mono_mute; /* non-zero mutes speaker */ -} sound_setup; - - -/* ----- - control opcodes for sound driver ------ */ - -enum { - SOUND_GET_PARAMS = B_DEVICE_OP_CODES_END, - SOUND_SET_PARAMS, /* 10000 */ - SOUND_SET_PLAYBACK_COMPLETION_SEM, - SOUND_SET_CAPTURE_COMPLETION_SEM, - SOUND_GET_PLAYBACK_TIMESTAMP, /* 10003 */ - SOUND_GET_CAPTURE_TIMESTAMP, - SOUND_DEBUG_ON, - SOUND_DEBUG_OFF, /* 10006 */ - SOUND_UNSAFE_WRITE, - SOUND_UNSAFE_READ, - SOUND_LOCK_FOR_DMA, /* 10009 */ - SOUND_SET_CAPTURE_PREFERRED_BUF_SIZE, /* 10010 */ - SOUND_SET_PLAYBACK_PREFERRED_BUF_SIZE, /* 10011 */ - SOUND_GET_CAPTURE_PREFERRED_BUF_SIZE, /* 10012 */ - SOUND_GET_PLAYBACK_PREFERRED_BUF_SIZE /* 10013 */ -}; - -#endif /* _SOUND_H */ diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/usb_audio.c b/src/add-ons/kernel/drivers/audio/usb_audio/usb_audio.c deleted file mode 100644 index 57514ef8cb..0000000000 --- a/src/add-ons/kernel/drivers/audio/usb_audio/usb_audio.c +++ /dev/null @@ -1,906 +0,0 @@ -/* Copyright 2000 Be Incorporated. All Rights Reserved. -** This file may be used under the terms of the Be Sample Code -** License. -*/ - -#include -#include -#include -#include -#include - -#include -#include "audio.h" // USB Audio Class - -#include "sound.h" // OLD-style ioctl()'s -#include "R3MediaDefs.h" // media kit - -#include "USB_audio_utils.h" // Mark-Jan - -#define DEBUG_DRIVER 1 - -#if DEBUG_DRIVER -#define DPRINTF(x) dprintf x -#else -#define DPRINTF(x) ((void)0) -#endif - -static status_t device_open(const char *name, uint32 flags, void **cookie); -static status_t device_close(void *cookie); -static status_t device_free(void *cookie); -static status_t device_control(void *cookie, uint32 op, void *data, size_t len); -//static status_t pcm_read(void *cookie, off_t pos, void *data, size_t *len); -static status_t device_write(void *cookie, off_t pos, const void *data, size_t *len); -//static status_t pcm_writev(void *cookie, off_t pos, const iovec *vec, size_t count, size_t *len); /* */ - -/* ~44 kHz 16bit stereo */ -#define SAMPLE_SIZE 4 -#define QS_OUT 1764 //(44*SAMPLE_SIZE*TS) /* 1764, TS=10 was: (44*SAMPLE_SIZE*TS) */ - -#define TS 10 // was 4 /* 4 ms buffers */ -#define NB 2 - -int32 api_version = B_CUR_DRIVER_API_VERSION; - -int qs_out = QS_OUT; - -typedef struct audiodev audiodev; -typedef struct iso_channel iso_channel; -typedef struct iso_packet iso_packet; - -struct iso_packet -{ - struct iso_packet* next; - struct iso_channel* channel; - void* buffer; - uint32 status; - size_t buffer_size; - rlea* rle_array; - int64 rw_count; - bigtime_t time; -}; - -struct iso_channel -{ - usb_pipe* ep; - - iso_packet* next_to_queue; - iso_packet* current_rw; - size_t remain; - char* buf_ptr; - sem_id num_available_packets; - area_id buffer_area; - iso_packet iso_packets[NB]; - - int64 rw_count; - int64 current_count; - bigtime_t current_time; - - int active; -}; - -struct audiodev { - audiodev *next; - - int open; - int number; - const usb_device *dev; - - iso_channel out_channel; - - // audio specific stuff - sound_setup setup; - sem_id playback_sem; - - bigtime_t write_time; - uint64 write_total; - -}; - - -static sound_setup usb_sound_setup = { - // left channel - { - aux1, // adc_source - 20, // adc_gain - 0, // mic_gain_enable - 30, // aux1_mix_gain - 0, // aux1_mix_mute - 20, // aux2_mix_gain - 0, // aux2_mix_mute - 20, // line_mix_gain - 0, // line_mix_mute - 10, // dac_attn - 0 // dac_mute - }, - // right channel - { - aux1, // adc_source - 20, // adc_gain - 0, // mic_gain_enable - 30, // aux1_mix_gain - 0, // aux1_mix_mute - 20, // aux2_mix_gain - 0, // aux2_mix_mute - 20, // line_mix_gain - 0, // line_mix_mute - 10, // dac_attn - 0 // dac_mute - }, - kHz_44_1, // sample_rate - 16, // playback_format (ignored, always 16bit-linear) - 16, // capture_format (ignored, always 16bit-linear) - 0, // dither_enable - 0, // mic_attn - 0, // mic_enable - 0, // output_boost (ignored, always on) - 0, // highpass_enable (ignored, always on) - 0, // mono_gain - 1 // mono_mute -}; - - -/* handy strings for referring to ourself */ -#define ID "usb_audio: " -static const char *drivername = "usb_audio"; -static const char *basename = "audio/old/usb_audio/"; - -/* list of device instances and names for publishing */ -static audiodev *device_list = NULL; -static sem_id dev_list_lock = -1; -static int device_count = 0; - -static char **device_names = NULL; - -/* handles for the USB bus manager */ -static char *usb_name = B_USB_MODULE_NAME; -static usb_module_info *usb; - -/* USB Isoch Transaction Stuff ------------------------------------------ -** -** Create and Destroy an Isochronous "channel" and handle queueing -** packets, callbacks, etc. -** -*/ - -void -init_iso_channel(iso_channel* ch, usb_pipe* ep, size_t buf_size, bool is_in) -{ - int pn; - void* big_buffer; - - ch->ep = ep; - - ch->buffer_area = create_area("usb_device_buffer", - (void **)&big_buffer, - B_ANY_KERNEL_ADDRESS, - ((buf_size*NB) + B_PAGE_SIZE-1) & ~(B_PAGE_SIZE-1), - B_CONTIGUOUS, - B_READ_AREA | B_WRITE_AREA); - - DPRINTF((ID "buffer_area %d @ 0x%08x\n", ch->buffer_area, big_buffer)); - - for(pn=0; pniso_packets[pn].channel = ch; - ch->iso_packets[pn].buffer = (char*)big_buffer + buf_size*pn; - - ch->iso_packets[pn].rw_count = 0; - ch->iso_packets[pn].time = 0; - if(is_in) { - ch->iso_packets[pn].rle_array = malloc(sizeof(rlea) + (4-1)*sizeof(rle)); - ch->iso_packets[pn].rle_array->length = 4; - } else { - ch->iso_packets[pn].rle_array = NULL; - } - - ch->iso_packets[pn].next = &ch->iso_packets[(pn+1)%NB]; - } - - ch->current_rw = ch->next_to_queue = &ch->iso_packets[0]; - if(!is_in) ch->current_rw = &ch->iso_packets[NB-1]; - ch->num_available_packets = create_sem(0, "iso_channel"); - - ch->remain = 0; - ch->buf_ptr = 0; - ch->active = 1; - ch->rw_count = 0; - ch->current_count = 0; - ch->current_time = 0; -} - -void -uninit_iso_channel(iso_channel* ch) -{ - int pn; - - ch->active = 0; - - for(pn=0; pniso_packets[pn].rle_array); - } - - delete_sem(ch->num_available_packets); - delete_area(ch->buffer_area); -} - -static void cb_notify_out(void *cookie, uint32 status, - void *data, uint32 actual_len); -static void queue_packet_out(iso_channel* ch); - -static void -cb_notify_out(void *cookie, uint32 status, void *data, uint32 actual_len) -{ - iso_packet* const packet = (iso_packet*) cookie; - iso_channel* const channel = packet->channel; - int num_queued_packets; - static bigtime_t t, t0; - static c; - int i; - int32 sc; - - #if 0 - uint8 *data8 = (uint8*)data; - if (data8[0] != 0 && - data8[1] != 0 && - data8[2] != 0) { - uint32 val = (data8[2] << 16) | (data8[1] << 8) | data8[0]; - - //dprintf(ID "actual_len: %ld\n", actual_len); - - dprintf(ID "cb_notify_out: data - 0x%.2X%.2X%.2X / %d (%d)\n",data8[2],data8[1],data8[0],val, val>>10); - } - #endif - - packet->status = status; - packet->buffer_size = qs_out; - - if((status == B_OK) && channel->active) { - queue_packet_out(channel); - channel->current_rw = packet; - - channel->rw_count += packet->buffer_size; - packet->rw_count = channel->rw_count; - - // hack for startup time - get_sem_count(channel->num_available_packets, &sc); - if(sc < 2) release_sem(channel->num_available_packets); - } -} - -static void -queue_packet_out(iso_channel* ch) -{ - iso_packet* packet = ch->next_to_queue; - status_t s; - - - - //dprintf("queue_isochronous_out(%d, %p, %p)\n", qs_out, packet->buffer, packet->rle_array); - if(s = usb->queue_isochronous(ch->ep, packet->buffer, qs_out, - NULL, TS, cb_notify_out, packet)) { - dprintf(ID "packet out %p status %d\n", packet, s); - } else { - ch->next_to_queue = packet->next; - } -} - -void -start_iso_channel_out(iso_channel* channel) -{ - int pn; - - for(pn=0; pncancel_queued_transfers(channel->ep); -} - - -/* These rather inelegant routines are used to assign numbers to -** device instances so that they have unique names in devfs. -*/ - -static uint32 device_numbers = 0; - -static int -get_number() -{ - int num; - - for(num = 0; num < 32; num++) { - if(!(device_numbers & (1 << num))){ - device_numbers |= (1 << num); - return num; - } - } - - return -1; -} - -static void -put_number(int num) -{ - device_numbers &= ~(1 << num); -} - -/* Device addition and removal --------------------------------------- -** -** add_device() and remove_device() are used to create and tear down -** device instances. They are driver by the callbacks device_added() -** and device_removed() which are invoked by the USB bus manager. -*/ - -static audiodev * -add_device(const usb_device *dev, const usb_configuration_info *conf) -{ - audiodev *ad = NULL; - int num,i,ifc,alt; - const usb_interface_info *ii; - status_t st; - usb_pipe *out; - - DPRINTF((ID "add_device(%p, %p)\n", dev, conf)); - - if((num = get_number()) < 0) { - return NULL; - } - - for(ifc = 0; ifc < conf->interface_count; ifc++){ - for(alt = 0; alt < conf->interface[ifc].alt_count; alt++) { - int j; - int16 sample_rate; - ii = &conf->interface[ifc].alt[alt]; - - - - /* does it have an AudioStreaming interface? */ - if(ii->descr->interface_class != AC_AUDIO) continue; - - /* mute that damn mic! */ - if (ii->descr->interface_subclass == AC_AUDIOCONTROL) { - int8 **ptr = (int8 **)ii->generic; - int32 count = ii->generic_count; - int8 *data = 0; - for (i=0; isource_id: 0x%X, controls = 0x%X\n", fu->source_id, fu->controls)); - } break; - case AC_PROCESSING_UNIT: - case AC_EXTENSION_UNIT: - //dump_data(&data[0], length); - break; - } - } - } - } - - if(ii->descr->interface_subclass != AC_AUDIOSTREAMING) continue; - - dump_usb_class_120(ii->endpoint, ii->endpoint_count, - (int8**)ii->generic, ii->generic_count); - - if(ii->endpoint_count != 1) continue; - - /* ignore input endpoints */ - if(ii->endpoint[0].descr->endpoint_address & 0x80) continue; - - //dprintf(ID "found an AudioStreaming output interface @ %d/%d..\n", ifc, alt); - - /* does it support 2 channel, 16 bit audio? */ - for(i = 0; i < ii->generic_count; i++){ - uint8 param_block[3]; - size_t written=10; - status_t status = 10; - usb_format_type_descr *ft = (usb_format_type_descr*) ii->generic[i]; - - //dprintf(ID "type: 0x%X(0x%X), subtype: 0x%X(0x%X), length: %d(%d), num_channels: %d(%d), subframe_size: %d(%d), sample_freq_type: %d(%d)\n", - // ft->type, AC_CS_INTERFACE, ft->subtype, AC_FORMAT_TYPE, - // ft->length, sizeof(usb_format_type_descr), - // ft->num_channels, 2,ft->subframe_size, 2,ft->sample_freq_type, 0); - - if(ft->type != AC_CS_INTERFACE) continue; - if(ft->subtype != AC_FORMAT_TYPE) continue; - if(ft->length < sizeof(usb_format_type_descr)) continue; - if(ft->num_channels != 2) continue; - if(ft->subframe_size != 2) continue; - - #if 0 - status_t (*send_request)(const usb_device *d, - uint8 request_type, uint8 request, - uint16 value, uint16 index, uint16 length, - void *data, size_t data_len, size_t *actual_len); - #endif - - // TRY SET: - // page 96, usb audio spec 1.0 - - sample_rate = 44100; - param_block[0] = sample_rate; - param_block[1] = sample_rate >> 8; - param_block[2] = sample_rate >> 16; - status = usb->send_request(dev, - USB_REQTYPE_CLASS|USB_REQTYPE_ENDPOINT_OUT, - AC_SET_CUR, - 1 << 8, // sampling freq control - ii->endpoint[0].descr->endpoint_address, /* endpoint */ - 3, - param_block, 3, &written); - - dprintf(ID "%d bytes written, 0x%.2X%.2X%.2X back (status: %d).\n", - written, param_block[0],param_block[1],param_block[2], status); - - #if 0 // LINUX - usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN) - SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { - #endif - - - #if 0 - dump_endpoint_info(ii->endpoint[0].descr->endpoint_address, &ii->endpoint[0]); - - // TRY SET: - // page 96, usb audio spec 1.0 - param_block[0] = 0x44; - param_block[1] = 0xAC; - param_block[2] = 0x00; - usb->send_request(dev, - 34 /* 00100010b */, AC_SET_CUR, - 0x0100, /* SAMPLING_FREQ_CONTROL, high-byte */ - ii->endpoint[0].descr->endpoint_address, /* endpoint */ - 3, - param_block, 3, &written); - - //tic - //if(ft->sample_freq_type != 0) continue; - - dprintf(ID "sample freq: 0x%X%X%X/0x%X%X%X\n", - ft->lower_sample_freq[0], - ft->lower_sample_freq[1], - ft->lower_sample_freq[2], - ft->upper_sample_freq[0], - ft->upper_sample_freq[1], - ft->upper_sample_freq[2]); - #endif - - - dprintf(ID "found a 2ch, 16bit isoch output (ifc=%d, alt=%d, mp=%d)\n", - ifc, alt, ii->endpoint[0].descr->max_packet_size); - - goto got_one; - } - } - } - -fail: - put_number(num); - if(ad) free(ad); - return NULL; - -got_one: - if((ad = (audiodev *) malloc(sizeof(audiodev))) == NULL) goto fail; - - ad->dev = dev; - ad->number = num; - ad->open = 0; - - - if((st = usb->set_alt_interface(dev, ii)) != B_OK) { - dprintf(ID "set_alt_interface(0) returns %d\n", st); - goto fail; - } - - if((st = usb->set_configuration(dev,conf)) != B_OK) { - dprintf(ID "set_configuration() returns %d\n", st); - goto fail; - } - - out = ii->endpoint[0].handle; - DPRINTF((ID "added %p (out=%p) (/dev/%s%d)\n", ad, out, basename, num)); - - if((st = usb->set_pipe_policy(out, NB, TS, SAMPLE_SIZE)) != B_OK){ - dprintf(ID "set_pipe_policy(out) returns %d\n", st); - goto fail; - } - - init_iso_channel(&ad->out_channel, out, qs_out, FALSE); - start_iso_channel_out(&ad->out_channel); - - /* add it to the list of devices so it will be published, etc */ - acquire_sem(dev_list_lock); - ad->next = device_list; - device_list = ad; - device_count++; - release_sem(dev_list_lock); - - return ad; -} - -static void -remove_device(audiodev *ad) -{ - uninit_iso_channel(&ad->out_channel); - put_number(ad->number); - free(ad); -} - -static status_t -device_added(const usb_device *dev, void **cookie) -{ - const usb_configuration_info *conf; - audiodev *ad; - int i; - - DPRINTF((ID "device_added(%p,...)\n", dev)); - - if(conf = usb->get_nth_configuration(dev, 0)) { - if((ad = add_device(dev, conf)) != NULL){ - *cookie = (void*) ad; - return B_OK; - } - } - return B_ERROR; -} - - -static status_t -device_removed(void *cookie) -{ - audiodev *ad = (audiodev *) cookie; - int i; - - DPRINTF((ID "device_removed(%p)\n",ad)); - - acquire_sem(dev_list_lock); - - /* mark it as inactive and encourage IO to finish */ - ad->out_channel.active = 0; - delete_sem(ad->out_channel.num_available_packets); - - /* remove it from the list of devices */ - if(ad == device_list){ - device_list = ad->next; - } else { - audiodev *n; - for(n = device_list; n; n = n->next){ - if(n->next == ad){ - n->next = ad->next; - break; - } - } - } - device_count--; - - /* tear it down if it's not open -- - otherwise the last device_free() will handle it */ - - if(ad->open == 0){ - remove_device(ad); - } else { - DPRINTF((ID "device /dev/%s%d still open -- marked for removal\n", - basename,ad->number)); - } - - release_sem(dev_list_lock); - - return B_OK; -} - -/* Device Hooks ----------------------------------------------------------- -** -** Here we implement the posixy driver hooks (open/close/read/write/ioctl) -*/ - -static status_t -device_open(const char *dname, uint32 flags, void **cookie) -{ - audiodev *ad; - int n; - - n = atoi(dname + strlen(basename)); - - DPRINTF((ID "device_open(\"%s\",%d,...)\n",dname,flags)); - - acquire_sem(dev_list_lock); - for(ad = device_list; ad; ad = ad->next){ - if(ad->number == n){ - if(ad->out_channel.active) { - ad->open++; - - // set default values - memcpy(&ad->setup, &usb_sound_setup, sizeof(struct sound_setup)); - ad->write_time = 0; - ad->write_total = 0; - *cookie = ad; - - - release_sem(dev_list_lock); - return B_OK; - } else { - dprintf(ID "device is going offline. cannot open\n"); - } - } - } - release_sem(dev_list_lock); - return B_ERROR; -} - -static status_t -device_close (void *cookie) -{ - audiodev *ad = (audiodev *)cookie; - if(ad->out_channel.active) stop_iso_channel(&ad->out_channel); - DPRINTF((ID "device_close() name = \"%s%d\"\n",basename,ad->number)); - return B_OK; -} - -static status_t -device_free(void *cookie) -{ - audiodev *ad = (audiodev *) cookie; - - DPRINTF((ID "device_free() name = \"%s%d\"\n",basename,ad->number)); - - acquire_sem(dev_list_lock); - ad->open--; - if((ad->open == 0) && (ad->out_channel.active == 0)) remove_device(ad); - release_sem(dev_list_lock); - - return B_OK; -} - -static status_t -device_read(void *cookie, off_t pos, void *buf, size_t *count) -{ - return B_ERROR; -} - -static status_t -device_write(void *cookie, off_t pos, const void *buf, size_t *count) -{ - audiodev* ad = (audiodev*) cookie; - iso_channel* channel = &ad->out_channel; - status_t st; - -#if 0 - DPRINTF((ID "device_write(%p,%Ld,0x%x,%d) name = \"%s%d\"\n", - cookie, pos, buf, *count, basename, ad->number)); -#endif - - if(channel->remain == 0) { - st = acquire_sem_etc(channel->num_available_packets, 1, B_RELATIVE_TIMEOUT, 4*1000*1000); - if(st) { - if(st == B_TIMED_OUT) { - dprintf("st = B_TIMED_OUT\n"); - *count = 0; - } - return st; - } - - if(channel->current_rw == channel->next_to_queue) dprintf("e"); - - channel->remain = channel->current_rw->buffer_size; - channel->buf_ptr = channel->current_rw->buffer; - } - - if(channel->remain >= *count) { - memcpy(channel->buf_ptr, buf, *count); - channel->remain -= *count; - channel->buf_ptr += *count; - } else { - memcpy(channel->buf_ptr, buf, channel->remain); - *count = channel->remain; - channel->remain = 0; - channel->buf_ptr = NULL; - } - - ad->write_total += *count; - return B_OK; -} - -static status_t -device_control(void *cookie, uint32 msg, void *data, size_t len) -{ - status_t err = B_BAD_VALUE; - audiodev *ad = (audiodev *)cookie; - // only support old-style drivers. - switch (msg) { - case SOUND_GET_PARAMS: { - sound_setup *setup = (sound_setup *)data; - memcpy(setup, &ad->setup, sizeof(struct sound_setup)); - err = B_OK; - } break; - case SOUND_SET_PARAMS: { - sound_setup *setup = (sound_setup *)data; - memcpy(&ad->setup, setup, sizeof(struct sound_setup)); - err = B_OK; - } break; - case SOUND_SET_PLAYBACK_COMPLETION_SEM: { - ad->playback_sem = *(sem_id *)data; - } break; - case SOUND_UNSAFE_WRITE: { - audio_buffer_header *header = (audio_buffer_header *)data; - int32 data_length = header->reserved_1 - sizeof(*header); - int16 *data_address = (int16 *)(header + 1); - iso_channel* ch = &ad->out_channel; - - //header->time = (ch->current_time - ch->remain * 2500LL / 441 - // + NB * TS * 1000); - //header->sample_clock = (ch->current_rw->rw_count - ch->remain) * 2500LL / 441; - - - header->sample_clock = ad->write_total*1000/48;// * 10000/441; - header->time = header->sample_clock; - - device_write(cookie, 0, data_address, &data_length); - - release_sem(ad->playback_sem); - err = B_OK; - } break; - - case 10012: - case 10013: - *(int32*)data = QS_OUT; - err = B_OK; - break; - - default: { - dprintf(ID "ioctl() - unknown msg %d\n", msg); - } break; - } - - return err; -} - - -/* Driver Hooks --------------------------------------------------------- -** -** These functions provide the glue used by DevFS to load/unload -** the driver and also handle registering with the USB bus manager -** to receive device added and removed events -*/ - -static usb_notify_hooks notify_hooks = -{ - &device_added, - &device_removed -}; - -usb_support_descriptor supported_devices[] = -{ - { AC_AUDIO, 0, 0, 0, 0} -}; - -_EXPORT status_t -init_hardware(void) -{ - return B_OK; -} - -_EXPORT status_t -init_driver(void) -{ - int i; - DPRINTF((ID "init_driver(), built %s %s\n", __DATE__, __TIME__)); - -#if DEBUG_DRIVER && !defined(__HAIKU__) - if(load_driver_symbols(drivername) == B_OK) { - DPRINTF((ID "loaded symbols\n")); - } else { - DPRINTF((ID "no symbols for you!\n")); - } -#endif - - if(get_module(usb_name,(module_info**) &usb) != B_OK){ - dprintf(ID "cannot get module \"%s\"\n",usb_name); - return B_ERROR; - } - - if((dev_list_lock = create_sem(1,"dev_list_lock")) < 0){ - put_module(usb_name); - return dev_list_lock; - } - - usb->register_driver(drivername, supported_devices, 1, NULL); - usb->install_notify(drivername, ¬ify_hooks); - - return B_OK; -} - -_EXPORT void -uninit_driver(void) -{ - int i; - - DPRINTF((ID "uninit_driver()\n")); - - usb->uninstall_notify(drivername); - - delete_sem(dev_list_lock); - - put_module(usb_name); - - if(device_names){ - for(i=0;device_names[i];i++) free(device_names[i]); - free(device_names); - } - -} - -_EXPORT const char** -publish_devices() -{ - audiodev *ad; - int i; - - DPRINTF((ID "publish_devices()\n")); - - if(device_names){ - for(i=0;device_names[i];i++) free((char *) device_names[i]); - free(device_names); - } - - acquire_sem(dev_list_lock); - device_names = (char **) malloc(sizeof(char*) * (device_count + 1)); - if(device_names){ - for(i = 0, ad = device_list; ad; ad = ad->next){ - if(ad->out_channel.active){ - if(device_names[i] = (char *) malloc(strlen(basename) + 4)){ - sprintf(device_names[i],"%s%d",basename,ad->number); - DPRINTF((ID "publishing: \"/dev/%s\"\n",device_names[i])); - i++; - } - } - } - device_names[i] = NULL; - } - release_sem(dev_list_lock); - - return (const char **) device_names; -} - -static device_hooks DeviceHooks = { - device_open, - device_close, - device_free, - device_control, - device_read, - device_write, -}; - -_EXPORT device_hooks* -find_device(const char* name) -{ - return &DeviceHooks; -} diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/usb_audio.settings b/src/add-ons/kernel/drivers/audio/usb_audio/usb_audio.settings new file mode 100644 index 0000000000..4e4f748bbc --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/usb_audio.settings @@ -0,0 +1,28 @@ +## +## Driver for USB Audio Device Class devices. +## Copyright (c) 2009,10,12 S.Zharski +## Distributed under the terms of the MIT license. +## + +## trace [on|off] - activate additional tracing. +## default value: off + +trace on + +## logfile [full path to private log file] +## default path value: /var/log/usb_asix.log +## if disabled - all output goes to syslog + +logfile /boot/home/usb_audio.log + +## reset_logfile [on|off] - truncate private log file on driver/system restart +## default value: off +## + +# reset_logfile off + +## add_timestamp [on|off] - add time of writing the string in private log file. +## default value: on +## + +# add_timestamp off