* Driver for USB Audio Device Class devices.
* Copyright (c) 2009-13 S.Zharski <imker@gmx.li>
* Distributed under the tems of the MIT license.
*
*/
#include "AudioControlInterface.h"
#include <usb/USB_audio.h>
#include "Device.h"
#include "Driver.h"
#include "Settings.h"
#define CTL_ID(_CS, _CN, _ID, _IF) \
(((_CS) << 24) | ((_CN) << 16) | ((_ID) << 8) | (_IF))
#define CS_FROM_CTLID(_ID) (0xff & ((_ID) >> 24))
#define CN_FROM_CTLID(_ID) (0xff & ((_ID) >> 16))
#define ID_FROM_CTLID(_ID) (0xff & ((_ID) >> 8))
#define REQ_VALUE(_ID) (0xffff & ((_ID) >> 16))
#define REQ_INDEX(_ID) (0xffff & (_ID))
struct _Designation {
uint32 ch;
uint32 bus;
} gDesignations[AudioControlInterface::kChannels] = {
{ B_CHANNEL_LEFT, B_CHANNEL_STEREO_BUS },
{ B_CHANNEL_RIGHT, B_CHANNEL_STEREO_BUS },
{ B_CHANNEL_CENTER, B_CHANNEL_SURROUND_BUS },
{ B_CHANNEL_SUB, B_CHANNEL_SURROUND_BUS },
{ B_CHANNEL_REARLEFT, B_CHANNEL_STEREO_BUS },
{ B_CHANNEL_REARRIGHT, B_CHANNEL_STEREO_BUS },
{ B_CHANNEL_FRONT_LEFT_CENTER, B_CHANNEL_STEREO_BUS },
{ B_CHANNEL_FRONT_RIGHT_CENTER, B_CHANNEL_STEREO_BUS },
{ B_CHANNEL_BACK_CENTER, B_CHANNEL_SURROUND_BUS },
{ B_CHANNEL_SIDE_LEFT, B_CHANNEL_STEREO_BUS },
{ B_CHANNEL_SIDE_RIGHT, B_CHANNEL_STEREO_BUS },
{ B_CHANNEL_TOP_CENTER, B_CHANNEL_SURROUND_BUS },
{ B_CHANNEL_TOP_FRONT_LEFT, B_CHANNEL_STEREO_BUS },
{ B_CHANNEL_TOP_FRONT_CENTER, B_CHANNEL_SURROUND_BUS },
{ B_CHANNEL_TOP_FRONT_RIGHT, B_CHANNEL_STEREO_BUS },
{ B_CHANNEL_TOP_BACK_LEFT, B_CHANNEL_STEREO_BUS },
{ B_CHANNEL_TOP_BACK_CENTER, B_CHANNEL_SURROUND_BUS },
{ B_CHANNEL_TOP_BACK_RIGHT, B_CHANNEL_STEREO_BUS }
};
struct _MixPageCollector : public Vector<multi_mix_control> {
_MixPageCollector(const char* pageName) {
multi_mix_control page;
memset(&page, 0, sizeof(multi_mix_control));
page.flags = B_MULTI_MIX_GROUP;
strlcpy(page.name, pageName, sizeof(page.name));
PushBack(page);
}
};
_AudioControl::_AudioControl(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header)
:
fStatus(B_NO_INIT),
fInterface(interface),
fSubType(Header->descriptor_subtype),
fID(0),
fSourceID(0),
fStringIndex(0)
{
}
_AudioControl::~_AudioControl()
{
}
AudioChannelCluster*
_AudioControl::OutCluster()
{
if (SourceID() == 0 || fInterface == NULL)
return NULL;
_AudioControl* control = fInterface->Find(SourceID());
if (control == NULL)
return NULL;
return control->OutCluster();
}
AudioChannelCluster::AudioChannelCluster()
:
fOutChannelsNumber(0),
fChannelsConfig(0),
fChannelNames(0)
{
}
AudioChannelCluster::~AudioChannelCluster()
{
}
bool
AudioChannelCluster::HasChannel(uint32 location)
{
return (fChannelsConfig & location) == location;
}
_Terminal::_Terminal(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header)
:
_AudioControl(interface, Header),
fTerminalType(0),
fAssociatedTerminal(0),
fClockSourceId(0),
fControlsBitmap(0)
{
}
_Terminal::~_Terminal()
{
}
const char*
_Terminal::Name()
{
return _GetTerminalDescription(fTerminalType);
}
bool
_Terminal::IsUSBIO()
{
return (fTerminalType & 0xff00) == USB_AUDIO_UNDEFINED_USB_IO;
}
const char*
_Terminal::_GetTerminalDescription(uint16 TerminalType)
{
static struct _pair {
uint16 type;
const char* description;
} termInfoPairs[] = {
{ USB_AUDIO_UNDEFINED_USB_IO, "USB I/O" },
{ USB_AUDIO_STREAMING_USB_IO, "USB I/O" },
{ USB_AUDIO_VENDOR_USB_IO, "Vendor USB I/O" },
{ USB_AUDIO_UNDEFINED_IN, "Undefined Input" },
{ USB_AUDIO_MICROPHONE_IN, "Microphone" },
{ USB_AUDIO_DESKTOPMIC_IN, "Desktop Microphone" },
{ USB_AUDIO_PERSONALMIC_IN, "Personal Microphone" },
{ USB_AUDIO_OMNI_MIC_IN, "Omni-directional Mic" },
{ USB_AUDIO_MICS_ARRAY_IN, "Microphone Array" },
{ USB_AUDIO_PROC_MICS_ARRAY_IN, "Processing Mic Array" },
{ USB_AUDIO_UNDEFINED_OUT, "Undefined Output" },
{ USB_AUDIO_SPEAKER_OUT, "Speaker" },
{ USB_AUDIO_HEAD_PHONES_OUT, "Headphones" },
{ USB_AUDIO_HMD_AUDIO_OUT, "Head Mounted Disp.Audio" },
{ USB_AUDIO_DESKTOP_SPEAKER, "Desktop Speaker" },
{ USB_AUDIO_ROOM_SPEAKER, "Room Speaker" },
{ USB_AUDIO_COMM_SPEAKER, "Communication Speaker" },
{ USB_AUDIO_LFE_SPEAKER, "LFE Speaker" },
{ USB_AUDIO_UNDEFINED_IO, "Undefined I/O" },
{ USB_AUDIO_HANDSET_IO, "Handset" },
{ USB_AUDIO_HEADSET_IO, "Headset" },
{ USB_AUDIO_SPEAKER_PHONE_IO, "Speakerphone" },
{ USB_AUDIO_SPEAKER_PHONEES_IO, "Echo-supp Speakerphone" },
{ USB_AUDIO_SPEAKER_PHONEEC_IO, "Echo-cancel Speakerphone" },
{ USB_AUDIO_UNDEF_TELEPHONY_IO, "Undefined Telephony" },
{ USB_AUDIO_PHONE_LINE_IO, "Phone Line" },
{ USB_AUDIO_TELEPHONE_IO, "Telephone" },
{ USB_AUDIO_DOWNLINE_PHONE_IO, "Down Line Phone" },
{ USB_AUDIO_UNDEFINEDEXT_IO, "Undefined External I/O" },
{ USB_AUDIO_ANALOG_CONNECTOR_IO, "Analog Connector" },
{ USB_AUDIO_DAINTERFACE_IO, "Digital Audio Interface" },
{ USB_AUDIO_LINE_CONNECTOR_IO, "Line Connector" },
{ USB_AUDIO_LEGACY_CONNECTOR_IO, "LegacyAudioConnector" },
{ USB_AUDIO_SPDIF_INTERFACE_IO, "S/PDIF Interface" },
{ USB_AUDIO_DA1394_STREAM_IO, "1394 DA Stream" },
{ USB_AUDIO_DV1394_STREAMSOUND_IO, "1394 DV Stream Soundtrack" },
{ USB_AUDIO_ADAT_LIGHTPIPE_IO, "Alesis DAT Stream" },
{ USB_AUDIO_TDIF_IO, "Tascam Digital Interface" },
{ USB_AUDIO_MADI_IO, "AES Multi-channel interface" },
{ USB_AUDIO_UNDEF_EMBEDDED_IO, "Undefined Embedded I/O" },
{ USB_AUDIO_LC_NOISE_SOURCE_OUT, "Level Calibration Noise Source" },
{ USB_AUDIO_EQUALIZATION_NOISE_OUT, "Equalization Noise" },
{ USB_AUDIO_CDPLAYER_IN, "CD Player" },
{ USB_AUDIO_DAT_IO, "DAT" },
{ USB_AUDIO_DCC_IO, "DCC" },
{ USB_AUDIO_MINI_DISK_IO, "Mini Disk" },
{ USB_AUDIO_ANALOG_TAPE_IO, "Analog Tape" },
{ USB_AUDIO_PHONOGRAPH_IN, "Phonograph" },
{ USB_AUDIO_VCR_AUDIO_IN, "VCR Audio" },
{ USB_AUDIO_VIDEO_DISC_AUDIO_IN, "Video Disc Audio" },
{ USB_AUDIO_DVD_AUDIO_IN, "DVD Audio" },
{ USB_AUDIO_TV_TUNER_AUDIO_IN, "TV Tuner Audio" },
{ USB_AUDIO_SAT_RECEIVER_AUDIO_IN, "Satellite Receiver Audio" },
{ USB_AUDIO_CABLE_TUNER_AUDIO_IN, "Cable Tuner Audio" },
{ USB_AUDIO_DSS_AUDIO_IN, "DSS Audio" },
{ USB_AUDIO_RADIO_RECEIVER_IN, "Radio Receiver" },
{ USB_AUDIO_RADIO_TRANSMITTER_IN, "Radio Transmitter" },
{ USB_AUDIO_MULTI_TRACK_RECORDER_IO,"Multi-track Recorder" },
{ USB_AUDIO_SYNTHESIZER_IO, "Synthesizer" },
{ USB_AUDIO_PIANO_IO, "Piano" },
{ USB_AUDIO_GUITAR_IO, "Guitar" },
{ USB_AUDIO_DRUMS_IO, "Percussion Instrument" },
{ USB_AUDIO_INSTRUMENT_IO, "Musical Instrument" }
};
for (size_t i = 0; i < B_COUNT_OF(termInfoPairs); i++)
if (termInfoPairs[i].type == TerminalType)
return termInfoPairs[i].description;
TRACE(ERR, "Unknown Terminal Type: %#06x", TerminalType);
return "Unknown";
}
InputTerminal::InputTerminal(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header)
:
_AudioChannelCluster<_Terminal>(interface, Header)
{
usb_audio_input_terminal_descriptor* Terminal
= (usb_audio_input_terminal_descriptor*) Header;
fID = Terminal->terminal_id;
fTerminalType = Terminal->terminal_type;
fAssociatedTerminal = Terminal->assoc_terminal;
TRACE(UAC, "Input Terminal ID:%d >>>\n", fID);
TRACE(UAC, "Terminal type:%s (%#06x)\n",
_GetTerminalDescription(fTerminalType), fTerminalType);
TRACE(UAC, "Assoc.terminal:%d\n", fAssociatedTerminal);
if (fInterface->SpecReleaseNumber() < 0x200) {
fOutChannelsNumber = Terminal->r1.num_channels;
fChannelsConfig = Terminal->r1.channel_config;
fChannelNames = Terminal->r1.channel_names;
fStringIndex = Terminal->r1.terminal;
} else {
fClockSourceId = Terminal->r2.clock_source_id;
fOutChannelsNumber = Terminal->r2.num_channels;
fChannelsConfig = Terminal->r2.channel_config;
fChannelNames = Terminal->r2.channel_names;
fControlsBitmap = Terminal->r2.bm_controls;
fStringIndex = Terminal->r2.terminal;
TRACE(UAC, "Clock Source ID:%d\n", fClockSourceId);
TRACE(UAC, "Controls Bitmap:%#04x\n", fControlsBitmap);
}
TRACE(UAC, "Out.channels num:%d\n", fOutChannelsNumber);
TRACE(UAC, "Channels config:%#06x\n", fChannelsConfig);
TRACE(UAC, "Channels names:%d\n", fChannelNames);
TRACE(UAC, "StringIndex:%d\n", fStringIndex);
fStatus = B_OK;
}
InputTerminal::~InputTerminal()
{
}
const char*
InputTerminal::Name()
{
if (fTerminalType == USB_AUDIO_STREAMING_USB_IO)
return "USB Input";
return _Terminal::Name();
}
OutputTerminal::OutputTerminal(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header)
:
_Terminal(interface, Header)
{
usb_audio_output_terminal_descriptor* Terminal
= (usb_audio_output_terminal_descriptor*) Header;
fID = Terminal->terminal_id;
fTerminalType = Terminal->terminal_type;
fAssociatedTerminal = Terminal->assoc_terminal;
fSourceID = Terminal->source_id;
TRACE(UAC, "Output Terminal ID:%d >>>\n", fID);
TRACE(UAC, "Terminal type:%s (%#06x)\n",
_GetTerminalDescription(fTerminalType), fTerminalType);
TRACE(UAC, "Assoc.terminal:%d\n", fAssociatedTerminal);
TRACE(UAC, "Source ID:%d\n", fSourceID);
if (fInterface->SpecReleaseNumber() < 0x200) {
fStringIndex = Terminal->r1.terminal;
} else {
fClockSourceId = Terminal->r2.clock_source_id;
fControlsBitmap = Terminal->r2.bm_controls;
fStringIndex = Terminal->r2.terminal;
TRACE(UAC, "Clock Source ID:%d\n", fClockSourceId);
TRACE(UAC, "Controls Bitmap:%#04x\n", fControlsBitmap);
}
TRACE(UAC, "StringIndex:%d\n", fStringIndex);
fStatus = B_OK;
}
OutputTerminal::~OutputTerminal()
{
}
const char*
OutputTerminal::Name()
{
if (fTerminalType == USB_AUDIO_STREAMING_USB_IO)
return "USB Output";
return _Terminal::Name();
}
MixerUnit::MixerUnit(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header)
:
_AudioChannelCluster<_AudioControl>(interface, Header),
fBmControlsR2(0)
{
usb_audio_mixer_unit_descriptor* Mixer
= (usb_audio_mixer_unit_descriptor*) Header;
fID = Mixer->unit_id;
TRACE(UAC, "Mixer ID:%d >>>\n", fID);
TRACE(UAC, "Number of input pins:%d\n", Mixer->num_input_pins);
for (size_t i = 0; i < Mixer->num_input_pins; i++) {
fInputPins.PushBack(Mixer->input_pins[i]);
TRACE(UAC, "Input pin #%d:%d\n", i, fInputPins[i]);
}
uint8* mixerControlsData = NULL;
uint8 mixerControlsSize = 0;
if (fInterface->SpecReleaseNumber() < 0x200) {
usb_audio_output_channels_descriptor_r1* OutChannels
= (usb_audio_output_channels_descriptor_r1*)
&Mixer->input_pins[Mixer->num_input_pins];
fOutChannelsNumber = OutChannels->num_output_pins;
fChannelsConfig = OutChannels->channel_config;
fChannelNames = OutChannels->channel_names;
mixerControlsData = (uint8*) ++OutChannels;
mixerControlsSize = Mixer->length - 10 - Mixer->num_input_pins;
fStringIndex = *(mixerControlsData + mixerControlsSize);
#if 0
if (fOutChannelsNumber > 2) {
mixerControlsData[0] = 0x80;
mixerControlsData[1] = 0x40;
mixerControlsData[2] = 0x20;
mixerControlsData[3] = 0x10;
mixerControlsData[4] = 0x08;
mixerControlsData[5] = 0x04;
mixerControlsData[6] = 0x02;
mixerControlsData[7] = 0x01;
mixerControlsData[8] = 0x80;
mixerControlsData[9] = 0x40;
mixerControlsData[10] = 0x02;
mixerControlsData[11] = 0x01;
}
#endif
} else {
usb_audio_output_channels_descriptor* OutChannels
= (usb_audio_output_channels_descriptor*)
&Mixer->input_pins[Mixer->num_input_pins];
fOutChannelsNumber = OutChannels->num_output_pins;
fChannelsConfig = OutChannels->channel_config;
fChannelNames = OutChannels->channel_names;
mixerControlsData = (uint8*) ++OutChannels;
mixerControlsSize = Mixer->length - 13 - Mixer->num_input_pins;
fBmControlsR2 = *(mixerControlsData + mixerControlsSize);
fStringIndex = *(mixerControlsData + mixerControlsSize + 1);
TRACE(UAC, "Control Bitmap:%#04x\n", fBmControlsR2);
}
TRACE(UAC, "Out channels number:%d\n", fOutChannelsNumber);
TRACE(UAC, "Out channels config:%#06x\n", fChannelsConfig);
TRACE(UAC, "Out channels names:%d\n", fChannelNames);
TRACE(UAC, "Controls Size:%d\n", mixerControlsSize);
for (size_t i = 0; i < mixerControlsSize; i++) {
fControlsBitmap.PushBack(mixerControlsData[i]);
TRACE(UAC, "Controls Data[%d]:%#x\n", i, fControlsBitmap[i]);
}
TRACE(UAC, "StringIndex:%d\n", fStringIndex);
fStatus = B_OK;
}
MixerUnit::~MixerUnit()
{
}
bool
MixerUnit::HasProgrammableControls()
{
for (int i = 0; i < fControlsBitmap.Count(); i++)
if (fControlsBitmap[i] != 0)
return true;
return false;
}
bool
MixerUnit::IsControlProgrammable(int inChannel, int outChannel)
{
AudioChannelCluster* outCluster = OutCluster();
if (outCluster == NULL) {
TRACE(ERR, "Output cluster is not valid.\n");
return false;
}
bool result = false;
if (outChannel < outCluster->ChannelsCount()) {
int index = inChannel * outCluster->ChannelsCount()+ outChannel;
result = (fControlsBitmap[index >> 3] & (0x80 >> (index & 7))) != 0;
}
return result;
}
SelectorUnit::SelectorUnit(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header)
:
_AudioControl(interface, Header),
fControlsBitmap(0)
{
usb_audio_selector_unit_descriptor* Selector
= (usb_audio_selector_unit_descriptor*) Header;
fID = Selector->unit_id;
TRACE(UAC, "Selector ID:%d >>>\n", fID);
TRACE(UAC, "Number of input pins:%d\n", Selector->num_input_pins);
for (size_t i = 0; i < Selector->num_input_pins; i++) {
fInputPins.PushBack(Selector->input_pins[i]);
TRACE(UAC, "Input pin #%d:%d\n", i, fInputPins[i]);
}
if (fInterface->SpecReleaseNumber() < 0x200) {
fStringIndex = Selector->input_pins[Selector->num_input_pins];
} else {
fControlsBitmap = Selector->input_pins[Selector->num_input_pins];
fStringIndex = Selector->input_pins[Selector->num_input_pins + 1];
TRACE(UAC, "Controls Bitmap:%d\n", fControlsBitmap);
}
TRACE(UAC, "StringIndex:%d\n", fStringIndex);
fStatus = B_OK;
}
SelectorUnit::~SelectorUnit()
{
}
AudioChannelCluster*
SelectorUnit::OutCluster()
{
if (fInterface == NULL)
return NULL;
for (int i = 0; i < fInputPins.Count(); i++) {
_AudioControl* control = fInterface->Find(fInputPins[i]);
if (control == NULL)
continue;
if (control->OutCluster() != NULL)
return control->OutCluster();
}
return NULL;
}
FeatureUnit::FeatureUnit(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header)
:
_AudioControl(interface, Header)
{
usb_audio_feature_unit_descriptor* Feature
= (usb_audio_feature_unit_descriptor*) Header;
fID = Feature->unit_id;
TRACE(UAC, "Feature ID:%d >>>\n", fID);
fSourceID = Feature->source_id;
TRACE(UAC, "Source ID:%d\n", fSourceID);
uint8 controlSize = 4;
uint8 channelsCount = (Feature->length - 6) / controlSize;
uint8* ControlsBitmapPointer = (uint8*)&Feature->r2.bma_controls[0];
if (fInterface->SpecReleaseNumber() < 0x200) {
controlSize = Feature->r1.control_size;
channelsCount = (Feature->length - 7) / Feature->r1.control_size;
ControlsBitmapPointer = &Feature->r1.bma_controls[0];
}
TRACE(UAC, "Channel bitmap size:%d\n", controlSize);
TRACE(UAC, "Channels number:%d\n", channelsCount - 1);
for (size_t i = 0; i < channelsCount; i++) {
uint8* controlPointer = &ControlsBitmapPointer[i* controlSize];
switch(controlSize) {
case 1: fControlBitmaps.PushBack(*controlPointer); break;
case 2: fControlBitmaps.PushBack(*(uint16*)controlPointer); break;
case 4: fControlBitmaps.PushBack(*(uint32*)controlPointer); break;
default:
TRACE(ERR, "Feature control of unsupported size %d ignored\n",
controlSize);
continue;
}
NormalizeAndTraceChannel(i);
}
fStringIndex = ControlsBitmapPointer[channelsCount* controlSize];
TRACE(UAC, "StringIndex:%d\n", fStringIndex);
fStatus = B_OK;
}
FeatureUnit::~FeatureUnit()
{
}
const char*
FeatureUnit::Name()
{
_AudioControl* control = fInterface->Find(fSourceID);
while (control != NULL) {
if (control->SubType() != USB_AUDIO_AC_INPUT_TERMINAL)
break;
if (static_cast<_Terminal*>(control)->IsUSBIO())
break;
return control->Name();
}
control = fInterface->FindOutputTerminal(fID);
while (control != NULL) {
if (control->SubType() != USB_AUDIO_AC_OUTPUT_TERMINAL)
break;
if (static_cast<_Terminal*>(control)->IsUSBIO())
break;
return control->Name();
}
control = fInterface->Find(fSourceID);
if (control != NULL && control->Name() != NULL
&& strlen(control->Name()) > 0)
return control->Name();
return "Unknown";
}
bool
FeatureUnit::HasControl(int32 Channel, uint32 Control)
{
if (Channel >= fControlBitmaps.Count()) {
TRACE(ERR, "Out of limits error of retrieving control %#010x "
"for channel %d\n", Control, Channel);
return false;
}
return (Control & fControlBitmaps[Channel]) != 0;
}
void
FeatureUnit::NormalizeAndTraceChannel(int32 Channel)
{
if (Channel >= fControlBitmaps.Count()) {
TRACE(ERR, "Out of limits error of tracing channel %d\n", Channel);
return;
}
struct _RemapInfo {
uint32 rev1Bits;
uint32 rev2Bits;
const char* name;
} remapInfos[] = {
{ BMA_CTL_MUTE_R1, BMA_CTL_MUTE, "Mute" },
{ BMA_CTL_VOLUME_R1, BMA_CTL_VOLUME, "Volume" },
{ BMA_CTL_BASS_R1, BMA_CTL_BASS, "Bass" },
{ BMA_CTL_MID_R1, BMA_CTL_MID, "Mid" },
{ BMA_CTL_TREBLE_R1, BMA_CTL_TREBLE, "Treble" },
{ BMA_CTL_GRAPHEQ_R1, BMA_CTL_GRAPHEQ, "Graphic Equalizer" },
{ BMA_CTL_AUTOGAIN_R1, BMA_CTL_AUTOGAIN, "Automatic Gain"},
{ BMA_CTL_DELAY_R1, BMA_CTL_DELAY, "Delay" },
{ BMA_CTL_BASSBOOST_R1, BMA_CTL_BASSBOOST, "Bass Boost" },
{ BMA_CTL_LOUDNESS_R1, BMA_CTL_LOUDNESS, "Loudness" },
{ 0, BMA_CTL_INPUTGAIN, "InputGain" },
{ 0, BMA_CTL_INPUTGAINPAD, "InputGainPad" },
{ 0, BMA_CTL_PHASEINVERTER, "PhaseInverter" },
{ 0, BMA_CTL_UNDERFLOW, "Underflow" },
{ 0, BMA_CTL_OVERFLOW, "Overflow" }
};
if (Channel == 0)
TRACE(UAC, "Master channel bitmap:%#x\n", fControlBitmaps[Channel]);
else
TRACE(UAC, "Channel %d bitmap:%#x\n", Channel, fControlBitmaps[Channel]);
bool isRev1 = (fInterface->SpecReleaseNumber() < 0x200);
uint32 remappedBitmap = 0;
for (size_t i = 0; i < B_COUNT_OF(remapInfos); i++) {
uint32 bits = isRev1 ? remapInfos[i].rev1Bits : remapInfos[i].rev2Bits;
if ((fControlBitmaps[Channel] & bits) > 0) {
if (isRev1)
remappedBitmap |= remapInfos[i].rev2Bits;
TRACE(UAC, "\t%s\n", remapInfos[i].name);
}
}
if (isRev1) {
TRACE(UAC, "\t%#08x -> %#08x.\n",
fControlBitmaps[Channel], remappedBitmap);
fControlBitmaps[Channel] = remappedBitmap;
}
}
EffectUnit::EffectUnit(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header)
:
_AudioControl(interface, Header)
{
usb_audio_input_terminal_descriptor* descriptor
= (usb_audio_input_terminal_descriptor*) Header;
TRACE(UAC, "Effect Unit:%d >>>\n", descriptor->terminal_id);
}
EffectUnit::~EffectUnit()
{
}
ProcessingUnit::ProcessingUnit(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header)
:
_AudioChannelCluster<_AudioControl>(interface, Header),
fProcessType(0),
fControlsBitmap(0)
{
usb_audio_processing_unit_descriptor* Processing
= (usb_audio_processing_unit_descriptor*) Header;
fID = Processing->unit_id;
TRACE(UAC, "Processing ID:%d >>>\n", fID);
fProcessType = Processing->process_type;
TRACE(UAC, "Processing Type:%d\n", fProcessType);
TRACE(UAC, "Number of input pins:%d\n", Processing->num_input_pins);
for (size_t i = 0; i < Processing->num_input_pins; i++) {
fInputPins.PushBack(Processing->input_pins[i]);
TRACE(UAC, "Input pin #%d:%d\n", i, fInputPins[i]);
}
if (fInterface->SpecReleaseNumber() < 0x200) {
usb_audio_output_channels_descriptor_r1* OutChannels
= (usb_audio_output_channels_descriptor_r1*)
&Processing->input_pins[Processing->num_input_pins];
fOutChannelsNumber = OutChannels->num_output_pins;
fChannelsConfig = OutChannels->channel_config;
fChannelNames = OutChannels->channel_names;
} else {
usb_audio_output_channels_descriptor* OutChannels
= (usb_audio_output_channels_descriptor*)
&Processing->input_pins[Processing->num_input_pins];
fOutChannelsNumber = OutChannels->num_output_pins;
fChannelsConfig = OutChannels->channel_config;
fChannelNames = OutChannels->channel_names;
}
TRACE(UAC, "Out channels number:%d\n", fOutChannelsNumber);
TRACE(UAC, "Out channels config:%#06x\n", fChannelsConfig);
TRACE(UAC, "Out channels names:%d\n", fChannelNames);
uint8 controlsSize = Processing->length - 10 - Processing->num_input_pins;
TRACE(UAC, "Controls Size:%d\n", controlsSize);
uint8* controlsData = (uint8*) ++OutChannels;
for (size_t i = 0; i < controlsSize; i++) {
fProgrammableControls.PushBack(controlsData[i]);
TRACE(UAC, "Controls Data[%d]:%#x\n", i, controlsData[i]);
}
fStringIndex = *(controlsData + controlsSize);
TRACE(UAC, "StringIndex:%d\n", fStringIndex);
*/
fStatus = B_OK;
}
ProcessingUnit::~ProcessingUnit()
{
}
ExtensionUnit::ExtensionUnit(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header)
:
_AudioChannelCluster<_AudioControl>(interface, Header),
fExtensionCode(0),
fControlsBitmap(0)
{
usb_audio_extension_unit_descriptor* Extension
= (usb_audio_extension_unit_descriptor*) Header;
fID = Extension->unit_id;
TRACE(UAC, "Extension ID:%d >>>\n", fID);
fExtensionCode = Extension->extension_code;
TRACE(UAC, "Extension Type:%d\n", fExtensionCode);
TRACE(UAC, "Number of input pins:%d\n", Extension->num_input_pins);
for (size_t i = 0; i < Extension->num_input_pins; i++) {
fInputPins.PushBack(Extension->input_pins[i]);
TRACE(UAC, "Input pin #%d:%d\n", i, fInputPins[i]);
}
if (fInterface->SpecReleaseNumber() < 0x200) {
usb_audio_output_channels_descriptor_r1* OutChannels
= (usb_audio_output_channels_descriptor_r1*)
&Extension->input_pins[Extension->num_input_pins];
fOutChannelsNumber = OutChannels->num_output_pins;
fChannelsConfig = OutChannels->channel_config;
fChannelNames = OutChannels->channel_names;
} else {
usb_audio_output_channels_descriptor* OutChannels
= (usb_audio_output_channels_descriptor*)
&Extension->input_pins[Extension->num_input_pins];
fOutChannelsNumber = OutChannels->num_output_pins;
fChannelsConfig = OutChannels->channel_config;
fChannelNames = OutChannels->channel_names;
}
TRACE(UAC, "Out channels number:%d\n", fOutChannelsNumber);
TRACE(UAC, "Out channels config:%#06x\n", fChannelsConfig);
TRACE(UAC, "Out channels names:%d\n", fChannelNames);
uint8 controlsSize = Processing->length - 10 - Processing->num_input_pins;
TRACE(UAC, "Controls Size:%d\n", controlsSize);
uint8* controlsData = (uint8*) ++OutChannels;
for (size_t i = 0; i < controlsSize; i++) {
fProgrammableControls.PushBack(controlsData[i]);
TRACE(UAC, "Controls Data[%d]:%#x\n", i, controlsData[i]);
}
fStringIndex = *(controlsData + controlsSize);
TRACE(UAC, "StringIndex:%d\n", fStringIndex);
*/
fStatus = B_OK;
}
ExtensionUnit::~ExtensionUnit()
{
}
ClockSource::ClockSource(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header)
:
_AudioControl(interface, Header)
{
usb_audio_input_terminal_descriptor* descriptor
= (usb_audio_input_terminal_descriptor*) Header;
TRACE(UAC, "Clock Source:%d >>>\n", descriptor->terminal_id);
}
ClockSource::~ClockSource()
{
}
ClockSelector::ClockSelector(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header)
:
_AudioControl(interface, Header)
{
usb_audio_input_terminal_descriptor* descriptor
= (usb_audio_input_terminal_descriptor*) Header;
TRACE(UAC, "Clock Selector:%d >>>\n", descriptor->terminal_id);
}
ClockSelector::~ClockSelector()
{
}
ClockMultiplier::ClockMultiplier(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header)
:
_AudioControl(interface, Header)
{
usb_audio_input_terminal_descriptor* descriptor
= (usb_audio_input_terminal_descriptor*) Header;
TRACE(UAC, "Clock Multiplier:%d >>>\n", descriptor->terminal_id);
}
ClockMultiplier::~ClockMultiplier()
{
}
SampleRateConverter::SampleRateConverter(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header)
:
_AudioControl(interface, Header)
{
usb_audio_input_terminal_descriptor* descriptor
= (usb_audio_input_terminal_descriptor*) Header;
TRACE(UAC, "Sample Rate Converter:%d >>>\n", descriptor->terminal_id);
}
SampleRateConverter::~SampleRateConverter()
{
}
AudioControlInterface::AudioControlInterface(Device* device)
:
fInterface(0),
fStatus(B_NO_INIT),
fADCSpecification(0),
fFunctionCategory(0),
fControlsBitmap(0),
fDevice(device)
{
}
AudioControlInterface::~AudioControlInterface()
{
for (AudioControlsIterator I = fAudioControls.Begin();
I != fAudioControls.End(); I++)
delete I->Value();
fAudioControls.MakeEmpty();
fOutputTerminals.MakeEmpty();
fInputTerminals.MakeEmpty();
}
status_t
AudioControlInterface::Init(size_t interface, usb_interface_info* Interface)
{
for (size_t i = 0; i < Interface->generic_count; i++) {
usb_audiocontrol_header_descriptor* Header
= (usb_audiocontrol_header_descriptor* )Interface->generic[i];
if (Header->descriptor_type != USB_AUDIO_CS_INTERFACE) {
TRACE(ERR, "Ignore Audio Control of "
"unknown descriptor type %#04x.\n", Header->descriptor_type);
continue;
}
_AudioControl* control = NULL;
switch(Header->descriptor_subtype) {
default:
TRACE(ERR, "Ignore Audio Control of unknown "
"descriptor sub-type %#04x\n", Header->descriptor_subtype);
break;
case USB_AUDIO_AC_DESCRIPTOR_UNDEFINED:
TRACE(ERR, "Ignore Audio Control of undefined sub-type\n");
break;
case USB_AUDIO_AC_HEADER:
InitACHeader(interface, Header);
break;
case USB_AUDIO_AC_INPUT_TERMINAL:
control = new(std::nothrow) InputTerminal(this, Header);
break;
case USB_AUDIO_AC_OUTPUT_TERMINAL:
control = new(std::nothrow) OutputTerminal(this, Header);
break;
case USB_AUDIO_AC_MIXER_UNIT:
control = new(std::nothrow) MixerUnit(this, Header);
break;
case USB_AUDIO_AC_SELECTOR_UNIT:
control = new(std::nothrow) SelectorUnit(this, Header);
break;
case USB_AUDIO_AC_FEATURE_UNIT:
control = new(std::nothrow) FeatureUnit(this, Header);
break;
case USB_AUDIO_AC_PROCESSING_UNIT:
if (SpecReleaseNumber() < 200)
control = new(std::nothrow) ProcessingUnit(this, Header);
else
control = new(std::nothrow) EffectUnit(this, Header);
break;
case USB_AUDIO_AC_EXTENSION_UNIT:
if (SpecReleaseNumber() < 200)
control = new(std::nothrow) ExtensionUnit(this, Header);
else
control = new(std::nothrow) ProcessingUnit(this, Header);
break;
case USB_AUDIO_AC_EXTENSION_UNIT_R2:
control = new(std::nothrow) ExtensionUnit(this, Header);
break;
case USB_AUDIO_AC_CLOCK_SOURCE_R2:
control = new(std::nothrow) ClockSource(this, Header);
break;
case USB_AUDIO_AC_CLOCK_SELECTOR_R2:
control = new(std::nothrow) ClockSelector(this, Header);
break;
case USB_AUDIO_AC_CLOCK_MULTIPLIER_R2:
control = new(std::nothrow) ClockMultiplier(this, Header);
break;
case USB_AUDIO_AC_SAMPLE_RATE_CONVERTER_R2:
control = new(std::nothrow) SampleRateConverter(this, Header);
break;
}
if (control != 0 && control->InitCheck() == B_OK) {
switch(control->SubType()) {
case USB_AUDIO_AC_OUTPUT_TERMINAL:
fOutputTerminals.Put(control->SourceID(), control);
break;
case USB_AUDIO_AC_INPUT_TERMINAL:
fInputTerminals.Put(control->ID(), control);
break;
}
fAudioControls.Put(control->ID(), control);
} else
delete control;
}
return fStatus = B_OK;
}
_AudioControl*
AudioControlInterface::Find(uint8 id)
{
return fAudioControls.Get(id);
}
_AudioControl*
AudioControlInterface::FindOutputTerminal(uint8 id)
{
return fOutputTerminals.Get(id);
}
status_t
AudioControlInterface::InitACHeader(size_t interface,
usb_audiocontrol_header_descriptor* Header)
{
if (Header == NULL)
return fStatus = B_NO_INIT;
fInterface = interface;
fADCSpecification = Header->bcd_release_no;
TRACE(UAC, "ADCSpecification:%#06x\n", fADCSpecification);
if (fADCSpecification < 0x200) {
TRACE(UAC, "InterfacesCount:%d\n", Header->r1.in_collection);
for (size_t i = 0; i < Header->r1.in_collection; i++) {
fStreams.PushBack(Header->r1.interface_numbers[i]);
TRACE(UAC, "Interface[%d] number is %d\n", i, fStreams[i]);
}
} else {
fFunctionCategory = Header->r2.function_category;
fControlsBitmap = Header->r2.bm_controls;
TRACE(UAC, "Function Category:%#04x\n", fFunctionCategory);
TRACE(UAC, "Controls Bitmap:%#04x\n", fControlsBitmap);
}
return B_OK;
}
uint32
AudioControlInterface::GetChannelsDescription(
Vector<multi_channel_info>& Channels, multi_description* Description,
Vector<_AudioControl*>& Terminals, bool isForInput)
{
uint32 addedChannels = 0;
for (int32 i = 0; i < Terminals.Count(); i++) {
AudioChannelCluster* cluster = Terminals[i]->OutCluster();
if (cluster == NULL || cluster->ChannelsCount() <= 0) {
TRACE(ERR, "Terminal #%d ignored due null "
"channels cluster (%08x)\n", Terminals[i]->ID(), cluster);
continue;
}
uint32 channels = GetTerminalChannels(Channels, cluster,
isForInput ? B_MULTI_INPUT_CHANNEL : B_MULTI_OUTPUT_CHANNEL);
if (isForInput)
Description->input_channel_count += channels;
else
Description->output_channel_count += channels;
addedChannels += channels;
}
return addedChannels;
}
uint32
AudioControlInterface::GetTerminalChannels(Vector<multi_channel_info>& Channels,
AudioChannelCluster* cluster, channel_kind kind, uint32 connectors)
{
if (cluster->ChannelsCount() < 2) {
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();
for (size_t i = 0; i < kChannels; i++) {
uint32 designation = 1 << i;
if ((cluster->ChannelsConfig() & designation) == designation) {
multi_channel_info info;
info.channel_id = Channels.Count();
info.kind = kind;
info.designations= gDesignations[i].ch | gDesignations[i].bus;
info.connectors = connectors;
Channels.PushBack(info);
}
}
return Channels.Count() - startCount;
}
uint32
AudioControlInterface::GetBusChannelsDescription(
Vector<multi_channel_info>& Channels, multi_description* Description)
{
uint32 addedChannels = 0;
for (AudioControlsIterator I = fOutputTerminals.Begin();
I != fOutputTerminals.End(); I++) {
_AudioControl* control = I->Value();
if (static_cast<_Terminal*>(control)->IsUSBIO())
continue;
AudioChannelCluster* cluster = control->OutCluster();
if (cluster == 0 || cluster->ChannelsCount() <= 0) {
TRACE(ERR, "Terminal #%d ignored due null "
"channels cluster (%08x)\n", control->ID(), cluster);
continue;
}
uint32 channels = GetTerminalChannels(Channels,
cluster, B_MULTI_OUTPUT_BUS);
Description->output_bus_channel_count += channels;
addedChannels += channels;
}
for (AudioControlsIterator I = fInputTerminals.Begin();
I != fInputTerminals.End(); I++) {
_AudioControl* control = I->Value();
if (static_cast<_Terminal*>(control)->IsUSBIO())
continue;
AudioChannelCluster* cluster = control->OutCluster();
if (cluster == NULL || cluster->ChannelsCount() <= 0) {
TRACE(ERR, "Terminal #%d ignored due null "
"channels cluster (%08x)\n", control->ID(), cluster);
continue;
}
uint32 channels = GetTerminalChannels(Channels,
cluster, B_MULTI_INPUT_BUS);
Description->input_bus_channel_count += channels;
addedChannels += channels;
}
return addedChannels;
}
void
AudioControlInterface::_HarvestRecordFeatureUnits(_AudioControl* rootControl,
AudioControlsMap& Map)
{
if (rootControl == 0) {
TRACE(ERR, "Not processing due NULL root control.\n");
return;
}
switch(rootControl->SubType()) {
case USB_AUDIO_AC_SELECTOR_UNIT:
{
SelectorUnit* unit = static_cast<SelectorUnit*>(rootControl);
for (int i = 0; i < unit->fInputPins.Count(); i++)
_HarvestRecordFeatureUnits(Find(unit->fInputPins[i]), Map);
Map.Put(rootControl->ID(), rootControl);
}
break;
case USB_AUDIO_AC_FEATURE_UNIT:
Map.Put(rootControl->ID(), rootControl);
break;
}
}
void
AudioControlInterface::_HarvestOutputFeatureUnits(_AudioControl* rootControl,
AudioControlsMap& Map)
{
if (rootControl == 0) {
TRACE(ERR, "Not processing due NULL root control.\n");
return;
}
switch(rootControl->SubType()) {
case USB_AUDIO_AC_MIXER_UNIT:
{
MixerUnit* unit = static_cast<MixerUnit*>(rootControl);
for (int i = 0; i < unit->fInputPins.Count(); i++)
_HarvestOutputFeatureUnits(Find(unit->fInputPins[i]), Map);
Map.Put(rootControl->ID(), rootControl);
}
break;
case USB_AUDIO_AC_FEATURE_UNIT:
Map.Put(rootControl->ID(), rootControl);
break;
}
}
bool
AudioControlInterface::_InitGainLimits(multi_mix_control& Control)
{
bool canControl = false;
float current = 0.;
struct _GainInfo {
uint8 request;
int16 data;
float& value;
} gainInfos[] = {
{ USB_AUDIO_GET_CUR, 0, current },
{ USB_AUDIO_GET_MIN, 0, Control.gain.min_gain },
{ USB_AUDIO_GET_MAX, 0, Control.gain.max_gain },
{ USB_AUDIO_GET_RES, 0, Control.gain.granularity }
};
Control.gain.min_gain = 0.;
Control.gain.max_gain = 100.;
Control.gain.granularity = 1.;
size_t actualLength = 0;
for (size_t i = 0; i < B_COUNT_OF(gainInfos); i++) {
status_t status = gUSBModule->send_request(fDevice->USBDevice(),
USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_CLASS,
gainInfos[i].request, REQ_VALUE(Control.id),
REQ_INDEX(Control.id), sizeof(gainInfos[i].data),
&gainInfos[i].data, &actualLength);
if (status != B_OK || actualLength != sizeof(gainInfos[i].data)) {
TRACE(ERR, "Request %d (%04x:%04x) fail:%#08x; received %d of %d\n",
i, REQ_VALUE(Control.id), REQ_INDEX(Control.id), status,
actualLength, sizeof(gainInfos[i].data));
continue;
}
if (i == 0)
canControl = true;
gainInfos[i].value = static_cast<float>(gainInfos[i].data) / 256.;
}
TRACE(ERR, "Control %s: %f dB, from %f to %f dB, step %f dB.\n",
Control.name, current, Control.gain.min_gain, Control.gain.max_gain,
Control.gain.granularity);
return canControl;
}
uint32
AudioControlInterface::_ListFeatureUnitOption(uint32 controlType,
int32& index, int32 parentIndex, multi_mix_control_info* Info,
FeatureUnit* unit, uint32 channel, uint32 channels)
{
int32 startIndex = index;
uint32 id = 0;
uint32 flags = 0;
strind_id string = S_null;
const char* name = NULL;
bool initGainLimits = false;
switch(controlType) {
case BMA_CTL_MUTE:
id = USB_AUDIO_MUTE_CONTROL;
flags = B_MULTI_MIX_ENABLE;
string = S_MUTE;
break;
case BMA_CTL_VOLUME:
id = USB_AUDIO_VOLUME_CONTROL;
flags = B_MULTI_MIX_GAIN;
string = S_GAIN;
initGainLimits = true;
break;
case BMA_CTL_AUTOGAIN:
id = USB_AUDIO_AUTOMATIC_GAIN_CONTROL;
flags = B_MULTI_MIX_ENABLE;
name = "Auto Gain";
break;
default:
TRACE(ERR, "Unsupported type %#08x ignored.\n", controlType);
return 0;
}
multi_mix_control* Controls = Info->controls;
if (unit->HasControl(channel, controlType)) {
uint32 masterIndex = CTL_ID(id, channel, unit->ID(), fInterface);
Controls[index].id = masterIndex;
Controls[index].flags = flags;
Controls[index].parent = parentIndex;
Controls[index].string = string;
if (name != NULL)
strlcpy(Controls[index].name, name, sizeof(Controls[index].name));
if (initGainLimits)
_InitGainLimits(Controls[index]);
index++;
if (channels == 2) {
Controls[index].id = CTL_ID(id, channel + 1, unit->ID(), fInterface);
Controls[index].flags = flags;
Controls[index].parent = parentIndex;
Controls[index].master = masterIndex;
Controls[index].string = string;
if (name != NULL)
strlcpy(Controls[index].name, name, sizeof(Controls[index].name));
if (initGainLimits)
_InitGainLimits(Controls[index]);
index++;
}
}
return index - startIndex;
}
int32
AudioControlInterface::_ListFeatureUnitControl(int32& index, int32 parentIndex,
multi_mix_control_info* Info, _AudioControl* control)
{
FeatureUnit* unit = static_cast<FeatureUnit*>(control);
if (unit == 0) {
TRACE(ERR, "Feature Unit for null control ignored.\n");
return 0;
}
if (index + 4 > Info->control_count) {
TRACE(ERR, "Could not list feature control group."
" Limit %d of %d has been reached.\n",
index, Info->control_count);
return 0;
}
AudioChannelCluster* cluster = unit->OutCluster();
if (cluster == 0) {
TRACE(ERR, "Control %s with null cluster ignored.\n", unit->Name());
return 0;
}
struct _ChannelInfo {
const char* Name;
uint8 channels;
uint32 Mask;
} channelInfos[] = {
{ "", 1, 0 },
{ "", 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;
for (size_t i = 0; i < B_COUNT_OF(channelInfos); i++) {
if ((channelsConfig & channelInfos[i].Mask) != channelInfos[i].Mask) {
continue;
}
if (masterIndex == 0) {
groupIndex = index;
Controls[index].id = groupIndex;
Controls[index].flags = B_MULTI_MIX_GROUP;
Controls[index].parent = parentIndex;
snprintf(Controls[index].name, sizeof(Controls[index].name),
"%s %s", unit->Name(), channelInfos[i].Name);
index++;
} else {
groupIndex = masterIndex;
masterIndex = 0;
}
_ListFeatureUnitOption(BMA_CTL_MUTE, index, groupIndex, Info,
unit, channel, channelInfos[i].channels);
if (_ListFeatureUnitOption(BMA_CTL_VOLUME, index, groupIndex, Info,
unit, channel, channelInfos[i].channels) == 0) {
masterIndex = (i == 0) ? groupIndex : 0 ;
TRACE(UAC, "channel:%d set master index to %d\n",
channel, masterIndex);
}
_ListFeatureUnitOption(BMA_CTL_AUTOGAIN, index, groupIndex, Info,
unit, channel, channelInfos[i].channels);
if (Controls[index - 1].flags == B_MULTI_MIX_GROUP) {
Controls[index - 1].id = 0;
index--;
masterIndex = 0;
}
channel += channelInfos[i].channels;
channelsConfig &= ~channelInfos[i].Mask;
if (0 == channelsConfig)
break;
}
if (channelsConfig > 0)
TRACE(ERR, "Following channels were not processed: %#08x.\n",
channelsConfig);
return groupIndex;
}
void
AudioControlInterface::_ListSelectorUnitControl(int32& index, int32 parentGroup,
multi_mix_control_info* Info, _AudioControl* control)
{
SelectorUnit* selector = static_cast<SelectorUnit*>(control);
if (selector == 0 || selector->SubType() != USB_AUDIO_AC_SELECTOR_UNIT)
return;
if ((index + 1 + selector->fInputPins.Count()) > Info->control_count) {
TRACE(ERR, "Could not list selector control."
" Limit %d of %d has been reached.\n",
index, Info->control_count);
return;
}
multi_mix_control* Controls = Info->controls;
int32 recordMUX = CTL_ID(0, 0, selector->ID(), fInterface);
Controls[index].id = recordMUX;
Controls[index].flags = B_MULTI_MIX_MUX;
Controls[index].parent = parentGroup;
Controls[index].string = S_null;
strlcpy(Controls[index].name, "Source", sizeof(Controls[index].name));
index++;
for (int i = 0; i < selector->fInputPins.Count(); i++) {
Controls[index].id = CTL_ID(0, 1, selector->ID(), fInterface);
Controls[index].flags = B_MULTI_MIX_MUX_VALUE;
Controls[index].master = 0;
Controls[index].string = S_null;
Controls[index].parent = recordMUX;
_AudioControl* control = Find(selector->fInputPins[i]);
if (control != NULL)
strlcpy(Controls[index].name,
control->Name(), sizeof(Controls[index].name));
else
snprintf(Controls[index].name,
sizeof(Controls[index].name), "Input #%d", i + 1);
index++;
}
}
size_t
AudioControlInterface::_CollectMixerUnitControls(
const uint32 controlIds[kChannels][kChannels],
size_t inLeft, size_t outLeft, size_t inRight, size_t outRight,
const char* inputName, const char* name,
Vector<multi_mix_control>& Controls)
{
size_t count = 0;
uint32 leftId = controlIds[inLeft][outLeft];
uint32 rightId = controlIds[inRight][outRight];
multi_mix_control control;
memset(&control, 0, sizeof(multi_mix_control));
snprintf(control.name, sizeof(control.name), "%s %s", inputName, name);
for (size_t i = 0; i < 2; i++) {
if (leftId != 0 || rightId != 0) {
control.flags = B_MULTI_MIX_GROUP;
control.string = S_null;
Controls.PushBack(control);
int gainControls = 0;
if (leftId != 0) {
control.id = leftId;
control.flags = B_MULTI_MIX_GAIN;
control.string = S_GAIN;
if (_InitGainLimits(control)) {
gainControls++;
Controls.PushBack(control);
}
}
if (rightId != 0) {
control.id = rightId;
control.flags = B_MULTI_MIX_GAIN;
control.string = S_GAIN;
control.master = leftId;
if (_InitGainLimits(control)) {
gainControls++;
Controls.PushBack(control);
}
}
if (gainControls == 0)
Controls.PopBack();
else
count++;
}
if (inLeft == inRight)
break;
leftId = controlIds[inLeft][outRight];
rightId = controlIds[inRight][outLeft];
snprintf(control.name, sizeof(control.name),
"%s %s (Reverse)", inputName, name);
}
return count;
}
void
AudioControlInterface::_ListMixerUnitControls(int32& index,
multi_mix_control_info* Info, Vector<multi_mix_control>& controls)
{
multi_mix_control* Controls = Info->controls;
uint32 groupParent = 0;
uint32 gainParent = 0;
for (Vector<multi_mix_control>::Iterator I = controls.Begin();
I != controls.End() && index < Info->control_count; I++) {
memcpy(Controls + index, &*I, sizeof(multi_mix_control));
switch (I->flags) {
case B_MULTI_MIX_GROUP:
Controls[index].id = index;
Controls[index].parent = groupParent;
if (groupParent == 0) {
Controls[index].id |= 0x10000;
groupParent = Controls[index].id;
}
gainParent = Controls[index].id;
break;
case B_MULTI_MIX_GAIN:
Controls[index].parent = gainParent;
break;
default:
TRACE(ERR, "Control type %d ignored\n", I->flags);
continue;
}
index++;
}
if (index == Info->control_count)
TRACE(ERR, "Control count limit %d has been reached.\n", index);
}
void
AudioControlInterface::_ListMixControlsForMixerUnit(int32& index,
multi_mix_control_info* Info, _AudioControl* control)
{
MixerUnit* mixer = static_cast<MixerUnit*>(control);
if (mixer == 0 || mixer->SubType() != USB_AUDIO_AC_MIXER_UNIT)
return;
struct _ChannelPair {
size_t inLeft;
size_t inRight;
const char* name;
} channelPairs[] = {
{ 0, 1, "" },
{ 2, 2, "Center" },
{ 3, 3, "L.F.E" },
{ 4, 5, "Back" },
{ 6, 7, "Front of Center" },
{ 8, 8, "Back Center" },
{ 9, 10, "Side" },
{ 11, 11, "Top Center" },
{ 12, 14, "Top Front" },
{ 13, 13, "Top Front Center" },
{ 15, 17, "Top Back" },
{ 16, 16, "Top Back Center" }
};
Vector<_MixPageCollector*> mixControls;
_MixPageCollector* genericPage = new(std::nothrow) _MixPageCollector("Mixer");
mixControls.PushBack(genericPage);
size_t controlsOnExMixerPage = 0;
_MixPageCollector* exMixerPage = new(std::nothrow) _MixPageCollector("Mixer");
AudioChannelCluster* outCluster = mixer->OutCluster();
int inOffset = 0;
for (int iPin = 0; iPin < mixer->fInputPins.Count(); iPin++) {
_AudioControl* control = Find(mixer->fInputPins[iPin]);
AudioChannelCluster* inCluster = NULL;
if (control != NULL)
inCluster = control->OutCluster();
if (inCluster == NULL) {
TRACE(ERR, "control %p cluster %p failed!\n", control, inCluster);
break;
}
uint32 controlIds[kChannels][kChannels] = { { 0 } };
int inChannel = 0;
for (size_t in = 0; in < kChannels
&& inChannel < inCluster->ChannelsCount(); in++) {
if ((inCluster->ChannelsConfig() & (1 << in)) == 0)
continue;
for (size_t out = 0, outChannel = 0; out < kChannels
&& outChannel < outCluster->ChannelsCount(); out++) {
if ((outCluster->ChannelsConfig() & (1 << out)) == 0)
continue;
if (mixer->IsControlProgrammable(
inOffset + inChannel, outChannel)) {
if (SpecReleaseNumber() < 0x200)
controlIds[in][out] = CTL_ID(inOffset + inChannel + 1,
outChannel + 1, mixer->ID(), fInterface);
else
controlIds[in][out] = CTL_ID(USB_AUDIO_MIXER_CONTROL,
(inOffset + inChannel) * outCluster->ChannelsCount()
+ outChannel, mixer->ID(), fInterface);
}
outChannel++;
}
inChannel++;
}
inOffset += inChannel;
for (size_t in = 0; in < kChannels; in++)
for (size_t out = 0; out < kChannels; out++)
if (controlIds[in][out] != 0)
TRACE(UAC, "ctrl:%08x for in %d; out %d;\n",
controlIds[in][out], in, out);
uint32 exChannelsMask = ~(B_CHANNEL_LEFT | B_CHANNEL_RIGHT);
bool inIsEx = (inCluster->ChannelsConfig() & exChannelsMask) != 0;
bool outIsEx = (outCluster->ChannelsConfig() & exChannelsMask) != 0;
if (!inIsEx && !outIsEx) {
for (size_t i = 0; i < 2; i++)
_CollectMixerUnitControls(controlIds,
kLeftChannel, channelPairs[i].inLeft,
kRightChannel, channelPairs[i].inRight,
control->Name(), channelPairs[i].name,
*mixControls[0]);
continue;
}
if (!outIsEx) {
for (size_t i = 0; i < B_COUNT_OF(channelPairs); i++)
_CollectMixerUnitControls(controlIds,
channelPairs[i].inLeft, kLeftChannel,
channelPairs[i].inRight, kRightChannel,
control->Name(), channelPairs[i].name,
*mixControls[0]);
continue;
}
for (size_t in = 0; in < B_COUNT_OF(channelPairs); in++) {
for (size_t out = 0; out < B_COUNT_OF(channelPairs); out++) {
char outName[sizeof(Info->controls->name)] = { 0 };
if (in == out)
strlcpy(outName, channelPairs[out].name, sizeof(outName));
else
snprintf(outName, sizeof(outName), "%s to %s",
channelPairs[in].name, channelPairs[out].name);
controlsOnExMixerPage += _CollectMixerUnitControls(controlIds,
channelPairs[in].inLeft, channelPairs[out].inLeft,
channelPairs[in].inRight, channelPairs[out].inRight,
control->Name(), outName, *exMixerPage);
}
if (controlsOnExMixerPage >= 6) {
mixControls.PushBack(exMixerPage);
exMixerPage = new(std::nothrow) _MixPageCollector("Mixer");
controlsOnExMixerPage = 0;
}
}
}
if (exMixerPage->Count() > 1)
mixControls.PushBack(exMixerPage);
else
delete exMixerPage;
for (Vector<_MixPageCollector*>::Iterator I = mixControls.Begin();
I != mixControls.End(); I++) {
Vector<multi_mix_control>* controls = *I;
TRACE(UAC, "controls count: %d\n", controls->Count());
if (controls->Count() > 1)
_ListMixerUnitControls(index, Info, *controls);
delete controls;
}
}
void
AudioControlInterface::_ListMixControlsPage(int32& index,
multi_mix_control_info* Info, AudioControlsMap& Map, const char* Name)
{
multi_mix_control* Controls = Info->controls;
int32 groupIndex = index | 0x10000;
Controls[index].id = groupIndex;
Controls[index].flags = B_MULTI_MIX_GROUP;
Controls[index].parent = 0;
strlcpy(Controls[index].name, Name, sizeof(Controls[index].name));
index++;
int32 group = groupIndex;
for (AudioControlsIterator I = Map.Begin(); I != Map.End(); I++) {
TRACE(UAC, "%s control %d listed.\n", Name, I->Value()->ID());
switch(I->Value()->SubType()) {
case USB_AUDIO_AC_FEATURE_UNIT:
group = _ListFeatureUnitControl(index, groupIndex,
Info, I->Value());
break;
case USB_AUDIO_AC_SELECTOR_UNIT:
_ListSelectorUnitControl(index, group, Info, I->Value());
break;
default:
break;
}
}
}
status_t
AudioControlInterface::ListMixControls(multi_mix_control_info* Info)
{
AudioControlsMap RecordControlsMap;
AudioControlsMap OutputControlsMap;
for (AudioControlsIterator I = fOutputTerminals.Begin();
I != fOutputTerminals.End(); I++) {
_Terminal* terminal = static_cast<_Terminal*>(I->Value());
if (terminal->IsUSBIO())
_HarvestRecordFeatureUnits(terminal, RecordControlsMap);
else
_HarvestOutputFeatureUnits(terminal, OutputControlsMap);
}
AudioControlsMap InputControlsMap;
AudioControlsMap MixerControlsMap;
for (AudioControlsIterator I = fAudioControls.Begin();
I != fAudioControls.End(); I++) {
_AudioControl* control = I->Value();
if (control->SubType() == USB_AUDIO_AC_MIXER_UNIT) {
MixerUnit* mixerControl = static_cast<MixerUnit*>(control);
if (mixerControl->HasProgrammableControls())
MixerControlsMap.Put(control->ID(), control);
continue;
}
if (control->SubType() != USB_AUDIO_AC_FEATURE_UNIT)
continue;
if (RecordControlsMap.Find(control->ID()) != RecordControlsMap.End()
|| OutputControlsMap.Find(control->ID()) != OutputControlsMap.End())
continue;
_AudioControl* sourceControl = Find(control->SourceID());
if (sourceControl != 0
&& sourceControl->SubType() == USB_AUDIO_AC_INPUT_TERMINAL)
InputControlsMap.Put(control->ID(), control);
else
OutputControlsMap.Put(control->ID(), control);
}
int32 index = 0;
if (InputControlsMap.Count() > 0)
_ListMixControlsPage(index, Info, InputControlsMap, "Input");
if (OutputControlsMap.Count() > 0)
_ListMixControlsPage(index, Info, OutputControlsMap, "Output");
if (RecordControlsMap.Count() > 0)
_ListMixControlsPage(index, Info, RecordControlsMap, "Record");
for (AudioControlsIterator I = MixerControlsMap.Begin();
I != MixerControlsMap.End(); I++)
_ListMixControlsForMixerUnit(index, Info, I->Value());
Info->control_count = index;
return B_OK;
}
status_t
AudioControlInterface::GetMix(multi_mix_value_info* Info)
{
for (int32 i = 0; i < Info->item_count; i++) {
uint16 length = 0;
int16 data = 0;
_AudioControl* control = Find(ID_FROM_CTLID(Info->values[i].id));
if (control == NULL) {
TRACE(ERR, "No control found for unit id %#02x. Ignore it.\n",
ID_FROM_CTLID(Info->values[i].id));
continue;
}
switch (control->SubType()) {
case USB_AUDIO_AC_FEATURE_UNIT:
switch(CS_FROM_CTLID(Info->values[i].id)) {
case USB_AUDIO_VOLUME_CONTROL:
length = 2;
break;
case USB_AUDIO_MUTE_CONTROL:
case USB_AUDIO_AUTOMATIC_GAIN_CONTROL:
length = 1;
break;
default:
TRACE(ERR, "Unsupported control id:%08x of type %#02x "
"ignored.\n", ID_FROM_CTLID(Info->values[i].id),
CS_FROM_CTLID(Info->values[i].id));
continue;
}
break;
case USB_AUDIO_AC_SELECTOR_UNIT:
length = 1;
break;
case USB_AUDIO_AC_MIXER_UNIT:
length = 2;
break;
default:
TRACE(ERR, "Control id:%08x of type %d is not supported\n",
ID_FROM_CTLID(Info->values[i].id), control->SubType());
continue;
}
size_t actualLength = 0;
status_t status = gUSBModule->send_request(fDevice->USBDevice(),
USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_CLASS, USB_AUDIO_GET_CUR,
REQ_VALUE(Info->values[i].id), REQ_INDEX(Info->values[i].id),
length, &data, &actualLength);
if (status != B_OK || actualLength != length) {
TRACE(ERR, "Request (%04x:%04x) failed:%#08x; received %d of %d\n",
REQ_VALUE(Info->values[i].id), REQ_INDEX(Info->values[i].id),
status, actualLength, length);
continue;
}
switch (control->SubType()) {
case USB_AUDIO_AC_FEATURE_UNIT:
switch(CS_FROM_CTLID(Info->values[i].id)) {
case USB_AUDIO_VOLUME_CONTROL:
Info->values[i].gain = static_cast<float>(data) / 256.;
TRACE(MIX, "Gain control %d; channel: %d; is %f dB.\n",
ID_FROM_CTLID(Info->values[i].id),
CN_FROM_CTLID(Info->values[i].id),
Info->values[i].gain);
break;
case USB_AUDIO_MUTE_CONTROL:
Info->values[i].enable = data > 0;
TRACE(MIX, "Mute control %d; channel: %d; is %d.\n",
ID_FROM_CTLID(Info->values[i].id),
CN_FROM_CTLID(Info->values[i].id),
Info->values[i].enable);
break;
case USB_AUDIO_AUTOMATIC_GAIN_CONTROL:
Info->values[i].enable = data > 0;
TRACE(MIX, "AGain control %d; channel: %d; is %d.\n",
ID_FROM_CTLID(Info->values[i].id),
CN_FROM_CTLID(Info->values[i].id),
Info->values[i].enable);
break;
default:
break;
}
break;
case USB_AUDIO_AC_SELECTOR_UNIT:
Info->values[i].mux = data - 1;
TRACE(MIX, "Selector control %d; is %d.\n",
ID_FROM_CTLID(Info->values[i].id),
Info->values[i].mux);
break;
case USB_AUDIO_AC_MIXER_UNIT:
Info->values[i].gain = static_cast<float>(data) / 256.;
TRACE(MIX, "Mixer #%d channels in: %d; out: %d; is %f dB.\n",
ID_FROM_CTLID(Info->values[i].id),
CS_FROM_CTLID(Info->values[i].id),
CN_FROM_CTLID(Info->values[i].id),
Info->values[i].gain);
break;
}
}
return B_OK;
}
status_t
AudioControlInterface::SetMix(multi_mix_value_info* Info)
{
for (int32 i = 0; i < Info->item_count; i++) {
uint16 length = 0;
int16 data = 0;
_AudioControl* control = Find(ID_FROM_CTLID(Info->values[i].id));
if (control == NULL) {
TRACE(ERR, "No control found for unit id %#02x. Ignore it.\n",
ID_FROM_CTLID(Info->values[i].id));
continue;
}
switch (control->SubType()) {
case USB_AUDIO_AC_FEATURE_UNIT:
switch(CS_FROM_CTLID(Info->values[i].id)) {
case USB_AUDIO_VOLUME_CONTROL:
data = static_cast<int16>(Info->values[i].gain * 256.);
length = 2;
TRACE(MIX, "Gain control %d; channel: %d; "
"about to set to %f dB.\n",
ID_FROM_CTLID(Info->values[i].id),
CN_FROM_CTLID(Info->values[i].id),
Info->values[i].gain);
break;
case USB_AUDIO_MUTE_CONTROL:
data = (Info->values[i].enable ? 1 : 0);
length = 1;
TRACE(MIX, "Mute control %d; channel: %d; "
"about to set to %d.\n",
ID_FROM_CTLID(Info->values[i].id),
CN_FROM_CTLID(Info->values[i].id),
Info->values[i].enable);
break;
case USB_AUDIO_AUTOMATIC_GAIN_CONTROL:
data = (Info->values[i].enable ? 1 : 0);
length = 1;
TRACE(MIX, "AGain control %d; channel: %d; "
"about to set to %d.\n",
ID_FROM_CTLID(Info->values[i].id),
CN_FROM_CTLID(Info->values[i].id),
Info->values[i].enable);
break;
default:
TRACE(ERR, "Unsupported control id:%08x of type %#02x "
"ignored.\n", ID_FROM_CTLID(Info->values[i].id),
CS_FROM_CTLID(Info->values[i].id));
continue;
}
break;
case USB_AUDIO_AC_SELECTOR_UNIT:
data = Info->values[i].mux + 1;
length = 1;
TRACE(MIX, "Selector Control %d about to set to %d.\n",
ID_FROM_CTLID(Info->values[i].id),
Info->values[i].mux);
break;
case USB_AUDIO_AC_MIXER_UNIT:
data = static_cast<int16>(Info->values[i].gain * 256.);
length = 2;
TRACE(MIX, "Mixer %d channels in: %d; out: %d; "
"about to set to %f dB.\n",
ID_FROM_CTLID(Info->values[i].id),
CS_FROM_CTLID(Info->values[i].id),
CN_FROM_CTLID(Info->values[i].id),
Info->values[i].gain);
break;
default:
TRACE(ERR, "Control id:%08x of type %d is not supported\n",
Info->values[i].id, control->SubType());
continue;
}
size_t actualLength = 0;
status_t status = gUSBModule->send_request(fDevice->USBDevice(),
USB_REQTYPE_INTERFACE_OUT | USB_REQTYPE_CLASS, USB_AUDIO_SET_CUR,
REQ_VALUE(Info->values[i].id), REQ_INDEX(Info->values[i].id),
length, &data, &actualLength);
if (status != B_OK || actualLength != length) {
TRACE(ERR, "Request (%04x:%04x) failed:%#08x; send %d of %d\n",
REQ_VALUE(Info->values[i].id), REQ_INDEX(Info->values[i].id),
status, actualLength, length);
continue;
}
TRACE(MIX, "Value set OK\n");
}
return B_OK;
}