/*
    An object whose purpose is to abstract a USB audio class device descriptor.

    The basic idea is that a USB device is described by a chunk of memory on the device.
    This chunk of memory is formatted with length bytes so that you can skip over any
    data that you don't care about, and is aranged into a basic list, starting with the
    device descriptor, then one or more configuration descriptors, one or more stream
    descriptors, and one or more HID descriptors.  It is possible for the configutation
    and stream descriptors to come in any order and not necessarily all config descriptors,
    then all stream descriptors.

    Because Mac OS X's implementation of USB services doesn't allow you to get the device
    descriptor, this object only parses configuration and interface descriptors.  Code
    calling this object's init function must pass it configuration descriptor pointer,
    which is gotten from the USB services API via GetFullConfigurationDescriptor ().

    To use this code you do all of your interactions through the USBAudioConfigObject.
    You do not directly call any of the other objects.  First allocate a USBAudioConfigObject
    and call its init function which will do the parsing of config descriptor and create
    the other objects as necessary.  Once the config object has been init'ed you should
    call GetNumStreamInterfaces, GetNumAltStreamInterfaces, and GetFirstStreamInterfaceNum
    so that you know how many interfaces there are and can query each interface for its
    properties.  Once you have found an interface that has the properties that you want
    you can proceed with the normal USB calls to select and configure that interface.

    The implementation of USBAudioConfigObject is to create two OSArrays, one for the
    control interfaces and one for the stream interfaces.  As the configuration descriptor
    is parsed new control or stream objects are created and inserted in the appropriate
    array after being filled in with the information from the parsed descriptor.
*/

#include <libkern/c++/OSArray.h>

#include <IOKit/audio/IOAudioTypes.h>
#include "USBAudioObject.h"
#include "AppleUSBAudioCommon.h"

/* ------------------------------------------------------
    USBAudioConfigObject
------------------------------------------------------ */
OSDefineMetaClassAndStructors (USBAudioConfigObject, OSObject);

/*
 	Public methods
*/
USBAudioConfigObject * USBAudioConfigObject::create (const IOUSBConfigurationDescriptor * newConfigurationDescriptor, UInt8 controlInterfaceNum) {
    USBAudioConfigObject *			configObject;

    configObject = new USBAudioConfigObject;

    FailIf (NULL == configObject, Exit);

    if (FALSE == configObject->init (newConfigurationDescriptor, controlInterfaceNum)) {
        configObject->release();
        configObject = 0;
    }

Exit:
    return configObject;
}

#if DEBUGLOGGING
void USBAudioConfigObject::DumpConfigMemoryToIOLog (void) {
#if 1
	UInt8 *	descriptorPtr;
	UInt8	descriptorIndex;
	UInt8	length;
	char	descriptor[256];
	char	num[4];

	descriptorPtr = (UInt8*)theConfigurationDescriptorPtr;
	while (*descriptorPtr != 0) {
		length = *descriptorPtr;
		descriptor[0] = 0;
		for (descriptorIndex = 0; descriptorIndex < length; descriptorIndex++) {
			sprintf (num, "%2x ", *descriptorPtr++);
			strcat (descriptor, num);
		}
		descriptor[length * 3] = 0;
		debugIOLog ("%s", descriptor);
	}
#endif
}
#endif

bool USBAudioConfigObject::init (const IOUSBConfigurationDescriptor * newConfigurationDescriptor, UInt8 controlInterfaceNum) {
    Boolean				result;

    result = FALSE;
    FailIf (FALSE == OSObject::init (), Exit);
    FailIf (NULL == newConfigurationDescriptor, Exit);

	theControlInterfaceNum = controlInterfaceNum;

    debugIOLog ("allocating %d bytes for config descriptor", USBToHostWord (newConfigurationDescriptor->wTotalLength));
    theConfigurationDescriptorPtr = (IOUSBConfigurationDescriptor *)IOMalloc (USBToHostWord (newConfigurationDescriptor->wTotalLength) + 1);
    FailIf (NULL == theConfigurationDescriptorPtr, Exit);

    memcpy (theConfigurationDescriptorPtr, newConfigurationDescriptor, USBToHostWord (newConfigurationDescriptor->wTotalLength));
    ((UInt8 *)theConfigurationDescriptorPtr)[USBToHostWord (newConfigurationDescriptor->wTotalLength)] = 0;
#if DEBUGLOGGING
	DumpConfigMemoryToIOLog ();
#endif

    ParseConfigurationDescriptor ();
	result = TRUE;

Exit:
	if (NULL != theConfigurationDescriptorPtr) {
		IOFree (theConfigurationDescriptorPtr, USBToHostWord (newConfigurationDescriptor->wTotalLength) + 1);
		theConfigurationDescriptorPtr = NULL;
	}
    return result;
}

void USBAudioConfigObject::free (void) {
	if (NULL != theControls) {
		theControls->release ();
		theControls = NULL;
	}

	if (NULL != theStreams) {
		theStreams->release ();
		theStreams = NULL;
	}

	if (NULL != theConfigurationDescriptorPtr) {
		IOFree (theConfigurationDescriptorPtr, USBToHostWord (theConfigurationDescriptorPtr->wTotalLength) + 1);
	}

    OSObject::free ();
}

Boolean USBAudioConfigObject::ChannelHasMuteControl (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 featureUnitID, UInt8 channelNum) {
    USBAudioControlObject *				thisControl;
    Boolean								theControl;

    theControl = FALSE;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
		theControl = thisControl->ChannelHasMuteControl (featureUnitID, channelNum);

    return theControl;
}

Boolean USBAudioConfigObject::ChannelHasVolumeControl (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 featureUnitID, UInt8 channelNum) {
    USBAudioControlObject *				thisControl;
    Boolean								theControl;

    theControl = FALSE;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
		theControl = thisControl->ChannelHasVolumeControl (featureUnitID, channelNum);

    return theControl;
}

UInt8 USBAudioConfigObject::FindNextAltInterfaceWithNumChannels (UInt8 interfaceNum, UInt8 startingAltInterface, UInt8 numChannelsRequested) {
    UInt16								numAltInterfaces;
	UInt8								altInterface;
	UInt8								altInterfaceIndx;

	altInterface = 255;
	if (startingAltInterface == 0)
		startingAltInterface = 1;
	numAltInterfaces = GetNumAltStreamInterfaces (interfaceNum);
	for (altInterfaceIndx = startingAltInterface; altInterfaceIndx < numAltInterfaces && altInterface == 255; altInterfaceIndx++) {
		if (numChannelsRequested == GetNumChannels (interfaceNum, altInterfaceIndx)) {
			altInterface = altInterfaceIndx;
		}
	}

	return altInterface;
}

UInt8 USBAudioConfigObject::FindNextAltInterfaceWithSampleSize (UInt8 interfaceNum, UInt8 startingAltInterface, UInt8 sampleSizeRequested) {
    UInt16								numAltInterfaces;
	UInt8								altInterface;
	UInt8								altInterfaceIndx;

	altInterface = 255;
	if (startingAltInterface == 0)
		startingAltInterface = 1;
	numAltInterfaces = GetNumAltStreamInterfaces (interfaceNum);
	for (altInterfaceIndx = startingAltInterface; altInterfaceIndx < numAltInterfaces && altInterface == 255; altInterfaceIndx++) {
		if (sampleSizeRequested == GetSampleSize (interfaceNum, altInterfaceIndx)) {
			altInterface = altInterfaceIndx;
		}
	}

	return altInterface;
}

UInt8 USBAudioConfigObject::FindNextAltInterfaceWithSampleRate (UInt8 interfaceNum, UInt8 startingAltInterface, UInt32 sampleRateRequested) {
	UInt16								numAltInterfaces;
	UInt8								altInterface;
	UInt8								altInterfaceIndx;

	altInterface = 255;
	if (startingAltInterface == 0)
		startingAltInterface = 1;
	numAltInterfaces = GetNumAltStreamInterfaces (interfaceNum);
	for (altInterfaceIndx = startingAltInterface; altInterfaceIndx < numAltInterfaces && altInterface == 255; altInterfaceIndx++) {
		if (TRUE == VerifySampleRateIsSupported (interfaceNum, altInterfaceIndx, sampleRateRequested)) {
			altInterface = altInterfaceIndx;
		}
	}

	return altInterface;
}

UInt8 USBAudioConfigObject::FindAltInterfaceWithSettings (UInt8 interfaceNum, UInt8 numChannels, UInt8 sampleSize, UInt32 sampleRate) {
	UInt8			potentialAltInterface;
	UInt8			theAltInterface;
	Boolean			found;

	potentialAltInterface = 1;
	theAltInterface = 255;
	found = FALSE;
	while (!found && potentialAltInterface != 255) {
		if ((GetFormat (interfaceNum, potentialAltInterface) & 0x0FFF) != 0) {	// Make sure it's not an undefined format
			potentialAltInterface = FindNextAltInterfaceWithNumChannels (interfaceNum, potentialAltInterface, numChannels);
			FailIf (255 == potentialAltInterface, Exit);
			if (potentialAltInterface == FindNextAltInterfaceWithSampleSize (interfaceNum, potentialAltInterface, sampleSize)) {
				FailIf (255 == potentialAltInterface, Exit);
				if (0 != sampleRate) {
					// they want a specific sample rate on this interface
					if (potentialAltInterface == FindNextAltInterfaceWithSampleRate (interfaceNum, potentialAltInterface, sampleRate)) {
						found = TRUE;
						theAltInterface = potentialAltInterface;
					} else {
						potentialAltInterface++;
					}
				} else {
					// they don't care about the sample rate
					found = TRUE;
					theAltInterface = potentialAltInterface;
				}
			} else {
				potentialAltInterface++;
			}
		} else {
			potentialAltInterface++;
		}
	}

Exit:
	return theAltInterface;
}

UInt32 USBAudioConfigObject::GetAC3BSID (UInt8 interfaceNum, UInt8 altInterfaceNum) {
	USBAudioStreamObject * 				thisStream;
	UInt32								bmBSID;
	
	bmBSID = 0;
	thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
	if (thisStream)
		bmBSID = thisStream->GetAC3BSID ();
	
	return bmBSID;
}

UInt8 USBAudioConfigObject::GetFeatureUnitIDConnectedToOutputTerminal (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 outputTerminalID) {
    USBAudioControlObject *				thisControl;
    UInt8								featureUnitID;

	debugIOLog ("GetFeatureUnitIDConnectedToOutputTerminal (%d, %d, %d)", interfaceNum, altInterfaceNum, outputTerminalID);
    featureUnitID = 0;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
	debugIOLog ("GetControlObject returns %p", thisControl);
    if (thisControl)
		featureUnitID = thisControl->GetFeatureUnitIDConnectedToOutputTerminal (outputTerminalID);

    return featureUnitID;
}

UInt8 USBAudioConfigObject::GetFirstStreamInterfaceNum (void) {
    USBAudioStreamObject * 				thisStream;
	UInt8								interfaceNum;

	thisStream = NULL;
	interfaceNum = 0;
	if (NULL != theStreams) {
		thisStream = OSDynamicCast (USBAudioStreamObject, theStreams->getObject (0));
	}

	if (thisStream)
		interfaceNum = thisStream->GetInterfaceNum ();

    return interfaceNum;
}

void USBAudioConfigObject::GetControlledStreamNumbers (UInt8 **controledStreams, UInt8 *numControledStreams) {
    USBAudioControlObject * 			thisControl;

	*controledStreams = 0;
	*numControledStreams = 0;
	thisControl = NULL;

	if (NULL != theControls) {
		thisControl = OSDynamicCast (USBAudioControlObject, theControls->getObject (0));
	} else {
		debugIOLog ("couldn't get the control object");
	}

	if (thisControl) {
		*controledStreams = thisControl->GetStreamInterfaceNumbers ();
		*numControledStreams = thisControl->GetNumStreamInterfaces ();
	}
}

UInt8 USBAudioConfigObject::GetControlInterfaceNum (void) {
    USBAudioControlObject * 			thisControl;
	UInt8								interfaceNum;

	thisControl = NULL;
	interfaceNum = 0;
	if (NULL != theControls) {
		thisControl = OSDynamicCast (USBAudioControlObject, theControls->getObject (0));
	}

	if (thisControl)
		interfaceNum = thisControl->GetInterfaceNum ();

    return interfaceNum;
}

UInt16 USBAudioConfigObject::GetFormat (UInt8 interfaceNum, UInt8 altInterfaceNum) {
	USBAudioStreamObject * 				thisStream;
	UInt16								formatTag;
	
	formatTag = TYPE_I_UNDEFINED;
	thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
	if (thisStream)
		formatTag = USBToHostWord (thisStream->GetFormatTag ());
	
	return formatTag;
}

UInt32 USBAudioConfigObject::GetHighestSampleRate (UInt8 interfaceNum, UInt8 altInterfaceNum) {
	UInt32 *				sampleRates;
	UInt32					highSampleRate;
	UInt32					i;
	UInt8					numSampleRates;
	UInt8					highest;

	highSampleRate = 0;
	numSampleRates = GetNumSampleRates (interfaceNum, altInterfaceNum);
	sampleRates = GetSampleRates (interfaceNum, altInterfaceNum);
	FailIf (NULL == sampleRates, Exit);

	highest = 0;
	for (i = 1; i < numSampleRates; i++) {
		if (sampleRates[i] > sampleRates[highest])
			highest = i;
	}

	highSampleRate = sampleRates[highest];

Exit:
	return highSampleRate;
}

