AppleUSBAudioDevice.cpp [plain text]
/*
 * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
 
//--------------------------------------------------------------------------------

//
//	File:		AppleUSBAudioDevice.cpp
//
//	Contains:	Support for the USB Audio Class Control Interface.
//			This includes support for exporting device controls
//			to the Audio HAL such as Volume, Bass, Treble and
//			Mute.
//
//			Future support will include parsing of the device
//			topology and exporting of all appropriate device
//			control functions through the Audio HAL.
//
//	Technology:	OS X
//
//--------------------------------------------------------------------------------

#include "AppleUSBAudioDevice.h"
#include "AppleUSBAudioDictionary.h"

#define super					IOAudioDevice
#define LOCALIZABLE				FALSE

OSDefineMetaClassAndStructors (AppleUSBAudioDevice, super)

void AppleUSBAudioDevice::free () {
    debugIOLog ("+ AppleUSBAudioDevice[%p]::free ()", this);

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

    if (mInterfaceLock) 
	{
        IORecursiveLockFree (mInterfaceLock);
        mInterfaceLock = NULL;
    }

	if (mConfigDictionary)
	{
        mConfigDictionary->release ();
        mConfigDictionary = NULL;
    }
	
	if (mRegisteredEngines) 
	{
		mRegisteredEngines->release ();
		mRegisteredEngines = NULL;
	}
	
	if (mMonoControlsArray)
	{
		mMonoControlsArray->release ();
		mMonoControlsArray = NULL;
	}

	if (mRetryEQDownloadThread)
	{
		thread_call_free (mRetryEQDownloadThread);
	}
    super::free ();
    debugIOLog ("- AppleUSBAudioDevice[%p]::free ()", this);
}

bool AppleUSBAudioDevice::ControlsStreamNumber (UInt8 streamNumber) {
	OSArray *						streamNumberArray = NULL;
	OSObject *						arrayObject = NULL;
	OSNumber *						arrayNumber = NULL;
	UInt8							numStreams;
	UInt8							streamIndex;
	bool							doesControl;

	doesControl = FALSE;

	if (mConfigDictionary)
	{
		FailIf (kIOReturnSuccess != mConfigDictionary->getControlledStreamNumbers (&streamNumberArray, &numStreams), Exit);
		for (streamIndex = 0; streamIndex < numStreams; streamIndex++) 
		{
			FailIf (NULL == (arrayObject = streamNumberArray->getObject (streamIndex)), Exit);
			FailIf (NULL == (arrayNumber = OSDynamicCast (OSNumber, arrayObject)), Exit);
			
			debugIOLog ("? AppleUSBAudioDevice[%p]::ControlsStreamNumber () - Checking stream %d against controled stream %d", this, streamNumber, arrayNumber->unsigned8BitValue());
			if (streamNumber == arrayNumber->unsigned8BitValue()) 
			{
				doesControl = TRUE;
				break;				// get out of for loop
			}
		}
	}
		
Exit:	
	return doesControl;
}

bool AppleUSBAudioDevice::start (IOService * provider) {
	bool								result;

    debugIOLog ("+ AppleUSBAudioDevice[%p]::start (%p)", this, provider);
	result = FALSE;

	mControlInterface = OSDynamicCast (IOUSBInterface, provider);
	FailIf (FALSE == mControlInterface->open (this), Exit);

	mInitHardwareThread = thread_call_allocate ((thread_call_func_t)AppleUSBAudioDevice::initHardwareThread, (thread_call_param_t)this);
	FailIf (NULL == mInitHardwareThread, Exit);

	result = super::start (provider);				// Causes our initHardware routine to be called.

Exit:
	debugIOLog ("- AppleUSBAudioDevice[%p]::start (%p) = result", this, provider, result);

	return result;
}

bool AppleUSBAudioDevice::initHardware (IOService * provider) {
	bool								result;

	result = FALSE;

	FailIf (NULL == mInitHardwareThread, Exit);
	thread_call_enter1 (mInitHardwareThread, (void *)provider);

	result = TRUE;

Exit:
	return result;
}

void AppleUSBAudioDevice::initHardwareThread (AppleUSBAudioDevice * aua, void * provider) {
	IOCommandGate *						cg;
	IOReturn							result;

	FailIf (NULL == aua, Exit);
//	FailIf (TRUE == aua->mTerminating, Exit);	

	cg = aua->getCommandGate ();
	if (cg) 
	{
		result = cg->runAction (aua->initHardwareThreadAction, provider);
	}

Exit:
	return;
}

IOReturn AppleUSBAudioDevice::initHardwareThreadAction (OSObject * owner, void * provider, void * arg2, void * arg3, void * arg4) {
	AppleUSBAudioDevice *				aua;
	IOReturn							result;

	result = kIOReturnError;

	aua = (AppleUSBAudioDevice *)owner;
	FailIf (NULL == aua, Exit);

	result = aua->protectedInitHardware ((IOService *)provider);

Exit:
	return result;
}

IOReturn AppleUSBAudioDevice::protectedInitHardware (IOService * provider) {
	char							string[kStringBufferSize];
	UInt8							stringIndex;
	IOReturn						err;
    Boolean							resultCode;
	OSArray *						streamNumberArray;
	UInt8							numStreamInterfaces;
	UInt8							numStreams;

	debugIOLog ("+ AppleUSBAudioDevice[%p]::start (%p)", this, provider);

	resultCode = FALSE;
	FailIf (NULL == mControlInterface, Exit);

	debugIOLog ("? AppleUSBAudioDevice[%p]::protectedInitHardware () - %d configuration(s) on this device. This control interface number is %d", this, mControlInterface->GetDevice()->GetNumConfigurations (), mControlInterface->GetInterfaceNumber ());
		
	debugIOLog ("? AppleUSBAudioDevice[%p]::protectedInitHardware () - Attempting to create configuration dictionary...", this);
	mConfigDictionary = AUAConfigurationDictionary::create (mControlInterface->GetDevice()->GetFullConfigurationDescriptor (0), mControlInterface->GetInterfaceNumber());
	FailIf (NULL == mConfigDictionary, Exit);
	debugIOLog ("? AppleUSBAudioDevice[%p]::protectedInitHardware () - Successfully created configuration dictionary.", this);

	mInterfaceLock = IORecursiveLockAlloc ();
	FailIf (NULL == mInterfaceLock, Exit);

	mControlGraph = BuildConnectionGraph (mControlInterface->GetInterfaceNumber ());
	FailIf (NULL == mControlGraph, Exit);
	
	// [rdar://4801032]
	if ( IP_VERSION_02_00 == mControlInterface->GetInterfaceProtocol() )
	{
		FailIf ( NULL == ( mClockGraph = buildClockGraph ( mControlInterface->GetInterfaceNumber () ) ), Exit );
		// From this moment forward, we may assume that a device is attempting to be USB 2.0 audio class-compliant by the presence of mClockGraph
		
		// Since supported sample rates are no longer listed explicitly in the USB 2.0 audio specification, we must discover them through device
		// request inquiries.
		
		FailIf ( kIOReturnSuccess != addSampleRatesFromClockSpace (), Exit );
		
	}

	// Check to make sure that the control interface we loaded against has audio streaming interfaces and not just MIDI.
	FailIf (kIOReturnSuccess != mConfigDictionary->getControlledStreamNumbers (&streamNumberArray, &numStreams), Exit);
	FailIf (kIOReturnSuccess != mConfigDictionary->getNumStreamInterfaces (&numStreamInterfaces), Exit);
	debugIOLog ("? AppleUSBAudioDevice[%p]::protectedInitHardware () - %d controlled stream(s). %d stream interface(s).", this, numStreams, numStreamInterfaces);
	FailIf (0 == numStreams, Exit);

	// If this is an iSub, we need to not go any further because we don't support it in this driver
	// This will cause the driver to not load on any device that has _only_ a low frequency effect output terminal
	// [rdar://5064139] Only perform this check if the control interface is 0.
	if ( 0 == mControlInterface->GetInterfaceNumber() )
	{
		FailIf (mConfigDictionary->isiSub(), Exit);
	}
	
	err = kIOReturnError;
	string[0] = 0;
	stringIndex = mControlInterface->GetInterfaceStringIndex ();
	if (0 != stringIndex) 
	{
		err = mControlInterface->GetDevice()->GetStringDescriptor (stringIndex, string, kStringBufferSize);
		// [rdar://3886272] - This could fail on some devices, so we should retry
		if (kIOReturnSuccess != err)
		{
			// Try a regular retry once
			debugIOLog ("! AppleUSBAudioDevice[%p]::protectedInitHardware () - couldn't get string descriptor. Retrying ...", this);
			err = mControlInterface->GetDevice()->GetStringDescriptor (stringIndex, string, kStringBufferSize);
		}
		if (kIOReturnSuccess != err)
		{
			// Reset the device and try one last time
			debugIOLog ("! AppleUSBAudioDevice[%p]::protectedInitHardware () - Still couldn't get string descriptor. Resetting device ...", this);
			mControlInterface->GetDevice()->ResetDevice();      // Doesn't matter if this fails
			IOSleep (50);										// Give the device 50 ms to get ready
			debugIOLog ("! AppleUSBAudioDevice[%p]::protectedInitHardware - Last retry ...", this);
			err = mControlInterface->GetDevice()->GetStringDescriptor (stringIndex, string, kStringBufferSize);
		}
	} 
	else 
	{
		stringIndex = mControlInterface->GetDevice()->GetProductStringIndex ();
		if (0 != stringIndex) 
		{
			err = mControlInterface->GetDevice()->GetStringDescriptor (stringIndex, string, kStringBufferSize);
			// rdar://3886272 - This could fail on some devices, so we should retry
			if (kIOReturnSuccess != err)
			{
				// Try a regular retry once
				debugIOLog ("! AppleUSBAudioDevice[%p]::protectedInitHardware () - couldn't get string descriptor. Retrying ...", this);
				err = mControlInterface->GetDevice()->GetStringDescriptor (stringIndex, string, kStringBufferSize);
			}
			if (kIOReturnSuccess != err)
			{
				// Reset the device and try one last time
				debugIOLog ("! AppleUSBAudioDevice[%p]::protectedInitHardware () - Still couldn't get string descriptor. Resetting device ...", this);
				mControlInterface->GetDevice()->ResetDevice();      // Doesn't matter if this fails
				IOSleep (50);										// Give the device 50 ms to get ready
				debugIOLog ("! AppleUSBAudioDevice[%p]::protectedInitHardware () - Last retry ...", this);
				err = mControlInterface->GetDevice()->GetStringDescriptor (stringIndex, string, kStringBufferSize);
			}
		}
		else
		{
			// [rdar://5159683] This device may not have either string, so read it from the IOUSBDevice.
			OSObject *	nameObject = NULL;
			OSString *	nameString = NULL;
			debugIOLog ( "! AppleUSBAudioDevice[%p]::protectedInitHardware () - Trying to retrieve the product name from the IOUSBDevice ... ", this );
			nameObject = mControlInterface->GetDevice ()->getProperty ( "USB Product Name" );
			if ( nameObject )
			{
				if ( NULL != ( nameString = OSDynamicCast ( OSString, nameObject ) ) )
				{
					debugIOLog ( "! AppleUSBAudioDevice[%p]::protectedInitHardware () - Retrieved product name %s", this, nameString->getCStringNoCopy () );
					strcpy ( string, nameString->getCStringNoCopy() );
					err = kIOReturnSuccess;
				} 
			}
		}
	}

	if (0 == string[0] || kIOReturnSuccess != err) 
	{
		strcpy (string, "Unknown USB Audio Device");
	}

	setDeviceName (string);

	err = kIOReturnError;
	string[0] = 0;
	stringIndex = mControlInterface->GetDevice()->GetManufacturerStringIndex ();
	if (0 != stringIndex) 
	{
		err = mControlInterface->GetDevice()->GetStringDescriptor (stringIndex, string, kStringBufferSize);
	}

	if (0 == string[0] || kIOReturnSuccess != err) 
	{
		strcpy (string, "Unknown Manufacturer");
	}

	setManufacturerName (string);
	setDeviceTransportType (kIOAudioDeviceTransportTypeUSB);
	#if LOCALIZABLE
	setProperty (kIOAudioDeviceLocalizedBundleKey, "AppleUSBAudio.kext");
	#endif

	resultCode = super::initHardware (provider);
	
	// Start the anchored time stamp timer if necessary
	
	// Initialize time stamp member variables
	mLastUSBFrame = 0ull;
	mLastWallTime_nanos = 0ull;
	mAnchorResetCount = kRefreshCount;
	mNewReferenceUSBFrame = 0ull;
	
	// Initialize mWallTimePerUSBCycle
	resetRateTimer();
	
	// [rdar://] This member variable keeps track an engine that has had a catastrophic failure that requires an emergency format change.
	mFailingAudioEngine = NULL;

	// Register and start update timer
	mUpdateTimer = IOTimerEventSource::timerEventSource (this, TimerAction);
	FailIf (NULL == mUpdateTimer, Exit);
	workLoop->addEventSource (mUpdateTimer);
	debugIOLog ("? AppleUSBAudioDevice[%p]::protectedInitHardware () - starting rate timer");
	TimerAction ( this, mUpdateTimer);
	
	// Added for rdar://3993906 . This forces matchPropertyTable () to run again.
	IOService::registerService();

Exit:
	debugIOLog ("- AppleUSBAudioDevice[%p]::start (%p)", this, provider);

	return TRUE;
}

IOReturn AppleUSBAudioDevice::performPowerStateChange (IOAudioDevicePowerState oldPowerState, IOAudioDevicePowerState newPowerState, UInt32 *microSecsUntilComplete) {
	IOReturn						result;
	
	// debugIOLog ("+ AppleUSBAudioDevice[%p]::performPowerStateChange (%d, %d, %p)", this, oldPowerState, newPowerState, microSecsUntilComplete);

	result = super::performPowerStateChange (oldPowerState, newPowerState, microSecsUntilComplete);

	// We need to stop the time stamp rate timer now
	if	(		(mUpdateTimer)
			&&	(kIOAudioDeviceSleep == newPowerState))
	{
		// Stop the timer and reset the anchor.
		debugIOLog ("? AppleUSBAudioDevice[%p]::performPowerStateChange () - Going to sleep - stopping the rate timer.", this);
		mUpdateTimer->cancelTimeout ();
		
		// The frame/time correlation isn't preserved across sleep/wake
		mNewReferenceUSBFrame = 0ull;		
		mLastUSBFrame = 0ull;
		( * (UInt64 *) &mNewReferenceWallTime) = 0ull;
		mLastWallTime_nanos = 0ull;
	}
	
	if (oldPowerState == kIOAudioDeviceSleep) 
	{
		// A new anchor should be taken at the first opportunity. The timer action will handle this with the following instruction.
		mAnchorResetCount = kRefreshCount;
		
		// [rdar://4380545] We need to reset the wall time per USB cycle because the frame number could become invalid entering sleep.
		resetRateTimer();
		
		#if RESETAFTERSLEEP
		// [rdar://4234453] Reset the device after waking from sleep just to be safe.
		FailIf (NULL == mControlInterface, Exit);
		debugIOLog ("? AppleUSBAudioDevice[%p]::performPowerStateChange () - Resetting port after wake from sleep ...", this);
		mControlInterface->GetDevice()->ResetDevice();
		IOSleep (10);
		#endif
		
		// We need to restart the time stamp rate timer now
		debugIOLog ("? AppleUSBAudioDevice[%p]::performPowerStateChange () - Waking from sleep - restarting the rate timer.", this);
		TimerAction ( this, mUpdateTimer);
		
		debugIOLog ("? AppleUSBAudioDevice[%p]::performPowerStateChange () - Flushing controls to the device ...", this);
		flushAudioControls ();
	}

Exit:
	// debugIOLog ("- AppleUSBAudioDevice[%p]::performPowerStateChange (%d, %d, %p)", this, oldPowerState, newPowerState, microSecsUntilComplete);
	return result;
}

void AppleUSBAudioDevice::stop (IOService *provider) {

	debugIOLog ("+ AppleUSBAudioDevice[%p]::stop (%p) - audioEngines = %p - rc=%d", this, provider, audioEngines, getRetainCount());

	if (mUpdateTimer)
	{
		// Stop the rate calculation timer
		debugIOLog ("? AppleUSBAudioDevice[%p]::stop () - Cancelling time stamp rate timer ...", this);
		mUpdateTimer->cancelTimeout ();
		mUpdateTimer->disable();
	}
	
	super::stop (provider);  // call the IOAudioDevice generic stop routine

    if (mControlInterface) 
	{
        mControlInterface->close (this);
        mControlInterface = NULL;
    }
	
	if (mInitHardwareThread)
	{
		thread_call_cancel (mInitHardwareThread);
		thread_call_free (mInitHardwareThread);
		mInitHardwareThread = NULL;
	}

	debugIOLog("- AppleUSBAudioDevice[%p]::stop ()", this);
}

// Return FALSE if you don't want PRAM updated on a volume change, TRUE if you want it updated.
// Only update PRAM if we're on a Cube and the speakers are Cube, SoundSticks, or Mirconas (somethings).
Boolean AppleUSBAudioDevice::ShouldUpdatePRAM (void) {
	const IORegistryPlane *			usbPlane;
	IORegistryEntry *				usbRegEntry;
	OSObject *						obj;
	OSNumber *						number;
	UInt16							productID;
	UInt16							vendorID;
	Boolean							speakersGood;
	Boolean							connectionGood;
	Boolean							result;

	// Assume failure
	result = FALSE;
	speakersGood = FALSE;
	connectionGood = FALSE;
	FailIf (NULL == mControlInterface, Exit);

	// Make sure they're speakers that can support boot beep
	vendorID = mControlInterface->GetDevice()->GetVendorID ();
	debugIOLog ("? AppleUSBAudioDevice[%p]::ShouldUpdatePRAM () - speaker's vendorID = 0x%x", this, vendorID);
	if (kIOUSBVendorIDAppleComputer == vendorID || kIOUSBVendorIDHarmonKardon == vendorID || kIOUSBVendorMicronas == vendorID) 
	{
		speakersGood = TRUE;
	}
	// debugIOLog ("speakersGood = %d", speakersGood);

	// They have to be plugged into a root hub or a hub in monitor that can support boot beep
	if (TRUE == speakersGood) 
	{
		usbPlane = getPlane (kIOUSBPlane);
		FailIf (NULL == usbPlane, Exit);

		usbRegEntry = mControlInterface->GetDevice()->getParentEntry (usbPlane);
		FailIf (NULL == usbRegEntry, Exit);

		obj = usbRegEntry->getProperty (kUSBVendorID);
		number = OSDynamicCast (OSNumber, obj);
		FailIf (NULL == number, Exit);

		vendorID = number->unsigned32BitValue ();
		// debugIOLog ("hub's vendorID = 0x%x", vendorID);

		if (kIOUSBVendorIDAppleComputer == vendorID) 
		{
			obj = usbRegEntry->getProperty (kUSBDevicePropertyLocationID);
			number = OSDynamicCast (OSNumber, obj);
			FailIf (NULL == number, Exit);

			if (OSDynamicCast (IOUSBRootHubDevice, usbRegEntry)) 
			{
				// It's connected to the root hub
				connectionGood = TRUE;
				debugIOLog ("? AppleUSBAudioDevice[%p]::ShouldUpdatePRAM () - Directly connected to the root hub", this);
			} 
			else 
			{
				obj = usbRegEntry->getProperty (kUSBProductID);
				number = OSDynamicCast (OSNumber, obj);
				FailIf (NULL == number, Exit);

				productID = number->unsigned32BitValue ();
				// debugIOLog ("hub's productID = 0x%x", productID);

				if (kStudioDisplay15CRT == productID || kStudioDisplay17CRT == productID || kCinemaDisplay == productID || kStudioDisplay17FP == productID) 
				{
					// It's connected to a good monitor
					connectionGood = TRUE;
					// debugIOLog ("Connected to a capable monitor");
				}
			}
		}
	}
	// debugIOLog ("connectionGood = %d", connectionGood);

	// And there CANNOT be a "sound" node in the device tree so that OF will boot beep through them
	if (TRUE == connectionGood && FALSE == FindSoundNode ()) 
	{
		result = TRUE;
	}

Exit:
	debugIOLog ("? AppleUSBAudioDevice[%p]::ShouldUpdatePRAM () - result = %d", this, result);
	return result;
}

Boolean AppleUSBAudioDevice::FindSoundNode (void) {
	const IORegistryPlane *			dtPlane;
	IORegistryEntry *				regEntry;
	IORegistryIterator *			iterator;
	Boolean							found;
	Boolean							done;
	const char *					name;

	found = FALSE;

	dtPlane = IORegistryEntry::getPlane (kIODeviceTreePlane);
	FailIf (NULL == dtPlane, Exit);

	iterator = IORegistryIterator::iterateOver (dtPlane, kIORegistryIterateRecursively);
	FailIf (NULL == iterator, Exit);

	done = FALSE;
	regEntry = iterator->getNextObject ();
	while (NULL != regEntry && FALSE == done) 
	{
		name = regEntry->getName ();
		if (0 == strcmp (name, "mac-io")) 
		{
			// This is where we want to start the search
			iterator->release ();		// release the current iterator and make a new one rooted at "mac-io"
			iterator = IORegistryIterator::iterateOver (regEntry, dtPlane);
			done = TRUE;
		}
		regEntry = iterator->getNextObject ();
	}

	// Now the real search begins...
	regEntry = iterator->getNextObject ();
	while (NULL != regEntry && FALSE == found) 
	{
		name = regEntry->getName ();
		if (0 == strcmp (name, "sound")) 
		{
			found = TRUE;
		}
		regEntry = iterator->getNextObject ();
	}

	iterator->release ();

Exit:
	return found;
}

IOReturn AppleUSBAudioDevice::message (UInt32 type, IOService * provider, void * arg) {
	AppleUSBAudioEngine	*	currentEngine = NULL;
	OSDictionary *			currentEngineInfo = NULL;
	
	debugIOLog ("+ AppleUSBAudioDevice[%p]::message (0x%x, %p) - rc=%d", this, type, provider, getRetainCount ());

	switch (type) 
	{
		case kIOMessageServiceIsTerminated:
		case kIOMessageServiceIsRequestingClose:
			if (mControlInterface != NULL && mControlInterface == provider) 
			{
				mControlInterface->close (this);
				mControlInterface = NULL;
			}
			break;
		case kIOUSBMessagePortHasBeenReset:
			// If the device has been reset, we must take steps to ensure that streaming can be resumed.
			// Flush controls to the device in case the device reset changed their states.
			debugIOLog ("? AppleUSBAudioDevice[%p]::message () - Flushing controls to the device.", this);
			flushAudioControls ();
			FailIf (NULL == mRegisteredEngines, Exit);
			debugIOLog ("? AppleUSBAudioDevice[%p]::message () - Resetting engines.", this);
			for (UInt8 engineIndex = 0; engineIndex < mRegisteredEngines->getCount (); engineIndex++)
			{
				currentEngineInfo = OSDynamicCast (OSDictionary, mRegisteredEngines->getObject (engineIndex));
				FailIf (NULL == currentEngineInfo, Exit);
				currentEngine = OSDynamicCast (AppleUSBAudioEngine, currentEngineInfo->getObject (kEngine));
				FailIf (NULL == currentEngine, Exit);
				
				debugIOLog ("? AppleUSBAudioDevice[%p]::message () - Resetting engine %p...", this, currentEngine);
				currentEngine->pauseAudioEngine ();
				// Close the stream interface just to be safe.
				if (currentEngine->mStreamInterface)
				{
					currentEngine->mStreamInterface->close (currentEngine);
				}
				// Reopen the stream interface
				{
					currentEngine->mStreamInterface->open (currentEngine);
				}
				currentEngine->resumeAudioEngine ();
			}
			break;
		default:
			;
	}
Exit:
	debugIOLog ("- AppleUSBAudioDevice[%p]::message (0x%x, %p) - rc=%d", this, type, provider, getRetainCount ());
	return kIOReturnSuccess;
}

// added for rdar://4168019 . Returns the device speed (high, full, or low).
UInt8 AppleUSBAudioDevice::getDeviceSpeed () {
	IOUSBDevice *			usbDevice;
	UInt8					speed = 0;
	
	FailIf (NULL == mControlInterface, Exit);
	usbDevice = OSDynamicCast (IOUSBDevice, mControlInterface->GetDevice());
	speed = usbDevice->GetSpeed ();
	#if DEBUGLOGGING
	switch (speed)
	{
		case kUSBDeviceSpeedLow:
			debugIOLog ("? AppleUSBAudioDevice[%p]::getDeviceSpeed () = kUSBDeviceSpeedLow", this);
			break;
		case kUSBDeviceSpeedFull:
			debugIOLog ("? AppleUSBAudioDevice[%p]::getDeviceSpeed () = kUSBDeviceSpeedFull", this);
			break;
		case kUSBDeviceSpeedHigh:
			debugIOLog ("? AppleUSBAudioDevice[%p]::getDeviceSpeed () = kUSBDeviceSpeedHigh", this);
			break;
		default:
			debugIOLog ("? AppleUSBAudioDevice[%p]::getDeviceSpeed () = %d (UNKNOWN)", this, speed);
	}
	#endif
	
Exit:
	return speed;
}

// added for rdar://3959606 . Detects whether this is a full speed device plugged into a high speed hub.
bool AppleUSBAudioDevice::detectSplitTransactions () {
	IOUSBDevice *			usbDevice;
	const IORegistryPlane *	usbPlane = getPlane (kIOUSBPlane);
	IORegistryEntry *		currentEntry;
	UInt8					speed;
	bool					canStop = false;
	bool					splitTransactions = false;
	
	FailIf (NULL == mControlInterface, Exit);
	usbDevice = OSDynamicCast (IOUSBDevice, mControlInterface->GetDevice());
	FailIf (NULL == usbDevice, Exit);
	currentEntry = OSDynamicCast (IORegistryEntry, usbDevice);
	
	if (kUSBDeviceSpeedHigh == usbDevice->GetSpeed ())
	{
		debugIOLog ("? AppleUSBAudioDevice::detectSplitTransactions () - This is a high speed device, so there are no split transactions.");
		splitTransactions = false;
		canStop = true;
	}
	
	while	(		(!canStop)
				&&	(currentEntry)
				&&	(usbDevice))
	{
		// Searching for a high speed hub. Since it is not possible to run at high speed with a full speed
		// hub closer to the root hub in the chain, we can stop when we find the first high speed hub.
		
		speed = usbDevice->GetSpeed ();
		if (kUSBDeviceSpeedHigh == speed)
		{
			// Must be connected via USB 2.0 hub
			debugIOLog ("? AppleUSBAudioDevice::detectSplitTransactions () = true");
			splitTransactions = true;
			canStop = true;
		}
		else
		{
			// Get parent in USB plane
			currentEntry = OSDynamicCast (IORegistryEntry, currentEntry->getParentEntry (usbPlane));
			
			// If the current registry entry is not a device, this will make usbDevice NULL and exit the loop
			usbDevice = OSDynamicCast (IOUSBDevice, currentEntry);
		}
	} // end while
	
Exit:
	return splitTransactions;
}

Boolean AppleUSBAudioDevice::checkForUHCI () {
	Boolean					result = FALSE;
	IOUSBDevice *			usbDevice;
	const IORegistryPlane *	servicePlane = getPlane (kIOServicePlane);
	IOService *				currentEntry;
	IORegistryEntry *		parentEntry;
	char					serviceName[20];

	FailIf (NULL == mControlInterface, Exit);
	usbDevice = OSDynamicCast (IOUSBDevice, mControlInterface->GetDevice());
	FailIf (NULL == usbDevice, Exit);
	parentEntry = usbDevice->getParentEntry (servicePlane);
	FailIf (NULL == parentEntry, Exit);
	currentEntry = OSDynamicCast (IOService, parentEntry);
	FailIf (NULL == currentEntry, Exit);
	strcpy (serviceName, currentEntry->getName (servicePlane));

	while (    (currentEntry)
			&& strcmp (serviceName, "AppleUSBUHCI")
			&& strcmp (serviceName, "AppleUSBOHCI")
			&& strcmp (serviceName, "AppleUSBEHCI"))
	{
		// Searching for the root hub type. We only expect one of three types, so we can stop when we have found any of them.
		// The types are AppleUSBUHCI, AppleUSBOHCI, and AppleUSBEHCI.
		
		// Get parent in IOService plane
		parentEntry = currentEntry->getParentEntry (servicePlane);
		FailIf (NULL == parentEntry, Exit);
		currentEntry = OSDynamicCast (IOService, parentEntry);
		if (currentEntry)
		{
			strcpy (serviceName, currentEntry->getName (servicePlane));
		}
	} // end while
	FailIf (NULL == currentEntry, Exit);
	if (!strcmp (serviceName, "AppleUSBUHCI"))
	{
		// We are on a UHCI connection
		debugIOLog ("? AppleUSBAudioDevice::checkForUHCI () - UHCI connection detected!\n");
		result = TRUE;
	}
Exit:
	return result;
}

SInt32 AppleUSBAudioDevice::getEngineInfoIndex (AppleUSBAudioEngine * inAudioEngine) {
	OSDictionary *						engineInfo;
	AppleUSBAudioEngine *				usbAudioEngine;
	UInt16								engineIndex;
	SInt32								returnIndex;

	returnIndex = -1;
	if (mRegisteredEngines) 
	{
		for (engineIndex = 0; engineIndex < mRegisteredEngines->getCount (); engineIndex++) 
		{
			engineInfo = OSDynamicCast (OSDictionary, mRegisteredEngines->getObject (engineIndex));
			if (engineInfo) 
			{
				usbAudioEngine = OSDynamicCast (AppleUSBAudioEngine, engineInfo->getObject (kEngine));
				if (inAudioEngine == usbAudioEngine) 
				{
					returnIndex = engineIndex;
					break;		// Get out of for loop with correct index
				}
			}
		}
	}

	return returnIndex;
}

IOReturn AppleUSBAudioDevice::doControlStuff (IOAudioEngine *audioEngine, UInt8 interfaceNum, UInt8 altSettingNum) {
	AppleUSBAudioEngine *				usbAudioEngine;
	IOAudioSelectorControl *			inputSelector;
	OSArray *							arrayOfPathsFromOutputTerminal;
	OSArray *							aPath;
	OSArray *							playThroughPaths;
	OSNumber *							theUnitIDNum;
	OSNumber *							number;
	OSDictionary *						engineInfo;
	IOReturn							result;
	UInt32								numUnitsInPath, unitIndexInPath /*, pathIndex */;
	UInt32								outputTerminalIndex;
	UInt32								numOutputTerminalArrays;
	UInt32								numPathsFromOutputTerminal;
	UInt32								pathsToOutputTerminalN;
	UInt32								selection;
	SInt32								engineIndex;
	SInt32								oldEngineIndex;
	UInt16								terminalType;
	UInt8								numSelectorUnits;
	UInt8								subType;
	UInt8								numOutputTerminals;
	UInt8								selectorUnitID;
	UInt8								featureUnitID;
	UInt8								controlInterfaceNum;
	UInt8								unitID;
	UInt8								outputTerminalID;
	Boolean								done;
	// [rdar://5292769] Don't publish mute controls if the path is playthrough capable
	bool								playthroughCapable;

	debugIOLog ("+ AppleUSBAudioDevice::doControlStuff(0x%x, %d, %d)", audioEngine, interfaceNum, altSettingNum);
	result = kIOReturnError;
	inputSelector = NULL;
	done = FALSE;

    usbAudioEngine = OSDynamicCast (AppleUSBAudioEngine, audioEngine);
    FailIf (NULL == usbAudioEngine, Exit);
	FailIf (NULL == mControlInterface, Exit);
	debugIOLog ("? AppleUSBAudioDevice::doControlStuff () - This usbAudioEngine = %p", usbAudioEngine);

	if (NULL == mRegisteredEngines) 
	{
		mRegisteredEngines = OSArray::withCapacity (1);
		FailIf (NULL == mRegisteredEngines, Exit);
	}

	engineInfo = OSDictionary::withCapacity (1);
	FailIf (NULL == engineInfo, Exit);
	engineInfo->setObject (kEngine, usbAudioEngine);
	number = OSNumber::withNumber (interfaceNum, 8);
	engineInfo->setObject (kInterface, number);
	number->release ();
	number = OSNumber::withNumber (altSettingNum, 8);
	engineInfo->setObject (kAltSetting, number);
	number->release ();
	
	// [rdar://4102789] Be sure to preserve the integrity of mRegisteredEngines. It is vital to perform emergency format changes.
	oldEngineIndex = getEngineInfoIndex (usbAudioEngine);
	if (-1 != oldEngineIndex)
	{
		// This engine already has information stored. We should replace it.
		mRegisteredEngines->replaceObject (oldEngineIndex, engineInfo);
	}
	else
	{
		mRegisteredEngines->setObject (engineInfo);
	}
	engineInfo->release ();

	featureUnitID = 0;
	selectorUnitID = 0;
	outputTerminalID = 0;
	controlInterfaceNum = mControlInterface->GetInterfaceNumber ();

	FailIf (kIOReturnSuccess != mConfigDictionary->getNumOutputTerminals (&numOutputTerminals, controlInterfaceNum, 0), Exit);

	if (usbAudioEngine->getDirection () == kIOAudioStreamDirectionOutput) 
	{
		for (outputTerminalIndex = 0; outputTerminalIndex < numOutputTerminals && FALSE == done; outputTerminalIndex++) 
		{
			FailIf (kIOReturnSuccess != mConfigDictionary->getIndexedOutputTerminalType (&terminalType, controlInterfaceNum, 0, outputTerminalIndex), Exit);
			if (terminalType != 0x101) 
			{
				
				FailIf (kIOReturnSuccess != mConfigDictionary->getIndexedOutputTerminalID (&outputTerminalID, controlInterfaceNum, 0, outputTerminalIndex), Exit);
				numOutputTerminalArrays = mControlGraph->getCount ();
				for (pathsToOutputTerminalN = 0; pathsToOutputTerminalN < numOutputTerminalArrays; pathsToOutputTerminalN++) 
				{
					arrayOfPathsFromOutputTerminal = OSDynamicCast (OSArray, mControlGraph->getObject (pathsToOutputTerminalN));
					FailIf (NULL == arrayOfPathsFromOutputTerminal, Exit);
					aPath = OSDynamicCast (OSArray, arrayOfPathsFromOutputTerminal->getObject (0));
					FailIf (NULL == aPath, Exit);
					theUnitIDNum = OSDynamicCast (OSNumber, aPath->getObject (0));
					FailIf (NULL == theUnitIDNum, Exit);
					unitID = theUnitIDNum->unsigned8BitValue ();
		
					if (unitID == outputTerminalID) 
					{
						featureUnitID = getBestFeatureUnitInPath (aPath, kIOAudioControlUsageOutput, interfaceNum, altSettingNum, kVolumeControl);
						if (featureUnitID) 
						{
							// Create the output gain controls
							debugIOLog("? AppleUSBAudioDevice::doControlStuff () - Creating output gain controls");
							addVolumeControls (usbAudioEngine, featureUnitID, interfaceNum, altSettingNum, kIOAudioControlUsageOutput);
							featureUnitID = 0;
						}
						featureUnitID = getBestFeatureUnitInPath (aPath, kIOAudioControlUsageOutput, interfaceNum, altSettingNum, kMuteControl);
						if (featureUnitID) 
						{
							debugIOLog("? AppleUSBAudioDevice::doControlStuff () - Creating output mute controls");
							addMuteControl (usbAudioEngine, featureUnitID, interfaceNum, altSettingNum, kIOAudioControlUsageOutput);
							featureUnitID = 0;
							done = TRUE;
						}
					}
				} // for pathsToOutputTerminalN
			} // if (unitID == outputTerminalID
		} // for outputTerminalIndex
	} 
	else 
	{		// direction == kIOAudioStreamDirectionInput
		for (outputTerminalIndex = 0; outputTerminalIndex < numOutputTerminals; outputTerminalIndex++) 
		{
			FailIf (kIOReturnSuccess != mConfigDictionary->getIndexedOutputTerminalType (&terminalType, controlInterfaceNum, 0, outputTerminalIndex), Exit);
			if (0x101 == terminalType) 
			{ 
				FailIf (kIOReturnSuccess != mConfigDictionary->getIndexedOutputTerminalID (&outputTerminalID, controlInterfaceNum, 0, outputTerminalIndex), Exit);
				break;		// Found the (hopefully only) streaming output terminal we're looking for
			}
		}

		numOutputTerminalArrays = mControlGraph->getCount ();
		for (pathsToOutputTerminalN = 0; pathsToOutputTerminalN < numOutputTerminalArrays; pathsToOutputTerminalN++) 
		{
			playthroughCapable = false;
			arrayOfPathsFromOutputTerminal = OSDynamicCast (OSArray, mControlGraph->getObject (pathsToOutputTerminalN));
			FailIf (NULL == arrayOfPathsFromOutputTerminal, Exit);
			aPath = OSDynamicCast (OSArray, arrayOfPathsFromOutputTerminal->getObject (0));
			FailIf (NULL == aPath, Exit);
			theUnitIDNum = OSDynamicCast (OSNumber, aPath->getObject (0));
			FailIf (NULL == theUnitIDNum, Exit);
			unitID = theUnitIDNum->unsigned8BitValue ();

			if (unitID == outputTerminalID) 
			{
				// Check for a playthrough path that would require a playthrough control
				theUnitIDNum = OSDynamicCast (OSNumber, aPath->getLastObject ());
				FailIf (NULL == theUnitIDNum, Exit);
				unitID = theUnitIDNum->unsigned8BitValue ();
				playThroughPaths = getPlaythroughPaths ();
				if (playThroughPaths) 
				{
					// [rdar://5292769] Don't publish mute controls if the path is playthrough capable
					playthroughCapable = true;
					debugIOLog("? AppleUSBAudioDevice::doControlStuff () - performing playthrough setup");
					doPlaythroughSetup (usbAudioEngine, playThroughPaths, interfaceNum, altSettingNum);
					playThroughPaths->release ();
				}

				numPathsFromOutputTerminal = arrayOfPathsFromOutputTerminal->getCount ();
				FailIf (kIOReturnSuccess != mConfigDictionary->getNumSelectorUnits(&numSelectorUnits, controlInterfaceNum, 0), Exit);
				if (numPathsFromOutputTerminal > 1 && numSelectorUnits) 
				{
					// Found the array of paths that lead to our streaming output terminal
					numUnitsInPath = aPath->getCount ();
					for (unitIndexInPath = 1; unitIndexInPath < numUnitsInPath; unitIndexInPath++) 
					{
						theUnitIDNum = OSDynamicCast (OSNumber, aPath->getObject (unitIndexInPath));
						FailIf (NULL == theUnitIDNum, Exit);
						unitID = theUnitIDNum->unsigned8BitValue ();
						FailIf (kIOReturnSuccess != mConfigDictionary->getSubType (&subType, controlInterfaceNum, 0, unitID), Exit);
						if (SELECTOR_UNIT == subType) 
						{
							if (kIOReturnSuccess == setSelectorSetting (unitID, 1)) 
							{
								selectorUnitID = unitID;
								engineIndex = getEngineInfoIndex (usbAudioEngine);
								if (-1 != engineIndex) 
								{
									selection = (0xFF000000 & (pathsToOutputTerminalN << 24)) | (0x00FF0000 & (0 << 16)) | (0x0000FF00 & (selectorUnitID << 8)) | 1;
									inputSelector = IOAudioSelectorControl::createInputSelector (selection, kIOAudioControlChannelIDAll, 0, engineIndex);
									FailIf (NULL == inputSelector, Exit);
									inputSelector->setValueChangeHandler (controlChangedHandler, this);
									usbAudioEngine->addDefaultAudioControl (inputSelector);
									featureUnitID = getBestFeatureUnitInPath (aPath, kIOAudioControlUsageInput, interfaceNum, altSettingNum, kVolumeControl);
									if (featureUnitID) 
									{
										// Create the input gain controls
										debugIOLog("? AppleUSBAudioDevice::doControlStuff () - Creating input gain controls");
										addVolumeControls (usbAudioEngine, featureUnitID, interfaceNum, altSettingNum, kIOAudioControlUsageInput);
										featureUnitID = 0;
									}
									featureUnitID = getBestFeatureUnitInPath (aPath, kIOAudioControlUsageInput, interfaceNum, altSettingNum, kMuteControl);
									if (featureUnitID) 
									{
										debugIOLog("? AppleUSBAudioDevice::doControlStuff () - Creating input mute controls");
										addMuteControl (usbAudioEngine, featureUnitID, interfaceNum, altSettingNum, kIOAudioControlUsageInput);
										featureUnitID = 0;
									}
								} // if (-1 != engineIndex)
							} // if (kIOReturnSuccess == setSelectorSetting (unitID, 1))
							break;		// Get out of unitIndexInPath for loop
						} // if (SELECTOR_UNIT == subType)
					} // for unitIndexInPath

					if (NULL != inputSelector) 
					{
						addSelectorSourcesToSelectorControl (inputSelector, arrayOfPathsFromOutputTerminal, pathsToOutputTerminalN, unitIndexInPath);
						inputSelector->release ();
					} 
					else 
					{
						// There are no programmable selectors, so just find the one feature unit, if it exists.
						featureUnitID = getBestFeatureUnitInPath (aPath, kIOAudioControlUsageInput, interfaceNum, altSettingNum, kVolumeControl);
						if (featureUnitID) 
						{
							// Create the playthrough volume controls
							debugIOLog("? AppleUSBAudioDevice::doControlStuff () - Creating playthrough volume controls");
							addVolumeControls (usbAudioEngine, featureUnitID, interfaceNum, altSettingNum, kIOAudioControlUsageInput);
							featureUnitID = 0;
						}
					}
				} // if (numPathsFromOutputTerminal > 1 && numSelectorUnits)
				else 
				{
					// There are no selectors, so just find the one feature unit, if it exists.
					featureUnitID = getBestFeatureUnitInPath (aPath, kIOAudioControlUsageInput, interfaceNum, altSettingNum, kVolumeControl);
					if (featureUnitID) 
					{
						// Create the playthrough volume controls
						debugIOLog ( "? AppleUSBAudioDevice::doControlStuff () - Creating input volume control (no selectors)" );
						addVolumeControls (usbAudioEngine, featureUnitID, interfaceNum, altSettingNum, kIOAudioControlUsageInput);
						featureUnitID = 0;
					}
					if ( false == playthroughCapable )
					{
						featureUnitID = getBestFeatureUnitInPath (aPath, kIOAudioControlUsageInput, interfaceNum, altSettingNum, kMuteControl);
						if (featureUnitID) 
						{
							// Create the mute controls
							debugIOLog ( "? AppleUSBAudioDevice::doControlStuff () - Creating input mute control (no selectors)" );
							addMuteControl (usbAudioEngine, featureUnitID, interfaceNum, altSettingNum, kIOAudioControlUsageInput);
							featureUnitID = 0;
						}
					}
				}
				break;		// Get out of pathsToOutputTerminalN for loop
			}
		}
	}

	result = kIOReturnSuccess;