UInt8 USBAudioConfigObject::GetIsocAssociatedEndpointAddress (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 address) {
	USBAudioStreamObject * 				thisStream;
	UInt8								assocEndpointAddress;
	
	assocEndpointAddress = 0;
	thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
	if (thisStream)
		assocEndpointAddress = thisStream->GetIsocAssociatedEndpointAddress (address);
	
	return assocEndpointAddress;
}

UInt8 USBAudioConfigObject::GetIsocAssociatedEndpointRefreshInt (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 address) {
	USBAudioStreamObject * 				thisStream;
	UInt8								assocEndpointRefresh;
	
	assocEndpointRefresh = 0;
	thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
	if (thisStream)
		assocEndpointRefresh = thisStream->GetIsocAssociatedEndpointRefreshInt (address);
	
	return assocEndpointRefresh;
}

UInt8 USBAudioConfigObject::GetIsocEndpointAddress (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 direction) {
	USBAudioStreamObject * 				thisStream;
	UInt8								endpointAddress;
	
	endpointAddress = 0;
	thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
	if (thisStream)
		endpointAddress = thisStream->GetIsocEndpointAddress (direction);
	
	return endpointAddress;
}

// Use GetTerminalLink to get the unit number of the input or output terminal that the endpoint is associated with.
// Once you have that terminal, you can figure out if it's an input or output terminal, and once you know that you
// know which direction the endpoint is supposed to be.
UInt8 USBAudioConfigObject::GetIsocEndpointDirection (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioStreamObject * 				thisStream;
    USBAudioControlObject * 			thisControl;
    UInt8								endpointDirection;
	UInt8								terminalLink;
	UInt8								numOutputs;
	UInt8								numInputs;
	UInt8								numEndpoints;
	UInt8								index;
	UInt8								terminalID;
	UInt8								direction;

	endpointDirection = 0xFF;
	direction = 0xFF;
	index = 0;
	numEndpoints = 0;
    thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
	debugIOLog ("GetIsocEndpointDirection (%d, %d), thisStream = %p", interfaceNum, altInterfaceNum, thisStream);
    thisControl = GetControlObject (theControlInterfaceNum, 0);
	debugIOLog ("GetIsocEndpointDirection (%d, %d), thisControl = %p", interfaceNum, altInterfaceNum, thisControl);
	if (NULL != thisStream && NULL != thisControl) {
		terminalLink = thisStream->GetTerminalLink ();		// returns the unitID of the terminal the endpoint goes with
		debugIOLog ("GetIsocEndpointDirection (%d, %d), terminalLink = %d", interfaceNum, altInterfaceNum, terminalLink);
		if (0 != terminalLink) {
			numOutputs = thisControl->GetNumOutputTerminals ();
			debugIOLog ("GetIsocEndpointDirection (%d, %d), numOutputs = %d", interfaceNum, altInterfaceNum, numOutputs);
			while (0xFF == direction && index < numOutputs) {
				terminalID = thisControl->GetIndexedOutputTerminalID (index);
				debugIOLog ("GetIsocEndpointDirection (%d, %d), terminalID = %d", interfaceNum, altInterfaceNum, terminalID);
				if (terminalID == terminalLink) {
					direction = kUSBIn;
					numEndpoints = numOutputs;
					debugIOLog ("GetIsocEndpointDirection (%d, %d), found an output terminal (#%d) at index %d", interfaceNum, altInterfaceNum, terminalID, index);
				} else {
					index++;
				}
			}

			if (0xFF == direction) {
				numInputs = thisControl->GetNumInputTerminals ();
				debugIOLog ("GetIsocEndpointDirection (%d, %d), Didn't find an output terminal, checking for an input terminal", interfaceNum, altInterfaceNum);
				debugIOLog ("GetIsocEndpointDirection (%d, %d), numInputs = %d", interfaceNum, altInterfaceNum, numInputs);
				index = 0;
				while (0xFF == direction && index < numInputs) {
					terminalID = thisControl->GetIndexedInputTerminalID (index);
					debugIOLog ("GetIsocEndpointDirection, terminalID = %d", interfaceNum, altInterfaceNum, terminalID);
					if (terminalID == terminalLink) {
						direction = kUSBOut;
						numEndpoints = numInputs;
						debugIOLog ("GetIsocEndpointDirection (%d, %d), found an input terminal (#%d) at index %d", interfaceNum, altInterfaceNum, terminalID, index);
					} else {
						index++;
					}
				}
			}
		}

		if (0xFF != direction) {
			for (index = 0; index < numEndpoints; index++) {
				endpointDirection = thisStream->GetIsocEndpointDirection (index);
				if (endpointDirection == direction) {
					break;		// found the right endpoint, get out of for loop
				}
			}
		}
	}

	debugIOLog ("GetIsocEndpointDirection (%d, %d), endpointDirection = %d", interfaceNum, altInterfaceNum, endpointDirection);
    return endpointDirection;
}

UInt8 USBAudioConfigObject::GetIsocEndpointSyncType (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 address) {
	USBAudioStreamObject * 				thisStream;
	UInt8								endpointSyncType;
	
	endpointSyncType = 0;
	thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
	if (thisStream)
		endpointSyncType = thisStream->GetIsocEndpointSyncType (address);
	
	return endpointSyncType;
}

UInt8 USBAudioConfigObject::GetIndexedFeatureUnitID (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 featureUnitIndex) {
    USBAudioControlObject *				thisControl;
    UInt8								featureUnitID;

    featureUnitID = 0;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
		featureUnitID = thisControl->GetIndexedFeatureUnitID (featureUnitIndex);

    return featureUnitID;
}

UInt8 USBAudioConfigObject::GetIndexedMixerUnitID (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 mixerUnitIndex) {
    USBAudioControlObject *				thisControl;
    UInt8								mixerUnitID;

    mixerUnitID = 0;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
		mixerUnitID = thisControl->GetIndexedMixerUnitID (mixerUnitIndex);

    return mixerUnitID;
}

UInt8 USBAudioConfigObject::GetIndexedSelectorUnitID (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 selectorUnitIndex) {
    USBAudioControlObject *				thisControl;
    UInt8								selectorUnitID;

    selectorUnitID = 0;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
		selectorUnitID = thisControl->GetIndexedSelectorUnitID (selectorUnitIndex);

    return selectorUnitID;
}

UInt16 USBAudioConfigObject::GetIndexedInputTerminalType (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 index) {
    USBAudioControlObject *				thisControl;
    UInt16								terminalType;

	terminalType = USBToHostWord (INPUT_UNDEFINED);
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
		terminalType = USBToHostWord (thisControl->GetIndexedInputTerminalType (index));

    return terminalType;
}

UInt8 USBAudioConfigObject::GetIndexedInputTerminalID (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 index) {
    USBAudioControlObject *				thisControl;
    UInt16								terminalID;

	terminalID = 0;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
		terminalID = thisControl->GetIndexedInputTerminalID (index);

    return terminalID;
}

UInt8 USBAudioConfigObject::GetIndexedOutputTerminalID (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 index) {
    USBAudioControlObject *				thisControl;
    UInt16								terminalID;

	terminalID = 0;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
		terminalID = thisControl->GetIndexedOutputTerminalID (index);

    return terminalID;
}

UInt16 USBAudioConfigObject::GetIndexedOutputTerminalType (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 index) {
    USBAudioControlObject *				thisControl;
    UInt16								terminalType;

	terminalType = USBToHostWord (OUTPUT_UNDEFINED);
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
		terminalType = USBToHostWord (thisControl->GetIndexedOutputTerminalType (index));

    return terminalType;
}

UInt16 USBAudioConfigObject::GetInputTerminalType (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 terminalNum) {
    USBAudioControlObject *				thisControl;
    UInt16								terminalType;

    terminalType = USBToHostWord (INPUT_UNDEFINED);
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
		terminalType = USBToHostWord (thisControl->GetInputTerminalType (terminalNum));

    return terminalType;
}

UInt8 USBAudioConfigObject::GetInterfaceClass (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioStreamObject * 				thisStream;
    UInt8								interfaceClass;

    interfaceClass = 0;
    thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
    if (thisStream)
        interfaceClass = thisStream->GetInterfaceClass ();

    return interfaceClass;
}

UInt8 USBAudioConfigObject::GetInterfaceSubClass (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioStreamObject * 				thisStream;
    UInt8								interfaceSubClass;

    interfaceSubClass = 0;
    thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
    if (thisStream)
        interfaceSubClass = thisStream->GetInterfaceSubClass ();

    return interfaceSubClass;
}

UInt32 USBAudioConfigObject::GetLowestSampleRate (UInt8 interfaceNum, UInt8 altInterfaceNum) {
	UInt32 *				sampleRates;
	UInt32					lowSampleRate;
	UInt32					i;
	UInt8					numSampleRates;
	UInt8					lowest;

	lowSampleRate = 0xFFFFFFFF;
	numSampleRates = GetNumSampleRates (interfaceNum, altInterfaceNum);
	sampleRates = GetSampleRates (interfaceNum, altInterfaceNum);
	FailIf (NULL == sampleRates, Exit);

	lowest = 0;
	for (i = 1; i < numSampleRates; i++) {
		if (sampleRates[i] < sampleRates[lowest])
			lowest = i;
	}

	lowSampleRate = sampleRates[lowest];

Exit:
	return lowSampleRate;
}

UInt16 USBAudioConfigObject::GetMaxBitRate (UInt8 interfaceNum, UInt8 altInterfaceNum) {
	USBAudioStreamObject * 				thisStream;
	UInt16								maxBitRate;
	
	maxBitRate = 0;
	thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
	if (thisStream)
		maxBitRate = USBToHostWord (thisStream->GetMaxBitRate ());
	
	return maxBitRate;
}

UInt8 USBAudioConfigObject::GetNumAltStreamInterfaces (UInt8 interfaceNum) {
    USBAudioStreamObject * 				thisStream;
    UInt16								numAltInterfaces;
    UInt16								indx;

    numAltInterfaces = 0;
	if (NULL != theStreams) {
		for (indx = 0; indx < theStreams->getCount (); indx++) {
			thisStream = OSDynamicCast (USBAudioStreamObject, theStreams->getObject (indx));
			if (thisStream && thisStream->GetInterfaceNum () == interfaceNum) {
				numAltInterfaces++;
			}
		}
	}

    return numAltInterfaces;
}

UInt8 USBAudioConfigObject::GetNumChannels (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioStreamObject * 				thisStream;
    UInt8								numChannels;

    numChannels = 0;
    thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
    if (thisStream)
        numChannels = thisStream->GetNumChannels ();

    return numChannels;
}

UInt8 USBAudioConfigObject::GetNumControls (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 featureUnitID) {
    USBAudioControlObject *				thisControl;
    UInt8								numControls;

	numControls = 0;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
		numControls = thisControl->GetNumControls (featureUnitID);

    return numControls;
}

UInt8 USBAudioConfigObject::GetNumSampleRates (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioStreamObject * 				thisStream;
    UInt8								numSampleRates;

    numSampleRates = 0;
    thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
    if (thisStream)
        numSampleRates = thisStream->GetNumSampleRates ();

    return numSampleRates;
}

UInt8 USBAudioConfigObject::GetNumInputTerminals (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioControlObject * 			thisControl;
	UInt8								numInputTerminals;

	numInputTerminals = 0;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
		numInputTerminals = thisControl->GetNumInputTerminals ();

	return numInputTerminals;
}

UInt8 USBAudioConfigObject::GetNumOutputTerminals (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioControlObject * 			thisControl;
	UInt8								numOutputTerminals;

	numOutputTerminals = 0;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
		numOutputTerminals = thisControl->GetNumOutputTerminals ();

	return numOutputTerminals;
}

UInt8 USBAudioConfigObject::GetNumSelectorUnits (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioControlObject * 			thisControl;
	UInt8								numSelectorUnits;

	numSelectorUnits = 0;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
		numSelectorUnits = thisControl->GetNumSelectorUnits ();

	return numSelectorUnits;
}

UInt8 USBAudioConfigObject::GetNumSources (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 unitID) {
    USBAudioControlObject * 			thisControl;
	UInt8								numSources;

	numSources = 0;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
		numSources = thisControl->GetNumSources (unitID);

	return numSources;
}

UInt8 USBAudioConfigObject::GetNumStreamInterfaces (void) {
    USBAudioStreamObject * 				thisStream;
    UInt16								numInterfaces;
    UInt16								interfaceNum;
    UInt16								indx;

    numInterfaces = 0;
    interfaceNum = 0;
	if (NULL != theStreams) {
		for (indx = 0; indx < theStreams->getCount (); indx++) {
			thisStream = OSDynamicCast (USBAudioStreamObject, theStreams->getObject (indx));
			if (thisStream && thisStream->GetInterfaceNum () != interfaceNum) {
				interfaceNum = thisStream->GetInterfaceNum ();
				numInterfaces++;
			}
		}
	}

    return numInterfaces;
}