Exit:
	debugIOLog ("- AppleUSBAudioDevice::doControlStuff(0x%x, %d, %d)", audioEngine, interfaceNum, altSettingNum);
	return result;
}

IOReturn AppleUSBAudioDevice::doPlaythroughSetup (AppleUSBAudioEngine * usbAudioEngine, OSArray * playThroughPaths, UInt8 interfaceNum, UInt8 altSettingNum) {
	OSArray *							aPath;
	OSNumber *							theUnitIDNum;
	OSString *							nameString;
	IOAudioSelectorControl *			playThroughSelector;
	OSDictionary *						engineInfo;
	UInt32								numPlayThroughPaths;
	UInt32								pathIndex;
	SInt32								engineInfoIndex;
	UInt16								terminalType;
	UInt8								numControls;
	UInt8								channelNum;
	UInt8								featureUnitID;
	UInt8								inputTerminalID;
	UInt8								controlInterfaceNum;
	IOReturn							result;

	result = kIOReturnError;
	FailIf (NULL == mControlInterface, Exit);

	controlInterfaceNum = mControlInterface->GetInterfaceNumber ();

	engineInfoIndex = getEngineInfoIndex (usbAudioEngine);
	FailIf (-1 == engineInfoIndex, Exit);

	engineInfo = OSDynamicCast (OSDictionary, mRegisteredEngines->getObject (engineInfoIndex));
	FailIf (NULL == engineInfo, Exit);
	engineInfo->setObject (kPassThruPathsArray, playThroughPaths);

	numPlayThroughPaths = playThroughPaths->getCount ();
	if (numPlayThroughPaths > 1) 
	{
		// Create a virtual selector to manipulate the mutes on the feature units to toggle through playthrough sources.
		playThroughSelector = IOAudioSelectorControl::create (0, kIOAudioControlChannelIDAll, 0, engineInfoIndex, kIOAudioSelectorControlSubTypeInput, kIOAudioControlUsagePassThru);
		FailIf (NULL == playThroughSelector, Exit);
		playThroughSelector->setValueChangeHandler (controlChangedHandler, this);
		usbAudioEngine->addDefaultAudioControl (playThroughSelector);

		for (pathIndex = 0; pathIndex < numPlayThroughPaths; pathIndex++) 
		{
			aPath = OSDynamicCast (OSArray, playThroughPaths->getObject (pathIndex));
			FailIf (NULL == aPath, Exit);
			featureUnitID = getBestFeatureUnitInPath (aPath, kIOAudioControlUsageInput, interfaceNum, altSettingNum, kVolumeControl);
			if (featureUnitID) 
			{
				theUnitIDNum = OSDynamicCast (OSNumber, aPath->getLastObject ());
				FailIf (NULL == theUnitIDNum, Exit);
				inputTerminalID = theUnitIDNum->unsigned8BitValue ();
				FailIf (kIOReturnSuccess != mConfigDictionary->getInputTerminalType (&terminalType, controlInterfaceNum, 0, inputTerminalID), Exit);
				nameString = OSString::withCString (TerminalTypeString (terminalType));
				FailIf (NULL == nameString, Exit);
				playThroughSelector->addAvailableSelection (pathIndex, nameString);
				nameString->release ();
				FailIf (kIOReturnSuccess != mConfigDictionary->getNumControls (&numControls, controlInterfaceNum, 0, featureUnitID), Exit);
				for (channelNum = 0; channelNum < numControls; channelNum++) 
				{
					setCurVolume (featureUnitID, channelNum, 0);
					setCurMute (featureUnitID, channelNum, 0);
				}
			}
		}
		aPath = OSDynamicCast (OSArray, playThroughPaths->getObject (0));
		FailIf (NULL == aPath, Exit);
		featureUnitID = getBestFeatureUnitInPath (aPath, kIOAudioControlUsageInput, interfaceNum, altSettingNum, kVolumeControl);
		if (featureUnitID) 
		{
			// Create the playthrough volume controls
			addVolumeControls (usbAudioEngine, featureUnitID, interfaceNum, altSettingNum, kIOAudioControlUsagePassThru);
		}
		featureUnitID = getBestFeatureUnitInPath (aPath, kIOAudioControlUsageInput, interfaceNum, altSettingNum, kMuteControl);
		if (featureUnitID) 
		{
			addMuteControl (usbAudioEngine, featureUnitID, interfaceNum, altSettingNum, kIOAudioControlUsagePassThru);
		}
		result = kIOReturnSuccess;
	} // if (numPlayThroughPaths > 1)
	else 
	{
		// Only one playthrough path, so just publish its volume and mute controls.
		aPath = OSDynamicCast (OSArray, playThroughPaths->getObject (0));
		FailIf (NULL == aPath, Exit);
		featureUnitID = getBestFeatureUnitInPath (aPath, kIOAudioControlUsagePassThru, interfaceNum, altSettingNum, kVolumeControl);
		if (featureUnitID) 
		{
			// Create the playthrough volume controls
			addVolumeControls (usbAudioEngine, featureUnitID, interfaceNum, altSettingNum, kIOAudioControlUsagePassThru);
		}
		featureUnitID = getBestFeatureUnitInPath (aPath, kIOAudioControlUsagePassThru, interfaceNum, altSettingNum, kMuteControl);
		if (featureUnitID) 
		{
			addMuteControl (usbAudioEngine, featureUnitID, interfaceNum, altSettingNum, kIOAudioControlUsagePassThru);
		}
		result = kIOReturnSuccess;
	}

Exit:
	return result;
}

IOReturn AppleUSBAudioDevice::addSelectorSourcesToSelectorControl (IOAudioSelectorControl * theSelectorControl, OSArray * arrayOfPathsFromOutputTerminal, UInt32 pathsToOutputTerminalN, UInt8 selectorIndex) {
	OSArray *							aPath;
	OSNumber *							theUnitIDNum;
	OSString *							nameString;
	UInt32								selectorSourceIndex;
	UInt32								pathIndex;
	UInt32								selection;
	UInt8								numSelectorSources;
	UInt8								selectorID;
	UInt8								controlInterfaceNum;

	FailIf (NULL == mControlInterface, Exit);
	controlInterfaceNum = mControlInterface->GetInterfaceNumber ();

	aPath = OSDynamicCast (OSArray, arrayOfPathsFromOutputTerminal->getObject (0));
	FailIf (NULL == aPath, Exit);
	theUnitIDNum = OSDynamicCast (OSNumber, aPath->getObject (selectorIndex));
	FailIf (NULL == theUnitIDNum, Exit);
	selectorID = theUnitIDNum->unsigned8BitValue ();

	pathIndex = 0;
	FailIf (kIOReturnSuccess != mConfigDictionary->getNumSources (&numSelectorSources, controlInterfaceNum, 0, selectorID), Exit);
	for (selectorSourceIndex = 0; selectorSourceIndex < numSelectorSources; selectorSourceIndex++) 
	{
		nameString = getNameForPath (arrayOfPathsFromOutputTerminal, &pathIndex, selectorIndex + 1);
		if (NULL != nameString) 
		{
			selection = (0xFF000000 & (pathsToOutputTerminalN << 24)) | (0x00FF0000 & ((pathIndex - 1) << 16)) | (0x0000FF00 & (selectorID << 8)) | (0x000000FF & (selectorSourceIndex + 1));
			theSelectorControl->addAvailableSelection (selection, nameString);
			nameString->release ();
		}
	}

Exit:
	return kIOReturnSuccess;
}