UInt16 USBAudioConfigObject::GetSamplesPerFrame (UInt8 interfaceNum, UInt8 altInterfaceNum) {
	USBAudioStreamObject * 				thisStream;
	UInt16								samplesPerFrame;
	
	samplesPerFrame = 0;
	thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
	if (thisStream)
		samplesPerFrame = USBToHostWord (thisStream->GetMaxBitRate ());
	
	return samplesPerFrame;
}

UInt32 * USBAudioConfigObject::GetSampleRates (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioStreamObject * 				thisStream;
    UInt32 *							sampleRates;

    sampleRates = 0;
    thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
    if (thisStream)
        sampleRates = thisStream->GetSampleRates ();

    return sampleRates;
}

UInt8 USBAudioConfigObject::GetSampleSize (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioStreamObject * 				thisStream;
    UInt8								sampleSize;

    sampleSize = 0;
    thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
    if (thisStream)
        sampleSize = thisStream->GetSampleSize ();

    return sampleSize;
}

UInt8 * USBAudioConfigObject::GetSelectorSources (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 unitID) {
    USBAudioControlObject *				thisControl;
    UInt8 *								sources;

    sources = NULL;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
        sources = thisControl->GetSelectorSources (unitID);

    return sources;
}

UInt8 USBAudioConfigObject::GetSubType (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 unitID) {
    USBAudioControlObject *				thisControl;
    UInt8								subType;

    subType = 0;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
        subType = thisControl->GetSubType (unitID);

    return subType;
}

UInt8 USBAudioConfigObject::GetSourceID (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 unitID) {
    USBAudioControlObject *				thisControl;
    UInt8								sourceID;

    sourceID = 0;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
        sourceID = thisControl->GetSourceID (unitID);

    return sourceID;
}

UInt8 USBAudioConfigObject::GetUnitType (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 unitID) {
    USBAudioControlObject *				thisControl;
    UInt8								unitType;

    unitType = 0;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
        unitType = thisControl->GetSourceID (unitID);

    return unitType;
}

UInt8 * USBAudioConfigObject::GetSourceIDs (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 unitID) {
    USBAudioControlObject *				thisControl;
    UInt8 *								sourceIDs;

    sourceIDs = 0;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
        sourceIDs = thisControl->GetSourceIDs (unitID);

    return sourceIDs;
}

UInt8 USBAudioConfigObject::GetSubframeSize (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioStreamObject * 				thisStream;
    UInt8								subframeSize;

    subframeSize = 0;
    thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
    if (thisStream)
        subframeSize = thisStream->GetSubframeSize ();

    return subframeSize;
}

UInt8 USBAudioConfigObject::GetTerminalLink (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioStreamObject * 				thisStream;
    UInt8								terminalLink;

    terminalLink = 0;
    thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
    if (thisStream)
        terminalLink = thisStream->GetTerminalLink ();

    return terminalLink;
}

UInt16 USBAudioConfigObject::GetOutputTerminalType (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 terminalNum) {
    USBAudioControlObject *				thisControl;
    UInt16								terminalType;

    terminalType = USBToHostWord (OUTPUT_UNDEFINED);
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
		terminalType = USBToHostWord (thisControl->GetOutputTerminalType (terminalNum));

    return terminalType;
}

Boolean USBAudioConfigObject::IsocEndpointDoesMaxPacketsOnly (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioStreamObject * 				thisStream;
	Boolean								maxPacketsOnly;

	maxPacketsOnly = FALSE;
    thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
    if (thisStream)
        maxPacketsOnly = thisStream->IsocEndpointDoesMaxPacketsOnly ();

    return maxPacketsOnly;
}

UInt8 USBAudioConfigObject::IsocEndpointGetLockDelay (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioStreamObject * 				thisStream;
	UInt8								lockDelay;

	lockDelay = 0;
    thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
    if (thisStream)
        lockDelay = thisStream->IsocEndpointGetLockDelay ();

    return lockDelay;
}

UInt8 USBAudioConfigObject::IsocEndpointGetLockDelayUnits (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioStreamObject * 				thisStream;
	UInt8								lockDelayUnits;

	lockDelayUnits = 0;
    thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
    if (thisStream)
        lockDelayUnits = thisStream->IsocEndpointGetLockDelayUnits ();

    return lockDelayUnits;
}

Boolean USBAudioConfigObject::IsocEndpointHasPitchControl (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioStreamObject * 				thisStream;
	Boolean								hasPitchCntrl;

	hasPitchCntrl = FALSE;
    thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
    if (thisStream)
        hasPitchCntrl = thisStream->IsocEndpointHasPitchControl ();

    return hasPitchCntrl;
}

Boolean USBAudioConfigObject::IsocEndpointHasSampleFreqControl (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioStreamObject * 				thisStream;
	Boolean								hasSampleFreqCntrl;

	hasSampleFreqCntrl = FALSE;
    thisStream = GetStreamObject (interfaceNum, altInterfaceNum);
    if (thisStream)
        hasSampleFreqCntrl = thisStream->IsocEndpointHasSampleFreqControl ();

    return hasSampleFreqCntrl;
}

Boolean USBAudioConfigObject::MasterHasMuteControl (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt8 featureUnitID) {
    USBAudioControlObject *				thisControl;
    Boolean								theControl;

    theControl = FALSE;
    thisControl = GetControlObject (interfaceNum, altInterfaceNum);
    if (thisControl)
		theControl = thisControl->MasterHasMuteControl (featureUnitID);

    return theControl;
}

Boolean USBAudioConfigObject::VerifySampleRateIsSupported (UInt8 interfaceNum, UInt8 altInterfaceNum, UInt32 verifyRate) {
	IOReturn							result;
	UInt32 *							sampleRates;
	UInt8								rateIndx;
	UInt8								numSampleRates;

	result = FALSE;
	numSampleRates = GetNumSampleRates (interfaceNum, altInterfaceNum);
	sampleRates = GetSampleRates (interfaceNum, altInterfaceNum);
	if (numSampleRates) {
		// There are a discrete number of sample rates supported, so check each one to see if they support the one we want.
		for (rateIndx = 0; rateIndx < numSampleRates && result == FALSE; rateIndx++) {
			if (sampleRates[rateIndx] == verifyRate) {
				result = TRUE;
			}
		}
	} else if (sampleRates) {
		// There is a range of sample rates supported, so check to see if the one we want is within that range.
		if (sampleRates[0] <= verifyRate && sampleRates[1] >= verifyRate) {
			result = TRUE;
		}
	} else {
		debugIOLog ("!!!!There aren't any sample rates!!!!");
	}

	return result;
}

/*
    Private methods
*/
USBAudioStreamObject * USBAudioConfigObject::GetStreamObject (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioStreamObject * 				thisStream;
    UInt8								indx;
    Boolean								found;

    indx = 0;
    found = FALSE;
	thisStream = NULL;
	if (NULL != theStreams) {
		while (!found && indx < theStreams->getCount ()) {
			thisStream = OSDynamicCast (USBAudioStreamObject, theStreams->getObject (indx));
			if (thisStream && interfaceNum == thisStream->GetInterfaceNum () && altInterfaceNum == thisStream->GetAltInterfaceNum ()) {
				found = TRUE;
			}
			indx++;
		}
	}

	if (found != TRUE)
		thisStream = NULL;

    return thisStream;
}

USBAudioControlObject * USBAudioConfigObject::GetControlObject (UInt8 interfaceNum, UInt8 altInterfaceNum) {
    USBAudioControlObject * 			thisControl;
    UInt8								indx;
    Boolean								found;

    indx = 0;
    found = FALSE;
	thisControl = NULL;
	if (NULL != theControls) {
		while (!found && indx < theControls->getCount ()) {
			thisControl = OSDynamicCast (USBAudioControlObject, theControls->getObject (indx));
			if (thisControl && interfaceNum == thisControl->GetInterfaceNum () && altInterfaceNum == thisControl->GetAltInterfaceNum ()) {
				found = TRUE;
			}
			indx++;
		}
	}

	if (found != TRUE)
		thisControl = NULL;

    return thisControl;
}

void USBAudioConfigObject::ParseConfigurationDescriptor (void) {
    USBInterfaceDescriptorPtr			theInterfacePtr;
    USBAudioControlObject *				theControlObject;
    USBAudioStreamObject *				theStreamObject;
	UInt8 *								streamInterfaceNumbers;
	UInt8								thisInterfaceNumber;
	UInt8								lastInterfaceNumber;
	UInt8								numStreamInterfaces;
    UInt8								interfaceClass;
    UInt8								interfaceSubClass;
	UInt8								numParsedInterfaces;
	UInt8								index;
	bool								haveControlInterface;
	bool								foundStreamInterface;

    FailIf (NULL == theConfigurationDescriptorPtr, Exit);
    FailIf (0 == theConfigurationDescriptorPtr->bLength, Exit);
    FailIf (CONFIGURATION != theConfigurationDescriptorPtr->bDescriptorType, Exit);

	theControlObject = NULL;
	theStreamObject = NULL;
    theInterfacePtr = (USBInterfaceDescriptorPtr)((UInt8 *)theConfigurationDescriptorPtr + theConfigurationDescriptorPtr->bLength);
	numParsedInterfaces = 0;
	numStreamInterfaces = 0;
	lastInterfaceNumber = 0;
	haveControlInterface = FALSE;
	foundStreamInterface = FALSE;
    while (theInterfacePtr && 0 != theInterfacePtr->bLength) {
        if (INTERFACE == ((ACInterfaceDescriptorPtr)theInterfacePtr)->bDescriptorType) {
			debugIOLog ("in INTERFACE in ParseConfigurationDescriptor");
			thisInterfaceNumber = ((ACInterfaceDescriptorPtr)theInterfacePtr)->bInterfaceNumber;
			theInterfacePtr = ParseInterfaceDescriptor (theInterfacePtr, &interfaceClass, &interfaceSubClass);

			debugIOLog ("theControlInterfaceNum = %d, thisInterfaceNumber = %d", theControlInterfaceNum, thisInterfaceNumber);
			if (AUDIOCONTROL == interfaceSubClass && theControlInterfaceNum == thisInterfaceNumber) {
				debugIOLog ("found a AUDIOCONTROL CS_INTERFACE ");
				if (NULL != theControls) {
					theControlObject = OSDynamicCast (USBAudioControlObject, theControls->getLastObject ());
				}
				FailIf (NULL == theControlObject, Exit);
				theInterfacePtr = theControlObject->ParseACInterfaceDescriptor (theInterfacePtr, ((ACInterfaceDescriptorPtr)theInterfacePtr)->bInterfaceNumber);
				GetControlledStreamNumbers (&streamInterfaceNumbers, &numStreamInterfaces);
				haveControlInterface = TRUE;
			} else if (haveControlInterface && AUDIOSTREAMING == interfaceSubClass) {
				debugIOLog ("in CS_INTERFACE in ParseConfigurationDescriptor");
				for (index = 0; index < numStreamInterfaces; index++) {
					debugIOLog ("comparing thisInterfaceNum = %d with %d", thisInterfaceNumber, streamInterfaceNumbers[index]);
					if (thisInterfaceNumber == streamInterfaceNumbers[index]) {
						debugIOLog ("found a AUDIOSTREAMING CS_INTERFACE ");
						if (NULL != theStreams) {
							theStreamObject = OSDynamicCast (USBAudioStreamObject, theStreams->getLastObject ());
						}
						FailIf (NULL == theStreamObject, Exit);
						theInterfacePtr = theStreamObject->ParseASInterfaceDescriptor (theInterfacePtr, ((ACInterfaceDescriptorPtr)theInterfacePtr)->bInterfaceNumber);
						foundStreamInterface = TRUE;
						break;			// Get out of for loop
					}
				}
				if (thisInterfaceNumber != lastInterfaceNumber) {
					lastInterfaceNumber = thisInterfaceNumber;
					numParsedInterfaces++;
					if (numParsedInterfaces > numStreamInterfaces) {
						break;		// Get out of while loop, we've parsed everything associated with this control interface
					}
				}
			} else if (MIDISTREAMING == interfaceSubClass) {
				debugIOLog ("MIDI, jumping forward %d bytes", theInterfacePtr->bLength);
				for (index = 0; index < numStreamInterfaces; index++) {
					if (thisInterfaceNumber == streamInterfaceNumbers[index]) {
						break;
					}
				}
				theInterfacePtr = (USBInterfaceDescriptorPtr)((UInt8 *)theInterfacePtr + theInterfacePtr->bLength);
			} else if (AUDIOCONTROL == interfaceSubClass) {
				debugIOLog ("Found a control interface that we don't care about");
				debugIOLog ("jumping forward %d bytes", ((((ACInterfaceHeaderDescriptorPtr)theInterfacePtr)->wTotalLength[1] << 8) | (((ACInterfaceHeaderDescriptorPtr)theInterfacePtr)->wTotalLength[0])));
				theInterfacePtr = (USBInterfaceDescriptorPtr)((UInt8 *)theInterfacePtr + ((((ACInterfaceHeaderDescriptorPtr)theInterfacePtr)->wTotalLength[1] << 8) | (((ACInterfaceHeaderDescriptorPtr)theInterfacePtr)->wTotalLength[0])));
			} else {
				debugIOLog ("Unknown, jumping forward %d bytes", theInterfacePtr->bLength);
				theInterfacePtr = (USBInterfaceDescriptorPtr)((UInt8 *)theInterfacePtr + theInterfacePtr->bLength);
			}
		} else {
			debugIOLog ("in default in ParseConfigurationDescriptor, jumping forward %d bytes", theInterfacePtr->bLength);
			theInterfacePtr = (USBInterfaceDescriptorPtr)((UInt8 *)theInterfacePtr + theInterfacePtr->bLength);
        }
    }

	if (theControlObject && FALSE == foundStreamInterface) {
		theControls->removeObject (theControls->getCount () - 1);
	}

Exit:
    return;
}

USBInterfaceDescriptorPtr USBAudioConfigObject::ParseInterfaceDescriptor (USBInterfaceDescriptorPtr theInterfacePtr, UInt8 * interfaceClass, UInt8 * interfaceSubClass) {
	USBAudioControlObject *				theControlObject;
	USBAudioStreamObject *				theStreamObject;

	debugIOLog ("in ParseInterfaceDescriptor");

	FailIf (NULL == theInterfacePtr, Exit);
	FailIf (0 == theInterfacePtr->bLength, Exit);

	if (NULL != interfaceClass)
		*interfaceClass = theInterfacePtr->bInterfaceClass;
	if (NULL != interfaceSubClass)
		*interfaceSubClass = theInterfacePtr->bInterfaceSubClass;

	if (AUDIOCONTROL == theInterfacePtr->bInterfaceSubClass) {
		debugIOLog ("found a AUDIOCONTROL interface");
		if (theControlInterfaceNum == theInterfacePtr->bDescriptorSubtype) {
			theControlObject = USBAudioControlObject::create ();

			FailIf (NULL == theControlObject, Exit);
			theControlObject->SetInterfaceNumber (theInterfacePtr->bDescriptorSubtype);
			theControlObject->SetAlternateSetting (theInterfacePtr->bAlternateSetting);
			theControlObject->SetNumEndpoints (theInterfacePtr->bNumEndpoints);
			theControlObject->SetInterfaceClass (theInterfacePtr->bInterfaceClass);
			theControlObject->SetInterfaceSubClass (theInterfacePtr->bInterfaceSubClass);
			theControlObject->SetInterfaceProtocol (theInterfacePtr->bInterfaceProtocol);

			if (NULL == theControls) {
				theControls = OSArray::withObjects ((const OSObject **)&theControlObject, 1);
				FailIf (NULL == theControls, Exit);
			} else {
				theControls->setObject (theControlObject);
			}

			theControlObject->release ();
		}
	} else if (AUDIOSTREAMING == theInterfacePtr->bInterfaceSubClass) {
		debugIOLog ("found a AUDIOSTREAMING interface");
		theStreamObject = USBAudioStreamObject::create ();

		FailIf (NULL == theStreamObject, Exit);
		theStreamObject->SetInterfaceNumber (theInterfacePtr->bDescriptorSubtype);
		theStreamObject->SetAlternateSetting (theInterfacePtr->bAlternateSetting);
		theStreamObject->SetNumEndpoints (theInterfacePtr->bNumEndpoints);
		theStreamObject->SetInterfaceClass (theInterfacePtr->bInterfaceClass);
		theStreamObject->SetInterfaceSubClass (theInterfacePtr->bInterfaceSubClass);
		theStreamObject->SetInterfaceProtocol (theInterfacePtr->bInterfaceProtocol);

		if (NULL == theStreams) {
			theStreams = OSArray::withObjects ((const OSObject **)&theStreamObject, 1);
			FailIf (NULL == theStreams, Exit);
		} else {
			theStreams->setObject (theStreamObject);
		}

		theStreamObject->release ();
	}

	theInterfacePtr = (USBInterfaceDescriptorPtr)((UInt8 *)theInterfacePtr + theInterfacePtr->bLength);

Exit:
	return theInterfacePtr;
}

/* ------------------------------------------------------
    USBAudioControlObject
------------------------------------------------------ */
/*
	Public methods
*/
OSDefineMetaClassAndStructors (USBAudioControlObject, OSObject);

USBAudioControlObject * USBAudioControlObject::create (void) {
    USBAudioControlObject *			controlObject;

    controlObject = new USBAudioControlObject;

    FailIf (NULL == controlObject, Exit);
    if (FALSE == controlObject->init ()) {
        controlObject->release ();
        controlObject = 0;
    }

Exit:
    return controlObject;
}

void USBAudioControlObject::free (void) {
	if (NULL != theInputTerminals) {
		theInputTerminals->release ();
		theInputTerminals = NULL;
	}

	if (NULL != theOutputTerminals) {
		theOutputTerminals->release ();
		theOutputTerminals = NULL;
	}

	if (NULL != theFeatureUnits) {
		theFeatureUnits->release ();
		theFeatureUnits = NULL;
	}

	if (NULL != theMixerUnits) {
		theMixerUnits->release ();
		theMixerUnits = NULL;
	}

	if (NULL != theSelectorUnits) {
		theSelectorUnits->release ();
		theSelectorUnits = NULL;
	}

	if (NULL != theProcessingUnits) {
		theProcessingUnits->release ();
		theProcessingUnits = NULL;
	}

	if (NULL != theExtensionUnits) {
		theExtensionUnits->release ();
		theExtensionUnits = NULL;
	}

    OSObject::free ();
}

UInt8 USBAudioControlObject::GetNumControls (UInt8 featureUnitID) {
	USBFeatureUnitObject *				thisFeatureUnit;
    UInt8								numControls;

	numControls = 0;
	thisFeatureUnit = GetFeatureUnitObject (featureUnitID);
	if (thisFeatureUnit)
        numControls = thisFeatureUnit->GetNumControls ();

    return numControls;
}

// Channel #1 is left channel, #2 is right channel
Boolean USBAudioControlObject::ChannelHasMuteControl (UInt8 featureUnitID, UInt8 channelNum) {
	USBFeatureUnitObject *				thisFeatureUnit;
	Boolean								result;

	result = FALSE;
	thisFeatureUnit = GetFeatureUnitObject (featureUnitID);
	if (thisFeatureUnit)
		result = thisFeatureUnit->ChannelHasMuteControl (channelNum);

	return result;
}

Boolean USBAudioControlObject::ChannelHasVolumeControl (UInt8 featureUnitID, UInt8 channelNum) {
	USBFeatureUnitObject *				thisFeatureUnit;
	Boolean								result;

	result = FALSE;
	thisFeatureUnit = GetFeatureUnitObject (featureUnitID);
	if (thisFeatureUnit)
		result = thisFeatureUnit->ChannelHasVolumeControl (channelNum);

	return result;
}

USBACDescriptorObject * USBAudioControlObject::GetACDescriptorObject (UInt8 unitID) {
    USBACDescriptorObject * 			thisACDescriptorObject;

	thisACDescriptorObject = GetInputTerminalObject (unitID);
	if (!thisACDescriptorObject)
		thisACDescriptorObject = GetOutputTerminalObject (unitID);
	if (!thisACDescriptorObject)
		thisACDescriptorObject = GetMixerObject (unitID);
	if (!thisACDescriptorObject)
		thisACDescriptorObject = GetSelectorUnitObject (unitID);
	if (!thisACDescriptorObject)
		thisACDescriptorObject = GetFeatureUnitObject (unitID);
	if (!thisACDescriptorObject)
		thisACDescriptorObject = GetProcessingUnitObject (unitID);
	if (!thisACDescriptorObject)
		thisACDescriptorObject = GetExtensionUnitObject (unitID);

    return thisACDescriptorObject;
}

UInt8 USBAudioControlObject::GetFeatureSourceID (UInt8 featureUnitID) {
	USBFeatureUnitObject *		theFeatureUnit;
	UInt8						sourceID;

	sourceID = 0;
	theFeatureUnit = GetFeatureUnitObject (featureUnitID);
	if (theFeatureUnit)
		sourceID = theFeatureUnit->GetSourceID ();

	return sourceID;
}

UInt8 USBAudioControlObject::GetIndexedFeatureUnitID (UInt8 featureUnitIndex) {
	USBFeatureUnitObject *				theFeatureUnit;
	UInt8								unitID;

	unitID = 0;
	theFeatureUnit = GetIndexedFeatureUnitObject (featureUnitIndex);
	if (theFeatureUnit)
		unitID = theFeatureUnit->GetUnitID ();

	return unitID;
}

UInt8 USBAudioControlObject::GetIndexedMixerUnitID (UInt8 mixerUnitIndex) {
	USBMixerUnitObject *				theMixerUnit;
	UInt8								unitID;

	unitID = 0;
	theMixerUnit = GetIndexedMixerUnitObject (mixerUnitIndex);
	if (theMixerUnit)
		unitID = theMixerUnit->GetUnitID ();

	return unitID;
}

UInt8 USBAudioControlObject::GetIndexedSelectorUnitID (UInt8 selectorUnitIndex) {
	USBSelectorUnitObject *				theSelectorUnit;
	UInt8								unitID;

	unitID = 0;
	theSelectorUnit = GetIndexedSelectorUnitObject (selectorUnitIndex);
	if (theSelectorUnit)
		unitID = theSelectorUnit->GetUnitID ();

	return unitID;
}

UInt8 USBAudioControlObject::GetFeatureUnitIDConnectedToOutputTerminal (UInt8 outputTerminalID) {
    USBOutputTerminalObject *			thisOutputTerminal;
	USBFeatureUnitObject *				thisFeatureUnit;
	UInt8								outputTerminalSourceID;
	UInt8								featureUnitID;

	outputTerminalSourceID = 0;
	featureUnitID = 0;
	thisOutputTerminal = GetOutputTerminalObject (outputTerminalID);
    if (thisOutputTerminal)
		outputTerminalSourceID = thisOutputTerminal->GetSourceID ();

	if (0 != outputTerminalSourceID) {
		thisFeatureUnit = GetFeatureUnitObject (outputTerminalSourceID);
		if (thisFeatureUnit)
			featureUnitID = outputTerminalSourceID;
		else {
			// have to keep looking upstream of whatever this object is
		}
	}

	return featureUnitID;
}

UInt16 USBAudioControlObject::GetIndexedInputTerminalType (UInt8 index) {
    USBInputTerminalObject *			thisInputTerminal;
    UInt16								terminalType;

	thisInputTerminal = NULL;
    terminalType =  USBToHostWord (INPUT_UNDEFINED);
	if (NULL != theInputTerminals) {
		thisInputTerminal = OSDynamicCast (USBInputTerminalObject, theInputTerminals->getObject (index));
	}

    if (thisInputTerminal)
		terminalType = USBToHostWord (thisInputTerminal->GetTerminalType ());

	return terminalType;
}

UInt8 USBAudioControlObject::GetIndexedInputTerminalID (UInt8 index) {
    USBInputTerminalObject *			thisInputTerminal;
    UInt8								terminalID;

	thisInputTerminal = NULL;
    terminalID = 0;
	if (NULL != theInputTerminals) {
		thisInputTerminal = OSDynamicCast (USBInputTerminalObject, theInputTerminals->getObject (index));
	}

    if (thisInputTerminal)
		terminalID = thisInputTerminal->GetUnitID ();

	return terminalID;
}

UInt8 USBAudioControlObject::GetIndexedOutputTerminalID (UInt8 index) {
    USBOutputTerminalObject *			thisOutputTerminal;
    UInt8								terminalID;

	thisOutputTerminal = NULL;
    terminalID = 0;
	if (NULL != theOutputTerminals) {
		thisOutputTerminal = OSDynamicCast (USBOutputTerminalObject, theOutputTerminals->getObject (index));
	}

    if (thisOutputTerminal)
		terminalID = thisOutputTerminal->GetUnitID ();

	return terminalID;
}

UInt16 USBAudioControlObject::GetIndexedOutputTerminalType (UInt8 index) {
    USBOutputTerminalObject *			thisOutputTerminal;
    UInt16								terminalType;

	thisOutputTerminal = NULL;
    terminalType = USBToHostWord (OUTPUT_UNDEFINED);
	if (NULL != theOutputTerminals) {
		thisOutputTerminal = OSDynamicCast (USBOutputTerminalObject, theOutputTerminals->getObject (index));
	}

    if (thisOutputTerminal)
		terminalType = USBToHostWord (thisOutputTerminal->GetTerminalType ());

	return terminalType;
}

USBFeatureUnitObject * USBAudioControlObject::GetIndexedFeatureUnitObject (UInt8 index) {
	USBFeatureUnitObject *				indexedFeatureUnit;

	indexedFeatureUnit = NULL;
	if (NULL != theFeatureUnits) {
		indexedFeatureUnit = OSDynamicCast (USBFeatureUnitObject, theFeatureUnits->getObject (index));
	}

	return indexedFeatureUnit;
}

USBMixerUnitObject * USBAudioControlObject::GetIndexedMixerUnitObject (UInt8 index) {
	USBMixerUnitObject *				indexedMixerUnit;

	indexedMixerUnit = NULL;
	if (NULL != theMixerUnits) {
		indexedMixerUnit = OSDynamicCast (USBMixerUnitObject, theMixerUnits->getObject (index));
	}

	return indexedMixerUnit;
}