// Starting point is the array index of the element after the selector unit.
OSString * AppleUSBAudioDevice::getNameForPath (OSArray * arrayOfPathsFromOutputTerminal, UInt32 * pathIndex, UInt8 startingPoint) {
	OSString *							theString;
	OSString *							tempString;
	OSArray *							aPath;
	OSNumber *							theUnitIDNum;
	UInt32								numElementsInPath;
	UInt32								elementIndex;
	UInt16								terminalType;
	UInt8								unitID;
	UInt8								subType;
	UInt8								controlInterfaceNum;
	Boolean								done;

	done = FALSE;
	theString = NULL;
	FailIf (NULL == mControlInterface, Exit);

	aPath = OSDynamicCast (OSArray, arrayOfPathsFromOutputTerminal->getObject (*pathIndex));
	FailIf (NULL == aPath, Exit);

	numElementsInPath = aPath->getCount ();
	controlInterfaceNum = mControlInterface->GetInterfaceNumber ();
	for (elementIndex = startingPoint; elementIndex < numElementsInPath && FALSE == done; elementIndex++) 
	{
		theUnitIDNum = OSDynamicCast (OSNumber, aPath->getObject (elementIndex));
		FailIf (NULL == theUnitIDNum, Exit);
		unitID = theUnitIDNum->unsigned8BitValue ();
		FailIf (kIOReturnSuccess != mConfigDictionary->getSubType (&subType, controlInterfaceNum, 0, unitID), Exit);
		switch (subType) 
		{
			case INPUT_TERMINAL:
				FailIf (kIOReturnSuccess != mConfigDictionary->getInputTerminalType (&terminalType, controlInterfaceNum, 0, unitID), Exit);
				tempString = OSString::withCString (TerminalTypeString (terminalType));
				FailIf (NULL == tempString, Exit);
				if (!tempString->isEqualTo ("USB streaming")) 
				{
					theString = OSString::withString (tempString);
				}
				tempString->release ();
				(*pathIndex)++;
				break;
			case MIXER_UNIT:
				theString = getNameForMixerPath (arrayOfPathsFromOutputTerminal, pathIndex, elementIndex);
				done = TRUE;
				break;
		}
	}

Exit:
	return theString;
}

// Starting point is the array index of the mixer unit.
OSString * AppleUSBAudioDevice::getNameForMixerPath (OSArray * arrayOfPathsFromOutputTerminal, UInt32 * pathIndex, UInt8 startingPoint) {
	char								string[255];
	OSString *							theString;
	OSString *							tempString;
	OSArray *							aPath;
	OSNumber *							theUnitIDNum;
	UInt32								numElementsInPath;
	UInt32								mixerSourceIndex;
	UInt32								elementIndex;
	UInt8								numMixerSources;
	UInt8								unitID;
	UInt8								subType;
	UInt8								controlInterfaceNum;

	string[0] = 0;
	FailIf (NULL == mControlInterface, Exit);

	aPath = OSDynamicCast (OSArray, arrayOfPathsFromOutputTerminal->getObject (*pathIndex));
	FailIf (NULL == aPath, Exit);
	theUnitIDNum = OSDynamicCast (OSNumber, aPath->getObject (startingPoint));
	FailIf (NULL == theUnitIDNum, Exit);
	unitID = theUnitIDNum->unsigned8BitValue ();

	numElementsInPath = aPath->getCount ();
	controlInterfaceNum = mControlInterface->GetInterfaceNumber ();
	FailIf (kIOReturnSuccess != mConfigDictionary->getNumSources (&numMixerSources, controlInterfaceNum, 0, unitID), Exit);
	for (mixerSourceIndex = *pathIndex; mixerSourceIndex < *pathIndex + numMixerSources; /* mixerSourceIndex incremented elsewhere */) 
	{
		for (elementIndex = startingPoint + 1; elementIndex < numElementsInPath; elementIndex++) 
		{
			theUnitIDNum = OSDynamicCast (OSNumber, aPath->getObject (elementIndex));
			FailIf (NULL == theUnitIDNum, Exit);
			unitID = theUnitIDNum->unsigned8BitValue ();
			FailIf (kIOReturnSuccess != mConfigDictionary->getSubType (&subType, controlInterfaceNum, 0, unitID), Exit);
			switch (subType) 
			{
				case INPUT_TERMINAL:
					tempString = getNameForPath (arrayOfPathsFromOutputTerminal, &mixerSourceIndex, elementIndex);
					if (NULL != tempString) 
					{
						strcat (string, tempString->getCStringNoCopy ());
						strcat (string, " & ");
						tempString->release ();
					}
					break;
				case MIXER_UNIT:
					tempString = getNameForMixerPath (arrayOfPathsFromOutputTerminal, &mixerSourceIndex, elementIndex);
					if (NULL != tempString) 
					{
						strcat (string, tempString->getCStringNoCopy ());
						tempString->release ();
					}
					break;
			}
		}
	}
	*pathIndex = mixerSourceIndex;

	if (strlen (string) > 3) 
	{
		string[strlen (string) - 3] = 0;
	}

Exit:
	theString = OSString::withCString (string);
	return theString;
}

void AppleUSBAudioDevice::addVolumeControls (AppleUSBAudioEngine * usbAudioEngine, UInt8 featureUnitID, UInt8 interfaceNum, UInt8 altSettingNum, UInt32 usage) {
	OSArray *							inputGainControlsArray;
	OSArray *							passThruVolControlsArray;
	OSArray *							outputVolControlsArray;
	OSDictionary *						engineInfo;
	IOAudioLevelControl *				theLevelControl;
	IOFixed								deviceMinDB;
	IOFixed								deviceMaxDB;
	SInt32								engineInfoIndex;
	SInt16								deviceCur;
	SInt16								deviceMin;
	SInt16								deviceMax;
	UInt16								volRes;
	SInt32								controlCur;
	SInt32								controlMax;
	SInt32								controlMin;
	UInt8								channelNum;
	UInt8								controlInterfaceNum;
	UInt8								numControls;
	bool								extraStep = false;

	debugIOLog ("+ AppleUSBAudioDevice::addVolumeControls (0x%x, %d, %d, %d, %u)", usbAudioEngine, featureUnitID, interfaceNum, altSettingNum, usage);
	engineInfoIndex = getEngineInfoIndex (usbAudioEngine);
	FailIf (-1 == engineInfoIndex, Exit);
	FailIf (NULL == mControlInterface, Exit);

	engineInfo = OSDynamicCast (OSDictionary, mRegisteredEngines->getObject (engineInfoIndex));
	FailIf (NULL == engineInfo, Exit);
	inputGainControlsArray = NULL;
	passThruVolControlsArray = NULL;
	outputVolControlsArray = NULL;
	
	// remove mono controls array if adding volume controls for output
	if (    (kIOAudioControlUsageOutput == usage)
	     && (NULL != mMonoControlsArray))
	{
		mMonoControlsArray->release ();
		mMonoControlsArray = NULL;
	}

	controlInterfaceNum = mControlInterface->GetInterfaceNumber ();
	FailIf (kIOReturnSuccess != mConfigDictionary->getNumControls (&numControls, controlInterfaceNum, 0, featureUnitID), Exit);
	for (channelNum = 0; channelNum <= numControls; channelNum++) 
	{
		extraStep = false;
		if (mConfigDictionary->channelHasVolumeControl (controlInterfaceNum, 0, featureUnitID, channelNum))
		{
			debugIOLog ("? AppleUSBAudioDevice[%p]::addVolumeControls () - Creating volume controls for channel %d", this, channelNum);
			FailIf (kIOReturnSuccess != getCurVolume (featureUnitID, channelNum, &deviceCur), Error);
			debugIOLog ("? AppleUSBAudioDevice[%p]::addVolumeControls () - deviceCur = 0x%04x", this, deviceCur);
			FailIf (kIOReturnSuccess != getMinVolume (featureUnitID, channelNum, &deviceMin), Error);
			debugIOLog ("? AppleUSBAudioDevice[%p]::addVolumeControls () - deviceMin = 0x%04x", this, deviceMin);
			FailIf (kIOReturnSuccess != getMaxVolume (featureUnitID, channelNum, &deviceMax), Error);
			debugIOLog ("? AppleUSBAudioDevice[%p]::addVolumeControls () - deviceMax = 0x%04x", this, deviceMax);
			getVolumeResolution (featureUnitID, channelNum, &volRes);
			debugIOLog ("? AppleUSBAudioDevice[%p]::addVolumeControls () - volRes = 0x%04x", this, volRes);
			// [rdar://4511427] Need to check volRes for class compliance (Audio Spec 5.2.2.4.3.2).
			FailIf (0 == volRes, Error);
			
			// [rdar://4228556] Unless the current volume is negative infinity, we should try to flush the volume out to the device.
			if ( ( SInt16 ) kNegativeInfinity != deviceCur )
			{
				// [rdar://5292769] If deviceCur lies outside the accepted range, flush out the deviceMin value.
				if (		( deviceCur < deviceMin )
						||	( deviceCur > deviceMax ) )
				{
					debugIOLog ( "! AppleUSBAudioDevice::addVolumeControls () - deviceCur is not in volume range! Setting to deviceMin ..." );
					deviceCur = deviceMin;
				}
				debugIOLog ("? AppleUSBAudioDevice[%p]::addVolumeControls () - Attempting to set volume to current volume ...", this);
				FailIf (kIOReturnSuccess != setCurVolume (featureUnitID, channelNum, HostToUSBWord(deviceCur)), Error);
				debugIOLog ("? AppleUSBAudioDevice[%p]::addVolumeControls () - Volume was set successfully.", this);
			}

			if ( (SInt16) kNegativeInfinity == deviceMin ) 
			{
				debugIOLog ( "! AppleUSBAudioDevice[%p]::addVolumeControls () - device violates USB 2.0 audio spec Section 5.2.5.7.2! Setting deviceMin to 0x8001 ..." );
				deviceMin = (SInt16) 0x8001;
				// Even though the firmware on the device is incorrect, we need to account for this loss of a control step by adding to the maximum later.
				extraStep = true;
				
			}
			deviceMinDB = ConvertUSBVolumeTodB (deviceMin);
			deviceMaxDB = ConvertUSBVolumeTodB (deviceMax);

			// [rdar://5118683] Create volume controls with correct ranges.
			
			controlMin = 0;
			// Also need to add an extra step here if the device reported negative infinity as the min value.
			controlMax = ( ( deviceMax - deviceMin ) / volRes ) + ( extraStep ? 1 : 0 );
			// The current control value is a bit of a special case because the device may violate the spec. The rules are as follows:
			// * If the current value is negative infinity, use a control value of 0.
			// * If the current value is the minimum value, use a control value of 0.
			// * If the current value is the maximum value, use the maximum control value (may be altered by an extra step if the device breaks spec).
			// * Otherwise calculate the current value as normal.
			if (		( (SInt16) kNegativeInfinity == deviceCur )
					||	( deviceCur == deviceMin ) )
			{
				controlCur = 0;
			}
			else if ( deviceCur == deviceMax )
			{
				controlCur = controlMax;
			}
			else
			{
				// If this device breaks spec, we don't care if this value matches with what the control published.
				controlCur = ( ( deviceCur - deviceMin ) / volRes );
			}
			
			debugIOLog ("? AppleUSBAudioDevice[%p]::addVolumeControls () - Creating control... [%d : %d : %d]", this, controlMin, controlCur, controlMax);
			theLevelControl = IOAudioLevelControl::createVolumeControl (controlCur, controlMin, controlMax, deviceMinDB, deviceMaxDB, channelNum, 0, featureUnitID, usage);
			FailIf (NULL == theLevelControl, Exit);
			debugIOLog ("? AppleUSBAudioDevice[%p]::addVolumeControls () - Created control %p", this, theLevelControl);
			theLevelControl->setValueChangeHandler (controlChangedHandler, this);
			usbAudioEngine->addDefaultAudioControl (theLevelControl);
			switch (usage) 
			{
				case kIOAudioControlUsageInput:
					if (NULL == inputGainControlsArray) 
					{
						inputGainControlsArray = OSArray::withObjects ((const OSObject **)&theLevelControl, 1);
					} 
					else 
					{
						inputGainControlsArray->setObject (theLevelControl);
					}
					break;
				case kIOAudioControlUsagePassThru:
					if (NULL == passThruVolControlsArray) 
					{
						passThruVolControlsArray = OSArray::withObjects ((const OSObject **)&theLevelControl, 1);
					} 
					else 
					{
						passThruVolControlsArray->setObject (theLevelControl);
					}
					break;
				case kIOAudioControlUsageOutput:
					if (NULL == outputVolControlsArray) 
					{
						outputVolControlsArray = OSArray::withObjects ((const OSObject **)&theLevelControl, 1);
					} 
					else 
					{
						outputVolControlsArray->setObject (theLevelControl);
					}
					
					// add channel number to mono output controls array if necessary
					if (mDeviceIsInMonoMode)
					{
						OSNumber *number = OSNumber::withNumber (channelNum, 8);
						if (NULL == mMonoControlsArray)
						{
							mMonoControlsArray = OSArray::withObjects ((const OSObject **) &number, 1);
						}
						else
						{
							mMonoControlsArray->setObject (number);
						}
						debugIOLog ("? AppleUSBAudioDevice[%p]::addVolumeControls () - Added channel %d to mono controls array", this, channelNum);
						number->release ();
					}
					
					break;
				default:
					debugIOLog ("! AppleUSBAudioDevice[%p]::addVolumeControls () - Control %p has an unknown usage!", this, theLevelControl);
			}
			theLevelControl->release ();
		} 
		else 
		{
			debugIOLog ("? AppleUSBAudioDevice[%p]::addVolumeControls () - Channel %d has no volume controls; skipping ...", this, channelNum);
		}
		goto NoError;
Error: 
		debugIOLog ("! AppleUSBAudioDevice[%p]::addVolumeControls () - Error creating controls for channel %d!", this, channelNum);
NoError:
		debugIOLog ("? AppleUSBAudioDevice[%p]::addVolumeControls () - Done with channel %d", this, channelNum);
	}

	if (NULL != inputGainControlsArray) 
	{
		engineInfo->setObject (kInputGainControls, inputGainControlsArray);
		inputGainControlsArray->release ();
	}
	if (NULL != passThruVolControlsArray) 
	{
		engineInfo->setObject (kPassThruVolControls, passThruVolControlsArray);
		passThruVolControlsArray->release ();
	}
	if (NULL != outputVolControlsArray) 
	{
		engineInfo->setObject (kOutputVolControls, outputVolControlsArray);
		outputVolControlsArray->release ();
	}

Exit:
	debugIOLog ("- AppleUSBAudioDevice::addVolumeControls (0x%x, %d, %d, %d, %u)", usbAudioEngine, featureUnitID, interfaceNum, altSettingNum, usage);
	return;
}

void AppleUSBAudioDevice::addMuteControl (AppleUSBAudioEngine * usbAudioEngine, UInt8 featureUnitID, UInt8 interfaceNum, UInt8 altSettingNum, UInt32 usage) 
{
	OSArray *							inputMuteControlsArray;
	OSDictionary *						engineInfo;
	IOAudioToggleControl *				theMuteControl;
	SInt32								engineInfoIndex;
	SInt16								deviceCur;
	UInt8								channelNum;
	UInt8								controlInterfaceNum;
	UInt8								numControls;
	IOReturn							resultCode;

	debugIOLog ( "+ AppleUSBAudioDevice[%p]::addMuteControl ( %p, %d, %d, %d, 0%x )", this, usbAudioEngine, featureUnitID, interfaceNum, altSettingNum, usage );
	engineInfoIndex = getEngineInfoIndex ( usbAudioEngine );
	FailIf ( -1 == engineInfoIndex, Exit );
	FailIf ( NULL == mControlInterface, Exit );

	engineInfo = OSDynamicCast ( OSDictionary, mRegisteredEngines->getObject ( engineInfoIndex ) );
	FailIf ( NULL == engineInfo, Exit );
	inputMuteControlsArray = NULL;

	controlInterfaceNum = mControlInterface->GetInterfaceNumber ();
	FailIf ( kIOReturnSuccess != mConfigDictionary->getNumControls ( &numControls, controlInterfaceNum, 0, featureUnitID ), Exit );
	for ( channelNum = 0; channelNum <= numControls; channelNum++ ) 
	{
		if ( mConfigDictionary->channelHasMuteControl ( controlInterfaceNum, 0, featureUnitID, channelNum ) )
		{
			resultCode = getCurMute ( featureUnitID, channelNum, &deviceCur );
			debugIOLog ( "? AppleUSBAudioDevice::addMuteControl () - channel %d, feature unit %d has current mute value 0x%x", channelNum, featureUnitID, deviceCur );
			// [rdar://5292769] Force unmute the device since the only easy way to unmute it afterwards is Audio MIDI Setup
			if (		( 0 != deviceCur )
					&&	( kIOAudioControlUsageInput == usage ) )
			{
				debugIOLog ("! AppleUSBAudioDevice::addMuteControl () - forcing channel %d of this device to unmute in hardware.", channelNum );
				deviceCur = 0x0;
			} 
			theMuteControl = IOAudioToggleControl::createMuteControl ( deviceCur, channelNum, 0, featureUnitID, usage );
			FailIf ( NULL == theMuteControl, Exit );
			setCurMute ( featureUnitID, channelNum, HostToUSBWord( deviceCur ) );
			theMuteControl->setValueChangeHandler ( controlChangedHandler, this );
			usbAudioEngine->addDefaultAudioControl ( theMuteControl );
			switch (usage) 
			{
				case kIOAudioControlUsageInput:
					if ( NULL == inputMuteControlsArray ) 
					{
						inputMuteControlsArray = OSArray::withObjects ( ( const OSObject ** ) &theMuteControl, 1 );
					} 
					else 
					{
						inputMuteControlsArray->setObject ( theMuteControl );
					}
					break;
				case kIOAudioControlUsagePassThru:
					break;
				case kIOAudioControlUsageOutput:
					break;
			}
			theMuteControl->release ();
		}
	}

	if ( NULL != inputMuteControlsArray ) 
	{
		engineInfo->setObject ( kInputMuteControls, inputMuteControlsArray );
		inputMuteControlsArray->release ();
	}

Exit:
	debugIOLog ( "- AppleUSBAudioDevice[%p]::addMuteControl ( %p, %d, %d, %d, 0x%x )", this, usbAudioEngine, featureUnitID, interfaceNum, altSettingNum, usage );
	return;
}

// This is how the thing is defined in the USB Audio spec (section 5.2.2.4.3.2 for the curious).
// The volume setting of a device is described in 1/256 dB increments using a number that goes from
// a max of 0x7fff (127.9961 dB) down to 0x8001 (-127.9961 dB) using standard signed math, but 0x8000
// is actually negative infinity (not -128 dB), so I have to special case it.
IOFixed AppleUSBAudioDevice::ConvertUSBVolumeTodB (SInt16 volume) {
	IOFixed							dBVolumeFixed;

	if (volume == (SInt16)0x8000) 
	{
		dBVolumeFixed = ((SInt16)0x8000 * 256) << 8;	// really is negative infinity
	} 
	else 
	{
		dBVolumeFixed = volume * 256;
	}

	// debugIOLog ("volume = %d, dBVolumeFixed = 0x%x", volume, dBVolumeFixed);

	return dBVolumeFixed;
}

IOReturn AppleUSBAudioDevice::getFeatureUnitRange (UInt8 controlSelector, UInt8 unitID, UInt8 channelNumber, UInt8 requestType, SubRange16 * target) {
    IOReturn							result;
	IOUSBDevRequestDesc					devReq;
	struct {
	UInt16								wNumSubRanges;
	SubRange16							subRanges[1];
	}									theSetting;
	IOBufferMemoryDescriptor *			theSettingDesc = NULL;
	UInt8								length = sizeof( theSetting );

	result = kIOReturnError;
	// Initialize theSetting so that 
	theSetting.subRanges[0].wMIN = 0;
	theSetting.subRanges[0].wMAX = 0;
	theSetting.subRanges[0].wRES = 0;
	FailIf (NULL == target, Exit);
	FailIf (NULL == mControlInterface, Exit);

	theSettingDesc = IOBufferMemoryDescriptor::withOptions (kIODirectionIn, length);
	FailIf (NULL == theSettingDesc, Exit);

    devReq.bmRequestType = USBmakebmRequestType (kUSBIn, kUSBClass, kUSBInterface);
	#ifdef HACK_FOR_EMU_0404_USB
	#warning HACK: The 0x80 is a hack for E-MU 0404 USB. Should remove it when E-MU fixes their firmware bug.
    devReq.bRequest = requestType | 0x80;
	#else
    devReq.bRequest = requestType;
	#endif
    devReq.wValue = (controlSelector << 8) | channelNumber;
    devReq.wIndex = (0xFF00 & (unitID << 8)) | (0x00FF & mControlInterface->GetInterfaceNumber ());
    devReq.wLength = length;
    devReq.pData = theSettingDesc;

	result = deviceRequest (&devReq);
	FailIf (kIOReturnSuccess != result, Exit);
	memcpy (&theSetting, theSettingDesc->getBytesNoCopy (), length);
	
Exit:
	if (NULL != theSettingDesc) 
	{
		theSettingDesc->release ();
	}
	if (NULL != target) 
	{
		if ( USBToHostWord (theSetting.wNumSubRanges) > 0 )
		{
			target->wMIN = USBToHostWord (theSetting.subRanges[0].wMIN);
			target->wMAX = USBToHostWord (theSetting.subRanges[0].wMAX);
			target->wRES = USBToHostWord (theSetting.subRanges[0].wRES);
		}
	}
	return result;
}

IOReturn AppleUSBAudioDevice::getFeatureUnitSetting (UInt8 controlSelector, UInt8 unitID, UInt8 channelNumber, UInt8 requestType, SInt16 * target) {
    IOReturn							result;
	IOUSBDevRequestDesc					devReq;
	UInt16								theSetting;
	IOBufferMemoryDescriptor *			theSettingDesc = NULL;
	UInt8								length;

	result = kIOReturnError;
	// Initialize theSetting so that 
	theSetting = 0;
	FailIf (NULL == target, Exit);
	FailIf (NULL == mControlInterface, Exit);

	switch (controlSelector) 
	{
		case MUTE_CONTROL:
			length = 1;
			break;
		case VOLUME_CONTROL:
			length = 2;
			break;
		default:
			length = 0;
	}
	theSettingDesc = IOBufferMemoryDescriptor::withOptions (kIODirectionIn, length);
	FailIf (NULL == theSettingDesc, Exit);

    devReq.bmRequestType = USBmakebmRequestType (kUSBIn, kUSBClass, kUSBInterface);
	#ifdef HACK_FOR_EMU_0404_USB
	#warning HACK: The 0x80 is a hack for E-MU 0404 USB. Should remove it when E-MU fixes their firmware bug.
    devReq.bRequest = requestType | 0x80;
	#else
    devReq.bRequest = requestType;
	#endif
    devReq.wValue = (controlSelector << 8) | channelNumber;
    devReq.wIndex = (0xFF00 & (unitID << 8)) | (0x00FF & mControlInterface->GetInterfaceNumber ());
    devReq.wLength = length;
    devReq.pData = theSettingDesc;

	result = deviceRequest (&devReq);
	FailIf (kIOReturnSuccess != result, Exit);
	memcpy (&theSetting, theSettingDesc->getBytesNoCopy (), length);
	
Exit:
	if (NULL != theSettingDesc) 
	{
		theSettingDesc->release ();
	}
	if (NULL != target) 
	{
		*target = USBToHostWord (theSetting);
	}
	return result;
}

IOReturn AppleUSBAudioDevice::setFeatureUnitSetting (UInt8 controlSelector, UInt8 unitID, UInt8 channelNumber, UInt8 requestType, UInt16 newValue, UInt16 newValueLen) {
    IOUSBDevRequestDesc					devReq;
	IOBufferMemoryDescriptor *			theSettingDesc = NULL;
	IOReturn							result;

	result = kIOReturnError;
	FailIf (NULL == mControlInterface, Exit);

	theSettingDesc = OSTypeAlloc (IOBufferMemoryDescriptor);
	FailIf (NULL == theSettingDesc, Exit);
	theSettingDesc->initWithBytes (&newValue, newValueLen, kIODirectionOut);

    devReq.bmRequestType = USBmakebmRequestType (kUSBOut, kUSBClass, kUSBInterface);
    devReq.bRequest = requestType;
    devReq.wValue = (controlSelector << 8) | channelNumber;
    devReq.wIndex = (0xFF00 & (unitID << 8)) | (0x00FF & mControlInterface->GetInterfaceNumber ());
    devReq.wLength = newValueLen;
    devReq.pData = theSettingDesc;

	FailIf ((TRUE == isInactive()), DeviceInactive);  	// In case we've been unplugged during sleep
	result = deviceRequest (&devReq);

Exit:
	if (NULL != theSettingDesc) 
	{
		theSettingDesc->release ();
	}
	return result;
	
DeviceInactive:
	debugIOLog("? AppleUSBAudioLevelControl::setFeatureUnitSetting () - ERROR attempt to send a device request to an inactive device");
	goto Exit;
}

IOReturn AppleUSBAudioDevice::getCurMute (UInt8 unitID, UInt8 channelNumber, SInt16 * target) {
	if ( IP_VERSION_02_00 == mControlInterface->GetInterfaceProtocol() )
	{
		return getFeatureUnitSetting (MUTE_CONTROL, unitID, channelNumber, USBAUDIO_0200::CUR, target);
	}
	else
	{
		return getFeatureUnitSetting (MUTE_CONTROL, unitID, channelNumber, GET_CUR, target);
	}
}

IOReturn AppleUSBAudioDevice::getCurVolume (UInt8 unitID, UInt8 channelNumber, SInt16 * target) {
	if ( IP_VERSION_02_00 == mControlInterface->GetInterfaceProtocol() )
	{
		return getFeatureUnitSetting (VOLUME_CONTROL, unitID, channelNumber, USBAUDIO_0200::CUR, target);
	}
	else
	{
		return getFeatureUnitSetting (VOLUME_CONTROL, unitID, channelNumber, GET_CUR, target);
	}
}

IOReturn AppleUSBAudioDevice::getMaxVolume (UInt8 unitID, UInt8 channelNumber, SInt16 * target) {
	IOReturn							result;

	if ( IP_VERSION_02_00 == mControlInterface->GetInterfaceProtocol() )
	{
		SubRange16 subRange;
		result = getFeatureUnitRange (VOLUME_CONTROL, unitID, channelNumber, USBAUDIO_0200::RANGE, &subRange);
		
		if ( target )
		{
			memcpy ( target, &subRange.wMAX, 2 );
		}
	}
	else
	{
		result = getFeatureUnitSetting (VOLUME_CONTROL, unitID, channelNumber, GET_MAX, target);
	}
	
	return result;
}

IOReturn AppleUSBAudioDevice::getMinVolume (UInt8 unitID, UInt8 channelNumber, SInt16 * target) {
	IOReturn							result;

	if ( IP_VERSION_02_00 == mControlInterface->GetInterfaceProtocol() )
	{
		SubRange16 subRange;
		result = getFeatureUnitRange (VOLUME_CONTROL, unitID, channelNumber, USBAUDIO_0200::RANGE, &subRange);
		
		if ( target )
		{
			memcpy ( target, &subRange.wMIN, 2 );
		}
	}
	else
	{
		result = getFeatureUnitSetting (VOLUME_CONTROL, unitID, channelNumber, GET_MIN, target);
	}
	
	return result;
}

IOReturn AppleUSBAudioDevice::getVolumeResolution (UInt8 unitID, UInt8 channelNumber, UInt16 * target) {
	IOReturn							result;

	if ( IP_VERSION_02_00 == mControlInterface->GetInterfaceProtocol() )
	{
		SubRange16 subRange;
		result = getFeatureUnitRange (VOLUME_CONTROL, unitID, channelNumber, USBAUDIO_0200::RANGE, &subRange);
		
		if ( target )
		{
			memcpy ( target, &subRange.wRES, 2 );
		}
	}
	else
	{
		result = getFeatureUnitSetting (VOLUME_CONTROL, unitID, channelNumber, GET_RES, (SInt16 *) target);
	}
	
	return result;
}

IOReturn AppleUSBAudioDevice::setCurVolume (UInt8 unitID, UInt8 channelNumber, SInt16 volume) {
	return setFeatureUnitSetting (VOLUME_CONTROL, unitID, channelNumber, SET_CUR, volume, 2);
}

IOReturn AppleUSBAudioDevice::setCurMute (UInt8 unitID, UInt8 channelNumber, SInt16 mute) {
	return setFeatureUnitSetting (MUTE_CONTROL, unitID, channelNumber, SET_CUR, mute, 1);
}

IOReturn AppleUSBAudioDevice::controlChangedHandler (OSObject * target, IOAudioControl * audioControl, SInt32 oldValue, SInt32 newValue) {
    IOReturn							result;
	AppleUSBAudioDevice *				self;

	result = kIOReturnError;

	self = OSDynamicCast (AppleUSBAudioDevice, target);
	FailIf (NULL == self, Exit);
	result = self->protectedControlChangedHandler (audioControl, oldValue, newValue);

Exit:
	return result;
}

IOReturn AppleUSBAudioDevice::protectedControlChangedHandler (IOAudioControl * audioControl, SInt32 oldValue, SInt32 newValue) {
    IOReturn							result;

	result = kIOReturnError;
    switch (audioControl->getType ()) 
	{
		case kIOAudioControlTypeLevel:
			result = doVolumeControlChange (audioControl, oldValue, newValue);
			break;
		case kIOAudioControlTypeToggle:
			result = doToggleControlChange (audioControl, oldValue, newValue);
			break;
		case kIOAudioControlTypeSelector:
			result = doSelectorControlChange (audioControl, oldValue, newValue);
			break;
	}

	return result;
}

IOReturn AppleUSBAudioDevice::doSelectorControlChange (IOAudioControl * audioControl, SInt32 oldValue, SInt32 newValue) {
    IOReturn							result;

	result = kIOReturnError;
	switch (audioControl->getUsage ()) 
	{
		case kIOAudioControlUsageInput:
			result = doInputSelectorChange (audioControl, oldValue, newValue);
			break;
		case kIOAudioControlUsageOutput:
			break;
		case kIOAudioControlUsagePassThru:
			result = doPassThruSelectorChange (audioControl, oldValue, newValue);
			break;
	}

	return result;
}

IOReturn AppleUSBAudioDevice::doVolumeControlChange (IOAudioControl * audioControl, SInt32 oldValue, SInt32 newValue) {
	IOReturn							result;
	SInt16								newVolume;
	SInt16								deviceMin;
	SInt16								offset;
	UInt16								volRes;
	UInt8								unitID;
	UInt8								channelNum;

	debugIOLog ("+ AppleUSBAudioDevice[%p]::doVolumeControlChange( %p, 0x%x, 0x%x )", this, audioControl, oldValue, newValue);
	unitID = audioControl->getControlID ();
	channelNum = audioControl->getChannelID ();
	result = kIOReturnError;

	if (    (kIOAudioControlUsageInput == audioControl->getUsage())
	     || (FALSE == mDeviceIsInMonoMode))
	{
		getMinVolume (unitID, channelNum, &deviceMin);
		offset = -deviceMin;

		if (newValue < 0) 
		{
			newVolume = 0x8000;
		} 
		else 
		{
			getVolumeResolution (unitID, channelNum, &volRes);
			if (newValue > 0) 
			{
				newVolume = ((newValue - 1) * volRes) - offset;
			} 
			else 
			{
				newVolume = (newValue * volRes) - offset;
			}
		}

		debugIOLog ("? AppleUSBAudioDevice[%p]::doVolumeControlChange () - Setting channel %d (unit %d) volume to 0x%x", this, channelNum, unitID, newVolume);
		result = setCurVolume (unitID, channelNum, HostToUSBWord (newVolume));
	
	}
	else
	{	// mono output case
		UInt8 i;
		FailIf (NULL == mMonoControlsArray, Exit);
		debugIOLog ("? AppleUSBAudioDevice[%p]::doVolumeControlChange () - Performing mono volume control change", this);
		for (i = 0; i < mMonoControlsArray->getCount (); i++)
		{
			channelNum = ((OSNumber *) mMonoControlsArray->getObject(i))->unsigned8BitValue ();
			getMinVolume (unitID, channelNum, &deviceMin);
			offset = -deviceMin;
		
			if (newValue < 0) 
			{
				newVolume = 0x8000;
			} 
			else 
			{
				getVolumeResolution (unitID, channelNum, &volRes);
				if (newValue > 0) 
				{
					newVolume = ((newValue - 1) * volRes) - offset;
				} 
				else 
				{
					newVolume = (newValue * volRes) - offset;
				}
			}
		
			result = setCurVolume (unitID, channelNum, HostToUSBWord (newVolume));
			debugIOLog ("? AppleUSBAudioDevice[%p]::doVolumeControlChange () - Set volume for channel %d to 0x%x = %d", this, channelNum, newVolume, result);
		}
	}
	
Exit:
	debugIOLog ("- AppleUSBAudioDevice[%p]::doVolumeControlChange( %p, 0x%x, 0x%x ) = 0x%x", this, audioControl, oldValue, newValue, result);
	return result;
}

IOReturn AppleUSBAudioDevice::doToggleControlChange (IOAudioControl * audioControl, SInt32 oldValue, SInt32 newValue) {
	IOReturn							result;
	UInt8								unitID;
	UInt8								channelNum;

	unitID = audioControl->getControlID ();
	channelNum = audioControl->getChannelID ();

	result = setCurMute (unitID, channelNum, HostToUSBWord (newValue));

	return result;
}

IOReturn AppleUSBAudioDevice::doPassThruSelectorChange (IOAudioControl * audioControl, SInt32 oldValue, SInt32 newValue) {
	AppleUSBAudioEngine *				usbAudioEngine;
	OSArray *							playThroughPaths;
	OSArray *							passThruVolControlsArray;
	OSArray *							thePath;
	OSNumber *							number;
	OSDictionary *						engineInfo;
	UInt32								i;
	UInt32								numPassThruControls;
	UInt8								interfaceNum;
	UInt8								altSetting;
	UInt8								featureUnitID;
	UInt8								pathIndex;

	pathIndex = (newValue & 0x000000FF);

	engineInfo = OSDynamicCast (OSDictionary, mRegisteredEngines->getObject (audioControl->getControlID ()));
	FailIf (NULL == engineInfo, Exit);
	usbAudioEngine = OSDynamicCast (AppleUSBAudioEngine, engineInfo->getObject (kEngine));
	FailIf (NULL == usbAudioEngine, Exit);
	number = OSDynamicCast (OSNumber, engineInfo->getObject (kInterface));
	FailIf (NULL == number, Exit);
	interfaceNum = number->unsigned8BitValue ();
	number = OSDynamicCast (OSNumber, engineInfo->getObject (kAltSetting));
	FailIf (NULL == number, Exit);
	altSetting = number->unsigned8BitValue ();
	passThruVolControlsArray = OSDynamicCast (OSArray, engineInfo->getObject (kPassThruVolControls));
	FailIf (NULL == passThruVolControlsArray, Exit);
	numPassThruControls = passThruVolControlsArray->getCount ();

	usbAudioEngine->pauseAudioEngine ();
	usbAudioEngine->beginConfigurationChange ();
	for (i = 0; i < numPassThruControls; i++) 
	{
		usbAudioEngine->removeDefaultAudioControl ((IOAudioLevelControl *)passThruVolControlsArray->getObject (i));
	}
	passThruVolControlsArray->flushCollection ();

	playThroughPaths = OSDynamicCast (OSArray, engineInfo->getObject (kPassThruPathsArray));
	FailIf (NULL == playThroughPaths, Exit);
	thePath = OSDynamicCast (OSArray, playThroughPaths->getObject (pathIndex));
	FailIf (NULL == thePath, Exit);
	featureUnitID = getBestFeatureUnitInPath (thePath, kIOAudioControlUsagePassThru, interfaceNum, altSetting, kVolumeControl);
	addVolumeControls (usbAudioEngine, featureUnitID, interfaceNum, altSetting, kIOAudioControlUsagePassThru);
	usbAudioEngine->completeConfigurationChange ();
	usbAudioEngine->resumeAudioEngine ();

Exit:
	return kIOReturnSuccess;
}

IOReturn AppleUSBAudioDevice::doInputSelectorChange (IOAudioControl * audioControl, SInt32 oldValue, SInt32 newValue) {
	AppleUSBAudioEngine *				usbAudioEngine;
	OSArray *							inputGainControlsArray;
	OSArray *							arrayOfPathsFromOutputTerminal;
	OSArray *							thePath;
	OSNumber *							number;
	OSDictionary *						engineInfo;
    IOReturn							result;
	UInt32								i;
	UInt32								numGainControls;
	UInt8								interfaceNum;
	UInt8								altSetting;
	UInt8								featureUnitID;
	UInt8								selectorUnitID;
	UInt8								selectorPosition;
	UInt8								pathsToOutputTerminal;
	UInt8								pathIndex;

	pathsToOutputTerminal = (newValue & 0xFF000000) >> 24;
	pathIndex = (newValue & 0x00FF0000) >> 16;
	selectorUnitID = (newValue & 0x0000FF00) >> 8;
	selectorPosition = newValue & 0x000000FF;
	result = setSelectorSetting (selectorUnitID, selectorPosition);
	FailIf (kIOReturnSuccess != result, Exit);

	engineInfo = OSDynamicCast (OSDictionary, mRegisteredEngines->getObject (audioControl->getControlID ()));
	FailIf (NULL == engineInfo, Exit);
	usbAudioEngine = OSDynamicCast (AppleUSBAudioEngine, engineInfo->getObject (kEngine));
	FailIf (NULL == usbAudioEngine, Exit);
	number = OSDynamicCast (OSNumber, engineInfo->getObject (kInterface));
	FailIf (NULL == number, Exit);
	interfaceNum = number->unsigned8BitValue ();
	number = OSDynamicCast (OSNumber, engineInfo->getObject (kAltSetting));
	FailIf (NULL == number, Exit);
	altSetting = number->unsigned8BitValue ();
	inputGainControlsArray = OSDynamicCast (OSArray, engineInfo->getObject (kInputGainControls));
	FailIf (NULL == inputGainControlsArray, Exit);
	numGainControls = inputGainControlsArray->getCount ();

	usbAudioEngine->pauseAudioEngine ();
	usbAudioEngine->beginConfigurationChange ();
	for (i = 0; i < numGainControls; i++) 
	{
		usbAudioEngine->removeDefaultAudioControl ((IOAudioLevelControl *)inputGainControlsArray->getObject (i));
	}
	inputGainControlsArray->flushCollection ();

	arrayOfPathsFromOutputTerminal = OSDynamicCast (OSArray, mControlGraph->getObject (pathsToOutputTerminal));
	FailIf (NULL == arrayOfPathsFromOutputTerminal, Exit);
	thePath = OSDynamicCast (OSArray, arrayOfPathsFromOutputTerminal->getObject (pathIndex));
	FailIf (NULL == thePath, Exit);
	featureUnitID = getBestFeatureUnitInPath (thePath, kIOAudioControlUsageInput, interfaceNum, altSetting, kVolumeControl);
	addVolumeControls (usbAudioEngine, featureUnitID, interfaceNum, altSetting, kIOAudioControlUsageInput);
	usbAudioEngine->completeConfigurationChange ();
	usbAudioEngine->resumeAudioEngine ();

Exit:
	return result;
}

// This should detect a playthrough path; which is a non-streaming input terminal connected to a non-streaming output terminal.
OSArray * AppleUSBAudioDevice::getPlaythroughPaths () {
	OSArray *							arrayOfPathsFromOutputTerminal;
	OSArray *							playThroughPaths;
	OSArray *							aPath;
	OSNumber *							theUnitIDNum;
	UInt32								numOutputTerminalArrays;
	UInt32								numPathsFromOutputTerminal;
	UInt32								pathsToOutputTerminalN;
	UInt32								pathNumber;
	UInt16								terminalType;
	UInt8								controlInterfaceNum;
	UInt8								unitID;

	playThroughPaths = NULL;
	FailIf (NULL == mControlInterface, Exit);
	controlInterfaceNum = mControlInterface->GetInterfaceNumber ();

	numOutputTerminalArrays = mControlGraph->getCount ();
	for (pathsToOutputTerminalN = 0; pathsToOutputTerminalN < numOutputTerminalArrays; pathsToOutputTerminalN++) 
	{
		arrayOfPathsFromOutputTerminal = OSDynamicCast (OSArray, mControlGraph->getObject (pathsToOutputTerminalN));
		FailIf (NULL == arrayOfPathsFromOutputTerminal, Exit);
		aPath = OSDynamicCast (OSArray, arrayOfPathsFromOutputTerminal->getObject (0));
		FailIf (NULL == aPath, Exit);
		theUnitIDNum = OSDynamicCast (OSNumber, aPath->getObject (0));
		FailIf (NULL == theUnitIDNum, Exit);
		unitID = theUnitIDNum->unsigned8BitValue ();
		FailIf (kIOReturnSuccess != mConfigDictionary->getOutputTerminalType (&terminalType, controlInterfaceNum, 0, unitID), Exit);
		if (0x101 == terminalType) continue;		// only looking for non-streaming outputs

		numPathsFromOutputTerminal = arrayOfPathsFromOutputTerminal->getCount ();
		for (pathNumber = 0; pathNumber < numPathsFromOutputTerminal; pathNumber++) 
		{
			aPath = OSDynamicCast (OSArray, arrayOfPathsFromOutputTerminal->getObject (pathNumber));
			FailIf (NULL == aPath, Exit);
			theUnitIDNum = OSDynamicCast (OSNumber, aPath->getLastObject ());
			FailIf (NULL == theUnitIDNum, Exit);
			unitID = theUnitIDNum->unsigned8BitValue ();
			FailIf (kIOReturnSuccess != mConfigDictionary->getInputTerminalType (&terminalType, controlInterfaceNum, 0, unitID), Exit);
			if (terminalType != 0x101) 
			{
				if (NULL == playThroughPaths) 
				{
					playThroughPaths = OSArray::withObjects ((const OSObject **)&aPath, 1);
				} 
				else 
				{
					playThroughPaths->setObject (aPath);
				}
			}
		}
	}

Exit:
	return playThroughPaths;
}

// This finds the feature unit closest to the input terminal.
UInt8 AppleUSBAudioDevice::getBestFeatureUnitInPath (OSArray * thePath, UInt32 direction, UInt8 interfaceNum, UInt8 altSettingNum, UInt32 controlTypeWanted) {
	OSNumber *							theUnitIDNum;
	UInt32								numUnitsInPath;
	UInt8								featureUnitID;
	UInt8								unitIndex;
	UInt8								controlInterfaceNum;
	UInt8								unitID;
	UInt8								subType;
	UInt8								channelNum;
	bool								requireUniqueness = false;
	UInt8								numChannels;
	Boolean								found;

	debugIOLog ( "+ AppleUSBAudioDevice[%p]::getBestFeatureUnitInPath (%p, %lu, %d, %d, %lu)", this, thePath, direction, interfaceNum, altSettingNum, controlTypeWanted );
	featureUnitID = 0;
	FailIf (NULL == mControlInterface, Exit);
	FailIf (NULL == thePath, Exit);
	controlInterfaceNum = mControlInterface->GetInterfaceNumber ();
	numUnitsInPath = thePath->getCount ();
	found = FALSE;

	switch (direction) 
	{
		case kIOAudioControlUsagePassThru:
			// [rdar://4586274] Require playthrough control feature units to be path unique.
			requireUniqueness = true;
		case kIOAudioControlUsageInput:
		// Find the feature unit closest to the input terminal.
			for (unitIndex = numUnitsInPath - 2; unitIndex > 0; unitIndex--) 
			{
				theUnitIDNum = OSDynamicCast (OSNumber, thePath->getObject (unitIndex));
				if (NULL != theUnitIDNum) 
				{
					unitID = theUnitIDNum->unsigned8BitValue ();
					FailIf (kIOReturnSuccess != mConfigDictionary->getSubType (&subType, controlInterfaceNum, 0, unitID), Exit);
					if	(		(FEATURE_UNIT == subType)
							&&	(		(false == requireUniqueness)
									||	( 1 == pathsContaining (unitID) ) ) )
					{
						FailIf (kIOReturnSuccess != mConfigDictionary->getNumChannels (&numChannels, interfaceNum, altSettingNum), Exit);
						for (channelNum = 0; channelNum <= numChannels; channelNum++) 
						{
							switch (controlTypeWanted) 
							{
								case kVolumeControl:
									if (mConfigDictionary->channelHasVolumeControl (controlInterfaceNum, 0, unitID, channelNum))
									{
										featureUnitID = unitID;
										found = TRUE;
									}
									break;
								case kMuteControl:
									if (mConfigDictionary->channelHasMuteControl (controlInterfaceNum, 0, unitID, channelNum))
									{
										featureUnitID = unitID;
										found = TRUE;
									}
									break;
							} // switch
						} // for channelNum
					} // if (FEATURE_UNIT == subType)
				} // if (NULL != theUnitIDNum)
				else
				{
					debugIOLog ( "! AppleUSBAudioDevice::getBestFeatureUnitInPath () - something is wrong here!!!" );
				}
			} // for unitIndex
			break;
		case kIOAudioControlUsageOutput:
		default:
			// Find the feature unit closest to the output terminal.
			debugIOLog ("? AppleUSBAudioDevice::getBestFeatureUnitInPath () - kIOAudioControlUsageOutput ");
			for (unitIndex = 1; unitIndex < numUnitsInPath && !found; unitIndex++) 
			{
				theUnitIDNum = OSDynamicCast (OSNumber, thePath->getObject (unitIndex));
				if (NULL != theUnitIDNum) 
				{
					unitID = theUnitIDNum->unsigned8BitValue ();
					FailIf (kIOReturnSuccess != mConfigDictionary->getSubType (&subType, controlInterfaceNum, 0, unitID), Exit);
					if (FEATURE_UNIT == subType) 
					{
						debugIOLog ( "  examining feature unit %d ...", unitID );
						FailIf (kIOReturnSuccess != mConfigDictionary->getNumChannels (&numChannels, interfaceNum, altSettingNum), Exit);
						for (channelNum = 0; channelNum <= numChannels; channelNum++) 
						{
							switch (controlTypeWanted) 
							{
								case kVolumeControl:
									if (mConfigDictionary->channelHasVolumeControl (controlInterfaceNum, 0, unitID, channelNum))
									{
										featureUnitID = unitID;
										found = TRUE;
									}
									break;
								case kMuteControl:
									if (mConfigDictionary->channelHasMuteControl (controlInterfaceNum, 0, unitID, channelNum)) 
									{
										featureUnitID = unitID;
										found = TRUE;
									}
									break;
							} // switch
						} // for channelNum
					} // if (FEATURE_UNIT == subType)
				} // if (NULL != theUnitIDNum)
				else
				{
					debugIOLog ( "! AppleUSBAudioDevice::getBestFeatureUnitInPath () - something is wrong here!!!" );
				}
			} // for unitIndex
			break;
	} // switch (direction)

Exit:
	debugIOLog ( "- AppleUSBAudioDevice[%p]::getBestFeatureUnitInPath () = %d", this, featureUnitID );
	return featureUnitID;
}

UInt8 AppleUSBAudioDevice::pathsContaining (UInt8 unitID)
{
	OSArray *	thisPathGroup = NULL;
	OSArray *	thisPath = NULL;
	OSNumber *  unitNumber = NULL;
	UInt8		numPaths = 0;
	
	if ( mControlGraph )
	{
		for ( UInt8 pathGroupIndex = 0; pathGroupIndex < mControlGraph->getCount(); pathGroupIndex++ )
		{
			thisPathGroup = OSDynamicCast ( OSArray, mControlGraph->getObject ( pathGroupIndex ) );
			if ( thisPathGroup )
			{
				for (UInt8 pathIndex = 0; pathIndex < thisPathGroup->getCount(); pathIndex++ )
				{
					thisPath = OSDynamicCast ( OSArray, thisPathGroup->getObject (pathIndex) );
					if ( thisPath )
					{
						for ( UInt8 unitIndex = 0; unitIndex < thisPath->getCount (); unitIndex++ )
						{
							unitNumber = OSDynamicCast ( OSNumber, thisPath->getObject (unitIndex) );
							if ( unitID == unitNumber->unsigned8BitValue() )
							{
								numPaths++;
								// Circular topologies are not allowed.
								break;
							}
						}
					}
				}
			}
		} 
	}
	return numPaths;
}

UInt8 AppleUSBAudioDevice::getSelectorSetting (UInt8 selectorID) {
    IOUSBDevRequestDesc					devReq;
	IOReturn							result;
	UInt8								setting;
	IOBufferMemoryDescriptor *			settingDesc = NULL;

	setting = 0;
	FailIf (NULL == mControlInterface, Exit);

	settingDesc = IOBufferMemoryDescriptor::withOptions (kIODirectionIn, 1);
	FailIf (NULL == settingDesc, Exit);

    devReq.bmRequestType = USBmakebmRequestType (kUSBIn, kUSBClass, kUSBInterface);
    devReq.bRequest = ( IP_VERSION_02_00 == mControlInterface->GetInterfaceProtocol() ) ? USBAUDIO_0200::CUR : GET_CUR;
    devReq.wValue = 0;
    devReq.wIndex = (0xFF00 & (selectorID << 8)) | (0x00FF & mControlInterface->GetInterfaceNumber ());
    devReq.wLength = 1;
    devReq.pData = settingDesc;

	result = deviceRequest (&devReq);
	FailIf (kIOReturnSuccess != result, Exit);
	memcpy (&setting, settingDesc->getBytesNoCopy (), 1);

Exit:
	if (NULL != settingDesc) 
	{
		settingDesc->release ();
	}
	return setting;
}

IOReturn AppleUSBAudioDevice::setSelectorSetting (UInt8 selectorID, UInt8 setting) {
    IOUSBDevRequestDesc					devReq;
	IOReturn							result;
	IOBufferMemoryDescriptor *			settingDesc = NULL;

	result = kIOReturnError;
	FailIf (NULL == mControlInterface, Exit);

	settingDesc = OSTypeAlloc (IOBufferMemoryDescriptor);
	FailIf (NULL == settingDesc, Exit);
	settingDesc->initWithBytes (&setting, 1, kIODirectionIn);

    devReq.bmRequestType = USBmakebmRequestType (kUSBOut, kUSBClass, kUSBInterface);
    devReq.bRequest = SET_CUR;
    devReq.wValue = 0;
    devReq.wIndex = (0xFF00 & (selectorID << 8)) | (0x00FF & mControlInterface->GetInterfaceNumber ());
    devReq.wLength = 1;
    devReq.pData = settingDesc;

	result = deviceRequest (&devReq);

Exit:
	if (NULL != settingDesc) 
	{
		settingDesc->release ();
	}
	return result;
}

void AppleUSBAudioDevice::setMonoState (Boolean state)
{
	mDeviceIsInMonoMode = state;
}