USBSelectorUnitObject * USBAudioControlObject::GetIndexedSelectorUnitObject (UInt8 index) {
	USBSelectorUnitObject *				indexedSelectorUnit;

	indexedSelectorUnit = NULL;
	if (NULL != theSelectorUnits) {
		indexedSelectorUnit = OSDynamicCast (USBSelectorUnitObject, theSelectorUnits->getObject (index));
	}

	return indexedSelectorUnit;
}

USBFeatureUnitObject * USBAudioControlObject::GetFeatureUnitObject (UInt8 unitID) {
    USBFeatureUnitObject *	 			thisFeatureUnit;
    UInt8								indx;
    Boolean								found;

    indx = 0;
    found = FALSE;
	thisFeatureUnit = NULL;
	if (NULL != theFeatureUnits) {
		while (!found && indx < theFeatureUnits->getCount ()) {
			thisFeatureUnit = OSDynamicCast (USBFeatureUnitObject, theFeatureUnits->getObject (indx));
			if (thisFeatureUnit && unitID == thisFeatureUnit->GetUnitID ()) {
				found = TRUE;
			}
			indx++;
		}
	}

	if (found != TRUE)
		thisFeatureUnit = NULL;

    return thisFeatureUnit;
}

USBInputTerminalObject * USBAudioControlObject::GetInputTerminalObject (UInt8 unitID) {
    USBInputTerminalObject * 			thisInputTerminal;
    UInt8								indx;
    Boolean								found;

    indx = 0;
    found = FALSE;
	thisInputTerminal = NULL;
	if (NULL != theInputTerminals) {
		while (!found && indx < theInputTerminals->getCount ()) {
			thisInputTerminal = OSDynamicCast (USBInputTerminalObject, theInputTerminals->getObject (indx));
			if (thisInputTerminal && unitID == thisInputTerminal->GetUnitID ()) {
				found = TRUE;
			}
			indx++;
		}
	}

	if (found != TRUE)
		thisInputTerminal = NULL;

    return thisInputTerminal;
}

USBOutputTerminalObject * USBAudioControlObject::GetOutputTerminalObject (UInt8 unitID) {
    USBOutputTerminalObject * 			thisOutputTerminal;
    UInt8								indx;
    Boolean								found;

    indx = 0;
    found = FALSE;
	thisOutputTerminal = NULL;
	if (NULL != theOutputTerminals) {
		while (!found && indx < theOutputTerminals->getCount ()) {
			thisOutputTerminal = OSDynamicCast (USBOutputTerminalObject, theOutputTerminals->getObject (indx));
			if (thisOutputTerminal && unitID == thisOutputTerminal->GetUnitID ()) {
				found = TRUE;
			}
			indx++;
		}
	}

	if (found != TRUE)
		thisOutputTerminal = NULL;

    return thisOutputTerminal;
}

UInt16 USBAudioControlObject::GetInputTerminalType (UInt8 unitID) {
    USBInputTerminalObject *			thisInputTerminal;
    UInt16								terminalType;

    terminalType = USBToHostWord (INPUT_UNDEFINED);
    thisInputTerminal = GetInputTerminalObject (unitID);
    if (thisInputTerminal)
		terminalType = USBToHostWord (thisInputTerminal->GetTerminalType ());

    return terminalType;
}

UInt8 USBAudioControlObject::GetNumInputTerminals (void) {
	UInt8								count;

	count = 0;
	if (NULL != theInputTerminals) {
		count = theInputTerminals->getCount ();
	}

	return count;
}

UInt8 USBAudioControlObject::GetNumOutputTerminals (void) {
	UInt8								count;

	count = 0;
	if (NULL != theOutputTerminals) {
		count = theOutputTerminals->getCount ();
	}

	return count;
}

UInt8 USBAudioControlObject::GetNumSelectorUnits (void) {
	UInt8								count;

	count = 0;
	if (NULL != theSelectorUnits) {
		count = theSelectorUnits->getCount ();
	}

	return count;
}

UInt8 USBAudioControlObject::GetNumSources (UInt8 unitID) {
    USBACDescriptorObject *				thisProcessingUnit;
    UInt8								numSources;

    numSources = 0;
    thisProcessingUnit = GetACDescriptorObject (unitID);
    if (thisProcessingUnit)
		numSources = thisProcessingUnit->GetNumInPins ();

    return numSources;
}

UInt8 USBAudioControlObject::GetSourceID (UInt8 unitID) {
	USBACDescriptorObject *				theUnit;
	UInt8								sourceID;

	sourceID = 0;
	theUnit = GetACDescriptorObject (unitID);
	if (NULL != theUnit) {
		sourceID = theUnit->GetSourceID ();
	}

	return sourceID;
}

UInt8 * USBAudioControlObject::GetSourceIDs (UInt8 unitID) {
	USBACDescriptorObject *				theUnit;
	UInt8 *								sourceIDs;

	sourceIDs = NULL;
	theUnit = GetACDescriptorObject (unitID);
	if (NULL != theUnit) {
		switch (theUnit->GetDescriptorSubType ()) {
			case MIXER_UNIT:
				sourceIDs = GetMixerSources (unitID);
				break;
			case SELECTOR_UNIT:
				sourceIDs = GetSelectorSources (unitID);
				break;
			case PROCESSING_UNIT:
				sourceIDs = GetProcessingUnitSources (unitID);
				break;
			case EXTENSION_UNIT:
				sourceIDs = GetExtensionUnitSources (unitID);
				break;
		}
	}

	return sourceIDs;
}

UInt8 USBAudioControlObject::GetSubType (UInt8 unitID) {
	USBACDescriptorObject *				theUnit;
	UInt8								subType;

	subType = 0;
	theUnit = GetACDescriptorObject (unitID);
	if (NULL != theUnit) {
		subType = theUnit->GetDescriptorSubType ();
	}

	return subType;
}

USBProcessingUnitObject * USBAudioControlObject::GetProcessingUnitObject (UInt8 unitID) {
    USBProcessingUnitObject * 			thisProcessingUnit;
    UInt8								indx;
    Boolean								found;

    indx = 0;
    found = FALSE;
	thisProcessingUnit = NULL;
	if (NULL != theProcessingUnits) {
		while (!found && indx < theProcessingUnits->getCount ()) {
			thisProcessingUnit = OSDynamicCast (USBProcessingUnitObject, theProcessingUnits->getObject (indx));
			if (thisProcessingUnit && unitID == thisProcessingUnit->GetUnitID ()) {
				found = TRUE;
			}
			indx++;
		}
	}

	if (found != TRUE)
		thisProcessingUnit = NULL;

    return thisProcessingUnit;
}

USBMixerUnitObject * USBAudioControlObject::GetMixerObject (UInt8 unitID) {
    USBMixerUnitObject *		 		thisMixerObject;
    UInt8								indx;
    Boolean								found;

    indx = 0;
    found = FALSE;
	thisMixerObject = NULL;
	if (NULL != theMixerUnits) {
		while (!found && indx < theMixerUnits->getCount ()) {
			thisMixerObject = OSDynamicCast (USBMixerUnitObject, theMixerUnits->getObject (indx));
			if (thisMixerObject && unitID == thisMixerObject->GetUnitID ()) {
				found = TRUE;
			}
			indx++;
		}
	}

	if (found != TRUE)
		thisMixerObject = NULL;

    return thisMixerObject;
}

USBExtensionUnitObject * USBAudioControlObject::GetExtensionUnitObject (UInt8 unitID) {
    USBExtensionUnitObject * 			thisExtensionUnit;
    UInt8								indx;
    Boolean								found;

    indx = 0;
    found = FALSE;
	thisExtensionUnit = NULL;
	if (NULL != theExtensionUnits) {
		while (!found && indx < theExtensionUnits->getCount ()) {
			thisExtensionUnit = OSDynamicCast (USBExtensionUnitObject, theExtensionUnits->getObject (indx));
			if (thisExtensionUnit && unitID == thisExtensionUnit->GetUnitID ()) {
				found = TRUE;
			}
			indx++;
		}
	}

	if (found != TRUE)
		thisExtensionUnit = NULL;

    return thisExtensionUnit;
}

USBSelectorUnitObject * USBAudioControlObject::GetSelectorUnitObject (UInt8 unitID) {
    USBSelectorUnitObject *		 		thisSelectorObject;
    UInt8								indx;
    Boolean								found;

    indx = 0;
    found = FALSE;
	thisSelectorObject = NULL;
	if (NULL != theSelectorUnits) {
		while (!found && indx < theSelectorUnits->getCount ()) {
			thisSelectorObject = OSDynamicCast (USBSelectorUnitObject, theSelectorUnits->getObject (indx));
			if (thisSelectorObject && unitID == thisSelectorObject->GetUnitID ()) {
				found = TRUE;
			}
			indx++;
		}
	}

	if (found != TRUE)
		thisSelectorObject = NULL;

    return thisSelectorObject;
}

UInt16 USBAudioControlObject::GetOutputTerminalType (UInt8 unitID) {
    USBOutputTerminalObject *			thisOutputTerminal;
    UInt16								terminalType;

    terminalType = USBToHostWord (OUTPUT_UNDEFINED);
    thisOutputTerminal = GetOutputTerminalObject (unitID);
    if (thisOutputTerminal)
		terminalType = USBToHostWord (thisOutputTerminal->GetTerminalType ());

    return terminalType;
}

UInt8 * USBAudioControlObject::GetSelectorSources (UInt8 unitID) {
    USBSelectorUnitObject *				thisSelectorUnit;
    UInt8 *								selectorSources;

    selectorSources = NULL;
    thisSelectorUnit = GetSelectorUnitObject (unitID);
    if (thisSelectorUnit)
		selectorSources = thisSelectorUnit->GetSelectorSources ();

    return selectorSources;
}

UInt8 * USBAudioControlObject::GetMixerSources (UInt8 unitID) {
    USBMixerUnitObject *				thisMixerUnit;
    UInt8 *								mixerSources;

    mixerSources = NULL;
    thisMixerUnit = GetMixerObject (unitID);
    if (thisMixerUnit)
		mixerSources = thisMixerUnit->GetSources ();

    return mixerSources;
}

UInt8 * USBAudioControlObject::GetExtensionUnitSources (UInt8 unitID) {
    USBExtensionUnitObject *			thisExtensionUnit;
    UInt8 *								extensionUnitSources;

    extensionUnitSources = NULL;
    thisExtensionUnit = GetExtensionUnitObject (unitID);
    if (thisExtensionUnit)
		extensionUnitSources = thisExtensionUnit->GetSources ();

    return extensionUnitSources;
}

UInt8 * USBAudioControlObject::GetProcessingUnitSources (UInt8 unitID) {
    USBProcessingUnitObject *			thisProcessingUnit;
    UInt8 *								processingUnitSources;

    processingUnitSources = NULL;
    thisProcessingUnit = GetProcessingUnitObject (unitID);
    if (thisProcessingUnit)
		processingUnitSources = thisProcessingUnit->GetSources ();

    return processingUnitSources;
}

Boolean USBAudioControlObject::MasterHasMuteControl (UInt8 featureUnitID) {
	USBFeatureUnitObject *				thisFeatureUnit;
	Boolean								result;

	result = FALSE;
	thisFeatureUnit = GetFeatureUnitObject (featureUnitID);
	if (thisFeatureUnit)
		result = thisFeatureUnit->MasterHasMuteControl ();

	return result;
}