IOReturn AppleUSBAudioDevice::createControlsForInterface (IOAudioEngine *audioEngine, UInt8 interfaceNum, UInt8 altSettingNum) {
    AppleUSBAudioEngine *				usbAudioEngine;
    IOReturn							result;
    debugIOLog ("? AppleUSBAudioDevice[%p]::createControlsForInterface () - Interface %d alternate setting %d", this, interfaceNum, altSettingNum);

    result = kIOReturnError;
	mTerminatingDriver = FALSE;

    usbAudioEngine = OSDynamicCast (AppleUSBAudioEngine, audioEngine);
    FailIf (NULL == usbAudioEngine, Exit);

	doControlStuff (audioEngine, interfaceNum, altSettingNum);

Exit:
	return result;
}

// [rdar://4867843]
OSArray * AppleUSBAudioDevice::getOptimalClockPath ( AppleUSBAudioEngine * thisEngine, UInt8 streamInterface, UInt8 altSetting, UInt32 sampleRate, Boolean * otherEngineNeedSampleRateChange )
{
	OSNumber *						clockSourceIDNumber = NULL;
	OSArray *						pathGroupArray = NULL;
	OSArray *						pathArray = NULL;
	OSArray *						optimalPathArray = NULL;
	UInt8							pathIndex;
	UInt8							terminalID;
	UInt8							clockSourceID;
	UInt32							clockPathUnitUsage = 0xFFFFFFFF;
	
	debugIOLog ( "+ AppleUSBAudioDevice[%p]::getOptimalClockPath ( %d, %d, %d )", this, streamInterface, altSetting, sampleRate );
	FailIf ( NULL == mConfigDictionary, Exit );
	
	FailIf ( kIOReturnSuccess != mConfigDictionary->getTerminalLink ( &terminalID, streamInterface, altSetting ), Exit );
	FailIf ( kIOReturnSuccess != mConfigDictionary->getClockSourceID ( &clockSourceID, mControlInterface->GetInterfaceNumber (), 0, terminalID ), Exit ); 
	
	debugIOLog (" ? AppleUSBAudioDevice::getOptimalClockPath () - interface %d, alt setting %d, clock source ID %d", streamInterface, altSetting, clockSourceID );
	
	// Find the path group that begins with the clockSourceID.
	for ( UInt8 pathGroupIndex = 0; pathGroupIndex < mClockGraph->getCount (); pathGroupIndex++ )
	{
		FailIf ( NULL == ( pathGroupArray = OSDynamicCast ( OSArray, mClockGraph->getObject ( pathGroupIndex ) ) ), Exit );
		FailIf ( NULL == ( pathArray = OSDynamicCast ( OSArray, pathGroupArray->getObject ( 0 ) ) ), Exit );
		FailIf ( NULL == ( clockSourceIDNumber = OSDynamicCast ( OSNumber, pathArray->getObject ( 0 ) ) ), Exit );
		if ( clockSourceID == clockSourceIDNumber->unsigned8BitValue () )
		{
			// We have found the path group in which we are interested.
			break;
		}
		else
		{
			pathGroupArray = NULL;
		}
	}
	FailIf ( NULL == pathGroupArray, Exit );

	// For each path in the path group, determine if it supported the requested sample rate.
	for ( pathIndex = 0; pathIndex < pathGroupArray->getCount (); pathIndex++ )
	{
		FailIf ( NULL == ( pathArray = OSDynamicCast ( OSArray, pathGroupArray->getObject ( pathIndex ) ) ), Exit );
		
		if ( supportSampleRateInClockPath ( pathArray, sampleRate ) )
		{
			// Need to select the path with the clock units that are not used (or has the smallest usage count) by other paths 
			// in other engines (if possible).
			UInt32 usageCount = determineClockPathUnitUsage ( thisEngine, pathArray );
			
			if ( usageCount < clockPathUnitUsage )
			{
				clockPathUnitUsage = usageCount;
				optimalPathArray = pathArray;
			}
			
			if ( 0 == clockPathUnitUsage )
			{
				// Found a path that is independent of the paths in other engines.
				break;
			}
		}
	}	

Exit:
	if ( NULL != otherEngineNeedSampleRateChange )
	{
		* otherEngineNeedSampleRateChange = ( clockPathUnitUsage > 0 );
	}

	debugIOLog ( "- AppleUSBAudioDevice[%p]::getOptimalClockPath ( %d, %d, %d ) = %p", this, streamInterface, altSetting, sampleRate, optimalPathArray );
	return optimalPathArray;
}

Boolean AppleUSBAudioDevice::supportSampleRateInClockPath ( OSArray * pathArray, UInt32 sampleRate )
{
	Boolean							sampleRateSupported = false;
	UInt8							numRange;
	UInt8							rangeIndex;
	SubRange32						subRange;
	
	debugIOLog ( "+ AppleUSBAudioDevice[%p]::supportSampleRateInClockPath ()", this );

	FailIf ( kIOReturnSuccess != getNumSampleRatesForClockPath ( &numRange, pathArray ), Exit );
	
	for ( rangeIndex = 0; rangeIndex < numRange; rangeIndex++ )
	{
		if ( kIOReturnSuccess == getIndexedSampleRatesForClockPath ( &subRange, pathArray, rangeIndex ) )
		{		
			// If dRES is not zero, there should be a total of 1 + ( ( dMAX - dMIN ) / dRES ) sample rates including dMIN and dMAX.
			if ( 0 != subRange.dRES )
			{
				for ( UInt16 sampleRateIndex = 0; sampleRateIndex <  ( ( subRange.dMAX - subRange.dMIN) / subRange.dRES ) - 1; sampleRateIndex++ )
				{
					if ( sampleRate == ( subRange.dMIN + ( 1 + sampleRateIndex ) * subRange.dRES ) )
					{
						sampleRateSupported = true;				
						break;
					}
				}
			}
			else
			{
				if ( ( sampleRate == subRange.dMIN ) || ( sampleRate == subRange.dMAX ) )
				{
					sampleRateSupported = true;				
				}
			}
		}
		
		if ( sampleRateSupported )
		{
			break;
		}
	}
	
Exit:
	debugIOLog ( "- AppleUSBAudioDevice[%p]::supportSampleRateInClockPath () = %d", this, sampleRateSupported );
	return sampleRateSupported;
}

UInt32 AppleUSBAudioDevice::determineClockPathUnitUsage ( AppleUSBAudioEngine * thisEngine, OSArray * thisClockPath )
{
	UInt32							usageCount = 0;
	SInt32							engineIndex;
	OSDictionary *					otherAudioEngineInfo = NULL;
	AppleUSBAudioEngine *			otherAudioEngine = NULL;

	debugIOLog ( "+ AppleUSBAudioDevice[%p]::determineClockPathUnitUsage ( %p, %p )", this, thisEngine, thisClockPath );

	if ( NULL != mRegisteredEngines )
	{
		for ( engineIndex = 0; ; engineIndex++ )
		{	
			if ( NULL == ( otherAudioEngineInfo = OSDynamicCast (OSDictionary, mRegisteredEngines->getObject (engineIndex) ) ) )
			{
				break;
			}			
			
			if ( NULL == ( otherAudioEngine = OSDynamicCast (AppleUSBAudioEngine, otherAudioEngineInfo->getObject (kEngine) ) ) )
			{
				break;
			}
			
			if (thisEngine != otherAudioEngine)
			{
				if ( NULL != otherAudioEngine->mActiveClockPath )
				{
					if ( clockPathCrossed ( thisClockPath, otherAudioEngine->mActiveClockPath ) )
					{
						usageCount++;
					}
				}
			}
		}
	}

	debugIOLog ( "+ AppleUSBAudioDevice[%p]::determineClockPathUnitUsage ( %p, %p ) = %d", this, thisEngine, thisClockPath, usageCount );
	return usageCount;
}

Boolean AppleUSBAudioDevice::clockPathCrossed ( OSArray * clockPathA, OSArray * clockPathB )
{
	OSNumber *						clockIDNumberA = NULL;
	OSNumber *						clockIDNumberB = NULL;
	Boolean							pathCrossed = false;
	
	// Determine it the 2 paths crossed.
	for ( UInt8 pathItemA = 0; pathItemA < clockPathA->getCount(); pathItemA++ )
	{
		FailIf ( NULL == ( clockIDNumberA = OSDynamicCast ( OSNumber, clockPathA->getObject (pathItemA) ) ), Exit );
	
		for ( UInt8 pathItemB = 0; pathItemB < clockPathB->getCount(); pathItemB++ )
		{
			FailIf ( NULL == ( clockIDNumberB = OSDynamicCast ( OSNumber, clockPathB->getObject (pathItemB) ) ), Exit );
		
			if ( clockIDNumberA->unsigned8BitValue () == clockIDNumberB->unsigned8BitValue () )
			{
				pathCrossed = true;
				break;
			}						
		}			
		
		if ( pathCrossed )
		{
			break;
		}
	}
	
Exit:
	return pathCrossed;
}

// [rdar://4867779]
IOReturn AppleUSBAudioDevice::addSampleRatesFromClockSpace ()
{
	IOReturn						result = kIOReturnError;
	OSNumber *						clockSourceIDNumber = NULL;
	OSNumber *						streamInterfaceNumber = NULL;
	OSArray *						streamInterfaceNumbers = NULL;
	OSArray *						pathGroupArray = NULL;
	OSArray *						pathArray = NULL;
	UInt8							streamInterface;
	UInt8							numStreamInterfaces;
	UInt8							numAltSettings;
	UInt8							terminalID;
	UInt8							clockSourceID;
	bool							startAtZero;
	
	debugIOLog ( "+ AppleUSBAudioDevice[%p]::addSampleRatesFromClockSpace ()", this );
	FailIf ( NULL == mConfigDictionary, Exit );
	FailIf ( NULL == mClockGraph, Exit );
	
	// Discover sample rates for each stream interface
	FailIf ( kIOReturnSuccess != mConfigDictionary->getControlledStreamNumbers ( &streamInterfaceNumbers, &numStreamInterfaces ), Exit );
	for ( UInt8 streamInterfaceIndex = 0; streamInterfaceIndex < streamInterfaceNumbers->getCount (); streamInterfaceIndex++ )
	{
		FailIf ( NULL == ( streamInterfaceNumber = OSDynamicCast ( OSNumber, streamInterfaceNumbers->getObject ( streamInterfaceIndex ) ) ), Exit );
		streamInterface = streamInterfaceNumber->unsigned8BitValue ();
		startAtZero = mConfigDictionary->alternateSettingZeroCanStream ( streamInterface );
		
		// Discover sample rates for each alternate setting
		FailIf ( kIOReturnSuccess != mConfigDictionary->getNumAltSettings ( &numAltSettings, streamInterface ), Exit );
		for		(	UInt8 altSettingIndex = ( startAtZero ? 0 : 1 ); 
					altSettingIndex < numAltSettings;
					altSettingIndex++ )
		{
			FailIf ( kIOReturnSuccess != mConfigDictionary->getTerminalLink ( &terminalID, streamInterface, altSettingIndex ), Exit );
			FailIf ( kIOReturnSuccess != mConfigDictionary->getClockSourceID ( &clockSourceID, mControlInterface->GetInterfaceNumber (), 0, terminalID ), Exit ); 
			
			debugIOLog (" ? AppleUSBAudioDevice::addSampleRatesFromClockSpace () - interface %d, alt setting %d, clock source ID %d", streamInterface, altSettingIndex, clockSourceID );
			// Find the path group that begins with the clockSourceID.
			for ( UInt8 pathGroupIndex = 0; pathGroupIndex < mClockGraph->getCount (); pathGroupIndex++ )
			{
				FailIf ( NULL == ( pathGroupArray = OSDynamicCast ( OSArray, mClockGraph->getObject ( pathGroupIndex ) ) ), Exit );
				FailIf ( NULL == ( pathArray = OSDynamicCast ( OSArray, pathGroupArray->getObject ( 0 ) ) ), Exit );
				FailIf ( NULL == ( clockSourceIDNumber = OSDynamicCast ( OSNumber, pathArray->getObject ( 0 ) ) ), Exit );
				if ( clockSourceID == clockSourceIDNumber->unsigned8BitValue () )
				{
					// We have found the path group in which we are interested.
					break;
				}
				else
				{
					pathGroupArray = NULL;
				}
			}
			FailIf ( NULL == pathGroupArray, Exit );
			
			// For each path in the path group, add the available sample rates.
			for ( UInt8 pathIndex = 0; pathIndex < pathGroupArray->getCount (); pathIndex++ )
			{
				FailIf ( NULL == ( pathArray = OSDynamicCast ( OSArray, pathGroupArray->getObject ( pathIndex ) ) ), Exit );
				FailIf ( kIOReturnSuccess != addSampleRatesFromClockPath ( pathArray, streamInterface, altSettingIndex ), Exit );				
			}
		}
	}
	
	result = kIOReturnSuccess;
	
Exit:
	debugIOLog ( "- AppleUSBAudioDevice[%p]::addSampleRatesFromClockSpace () = 0x%x", this, result );
	return result;
}

IOReturn AppleUSBAudioDevice::addSampleRatesFromClockPath ( OSArray * path, UInt8 streamInterface, UInt8 altSetting )
{
	IOReturn						result = kIOReturnError;
	OSNumber *						clockSourceNumber;
	OSArray *						sampleRates;
	
	debugIOLog ( "+ AppleUSBAudioDevice[%p]::addSampleRatesFromClockPath ( %p, %d, %d )", this, path, streamInterface, altSetting );
	FailIf ( NULL == path, Exit );
	FailIf ( NULL == mConfigDictionary, Exit );
	
	// First get the samples rates on the clock source, which will be the last element of the path.
	FailIf ( NULL == ( clockSourceNumber = OSDynamicCast ( OSNumber, path->getLastObject () ) ), Exit );
	FailIf ( kIOReturnSuccess != getClockSourceSampleRates ( &sampleRates, clockSourceNumber->unsigned8BitValue () ), Exit );
	
	// Clock multipliers will modify the available sample rates, so search for them in the path.
	#warning Need to support adding sample rates via clock multipliers!
		
	// Add the list of discrete sample rates to the stream dictionary
	FailIf ( kIOReturnSuccess != ( result = mConfigDictionary->addSampleRatesToStreamDictionary ( sampleRates, streamInterface, altSetting ) ), Exit ); 
	
Exit:
	debugIOLog ( "- AppleUSBAudioDevice[%p]::addSampleRatesFromClockPath ( %p, %d, %d ) = 0x%x", this, path, streamInterface, altSetting, result );
	return result;
}

IOReturn AppleUSBAudioDevice::getClockSourceSampleRates ( OSArray ** sampleRates, UInt8 clockSource )
{
	IOReturn						result = kIOReturnError;
	OSNumber *						sampleRateNumber = NULL;
	SubRange32						subRange;
	UInt32							sampleRate;
	UInt16							numSampleRanges;
	bool							clockIsValid;
	bool							subRangeIsValid;

	debugIOLog ( "+ AppleUSBAudioDevice[%p]::getClockSourceSampleRates ( %p, %d )", this, sampleRates, clockSource );
	FailIf ( NULL == mConfigDictionary, Exit );
	FailIf ( NULL == sampleRates, Exit );
	FailIf ( 0 == clockSource, Exit );
	* sampleRates = NULL;
	
	if ( mConfigDictionary->clockSourceHasFrequencyControl ( mControlInterface->GetInterfaceNumber (), 0, clockSource ) )
	{
		// Get the number of sample ranges.
		FailIf ( kIOReturnSuccess != ( result = getNumClockSourceSamplingFrequencySubRanges ( clockSource, &numSampleRanges ) ), Exit ) ;
		
		for ( UInt8 sampleRangeIndex = 0; sampleRangeIndex < numSampleRanges; sampleRangeIndex++ )
		{
			FailIf ( kIOReturnSuccess != ( result = getIndexedClockSourceSamplingFrequencySubRange ( clockSource, &subRange, sampleRangeIndex ) ), Exit );
			
			// [rdar://4952486] Make sure that the device isn't just spewing garbage before trying to add these values.
			subRangeIsValid = (			( subRange.dMIN <= subRange.dMAX )
									&&	(		( 0 == subRange.dRES )
											||	( 0 == ( subRange.dMAX - subRange.dMIN ) % subRange.dRES ) ) );		
			if ( ! subRangeIsValid )
			{
				debugIOLog ( "! AppleUSBAudioDevice[%p]::getClockSourceSampleRates () - invalid subrange, skipping ...", this );
				debugIOLog ( "    subRange.dMIN = %lu", subRange.dMIN );
				debugIOLog ( "    subRange.dMAX = %lu", subRange.dMAX );
				debugIOLog ( "    subRange.dRES = %lu", subRange.dRES );
				continue;				
			}
			
			// Add the minimum sample rate no matter what.
			FailWithAction ( NULL == ( sampleRateNumber = OSNumber::withNumber ( subRange.dMIN, SIZEINBITS( sizeof ( UInt32 ) ) ) ), result = kIOReturnError, Exit );
			if ( * sampleRates )
			{
				(* sampleRates)->setObject ( sampleRateNumber );
			}
			else
			{
				* sampleRates = OSArray::withObjects ( ( const OSObject ** ) &sampleRateNumber, 1 );
			}
			
			sampleRateNumber->release ();
			sampleRateNumber = NULL;
			
			// Add the "middle" sample rates if necessary.
			// If dRES is not zero, there should be a total of 1 + ( ( dMAX - dMIN ) / dRES ) sample rates including dMIN and dMAX.
			if ( 0 != subRange.dRES )
			{
				for ( UInt16 sampleRateIndex = 0; sampleRateIndex <  ( ( subRange.dMAX - subRange.dMIN) / subRange.dRES ) - 1; sampleRateIndex++ )
				{
					sampleRate = subRange.dMIN + ( 1 + sampleRateIndex ) * subRange.dRES;
					FailWithAction ( NULL == ( sampleRateNumber = OSNumber::withNumber ( sampleRate, SIZEINBITS( sizeof ( UInt32 ) ) ) ), result = kIOReturnError, Exit );
					(* sampleRates)->setObject ( sampleRateNumber );
					sampleRateNumber->release ();
					sampleRateNumber = NULL;
				}
			}

			// Add the maximum sample rate if necessary.
			if	( subRange.dMAX != subRange.dMIN )
			{
				FailWithAction ( NULL == ( sampleRateNumber = OSNumber::withNumber ( subRange.dMAX, SIZEINBITS( sizeof ( UInt32 ) ) ) ), result = kIOReturnError, Exit );
				(* sampleRates)->setObject ( sampleRateNumber );
				sampleRateNumber->release ();
				sampleRateNumber = NULL;
			}
		}		
	}
	else
	{
		// There is no frequency control. Get the current sample rate only.
		FailIf ( kIOReturnSuccess != ( result = getCurClockSourceSamplingFrequency ( clockSource, &sampleRate, &clockIsValid ) ), Exit );
		FailWithAction ( NULL == ( sampleRateNumber = OSNumber::withNumber ( sampleRate, SIZEINBITS( sizeof ( UInt32 ) ) ) ), result = kIOReturnError, Exit );
		* sampleRates = OSArray::withObjects ( ( const OSObject ** ) &sampleRateNumber, 1 );
		sampleRateNumber->release ();
		sampleRateNumber = NULL;
	}
	
	// All sample rates have been added to the array. We are done.
	
	result = kIOReturnSuccess;
	
Exit:
	debugIOLog ( "- AppleUSBAudioDevice[%p]::getClockSourceSampleRates ( %p, %d ) = 0x%x", this, sampleRates, clockSource, result );
	return result;
}

OSArray * AppleUSBAudioDevice::buildClockGraph ( UInt8 controlInterfaceNum )
{
	OSArray *						allClockPaths = NULL;
	OSArray *						terminalClockEntities = NULL;
	OSArray *						pathsFromClockEntityN = NULL;
	OSArray *						thisPath = NULL;
	OSArray *						thisGroup = NULL;
	OSNumber *						clockIDNum = NULL;
	UInt8							clockID;
	
	debugIOLog ( "+ AppleUSBAudioDevice[%p]::buildClockGraph ( %d )", this, controlInterfaceNum );
	allClockPaths = OSArray::withCapacity ( 1 );
	FailIf ( NULL == allClockPaths, Exit );
	pathsFromClockEntityN = OSArray::withCapacity ( 1 );
	FailIf ( NULL == pathsFromClockEntityN, Exit );
	FailIf ( NULL == ( terminalClockEntities = mConfigDictionary->getTerminalClockEntities ( controlInterfaceNum, 0 ) ), Exit );
	for ( UInt8 clockIndex = 0; clockIndex < terminalClockEntities->getCount (); clockIndex++) 
	{
		FailIf ( NULL == ( clockIDNum = OSDynamicCast ( OSNumber, terminalClockEntities->getObject ( clockIndex ) ) ), Exit );
		clockID = clockIDNum->unsigned8BitValue ();
		debugIOLog ( "? AppleUSBAudioDevice[%p]::buildClockGraph () - Building clock paths from ID %d", this, clockID ); 
		buildClockPath (controlInterfaceNum, clockID, pathsFromClockEntityN, thisPath);
		allClockPaths->setObject ( pathsFromClockEntityN );
		pathsFromClockEntityN->release ();
		pathsFromClockEntityN = OSArray::withCapacity ( 1 );
		FailIf ( NULL == pathsFromClockEntityN, Exit );
	}
	
	// Print clock graph of interest
	char		pathLine[256];
	char		tempString[10];
	
	debugIOLog ( "? AppleUSBAudioDevice[%p]::buildClockGraph ( %d ) - Displaying graph ...", this, controlInterfaceNum, allClockPaths->getCount() );
	for ( UInt8 groupIndex = 0; ( allClockPaths && groupIndex < allClockPaths->getCount () ); groupIndex++ )
	{
		debugIOLog ("   Path Group # %d", groupIndex );
		FailIf ( NULL == ( thisGroup = OSDynamicCast ( OSArray, allClockPaths->getObject ( groupIndex ) ) ), Exit );
		for ( UInt8 pathIndex = 0; pathIndex < thisGroup->getCount(); pathIndex++ )
		{
			* pathLine = '\0';
			sprintf ( tempString, "%2d: ", pathIndex );
			strcat ( pathLine, tempString );
			FailIf ( NULL == ( thisPath = OSDynamicCast ( OSArray, thisGroup->getObject ( pathIndex ) ) ), Exit );
			for ( UInt8 pathItem = 0; pathItem < thisPath->getCount(); pathItem++ )
			{
				FailIf ( NULL == ( clockIDNum = ( OSNumber * ) thisPath->getObject ( pathItem ) ), Exit );
				sprintf ( tempString, "%d ", clockIDNum->unsigned8BitValue () );
				strcat ( pathLine, tempString );
			}
			debugIOLog ( "  %s", pathLine );
		}
	}
	
Exit:
	if ( NULL != pathsFromClockEntityN )
	{
		pathsFromClockEntityN->release();
	}
	
	debugIOLog ("- AppleUSBAudioDevice[%p]::buildClockGraph (%d) = %p", this, controlInterfaceNum, allClockPaths );
	return allClockPaths;
}

OSArray * AppleUSBAudioDevice::buildClockPath ( UInt8 controlInterfaceNum, UInt8 startingUnitID, OSArray * allPaths, OSArray * startingPath ) 
{
	OSArray *						curPath = NULL;
	OSArray *						sourceArray = NULL;
	OSNumber *						arrayNumber = NULL;
	OSNumber *						thisUnitIDNum;
	UInt8							thisUnitID;
	UInt8							numSources;
	UInt8							sourceID;
	UInt8							subType;
	
	debugIOLog ( "+ AppleUSBAudioDevice[%p]::buildClockPath ( %d, %d, %p, %p )", controlInterfaceNum, startingUnitID, allPaths, startingPath );
	FailIf ( NULL == mConfigDictionary, Exit );
	thisUnitID = startingUnitID;
	FailIf ( NULL == ( thisUnitIDNum = OSNumber::withNumber ( thisUnitID, 8 ) ), Exit);
	if ( NULL != startingPath ) 
	{
		curPath = OSArray::withArray ( startingPath );
	}
	if ( NULL == curPath ) 
	{
		curPath = OSArray::withObjects ( ( const OSObject ** ) &thisUnitIDNum, 1 );
	} 
	else 
	{
		curPath->setObject ( thisUnitIDNum );
	}
	thisUnitIDNum->release ();
	thisUnitIDNum = NULL;

	FailIf ( kIOReturnSuccess != mConfigDictionary->getSubType ( &subType, controlInterfaceNum, 0, thisUnitID ), Exit );

	while	(		( 0 != subType )
				&&  ( curPath )
				&&	( USBAUDIO_0200::CLOCK_SOURCE != subType ) )
	{
		if ( USBAUDIO_0200::CLOCK_SELECTOR == subType )						 
		{
			debugIOLog ( "    found clock selector @ ID %d", thisUnitID ); 
			FailIf ( kIOReturnSuccess != mConfigDictionary->getNumSources ( &numSources, controlInterfaceNum, 0, thisUnitID ), Exit );
			debugIOLog ( "    found clock selector %d has %d sources", thisUnitID, numSources ); 			
			FailIf ( kIOReturnSuccess != mConfigDictionary->getSourceIDs ( &sourceArray, controlInterfaceNum, 0, thisUnitID ), Exit );			
			for ( UInt8 sourceIndex = 0; sourceIndex < numSources; sourceIndex++ ) 
			{
				FailIf ( NULL == sourceArray, Exit );
				FailIf ( NULL == ( arrayNumber = OSDynamicCast ( OSNumber, sourceArray->getObject ( sourceIndex ) ) ), Exit);
				buildClockPath ( controlInterfaceNum, arrayNumber->unsigned8BitValue(), allPaths, curPath );
			}
			subType = 0;
		}
		else
		{
			// USBAUDIO_0200::CLOCK_MULTIPLIER:
			debugIOLog ( "    found clock multiplier @ ID %d", thisUnitID );
			if ( 1 != curPath->getCount () )
			{
				// We haven't added this yet. We should do so.
				thisUnitIDNum = OSNumber::withNumber ( thisUnitID, 8 );
				if ( NULL != thisUnitIDNum ) 
				{
					curPath->setObject ( thisUnitIDNum );
					thisUnitIDNum->release ();
					thisUnitIDNum = NULL;
				}
			}
			
			// Continue down the path.
			FailIf (kIOReturnSuccess != mConfigDictionary->getSourceID ( &sourceID, controlInterfaceNum, 0, thisUnitID ), Exit );
			thisUnitID = sourceID;
			FailIf ( kIOReturnSuccess != mConfigDictionary->getSubType ( &subType, controlInterfaceNum, 0, thisUnitID ), Exit );
		}
	} // while ( subType != 0 )

	if ( USBAUDIO_0200::CLOCK_SOURCE == subType )
	{
		debugIOLog ( "    found clock source @ ID %d", thisUnitID );
		// [rdar://4952145] This is the end of a clock path. We should set it now.
		debugIOLog (  "    adding path..." );
		allPaths->setObject ( curPath );
	}

Exit:
	if ( curPath )
	{
		curPath->release ();
		curPath = NULL;
	}
	debugIOLog ( "- AppleUSBAudioDevice[%p]::buildClockPath () = %p", curPath );
	return curPath;
}

OSArray * AppleUSBAudioDevice::BuildConnectionGraph (UInt8 controlInterfaceNum) 
{
	OSArray *						allOutputTerminalPaths = NULL;
	OSArray *						pathsFromOutputTerminalN = NULL;
	OSArray *						thisPath = NULL;
	UInt8							terminalIndex;
	UInt8							numTerminals;
	UInt8							terminalID;

	debugIOLog ("+ AppleUSBAudioDevice[%p]::BuildConnectionGraph (%d)", this, controlInterfaceNum);
	allOutputTerminalPaths = OSArray::withCapacity (1);
	FailIf (NULL == allOutputTerminalPaths, Exit);
	pathsFromOutputTerminalN = OSArray::withCapacity (1);
	FailIf (NULL == pathsFromOutputTerminalN, Exit);
	FailIf (kIOReturnSuccess != mConfigDictionary->getNumOutputTerminals (&numTerminals, controlInterfaceNum, 0), Exit);
	for (terminalIndex = 0; terminalIndex < numTerminals; terminalIndex++) 
	{
		FailIf (kIOReturnSuccess != mConfigDictionary->getIndexedOutputTerminalID (&terminalID, controlInterfaceNum, 0, terminalIndex), Exit);
		BuildPath (controlInterfaceNum, terminalID, pathsFromOutputTerminalN, thisPath);
		allOutputTerminalPaths->setObject (pathsFromOutputTerminalN);
		pathsFromOutputTerminalN->release ();
		pathsFromOutputTerminalN = OSArray::withCapacity (1);
		FailIf (NULL == pathsFromOutputTerminalN, Exit);
	}

Exit:
	if (NULL != pathsFromOutputTerminalN)
	{
		pathsFromOutputTerminalN->release();
	}
	
	debugIOLog ("- AppleUSBAudioDevice[%p]::BuildConnectionGraph (%d) = %p", this, controlInterfaceNum, allOutputTerminalPaths);
	return allOutputTerminalPaths;
}

OSArray * AppleUSBAudioDevice::BuildPath (UInt8 controlInterfaceNum, UInt8 startingUnitID, OSArray * allPaths, OSArray * startingPath) {
	OSArray *						curPath = NULL;
	OSArray *						tempPath = NULL;
	OSArray *						sourceArray = NULL;
	OSObject *						arrayObject = NULL;
	OSNumber *						arrayNumber = NULL;
	OSNumber *						thisUnitIDNum;
	UInt8							unitID;	
	// UInt8 *							sourceIDs = NULL;
	UInt32							i;
	UInt8							thisUnitID;
	UInt8							numSources;
	UInt8							sourceID;
	UInt8							startingSubType;
	UInt8							subType;
	UInt16							adcVersion;

	FailIf (kIOReturnSuccess != mConfigDictionary->getADCVersion (&adcVersion), Exit);
	
	thisUnitID = startingUnitID;
	FailIf (NULL == (thisUnitIDNum = OSNumber::withNumber (thisUnitID, 8)), Exit);
	if (NULL != startingPath) 
	{
		curPath = OSArray::withArray (startingPath);
	}
	if (NULL == curPath) 
	{
		curPath = OSArray::withObjects ((const OSObject **)&thisUnitIDNum, 1);
	} 
	else 
	{
		curPath->setObject (thisUnitIDNum);
	}
	thisUnitIDNum->release ();
	thisUnitIDNum = NULL;

	FailIf (kIOReturnSuccess != mConfigDictionary->getSubType (&subType, controlInterfaceNum, 0, thisUnitID), Exit);

	while (INPUT_TERMINAL != subType && subType != 0) 
	{
		if (((kAUAUSBSpec1_0 == adcVersion) && ((MIXER_UNIT == subType) || (SELECTOR_UNIT == subType) || (EXTENSION_UNIT == subType) || (PROCESSING_UNIT == subType))) ||
			((kAUAUSBSpec2_0 == adcVersion) && ((USBAUDIO_0200::MIXER_UNIT == subType) || (USBAUDIO_0200::SELECTOR_UNIT == subType) || (USBAUDIO_0200::EXTENSION_UNIT == subType) || (USBAUDIO_0200::PROCESSING_UNIT == subType))))						 
		{
			FailIf (kIOReturnSuccess != mConfigDictionary->getNumSources (&numSources, controlInterfaceNum, 0, thisUnitID), Exit);
			FailIf (kIOReturnSuccess != mConfigDictionary->getSourceIDs (&sourceArray, controlInterfaceNum, 0, thisUnitID), Exit);
			tempPath = OSArray::withArray (curPath);
			for (i = 0; i < numSources; i++) 
			{
				if (NULL == curPath) 
				{
					curPath = OSArray::withCapacity (1);
				}
				FailIf (NULL == curPath, Exit);
				FailIf (NULL == (arrayObject = sourceArray->getObject (i)), Exit);
				FailIf (NULL == (arrayNumber = OSDynamicCast (OSNumber, arrayObject)), Exit);
				curPath = BuildPath (controlInterfaceNum, arrayNumber->unsigned8BitValue(), allPaths, tempPath);
				if (curPath && curPath->getCount ()) 
				{
					thisUnitIDNum = OSDynamicCast (OSNumber, curPath->getLastObject ());
					FailIf (NULL == thisUnitIDNum, Exit);
					unitID = thisUnitIDNum->unsigned8BitValue ();
					FailIf (kIOReturnSuccess != mConfigDictionary->getSubType (&subType, controlInterfaceNum, 0, unitID), Exit);
					if (unitID && subType == INPUT_TERMINAL) 
					{
						allPaths->setObject (curPath);
					}
				}
				if (curPath) 
				{
					curPath->release ();
					curPath = NULL;
				}
			}
			tempPath->release ();
			subType = 0;
		}
		else
		{
			// OUTPUT_TERMINAL, FEATURE_UNIT, EFFECT_UNIT:
			FailIf (kIOReturnSuccess != mConfigDictionary->getSourceID (&sourceID, controlInterfaceNum, 0, thisUnitID), Exit);
			thisUnitID = sourceID;
			thisUnitIDNum = OSNumber::withNumber (thisUnitID, 8);
			if (NULL != thisUnitIDNum) 
			{
				curPath->setObject (thisUnitIDNum);
				thisUnitIDNum->release ();
				thisUnitIDNum = NULL;
			}
			FailIf (kIOReturnSuccess != mConfigDictionary->getSubType (&subType, controlInterfaceNum, 0, thisUnitID), Exit);
			FailIf (kIOReturnSuccess != mConfigDictionary->getSubType (&startingSubType, controlInterfaceNum, 0, startingUnitID), Exit);
			if (subType == INPUT_TERMINAL && startingSubType == OUTPUT_TERMINAL) 
			{
				allPaths->setObject (curPath);
			}
		}
	} // while (INPUT_TERMINAL != subType && subType != 0)

Exit:
	return curPath;
}

char * AppleUSBAudioDevice::TerminalTypeString (UInt16 terminalType) 
{
	char *					terminalTypeString;

	switch (terminalType) {
		case 0x101:											terminalTypeString = "USB streaming";									break;
	#if LOCALIZABLE
		case INPUT_UNDEFINED:								terminalTypeString = "StringInputUndefined";							break;
		case INPUT_MICROPHONE:								terminalTypeString = "StringMicrophone";								break;
		case INPUT_DESKTOP_MICROPHONE:						terminalTypeString = "StringDesktopMicrophone";							break;
		case INPUT_PERSONAL_MICROPHONE:						terminalTypeString = "StringPersonalMicrophone";						break;
		case INPUT_OMNIDIRECTIONAL_MICROPHONE:				terminalTypeString = "StringOmnidirectionalMicrophone";					break;
		case INPUT_MICROPHONE_ARRAY:						terminalTypeString = "StringMicrophoneArray";							break;
		case INPUT_PROCESSING_MICROPHONE_ARRAY:				terminalTypeString = "StringProcessingMicrophoneArray";					break;
		case INPUT_MODEM_AUDIO:								terminalTypeString = "StringModemAudio";								break;
		case OUTPUT_UNDEFINED:								terminalTypeString = "StringOutputUndefined";							break;
		case OUTPUT_SPEAKER:								terminalTypeString = "StringSpeaker";									break;
		case OUTPUT_HEADPHONES:								terminalTypeString = "StringHeadphones";								break;
		case OUTPUT_HEAD_MOUNTED_DISPLAY_AUDIO:				terminalTypeString = "StringHeadMountedDisplayAudio";					break;
		case OUTPUT_DESKTOP_SPEAKER:						terminalTypeString = "StringDesktopSpeaker";							break;
		case OUTPUT_ROOM_SPEAKER:							terminalTypeString = "StringRoomSpeaker";								break;
		case OUTPUT_COMMUNICATION_SPEAKER:					terminalTypeString = "StringCommunicationSpeaker";						break;
		case OUTPUT_LOW_FREQUENCY_EFFECTS_SPEAKER:			terminalTypeString = "StringLowFrequencyEffectsSpeaker";				break;
		case BIDIRECTIONAL_UNDEFINED:						terminalTypeString = "StringBidirectionalUndefined";					break;
		case BIDIRECTIONAL_HANDSET:							terminalTypeString = "StringBidirectionalHandset";						break;
		case BIDIRECTIONAL_HEADSET:							terminalTypeString = "StringBidirectionalHeadset";						break;
		case BIDIRECTIONAL_SPEAKERPHONE_NO_ECHO_REDX:		terminalTypeString = "StringBidirectionalSpeakerphoneNoEchoRedx";		break;
		case BIDIRECTIONAL_ECHO_SUPPRESSING_SPEAKERPHONE:	terminalTypeString = "StringBidirectionalEchoSuppressingSpeakerphone";	break;
		case BIDIRECTIONAL_ECHO_CANCELING_SPEAKERPHONE:		terminalTypeString = "StringBidirectionalEchoCancelingSpeakerphone";	break;
		case TELEPHONY_UNDEFINED:							terminalTypeString = "StringTelephoneUndefined";						break;
		case TELEPHONY_PHONE_LINE:							terminalTypeString = "StringTelephoneLine";								break;
		case TELEPHONY_TELEPHONE:							terminalTypeString = "StringTelephone";									break;
		case TELEPHONY_DOWN_LINE_PHONE:						terminalTypeString = "StringDownLinePhone";								break;
		case EXTERNAL_UNDEFINED:							terminalTypeString = "StringExternalUndefined";							break;
		case EXTERNAL_ANALOG_CONNECTOR:						terminalTypeString = "StringExternalAnalogConnector";					break;
		case EXTERNAL_DIGITAL_AUDIO_INTERFACE:				terminalTypeString = "StringExternalDigitalAudioInterface";				break;
		case EXTERNAL_LINE_CONNECTOR:						terminalTypeString = "StringExternalLineConnector";						break;
		case EXTERNAL_LEGACY_AUDIO_CONNECTOR:				terminalTypeString = "StringExternalLegacyAudioConnector";				break;
		case EXTERNAL_SPDIF_INTERFACE:						terminalTypeString = "StringExternalSPDIFInterface";					break;
		case EXTERNAL_1394_DA_STREAM:						terminalTypeString = "StringExternal1394DAStream";						break;
		case EXTERNAL_1394_DV_STREAM_SOUNDTRACK:			terminalTypeString = "StringExternal1394DVStreamSoundtrack";			break;
		case EMBEDDED_UNDEFINED:							terminalTypeString = "StringEmbeddedUndefined";							break;
		case EMBEDDED_LEVEL_CALIBRATION_NOISE_SOURCE:		terminalTypeString = "StringEmbeddedLevelCalibrationNoiseSource";		break;
		case EMBEDDED_EQUALIZATION_NOISE:					terminalTypeString = "StringEmbeddedEqualizationNoise";					break;
		case EMBEDDED_CD_PLAYER:							terminalTypeString = "StringEmbeddedCDPlayer";							break;
		case EMBEDDED_DAT:									terminalTypeString = "StringEmbeddedDAT";								break;
		case EMBEDDED_DCC:									terminalTypeString = "StringEmbeddedDCC";								break;
		case EMBEDDED_MINIDISK:								terminalTypeString = "StringEmbeddedMiniDisc";							break;
		case EMBEDDED_ANALOG_TAPE:							terminalTypeString = "StringEmbeddedAnalogTape";						break;
		case EMBEDDED_PHONOGRAPH:							terminalTypeString = "StringEmbeddedPhonograph";						break;
		case EMBEDDED_VCR_AUDIO:							terminalTypeString = "StringEmbeddedVCRAudio";							break;
		case EMBEDDED_VIDEO_DISC_AUDIO:						terminalTypeString = "StringEmbeddedVideoDiscAudio";					break;
		case EMBEDDED_DVD_AUDIO:							terminalTypeString = "StringEmbeddedDVDAudio";							break;
		case EMBEDDED_TV_TUNER_AUDIO:						terminalTypeString = "StringEmbeddedTVTunerAudio";						break;
		case EMBEDDED_SATELLITE_RECEIVER_AUDIO:				terminalTypeString = "StringEmbeddedSatelliteReceiverAudio";			break;
		case EMBEDDED_CABLE_TUNER_AUDIO:					terminalTypeString = "StringEmbeddedCableTunerAudio";					break;
		case EMBEDDED_DSS_AUDIO:							terminalTypeString = "StringEmbeddedDSSAudio";							break;
		case EMBEDDED_RADIO_RECEIVER:						terminalTypeString = "StringEmbeddedRadioReceiver";						break;
		case EMBEDDED_RADIO_TRANSMITTER:					terminalTypeString = "StringEmbeddedRadioTransmitter";					break;
		case EMBEDDED_MULTITRACK_RECORDER:					terminalTypeString = "StringEmbeddedMultitrackRecorder";				break;
		case EMBEDDED_SYNTHESIZER:							terminalTypeString = "StringEmbeddedSynthesizer";						break;
		default:											terminalTypeString = "StringUnknown";									break;
	#else
		case INPUT_UNDEFINED:								terminalTypeString = "InputUndefined";									break;
		case INPUT_MICROPHONE:								terminalTypeString = "Microphone";										break;
		case INPUT_DESKTOP_MICROPHONE:						terminalTypeString = "Desktop Microphone";								break;
		case INPUT_PERSONAL_MICROPHONE:						terminalTypeString = "Personal Microphone";								break;
		case INPUT_OMNIDIRECTIONAL_MICROPHONE:				terminalTypeString = "Omnidirectional Microphone";						break;
		case INPUT_MICROPHONE_ARRAY:						terminalTypeString = "Microphone Array";								break;
		case INPUT_PROCESSING_MICROPHONE_ARRAY:				terminalTypeString = "Processing Microphone Array";						break;
		case INPUT_MODEM_AUDIO:								terminalTypeString = "Modem Audio";										break;
		case OUTPUT_UNDEFINED:								terminalTypeString = "Output Undefined";								break;
		case OUTPUT_SPEAKER:								terminalTypeString = "Speaker";											break;
		case OUTPUT_HEADPHONES:								terminalTypeString = "Headphones";										break;
		case OUTPUT_HEAD_MOUNTED_DISPLAY_AUDIO:				terminalTypeString = "Head Mounted Display Audio";						break;
		case OUTPUT_DESKTOP_SPEAKER:						terminalTypeString = "Desktop Speaker";									break;
		case OUTPUT_ROOM_SPEAKER:							terminalTypeString = "Room Speaker";									break;
		case OUTPUT_COMMUNICATION_SPEAKER:					terminalTypeString = "Communication Speaker";							break;
		case OUTPUT_LOW_FREQUENCY_EFFECTS_SPEAKER:			terminalTypeString = "Low Frequency Effects Speaker";					break;
		case BIDIRECTIONAL_UNDEFINED:						terminalTypeString = "Bidirectional Undefined";							break;
		case BIDIRECTIONAL_HANDSET:							terminalTypeString = "Bidirectional Handset";							break;
		case BIDIRECTIONAL_HEADSET:							terminalTypeString = "Bidirectional Headset";							break;
		case BIDIRECTIONAL_SPEAKERPHONE_NO_ECHO_REDX:		terminalTypeString = "Bidirectional Speakerphone No Echo Redx";			break;
		case BIDIRECTIONAL_ECHO_SUPPRESSING_SPEAKERPHONE:	terminalTypeString = "Bidirectional Echo Suppressing Speakerphone";		break;
		case BIDIRECTIONAL_ECHO_CANCELING_SPEAKERPHONE:		terminalTypeString = "Bidirectional Echo Canceling Speakerphone";		break;
		case TELEPHONY_UNDEFINED:							terminalTypeString = "Telephone Undefined";								break;
		case TELEPHONY_PHONE_LINE:							terminalTypeString = "Telephone Line";									break;
		case TELEPHONY_TELEPHONE:							terminalTypeString = "Telephone";										break;
		case TELEPHONY_DOWN_LINE_PHONE:						terminalTypeString = "Down Line Phone";									break;
		case EXTERNAL_UNDEFINED:							terminalTypeString = "External Undefined";								break;
		case EXTERNAL_ANALOG_CONNECTOR:						terminalTypeString = "External Analog Connector";						break;
		case EXTERNAL_DIGITAL_AUDIO_INTERFACE:				terminalTypeString = "External Digital Audio Interface";				break;
		case EXTERNAL_LINE_CONNECTOR:						terminalTypeString = "External Line Connector";							break;
		case EXTERNAL_LEGACY_AUDIO_CONNECTOR:				terminalTypeString = "External Legacy Audio Connector";					break;
		case EXTERNAL_SPDIF_INTERFACE:						terminalTypeString = "External SPDIF Interface";						break;
		case EXTERNAL_1394_DA_STREAM:						terminalTypeString = "External 1394 DA Stream";							break;
		case EXTERNAL_1394_DV_STREAM_SOUNDTRACK:			terminalTypeString = "External 1394 DV Stream Soundtrack";				break;
		case EMBEDDED_UNDEFINED:							terminalTypeString = "Embedded Undefined";								break;
		case EMBEDDED_LEVEL_CALIBRATION_NOISE_SOURCE:		terminalTypeString = "Embedded Level Calibration Noise Source";			break;
		case EMBEDDED_EQUALIZATION_NOISE:					terminalTypeString = "Embedded Equalization Noise";						break;
		case EMBEDDED_CD_PLAYER:							terminalTypeString = "Embedded CD Player";								break;
		case EMBEDDED_DAT:									terminalTypeString = "Embedded DAT";									break;
		case EMBEDDED_DCC:									terminalTypeString = "Embedded DCC";									break;
		case EMBEDDED_MINIDISK:								terminalTypeString = "Embedded Mini Disc";								break;
		case EMBEDDED_ANALOG_TAPE:							terminalTypeString = "Embedded Analog Tape";							break;
		case EMBEDDED_PHONOGRAPH:							terminalTypeString = "Embedded Phonograph";								break;
		case EMBEDDED_VCR_AUDIO:							terminalTypeString = "Embedded VCR Audio";								break;
		case EMBEDDED_VIDEO_DISC_AUDIO:						terminalTypeString = "Embedded Video Disc Audio";						break;
		case EMBEDDED_DVD_AUDIO:							terminalTypeString = "Embedded DVD Audio";								break;
		case EMBEDDED_TV_TUNER_AUDIO:						terminalTypeString = "Embedded TV Tuner Audio";							break;
		case EMBEDDED_SATELLITE_RECEIVER_AUDIO:				terminalTypeString = "Embedded Satellite Receiver Audio";				break;
		case EMBEDDED_CABLE_TUNER_AUDIO:					terminalTypeString = "Embedded Cable Tuner Audio";						break;
		case EMBEDDED_DSS_AUDIO:							terminalTypeString = "Embedded DSS Audio";								break;
		case EMBEDDED_RADIO_RECEIVER:						terminalTypeString = "Embedded Radio Receiver";							break;
		case EMBEDDED_RADIO_TRANSMITTER:					terminalTypeString = "Embedded Radio Transmitter";						break;
		case EMBEDDED_MULTITRACK_RECORDER:					terminalTypeString = "Embedded Multitrack Recorder";					break;
		case EMBEDDED_SYNTHESIZER:							terminalTypeString = "Embedded Synthesizer";							break;
		default:											terminalTypeString = "Unknown";											break;
	#endif
	}

	return terminalTypeString;
}

IOReturn AppleUSBAudioDevice::deviceRequest (IOUSBDevRequestDesc * request, IOUSBCompletion * completion) {
	IOReturn						result;
	UInt32							timeout;
	Boolean							done;

	result = kIOReturnSuccess;
	FailIf (NULL == mInterfaceLock, Exit);
	IORecursiveLockLock (mInterfaceLock);

	if (FALSE == mTerminatingDriver) 
	{
		done = FALSE;
		timeout = 5;
		while (!done && timeout && mControlInterface) 
		{
			result = mControlInterface->DeviceRequest (request, completion);
			if (result != kIOReturnSuccess) 
			{
				timeout--;
				IOSleep (1);
			} 
			else 
			{
				done = TRUE;
			}
		}
    }
	IORecursiveLockUnlock (mInterfaceLock);
	#if LOGDEVICEREQUESTS
	debugIOLog ("? AppleUSBAudioDevice[%p]::deviceRequest (%p, %p) = %lx", this, request, completion, result);
	#endif
Exit:
	return result;
}

IOReturn AppleUSBAudioDevice::deviceRequest (IOUSBDevRequest * request, IOUSBCompletion * completion) {
	IOReturn						result;
	UInt32							timeout;
	Boolean							done;

	result = kIOReturnSuccess;
	FailIf (NULL == mInterfaceLock, Exit);
	IORecursiveLockLock (mInterfaceLock);

	if (FALSE == mTerminatingDriver) 
	{
		done = FALSE;
		timeout = 5;
		while (!done && timeout && mControlInterface) 
		{
			result = mControlInterface->DeviceRequest (request, completion);
			if (result != kIOReturnSuccess) 
			{
				timeout--;
				IOSleep (1);
			} 
			else 
			{
				done = TRUE;
			}
		}
    }
	IORecursiveLockUnlock (mInterfaceLock);
	#if LOGDEVICEREQUESTS
	debugIOLog ("? AppleUSBAudioDevice[%p]::deviceRequest (%p, %p) = %lx", this, request, completion, result);
	#endif
Exit:
	return result;
}

IOReturn AppleUSBAudioDevice::deviceRequest (IOUSBDevRequest *request, AppleUSBAudioDevice * self, IOUSBCompletion *completion) {
	IOReturn						result;
	UInt32							timeout;
	Boolean							done;

	result = kIOReturnSuccess;
	FailIf (NULL == self->mInterfaceLock, Exit);
	IORecursiveLockLock (self->mInterfaceLock);

	if (FALSE == self->mTerminatingDriver) 
	{
		done = FALSE;
		timeout = 5;
		while (!done && timeout && self->mControlInterface) 
		{
			result = self->mControlInterface->DeviceRequest (request, completion);
			if (result != kIOReturnSuccess) 
			{
				timeout--;
				IOSleep (1);
			} 
			else 
			{
				done = TRUE;
			}
		}
	}
	IORecursiveLockUnlock (self->mInterfaceLock);
	#if LOGDEVICEREQUESTS
	debugIOLog ("? AppleUSBAudioDevice[%p]::deviceRequest (%p, %p) = %lx", self, request, completion, result);
	#endif
Exit:
	return result;
}

bool AppleUSBAudioDevice::willTerminate (IOService * provider, IOOptionBits options) {
	debugIOLog ("+ AppleUSBAudioDevice[%p]::willTerminate (%p)", this, provider);

	if (mControlInterface == provider) 
	{
		mTerminatingDriver = TRUE;
	}

	debugIOLog ("- AppleUSBAudioDevice[%p]::willTerminate ()", this);

	return super::willTerminate (provider, options);
}

void AppleUSBAudioDevice::setConfigurationApp (const char *bundleID) {
	setConfigurationApplicationBundle (bundleID);
}

#ifdef DEBUG

void AppleUSBAudioDevice::retain() const
{
//    debugIOLog("AppleUSBAudioDevice(%p)::retain() - rc=%d", this, getRetainCount());
    super::retain();
}