USBInterfaceDescriptorPtr USBAudioControlObject::ParseACInterfaceDescriptor (USBInterfaceDescriptorPtr theInterfacePtr, UInt8 const currentInterface) {
	USBInputTerminalObject *	inputTerminal;
	USBOutputTerminalObject *	outputTerminal;
	USBFeatureUnitObject *		featureUnit;
	USBMixerUnitObject *		mixerUnit;
	USBSelectorUnitObject *		selectorUnit;
	USBProcessingUnitObject *	processingUnit;
	USBExtensionUnitObject *	extensionUnit;
	UInt8						index;

	debugIOLog ("+USBAudioControlObject::ParseACInterfaceDescriptor (%p, %d)", theInterfacePtr, currentInterface);

    FailIf (NULL == theInterfacePtr, Exit);
    FailIf (0 == theInterfacePtr->bLength, Exit);
    FailIf (CS_INTERFACE != theInterfacePtr->bDescriptorType, Exit);

    while (theInterfacePtr->bLength && CS_INTERFACE == theInterfacePtr->bDescriptorType) {
        switch (theInterfacePtr->bDescriptorSubtype) {
            case HEADER:
                debugIOLog ("in HEADER in ParseACInterfaceDescriptor");
                adcVersion = USBToHostWord (((ACInterfaceHeaderDescriptorPtr)theInterfacePtr)->bcdADC[0]);
				if (adcVersion != 0x0100) {
				}
				// !!!Need to check the adcVersion to make sure that it's 0x0100 or else we don't know how to parse this descriptor!!!
                numStreamInterfaces = ((ACInterfaceHeaderDescriptorPtr)theInterfacePtr)->bInCollection;
				debugIOLog ("numStreamInterfaces = %d", numStreamInterfaces);
				streamInterfaceNumbers = (UInt8 *)IOMalloc (numStreamInterfaces);
				memcpy (streamInterfaceNumbers, ((ACInterfaceHeaderDescriptorPtr)theInterfacePtr)->baInterfaceNr, numStreamInterfaces);
				debugIOLog ("they are: ");
				for (index = 0; index < numStreamInterfaces; index++) {
					debugIOLog ("%d, ", streamInterfaceNumbers[index]);
				}
				debugIOLog ("");
                break;
            case INPUT_TERMINAL:
                debugIOLog ("in INPUT_TERMINAL in ParseACInterfaceDescriptor");
				inputTerminal = new USBInputTerminalObject;
				FailIf (NULL == inputTerminal, Exit);
				inputTerminal->SetDescriptorSubType (theInterfacePtr->bDescriptorSubtype);
                inputTerminal->SetUnitID (((ACInputTerminalDescriptorPtr)theInterfacePtr)->bTerminalID);
                inputTerminal->SetTerminalType (USBToHostWord (((ACInputTerminalDescriptorPtr)theInterfacePtr)->wTerminalType));
                inputTerminal->SetAssocTerminal (((ACInputTerminalDescriptorPtr)theInterfacePtr)->bAssocTerminal);
                inputTerminal->SetNumChannels (((ACInputTerminalDescriptorPtr)theInterfacePtr)->bNrChannels);
                inputTerminal->SetChannelConfig (USBToHostWord (((ACInputTerminalDescriptorPtr)theInterfacePtr)->wChannelConfig));

				if (NULL == theInputTerminals) {
					theInputTerminals = OSArray::withObjects ((const OSObject **)&inputTerminal, 1);
					FailIf (NULL == theInputTerminals, Exit);
				} else {
					theInputTerminals->setObject (inputTerminal);
				}

				inputTerminal->release ();
                break;
            case OUTPUT_TERMINAL:
                debugIOLog ("in OUTPUT_TERMINAL in ParseACInterfaceDescriptor");
				outputTerminal = new USBOutputTerminalObject;
				FailIf (NULL == outputTerminal, Exit);
				outputTerminal->SetDescriptorSubType (theInterfacePtr->bDescriptorSubtype);
                outputTerminal->SetUnitID (((ACOutputTerminalDescriptorPtr)theInterfacePtr)->bTerminalID);
                outputTerminal->SetTerminalType (USBToHostWord (((ACOutputTerminalDescriptorPtr)theInterfacePtr)->wTerminalType));
                outputTerminal->SetAssocTerminal (((ACOutputTerminalDescriptorPtr)theInterfacePtr)->bAssocTerminal);
                outputTerminal->SetSourceID (((ACOutputTerminalDescriptorPtr)theInterfacePtr)->bSourceID);

				if (NULL == theOutputTerminals) {
					theOutputTerminals = OSArray::withObjects ((const OSObject **)&outputTerminal, 1);
					FailIf (NULL == theOutputTerminals, Exit);
				} else {
					theOutputTerminals->setObject (outputTerminal);
				}

				outputTerminal->release ();
                break;
            case FEATURE_UNIT:
				{
				UInt8						numControls;

                debugIOLog ("in FEATURE_UNIT in ParseACInterfaceDescriptor");
				featureUnit = new USBFeatureUnitObject;
				FailIf (NULL == featureUnit, Exit);
				featureUnit->SetDescriptorSubType (theInterfacePtr->bDescriptorSubtype);
                featureUnit->SetUnitID (((ACFeatureUnitDescriptorPtr)theInterfacePtr)->bUnitID);
                featureUnit->SetSourceID (((ACFeatureUnitDescriptorPtr)theInterfacePtr)->bSourceID);
                featureUnit->SetControlSize (((ACFeatureUnitDescriptorPtr)theInterfacePtr)->bControlSize);
                // subtract 7 because that's how many fields are guaranteed to be in the struct
				numControls = (((ACFeatureUnitDescriptorPtr)theInterfacePtr)->bLength - 7) / ((ACFeatureUnitDescriptorPtr)theInterfacePtr)->bControlSize;
				debugIOLog ("There are %d controls on this feature unit", numControls);
				featureUnit->InitControlsArray (&((ACFeatureUnitDescriptorPtr)theInterfacePtr)->bmaControls[0], numControls);

				if (NULL == theFeatureUnits) {
					theFeatureUnits = OSArray::withObjects ((const OSObject **)&featureUnit, 1);
					FailIf (NULL == theFeatureUnits, Exit);
				} else {
					theFeatureUnits->setObject (featureUnit);
				}

				featureUnit->release ();
                break;
				}
            case MIXER_UNIT:
				{
				UInt32						controlSize;
				UInt16 *					channelConfig;
				UInt8						nrChannels;

                debugIOLog ("in MIXER_UNIT in ParseACInterfaceDescriptor");
				mixerUnit = new USBMixerUnitObject;
				FailIf (NULL == mixerUnit, Exit);
				debugIOLog ("descriptor length = %d", theInterfacePtr->bLength);
				mixerUnit->SetDescriptorSubType (theInterfacePtr->bDescriptorSubtype);
				mixerUnit->SetUnitID (((ACMixerUnitDescriptorPtr)theInterfacePtr)->bUnitID);
				debugIOLog ("unit ID = %d", ((ACMixerUnitDescriptorPtr)theInterfacePtr)->bUnitID);
				debugIOLog ("numInPins = %d", ((ACMixerUnitDescriptorPtr)theInterfacePtr)->bNrInPins);
				mixerUnit->InitSourceIDs (&((ACMixerUnitDescriptorPtr)theInterfacePtr)->baSourceID[0], ((ACMixerUnitDescriptorPtr)theInterfacePtr)->bNrInPins);
				nrChannels = ((ACMixerUnitDescriptorPtr)theInterfacePtr)->baSourceID[((ACMixerUnitDescriptorPtr)theInterfacePtr)->bNrInPins];
				debugIOLog ("nrChannels = %d", nrChannels);
				mixerUnit->SetNumChannels (nrChannels);
				channelConfig = (UInt16 *)&((ACMixerUnitDescriptorPtr)theInterfacePtr)->baSourceID[((ACMixerUnitDescriptorPtr)theInterfacePtr)->bNrInPins + 1];
				*channelConfig = USBToHostWord (*channelConfig);
				debugIOLog ("channelConfig = %d", *channelConfig);
				mixerUnit->SetChannelConfig (*channelConfig);
				controlSize = ((ACMixerUnitDescriptorPtr)theInterfacePtr)->bLength - 10 - ((ACMixerUnitDescriptorPtr)theInterfacePtr)->bNrInPins;
				debugIOLog ("controlSize = %d", controlSize);
				mixerUnit->InitControlsArray (&((ACMixerUnitDescriptorPtr)theInterfacePtr)->baSourceID[((ACMixerUnitDescriptorPtr)theInterfacePtr)->bNrInPins + 3], controlSize);

				if (NULL == theMixerUnits) {
					theMixerUnits = OSArray::withObjects ((const OSObject **)&mixerUnit, 1);
					FailIf (NULL == theMixerUnits, Exit);
				} else {
					theMixerUnits->setObject (mixerUnit);
				}

				mixerUnit->release ();
				break;
				}
            case SELECTOR_UNIT:
                debugIOLog ("in SELECTOR_UNIT in ParseACInterfaceDescriptor");
				selectorUnit = new USBSelectorUnitObject;
				FailIf (NULL == selectorUnit, Exit);
				selectorUnit->SetDescriptorSubType (theInterfacePtr->bDescriptorSubtype);
				selectorUnit->SetUnitID (((ACSelectorUnitDescriptorPtr)theInterfacePtr)->bUnitID);
				selectorUnit->InitSourceIDs (&((ACSelectorUnitDescriptorPtr)theInterfacePtr)->baSourceID[0], ((ACSelectorUnitDescriptorPtr)theInterfacePtr)->bNrInPins);
				debugIOLog ("numInPins on selector = %d", ((ACSelectorUnitDescriptorPtr)theInterfacePtr)->bNrInPins);

				if (NULL == theSelectorUnits) {
					theSelectorUnits = OSArray::withObjects ((const OSObject **)&selectorUnit, 1);
					FailIf (NULL == theSelectorUnits, Exit);
				} else {
					theSelectorUnits->setObject (selectorUnit);
				}

				selectorUnit->release ();
				break;
            case PROCESSING_UNIT:
				{
				UInt16 *					channelConfig;
				UInt8						controlSize;
				UInt8						nrChannels;

                debugIOLog ("in PROCESSING_UNIT in ParseACInterfaceDescriptor");
				processingUnit = new USBProcessingUnitObject;
				FailIf (NULL == processingUnit, Exit);
				processingUnit->SetDescriptorSubType (theInterfacePtr->bDescriptorSubtype);
				processingUnit->SetUnitID (((ACProcessingUnitDescriptorPtr)theInterfacePtr)->bUnitID);
				processingUnit->SetProcessType (((ACProcessingUnitDescriptorPtr)theInterfacePtr)->wProcessType);
				debugIOLog ("processing unit type = 0x%x", ((ACProcessingUnitDescriptorPtr)theInterfacePtr)->wProcessType);
				processingUnit->InitSourceIDs (&((ACProcessingUnitDescriptorPtr)theInterfacePtr)->baSourceID[0], ((ACProcessingUnitDescriptorPtr)theInterfacePtr)->bNrInPins);
				debugIOLog ("numInPins = %d", ((ACProcessingUnitDescriptorPtr)theInterfacePtr)->bNrInPins);
				nrChannels = ((ACProcessingUnitDescriptorPtr)theInterfacePtr)->baSourceID[((ACProcessingUnitDescriptorPtr)theInterfacePtr)->bNrInPins];
				debugIOLog ("nrChannels = %d", nrChannels);
				processingUnit->SetNumChannels (nrChannels);
				channelConfig = (UInt16 *)&((ACProcessingUnitDescriptorPtr)theInterfacePtr)->baSourceID[((ACProcessingUnitDescriptorPtr)theInterfacePtr)->bNrInPins + 1];
				*channelConfig = USBToHostWord (*channelConfig);
				debugIOLog ("channelConfig = %d", *channelConfig);
				processingUnit->SetChannelConfig (*channelConfig);
				controlSize = ((ACProcessingUnitDescriptorPtr)theInterfacePtr)->baSourceID[((ACProcessingUnitDescriptorPtr)theInterfacePtr)->bNrInPins + 4];
				debugIOLog ("controlSize = %d", controlSize);
				processingUnit->InitControlsArray (&((ACProcessingUnitDescriptorPtr)theInterfacePtr)->baSourceID[((ACProcessingUnitDescriptorPtr)theInterfacePtr)->bNrInPins + 5], controlSize);

				if (NULL == theProcessingUnits) {
					theProcessingUnits = OSArray::withObjects ((const OSObject **)&processingUnit, 1);
					FailIf (NULL == theProcessingUnits, Exit);
				} else {
					theProcessingUnits->setObject (processingUnit);
				}

				processingUnit->release ();
				break;
				}
            case EXTENSION_UNIT:
                debugIOLog ("in EXTENSION_UNIT in ParseACInterfaceDescriptor");
				extensionUnit = new USBExtensionUnitObject;
				FailIf (NULL == extensionUnit, Exit);
				extensionUnit->SetDescriptorSubType (theInterfacePtr->bDescriptorSubtype);
				extensionUnit->SetUnitID (((ACExtensionUnitDescriptorPtr)theInterfacePtr)->bUnitID);
				extensionUnit->InitSourceIDs (&((ACExtensionUnitDescriptorPtr)theInterfacePtr)->baSourceID[0], ((ACExtensionUnitDescriptorPtr)theInterfacePtr)->bNrInPins);

				if (NULL == theExtensionUnits) {
					theExtensionUnits = OSArray::withObjects ((const OSObject **)&extensionUnit, 1);
					FailIf (NULL == theExtensionUnits, Exit);
				} else {
					theExtensionUnits->setObject (extensionUnit);
				}

				extensionUnit->release ();
				break;
            default:
                debugIOLog ("in default in ParseACInterfaceDescriptor");
        }
		theInterfacePtr = (USBInterfaceDescriptorPtr)((UInt8 *)theInterfacePtr + theInterfacePtr->bLength);
    }

Exit:
	debugIOLog ("-USBAudioControlObject::ParseACInterfaceDescriptor (%p, %d)", theInterfacePtr, currentInterface);
    return theInterfacePtr;
}

/* ------------------------------------------------------
    USBAudioStreamObject
------------------------------------------------------ */
/*
	Public methods
*/
OSDefineMetaClassAndStructors (USBAudioStreamObject, OSObject);

USBAudioStreamObject * USBAudioStreamObject::create (void) {
    USBAudioStreamObject *			streamObject;

    streamObject = new USBAudioStreamObject;

    FailIf (NULL == streamObject, Exit);
    if (FALSE == streamObject->init ()) {
        streamObject->release ();
        streamObject = 0;
    }

Exit:
    return streamObject;
}

void USBAudioStreamObject::free (void) {
    IOFree (sampleFreqs, numSampleFreqs * sizeof (UInt32));

	if (NULL != theEndpointObjects) {
		theEndpointObjects->release ();
		theEndpointObjects = NULL;
	}

    OSObject::free ();
}

USBEndpointObject * USBAudioStreamObject::GetIndexedEndpointObject (UInt8 index) {
    USBEndpointObject *		 			thisEndpoint;

	if (theEndpointObjects) {
		thisEndpoint = OSDynamicCast (USBEndpointObject, theEndpointObjects->getObject (index));
	} else {
		thisEndpoint = NULL;
	}

	return thisEndpoint;
}