void AppleUSBAudioDevice::release() const
{
//    debugIOLog("AppleUSBAudioDevice(%p)::release() - rc=%d", this, getRetainCount());
	super::release();
}

#endif

// Implemented for rdar://3993906 . Allows matching based on a custom dictionary.
bool AppleUSBAudioDevice::matchPropertyTable(OSDictionary * table, SInt32 *score)
{
	bool		returnValue = false;
	OSObject *	deviceName;
	
	// debugIOLog ("+AppleUSBAudioDevice[%p]::matchPropertyTable (%p, %p)", this, table, score);
	deviceName = table->getObject(kIOAudioDeviceNameKey);
	if (deviceName)
	{
		// This custom dictionary wants the device to have a name.
		if (getProperty (kIOAudioDeviceNameKey))
		{
			// No need to match name; it exists, and that's all that matters
			returnValue = true;
		}
		else
		{
			// Device doesn't have a name yet, so don't match
			returnValue = false;
		}
	}
	else
	{
		// This is our standard matchPropertyTable implementation
		returnValue = super::matchPropertyTable (table, score);
	}
	
	if (    (deviceName)
		 && (returnValue))
	{
		debugIOLog ("? AppleUSBAudioDevice[%p]::matchPropertyTable (%p, %p) = %d (custom dictionary match)", 
					this, table, score, returnValue);
	}
	
	return returnValue;
}

#pragma mark Anchored Time Stamps Methods

#if DEBUGANCHORS
void AppleUSBAudioDevice::accumulateAnchor (UInt64 anchorFrame, AbsoluteTime timeStamp)
{
	UInt32		i;
	
	if (mAnchorFrames[(kAnchorsToAccumulate - 1)] == 0ull)
	{
		// Find the last empty space and store this anchor frame and time stamp
		for ( i =  0; i < kAnchorsToAccumulate ; i++)
		{
			if (0 == mAnchorFrames[i])
			{
				mAnchorFrames[i] = anchorFrame;
				mAnchorTimes[i] = timeStamp;
				break;
			}
		}
	}
	
	if (mAnchorFrames[(kAnchorsToAccumulate - 1)] != 0ull)
	{
		UInt64	time_nanos;
		// Maximum number of anchors has already been accumulated.
		debugIOLog ("? AppleUSBAudioDevice::accumulateAnchor () - Frame # %d accumulated.", kAnchorsToAccumulate);
		for (i = 0; i < kAnchorsToAccumulate ; i++)
		{
			absolutetime_to_nanoseconds (mAnchorTimes[i], &time_nanos);
			debugIOLog ("  - %llu \t %llu", mAnchorFrames[i], time_nanos);
		}
		
		// Reset data
		for (i = 0; i < kAnchorsToAccumulate ; i++)
		{
			mAnchorFrames[i] = 0ull;
		}
	}
}
#endif

IOReturn AppleUSBAudioDevice::getAnchorFrameAndTimeStamp (UInt64 *frame, AbsoluteTime *time) {
	AbsoluteTime	finishTime;
	AbsoluteTime	offset;
	AbsoluteTime	curTime;
	AbsoluteTime	thisTime;
	UInt64			thisFrame;
	IOReturn		result = kIOReturnError;
	
	FailIf (NULL == mControlInterface, Exit);
	if (NULL == frame)
	{
		frame = &mNewReferenceUSBFrame;
	}
	if (NULL == time)
	{
		time = &mNewReferenceWallTime;
	}
	nanoseconds_to_absolutetime (1100000, &offset);
	clock_get_uptime (&finishTime);
	ADD_ABSOLUTETIME (&finishTime, &offset);	// finishTime is when we timeout
	
	thisFrame = mControlInterface->GetDevice()->GetBus()->GetFrameNumber ();
	// spin until the frame changes
	do
	{
		clock_get_uptime (&curTime);
	} while (		(mControlInterface)
				&&	(thisFrame == mControlInterface->GetDevice()->GetBus()->GetFrameNumber ()) 
				&&	(CMP_ABSOLUTETIME (&finishTime, &curTime) > 0));

	clock_get_uptime (&thisTime);
	FailIf (CMP_ABSOLUTETIME (&finishTime, &curTime) < 0, Exit);		// if we timed out
	*frame = ++thisFrame;
	*time = thisTime;

	result = kIOReturnSuccess;
Exit:
	return result;
}

IOReturn AppleUSBAudioDevice::getFrameAndTimeStamp (UInt64 *frame, AbsoluteTime *time)
{
	IOReturn	result = kIOReturnError;
	do
	{
		FailIf (NULL == mControlInterface, Exit);
		*frame = mControlInterface->GetDevice()->GetBus()->GetFrameNumber ();
		clock_get_uptime (time);
	} while	(		(mControlInterface)
				&&	(*frame != mControlInterface->GetDevice()->GetBus()->GetFrameNumber ()));
	
	result = kIOReturnSuccess;
Exit:
	return result;
}

UInt64 AppleUSBAudioDevice::getWallTimeInNanos (void)
{
	AbsoluteTime	time;
	UInt64			timeInNanos;
	
	clock_get_uptime (&time);
	absolutetime_to_nanoseconds (time, &timeInNanos);
	
	return timeInNanos;
}

// First order recursive filter for eliminating jitter from time measurements
UInt64 AppleUSBAudioDevice::jitterFilter (UInt64 invCoefficient, UInt64 prev, UInt64 curr) 
{
	UInt64 filteredValue;
	
	if (0llu != invCoefficient)
	{ 
		// Execute a low pass filter on the new rate
		filteredValue = curr + (invCoefficient - 1) * prev;
		filteredValue += invCoefficient / 2;
		filteredValue /= invCoefficient;
		// debugIOLog ("filtered value () = %llu", filteredValue);
	}
	else
	{
		filteredValue = curr;
	}
	return filteredValue;
}

// [rdar://5165798] At initHardware() time and if anything goes wrong, this is how we reset the timer code.
void AppleUSBAudioDevice::resetRateTimer ()
{
	UInt64		time_nanos = 0ull;
	
	mWallTimePerUSBCycle = 1000000ull * kWallTimeExtraPrecision;
	// We should get a new anchor immediately.
	if ( kIOReturnSuccess != getAnchorFrameAndTimeStamp (&mNewReferenceUSBFrame, &mNewReferenceWallTime) )
	{
		debugIOLog ( "! AppleUSBAudioDevice::resetRateTimer () - could not update anchor!" );
	}
	else
	{
		absolutetime_to_nanoseconds ( mNewReferenceWallTime, &time_nanos);
		debugIOLog ( "! AppleUSBAudioDevice::resetRateTimer () - mNewReferenceUSBFrame = %llu, mNewReferenceWallTime (ns) = %llu", mNewReferenceUSBFrame, time_nanos );
	}
}

void AppleUSBAudioDevice::updateWallTimePerUSBCycle () 
{
	UInt64			currentUSBFrame;
	UInt64			newWallTimePerUSBCycle;
	AbsoluteTime	time;
	UInt64			time_nanos;
	
	#if DEBUGTIMER
		debugIOLog ("+ AppleUSBAudioDevice::updateWallTimePerUSBCycle ()");
	#endif
	FailIf (NULL == mControlInterface, Exit);
	
	// Get wall time for the current USB frame
	FailIf (kIOReturnSuccess != getFrameAndTimeStamp (&currentUSBFrame, &time), Exit);
	
	if (0ull == mNewReferenceUSBFrame)
	{
		// Get first reference frame and time
		if (kIOReturnSuccess != getAnchorFrameAndTimeStamp (&mNewReferenceUSBFrame, &mNewReferenceWallTime))
		{
			// Just take these values for now since we couldn't get our anchor
			debugIOLog ("! AppleUSBAudioDevice[%p]::updateWallTimePerUSBCycle () - Couldn't get a solid first anchor!", this);
			mNewReferenceUSBFrame = currentUSBFrame;
			mNewReferenceWallTime = time;
		}
		debugIOLog ("? AppleUSBAudioDevice[%p]::updateWallTimePerUSBCycle () - NOTICE: reference frame = %llu", this, mNewReferenceUSBFrame);
	}
	else
	{
		// Convert current time to nanoseconds
		absolutetime_to_nanoseconds (time, &time_nanos);
	}
	
	if (0ull == mLastUSBFrame)
	{
		// Initialize last reference frame and time
		debugIOLog ("? AppleUSBAudioDevice[%p]::updateWallTimePerUSBCycle () - NOTICE: initializing last USB frame and last wall time", this);
		mLastUSBFrame = mNewReferenceUSBFrame;
		absolutetime_to_nanoseconds (mNewReferenceWallTime, &mLastWallTime_nanos);
	}
	else
	{
		// Compute new slope
		FailIf (currentUSBFrame == mLastUSBFrame, Exit);
		newWallTimePerUSBCycle = (time_nanos - mLastWallTime_nanos) * kWallTimeExtraPrecision / (currentUSBFrame - mLastUSBFrame);
		// debugIOLog ("mWallTimePerUSBCycle = %llu, newWallTimePerUSBCycle = %llu", mWallTimePerUSBCycle, newWallTimePerUSBCycle);
		
		if (0ull == mWallTimePerUSBCycle)
		{
			// This is our first estimate. Just assign it
			mWallTimePerUSBCycle = newWallTimePerUSBCycle;
			debugIOLog ("! NOTICE: Initializing mWallTimePerUSBCycle = %llu", mWallTimePerUSBCycle);
		}
		else
		{
			// Need to filter and update mWallTimePerUSBCycle
			UInt64 a = 1024;	// (1 / filterCoefficient)
			
			mWallTimePerUSBCycle = jitterFilter (a, mWallTimePerUSBCycle, newWallTimePerUSBCycle);
			#if LOGWALLTIMEPERUSBCYCLE
			debugIOLog ("? AppleUSBAudioDevice[%p]::updateWallTimePerUSBCycle - mWallTimePerUSBCycle * kExtraPrecision = %llu", this, mWallTimePerUSBCycle);
			#endif
		}
		
		UInt64 max = kMaxWallTimePerUSBCycle * kWallTimeExtraPrecision;
		UInt64 min = kMinWallTimePerUSBCycle * kWallTimeExtraPrecision;
		
		// [rdar://5165798]
		if (		( mWallTimePerUSBCycle > max )
				||	( mWallTimePerUSBCycle < min ) )
		{
			if ( mWallTimePerUSBCycle > max )
			{
				debugIOLog ( "! AppleUSBAudioDevice::updateWallTimePerUSBCycle () - mWallTimePerUSBCycle = %llu > %llu", mWallTimePerUSBCycle, max );
			}
			else
			{
				debugIOLog ( "! AppleUSBAudioDevice::updateWallTimePerUSBCycle () - mWallTimePerUSBCycle = %llu < %llu", mWallTimePerUSBCycle, min );
			}
			// Something has altered our USB frame rate vs. wall time correlation (power state change?). We should reset the rate timer.
			IOLog ( "WARNING: AppleUSBAudio has detected that clock_get_uptime () value changed radically from previous values\n" );
			resetRateTimer();
		}

		
		// Update last values
		mLastUSBFrame = currentUSBFrame;
		mLastWallTime_nanos = time_nanos;
	}
	
Exit:
	#if DEBUGTIMER
		debugIOLog ("- AppleUSBAudioDevice::updateWallTimePerUSBCycle ()");
	#endif
	return;
}

void AppleUSBAudioDevice::TimerAction (OSObject * owner, IOTimerEventSource * sender) 
{
	AppleUSBAudioDevice *	self;
	
	#if DEBUGTIMER
		debugIOLog ("+ AppleUSBAudioDevice::TimerAction (%p, %p)", owner, sender);
	#endif
	FailIf (NULL == owner, Exit);
	self = (AppleUSBAudioDevice *) owner;
	FailIf (NULL == self, Exit);
	self->doTimerAction ( sender );
Exit:
	#if DEBUGTIMER
		debugIOLog ("- AppleUSBAudioDevice::TimerAction ()");
	#endif
	return;
}

void AppleUSBAudioDevice::doTimerAction (IOTimerEventSource * timer) 
{
	// This method updates our running wall time per USB cycle every kRefreshInterval ms.
	// After kRefreshCount updates, we re-anchor our reference frame and time to avoid accumulating error.
	
	// This timer thread may also be used to perform routine watchdog-type events.
	
	#if DEBUGTIMER
		debugIOLog ("+ AppleUSBAudioDevice::doTimerAction (%p)", timer);
	#endif
	FailIf (NULL == timer, Exit);
	
	updateWallTimePerUSBCycle ();
	mAnchorResetCount++;
	
	// Perform any watchdog-type events here.
	if (mFailingAudioEngine)
	{
		debugIOLog ("! AppleUSBAudioDevice[%p]::doTimerAction () - Detected failing audio engine (%p)! Performing emergency format change.", this, mFailingAudioEngine);
		// Attempt to synchronize the input and output sample rates.
		formatChangeController (mFailingAudioEngine, NULL, NULL, NULL);
		mFailingAudioEngine = NULL;
		setSingleSampleRateDevice (true);
	}
	else if (		( mEngineToRestart )
				&&	( mEngineToRestart->mUSBStreamRunning ) )
	{
		// [rdar://5355808] If AppleUSBAudioEngine::CoalesceInputSamples () gets more than a USB frame's worth of samples behind, we need to restart the engine.
		debugIOLog ( "! AppleUSBAudioDevice[%p]::doTimerAction () - Restarting engine %p", this, mEngineToRestart );
		mEngineToRestart->pauseAudioEngine();
		mEngineToRestart->resumeAudioEngine();
		mEngineToRestart = NULL;
	}
	
	if (mShouldAttemptDeviceRecovery)
	{
		attemptDeviceRecovery ();
		mShouldAttemptDeviceRecovery = false;
	}
	
	if ( mAnchorResetCount >= kRefreshCount)
	{
		// re-anchor our reference frame and time
		FailIf (NULL == mControlInterface, Exit);
		if (kIOReturnSuccess == getAnchorFrameAndTimeStamp (&mNewReferenceUSBFrame, &mNewReferenceWallTime))
		{
			#if DEBUGTIMESTAMPS
			UInt64	newReferenceWallTime_nanos;
			absolutetime_to_nanoseconds (mNewReferenceWallTime, &newReferenceWallTime_nanos);
			debugIOLog ("? AppleUSBAudioDevice::doTimerAction () - New anchor! mNewReferenceUSBFrame = %llu, mNewReferenceWallTime = %llu", mNewReferenceUSBFrame, newReferenceWallTime_nanos);
			#endif
			#if DEBUGANCHORS
			accumulateAnchor (mNewReferenceUSBFrame, mNewReferenceWallTime);
			#endif
		}
		else
		{
			#if DEBUGTIMESTAMPS
			debugIOLog ("! AppleUSBAudioDevice::doTimerAction () - ERROR: Couldn't get new anchor! Keeping old anchor ...\n");
			#endif
		}
		
		// reset the counter
		mAnchorResetCount = 0;
	}
	
	// Schedule the next anchor frame and time update
	if (timer)
	{
		timer->setTimeoutMS ( kRefreshInterval );
	}
Exit:
	#if DEBUGTIMER
		debugIOLog ("- AppleUSBAudioDevice::doTimerAction ()");
	#endif
	return;
}

#pragma mark Format Change Methods


UInt32 AppleUSBAudioDevice::formatChangeController (IOAudioEngine *audioEngine, IOAudioStream *audioStream, const IOAudioStreamFormat *newFormat, const IOAudioSampleRate *newSampleRate)
{
	AppleUSBAudioEngine *			thisAudioEngine = NULL;
	AppleUSBAudioEngine *			otherAudioEngine = NULL;
	IOAudioStream *					thisStream = NULL;
	IOAudioStream *					otherStream = NULL;
	const IOAudioStreamFormat *		thisFormat;
	const IOAudioStreamFormat *		otherFormat;
	const IOAudioSampleRate *		thisSampleRate;
	const IOAudioSampleRate *		otherSampleRate;
	UInt32							result = kAUAFormatChangeError;
	IOReturn						formatChangeReturnCode = kIOReturnError;
	UInt8							altSetting;
	bool							mustMatchFormats;
	bool							enginesPaused = false;
	Boolean							otherEngineNeedSampleRateChange = false;
	
	debugIOLog ("+ AppleUSBAudioDevice[%p]::formatChangeController (%p, %p, %p, %p)", this, audioEngine, audioStream, newFormat, newSampleRate);
	
	thisAudioEngine = (AppleUSBAudioEngine *) audioEngine;
	
	// [rdar://4801012]
	if ( IP_VERSION_02_00 == mControlInterface->GetInterfaceProtocol() )
	{
		if ( ( newFormat ) && ( newSampleRate ) )
		{
			getOptimalClockPath ( thisAudioEngine, (UInt8)(newFormat->fDriverTag >> 16), (UInt8)(newFormat->fDriverTag), newSampleRate->whole, &otherEngineNeedSampleRateChange );
		}
	}
	
	mustMatchFormats = (		(		( mRegisteredEngines) 
									&&	( 2 == mRegisteredEngines->getCount () )
									&&	(		( true == mSingleSampleRateDevice )
											||	( NULL == audioStream ) ) )
							||  ( true == otherEngineNeedSampleRateChange ) );
	
	if (mustMatchFormats)
	{
		debugIOLog ("? AppleUSBAudioDevice[%p]::formatChangeController () - Attempting to match this format with the format for the other stream interface.", this);
		result = kAUAFormatChangeForceFailure;
		
		if (NULL == thisAudioEngine)
		{
			// Get both engines.
			FailIf (kIOReturnSuccess != getBothEngines (&thisAudioEngine, &otherAudioEngine), Exit);
		}
		else
		{
			// Just get the other engine.
			otherAudioEngine = otherEngine (thisAudioEngine);
		}
		FailIf (NULL == thisAudioEngine, Exit);
		FailIf (NULL == otherAudioEngine, Exit);
		
		thisAudioEngine->pauseAudioEngine ();
		otherAudioEngine->pauseAudioEngine ();
		enginesPaused = true;
		
		// Get formats and other sample rate.
		thisStream = thisAudioEngine->mMainStream;
		otherStream = otherAudioEngine->mMainStream;
		FailIf (NULL == thisStream, Exit);
		FailIf (NULL == otherStream, Exit);
		thisFormat = thisStream->getFormat ();
		otherFormat = otherStream->getFormat ();
		FailIf (NULL == thisFormat, Exit);
		FailIf (NULL == otherFormat, Exit);
		thisSampleRate = thisAudioEngine->getSampleRate ();
		otherSampleRate = otherAudioEngine->getSampleRate ();
		FailIf (NULL == thisSampleRate, Exit);
		FailIf (NULL == otherSampleRate, Exit);
		
		// Log what we have so far.
		debugIOLog ("\n");
		debugIOLog ("-------------------- BEFORE --------------------");
		debugIOLog ("? AppleUSBAudioDevice[%p]::formatChangeController () - engine %p (interface %d, alternateSetting %d) info:", this, thisAudioEngine, thisAudioEngine->mInterfaceNumber, thisAudioEngine->mAlternateSettingID);
		debugIOLog ("    thisFormat = %p", thisFormat);
		debugIOLog ("        fNumChannels = %d", thisFormat->fNumChannels);
		debugIOLog ("        fBitDepth = %d", thisFormat->fBitDepth);
		debugIOLog ("        fDriverTag = 0x%x", thisFormat->fDriverTag);
		debugIOLog ("    thisSampleRate->whole = %lu", thisSampleRate->whole);
		debugIOLog ("\n");
		debugIOLog ("? AppleUSBAudioDevice[%p]::formatChangeController () - engine %p (interface %d, alternateSetting %d) info:", this, otherAudioEngine, otherAudioEngine->mInterfaceNumber, otherAudioEngine->mAlternateSettingID);
		debugIOLog ("    otherFormat = %p", otherFormat);
		debugIOLog ("        fNumChannels = %d", otherFormat->fNumChannels);
		debugIOLog ("        fBitDepth = %d", otherFormat->fBitDepth);
		debugIOLog ("        fDriverTag = 0x%x", otherFormat->fDriverTag);
		debugIOLog ("    otherSampleRate->whole = %lu", otherSampleRate->whole);
		debugIOLog ("\n");
		debugIOLog (" AppleUSBAudioDevice[%p]::formatChangeController () - newFormat = %p", this, newFormat);
		if (newFormat)
		{
			debugIOLog ("        fNumChannels = %d", newFormat->fNumChannels);
			debugIOLog ("        fBitDepth = %d", newFormat->fBitDepth);
			debugIOLog ("        fDriverTag = 0x%x", newFormat->fDriverTag);
		}
		debugIOLog ( " AppleUSBAudioDevice[%p]::formatChangeController () - newSampleRate = %p", this, newSampleRate );
		if ( newSampleRate )
		{
			debugIOLog ("        whole = %d", newSampleRate->whole );
		}
		
		debugIOLog ("------------------------------------------------");
		debugIOLog ("\n");
		
		if ( ( false == mSingleSampleRateDevice ) && ( false == otherEngineNeedSampleRateChange ) )
		{
			// This is an emergency format change. We need to determine which engine(s) need(!s) to get a format change. Our order of preferability is:
			//		1. This engine should change if the sample rate can be matched at the current bit depth and channel count. If not,
			//		2. The other engine should change if this engine's reported sample rate can be matched at the other engine's current bit depth and channel count. If not,
			//		3. Both engines should return to their default settings, which had better be compatible.
			
			if (kIOReturnSuccess == mConfigDictionary->getAltSettingWithSettings (&altSetting, thisAudioEngine->mInterfaceNumber, thisFormat->fNumChannels, thisFormat->fBitDepth, otherSampleRate->whole))
			{
				// We'll change this engine only.
				formatChangeReturnCode = thisAudioEngine->controlledFormatChange (NULL, NULL, otherSampleRate);
				if (kIOReturnSuccess == formatChangeReturnCode)
				{
					debugIOLog ("? AppleUSBAudioDevice[%p]::formatChangeController () - This engine (%p) sample rate changed successfully to %lu.", this, thisAudioEngine, otherSampleRate->whole);
					result = kAUAFormatChangeForced;
					thisAudioEngine->hardwareSampleRateChanged (otherSampleRate);
				}
			}
			else if (kIOReturnSuccess == mConfigDictionary->getAltSettingWithSettings (&altSetting, otherAudioEngine->mInterfaceNumber, otherFormat->fNumChannels, otherFormat->fBitDepth, thisSampleRate->whole))
			{
				// We'll change the other engine only.
				formatChangeReturnCode = otherAudioEngine->controlledFormatChange (NULL, NULL, thisSampleRate);
				if (kIOReturnSuccess == formatChangeReturnCode)
				{
					debugIOLog ("? AppleUSBAudioDevice[%p]::formatChangeController () - Other engine (%p) sample rate changed succsesfully to %lu.", this, otherAudioEngine, thisSampleRate->whole);
					result = kAUAFormatChangeForced;
					otherAudioEngine->hardwareSampleRateChanged (thisSampleRate);
				}
			}
			else
			{
				// We'll restore both engines to their default settings.
				debugIOLog ("! AppleUSBAudioDevice[%p]::formatChangeController () - Restoring both engines to their default settings.", this);
				thisStream->setFormat ( &(thisAudioEngine->mDefaultAudioStreamFormat), false );
				formatChangeReturnCode = thisAudioEngine->controlledFormatChange (thisStream, &(thisAudioEngine->mDefaultAudioStreamFormat), &(thisAudioEngine->mDefaultAudioSampleRate));
				if (kIOReturnSuccess == formatChangeReturnCode)
				{
					debugIOLog ("? AppleUSBAudioDevice[%p]::formatChangeController () - This engine (%p) restored to default settings succsesfully.", this, thisAudioEngine);
					result = kAUAFormatChangeForced;
					thisAudioEngine->hardwareSampleRateChanged (&(thisAudioEngine->mDefaultAudioSampleRate));
				}
				otherStream->setFormat ( &(otherAudioEngine->mDefaultAudioStreamFormat), false );
				formatChangeReturnCode = otherAudioEngine->controlledFormatChange (otherStream, &(otherAudioEngine->mDefaultAudioStreamFormat), &(otherAudioEngine->mDefaultAudioSampleRate));
				if (kIOReturnSuccess == formatChangeReturnCode)
				{
					debugIOLog ("? AppleUSBAudioDevice[%p]::formatChangeController () - Other engine (%p) restored to default settings succsesfully.", this, otherAudioEngine);
					otherAudioEngine->hardwareSampleRateChanged (&(otherAudioEngine->mDefaultAudioSampleRate));
				}
				else
				{
					result = kAUAFormatChangeForceFailure;
				}
			}
		} // Emergency format change case
		else
		{
			// This device is already known to be a single-sample rate device. If the sample rate is changing, change it for both engines or change it for neither.
			if (		(newSampleRate)
					&&	(newSampleRate->whole != otherSampleRate->whole))
			{
				// See if we can the change the other sample rate at the current format.
				if (kIOReturnSuccess == mConfigDictionary->getAltSettingWithSettings (&altSetting, otherAudioEngine->mInterfaceNumber, otherFormat->fNumChannels, otherFormat->fBitDepth, newSampleRate->whole))				
				{
					// Issue format changes to both engines.
					thisStream->setFormat ( newFormat, false );
					formatChangeReturnCode = thisAudioEngine->controlledFormatChange (thisStream, newFormat, newSampleRate);
					if (kIOReturnSuccess == formatChangeReturnCode)
					{
						result = kAUAFormatChangeForced;
						thisAudioEngine->hardwareSampleRateChanged (newSampleRate);
						formatChangeReturnCode = otherAudioEngine->controlledFormatChange (otherStream, otherFormat, newSampleRate);
						if (kIOReturnSuccess != formatChangeReturnCode)
						{
							result = kAUAFormatChangeForceFailure;
						}
						else
						{
							otherAudioEngine->hardwareSampleRateChanged (newSampleRate);
						}
					}
				}
				else
				{
					// Fail this request.
					debugIOLog ("! AppleUSBAudioDevice[%p]::formatChangeController () - Other audio engine (%p) does not support sample rate %lu at %d bit %d channel(s). Failing.", 
								this, otherAudioEngine, newSampleRate->whole, otherFormat->fBitDepth, otherFormat->fNumChannels);
					result = kAUAFormatChangeForceFailure;
				}
			}
			else
			{
				// The sample rate isn't changing, so process this request normally.
				result = kAUAFormatChangeNormal;
				thisStream->setFormat ( newFormat, false );
				formatChangeReturnCode = (thisAudioEngine->controlledFormatChange (audioStream, newFormat, newSampleRate));
				// [rdar://5204813] Sending a null pointer here can cause a kernel panic. This should be unnecessary since the sample rate didn't change.
				// thisAudioEngine->hardwareSampleRateChanged (newSampleRate);
			}
		}
		
		FailIf (NULL == thisAudioEngine, Exit);
		FailIf (NULL == otherAudioEngine, Exit);
		thisStream = thisAudioEngine->mMainStream;
		otherStream = otherAudioEngine->mMainStream;
		FailIf (NULL == thisStream, Exit);
		FailIf (NULL == otherStream, Exit);
		thisFormat = thisStream->getFormat ();
		otherFormat = otherStream->getFormat ();
		FailIf (NULL == thisFormat, Exit);
		FailIf (NULL == otherFormat, Exit);
		
		// Log the new values.
		debugIOLog ("\n");
		debugIOLog ("-------------------- AFTER --------------------");
		debugIOLog ("? AppleUSBAudioDevice[%p]::formatChangeController () - engine %p (interface %d, alternateSetting %d) info:", this, thisAudioEngine, thisAudioEngine->mInterfaceNumber, thisAudioEngine->mAlternateSettingID);
		debugIOLog ("    thisFormat = %p", thisFormat);
		debugIOLog ("        fNumChannels = %d", thisFormat->fNumChannels);
		debugIOLog ("        fBitDepth = %d", thisFormat->fBitDepth);
		debugIOLog ("        fDriverTag = 0x%x", thisFormat->fDriverTag);
		debugIOLog ("    thisSampleRate->whole = %lu", thisSampleRate->whole);
		debugIOLog ("\n");
		debugIOLog ("? AppleUSBAudioDevice[%p]::formatChangeController () - engine %p (interface %d, alternateSetting %d) info:", this, otherAudioEngine, otherAudioEngine->mInterfaceNumber, otherAudioEngine->mAlternateSettingID);
		debugIOLog ("    otherFormat = %p", otherFormat);
		debugIOLog ("        fNumChannels = %d", otherFormat->fNumChannels);
		debugIOLog ("        fBitDepth = %d", otherFormat->fBitDepth);
		debugIOLog ("        fDriverTag = 0x%x", otherFormat->fDriverTag);
		debugIOLog ("    otherSampleRate->whole = %lu", otherSampleRate->whole);
		debugIOLog ("-----------------------------------------------");
		debugIOLog ("\n");
	}
	else
	{
		debugIOLog ("? AppleUSBAudioDevice[%p]::formatChangeController () - Attempting normal format change request.", this);
		result = kAUAFormatChangeNormal;
		// Issue this format change request.
		formatChangeReturnCode = thisAudioEngine->controlledFormatChange (audioStream, newFormat, newSampleRate);
	}
	
	if (kIOReturnSuccess != formatChangeReturnCode)
	{
		result = kAUAFormatChangeError;
		debugIOLog ("! AppleUSBAudioDevice[%p]::formatChangeController () - This format change failed with error 0x%x.", this, result);
	}
	else
	{
		debugIOLog ("? AppleUSBAudioDevice[%p]::formatChangeController () - This format change was successful.", this);
		if	(		(kAUAFormatChangeNormal != result)
				&&	(kAUAFormatChangeForced != result))
		{
			debugIOLog ("! AppleUSBAudioDevice[%p]::formatChangeController () - Forced format change failed.", this);
			result = kAUAFormatChangeForceFailure;
		}
	}
	
Exit:
	if (enginesPaused)
	{
		thisAudioEngine->resumeAudioEngine ();
		otherAudioEngine->resumeAudioEngine ();
	}
	debugIOLog ("- AppleUSBAudioDevice[%p]::formatChangeController (%p, %p, %p, %p) = 0x%x", this, audioEngine, audioStream, newFormat, newSampleRate, result);
	return result;
}

AppleUSBAudioEngine * AppleUSBAudioDevice::otherEngine (AppleUSBAudioEngine * thisEngine)
{
	SInt32	engineIndex;
	SInt32	otherEngineIndex;
	OSDictionary * otherAudioEngineInfo = NULL;
	AppleUSBAudioEngine * otherAudioEngine = NULL;
	
	FailIf (NULL == thisEngine, Exit);
	FailIf (NULL == mRegisteredEngines, Exit);
	engineIndex = getEngineInfoIndex (thisEngine);
		
	// Must stop here if we didn't find this engine.
	FailIf (-1 == engineIndex, Exit);
	
	otherEngineIndex = ((1 == engineIndex) ? 0 : 1);
	// The two engine indeces should be 0 and 1, so we'll NOT this engine to get the other one.
	otherAudioEngineInfo = OSDynamicCast (OSDictionary, mRegisteredEngines->getObject (otherEngineIndex));
	FailIf (NULL == otherAudioEngineInfo, Exit);
		
	otherAudioEngine = OSDynamicCast (AppleUSBAudioEngine, otherAudioEngineInfo->getObject (kEngine));
	FailIf (NULL == otherAudioEngine, Exit);
Exit:
	return otherAudioEngine;
}

IOReturn AppleUSBAudioDevice::getBothEngines (AppleUSBAudioEngine ** firstEngine, AppleUSBAudioEngine ** secondEngine)
{
	SInt32			engineIndex;
	OSDictionary *	firstAudioEngineInfo = NULL;
	IOReturn		result = kIOReturnError;
	
	FailIf (NULL == mRegisteredEngines, Exit);
	engineIndex = 0;
	
	firstAudioEngineInfo = OSDynamicCast (OSDictionary, mRegisteredEngines->getObject (engineIndex));
	// Must stop here if we didn't find this engine's dictionary.
	FailIf (NULL == firstAudioEngineInfo, Exit);
	*firstEngine = OSDynamicCast (AppleUSBAudioEngine, firstAudioEngineInfo->getObject (kEngine));
	FailIf (NULL == *firstEngine, Exit);
	
	// Now that we have the firstEngine, get the second.
	*secondEngine = otherEngine (*firstEngine);
	FailIf (NULL == secondEngine, Exit);
	result = kIOReturnSuccess;
Exit:
	return result;

}

#pragma mark Device Recovery

void AppleUSBAudioDevice::attemptDeviceRecovery ()
{
	debugIOLog ("+ AppleUSBAudioDevice[%p]::attemptDeviceRecovery ()", this);
	
	FailIf (NULL == mControlInterface, Exit);
	/*
	debugIOLog ("? AppleUSBAudioDevice[%p]::attemptDeviceRecovery () - Issuing device reset.", this);
	mControlInterface->GetDevice()->ResetDevice();
	IOSleep (10);
	*/
	
Exit:
	debugIOLog ("- AppleUSBAudioDevice[%p]::attemptDeviceRecovery ()", this);
	
	
	
}

#pragma mark Clock Entity Requests

IOReturn AppleUSBAudioDevice::getClockSetting (UInt8 controlSelector, UInt8 unitID, UInt8 requestType, void * target, UInt16 length) {
    IOReturn							result;
	IOUSBDevRequestDesc					devReq;
	UInt16								theSetting;
	IOBufferMemoryDescriptor *			theSettingDesc = NULL;

	result = kIOReturnError;
	// Initialize theSetting so that 
	theSetting = 0;
	FailIf (NULL == target, Exit);
	FailIf (NULL == mControlInterface, Exit);

	theSettingDesc = IOBufferMemoryDescriptor::withOptions (kIODirectionIn, length);
	FailIf (NULL == theSettingDesc, Exit);

    devReq.bmRequestType = USBmakebmRequestType (kUSBIn, kUSBClass, kUSBInterface);
	#ifdef HACK_FOR_EMU_0404_USB
	#warning HACK: The 0x80 is a hack for E-MU 0404 USB. Should remove it when E-MU fixes their firmware bug.
    devReq.bRequest = requestType | 0x80;
	#else
    devReq.bRequest = requestType;
    #endif
	devReq.wValue = (controlSelector << 8);
    devReq.wIndex = (0xFF00 & (unitID << 8)) | (0x00FF & mControlInterface->GetInterfaceNumber ());
    devReq.wLength = length;
    devReq.pData = theSettingDesc;

	result = deviceRequest (&devReq);
	FailIf (kIOReturnSuccess != result, Exit);
	
	if (NULL != target) 
	{
		memcpy (target, theSettingDesc->getBytesNoCopy (), length);
	}
	
Exit:
	if (NULL != theSettingDesc) 
	{
		theSettingDesc->release ();
	}
	return result;
}

IOReturn AppleUSBAudioDevice::setClockSetting (UInt8 controlSelector, UInt8 unitID, UInt8 requestType, void * target, UInt16 length) {
    IOUSBDevRequestDesc					devReq;
	IOBufferMemoryDescriptor *			theSettingDesc = NULL;
	IOReturn							result;

	result = kIOReturnError;
	FailIf (NULL == mControlInterface, Exit);

	theSettingDesc = OSTypeAlloc (IOBufferMemoryDescriptor);
	FailIf (NULL == theSettingDesc, Exit);
	theSettingDesc->initWithBytes (target, length, kIODirectionOut);

    devReq.bmRequestType = USBmakebmRequestType (kUSBOut, kUSBClass, kUSBInterface);
    devReq.bRequest = requestType;
    devReq.wValue = (controlSelector << 8);
    devReq.wIndex = (0xFF00 & (unitID << 8)) | (0x00FF & mControlInterface->GetInterfaceNumber ());
    devReq.wLength = length;
    devReq.pData = theSettingDesc;

	FailIf ((TRUE == isInactive()), DeviceInactive);  	// In case we've been unplugged during sleep
	result = deviceRequest (&devReq);

Exit:
	if (NULL != theSettingDesc) 
	{
		theSettingDesc->release ();
	}
	return result;
	
DeviceInactive:
	debugIOLog("? AppleUSBAudioDevice::setClockSourceSetting () - ERROR attempt to send a device request to an inactive device");
	goto Exit;
}

IOReturn AppleUSBAudioDevice::getNumClockSourceSamplingFrequencySubRanges (UInt8 unitID, UInt16 * numSubRanges) {
	struct {
	UInt16								wNumSubRanges;
	SubRange32							SubRanges[1];
	}									rangeParameterBlock;
	IOReturn							result;

	rangeParameterBlock.wNumSubRanges = 0;

	result = kIOReturnError;
	FailIf (NULL == numSubRanges, Exit);
	
	result = getClockSetting (USBAUDIO_0200::CS_SAM_FREQ_CONTROL, unitID, USBAUDIO_0200::RANGE, &rangeParameterBlock, sizeof(rangeParameterBlock));
	FailIf (kIOReturnSuccess != result, Exit);
	
Exit:
	if (NULL != numSubRanges) 
	{
		*numSubRanges = USBToHostWord (rangeParameterBlock.wNumSubRanges);
	}

	return result;
}

IOReturn AppleUSBAudioDevice::getIndexedClockSourceSamplingFrequencySubRange (UInt8 unitID, SubRange32 * subRange, UInt16 subRangeIndex) {
	struct {
	UInt16								wNumSubRanges;
	SubRange32							SubRanges[1];
	}									rangeParameterBlock;
	void *								theRangeParameterBlock;
	UInt32								theRangeParameterBlockLength;
	SubRange32 *						theSubRanges;
	IOReturn							result;

	rangeParameterBlock.wNumSubRanges = 0;
	theRangeParameterBlock = NULL;

	result = kIOReturnError;
	FailIf (NULL == subRange, Exit);
	
	result = getClockSetting (USBAUDIO_0200::CS_SAM_FREQ_CONTROL, unitID, USBAUDIO_0200::RANGE, &rangeParameterBlock, sizeof(rangeParameterBlock));
	FailIf (kIOReturnSuccess != result, Exit);
	
	rangeParameterBlock.wNumSubRanges = USBToHostWord (rangeParameterBlock.wNumSubRanges);

	result = kIOReturnError;
	FailIf (subRangeIndex >= rangeParameterBlock.wNumSubRanges, Exit);

	theRangeParameterBlockLength = 2 + (rangeParameterBlock.wNumSubRanges * sizeof(SubRange32));	
	theRangeParameterBlock = IOMalloc(theRangeParameterBlockLength);
	FailIf (NULL == theRangeParameterBlock, Exit);
	
	result = getClockSetting (USBAUDIO_0200::CS_SAM_FREQ_CONTROL, unitID, USBAUDIO_0200::RANGE, theRangeParameterBlock, theRangeParameterBlockLength);
	FailIf (kIOReturnSuccess != result, Exit);
	
	theSubRanges = (SubRange32 *)(((UInt8 *) theRangeParameterBlock) + 2);
	
	subRange->dMIN = USBToHostLong (theSubRanges[subRangeIndex].dMIN);
	subRange->dMAX = USBToHostLong (theSubRanges[subRangeIndex].dMAX);
	subRange->dRES = USBToHostLong (theSubRanges[subRangeIndex].dRES);
	
Exit:
	if (NULL != theRangeParameterBlock) 
	{
		IOFree (theRangeParameterBlock, theRangeParameterBlockLength);
	}	

	return result;
}

IOReturn AppleUSBAudioDevice::getCurClockSourceSamplingFrequency (UInt8 unitID, UInt32 * samplingFrequency, bool * validity) {
	UInt32								clockFrequency;
	Boolean								clockValidity;
	IOReturn							result;

	clockValidity = false;
	
	result = getClockSetting (USBAUDIO_0200::CS_SAM_FREQ_CONTROL, unitID, USBAUDIO_0200::CUR, &clockFrequency, 4);

	FailIf (kIOReturnSuccess != result, Exit);

	#ifdef HACK_FOR_EMU_0404_USB
	#warning HACK: The E-MU 0404 USB doesn't support the CS_CLOCK_VALID_CONTROL at this moment.  Should remove this when E-MU fixes their firmware bug.
	clockValidity = true;
	#else
	result = getClockSetting (USBAUDIO_0200::CS_CLOCK_VALID_CONTROL, unitID, USBAUDIO_0200::CUR, &clockValidity, 1);

	FailIf (kIOReturnSuccess != result, Exit);
	#endif
	
Exit:
	if (NULL != samplingFrequency) 
	{
		*samplingFrequency = USBToHostLong (clockFrequency);
	}

	if (NULL != validity) 
	{
		*validity = clockValidity;
	}

	return result;
}

IOReturn AppleUSBAudioDevice::setCurClockSourceSamplingFrequency (UInt8 unitID, UInt32 samplingFrequency) {
	UInt32								frequency;
	
	frequency = USBToHostLong(samplingFrequency);
		
	return setClockSetting (USBAUDIO_0200::CS_SAM_FREQ_CONTROL, unitID, USBAUDIO_0200::CUR, &frequency, 4);
}

IOReturn AppleUSBAudioDevice::getCurClockSelector (UInt8 unitID, UInt8 * selector) {
	return getClockSetting (USBAUDIO_0200::CX_CLOCK_SELECTOR_CONTROL, unitID, USBAUDIO_0200::CUR, selector, 1);
}

IOReturn AppleUSBAudioDevice::setCurClockSelector (UInt8 unitID, UInt8 selector) {
	return setClockSetting (USBAUDIO_0200::CX_CLOCK_SELECTOR_CONTROL, unitID, USBAUDIO_0200::CUR, &selector, 1);
}

IOReturn AppleUSBAudioDevice::getCurClockMultiplier (UInt8 unitID, UInt16 * numerator, UInt16 * denominator) {
	UInt8								clockNumerator;
	Boolean								clockDenominator;
	IOReturn							result;

	result = getClockSetting (USBAUDIO_0200::CM_NUMERATOR_CONTROL, unitID, USBAUDIO_0200::CUR, &clockNumerator, 2);

	FailIf (kIOReturnSuccess != result, Exit);

	result = getClockSetting (USBAUDIO_0200::CM_DENOMINATOR_CONTROL, unitID, USBAUDIO_0200::CUR, &clockDenominator, 2);

	FailIf (kIOReturnSuccess != result, Exit);

Exit:
	if (NULL != numerator) 
	{
		*numerator = USBToHostWord (clockNumerator);
	}

	if (NULL != denominator) 
	{
		*denominator = USBToHostWord (clockDenominator);
	}

	return result;
}

#pragma mark Clock Path Sample Rates Discovery and Requests

IOReturn AppleUSBAudioDevice::getNumSampleRatesForClockPath (UInt8 * numSampleRates, OSArray * clockPath) {
	OSObject *							arrayObject = NULL;
	OSNumber *							arrayNumber = NULL;
	UInt8								clockID;
	UInt8								subType;
	UInt16								numSubRanges;
	IOReturn							result;

	numSubRanges = 0;

	result = kIOReturnError;
	FailIf (NULL == numSampleRates, Exit);
	
	// The last object in the clock path is the clock source.
	FailIf (NULL == (arrayObject = clockPath->getLastObject ()), Exit);
	FailIf (NULL == (arrayNumber = OSDynamicCast (OSNumber, arrayObject)), Exit);
	clockID = arrayNumber->unsigned8BitValue();
	
	FailIf (kIOReturnSuccess != (result = mConfigDictionary->getSubType (&subType, mControlInterface->GetInterfaceNumber(), 0, clockID)), Exit);
	
	if (USBAUDIO_0200::CLOCK_SOURCE == subType)
	{
		FailIf (kIOReturnSuccess !=  (result = getNumClockSourceSamplingFrequencySubRanges (clockID, &numSubRanges)), Exit);
	}

Exit:
	if (NULL != numSampleRates)
	{
		*numSampleRates = numSubRanges;
	}
	
	return result;
}

IOReturn AppleUSBAudioDevice::getIndexedSampleRatesForClockPath (SubRange32 * sampleRates, OSArray * clockPath, UInt32 rangeIndex) {
	OSObject *							arrayObject = NULL;
	OSNumber *							arrayNumber = NULL;
	UInt32								clockIndex;
	UInt8								clockID;
	UInt8								subType;
	SubRange32							subRange;
	UInt16								numerator;
	UInt16								denominator;
	IOReturn							result;

	subRange.dMIN = subRange.dMAX = subRange.dRES = 0;
	
	result = kIOReturnError;
	FailIf (NULL == sampleRates, Exit);
	
	for (clockIndex = clockPath->getCount (); clockIndex > 0 ; clockIndex--) 
	{
		FailIf (NULL == (arrayObject = clockPath->getObject (clockIndex - 1)), Exit);
		FailIf (NULL == (arrayNumber = OSDynamicCast (OSNumber, arrayObject)), Exit);
		clockID = arrayNumber->unsigned8BitValue();
		
		FailIf (kIOReturnSuccess != (result = mConfigDictionary->getSubType (&subType, mControlInterface->GetInterfaceNumber(), 0, clockID)), Exit);
		
		if (USBAUDIO_0200::CLOCK_SOURCE == subType)
		{
			FailIf (kIOReturnSuccess !=  (result = getIndexedClockSourceSamplingFrequencySubRange (clockID, &subRange, rangeIndex)), Exit);
		}
		else if (USBAUDIO_0200::CLOCK_MULTIPLIER == subType)
		{
			FailIf (kIOReturnSuccess !=  (result = getCurClockMultiplier (clockID, &numerator, &denominator)), Exit);
			
			subRange.dMIN = subRange.dMIN * numerator / denominator;
			subRange.dMAX = subRange.dMAX * numerator / denominator;
			subRange.dRES = subRange.dRES * numerator / denominator;
		}
	}	
	
Exit:
	if (NULL != sampleRates)
	{
		*sampleRates = subRange;
	}
	
	return result;
}

IOReturn AppleUSBAudioDevice::getClockPathCurSampleRate (UInt32 * sampleRate, Boolean * validity, OSArray * clockPath) {
	OSObject *							arrayObject = NULL;
	OSNumber *							arrayNumber = NULL;
	UInt32								clockIndex;
	UInt8								clockID;
	UInt8								subType;
	UInt32								clockRate;
	bool								clockValidity;
	UInt16								numerator;
	UInt16								denominator;
	IOReturn							result;

	debugIOLog ("+ AppleUSBAudioDevice[%p]::getClockPathCurSampleRate", this);

	clockRate = 0;
	clockValidity = false;
	
	result = kIOReturnError;
	FailIf (NULL == sampleRate, Exit);

	for (clockIndex = clockPath->getCount (); clockIndex > 0 ; clockIndex--) 
	{
		FailIf (NULL == (arrayObject = clockPath->getObject (clockIndex - 1)), Exit);
		FailIf (NULL == (arrayNumber = OSDynamicCast (OSNumber, arrayObject)), Exit);
		clockID = arrayNumber->unsigned8BitValue();
		
		FailIf (kIOReturnSuccess != (result = mConfigDictionary->getSubType (&subType, mControlInterface->GetInterfaceNumber(), 0, clockID)), Exit);
		
		if (USBAUDIO_0200::CLOCK_SOURCE == subType)
		{
			FailIf (kIOReturnSuccess !=  (result = getCurClockSourceSamplingFrequency (clockID, &clockRate, &clockValidity)), Exit);
		}
		else if (USBAUDIO_0200::CLOCK_MULTIPLIER == subType)
		{
			FailIf (kIOReturnSuccess !=  (result = getCurClockMultiplier (clockID, &numerator, &denominator)), Exit);
			
			clockRate = clockRate * numerator / denominator;
		}
	}	
	
Exit:
	if (NULL != sampleRate)
	{
		*sampleRate = clockRate;
	}
	if (NULL != validity)
	{
		*validity = clockValidity;
	}
	
	debugIOLog ("- AppleUSBAudioDevice[%p]::getClockPathCurSampleRate (%d, %d) = %d", this, clockRate, clockValidity, result);

	return result;
}

IOReturn AppleUSBAudioDevice::setClockPathCurSampleRate (UInt32 sampleRate, OSArray * clockPath) {
	OSObject *							arrayObject = NULL;
	OSNumber *							arrayNumber = NULL;
	UInt32								clockIndex;
	UInt8								clockID;
	UInt8								subType;
	UInt16								numerator;
	UInt16								denominator;
	OSArray *							clockSourceIDs = NULL;
	UInt32								clockSourceIndex;
	UInt8								nextClockID;
	IOReturn							result;

	debugIOLog ("+ AppleUSBAudioDevice[%p]::setClockPathCurSampleRate (%d)", this, sampleRate);

	result = kIOReturnError;

	for (clockIndex = 0; clockIndex <  clockPath->getCount (); clockIndex++) 
	{
		FailIf (NULL == (arrayObject = clockPath->getObject (clockIndex)), Exit);
		FailIf (NULL == (arrayNumber = OSDynamicCast (OSNumber, arrayObject)), Exit);
		clockID = arrayNumber->unsigned8BitValue();
		
		FailIf (kIOReturnSuccess != (result = mConfigDictionary->getSubType (&subType, mControlInterface->GetInterfaceNumber(), 0, clockID)), Exit);
		
		if (USBAUDIO_0200::CLOCK_SOURCE == subType)
		{
			FailIf (kIOReturnSuccess !=  (result = setCurClockSourceSamplingFrequency (clockID, sampleRate)), Exit);
			break;
		}
		else if (USBAUDIO_0200::CLOCK_SELECTOR == subType)
		{
			FailIf (kIOReturnSuccess != (result = mConfigDictionary->getClockSelectorSources (&clockSourceIDs, mControlInterface->GetInterfaceNumber(), 0, clockID)), Exit);
			
			FailIf (NULL == (arrayObject = clockPath->getObject (clockIndex + 1)), Exit);
			FailIf (NULL == (arrayNumber = OSDynamicCast (OSNumber, arrayObject)), Exit);
			nextClockID = arrayNumber->unsigned8BitValue();			

			for (clockSourceIndex = 0; clockSourceIndex < clockSourceIDs->getCount (); clockSourceIndex++)
			{  
				FailIf (NULL == (arrayObject = clockSourceIDs->getObject (clockSourceIndex)), Exit);
				FailIf (NULL == (arrayNumber = OSDynamicCast (OSNumber, arrayObject)), Exit);
				
				if (arrayNumber->unsigned8BitValue() == nextClockID)
				{
					setCurClockSelector(clockID, clockSourceIndex + 1);
					break;
				}			
			}
   		}
		else if (USBAUDIO_0200::CLOCK_MULTIPLIER == subType)
		{
			FailIf (kIOReturnSuccess !=  (result = getCurClockMultiplier (clockID, &numerator, &denominator)), Exit);

			sampleRate = sampleRate * denominator / numerator;
		}
	}	
	
Exit:
	
	debugIOLog ("- AppleUSBAudioDevice[%p]::setClockPathCurSampleRate (%d) = %d", this, sampleRate, result);

	return result;
}

Generated by GNU enscript 1.6.4.