UInt8 USBAudioStreamObject::GetIsocAssociatedEndpointAddress (UInt8 address) {
    USBEndpointObject *		 			thisEndpoint;
    UInt8								assocEndpointAddress;

	assocEndpointAddress = 0;
	thisEndpoint = GetEndpointByAddress (address);
	if (thisEndpoint) {
		assocEndpointAddress = thisEndpoint->GetSynchAddress ();
	}

    return assocEndpointAddress;
}

UInt8 USBAudioStreamObject::GetIsocAssociatedEndpointRefreshInt (UInt8 address) {
    USBEndpointObject *		 			thisEndpoint;
    UInt8								assocEndpointRefresh;

	assocEndpointRefresh = 0;
	thisEndpoint = GetEndpointByAddress (address);
	if (thisEndpoint) {
		assocEndpointRefresh = thisEndpoint->GetRefreshInt ();
	}

    return assocEndpointRefresh;
}

UInt8 USBAudioStreamObject::GetIsocEndpointAddress (UInt8 direction) {
    USBEndpointObject *		 					thisEndpointObject;
	UInt8										address;
    UInt8										indx;

    indx = 0;
	address = 0;
	debugIOLog ("GetIsocEndpointAddress, looking for direction %d", direction);
	if (theEndpointObjects) {
		while (!address && indx < theEndpointObjects->getCount ()) {
			thisEndpointObject = GetIndexedEndpointObject (indx);
			debugIOLog ("GetIsocEndpointAddress, this endpoint's direction %d", thisEndpointObject->GetDirection ());
			if (thisEndpointObject && direction == thisEndpointObject->GetDirection ()) {
				address = thisEndpointObject->GetAddress ();
				debugIOLog ("GetIsocEndpointAddress, found endpoint address %d", address);
			}
		indx++;
		}
	}

	return address;
}

UInt8 USBAudioStreamObject::GetIsocEndpointDirection (UInt8 index) {
    USBEndpointObject *		 					thisEndpointObject;
	UInt8										direction;

	direction = 0xFF;
	thisEndpointObject = NULL;
	if (theEndpointObjects) {
		thisEndpointObject = GetIndexedEndpointObject (index);
	}

	if (NULL != thisEndpointObject)
        direction = thisEndpointObject->GetDirection ();

	return direction;
}

UInt8 USBAudioStreamObject::GetIsocEndpointSyncType (UInt8 address) {
    USBEndpointObject *		 					thisEndpointObject;
	UInt8										syncType;

	syncType = 0;
	thisEndpointObject = GetEndpointByAddress (address);

	if (NULL != thisEndpointObject)
        syncType = thisEndpointObject->GetSyncType ();

	return syncType;
}

inline UInt32 ConvertSampleFreq(UInt8 *p)	// convert little-endian 24-bit (unsigned) to native 32-bit.  Make it a macro.
{
	UInt32 bigEndianSampleFreq = (p[2] << 16) | (p[1] << 8) | p[0];
	return (bigEndianSampleFreq);
}

USBInterfaceDescriptorPtr USBAudioStreamObject::ParseASInterfaceDescriptor (USBInterfaceDescriptorPtr theInterfacePtr, UInt8 const currentInterface) {
    UInt32								indx;
	UInt16								wFormatTag;
    Boolean								done;
    USBEndpointObject *					theEndpoint;

	debugIOLog ("+USBAudioStreamObject::ParseASInterfaceDescriptor (%x, %d)", theInterfacePtr, currentInterface);

    FailIf (NULL == theInterfacePtr, Exit);
    FailIf (0 == theInterfacePtr->bLength, Exit);
//    FailIf (CS_INTERFACE != theInterfacePtr->bDescriptorType, Exit);

    done = FALSE;
    while (theInterfacePtr->bLength && !done) {
        if (CS_INTERFACE == theInterfacePtr->bDescriptorType) {
            switch (theInterfacePtr->bDescriptorSubtype) {
                case AS_GENERAL:
                    debugIOLog ("in AS_GENERAL in ParseASInterfaceDescriptor");
                    terminalLink = ((ASInterfaceDescriptorPtr)theInterfacePtr)->bTerminalLink;
                    delay = ((ASInterfaceDescriptorPtr)theInterfacePtr)->bDelay;
                    formatTag = USBToHostWord ((((ASInterfaceDescriptorPtr)theInterfacePtr)->wFormatTag[1] << 8) | ((ASInterfaceDescriptorPtr)theInterfacePtr)->wFormatTag[0]);
                    theInterfacePtr = (USBInterfaceDescriptorPtr)((UInt8 *)theInterfacePtr + theInterfacePtr->bLength);
                    break;
                case FORMAT_TYPE:
                    debugIOLog ("in FORMAT_TYPE in ParseASInterfaceDescriptor");
					switch (((ASFormatTypeIDescriptorPtr)theInterfacePtr)->bFormatType) {
						case FORMAT_TYPE_I:
						case FORMAT_TYPE_III:
							debugIOLog ("in FORMAT_TYPE_I/FORMAT_TYPE_III in FORMAT_TYPE");
							numChannels = ((ASFormatTypeIDescriptorPtr)theInterfacePtr)->bNrChannels;
							subframeSize = ((ASFormatTypeIDescriptorPtr)theInterfacePtr)->bSubframeSize;
							bitResolution = ((ASFormatTypeIDescriptorPtr)theInterfacePtr)->bBitResolution;
							numSampleFreqs = ((ASFormatTypeIDescriptorPtr)theInterfacePtr)->bSamFreqType;
							if (0 != numSampleFreqs) {
								debugIOLog ("device has a discrete number of sample rates");
								sampleFreqs = (UInt32 *)IOMalloc (numSampleFreqs * sizeof (UInt32));
								for (indx = 0; indx < numSampleFreqs; indx++) {
									sampleFreqs[indx] = ConvertSampleFreq( &((ASFormatTypeIDescriptorPtr)theInterfacePtr)->sampleFreq[indx * 3] );
									/*((UInt8 *)sampleFreqs)[indx * 4] = 0;
									((UInt8 *)sampleFreqs)[indx * 4 + 1] = ((ASFormatTypeIDescriptorPtr)theInterfacePtr)->sampleFreq[indx * 3 + 2];
									((UInt8 *)sampleFreqs)[indx * 4 + 2] = ((ASFormatTypeIDescriptorPtr)theInterfacePtr)->sampleFreq[indx * 3 + 1];
									((UInt8 *)sampleFreqs)[indx * 4 + 3] = ((ASFormatTypeIDescriptorPtr)theInterfacePtr)->sampleFreq[indx * 3]; */
								}
							} else {
								debugIOLog ("device has a variable number of sample rates");
								sampleFreqs = (UInt32 *)IOMalloc (2 * sizeof (UInt32));
								for (indx = 0; indx < 2; indx++) {
									sampleFreqs[indx] = ConvertSampleFreq( &((ASFormatTypeIDescriptorPtr)theInterfacePtr)->sampleFreq[indx * 3] );
									/*((UInt8 *)sampleFreqs)[indx * 4] = 0;
									((UInt8 *)sampleFreqs)[indx * 4 + 1] = ((ASFormatTypeIDescriptorPtr)theInterfacePtr)->sampleFreq[indx * 3 + 2];
									((UInt8 *)sampleFreqs)[indx * 4 + 2] = ((ASFormatTypeIDescriptorPtr)theInterfacePtr)->sampleFreq[indx * 3 + 1];
									((UInt8 *)sampleFreqs)[indx * 4 + 3] = ((ASFormatTypeIDescriptorPtr)theInterfacePtr)->sampleFreq[indx * 3]; */
								}
							}
							break;
						case FORMAT_TYPE_II:
							debugIOLog ("in FORMAT_TYPE_II in FORMAT_TYPE");
							maxBitRate = USBToHostWord (((ASFormatTypeIIDescriptorPtr)theInterfacePtr)->wMaxBitRate);
							samplesPerFrame = USBToHostWord (((ASFormatTypeIIDescriptorPtr)theInterfacePtr)->wSamplesPerFrame);
							numSampleFreqs = ((ASFormatTypeIIDescriptorPtr)theInterfacePtr)->bSamFreqType;
							if (0 != numSampleFreqs) {
								debugIOLog ("device has a discrete number of sample rates");
								sampleFreqs = (UInt32 *)IOMalloc (numSampleFreqs * sizeof (UInt32));
								for (indx = 0; indx < numSampleFreqs; indx++) {
									sampleFreqs[indx] = ConvertSampleFreq( &((ASFormatTypeIIDescriptorPtr)theInterfacePtr)->sampleFreq[indx * 3] );
									/*((UInt8 *)sampleFreqs)[indx * 4] = 0;
									((UInt8 *)sampleFreqs)[indx * 4 + 1] = ((ASFormatTypeIIDescriptorPtr)theInterfacePtr)->sampleFreq[indx * 3 + 2];
									((UInt8 *)sampleFreqs)[indx * 4 + 2] = ((ASFormatTypeIIDescriptorPtr)theInterfacePtr)->sampleFreq[indx * 3 + 1];
									((UInt8 *)sampleFreqs)[indx * 4 + 3] = ((ASFormatTypeIIDescriptorPtr)theInterfacePtr)->sampleFreq[indx * 3]; */
								}
							} else {
								debugIOLog ("device has a variable number of sample rates");
								sampleFreqs = (UInt32 *)IOMalloc (2 * sizeof (UInt32));
								for (indx = 0; indx < 2; indx++) {
									sampleFreqs[indx] = ConvertSampleFreq( &((ASFormatTypeIIDescriptorPtr)theInterfacePtr)->sampleFreq[indx * 3] );
									/*((UInt8 *)sampleFreqs)[indx * 4] = 0;
									((UInt8 *)sampleFreqs)[indx * 4 + 1] = ((ASFormatTypeIIDescriptorPtr)theInterfacePtr)->sampleFreq[indx * 3 + 2];
									((UInt8 *)sampleFreqs)[indx * 4 + 2] = ((ASFormatTypeIIDescriptorPtr)theInterfacePtr)->sampleFreq[indx * 3 + 1];
									((UInt8 *)sampleFreqs)[indx * 4 + 3] = ((ASFormatTypeIIDescriptorPtr)theInterfacePtr)->sampleFreq[indx * 3]; */
								}
							}
							break;
						default:
							debugIOLog ("!!!!unknown format type in FORMAT_TYPE!!!!");
					}
                    theInterfacePtr = (USBInterfaceDescriptorPtr)((UInt8 *)theInterfacePtr + theInterfacePtr->bLength);
                    break;
				case FORMAT_SPECIFIC:
                    debugIOLog ("in FORMAT_SPECIFIC in ParseASInterfaceDescriptor");
					wFormatTag = USBToHostWord (((ASFormatSpecificDescriptorHeaderPtr)theInterfacePtr)->wFormatTag[1] << 8 | ((ASFormatSpecificDescriptorHeaderPtr)theInterfacePtr)->wFormatTag[0]);
					switch (wFormatTag) {
						case MPEG:
							debugIOLog ("in FORMAT_SPECIFIC in MPEG");
							bmMPEGCapabilities = USBToHostWord(
												((ASMPEGFormatSpecificDescriptorPtr)theInterfacePtr)->bmMPEGCapabilities[1] << 8 |
												((ASMPEGFormatSpecificDescriptorPtr)theInterfacePtr)->bmMPEGCapabilities[0]);
							bmMPEGFeatures = ((ASMPEGFormatSpecificDescriptorPtr)theInterfacePtr)->bmMPEGFeatures;
							break;
						case AC3:
							debugIOLog ("in FORMAT_SPECIFIC in AC3");
							bmAC3BSID = USBToHostLong(
										((ASAC3FormatSpecificDescriptorPtr)theInterfacePtr)->bmBSID[3] << 24 |
										((ASAC3FormatSpecificDescriptorPtr)theInterfacePtr)->bmBSID[2] << 16 |
										((ASAC3FormatSpecificDescriptorPtr)theInterfacePtr)->bmBSID[1] << 8 |
										((ASAC3FormatSpecificDescriptorPtr)theInterfacePtr)->bmBSID[0]);
							bmAC3Features = ((ASAC3FormatSpecificDescriptorPtr)theInterfacePtr)->bmAC3Features;
							break;
						default:
							debugIOLog ("!!!!unknown format type 0x%x in FORMAT_SPECIFIC!!!!", wFormatTag);
							break;
					}
                    theInterfacePtr = (USBInterfaceDescriptorPtr)((UInt8 *)theInterfacePtr + theInterfacePtr->bLength);
                    break;
                default:
                    debugIOLog ("in default in ParseASInterfaceDescriptor");
                    theInterfacePtr = (USBInterfaceDescriptorPtr)((UInt8 *)theInterfacePtr + theInterfacePtr->bLength);
            }
        } else {
            switch (theInterfacePtr->bDescriptorType) {
                case INTERFACE:
                    // need to make a new interface object for this new interface or new alternate interface
                    debugIOLog ("in INTERFACE in ParseASInterfaceDescriptor");
                    done = TRUE;
                    break;
                case ENDPOINT:
                    debugIOLog ("in ENDPOINT in ParseASInterfaceDescriptor");
                    theEndpoint = USBEndpointObject::create ();
					theEndpoint->SetAddress (((USBEndpointDescriptorPtr)theInterfacePtr)->bEndpointAddress);
                    theEndpoint->SetAttributes (((USBEndpointDescriptorPtr)theInterfacePtr)->bmAttributes);
                    theEndpoint->SetMaxPacketSize (USBToHostWord (((USBEndpointDescriptorPtr)theInterfacePtr)->wMaxPacketSize));
					theEndpoint->SetRefreshInt (((USBEndpointDescriptorPtr)theInterfacePtr)->bRefresh);
					theEndpoint->SetSynchAddress (((USBEndpointDescriptorPtr)theInterfacePtr)->bSynchAddress);

					if (NULL == theEndpointObjects) {
						theEndpointObjects = OSArray::withObjects ((const OSObject **)&theEndpoint, 1);
						FailIf (NULL == theEndpointObjects, Exit);
					} else {
						theEndpointObjects->setObject (theEndpoint);
					}

					theEndpoint->release ();
                    theInterfacePtr = (USBInterfaceDescriptorPtr)((UInt8 *)theInterfacePtr + theInterfacePtr->bLength);
                    break;
                case CS_ENDPOINT:
                    debugIOLog ("in CS_ENDPOINT in ParseASInterfaceDescriptor");
                    if (EP_GENERAL == ((ASEndpointDescriptorPtr)theInterfacePtr)->bDescriptorSubtype) {
                        debugIOLog ("in EP_GENERAL in CS_ENDPOINT");
                        theIsocEndpointObject = new USBCSASIsocADEndpointObject (((ASEndpointDescriptorPtr)theInterfacePtr)->bmAttributes & (1 << sampleFreqControlBit),
                                                                            ((ASEndpointDescriptorPtr)theInterfacePtr)->bmAttributes & (1 << pitchControlBit),
                                                                            ((ASEndpointDescriptorPtr)theInterfacePtr)->bmAttributes & (1 << maxPacketsOnlyBit),
                                                                            ((ASEndpointDescriptorPtr)theInterfacePtr)->bLockDelayUnits,
																			USBToHostWord (((UInt16 *)((ASEndpointDescriptorPtr)theInterfacePtr)->wLockDelay)[0]));
                    }
                    theInterfacePtr = (USBInterfaceDescriptorPtr)((UInt8 *)theInterfacePtr + theInterfacePtr->bLength);
                    break;
                default:
                    debugIOLog ("in default in else in ParseASInterfaceDescriptor");
                    theInterfacePtr = (USBInterfaceDescriptorPtr)((UInt8 *)theInterfacePtr + theInterfacePtr->bLength);
            }
        }
    }

Exit:
	debugIOLog ("-USBAudioStreamObject::ParseASInterfaceDescriptor (%x, %d)", theInterfacePtr, currentInterface);
    return theInterfacePtr;
}

/*
 	 Private methods
*/

USBEndpointObject * USBAudioStreamObject::GetEndpointByAddress (UInt8 address) {
    USBEndpointObject *		 			thisEndpoint;
    UInt8								indx;
    Boolean								found;

    indx = 0;
    found = FALSE;
	thisEndpoint = NULL;
	if (theEndpointObjects) {
		while (!found && indx < theEndpointObjects->getCount ()) {
			thisEndpoint = GetIndexedEndpointObject (indx);
			if (thisEndpoint && (thisEndpoint->GetAddress () == address)) {
				found = TRUE;
			}
			indx++;
		}
	}

	if (found != TRUE)
		thisEndpoint = NULL;

    return thisEndpoint;
}

USBEndpointObject * USBAudioStreamObject::GetEndpointObjectByAddress (UInt8 address) {
    USBEndpointObject *		 			thisEndpointObject;
    UInt8								indx;
    Boolean								found;

    indx = 0;
    found = FALSE;
	thisEndpointObject = NULL;
	if (theEndpointObjects) {
		while (!found && indx < theEndpointObjects->getCount ()) {
			thisEndpointObject = OSDynamicCast (USBEndpointObject, theEndpointObjects->getObject (indx));
			if (thisEndpointObject && address == thisEndpointObject->GetAddress ()) {
				found = TRUE;
			}
			indx++;
		}
	}

    if (found != TRUE)
		thisEndpointObject = NULL;

    return thisEndpointObject;
}

//UInt8 GetEndpointAttributes (void) {if (theEndpointObject) return theEndpointObject->GetAttributes (); else return 0;}
//UInt8 GetEndpointDirection (void) {if (theEndpointObject) return theEndpointObject->GetDirection (); else return 0;}

/* ------------------------------------------------------
    USBEndpointObject
------------------------------------------------------ */
OSDefineMetaClassAndStructors (USBEndpointObject, OSObject);

USBEndpointObject * USBEndpointObject::create (void) {
    USBEndpointObject *			endpointObject;

    endpointObject = new USBEndpointObject;

    FailIf (NULL == endpointObject, Exit);
    if (FALSE == endpointObject->init ()) {
        endpointObject->release ();
        endpointObject = 0;
    }

Exit:
    return endpointObject;
}

void USBEndpointObject::free (void) {
    OSObject::free ();
}

/* ------------------------------------------------------
    USBASIsocEndpointObject
------------------------------------------------------ */
USBCSASIsocADEndpointObject::USBCSASIsocADEndpointObject (Boolean theSampleFreqControl, Boolean thePitchControl, Boolean theMaxPacketsOnly, UInt8 theLockDelayUnits, UInt16 theLockDelay)
    :	sampleFreqControl				(theSampleFreqControl),
        pitchControl					(thePitchControl),
        maxPacketsOnly					(theMaxPacketsOnly),
        lockDelayUnits					(theLockDelayUnits),
        lockDelay						(theLockDelay)
{
}

/* ------------------------------------------------------
    USBInputTerminalObject
------------------------------------------------------ */
//OSDefineMetaClassAndStructors (USBInputTerminalObject, OSObject);

void USBInputTerminalObject::free (void) {
    USBACDescriptorObject::free ();
}

/* ------------------------------------------------------
    USBOutputTerminalObject
------------------------------------------------------ */
//OSDefineMetaClassAndStructors (USBOutputTerminalObject, OSObject);

void USBOutputTerminalObject::free (void) {
    USBACDescriptorObject::free ();
}

/* ------------------------------------------------------
    USBMixerUnitObject
------------------------------------------------------ */
void USBMixerUnitObject::free (void) {
    USBACDescriptorObject::free ();
}

void USBMixerUnitObject::InitControlsArray (UInt8 * bmCntrls, UInt8 bmControlSize) {
#if DEBUGLOGGING
	UInt8					i;
#endif

	controlSize = bmControlSize;
	bmControls = ((UInt8 *)IOMalloc (bmControlSize));
	FailIf (NULL == bmControls, Exit);
	memcpy (bmControls, bmCntrls, bmControlSize);
#if DEBUGLOGGING
	for (i = 0; i < bmControlSize; i++) {
		debugIOLog ("bmCntrls[%d] = 0x%X", i, bmCntrls[i]);
		debugIOLog ("bmControls[%d] = 0x%X", i, bmControls[i]);
	}
#endif
Exit:
	return;
}

void USBMixerUnitObject::InitSourceIDs (UInt8 * baSrcIDs, UInt8 nrInPins) {
	numInPins = nrInPins;
	baSourceIDs = (UInt8 *)IOMalloc (nrInPins);
	FailIf (NULL == baSourceIDs, Exit);
	memcpy (baSourceIDs, baSrcIDs, nrInPins);

#if DEBUGLOGGING
	{
		UInt8			i;
		for (i = 0; i < nrInPins; i++) {
			debugIOLog ("baSourceIDs[%d] = %d", i, baSourceIDs[i]);
		}
	}
#endif

Exit:
	return;
}

/* ------------------------------------------------------
    USBSelectorUnitObject
------------------------------------------------------ */
void USBSelectorUnitObject::free (void) {
    USBACDescriptorObject::free ();
}

void USBSelectorUnitObject::InitSourceIDs (UInt8 * baSrcIDs, UInt8 nrInPins) {
	numInPins = nrInPins;
	baSourceIDs = (UInt8 *)IOMalloc (nrInPins);
	FailIf (NULL == baSourceIDs, Exit);
	memcpy (baSourceIDs, baSrcIDs, nrInPins);

Exit:
	return;
}

/* ------------------------------------------------------
    USBProcessingUnitObject
------------------------------------------------------ */
void USBProcessingUnitObject::free (void) {
    USBACDescriptorObject::free ();
}

void USBProcessingUnitObject::InitSourceIDs (UInt8 * baSrcIDs, UInt8 nrInPins) {
	numInPins = nrInPins;
	baSourceIDs = (UInt8 *)IOMalloc (nrInPins);
	FailIf (NULL == baSourceIDs, Exit);
	memcpy (baSourceIDs, baSrcIDs, nrInPins);

Exit:
	return;
}

void USBProcessingUnitObject::InitControlsArray (UInt8 * bmCntrls, UInt8 bmControlSize) {
	UInt8					i;

	controlSize = bmControlSize;
	bmControls = ((UInt8 *)IOMalloc (bmControlSize));
	FailIf (NULL == bmControls, Exit);
	memcpy (bmControls, bmCntrls, bmControlSize);
	for (i = 0; i < bmControlSize; i++) {
		debugIOLog ("bmCntrls[%d] = 0x%X", i, bmCntrls[i]);
		debugIOLog ("bmControls[%d] = 0x%X", i, bmControls[i]);
	}
Exit:
	return;
}

/* ------------------------------------------------------
    USBFeatureUnitObject
------------------------------------------------------ */
//OSDefineMetaClassAndStructors (USBFeatureUnitObject, OSObject);

void USBFeatureUnitObject::free (void) {
    IOFree (bmaControls, numControls * controlSize);
    USBACDescriptorObject::free ();
}

Boolean USBFeatureUnitObject::MasterHasMuteControl (void) {
	return ChannelHasMuteControl (0);		// Master channel is always bmaControls[0]
}

// Channel #1 is left channel, #2 is right channel
Boolean USBFeatureUnitObject::ChannelHasMuteControl (UInt8 channelNum) {
	Boolean						result;

	result = FALSE;
	if (numControls >= channelNum + 1) {
		if (1 == controlSize) {
			result = ((UInt8 *)bmaControls)[channelNum] & (1 << kMuteBit);
		} else {
			result = ((UInt16 *)bmaControls)[channelNum] & (1 << kMuteBit);
		}
	}

	return result;
}

Boolean USBFeatureUnitObject::ChannelHasVolumeControl (UInt8 channelNum) {
	Boolean						result;

	result = FALSE;
	if (numControls >= channelNum + 1) {
		if (1 == controlSize) {
			result = ((UInt8 *)bmaControls)[channelNum] & (1 << kVolumeBit);
		} else {
			result = ((UInt16 *)bmaControls)[channelNum] & (1 << kVolumeBit);
		}
	}

	return result;
}

void USBFeatureUnitObject::InitControlsArray (UInt8 * bmaControlsArrary, UInt8 numCntrls) {
	UInt32						bmaControlIndex;

	numControls = numCntrls;
	bmaControls = (UInt8 *)IOMalloc (numControls * controlSize);
	FailIf (NULL == bmaControls, Exit);
	memcpy (bmaControls, bmaControlsArrary, numControls * controlSize);
	if (2 == controlSize) {
		for (bmaControlIndex = 0; bmaControlIndex < numControls; bmaControlIndex++) {
			((UInt16 *)bmaControls)[bmaControlIndex] = USBToHostWord (((UInt16 *)bmaControls)[bmaControlIndex]);
		}
	}

Exit:
	return;
}

/* ------------------------------------------------------
    USBExtensionUnitObject
------------------------------------------------------ */
void USBExtensionUnitObject::free (void) {
    USBACDescriptorObject::free ();
}

void USBExtensionUnitObject::InitControlsArray (UInt8 * bmCntrls, UInt8 bmControlSize) {
	UInt8					i;

	controlSize = bmControlSize;
	bmControls = ((UInt8 *)IOMalloc (bmControlSize));
	FailIf (NULL == bmControls, Exit);
	memcpy (bmControls, bmCntrls, bmControlSize);
	for (i = 0; i < bmControlSize; i++) {
		debugIOLog ("bmCntrls[%d] = 0x%X", i, bmCntrls[i]);
		debugIOLog ("bmControls[%d] = 0x%X", i, bmControls[i]);
	}
Exit:
	return;
}

void USBExtensionUnitObject::InitSourceIDs (UInt8 * baSrcIDs, UInt8 nrInPins) {
	numInPins = nrInPins;
	baSourceIDs = (UInt8 *)IOMalloc (nrInPins);
	FailIf (NULL == baSourceIDs, Exit);
	memcpy (baSourceIDs, baSrcIDs, nrInPins);

#if DEBUGLOGGING
	{
		UInt8			i;
		for (i = 0; i < nrInPins; i++) {
			debugIOLog ("baSourceIDs[%d] = %d", i, baSourceIDs[i]);
		}
	}
#endif

Exit:
	return;
}

/* ------------------------------------------------------
    USBACDescriptorObject
------------------------------------------------------ */
OSDefineMetaClassAndStructors (USBACDescriptorObject, OSObject);

void USBACDescriptorObject::free (void) {
    OSObject::free ();
}

