/*
 * 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:		AppleUSBAudioEngine.cpp
//
//	Contains:	Support for the USB Audio Class Stream Interface.
//			This includes support for setting sample rate (via
//			a sample rate endpoint control and appropriate
//			sized construction of USB isochronous frame lists),
//			channel depth selection and bit depth selection.
//
//	Technology:	Mac OS X
//
//--------------------------------------------------------------------------------

#define DEBUGZEROTIME			FALSE
#define DEBUGUSB				FALSE
#define DEBUGISUB				FALSE
#define DEBUGLOADING			FALSE
#define DEBUGTIMESTAMPS			FALSE
#define	DEBUGINPUT				FALSE
#define DEBUGUHCI				FALSE

#define THREAD_ISUB_OPEN		FALSE

#include "AppleUSBAudioEngine.h"
#include "AppleUSBAudioPlugin.h"

#define super IOAudioEngine

OSDefineMetaClassAndStructors(AppleUSBAudioEngine, IOAudioEngine)

#pragma mark -IOKit Routines-

void AppleUSBAudioEngine::free () {
	UInt32			i;

	debugIOLog ("+ AppleUSBAudioEngine[%p]::free ()", this);

	if (NULL != mCoalescenceMutex)
	{
		IOLockFree (mCoalescenceMutex);
		mCoalescenceMutex = NULL;
	}

	if (NULL != mFrameQueuedForList) 
	{
		delete [] mFrameQueuedForList;
		mFrameQueuedForList = NULL;
	}

	if (mAssociatedEndpointMemoryDescriptor) 
	{
		mAssociatedEndpointMemoryDescriptor->release ();
		mAssociatedEndpointMemoryDescriptor = NULL;
	}

	if (mAverageSampleRateBuffer) 
	{
		IOFreeContiguous (mAverageSampleRateBuffer, sizeof (UInt32));
		mAverageSampleRateBuffer = NULL;
	}

	if (mDeviceAndEngineSyncer) 
	{
		mDeviceAndEngineSyncer->release ();
	}
	
	if (mUSBBufferDescriptor) 
	{
		mUSBBufferDescriptor->release ();
		mUSBBufferDescriptor = NULL;
	}

	if (mWrapRangeDescriptor) 
	{
		mWrapRangeDescriptor->release ();
		mWrapDescriptors[0]->release ();
		mWrapDescriptors[1]->release ();
		mWrapRangeDescriptor = NULL;
	}

	if (mSampleBufferMemoryDescriptor) 
	{
		mSampleBufferMemoryDescriptor->release ();
		mSampleBufferMemoryDescriptor = NULL;
	}

	if (NULL != mSampleBufferDescriptors) 
	{
		for (i = 0; i < mNumUSBFrameLists; i++) 
		{
			if (NULL != mSampleBufferDescriptors[i]) 
			{
				mSampleBufferDescriptors[i]->release ();
				mSampleBufferDescriptors[i] = NULL;
			}
		}

		IOFree (mSampleBufferDescriptors, mNumUSBFrameLists * sizeof (IOSubMemoryDescriptor *));
		mSampleBufferDescriptors = NULL;
	}

	if (NULL != mUSBIsocFrames) 
	{
		IOFree (mUSBIsocFrames, mNumUSBFrameLists * mNumUSBFramesPerList * sizeof (IOUSBLowLatencyIsocFrame));
		mUSBIsocFrames = NULL;
	}

	if (NULL != mUSBCompletion) 
	{
		IOFree (mUSBCompletion, mNumUSBFrameLists * sizeof (IOUSBLowLatencyIsocCompletion));
		mUSBCompletion = NULL;
	}

	if (NULL != getSampleBuffer ()) 
	{
		// IOFree (getSampleBuffer (), getSampleBufferSize ());
		IOFree (getSampleBuffer (), mSampleBufferSizeExtended);
	}
	
	if (NULL != mLowFreqSamples) 
	{
		IOFreeAligned (mLowFreqSamples, getSampleBufferSize () * 2);
	}

	if (NULL != mHighFreqSamples) 
	{
		IOFreeAligned (mHighFreqSamples, getSampleBufferSize () * 2);
	}

	if (NULL != miSubEngineNotifier) 
	{
		debugIOLog ("? AppleUSBAudioEngine[%p]::free () - Removing miSubEngineNotifier ...", this);
		miSubEngineNotifier->remove ();
		miSubEngineNotifier = NULL;
	}

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

	if (mMainStream) 
	{
		mMainStream->release ();
		mMainStream = NULL;
	}

	super::free ();
	debugIOLog ("- AppleUSBAudioEngine[%p]::free()", this);
}

bool AppleUSBAudioEngine::init (OSDictionary *properties) {
	Boolean			result;

	debugIOLog("+ AppleUSBAudioEngine[%p]::init ()", this);

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

	// Change this to use defines from the IOAudioFamily when they are available
	setProperty ("IOAudioStreamSampleFormatByteOrder", "Little Endian");

	mDeviceAndEngineSyncer = IOSyncer::create (FALSE);
	result = TRUE;
        
	mSrcPhase = 1.0;		// aml 3.4.02
	mSrcState = 0.0;		// aml 3.6.02
	mJustResetClipPosition = FALSE;	// aml 3.29.02
        
Exit:
	debugIOLog("- AppleUSBAudioEngine[%p]::init ()", this);
	return result;
}

bool AppleUSBAudioEngine::requestTerminate (IOService * provider, IOOptionBits options) {
	bool					result;

	debugIOLog ("+ AppleUSBAudioEngine[%p]::requestTerminate (%p, %x)", this, provider, options);

	// if interface or audio device
	if (mUSBAudioDevice == provider || mStreamInterface == provider) 
	{
		result = TRUE;		// it is OK to terminate us
	} 
	else 
	{
		result = FALSE;		// don't terminate us
	}

	debugIOLog ("- AppleUSBAudioEngine[%p]::requestTerminate (%p, %x) = %d", this, provider, options, result);
	return result;
}

bool AppleUSBAudioEngine::start (IOService * provider) {
	IONotifier *						audioDeviceNotifier;
	bool								resultCode;
	USBAudioConfigObject *				usbAudio;
	OSDictionary *						matchingDictionary;
	OSString *							name;

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

	resultCode = FALSE;

	// Find out what interface number the driver is being instantiated against so that we always ask about
	// our particular interface and not some other interface the device might have.
	mStreamInterface = OSDynamicCast (IOUSBInterface, provider);
	FailIf (NULL == mStreamInterface, Exit);
	mInterfaceNumber = mStreamInterface->GetInterfaceNumber ();
	debugIOLog ("? AppleUSBAudioEngine[%p]::start () - mInterfaceNumber = %d", this, mInterfaceNumber);

	//	This snippet of code goes with the audiodevicePublished function
	//	we wait until the Stream interface finds a partner.
	
	// Added this custom matching dictionary for rdar://3993906
	matchingDictionary = serviceMatching ("AppleUSBAudioDevice");
	name = OSString::withCString ("*");
	matchingDictionary->setObject (kIOAudioDeviceNameKey, name);
	name->release ();
	
	// Doesn't matter what the device name is; we're only concerned that it has a device name.
	// If it does, it already has its configuration descriptor, so we can call
	// AppleUSBAudioDevice::ControlsStreamNumber
	
	debugIOLog ("? AppleUSBAudioEngine[%p]::start () - Adding notification with custom matching dictionary", this);
	audioDeviceNotifier = addNotification (gIOMatchedNotification,
											matchingDictionary,
											(IOServiceNotificationHandler)&audioDevicePublished,
											this,
											NULL);

	mDeviceAndEngineSyncer->wait (FALSE);
	audioDeviceNotifier->remove ();
	mDeviceAndEngineSyncer->reinit ();

	FailIf (NULL == mUSBAudioDevice, Exit);

	// If this is an iSub, we need to not go any further because we don't support it in this driver
	usbAudio = mUSBAudioDevice->GetUSBAudioConfigObject ();
	FailIf (NULL == usbAudio, Exit);
	// This will cause the driver to not load on any device that has _only_ a low frequency effect output terminal
	FailIf (usbAudio->GetNumOutputTerminals (0, 0) == 1 && usbAudio->GetIndexedOutputTerminalType (0, 0, 0) == OUTPUT_LOW_FREQUENCY_EFFECTS_SPEAKER, Exit);

	resultCode = super::start (provider, mUSBAudioDevice);
    
	debugIOLog ("- AppleUSBAudioEngine[%p]::start (%p) = %d", this, provider, resultCode);

Exit:    
	return resultCode;
}
    
void AppleUSBAudioEngine::stop (IOService * provider) {
    debugIOLog("+ AppleUSBAudioEngine[%p]::stop (%p)", this, provider);

	if (provider == miSubEngine) 
	{
		goto Exit;
	}

	if (NULL != mPluginNotification) 
	{
		mPluginNotification->remove ();
		mPluginNotification = NULL;
	}
	
	if (NULL != mPluginInitThread)
	{
		thread_call_cancel (mPluginInitThread);
		thread_call_free (mPluginInitThread);
		mPluginInitThread = NULL;
	}

	if (NULL != miSubEngineNotifier) 
	{
		debugIOLog ("? AppleUSBAudioEngine[%p]::stop () - Removing miSubEngineNotifier ...", this);
		miSubEngineNotifier->remove ();
		miSubEngineNotifier = NULL;
	}

	if (mPlugin) 
	{
		mPlugin->close (this);
		mPlugin = NULL;
	}

	if (NULL != miSubEngine) 
	{
		iSubTeardownConnection ();
	}

	if (mUSBAudioDevice) 
	{
		mUSBAudioDevice->release ();
		mUSBAudioDevice = NULL;
	}

	if (mPipe) 
	{
		mPipe->release ();
		mPipe = NULL;
	}

	if (mAssociatedPipe) 
	{
		mAssociatedPipe->release ();
		mAssociatedPipe = NULL;
	}

	// [rdar://4287899] We don't expect the stream interface to need closing unless the following conditions are true.
	if (		(mStreamInterface)
			&&	(provider == mStreamInterface)
			&&	(mStreamInterface->isOpen())) 
	{
		debugIOLog ("! AppleUSBAudioEngine[%p]::stop () - mStreamInterface was still open when stop() was called. Closing ...", this);
		mStreamInterface->close (this);
		mStreamInterface = NULL;
	}

Exit:
	super::stop (provider);

	debugIOLog ("- AppleUSBAudioEngine[%p]::stop (%p) - rc=%ld", this, provider, getRetainCount());
}

bool AppleUSBAudioEngine::terminate (IOOptionBits options) {
	bool							shouldTerminate;
	bool							result;

	result = TRUE;
	shouldTerminate = TRUE;

	debugIOLog ("+ AppleUSBAudioEngine[%p]::terminate ()", this);

	if (shouldTerminate) 
	{
		result = super::terminate (options);
	}

	debugIOLog ("- AppleUSBAudioEngine[%p]::terminate ()", this);

	return result;
}

#pragma mark -USB Audio driver-

IOReturn AppleUSBAudioEngine::AddAvailableFormatsFromDevice (USBAudioConfigObject *usbAudio) {
    IOAudioStreamFormat					streamFormat;
    IOAudioStreamFormatExtension		streamFormatExtension;
    IOAudioSampleRate					lowSampleRate;
    IOAudioSampleRate					highSampleRate;
	UInt32 *							sampleRates;
	IOReturn							result;
    UInt16								numAltInterfaces;
    UInt8								numSampleRates;
    UInt8								altSettingIndx;
	UInt8								rateIndx;
	UInt8								candidateAC3AltSetting;
	Boolean								hasNativeAC3Format;
	Boolean								hasDigitalOutput;

	result = kIOReturnError;
	FailIf (NULL == usbAudio, Exit);
	FailIf (NULL == mMainStream, Exit);

	numAltInterfaces = usbAudio->GetNumAltStreamInterfaces (mInterfaceNumber);
	debugIOLog ("? AppleUSBAudioEngine[%p]::AddAvailableFormatsFromDevice () - There are %d alternate interfaces @ interface %d", this, numAltInterfaces, mInterfaceNumber);
	hasNativeAC3Format = FALSE;
	candidateAC3AltSetting = 0;

	// Find all of the available formats on the device.
	if (usbAudio->AlternateSettingZeroCanStream (mInterfaceNumber))
	{
		altSettingIndx = 0;
	}
	else
	{
		altSettingIndx = 1;
	}
	
	for ( ; altSettingIndx < numAltInterfaces; altSettingIndx++) 
	{
		numSampleRates = usbAudio->GetNumSampleRates (mInterfaceNumber, altSettingIndx);
		sampleRates = usbAudio->GetSampleRates (mInterfaceNumber, altSettingIndx);

		streamFormat.fNumChannels = usbAudio->GetNumChannels (mInterfaceNumber, altSettingIndx);
		streamFormat.fBitDepth = usbAudio->GetSampleSize (mInterfaceNumber, altSettingIndx);
		streamFormat.fBitWidth = usbAudio->GetSubframeSize (mInterfaceNumber, altSettingIndx) * 8;
		streamFormat.fAlignment = kIOAudioStreamAlignmentLowByte;
		streamFormat.fByteOrder = kIOAudioStreamByteOrderLittleEndian;
		streamFormat.fDriverTag = (mInterfaceNumber << 16) | altSettingIndx;

		streamFormatExtension.fVersion = kFormatExtensionCurrentVersion;
		streamFormatExtension.fFlags = 0;
		streamFormatExtension.fFramesPerPacket = 1;
		streamFormatExtension.fBytesPerPacket = usbAudio->GetNumChannels (mInterfaceNumber, altSettingIndx) * usbAudio->GetSubframeSize (mInterfaceNumber, altSettingIndx);

		switch (usbAudio->GetFormat (mInterfaceNumber, altSettingIndx)) 
		{
			case PCM:
				streamFormat.fSampleFormat = kIOAudioStreamSampleFormatLinearPCM;
				streamFormat.fNumericRepresentation = kIOAudioStreamNumericRepresentationSignedInt;
				streamFormat.fIsMixable = TRUE;
				if (2 == streamFormat.fNumChannels && 16 == streamFormat.fBitDepth && 16 == streamFormat.fBitWidth) 
				{
					candidateAC3AltSetting = altSettingIndx;
				}
				break;
			case AC3:	// just starting to stub something in for AC-3 support
				debugIOLog ("? AppleUSBAudioEngine[%p]::AddAvailableFormatsFromDevice () - Variable bit rate AC-3 audio format type", this);
				continue;	// We're not supporting this at the moment, so just skip it.
				streamFormat.fSampleFormat = kIOAudioStreamSampleFormatAC3;
				streamFormat.fIsMixable = FALSE;
				streamFormat.fNumChannels = 6;
				streamFormat.fNumericRepresentation = kIOAudioStreamNumericRepresentationSignedInt;
				streamFormat.fBitDepth = 16;
				streamFormat.fBitWidth = 16;
				streamFormat.fByteOrder = kIOAudioStreamByteOrderBigEndian;

				streamFormatExtension.fFlags = USBToHostLong (usbAudio->GetAC3BSID (mInterfaceNumber, altSettingIndx));
//				streamFormatExtension.fFramesPerPacket = usbAudio->GetSamplesPerFrame (mInterfaceNumber, altSettingIndx);
				streamFormatExtension.fFramesPerPacket = 1536;
//				streamFormatExtension.fBytesPerPacket = ((usbAudio->GetMaxBitRate (mInterfaceNumber, altSettingIndx) * 1024 / 8) + 500) / 1000;
				streamFormatExtension.fBytesPerPacket = streamFormatExtension.fFramesPerPacket * streamFormat.fNumChannels * usbAudio->GetSubframeSize (mInterfaceNumber, altSettingIndx);
				break;
			case IEC1937_AC3:
				debugIOLog ("? AppleUSBAudioEngine[%p]::AddAvailableFormatsFromDevice () - IEC1937 AC-3 audio format type", this);
				hasNativeAC3Format = TRUE;
				streamFormat.fSampleFormat = kIOAudioStreamSampleFormat1937AC3;
				streamFormat.fNumericRepresentation = kIOAudioStreamNumericRepresentationSignedInt;
				streamFormat.fIsMixable = FALSE;

				streamFormatExtension.fFramesPerPacket = 1536;
				streamFormatExtension.fBytesPerPacket = streamFormatExtension.fFramesPerPacket * streamFormat.fNumChannels * usbAudio->GetSubframeSize (mInterfaceNumber, altSettingIndx);
				break;
			default:
				debugIOLog ("? AppleUSBAudioEngine[%p]::AddAvailableFormatsFromDevice () - Interface format = 0x%x not published.", this, usbAudio->GetFormat (mInterfaceNumber, altSettingIndx));
				continue;	// skip this alternate interface
		}

		debugIOLog ("? AppleUSBAudioEngine[%p]::AddAvailableFormatsFromDevice () - Interface %d, Alt %d has a ", this, mInterfaceNumber, altSettingIndx);
		debugIOLog ("     %d bit interface, ", streamFormat.fBitDepth);
		debugIOLog ("     %d channel(s), and ", streamFormat.fNumChannels);
		debugIOLog ("     %d sample rate(s), which is/are:", numSampleRates);

		if (numSampleRates) 
		{
			for (rateIndx = 0; rateIndx < numSampleRates; rateIndx++) 
			{
				debugIOLog ("          %d", sampleRates[rateIndx]);
				lowSampleRate.whole = sampleRates[rateIndx];
				lowSampleRate.fraction = 0;
				mMainStream->addAvailableFormat (&streamFormat, &streamFormatExtension, &lowSampleRate, &lowSampleRate);
				if (kIOAudioStreamSampleFormatLinearPCM == streamFormat.fSampleFormat) 
				{
					streamFormat.fIsMixable = FALSE;
					mMainStream->addAvailableFormat (&streamFormat, &streamFormatExtension, &lowSampleRate, &lowSampleRate);
					streamFormat.fIsMixable = TRUE;		// set it back to TRUE for next time through the loop
				}
			}
			debugIOLog ("");
		} 
		else if (sampleRates) 
		{
			debugIOLog ("          %d to %d", sampleRates[0], sampleRates[1]);
			lowSampleRate.whole = sampleRates[0];
			lowSampleRate.fraction = 0;
			highSampleRate.whole = sampleRates[1];
			highSampleRate.fraction = 0;
			mMainStream->addAvailableFormat (&streamFormat, &streamFormatExtension, &lowSampleRate, &highSampleRate);
			if (kIOAudioStreamSampleFormatLinearPCM == streamFormat.fSampleFormat) 
			{
				streamFormat.fIsMixable = FALSE;
				mMainStream->addAvailableFormat (&streamFormat, &streamFormatExtension, &lowSampleRate, &highSampleRate);
			}
		}
	} // for altSettingIndx

	switch (usbAudio->GetOutputTerminalType (mUSBAudioDevice->mControlInterface->GetInterfaceNumber (), 0, altSettingIndx)) 
	{
		case EXTERNAL_DIGITAL_AUDIO_INTERFACE:
		case EXTERNAL_SPDIF_INTERFACE:
		case EMBEDDED_DVD_AUDIO:
			hasDigitalOutput = TRUE;
			break;
		default:
			hasDigitalOutput = FALSE;
	}

	if (TRUE == hasDigitalOutput && FALSE == hasNativeAC3Format && 0 != candidateAC3AltSetting && kIOAudioStreamDirectionOutput == getDirection ()) 
	{
		numSampleRates = usbAudio->GetNumSampleRates (mInterfaceNumber, candidateAC3AltSetting);
		sampleRates = usbAudio->GetSampleRates (mInterfaceNumber, candidateAC3AltSetting);

		streamFormat.fNumChannels = usbAudio->GetNumChannels (mInterfaceNumber, candidateAC3AltSetting);
		streamFormat.fBitDepth = usbAudio->GetSampleSize (mInterfaceNumber, candidateAC3AltSetting);
		streamFormat.fBitWidth = usbAudio->GetSubframeSize (mInterfaceNumber, candidateAC3AltSetting) * 8;
		streamFormat.fAlignment = kIOAudioStreamAlignmentLowByte;
		streamFormat.fByteOrder = kIOAudioStreamByteOrderLittleEndian;
		streamFormat.fDriverTag = (mInterfaceNumber << 16) | candidateAC3AltSetting;
		streamFormat.fSampleFormat = kIOAudioStreamSampleFormat1937AC3;
		streamFormat.fNumericRepresentation = kIOAudioStreamNumericRepresentationSignedInt;
		streamFormat.fIsMixable = FALSE;

		streamFormatExtension.fVersion = kFormatExtensionCurrentVersion;
		streamFormatExtension.fFlags = 0;
		streamFormatExtension.fFramesPerPacket = 1536;
		streamFormatExtension.fBytesPerPacket = streamFormatExtension.fFramesPerPacket * streamFormat.fNumChannels * usbAudio->GetSubframeSize (mInterfaceNumber, candidateAC3AltSetting);

		if (numSampleRates) 
		{
			for (rateIndx = 0; rateIndx < numSampleRates; rateIndx++) 
			{
				lowSampleRate.whole = sampleRates[rateIndx];
				lowSampleRate.fraction = 0;
				mMainStream->addAvailableFormat (&streamFormat, &streamFormatExtension, &lowSampleRate, &lowSampleRate);
			}
		} 
		else if (sampleRates) 
		{
			lowSampleRate.whole = sampleRates[0];
			lowSampleRate.fraction = 0;
			highSampleRate.whole = sampleRates[1];
			highSampleRate.fraction = 0;
			mMainStream->addAvailableFormat (&streamFormat, &streamFormatExtension, &lowSampleRate, &highSampleRate);
		}
	} // if hasDigitalOutput ...

	result = kIOReturnSuccess;

Exit:
	return result;
}

// [rdar://4487489] - Use this method to allocate all USB buffers.
IOBufferMemoryDescriptor * AppleUSBAudioEngine::allocateBufferDescriptor (IOOptionBits options, vm_size_t capacity, vm_offset_t alignment)
{
	IOBufferMemoryDescriptor *	bufferDescriptorPtr = NULL;
	mach_vm_address_t			physicalMask;
	IOOptionBits				usbOptions;
	IOUSBControllerV2 *			usbController;
	
	debugIOLog ("+ AppleUSBAudioEngine[%p]::allocateBufferDescriptor ()", this);
	FailIf (NULL == mUSBAudioDevice, Exit);
	
	FailIf (NULL == mStreamInterface, Exit);
	FailIf (NULL == (usbController = OSDynamicCast (IOUSBControllerV2, mStreamInterface->GetDevice()->GetBus ())), Exit);
	// The following API call was introduced in IOUSBFamily 2.6.0b6 [rdar://4492080]
	FailIf (kIOReturnSuccess !=  usbController->GetLowLatencyOptionsAndPhysicalMask (&usbOptions, &physicalMask), Exit);
	options |= usbOptions;
	debugIOLog ("? AppleUSBAudioEngine[%p]::allocateBufferDescriptor () - allocating a buffer with mask 0x%x", this, physicalMask);
	bufferDescriptorPtr = IOBufferMemoryDescriptor::inTaskWithPhysicalMask(kernel_task, options, capacity, physicalMask);
	
Exit:
	debugIOLog ("- AppleUSBAudioEngine[%p]::allocateBufferDescriptor () = %p", this, bufferDescriptorPtr);
	return bufferDescriptorPtr;
}

bool AppleUSBAudioEngine::audioDevicePublished (AppleUSBAudioEngine * audioEngine, void * ref, IOService * newService) {
	AppleUSBAudioDevice *		audioDevice;
	IOUSBInterface *			thisControlInterface;
	IOUSBInterface *			thisStreamInterface;
	bool						resultCode;

	debugIOLog ("+ AppleUSBAudioEngine::audioDevicePublished (%p, %p, %p)", audioEngine, (UInt32*)ref, newService);
	resultCode = FALSE;	// Assume failure

	//	This one is a trick : because we are not sure in which order the parts of the
	//	USB driver will be loaded, we have to wait until the stream interface finds a corresponding
	//	USB partner. This is the test that is telling us when we can stop waiting
	FailIf (NULL == audioEngine, Exit);
	FailIf (NULL == newService, Exit);

	audioDevice = OSDynamicCast (AppleUSBAudioDevice, newService);
	FailIf (NULL == audioDevice, Exit);

	thisControlInterface = OSDynamicCast (IOUSBInterface, audioDevice->getProvider ());
	FailIf (NULL == thisControlInterface, Exit);

	thisStreamInterface = OSDynamicCast (IOUSBInterface, audioEngine->getProvider ());
	FailIf (NULL == thisStreamInterface, Exit);

	if (thisControlInterface->GetDevice () == thisStreamInterface->GetDevice ()) 
	{
		if (audioDevice->ControlsStreamNumber (audioEngine->mInterfaceNumber)) 
		{
			debugIOLog ("? AppleUSBAudioEngine[%p]::audioDevicePublished () - Found device (%p) for Audio Engine (%p)", audioEngine, audioDevice, audioEngine);
			audioEngine->mUSBAudioDevice = audioDevice;
			audioEngine->mDeviceAndEngineSyncer->signal (kIOReturnSuccess, FALSE);
			resultCode = TRUE;	// Success!
		}
	}

Exit:
	debugIOLog ("- AppleUSBAudioEngine::audioDevicePublished (%p, %p, %p)", audioEngine, (UInt32 *)ref, newService);
	return resultCode;
}

void AppleUSBAudioEngine::CalculateSamplesPerFrame (UInt32 sampleRate, UInt16 * averageFrameSamples, UInt16 * additionalSampleFrameFreq) {
	UInt32					divisor;

	*averageFrameSamples = sampleRate / 1000;

	divisor = (sampleRate % 1000);

	if (divisor)
		*additionalSampleFrameFreq = 1000 / divisor;
	else
		*additionalSampleFrameFreq = 0;
}

IOReturn AppleUSBAudioEngine::CheckForAssociatedEndpoint (USBAudioConfigObject *usbAudio) {
	IOUSBFindEndpointRequest			associatedEndpoint;
	IOReturn							result;
	UInt8								assocEndpoint;
	UInt8								address;
 	UInt8								syncType;
	UInt8								maxPacketSize;

	result = kIOReturnSuccess;
	mAssociatedPipe = NULL;
	address = usbAudio->GetIsocEndpointAddress (mInterfaceNumber, mAlternateSettingID, mDirection);
	syncType = usbAudio->GetIsocEndpointSyncType (mInterfaceNumber, mAlternateSettingID, address);
	FailIf (NULL == mUSBAudioDevice, Exit);
	if (kAsynchSyncType == syncType) 
	{
		// debugIOLog ("checking endpoint %d for an associated endpoint", address);
		assocEndpoint = usbAudio->GetIsocAssociatedEndpointAddress (mInterfaceNumber, mAlternateSettingID, address);
		if (assocEndpoint != 0) 
		{
			mRefreshInterval = usbAudio->GetIsocAssociatedEndpointRefreshInt (mInterfaceNumber, mAlternateSettingID, assocEndpoint);
			maxPacketSize = usbAudio->GetIsocAssociatedEndpointMaxPacketSize (mInterfaceNumber, mAlternateSettingID, assocEndpoint);
			if (kUSBDeviceSpeedHigh == mUSBAudioDevice->getDeviceSpeed())
			{
				// Request 4 bytes for the 16.16 value if the endpoint allows it
				mFeedbackPacketSize = (maxPacketSize < kFixedPoint16_16ByteSize) ? maxPacketSize : kFixedPoint16_16ByteSize;
			}
			else
			{
				mFeedbackPacketSize = kFixedPoint10_14ByteSize;
			}
			debugIOLog ("? AppleUSBAudioEngine[%p]::CheckForAssociatedEndpoint () - Synch endpoint has refresh interval %d, feedback packet size %d", this, mRefreshInterval, mFeedbackPacketSize);
			mFramesUntilRefresh = 1 << mRefreshInterval;		// the same as 2^mRefreshInterval
			// The hardware might not need to be updated as often as we were planning on (currently we queue 10 lists with 10ms of audio each).
			// If they don't need to be updated that often, then just keep everything at 10ms intervals to keep things standard.
			if (mFramesUntilRefresh < mNumUSBFramesPerList) 
			{
				debugIOLog ("? AppleUSBAudioEngine[%p]::CheckForAssociatedEndpoint () - Need to adjust mNumUSBFramesPerList: %ld < %ld", mFramesUntilRefresh, mNumUSBFramesPerList);
				if (NULL != mUSBIsocFrames) 
				{
					debugIOLog ("? AppleUSBAudioEngine[%p]::CheckForAssociatedEndpoint () - Disposing of current mUSBIsocFrames [%p]", this, mUSBIsocFrames);
					IOFree (mUSBIsocFrames, mNumUSBFrameLists * mNumUSBFramesPerList * sizeof (IOUSBLowLatencyIsocFrame));
					mUSBIsocFrames = NULL;
				}
				mNumUSBFramesPerList = mFramesUntilRefresh;		// It needs to be updated more often, so run as the device requests.
				mUSBIsocFrames = (IOUSBLowLatencyIsocFrame *)IOMalloc (mNumUSBFrameLists * mNumUSBFramesPerList * sizeof (IOUSBLowLatencyIsocFrame));
				debugIOLog ("? AppleUSBAudioEngine[%p]::CheckForAssociatedEndpoint () - mUSBIsocFrames is now %p", this, mUSBIsocFrames);
				FailIf (NULL == mUSBIsocFrames, Exit);
			}
			associatedEndpoint.type = kUSBIsoc;
			associatedEndpoint.direction = kUSBIn;	// The associated endpoint always goes "in"
			// The sample rate should be either a 3-byte 10.14 or a 4-byte 16.16
			associatedEndpoint.maxPacketSize = mFeedbackPacketSize;
			associatedEndpoint.interval = 0xFF;		// don't care
			mAssociatedPipe = mStreamInterface->FindNextPipe (NULL, &associatedEndpoint);
			FailWithAction (NULL == mAssociatedPipe, result = kIOReturnError, Exit);

			if (NULL == mAssociatedEndpointMemoryDescriptor) 
			{
				mAverageSampleRateBuffer = (UInt32 *)IOMallocContiguous (sizeof (UInt32), sizeof (UInt32), NULL);
				FailIf (NULL == mAverageSampleRateBuffer, Exit);
				bzero (mAverageSampleRateBuffer, 4);
				mAssociatedEndpointMemoryDescriptor = IOMemoryDescriptor::withAddress (mAverageSampleRateBuffer, 4, kIODirectionIn);
				FailIf (NULL == mAssociatedEndpointMemoryDescriptor, Exit);
			}
			mSampleRateFrame.frStatus = -1;
			mSampleRateFrame.frReqCount = mFeedbackPacketSize;
			mSampleRateFrame.frActCount = 0;
			mSampleRateCompletion.target = (void *)this;
			mSampleRateCompletion.action = sampleRateHandler;
			mSampleRateCompletion.parameter = 0;

			mAssociatedPipe->retain ();
			// debugIOLog ("! retained mAssociatedPipe = %p", mAssociatedPipe);
		} // if (assocEndpoint != 0) 
		else 
		{
			debugIOLog ("! AppleUSBAudioEngine[%p]::CheckForAssociatedEndpoint () - No associated synch endpoint found.", this);
		}
	} // if (kAsynchSyncType == syncType)
	else 
	{
		debugIOLog ("? AppleUSBAudioEngine[%p]::CheckForAssociatedEndpoint () - No associated synch endpoint.", this);
	}

Exit:
	return result;
}

IOReturn AppleUSBAudioEngine::clipOutputSamples (const void *mixBuf, void *sampleBuf, UInt32 firstSampleFrame, UInt32 numSampleFrames, const IOAudioStreamFormat *streamFormat, IOAudioStream *audioStream) {
	UInt64						curUSBFrameNumber;
	UInt64						framesLeftInQueue;
	void *						iSubBuffer = NULL;
	UInt32						iSubBufferLen = 0;
	UInt32						sampleRate;
	SInt32						offsetDelta;
	SInt32						safetyOffset;
	IOReturn					result;
	iSubAudioFormatType			iSubFormat;	// aml 3.1.02

	result = kIOReturnError;
	
	if (0 == mShouldStop && TRUE != mInCompletion) 
	{
		curUSBFrameNumber = mStreamInterface->GetDevice()->GetBus()->GetFrameNumber ();
		framesLeftInQueue = mUSBFrameToQueue - curUSBFrameNumber;
		if (framesLeftInQueue < (mNumUSBFramesPerList * (mNumUSBFrameListsToQueue / 2)) / 2) 
		{
			debugIOLog ("! AppleUSBAudioEngine::clipOutputSamples () - Queue a write from clipOutputSamples: framesLeftInQueue = %ld", (UInt32)framesLeftInQueue);
			writeHandler (this, mUSBCompletion[mCurrentFrameList].parameter, kIOReturnSuccess, &mUSBIsocFrames[mCurrentFrameList * mNumUSBFramesPerList]);
		}
	}
    
	if (TRUE == streamFormat->fIsMixable) 
	{
		if (NULL != miSubBufferMemory && NULL != miSubEngine) 
		{
			iSubBufferLen = miSubBufferMemory->getLength ();
			iSubBuffer = (void*)miSubBufferMemory->getVirtualSegment (0, &iSubBufferLen);
			// (iSubBufferLen / 2) is because miSubBufferOffset is in UInt16s so convert iSubBufferLen to UInt16 length
			iSubBufferLen = iSubBufferLen / 2;

			sampleRate = getSampleRate()->whole;

			// aml 3.1.02
			iSubFormat.altInterface = miSubEngine->GetAltInterface();
			iSubFormat.numChannels = miSubEngine->GetNumChannels();
			iSubFormat.bytesPerSample = miSubEngine->GetBytesPerSample();
			iSubFormat.outputSampleRate = miSubEngine->GetSampleRate();

			// aml 3.29.02 begin move - moved out of sync check to before offset calculation

			// Detect being out of sync with the iSub
			// aml 3.29.02 updated to match AOA
			#if ABORT_PIPE_ON_START
			if (mNeedToSync == FALSE && mLastClippedFrame == firstSampleFrame && 0x0 != miSubEngine->GetCurrentLoopCount ())
			#else
			if (mNeedToSync == FALSE && mLastClippedFrame == firstSampleFrame && 0xFFFFFFFF != miSubEngine->GetCurrentLoopCount ())
			#endif
			{
				// aml 3.29.02 reordered and ajdusted these checks to match AOA
				// aml - make the reader/writer check more strict - this helps get rid of long term crunchy iSub audio
				// the reader is now not allowed within one frame (one millisecond of audio) of the writer
				safetyOffset = miSubBufferOffset - ((iSubFormat.outputSampleRate) / 1000);		// 6 samples at 6kHz
				if (safetyOffset < 0) 
				{
					safetyOffset += iSubBufferLen;
				}
				if (miSubLoopCount == miSubEngine->GetCurrentLoopCount () && safetyOffset < (SInt32)(miSubEngine->GetCurrentByteCount () / 2)) 
				{
					#if DEBUGISUB
					debugIOLog ("****iSub is in front of write head miSubBufferOffset = %ld, miSubEngine->GetCurrentByteCount () / 2 = %ld", miSubBufferOffset, miSubEngine->GetCurrentByteCount () / 2);
					#endif
					mNeedToSync = TRUE;
					mStartiSub = TRUE;
				} 
				else if (miSubLoopCount > (miSubEngine->GetCurrentLoopCount () + 1)) 
				{
					#if DEBUGISUB
					debugIOLog ("****looped more than the iSub miSubLoopCount = %ld, miSubEngine->GetCurrentLoopCount () = %ld", miSubLoopCount, miSubEngine->GetCurrentLoopCount ());
					#endif
					mNeedToSync = TRUE;
					mStartiSub = TRUE;
				} 
				else if (miSubLoopCount < miSubEngine->GetCurrentLoopCount ()) 
				{
					#if DEBUGISUB
					debugIOLog ("****iSub is ahead of us miSubLoopCount = %ld, miSubEngine->GetCurrentLoopCount () = %ld", miSubLoopCount, miSubEngine->GetCurrentLoopCount ());
					#endif
					mNeedToSync = TRUE;
					mStartiSub = TRUE;
				} 
				else if (miSubLoopCount == miSubEngine->GetCurrentLoopCount () && miSubBufferOffset > ((SInt32)((miSubEngine->GetCurrentByteCount() + (((iSubFormat.outputSampleRate)/1000 * NUM_ISUB_FRAME_LISTS_TO_QUEUE * NUM_ISUB_FRAMES_PER_LIST) * iSubFormat.bytesPerSample * iSubFormat.numChannels)) / 2))) 
				{		
				// aml 3.27.02, this is the right number here (buffersize was 2x too large).  This number should come eventually from the iSub engine reporting it's maximum number of queued bytes.
					#if DEBUGISUB		
					debugIOLog ("****iSub is too far behind write head miSubBufferOffset = %ld, (miSubEngine->GetCurrentByteCount () / 2 + iSubBufferLen) = %ld", miSubBufferOffset, (miSubEngine->GetCurrentByteCount() / 2 + iSubBufferLen));					
					#endif
					mNeedToSync = TRUE;
					mStartiSub = TRUE;
				}
			} // if

			// aml 3.29.02 end move

			if (FALSE == mNeedToSync && mLastClippedFrame != firstSampleFrame && !(mLastClippedFrame == getNumSampleFramesPerBuffer () && firstSampleFrame == 0)) 
			{
				#if DEBUGISUB
				debugIOLog ("miSubBufferOffset was %ld", miSubBufferOffset);
				#endif
				if (firstSampleFrame < mLastClippedFrame) 
				{
					// We've wrapped around the buffer
					// don't multiply by bit width because miSubBufferOffset is a UInt16 buffer pointer, not a UInt8 buffer pointer
					// aml 3.29.02 flipped +/- to match AOA
					offsetDelta = (getNumSampleFramesPerBuffer () - firstSampleFrame + mLastClippedFrame) * miSubEngine->GetNumChannels();
				} 
				else 
				{
					offsetDelta = (firstSampleFrame - mLastClippedFrame) * miSubEngine->GetNumChannels();
				}
				// aml 3.21.02, adjust for new sample rate
				offsetDelta = (offsetDelta * 1000) / ((sampleRate * 1000) / iSubFormat.outputSampleRate);
				miSubBufferOffset += offsetDelta;
				#if DEBUGISUB
				debugIOLog ("clip to point was %ld, now %ld (delta = %ld)", mLastClippedFrame, firstSampleFrame, offsetDelta);
				debugIOLog ("miSubBufferOffset is now %ld", miSubBufferOffset);
				#endif
				if (miSubBufferOffset > (SInt32)iSubBufferLen) 
				{
					// Our calculated spot has actually wrapped around the iSub's buffer.
					miSubLoopCount += miSubBufferOffset / iSubBufferLen;
					miSubBufferOffset = miSubBufferOffset % iSubBufferLen;
					#if DEBUGISUB
					debugIOLog ("miSubBufferOffset > iSubBufferLen, miSubBufferOffset is now %ld", miSubBufferOffset);
					#endif
				} 
				else if (miSubBufferOffset < 0) 
				{
					miSubBufferOffset += iSubBufferLen;
					#if DEBUGISUB
					debugIOLog ("miSubBufferOffset < 0, miSubBufferOffset is now %ld", miSubBufferOffset);
					#endif
				}
			}

		// aml 3.29.02 added this check to match AOA
		if (TRUE == mJustResetClipPosition) 
		{
			mJustResetClipPosition = FALSE;
			mNeedToSync = FALSE;
			mStartiSub = FALSE;
		}

		// sync up with iSub only if everything is proceeding normally.
		// aml [3095619] - added check with miSubEngine for sync state.
		if (    (TRUE == mNeedToSync) 
			 || (miSubEngine->GetNeedToSync())) 
		{
			mNeedToSync = FALSE;
				
			// aml [3095619] - reset state
			miSubEngine->SetNeedToSync(false);
				
			mSrcPhase = 1.0;		// aml 3.4.02	
			mSrcState = 0.0;		// aml 3.6.02

			// start the filter over again since old filter state is invalid
			mFilterState.xl_1 = 0.0;
			mFilterState.xr_1 = 0.0;
			mFilterState.xl_2 = 0.0;
			mFilterState.xr_2 = 0.0;
			mFilterState.yl_1 = 0.0;
			mFilterState.yr_1 = 0.0;
			mFilterState.yl_2 = 0.0;
			mFilterState.yr_2 = 0.0;

			// aml 2.14.02 added for 4th order filter
			mFilterState2.xl_1 = 0.0;
			mFilterState2.xr_1 = 0.0;
			mFilterState2.xl_2 = 0.0;
			mFilterState2.xr_2 = 0.0;
			mFilterState2.yl_1 = 0.0;
			mFilterState2.yr_1 = 0.0;
			mFilterState2.yl_2 = 0.0;
			mFilterState2.yr_2 = 0.0;

			// aml 2.18.02 added for 4th order filter phase compensator
			mPhaseCompState.xl_1 = 0.0;
			mPhaseCompState.xr_1 = 0.0;
			mPhaseCompState.xl_2 = 0.0;
			mPhaseCompState.xr_2 = 0.0;
			mPhaseCompState.yl_1 = 0.0;
			mPhaseCompState.yr_1 = 0.0;
			mPhaseCompState.yl_2 = 0.0;
			mPhaseCompState.yr_2 = 0.0;

			#if ABORT_PIPE_ON_START
			// aml 4.25.02 wipe out the iSub buffer, changed due to moving zeroing of iSub buffer in AUA write handler when aborting the pipe
			bzero(iSubBuffer, iSubBufferLen);
			#endif
			// aml 3.21.02, moving this offset calc code in from AOA (replaces one line above)
			UInt32 curSampleFrame = getCurrentSampleFrame ();
			if (firstSampleFrame < curSampleFrame) 
			{
				offsetDelta = (getNumSampleFramesPerBuffer () - curSampleFrame + firstSampleFrame) * miSubEngine->GetNumChannels();
			}
			else 
			{
				offsetDelta = (firstSampleFrame - curSampleFrame) * miSubEngine->GetNumChannels();
			}
			// aml 3.21.02, adjust for new sample rate
			offsetDelta = (offsetDelta * 1000) / ((sampleRate * 1000) / iSubFormat.outputSampleRate);

			// aml 4.24.02 this was supposed to set the offset, not add it!  Looks like a typo from case above.
			miSubBufferOffset = offsetDelta;

			#if DEBUGISUB
			debugIOLog ("USBEngine: need sync: starting miSubBufferOffset = %ld, miSubLoopCount = %ld", miSubBufferOffset, miSubLoopCount);
			#endif
		}

		if (miSubBufferOffset > (SInt32)iSubBufferLen) 
		{
			mNeedToSync = TRUE;	// aml 4.24.02, requests larger than our buffer size = bad!

			// Our calculated spot has actually wrapped around the iSub's buffer.
			miSubLoopCount += miSubBufferOffset / iSubBufferLen;
			miSubBufferOffset = miSubBufferOffset % iSubBufferLen;
			#if DEBUGISUB
			debugIOLog ("miSubBufferOffset > iSubBufferLen, miSubBufferOffset is now %ld", miSubBufferOffset);
			#endif
		} 
		else if (miSubBufferOffset < 0) 
		{
			miSubBufferOffset += iSubBufferLen;
			#if DEBUGISUB
			debugIOLog ("miSubBufferOffset < 0, miSubBufferOffset is now %ld", miSubBufferOffset);
			#endif
		}
		// aml 2.21.02 added extra filter states for 4th order with phase compensation
		// iSub crossover
		// aml 3.1.02 added format param
		// aml 3.4.02 added mSrcPhase
		// aml 3.6.02 added mSrcState
		if (mPlugin) 
		{
			mPlugin->pluginProcess ((Float32*)mixBuf + (firstSampleFrame * streamFormat->fNumChannels), numSampleFrames, streamFormat->fNumChannels);
		}
		result = clipAppleUSBAudioToOutputStreamiSub (mixBuf, sampleBuf, &mFilterState, &mFilterState2, &mPhaseCompState, mLowFreqSamples, mHighFreqSamples, firstSampleFrame, numSampleFrames, sampleRate, streamFormat, (SInt16*)iSubBuffer, &miSubLoopCount, &miSubBufferOffset, iSubBufferLen, &iSubFormat, &mSrcPhase, &mSrcState);
		if (TRUE == mStartiSub) 
		{
			miSubEngine->StartiSub ();
			mStartiSub = FALSE;
			miSubLoopCount = 0;
		}
		
		// aml 3.29.02 moved here to match AOA
		mLastClippedFrame = firstSampleFrame + numSampleFrames;
		} 
		else 
		{
			if (mPlugin) 
			{
				mPlugin->pluginProcess ((Float32*)mixBuf + (firstSampleFrame * streamFormat->fNumChannels), numSampleFrames, streamFormat->fNumChannels);
			}
			result = clipAppleUSBAudioToOutputStream (mixBuf, sampleBuf, firstSampleFrame, numSampleFrames, streamFormat);
			
			#if DEBUGLATENCY
				if (!mHaveClipped)
				{
					mHaveClipped = true;
					// debugIOLog ("! FIRST clip at sample frame %lu when current USB Frame is %llu", firstSampleFrame, mStreamInterface->GetDevice()->GetBus()->GetFrameNumber());
					// debugIOLog ("! AppleUSBAudioEngine::clipOutputSamples() - Sample frame %lu queued for USB frame %llu (current frame = %llu)", firstSampleFrame, getQueuedFrameForSample (firstSampleFrame), mStreamInterface->GetDevice()->GetBus()->GetFrameNumber());
				}
				else if ((0 == firstSampleFrame) || true)
				{
					// debugIOLog ("! clipOutputSamples: Index %lu of sample buffer written when current USB frame is %llu", firstSampleFrame, mStreamInterface->GetDevice()->GetBus()->GetFrameNumber());
					// debugIOLog ("! AppleUSBAudioEngine::clipOutputSamples() - Sample frame %lu queued for USB frame %llu (current frame = %llu)", firstSampleFrame, getQueuedFrameForSample (firstSampleFrame), mStreamInterface->GetDevice()->GetBus()->GetFrameNumber());
				}
			#endif
		}
		if (mUHCISupport)
		{
			// If we've wrapped, copy to sampleBuffer extension create/keep mLastClippedFrame for nonmixable & non isub cases.
			// If we return and see frame count lower than mLastClippedFrame, we've wrapped.
		
			UInt32 start = firstSampleFrame * streamFormat->fNumChannels * (streamFormat->fBitWidth / 8); // first byte index of first sample that is being clipped
			if ( start < mAlternateFrameSize )
			{
				// This is correct because mAverageFrameSize is in bytes. size is the amount of data we want to copy into the scribble-ahead area. 
				UInt32 size = mAlternateFrameSize - start;
				memcpy( &( ( ( char * )sampleBuf )[ mSampleBufferSize + start ] ), & ( ( ( char * )sampleBuf )[ start ] ), size );
				#if DEBUGUHCI
				debugIOLog( "? AppleUSBAudioEngine::clipOutputSamples () - firstSampleFrame = %d. Just copied %d bytes from %d to %d \n", firstSampleFrame, size, start, start + mSampleBufferSize );			
				#endif
			}
		} // UHCI support

	} 
	else 
	{
		// Non-mixable case
		UInt32			offset;

		offset = firstSampleFrame * streamFormat->fNumChannels * (streamFormat->fBitWidth / 8);

		memcpy ((UInt8 *)sampleBuf + offset, (UInt8 *)mixBuf, numSampleFrames * streamFormat->fNumChannels * (streamFormat->fBitWidth / 8));
		mLastClippedFrame = firstSampleFrame + numSampleFrames;
		result = kIOReturnSuccess;
	}

	return result;
}

// This function is called from both the IOProc's call to convertInputSamples and by the readHandler.
// To figure out where to start coalescing from, it looks at the mCurrentFrameList, which is updated by the readHandler.
// It will copy from currentFameList+1 the number of bytes requested or one USB frame list.
// When numBytesToCoalesce == 0 it will coalesce the current USB frame list (however big it is).
// If numBytesToCoalesce != 0, it will coalesce that many bytes starting from the current frame list and going to the next one if needed.
// When called from the readHandler it will just coalesce one USB frame starting from mCurrentFrameList.
// When called from convertInputSamples, it will convert the number of bytes that corresponds to the number of samples that are being asked to be converted,
// starting from mCurrentFrameList.

void AppleUSBAudioEngine::CoalesceInputSamples (UInt32 numBytesToCoalesce, IOUSBLowLatencyIsocFrame * pFrames) {
	AbsoluteTime					time;
	UInt32							usbFrameIndex;
	UInt32							numFramesChecked;
	UInt32							numBytesToCopy;
	UInt32							numBytesToEnd;
	UInt32							numBytesCopied;
	UInt32							originalBufferOffset;
	SInt32							numBytesLeft;
	UInt32							preWrapBytes = 0;
	UInt32							byteCount = 0;
	UInt8 *							source;
	UInt8 *							dest;
	Boolean							done;
	bool							onCoreAudioThread;
    
	if (mCoalescenceMutex)
	{
		IOLockLock (mCoalescenceMutex);
	}
	
	#if DEBUGINPUT
	debugIOLog ("+ AppleUSBAudioEngine[%p]::CoalesceInputSamples (%lu, %p)", this, numBytesToCoalesce, pFrames); 
	#endif
	
	originalBufferOffset = 0;
	if (0 != numBytesToCoalesce) 
	{
		// This is being called from the CoreAudio thread
		onCoreAudioThread = true;
		originalBufferOffset = mBufferOffset;		// So that when we later get called from the readHandler that we'll put everything in the right spot
		#if DEBUGINPUT
		debugIOLog ("! AppleUSBAudioEngine[%p]::CoalesceInputSamples () - Coalesce from %ld %ld bytes (framelist %ld) on CoreAudio thread", this, originalBufferOffset, numBytesToCoalesce, mCurrentFrameList);
		#endif
	}
	else
	{
		onCoreAudioThread = false;
	}

	if (NULL == pFrames) 
	{
		pFrames = &mUSBIsocFrames[mCurrentFrameList * mNumUSBFramesPerList];
	}

	dest = (UInt8 *)getSampleBuffer () + mBufferOffset;
	source = (UInt8 *)mReadBuffer + (mCurrentFrameList * mReadUSBFrameListSize);

	usbFrameIndex = 0;
	numFramesChecked = 0;
	numBytesCopied = 0;
	numBytesLeft = numBytesToCoalesce;
	done = FALSE;

	while (    (FALSE == done) 
			&& ('llit' != pFrames[usbFrameIndex].frStatus)			// IOUSBFamily is processing this now
			&& (-1 != pFrames[usbFrameIndex].frStatus))				// IOUSBFamily hasn't gotten here yet
	{
		// Log unusual status here
		if (		(!(mShouldStop))
				&&	(		(kIOReturnSuccess != pFrames[usbFrameIndex].frStatus)
				&&	(		(kIOReturnUnderrun != pFrames[usbFrameIndex].frStatus)
						||	(pFrames[usbFrameIndex].frActCount < mAverageFrameSize - mSampleSize))))
		{
			debugIOLog ("! AppleUSBAudioEngine[%p]::CoalesceInputSamples () - encountered unusual frame with status 0x%x in frame list %lu", this, pFrames[usbFrameIndex].frStatus, usbFrameIndex);
			debugIOLog ("     pFrames[%lu].frStatus = 0x%x", usbFrameIndex, pFrames[usbFrameIndex].frStatus);
			debugIOLog ("     pFrames[%lu].frReqCount = %lu", usbFrameIndex, pFrames[usbFrameIndex].frReqCount);
			debugIOLog ("     pFrames[%lu].frActCount = %lu", usbFrameIndex, pFrames[usbFrameIndex].frActCount);
			debugIOLog ("     pFrames[%lu].frTimeStamp = 0x%x", usbFrameIndex, (* (UInt64 *) &(pFrames[usbFrameIndex].frTimeStamp)));
			// break;
		}
		
		numBytesToEnd = getSampleBufferSize () - mBufferOffset;
		
		// We should take the first time stamp now if we are receiving our first byte when we expect; otherwise wait until the first buffer loop.
		if	(		(!mHaveTakenFirstTimeStamp)
				&&	(0 == mBufferOffset)
				&&	(pFrames[usbFrameIndex].frActCount > 0))
		{
			debugIOLog ("? AppleUSBAudioEngine::CoalesceInputSamples () - Taking first time stamp.");
			time = generateTimeStamp (usbFrameIndex, 0, 0);
			takeTimeStamp (false, &time);
		}
		
		if ((UInt32)(pFrames[usbFrameIndex].frActCount) > numBytesToEnd) 
		{
			// This copy will wrap
			numBytesToCopy = numBytesToEnd;
			
			// Store numbers for time stamping
			preWrapBytes = numBytesToEnd;
			byteCount = pFrames[usbFrameIndex].frActCount;
		} 
		else 
		{
			numBytesToCopy = pFrames[usbFrameIndex].frActCount;
			if (0 == numBytesToCoalesce) 
			{
				pFrames[usbFrameIndex].frActCount = 0;
				#ifdef DEBUG
				// We don't want to see these frames logged as errors later, so cook the error code if necessary.
				if	(kIOReturnUnderrun == pFrames[usbFrameIndex].frStatus)
				{ 
					pFrames[usbFrameIndex].frStatus = kIOReturnSuccess;
				}
				#endif DEBUG
			}
		}

		if (0 != numBytesToCopy)
		{
			memcpy (dest, source, numBytesToCopy);
			mBufferOffset 	+= numBytesToCopy;
			numBytesLeft 	-= numBytesToCopy;
			// debugIOLog ("   coalesced %lu bytes, %lu bytes to end of buffer", numBytesToCopy, getSampleBufferSize () - mBufferOffset);
		}
		numBytesCopied 	= numBytesToCopy;

		if (pFrames[usbFrameIndex].frActCount > numBytesToEnd) 
		{
			numBytesToCopy = pFrames[usbFrameIndex].frActCount - numBytesToEnd;
			dest = (UInt8 *)getSampleBuffer ();
			memcpy (dest, source + numBytesCopied, numBytesToCopy);
			mBufferOffset = numBytesToCopy;
			// debugIOLog ("   coalesced %lu bytes, %lu bytes to end of buffer", numBytesToCopy, getSampleBufferSize () - mBufferOffset);
			numBytesLeft -= numBytesToCopy;

			if (0 == numBytesToCoalesce) 
			{
				// we have wrapped and we were called by the completion routine -- take timestamp
				// Calculate a time stamp based on our filtered rates
				time = generateTimeStamp (usbFrameIndex, preWrapBytes, byteCount);
				takeTimeStamp (TRUE, &time);
			}
		}

		dest += numBytesToCopy;
		source += pFrames[usbFrameIndex].frReqCount;
		usbFrameIndex++;
		numFramesChecked++;
		if (0 != numBytesToCoalesce && (usbFrameIndex + (mCurrentFrameList * mNumUSBFramesPerList)) == (mNumUSBFrameLists * mNumUSBFramesPerList)) 
		{
			pFrames = &mUSBIsocFrames[0];			// wrap around the frame list and keep trying to coalesce
			usbFrameIndex = 0;
			source = (UInt8 *)mReadBuffer;
			//debugIOLog ("wrapping coalesce numBytesToCoalesce = %d", numBytesToCoalesce); 
		}
		if (((0 == numBytesToCoalesce) && (mNumUSBFramesPerList == usbFrameIndex)) ||		// We've coalesced the current frame list
			((0 != numBytesToCoalesce) && (0 >= numBytesLeft)) ||							// We've coalesced the requested number of bytes
			((0 != numBytesToCoalesce) && (numFramesChecked >= (mNumUSBFramesPerList * mNumUSBFrameLists)))) // We've gone through all the frame lists and there's nothing left to coalesce (starvation case)
		{			
			done = TRUE;
		}
	}

	if (0 != numBytesToCoalesce) 
	{
		mBufferOffset = originalBufferOffset;
	}

	// Log here if we are requesting more bytes than is possible to coalesce in mNumUSBFramesPerList.
	if (		(0 != numBytesToCoalesce)
			&&	(numBytesLeft > 0))
	{
		debugIOLog ("! AppleUSBAudioEngine[%p]::CoalesceInputSamples () - Requested: %lu, Remaining: %lu on frame list %lu", this, numBytesToCoalesce, numBytesLeft, mCurrentFrameList);
	}

	#if DEBUGINPUT
	debugIOLog ("- AppleUSBAudioEngine[%p]::CoalesceInputSamples (%lu, %p)", this, numBytesToCoalesce, pFrames);
	#endif
	
	if (mCoalescenceMutex)
	{
		IOLockUnlock (mCoalescenceMutex);
	}
	
	return;
}

// [rdar://3918719] The following method now does the work of performFormatChange after being regulated by AppleUSBAudioDevice::formatChangeController().
IOReturn AppleUSBAudioEngine::controlledFormatChange (IOAudioStream *audioStream, const IOAudioStreamFormat *newFormat, const IOAudioSampleRate *newSampleRate)
{
    IOReturn							result;
	USBAudioConfigObject *				usbAudio;
    void *								sampleBuffer;
	UInt32								i;
	UInt32								numSamplesInBuffer;
	UInt32								minimumSafeSampleOffset;
	UInt32								cautiousSafeSampleOffset;
	UInt16								averageFrameSamples;
	UInt16								additionalSampleFrameFreq;
	UInt8								mInterfaceNumber;
	UInt8								newAlternateSettingID;
	UInt8								newDirection;
	bool								highSpeedCompensation;
	bool								needToChangeChannels;
	bool								forcedFormatChange = false;
	// bool								streamIsRunning;

	debugIOLog ("+ AppleUSBAudioEngine[%p]::controlledFormatChange (%p, %p, %p)", this, audioStream, newFormat, newSampleRate);

	result = kIOReturnError;

	FailIf (NULL == mStreamInterface, Exit);
	FailIf (NULL == mUSBAudioDevice, Exit);
	
	usbAudio = mUSBAudioDevice->GetUSBAudioConfigObject ();
	FailIf (NULL == usbAudio, Exit);

	if	(		(newFormat == NULL)
			&&	(audioStream != NULL))
	{
		result = kIOReturnSuccess;
		goto Exit;
	}
	
	if (NULL == audioStream)
	{
		// This is an emergency format change request initiated to keep the input and output at the same sample rate. We need to supply the correct stream for this engine.
		forcedFormatChange = true;
		audioStream = mMainStream;
		
		// We also need to get the format if it wasn't supplied.
		if (NULL == newFormat)
		{
			newFormat = &(audioStream->format);
		}
	}
	FailIf (NULL == audioStream, Exit);
	FailIf (NULL == newFormat, Exit);
	
	// Can't rely on the driver tag to be correct because IOAudioFamily only looks for formats without respect to sample rate,
	// but it's an optimization in the general case.
	mInterfaceNumber = (UInt8)(newFormat->fDriverTag >> 16);
	newAlternateSettingID = (UInt8)(newFormat->fDriverTag);
	
	if (newFormat->fNumChannels != audioStream->format.fNumChannels) 
	{
		needToChangeChannels = TRUE;
		debugIOLog ("? AppleUSBAudioEngine[%p]::controlledFormatChange () - Need to adjust channel controls (cur = %d, new = %d)", this, audioStream->format.fNumChannels, newFormat->fNumChannels);
		
		if (kIOAudioStreamDirectionOutput == mDirection)
		{
			// check for mono mode
			if (1 == newFormat->fNumChannels)
			{
				mUSBAudioDevice->setMonoState (TRUE);
			}
			else
			{
				mUSBAudioDevice->setMonoState (FALSE);
			}
		}
	} 
	else
	{
		needToChangeChannels = FALSE;
		// debugIOLog ("No need to adjust channel controls.");
	}
	
	if (newSampleRate) 
	{
		debugIOLog ("? AppleUSBAudioEngine[%p]::controlledFormatChange () - Changing sampling rate to %d", this, newSampleRate->whole);
		mCurSampleRate = *newSampleRate;
	} 
	else 
	{
		// debugIOLog ("Keeping existing sampling rate of %d", mCurSampleRate.whole);
	}

	if (FALSE == usbAudio->VerifySampleRateIsSupported (mInterfaceNumber, newAlternateSettingID, mCurSampleRate.whole)) 
	{
		debugIOLog ("? AppleUSBAudioEngine[%p]::controlledFormatChange () - %d channel %d bit @ %d Hz is not supported by alternate setting %d.", this,
					newFormat->fNumChannels, newFormat->fBitDepth, mCurSampleRate.whole, newAlternateSettingID);
		newAlternateSettingID = usbAudio->FindAltInterfaceWithSettings (mInterfaceNumber, newFormat->fNumChannels, newFormat->fBitDepth, mCurSampleRate.whole);
		debugIOLog ("? AppleUSBAudioEngine[%p]::controlledFormatChange () - Suggesting alternate setting %d (255 indicates failure)", this, newAlternateSettingID);
		
		FailIf (255 == newAlternateSettingID, Exit);
	}

	newDirection = usbAudio->GetIsocEndpointDirection (mInterfaceNumber, newAlternateSettingID);
	FailIf (newDirection != mDirection, Exit);
	debugIOLog ("? AppleUSBAudioEngine[%p]::controlledFormatChange () - about to set: mInterfaceNumber = %d & newAlternateSettingID = %d", this, mInterfaceNumber, newAlternateSettingID);
	mAlternateSettingID = newAlternateSettingID;

	if (mPlugin) 
	{
		mPlugin->pluginSetFormat (newFormat, &mCurSampleRate);
	}

	// Set the sampling rate on the device
	(void)SetSampleRate (usbAudio, mCurSampleRate.whole);		// no need to check the error, it's not a real problem if it doesn't work

	mAverageSampleRate = mCurSampleRate.whole;	// Set this as the default until we are told otherwise
	// debugIOLog ("mAverageSampleRate = %d", mAverageSampleRate);
	CalculateSamplesPerFrame (mCurSampleRate.whole, &averageFrameSamples, &additionalSampleFrameFreq);
	debugIOLog ("? AppleUSBAudioEngine[%p]::controlledFormatChange () - averageFrameSamples = %d, additionalSampleFrameFreq = %d", this, averageFrameSamples, additionalSampleFrameFreq);

	mSampleSize = newFormat->fNumChannels * (newFormat->fBitWidth / 8);
	mAverageFrameSize = averageFrameSamples * mSampleSize;
	mAlternateFrameSize = (averageFrameSamples + 1) * mSampleSize;
	debugIOLog ("? AppleUSBAudioEngine[%p]::controlledFormatChange () - mAverageFrameSize = %d, mAlternateFrameSize = %d", this, mAverageFrameSize, mAlternateFrameSize);

	// You have to make the read buffer the size of the alternate frame size because we have to ask for mAlternateFrameSize bytes
	// with each read.  If you don't make the buffer big enough, you don't get all the data from the last frame...
	// USB says that if the device is running at an even multiple of the bus clock (i.e. 48KHz) that it can send frames that 
	// have +/- one sample (i.e. 47, 48, or 49 samples per frame) from the average.  This differs from when it's not a even 
	// multiple and it can send only +1.5 sample from the average.
	if (kUSBIn == mDirection) 
	{
		mReadUSBFrameListSize = mAlternateFrameSize * mNumUSBFramesPerList;
	}

	// Need a minimum of two pages in OHCI/UHCI
	numSamplesInBuffer = mAverageSampleRate / 4;
	numSamplesInBuffer += (PAGE_SIZE*2 - 1);
	numSamplesInBuffer /= PAGE_SIZE*2;
	numSamplesInBuffer *= PAGE_SIZE*2;
	mSampleBufferSize = numSamplesInBuffer * mSampleSize;
	if (mUHCISupport)
	{
		mSampleBufferSizeExtended = mSampleBufferSize + PAGE_SIZE;
	}
	else
	{
		mSampleBufferSizeExtended = mSampleBufferSize;
	}
	
	debugIOLog("? AppleUSBAudioEngine[%p]::controlledFormatChange () - New mSampleBufferSize = %d numSamplesInBuffer = %d", this, mSampleBufferSize, numSamplesInBuffer );

	// aml 7.16.02 iSub filters are set up for these sample rates:
	if (NULL != miSubBufferMemory) 
	{
		// debugIOLog ("Checking to see if we should close connection to iSub");
		if (FALSE == isiSubCompatibleFormat (newFormat, mCurSampleRate))
		{
			debugIOLog ("! AppleUSBAudioEngine[%p]::controlledFormatChange () - Closing connection to iSub due to incompatible format", this);
			iSubTeardownConnection ();
		}
	} // if
	else 
	{
		// debugIOLog ("Checking to see if we should open a connection to iSub");
		// debugIOLog ("miSubAttachToggle = %p", miSubAttachToggle);
		if (miSubAttachToggle) 
		{
			// debugIOLog ("miSubAttachToggle value = %d", miSubAttachToggle->getIntValue ());
		}
		if (NULL != miSubAttachToggle && 1 == miSubAttachToggle->getIntValue ()) 
		{
			// debugIOLog ("miSubAttachToggle says we should open connection to iSub");
			
			// Register for iSub attach notifications if necessary
			iSubAttachChangeCall (isiSubCompatibleFormat (newFormat, mCurSampleRate));
		}
	} // else

	if (NULL != mSampleBufferDescriptors) 
	{
		for (i = 0; i < mNumUSBFrameLists; i++) 
		{
			if (NULL != mSampleBufferDescriptors[i]) 
			{
				mSampleBufferDescriptors[i]->release ();
				mSampleBufferDescriptors[i] = NULL;
			}
		}
	}

	if (kUSBIn == mDirection) 
	{
		if (NULL != mReadBuffer) 
		{
			mUSBBufferDescriptor->release ();
		}
		
		mUSBBufferDescriptor = allocateBufferDescriptor (kIODirectionIn, mNumUSBFrameLists * mReadUSBFrameListSize, PAGE_SIZE);
		
		FailIf (NULL == mUSBBufferDescriptor, Exit);
		mReadBuffer = mUSBBufferDescriptor->getBytesNoCopy ();
		FailIf (NULL == mReadBuffer, Exit);

		for (i = 0; i < mNumUSBFrameLists; i++) 
		{
			mSampleBufferDescriptors[i] = OSTypeAlloc (IOSubMemoryDescriptor);
			mSampleBufferDescriptors[i]->initSubRange (mUSBBufferDescriptor, i * mReadUSBFrameListSize, mReadUSBFrameListSize, kIODirectionIn);
			FailIf (NULL == mSampleBufferDescriptors[i], Exit);
		}

		if (NULL != mSampleBufferMemoryDescriptor) 
		{
			mMainStream->setSampleBuffer (NULL, 0);
			setNumSampleFramesPerBuffer (0);
			mSampleBufferMemoryDescriptor->release ();
		}
		
		mSampleBufferMemoryDescriptor = IOBufferMemoryDescriptor::withOptions (kIODirectionInOut, mSampleBufferSize, PAGE_SIZE);
		FailIf (NULL == mSampleBufferMemoryDescriptor, Exit);
		sampleBuffer = mSampleBufferMemoryDescriptor->getBytesNoCopy ();
	} 
	else 
	{
		// This is the output case.
		if (NULL != miSubBufferMemory) 
		{
			if (NULL != mLowFreqSamples) 
			{
				IOFree (mLowFreqSamples, getSampleBufferSize () * 2);
			}

			if (NULL != mHighFreqSamples) 
			{
				IOFree (mHighFreqSamples, getSampleBufferSize () * 2);
			}

			// aml XXX iSub filters are set up for these sample rates:
			if ((2 == newFormat->fNumChannels) && ((8000 == mCurSampleRate.whole) || (11025 == mCurSampleRate.whole) || (22050 == mCurSampleRate.whole) ||
				(44100 == mCurSampleRate.whole) || (48000 == mCurSampleRate.whole) || (96000 == mCurSampleRate.whole))) 
			{
				mLowFreqSamples = (float *)IOMallocAligned (mSampleBufferSize * 2, PAGE_SIZE);
				FailIf (NULL == mLowFreqSamples, Exit);
				mHighFreqSamples = (float *)IOMallocAligned (mSampleBufferSize * 2, PAGE_SIZE);
				FailIf (NULL == mHighFreqSamples, Exit);
			}
		}

		if (NULL != mUSBBufferDescriptor) 
		{
			mMainStream->setSampleBuffer (NULL, 0);
			setNumSampleFramesPerBuffer (0);
			mUSBBufferDescriptor->release ();
		}
		if (mUHCISupport)
		{
			// Allocate an additional alternate frame size (mSampleBufferSizeExtended total) as our scribble ahead for frames that would be wrapped in clipOutputSamples
			mUSBBufferDescriptor = allocateBufferDescriptor (kIODirectionOut, mSampleBufferSizeExtended, PAGE_SIZE);
		}
		else
		{
			mUSBBufferDescriptor = allocateBufferDescriptor (kIODirectionOut, mSampleBufferSize, PAGE_SIZE);
		}
		FailIf (NULL == mUSBBufferDescriptor, Exit);

		for (i = 0; i < mNumUSBFrameLists; i++) 
		{
			if (NULL != mSampleBufferDescriptors[i]) 
			{
				mSampleBufferDescriptors[i]->release ();
			}
			mSampleBufferDescriptors[i] = OSTypeAlloc (IOSubMemoryDescriptor);
			mSampleBufferDescriptors[i]->initSubRange (mUSBBufferDescriptor, 0, mSampleBufferSize, kIODirectionOut);
			FailIf (NULL == mSampleBufferDescriptors[i], Exit);

			result = mSampleBufferDescriptors[i]->prepare();
			FailIf (kIOReturnSuccess != result, Exit);
		}

		sampleBuffer = mUSBBufferDescriptor->getBytesNoCopy();
		FailIf (NULL == sampleBuffer, Exit);
	}
	
	mMainStream->setSampleBuffer (sampleBuffer, mSampleBufferSize);
	setNumSampleFramesPerBuffer (numSamplesInBuffer);

	if (kUSBIn == mDirection) 
	{
		// Check to see if latency should be higher for EHCI (rdar://3959606 ) 
		if (mSplitTransactions)
		{
			debugIOLog ("? AppleUSBAudioEngine[%p]::controlledFormatChange () - Compensating for high speed timing difference in sample offset", this);
			highSpeedCompensation = true;
		}
		else
		{
			highSpeedCompensation = false;
		}
		minimumSafeSampleOffset = averageFrameSamples + 1;
		// Add an extra frame and a half of samples to the offset if going through a USB 2.0 hub.
		cautiousSafeSampleOffset = minimumSafeSampleOffset + (minimumSafeSampleOffset / kUSBInputRecoveryTimeFraction) + (highSpeedCompensation ? (5 * minimumSafeSampleOffset / 3) : 0);
		
		// Set the offset for input devices (microphones, etc.)
		setSampleOffset (cautiousSafeSampleOffset);
		debugIOLog ("? AppleUSBAudioEngine[%p]::controlledFormatChange () - setting input sample offset to %lu sample frames", this, minimumSafeSampleOffset);
		
		// setSampleLatency chosen via heuristics
		setSampleLatency (averageFrameSamples * (/* kMinimumFrameOffset + */ 1));
	} 
	else 
	{
		// Output case.
		cautiousSafeSampleOffset =  averageFrameSamples + 1;
		minimumSafeSampleOffset = cautiousSafeSampleOffset / 2;
		
		// Set the offset for output devices (speakers, etc.) to 1 USB frame (+1 ms to latency). This is necessary to ensure that samples are not clipped 
		// to a portion of the buffer whose DMA is in process.
		setSampleOffset (minimumSafeSampleOffset);
		debugIOLog ("? AppleUSBAudioEngine[%p]::controlledFormatChange () - setting output sample offset to %lu sample frames", this, minimumSafeSampleOffset);
		
		// setSampleLatency chosen via heuristics 
		setSampleLatency (additionalSampleFrameFreq ? averageFrameSamples + 1 : averageFrameSamples);
	}

	if (TRUE == needToChangeChannels) 
	{
		beginConfigurationChange ();
		removeAllDefaultAudioControls ();
		mUSBAudioDevice->createControlsForInterface (this, mInterfaceNumber, mAlternateSettingID);
		completeConfigurationChange ();
	}

	debugIOLog ("? AppleUSBAudioEngine[%p]::controlledFormatChange () - Called setNumSampleFramesPerBuffer with %d", this, mSampleBufferSize / (mSampleSize ? mSampleSize : 1));
	debugIOLog ("? AppleUSBAudioEngine[%p]::controlledFormatChange () - newFormat->fNumChannels = %d, newFormat->fBitWidth %d", this, newFormat->fNumChannels, newFormat->fBitWidth);

	result = kIOReturnSuccess;

Exit:
	debugIOLog ("- AppleUSBAudioEngine[%p]::controlledFormatChange () = 0x%x", this, result);
    return result;

}

// When convertInputSamples is called, we have a window of samples that might possibly still be in transit on the USB bus.
// The number of samples that might be in transit depends on how long our USB read completion routines have been held off.
// Best case is that we have already coalesced all the samples that have been recorded because we weren't held off.
// Worst case is that we've been held off for longer than (framesPerList * mNumUSBFrameListsToQueue) milliseconds and we haven't
// coalesced anything that's been recorded and we don't have any more reads queued up and we're being asked for something
// that hasn't been recorded.
// The normal case should be not much worse than the best case and that's what we're trying to make better.
// The case we are really trying to fix is when we have been held off for (framesPerList * 1 or 2) ms and we have the data
// that's being asked for, it just hasn't been coalesced yet.
// What we have is a window of samples that are outstanding, either they have been recorded and we haven't gotten the
// completion routine called for them, or they are still in the future and haven't been recorded yet.  We must figure out
// where that window is in our buffer and if the request is for sample inside of that window, coalesce and return those
// samples.  If the request is for samples outside of that window, just return those samples because there is no possibility
// that they are in the recording buffer (it's either the worst case, or someone is asking for old data).
// The window goes from (mCurrentFrameList + 1) to (mCurrentFrameList + numFramesListsToQueue) which is at most
// mReadUSBFrameListSize * mNumUSBFrameListsToQueue in size.  It's actually probably smaller than that, but we'll assume that it's
// that big and if can't get coalesce enough bytes from the read buffer, then we'll return old data since there is nothing
// else that we can do anyway (perhaps we could return an error to the HAL too).
IOReturn AppleUSBAudioEngine::convertInputSamples (const void *sampleBuf,
                                                                void *destBuf,
                                                                UInt32 firstSampleFrame,
                                                                UInt32 numSampleFrames,
                                                                const IOAudioStreamFormat *streamFormat,
                                                                IOAudioStream *audioStream) {
	UInt64						curUSBFrameNumber;
	SInt64						framesLeftInQueue;
	UInt32						lastSampleByte;
	UInt32						windowStartByte;
	UInt32						windowEndByte;
	IOReturn						result;
	
	#if DEBUGCONVERT
		debugIOLog ("+ AppleUSBAudioEngine::convertInputSamples (%p, %p, %lu, %lu, %p, %p)", sampleBuf, destBuf, firstSampleFrame, numSampleFrames, streamFormat, audioStream);
	#endif
	
	if (!mHaveTakenFirstTimeStamp)
	{
		debugIOLog ("! AppleUSBAudioEngine::convertInputSamples () - called before first time stamp!");
	}

	#if 1	// enabled for [3091812]
	if (0 == mShouldStop && TRUE != mInCompletion) 
	{
		curUSBFrameNumber = mStreamInterface->GetDevice()->GetBus()->GetFrameNumber ();
		framesLeftInQueue = mUSBFrameToQueue - curUSBFrameNumber;

		if (framesLeftInQueue < (mNumUSBFramesPerList * (mNumUSBFrameListsToQueue / 2)) / 2) 
		{
			while (framesLeftInQueue < mNumUSBFramesPerList * (mNumUSBFrameListsToQueue - 1) && 0 == mShouldStop) 
			{
				#if DEBUGLOADING
				debugIOLog ("! AppleUSBAudioEngine::convertInputSamples () - Queue a read from convertInputSamples: framesLeftInQueue = %ld", (UInt32)framesLeftInQueue);
				#endif
				readHandler (this, mUSBCompletion[mCurrentFrameList].parameter, kIOReturnSuccess, &mUSBIsocFrames[mCurrentFrameList * mNumUSBFramesPerList]);

				curUSBFrameNumber = mStreamInterface->GetDevice()->GetBus()->GetFrameNumber ();
				framesLeftInQueue = mUSBFrameToQueue - curUSBFrameNumber;
			}
		}
	}
	#endif
	
	#if 1 // enabled for [3091812]
	lastSampleByte = (firstSampleFrame + numSampleFrames) * streamFormat->fNumChannels * (streamFormat->fBitWidth / 8);
	// Is the request inside our window of possibly recorded samples?
	if (mBufferOffset + 1 > getSampleBufferSize ()) 
	{
		windowStartByte = 0;
	} 
	else 
	{
		windowStartByte = mBufferOffset + 1;
	}
	windowEndByte = windowStartByte + (mNumUSBFrameListsToQueue * mReadUSBFrameListSize);
	if (windowEndByte > getSampleBufferSize ()) 
	{
		windowEndByte -= getSampleBufferSize ();
	}
	if ((windowStartByte < lastSampleByte && windowEndByte > lastSampleByte) ||
		(windowEndByte > lastSampleByte && windowStartByte > windowEndByte) ||
		(windowStartByte < lastSampleByte && windowStartByte > windowEndByte && windowEndByte < lastSampleByte)) 
	{
		// debugIOLog ("%ld, %ld, %ld, %ld, %ld, %ld, %ld", firstSampleFrame * 4, numSampleFrames, lastSampleByte, mCurrentFrameList, mBufferOffset, windowStartByte, windowEndByte);
		if (mBufferOffset < lastSampleByte) 
		{
			CoalesceInputSamples (lastSampleByte - mBufferOffset, NULL);
			#if DEBUGLOADING
			debugIOLog ("! AppleUSBAudioEngine::convertInputSamples () - Coalesce from convert %d bytes", lastSampleByte - mBufferOffset);
			#endif
		} 
		else 
		{
			// Have to wrap around the buffer.
			UInt32		numBytesToCoalesce = getSampleBufferSize () - mBufferOffset + lastSampleByte;
			CoalesceInputSamples (numBytesToCoalesce, NULL);
			#if DEBUGLOADING
			debugIOLog ("! AppleUSBAudioEngine::convertInputSamples () - Coalesce from convert %d bytes (wrapping)", numBytesToCoalesce);
			#endif
		}
	}
	#endif

    result = convertFromAppleUSBAudioInputStream_NoWrap (sampleBuf, destBuf, firstSampleFrame, numSampleFrames, streamFormat);

	if (mPlugin) 
	{
		mPlugin->pluginProcessInput ((float *)destBuf, numSampleFrames, streamFormat->fNumChannels);
	}
	
    if (0 == firstSampleFrame) // if this convert began with sample 0
	{
        // debugIOLog("sample 0 converted on USB frame %llu", mStreamInterface->GetDevice()->GetBus()->GetFrameNumber());
	}
	#if DEBUGCONVERT
		debugIOLog ("- AppleUSBAudioEngine::convertInputSamples () = 0x%x", result);
	#endif
	return result;
}

IOReturn AppleUSBAudioEngine::copyAnchor (UInt64 * anchorFrame, AbsoluteTime * anchorTime)
{
	IOReturn		result = kIOReturnError;
	UInt64			frame = 0ull;
	AbsoluteTime	time;
	UInt8			triesLeft;
	
	FailIf (NULL == mUSBAudioDevice, Exit);
	
	// Try the following protected copy a maximum of three times.
	triesLeft = 3;
	do 
	{
		frame = mUSBAudioDevice->mNewReferenceUSBFrame;
		time = mUSBAudioDevice->mNewReferenceWallTime;
		triesLeft--;
	} while		(		(frame != mUSBAudioDevice->mNewReferenceUSBFrame)
					&&	(0 != triesLeft));
					
	FailIf (frame != mUSBAudioDevice->mNewReferenceUSBFrame, Exit);
	
	*anchorFrame = frame;
	*anchorTime = time;
	result = kIOReturnSuccess;
	
Exit:
	return result;
}

IOReturn AppleUSBAudioEngine::eraseOutputSamples(const void *mixBuf, void *sampleBuf, UInt32 firstSampleFrame, UInt32 numSampleFrames, const IOAudioStreamFormat *streamFormat, IOAudioStream *audioStream)
{
	super::eraseOutputSamples (mixBuf, sampleBuf, firstSampleFrame, numSampleFrames, streamFormat, audioStream);
	
	// if on a UHCI connection and using output, erase extended buffer area; necessary to avoid an audio artifact after stopping the stream
	if (    (mUHCISupport)
		 && (getDirection () == kIOAudioStreamDirectionOutput))
	
	{
		UInt32 start = firstSampleFrame * streamFormat->fNumChannels * (streamFormat->fBitWidth / 8); // first byte index of first sample that is being clipped
		if ( start < mAlternateFrameSize )
		{
			UInt32 size = mAlternateFrameSize - start;
			bzero( &( ( ( char * )sampleBuf )[ mSampleBufferSize + start ] ), size );
		}
	}
	return kIOReturnSuccess;
}

AbsoluteTime AppleUSBAudioEngine::generateTimeStamp (UInt32 usbFrameIndex, UInt32 preWrapBytes, UInt32 byteCount)
{
	UInt64			time_nanos = 0ull;
	UInt64			referenceWallTime_nanos = 0ull;
	UInt64			anchorFrame;
	AbsoluteTime	anchorTime;
	AbsoluteTime	time;
	#if DEBUGTIMESTAMPS
	SInt64			stampDifference = 0;
	UInt64			frameDifference = 0;
	SInt64			stampJitter = 0;
	#endif
	UInt64			thisFrameNum;
	bool			anchorInFuture;
	
	FailIf (NULL == mFrameQueuedForList, Exit);
	FailIf (NULL == mUSBAudioDevice, Exit);
	FailIf (kIOReturnSuccess != copyAnchor (&anchorFrame, &anchorTime), Exit);
	thisFrameNum = mFrameQueuedForList[mCurrentFrameList] + usbFrameIndex;
	anchorInFuture = (anchorFrame > thisFrameNum);
	
	if (anchorInFuture)
	{
		time_nanos = anchorFrame - thisFrameNum;
	}
	else
	{
		time_nanos = thisFrameNum - anchorFrame;
	}
	if (byteCount != 0)
	{
		time_nanos *= byteCount;
		if (anchorInFuture)
		{
			time_nanos -= preWrapBytes;
		}
		else
		{
			time_nanos += preWrapBytes;
		}
	}
	time_nanos *= mUSBAudioDevice->mWallTimePerUSBCycle / kWallTimeExtraPrecision;
	if (byteCount != 0)
	{
		time_nanos /= byteCount;
	}
	absolutetime_to_nanoseconds (anchorTime, &referenceWallTime_nanos);
	if (anchorInFuture)
	{
		time_nanos = referenceWallTime_nanos - time_nanos;
	}
	else
	{
		time_nanos += referenceWallTime_nanos;
	}
	
	// time_nanos now represents the time at which the byte in question should have begun transfer. In the case of input, we won't have access to this byte
	// until one USB frame later.
	if	(getDirection () == kIOAudioStreamDirectionInput)
	{
		time_nanos += mUSBAudioDevice->mWallTimePerUSBCycle / kWallTimeExtraPrecision;
	}
		
	#if DEBUGTIMESTAMPS
		#define MAGNITUDEOF( x ) ( ( ( x ) > 0 ) ? ( x ) : ( - ( x ) ) )
		if (0ull != mLastTimeStamp_nanos)
		{
		
			//Debugging numbers
			if (0ull != mLastWrapFrame)
				frameDifference = thisFrameNum - mLastWrapFrame;
			stampDifference = time_nanos - mLastTimeStamp_nanos;
			if (0ull != mLastStampDifference)
				stampJitter = stampDifference - mLastStampDifference;
			mStampDrift += stampJitter;
			debugIOLog (" usbFrameIndex = %lu, preWrapBytes = %u, byteCount = %u, mWallTimePerUSBCycle = %llu, time = %llu", 
				usbFrameIndex, preWrapBytes, byteCount, mWallTimePerUSBCycle, time_nanos);
			debugIOLog (" frameDifference = %llu, stampDifference = %lld, stampJitter = %lld, mStampDrift = %lld ", 
				frameDifference, stampDifference, stampJitter, mStampDrift);
			if ( MAGNITUDEOF( stampJitter ) > 1000000 )
			{
				debugIOLog( "\nthisFrameNum = %llu, mReferenceWallTime = %llu\n", thisFrameNum, referenceWallTime_nanos );
			}
		}
	
		//Update references
		mLastTimeStamp_nanos = time_nanos;
		if (0ull != stampDifference)
		{
			mLastStampDifference = stampDifference;
		}
		mLastWrapFrame = thisFrameNum;
	#endif
	
Exit:
	nanoseconds_to_absolutetime (time_nanos, &time);
	return time;
}

UInt32 AppleUSBAudioEngine::getCurrentSampleFrame () {
	const IOAudioStreamFormat			*theFormat;
	UInt32								currentSampleFrame;

	currentSampleFrame = 0;
	FailIf (NULL == mMainStream, Exit);
	theFormat = mMainStream->getFormat ();
	if (getDirection () == kIOAudioStreamDirectionOutput) 
	{
		currentSampleFrame = mSafeErasePoint;
	} 
	else 
	{
		currentSampleFrame = (mBufferOffset == mSampleBufferSize ? 0 : mBufferOffset);
	}
	currentSampleFrame /= (theFormat->fNumChannels * (theFormat->fBitWidth / 8));

Exit:
	return currentSampleFrame;
}

// GetDefaultSettings added for rdar://3866513

IOReturn AppleUSBAudioEngine::GetDefaultSettings (UInt8 * altSettingID, IOAudioSampleRate * sampleRate) {
	IOReturn				result;
	UInt8					newAltSettingID;
	IOAudioSampleRate		newSampleRate;
	USBAudioConfigObject *	usbAudio;
	
	debugIOLog ("+ AppleUSBAudioEngine[%p]::GetDefaultSettings ()", this);
	result = kIOReturnError;
	newSampleRate.whole = kDefaultSamplingRate;
	newSampleRate.fraction = 0;
	usbAudio = mUSBAudioDevice->GetUSBAudioConfigObject ();
	FailIf (NULL == usbAudio, Exit);
	
	// We, as the driver, have to pick a default sample rate, size, and number of channels, so try 16-bit stereo 44.1kHz
	newAltSettingID = usbAudio->FindAltInterfaceWithSettings (mInterfaceNumber, kChannelDepth_STEREO, kBitDepth_16bits, newSampleRate.whole);
	if (255 == newAltSettingID) 
	{
		// Didn't have stereo, so try mono
		newAltSettingID = usbAudio->FindAltInterfaceWithSettings (mInterfaceNumber, kChannelDepth_MONO, kBitDepth_16bits, newSampleRate.whole);
	}
	if (255 == newAltSettingID) 
	{
		// Don't have a mono or stereo 16-bit 44.1kHz interface, so try for a stereo 16-bit interface with any sample rate
		newAltSettingID = usbAudio->FindAltInterfaceWithSettings (mInterfaceNumber, kChannelDepth_STEREO, kBitDepth_16bits);
		newSampleRate.whole = usbAudio->GetHighestSampleRate (mInterfaceNumber, newAltSettingID);			// we'll run at the highest sample rate that the device has at stereo 16-bit
	}
	if (255 == newAltSettingID)
	{
		// Don't have a stereo 16-bit interface, so try for a mono 16-bit interface with any sample rate
		newAltSettingID = usbAudio->FindAltInterfaceWithSettings (mInterfaceNumber, kChannelDepth_MONO, kBitDepth_16bits);
		newSampleRate.whole = usbAudio->GetHighestSampleRate (mInterfaceNumber, newAltSettingID);			// we'll run at the highest sample rate that the device has at mono 16-bit
	}
	if (255 == newAltSettingID) 
	{
		// OK, we're not going to be picky here (since that didn't get us anywhere), we're going to try to run with the first interface we find.
		newAltSettingID = usbAudio->AlternateSettingZeroCanStream (mInterfaceNumber) ? 0 : 1;
		
		debugIOLog ("? AppleUSBAudioEngine[%p]::GetDefaultSettings () - Taking first available alternate setting (%d)", this, newAltSettingID);
		newSampleRate.whole = usbAudio->GetHighestSampleRate (mInterfaceNumber, newAltSettingID);
	}
	
	debugIOLog ("? AppleUSBAudioEngine[%p]::GetDefaultSettings () - Default sample rate is %d", this, newSampleRate.whole);
	debugIOLog ("? AppleUSBAudioEngine[%p]::GetDefaultSettings () - Default alternate setting ID is %d", this, newAltSettingID);
	FailIf (0 == newSampleRate.whole, Exit);
	*sampleRate = newSampleRate;
	*altSettingID = newAltSettingID;
	result = kIOReturnSuccess;
	
Exit:
	debugIOLog ("- AppleUSBAudioEngine[%p]::GetDefaultSettings (%d, %lu) = 0x%x", this, *altSettingID, sampleRate->whole, result);
	return result;
}

IOAudioStreamDirection AppleUSBAudioEngine::getDirection () {
	IOAudioStreamDirection		direction;

	direction = kIOAudioStreamDirectionOutput;
	FailIf (NULL == mMainStream, Exit);
	direction = mMainStream->getDirection ();

Exit:
	return direction;
}

OSString * AppleUSBAudioEngine::getGlobalUniqueID () {
    char *						uniqueIDStr;
    OSString *					localID;
    OSString *					uniqueID;
	OSNumber *					usbLocation;
	IOReturn					err;
    UInt32						uniqueIDSize;
	UInt32						locationID;
	UInt8						stringIndex;
	UInt8						interfaceNumber;
	char						productString[kStringBufferSize];
	char						manufacturerString[kStringBufferSize];
	char						serialNumberString[kStringBufferSize];
	char						locationIDString[kStringBufferSize];
	char						interfaceNumberString[4];	// biggest string possible is "255"

	usbLocation = NULL;
	uniqueIDStr = NULL;
	localID = NULL;
	uniqueID = NULL;

	uniqueIDSize = 5;		// This is the number of ":" in the final string, plus space for the trailing NULL that ends the string
	uniqueIDSize += strlen ("AppleUSBAudioEngine");

	err = kIOReturnSuccess;
	manufacturerString[0] = 0;
	stringIndex = mStreamInterface->GetDevice()->GetManufacturerStringIndex ();
	if (0 != stringIndex) 
	{
		err = mStreamInterface->GetDevice()->GetStringDescriptor (stringIndex, manufacturerString, kStringBufferSize);
	}

	if (0 == manufacturerString[0] || kIOReturnSuccess != err) 
	{
		strcpy (manufacturerString, "Unknown Manufacturer");
	}
	uniqueIDSize += strlen (manufacturerString);

	err = kIOReturnSuccess;
	productString[0] = 0;
	stringIndex = mStreamInterface->GetDevice()->GetProductStringIndex ();
	if (0 != stringIndex) 
	{
		err = mStreamInterface->GetDevice()->GetStringDescriptor (stringIndex, productString, kStringBufferSize);
	}

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

	err = kIOReturnSuccess;
	serialNumberString[0] = 0;
	stringIndex = mStreamInterface->GetDevice()->GetSerialNumberStringIndex ();
	stringIndex = 0;
	if (0 != stringIndex) 
	{
		err = mStreamInterface->GetDevice()->GetStringDescriptor (stringIndex, serialNumberString, kStringBufferSize);
	}

	if (0 == serialNumberString[0] || kIOReturnSuccess != err) 
	{
		// device doesn't have a serial number, get its location ID
		usbLocation = OSDynamicCast (OSNumber, mStreamInterface->GetDevice()->getProperty (kUSBDevicePropertyLocationID));
		if (NULL != usbLocation) 
		{
			locationID = usbLocation->unsigned32BitValue ();
			sprintf (locationIDString, "%x", locationID);
		} 
		else 
		{
			strcpy (locationIDString, "Unknown location");
		}
		uniqueIDSize += strlen (locationIDString);
	} 
	else 
	{
		// device has a serial number that we can use to track it
		debugIOLog ("? AppleUSBAudioEngine[%p]::getGlobalUniqueID () - Device has a serial number = %s", this, serialNumberString);
		uniqueIDSize += strlen (serialNumberString);
	}

	interfaceNumber = mStreamInterface->GetInterfaceNumber ();
	sprintf (interfaceNumberString, "%d", interfaceNumber);
	uniqueIDSize += strlen (interfaceNumberString);

	uniqueIDStr = (char *)IOMalloc (uniqueIDSize);

	if (NULL != uniqueIDStr) 
	{
		uniqueIDStr[0] = 0;
	
		if (0 == serialNumberString[0]) 
		{
			sprintf (uniqueIDStr, "AppleUSBAudioEngine:%s:%s:%s:%s", manufacturerString, productString, locationIDString, interfaceNumberString);
		} 
		else 
		{
			sprintf (uniqueIDStr, "AppleUSBAudioEngine:%s:%s:%s:%s", manufacturerString, productString, serialNumberString, interfaceNumberString);
		}
	
		uniqueID = OSString::withCString (uniqueIDStr);
		debugIOLog ("AppleUSBAudioEngine[%p]::getGlobalUniqueID () - getGlobalUniqueID = %s", this, uniqueIDStr);
		IOFree (uniqueIDStr, uniqueIDSize);
	}

	return uniqueID;
}

void * AppleUSBAudioEngine::getSampleBuffer (void) {
	void *						sampleBuffer;

	sampleBuffer = NULL;
	// FailIf  (NULL == mMainStream, Exit);	// rdar://3978130 causes an assertion error on iSub hotplug
	if (NULL != mMainStream)
	{
		sampleBuffer = mMainStream->getSampleBuffer ();
	}
	return sampleBuffer;
}

UInt32 AppleUSBAudioEngine::getSampleBufferSize (void) {
	UInt32						sampleBufferSize;

	sampleBufferSize = 0;
	FailIf (NULL == mMainStream, Exit);
	sampleBufferSize = mMainStream->getSampleBufferSize ();

Exit:
	return sampleBufferSize;
}

#if DEBUGLATENCY
UInt64 AppleUSBAudioEngine::getQueuedFrameForSample (UInt32 sampleFrame)
{
	UInt64	usbFrame = 0ull;
	UInt32	sampleByteOffset;
	UInt32	bufferByteOffset;
	UInt32	bytesToQueuePoint;
	UInt8	frameListNumber;
	UInt8	i;
	
	// debugIOLog ("+ AppleUSBAudioEngine::getQueuedFrameForSample (%lu)", sampleFrame);
	FailIf (NULL == mFrameQueuedForList, Exit);
	FailIf (0 == mSampleSize, Exit);
	sampleByteOffset = sampleFrame * mSampleSize;
	bytesToQueuePoint = (mLastPreparedBufferOffset > sampleByteOffset ? 0 : getSampleBufferSize()) + mLastPreparedBufferOffset - sampleByteOffset;
	// debugIOLog ("? AppleUSBAudioEngine::getQueuedFrameForSample (%lu) - mLastPreparedBufferOffset = %lu, sampleByteOffset = %lu, bytesToQueuePoint = %lu, sampleBufferSize = %lu", sampleFrame, mLastPreparedBufferOffset, sampleByteOffset, bytesToQueuePoint, getSampleBufferSize());
	
	if (bytesToQueuePoint > mThisFrameListSize + mLastFrameListSize)
	{
		debugIOLog ("? AppleUSBAudioEngine::getQueuedFrameForSample (%lu) - sample frame is not queued in the previous two frame lists", sampleFrame);
	}
	else
	{
		// Find the USB frame on which this sample frame is queued to be transmitted on the bus
		if (bytesToQueuePoint <= mThisFrameListSize)
		{
			// This sample frame is queued to go out in the most recently queued frame list (bad if this has been clipped!)
			// Store the initial offset.
			bufferByteOffset = mThisFrameListSize;
			
			// Find the frame list number.
			// frameListNumber = (mNumUSBFrameLists + mCurrentFrameList + mNumUSBFrameListsToQueue - 2) % mNumUSBFrameLists;
			frameListNumber = (mCurrentFrameList + 1) % mNumUSBFrameLists;
			// debugIOLog ("? AppleUSBAudioEngine::getQueuedFrameForSample (%lu) - sample frame is queued in *PREVIOUS* frame list (%d) (mThisFrameListSize = %lu, mLastFrameListSize = %lu)", sampleFrame, frameListNumber, mThisFrameListSize, mLastFrameListSize);
		}
		else
		{
			// This sample frame is queued to go out in the least recently queued frame list (expected if this has been clipped)
			// Store the intial offset.
			bufferByteOffset = mThisFrameListSize + mLastFrameListSize;
			
			// Find the frame list number.
			// frameListNumber = (mNumUSBFrameLists + mCurrentFrameList + mNumUSBFrameListsToQueue - 3) % mNumUSBFrameLists;
			frameListNumber = mCurrentFrameList;
			// debugIOLog ("? AppleUSBAudioEngine::getQueuedFrameForSample (%lu) - sample frame was queued in frame list %d (mThisFrameListSize = %lu, mLastFrameListSize = %lu)", sampleFrame, frameListNumber, mThisFrameListSize, mLastFrameListSize);
		}
			
		// Get the first byte of the frame list
		bufferByteOffset = (getSampleBufferSize() + mLastPreparedBufferOffset - bufferByteOffset) % getSampleBufferSize();
		// debugIOLog ("? AppleUSBAudioEngine::getQueuedFrameForSample (%lu) - frame list %d starting queueing at byte %lu", sampleFrame, frameListNumber, bufferByteOffset); 
			
		// We've already determined in which frame list the sample frame lies and the buffer offset at which we should start. We no longer care about the 
		// actual buffer offset, but we must preserve the order of the buffer offset and the sample byte offset. We can do this by "unrolling" the ring
		// buffer and not marking the wrap any more.
		
		if (bufferByteOffset > sampleByteOffset)
		{
			// Add a buffer size so we don't have to worry around looping back around to the zero byte again
			sampleByteOffset += getSampleBufferSize ();
		}
		
		// Find the sample byte
		for (i = 0; i < mNumUSBFramesPerList; i++)
		{
			bufferByteOffset += mUSBIsocFrames[frameListNumber * mNumUSBFramesPerList].frReqCount;
			// debugIOLog ("i: %d, bufferByteOffset = %ld", i, bufferByteOffset);
			if (sampleByteOffset < bufferByteOffset)
			{
				// The sample frame is queued to go out in this frame.
				usbFrame = mFrameQueuedForList[frameListNumber] + i;
				break;
			}
		}
		FailIf (0 == usbFrame, Exit);
	}
Exit:
	// debugIOLog ("- AppleUSBAudioEngine::getQueuedFrameForSample (%lu) = %llu", sampleFrame, usbFrame);
	return usbFrame;
	
}
#endif

//--------------------------------------------------------------------------------
bool AppleUSBAudioEngine::initHardware (IOService *provider) {
	char								vendorIDCString[7];
	char								productIDCString[7];
    USBAudioConfigObject *				usbAudio;
    IOAudioStreamFormat					streamFormat;
	IOReturn							resultCode;
	Boolean								resultBool;
	UInt32								index;
	UInt16								terminalType;

    debugIOLog ("+ AppleUSBAudioEngine[%p]::initHardware (%p)", this, provider);

    resultBool = FALSE;
	mTerminatingDriver = FALSE;
	mCoalescenceMutex = NULL;

    FailIf (FALSE == super::initHardware (provider), Exit);

	mStreamInterface = OSDynamicCast (IOUSBInterface, provider);
	FailIf (NULL == mStreamInterface, Exit);

    usbAudio = mUSBAudioDevice->GetUSBAudioConfigObject ();
    FailIf (NULL == usbAudio, Exit);

	mMainStream = new IOAudioStream;
    FailIf (NULL == mMainStream, Exit);

	// Choose default alternate setting ID and sampling rate ( rdar://3866513 )
	
	FailIf (kIOReturnSuccess != GetDefaultSettings (&mAlternateSettingID, &sampleRate), Exit);
	
	mDirection = usbAudio->GetIsocEndpointDirection (mInterfaceNumber, mAlternateSettingID);
	FailIf (!mMainStream->initWithAudioEngine (this, (IOAudioStreamDirection)mDirection, 1), Exit);
	if (kUSBIn == mDirection) 
	{
		// look for a streaming output terminal that's connected to a non-streaming input terminal
		debugIOLog ("? AppleUSBAudioEngine[%p]::initHardware () - This is an input type endpoint (mic, etc.)", this);
		index = 0;
		do 
		{
			terminalType = usbAudio->GetIndexedInputTerminalType (mUSBAudioDevice->mControlInterface->GetInterfaceNumber (), 0, index++);		// Change this to not use mControlInterface
		} while (terminalType == INPUT_UNDEFINED && index < 256);

		mNumUSBFrameLists = RECORD_NUM_USB_FRAME_LISTS;
		mNumUSBFramesPerList = RECORD_NUM_USB_FRAMES_PER_LIST;
		mNumUSBFrameListsToQueue = RECORD_NUM_USB_FRAME_LISTS_TO_QUEUE;
		
		// We need a mutex for CoalesceInputSamples () in case something goes wrong at the start of the stream.
		mCoalescenceMutex = IOLockAlloc ();
		FailIf (NULL == mCoalescenceMutex, Exit);
	} 
	else if (kUSBOut == mDirection) 
	{
		debugIOLog ("? AppleUSBAudioEngine[%p]::initHardware () - This is an output type endpoint (speaker, etc.)", this);
		index = 0;
		do 
		{
			terminalType = usbAudio->GetIndexedOutputTerminalType (mUSBAudioDevice->mControlInterface->GetInterfaceNumber (), 0, index++);		// Change this to not use mControlInterface
		} while (terminalType == OUTPUT_UNDEFINED && index < 256);

		mNumUSBFrameLists = PLAY_NUM_USB_FRAME_LISTS;
		mNumUSBFramesPerList = PLAY_NUM_USB_FRAMES_PER_LIST;
		mNumUSBFrameListsToQueue = PLAY_NUM_USB_FRAME_LISTS_TO_QUEUE;
	} 
	else 
	{
		FailMessage ("Couldn't get the endpoint direction!", Exit);
	}
	
	// See if UHCI support is necessary
	mUHCISupport = mUSBAudioDevice->checkForUHCI ();
	// mUHCISupport = TRUE;
	
	mSplitTransactions = mUSBAudioDevice->detectSplitTransactions ();
	
	mFrameQueuedForList = NULL;
	
	// Allocate frame list time stamp array
	mFrameQueuedForList = new UInt64[mNumUSBFrameLists];
	FailIf (NULL == mFrameQueuedForList, Exit);
	
	mMainStream->setTerminalType (terminalType);

	mUSBIsocFrames = (IOUSBLowLatencyIsocFrame *)IOMalloc (mNumUSBFrameLists * mNumUSBFramesPerList * sizeof (IOUSBLowLatencyIsocFrame));
	mUSBCompletion = (IOUSBLowLatencyIsocCompletion *)IOMalloc (mNumUSBFrameLists * sizeof (IOUSBLowLatencyIsocCompletion));
	mSampleBufferDescriptors = (IOSubMemoryDescriptor **)IOMalloc (mNumUSBFrameLists * sizeof (IOSubMemoryDescriptor *));
	bzero (mSampleBufferDescriptors, mNumUSBFrameLists * sizeof (IOSubMemoryDescriptor *));
	mWrapDescriptors[0] = OSTypeAlloc (IOSubMemoryDescriptor);
	mWrapDescriptors[1] = OSTypeAlloc (IOSubMemoryDescriptor);
	FailIf (NULL == mWrapDescriptors[0], Exit);
	FailIf (NULL == mWrapDescriptors[1], Exit);
	FailIf (NULL == mUSBIsocFrames, Exit);
	FailIf (NULL == mUSBCompletion, Exit);
	FailIf (NULL == mSampleBufferDescriptors, Exit);

	FailIf (kIOReturnSuccess != AddAvailableFormatsFromDevice (usbAudio), Exit);

	mCurSampleRate = sampleRate;
	setSampleRate (&sampleRate);

	// Tell the IOAudioFamily what format we are going to be running in.
	streamFormat.fNumChannels = usbAudio->GetNumChannels (mInterfaceNumber, mAlternateSettingID);
	streamFormat.fBitDepth = usbAudio->GetSampleSize (mInterfaceNumber, mAlternateSettingID);
	streamFormat.fBitWidth = usbAudio->GetSubframeSize (mInterfaceNumber, mAlternateSettingID) * 8;
	streamFormat.fAlignment = kIOAudioStreamAlignmentLowByte;
	streamFormat.fByteOrder = kIOAudioStreamByteOrderLittleEndian;
	streamFormat.fDriverTag = (mInterfaceNumber << 16) | mAlternateSettingID;

	switch (usbAudio->GetFormat (mInterfaceNumber, mAlternateSettingID)) 
	{
		case PCM:
			streamFormat.fSampleFormat = kIOAudioStreamSampleFormatLinearPCM;
			streamFormat.fNumericRepresentation = kIOAudioStreamNumericRepresentationSignedInt;
			streamFormat.fIsMixable = TRUE;
			break;
		case AC3:	// just starting to stub something in for AC-3 support
			streamFormat.fSampleFormat = kIOAudioStreamSampleFormatAC3;
			streamFormat.fNumericRepresentation = kIOAudioStreamNumericRepresentationSignedInt;
			streamFormat.fIsMixable = FALSE;
			streamFormat.fNumChannels = 6;
			streamFormat.fBitDepth = 16;
			streamFormat.fBitWidth = 16;
			streamFormat.fByteOrder = kIOAudioStreamByteOrderBigEndian;
			break;
		case IEC1937_AC3:
			streamFormat.fSampleFormat = kIOAudioStreamSampleFormat1937AC3;
			streamFormat.fNumericRepresentation = kIOAudioStreamNumericRepresentationSignedInt;
			streamFormat.fIsMixable = FALSE;
			break;
		default:
			FailMessage ("Interface doesn't have any supported formats!\n", Exit);
	}
	
	// Store default stream format and sample rate
	mDefaultAudioStreamFormat = streamFormat;
	mDefaultAudioSampleRate = sampleRate;
	
	FailIf (FALSE == mStreamInterface->open (this), Exit);		// Have to open the interface because calling setFormat will call performFormatChange, which expects the interface to be open.
	resultCode = mStreamInterface->SetAlternateInterface (this, kRootAlternateSetting);		// Select the first alternate interface to init the hardware
	FailIf (kIOReturnSuccess != resultCode, Exit);
	resultCode = mMainStream->setFormat (&streamFormat);
	FailIf (kIOReturnSuccess != resultCode, Exit);
	resultCode = addAudioStream (mMainStream);
	FailIf (kIOReturnSuccess != resultCode, Exit);

    // Verify that this 'start' request is targeting a USB Audio Stream interface
    // (i.e. it must be an audio class and a stream subclass).
    FailIf (kUSBAudioClass != usbAudio->GetInterfaceClass (mInterfaceNumber, mAlternateSettingID), Exit);
    FailIf (kUSBAudioStreamInterfaceSubclass != usbAudio->GetInterfaceSubClass (mInterfaceNumber, mAlternateSettingID), Exit);

	if (kUSBOut == mDirection) 
	{
		miSubBufferMemory = NULL;
		miSubEngine = NULL;

		// Set up a control that sound prefs can set to tell us if we should install our notifier or not
		miSubAttachToggle = IOAudioToggleControl::create (FALSE,
											kIOAudioControlChannelIDAll,
											kIOAudioControlChannelNameAll,
											0,
											kIOAudioToggleControlSubTypeiSubAttach,
											kIOAudioControlUsageOutput);
	
		if (NULL != miSubAttachToggle) 
		{
			addDefaultAudioControl (miSubAttachToggle);
			miSubAttachToggle->setValueChangeHandler ((IOAudioControl::IntValueChangeHandler)iSubAttachChangeHandler, this);
			// don't release it as we might use it later
		}
	}

    mUSBAudioDevice->activateAudioEngine (this, FALSE);

    resultBool = TRUE;

    if (resultBool) 
	{
        mUSBAudioDevice->retain ();
    }

	sprintf (vendorIDCString, "0x%04X", mStreamInterface->GetDevice()->GetVendorID ());
	sprintf (productIDCString, "0x%04X", mStreamInterface->GetDevice()->GetProductID ());

	setProperty (vendorIDCString, productIDCString);		// Ask for plugin to load (if it exists)
	IOService::registerService ();

Exit:
    debugIOLog("- AppleUSBAudioEngine[%p]::initHardware(%p), resultCode = %x, resultBool = %d", this, provider, resultCode, resultBool);
    return resultBool;
}

void AppleUSBAudioEngine::registerPlugin (AppleUSBAudioPlugin * thePlugin) {
	mPlugin = thePlugin;
	mPluginInitThread = thread_call_allocate ((thread_call_func_t)pluginLoaded, (thread_call_param_t)this);

	if (NULL != mPluginInitThread) 
	{
		thread_call_enter (mPluginInitThread);
	}
}

void AppleUSBAudioEngine::pluginLoaded (AppleUSBAudioEngine * usbAudioEngineObject) {
	IOReturn							result;

	if (usbAudioEngineObject->mPlugin) 
	{
		usbAudioEngineObject->mPlugin->open (usbAudioEngineObject);

		result = usbAudioEngineObject->mPlugin->pluginInit (usbAudioEngineObject, usbAudioEngineObject->mStreamInterface->GetDevice()->GetVendorID (), usbAudioEngineObject->mStreamInterface->GetDevice()->GetProductID ());
		if (result == kIOReturnSuccess) 
		{
			debugIOLog ("success initing the plugin");
			usbAudioEngineObject->mPlugin->pluginSetDirection ((IOAudioStreamDirection) usbAudioEngineObject->mDirection);
			usbAudioEngineObject->mPlugin->pluginSetFormat (usbAudioEngineObject->mMainStream->getFormat (), &usbAudioEngineObject->sampleRate);
		} 
		else 
		{
			debugIOLog ("Error initing the plugin");
			usbAudioEngineObject->mPlugin->close (usbAudioEngineObject);
			usbAudioEngineObject->mPlugin = NULL;
		}

		if (NULL != usbAudioEngineObject->mPluginNotification) 
		{
			usbAudioEngineObject->mPluginNotification->remove ();
			usbAudioEngineObject->mPluginNotification = NULL;
		}
	}
}

IOReturn AppleUSBAudioEngine::pluginDeviceRequest (IOUSBDevRequest * request, IOUSBCompletion * completion) {
	IOReturn						result;

	result = kIOReturnBadArgument;
	if (request) 
	{
		result = mUSBAudioDevice->deviceRequest (request, mUSBAudioDevice, completion);
	}

	return result;
}

void AppleUSBAudioEngine::pluginSetConfigurationApp (const char * bundleID) {
	if (bundleID) 
	{
		mUSBAudioDevice->setConfigurationApp (bundleID);
	}
}

IOReturn AppleUSBAudioEngine::iSubAttachChangeHandler (IOService *target, IOAudioControl *theControl, SInt32 oldValue, SInt32 newValue) {
    IOReturn						result;
    AppleUSBAudioEngine *			audioEngine;

	debugIOLog ("+ AppleUSBAudioEngine::iSubAttachChangeHandler (%p, %p, %ld, %ld)", target, theControl, oldValue, newValue);

	result = kIOReturnSuccess;
	// FailIf (oldValue == newValue, Exit);		// rdar://3951261 // Causes assertion failures on wake from sleep with iSub
    audioEngine = OSDynamicCast (AppleUSBAudioEngine, target);
	FailIf (NULL == audioEngine, Exit);

	if (    (newValue)
		 && (!oldValue)		// for rdar://3951261 
		 && (audioEngine->isiSubCompatibleFormat (audioEngine->mMainStream->getFormat (), audioEngine->sampleRate)))		// for rdar://3881790 
	{
		// Set up notifier to run when iSub shows up if necessary
		if (NULL == audioEngine->miSubEngineNotifier)
		{
			debugIOLog ("? AppleUSBAudioEngine::iSubAttachChangeHandler () - Adding miSubEngineNotifier ...");
			audioEngine->miSubEngineNotifier = addNotification (gIOPublishNotification, serviceMatching ("AppleiSubEngine"), (IOServiceNotificationHandler)&iSubEnginePublished, audioEngine);
			if (NULL != audioEngine->miSubBufferMemory) 
			{
				// it looks like the notifier could be called before miSubEngineNotifier is set, 
				// so if it was called, then miSubBufferMemory would no longer be NULL and we can remove the notifier
				debugIOLog ("? AppleUSBAudioEngine::iSubAttachChangeHandler () - Removing miSubEngineNotifier because notifier was already called ...");
				audioEngine->miSubEngineNotifier->remove ();
				audioEngine->miSubEngineNotifier = NULL;
			}
		}
		else
		{
			debugIOLog ("! AppleUSBAudioEngine::iSubAttachChangeHandler () - There is already a notifier @ 0x%x!", audioEngine->miSubEngineNotifier);
			// IOLog ("! AppleUSBAudioEngine::iSubAttachChangeHandler () - There is already a notifier @ 0x%x!\n", audioEngine->miSubEngineNotifier);
		}
    } 
	else if (oldValue != newValue)		// for rdar://3951261 
	{
		if (NULL != audioEngine->miSubBufferMemory && NULL != audioEngine->miSubEngine) 
		{
			// We're already attached to an iSub, so detach.  Runs on a thread to avoid deadlocks.
			audioEngine->iSubTeardownConnection ();
		}

		// We're not attached to the iSub, so just remove our notifier
		if (NULL != audioEngine->miSubEngineNotifier) 
		{
			debugIOLog ("? AppleUSBAudioEngine::iSubAttachChangeHandler () - Removing miSubEngineNotifier because iSub was detached ...");
			audioEngine->miSubEngineNotifier->remove ();
			audioEngine->miSubEngineNotifier = NULL;
		}
	}

Exit:
	debugIOLog("- AppleUSBAudioEngine::iSubAttachChangeHandler ()");
    return result;
}

// Added for rdar:://3881790. Can't get the format from mMainStream in the middle of a format change so send iSub compatibility from performFormatChange () 
IOReturn AppleUSBAudioEngine::iSubAttachChangeCall (Boolean isiSubCompatible) {
    IOReturn						result;

	debugIOLog ("+ AppleUSBAudioEngine[%p]::iSubAttachChangeCall (%d)", this, isiSubCompatible);
	
	result = kIOReturnSuccess;
	
	if (isiSubCompatible) 
	{
		// Set up notifier to run when iSub shows up if necessary
		if (NULL == miSubEngineNotifier)
		{
			debugIOLog ("? AppleUSBAudioEngine::iSubAttachChangeCall () - Adding miSubEngineNotifier ...");
			miSubEngineNotifier = addNotification (gIOPublishNotification, serviceMatching ("AppleiSubEngine"), (IOServiceNotificationHandler)&iSubEnginePublished, this);
			if (NULL != miSubBufferMemory) 
			{
				// it looks like the notifier could be called before miSubEngineNotifier is set, 
				// so if it was called, then miSubBufferMemory would no longer be NULL and we can remove the notifier
				debugIOLog ("? AppleUSBAudioEngine::iSubAttachChangeCall () - Removing miSubEngineNotifier because notifier was already called ...");
				miSubEngineNotifier->remove ();
				miSubEngineNotifier = NULL;
			}
		}
		else
		{
			debugIOLog ("! AppleUSBAudioEngine::iSubAttachChangeHandler () - There is already a notifier @ 0x%x!", miSubEngineNotifier);
			// IOLog ("! AppleUSBAudioEngine::iSubAttachChangeHandler () - There is already a notifier @ 0x%x!\n", miSubEngineNotifier);
		}
    } 
	else 
	{
		// We should get rid of our notifier because we don't care if an iSub is added in this state ( rdar://3881790 )
		if (NULL != miSubEngineNotifier) 
		{
			debugIOLog ("? AppleUSBAudioEngine::iSubAttachChangeCall () - Removing miSubEngineNotifier because this is not an iSub-compatible format ...");
			miSubEngineNotifier->remove ();
			miSubEngineNotifier = NULL;
		}
	}

	debugIOLog("- AppleUSBAudioEngine::iSubAttachChangeCall");
    return result;
}


// This runs on the IOAudioFamily workloop on a separate thread than the one it was called on, which might have been the USB workloop (in the case of a hot unplug).
IOReturn AppleUSBAudioEngine::iSubCloseAction (OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4) {
	debugIOLog("+ AppleUSBAudioEngine::iSubCloseAction");

	AppleUSBAudioEngine *audioEngine = OSDynamicCast (AppleUSBAudioEngine, owner);
	FailIf (NULL == audioEngine, Exit);

	if (TRUE == audioEngine->miSubIsOpen && NULL != audioEngine->mOldiSubEngine) 
	{
		audioEngine->mOldiSubEngine->closeiSub (audioEngine);
		// debugIOLog ("iSub closed");
		audioEngine->detach (audioEngine->mOldiSubEngine);
		audioEngine->miSubIsOpen = FALSE;
		// audioEngine->miSubEngine->release ();
		audioEngine->miSubEngine = NULL;
		audioEngine->miSubBufferMemory = NULL;
	#if DEBUGISUB
	} 
	else 
	{
		if (TRUE != audioEngine->miSubIsOpen) {debugIOLog ("TRUE != audioEngine->miSubIsOpen");}
		if (NULL == audioEngine->mOldiSubEngine) {debugIOLog ("NULL == audioEngine->mOldiSubEngine");}
	#endif
	}

	if (NULL != audioEngine->mLowFreqSamples) 
	{
		IOFree (audioEngine->mLowFreqSamples, audioEngine->getSampleBufferSize () * 2);
	}

	if (NULL != audioEngine->mHighFreqSamples) 
	{
		IOFree (audioEngine->mHighFreqSamples, audioEngine->getSampleBufferSize () * 2);
	}

Exit:
	debugIOLog("- AppleUSBAudioEngine::iSubCloseAction");
	return kIOReturnSuccess;
}

bool AppleUSBAudioEngine::iSubEnginePublished (AppleUSBAudioEngine * usbAudioEngineObject, void * refCon, IOService * newService) {
	debugIOLog ("? AppleUSBAudioEngine::iSubEnginePublished (%p, %p, %p)", usbAudioEngineObject, refCon, newService);

	FailIf (NULL == usbAudioEngineObject, Exit);
	FailIf (NULL == newService, Exit);

	if (FALSE == usbAudioEngineObject->miSubIsOpen) 
	{
		usbAudioEngineObject->miSubEngine = (AppleiSubEngine *)newService;

		#if THREAD_ISUB_OPEN
		usbAudioEngineObject->miSubOpenThreadCall = thread_call_allocate ((thread_call_func_t)usbAudioEngineObject->iSubOpen, (thread_call_param_t)usbAudioEngineObject);

		if (NULL != usbAudioEngineObject->miSubOpenThreadCall) 
		{
			AbsoluteTime			callTime;
			AbsoluteTime			delayAmount;
			clock_get_uptime (&callTime);
			nanoseconds_to_absolutetime (750ULL * 1000 * 1000, &delayAmount);
			ADD_ABSOLUTETIME (&callTime, &delayAmount);
			thread_call_enter_delayed (usbAudioEngineObject->miSubOpenThreadCall, callTime);
		} 
		else 
		{
			// error - do something
		}
	#else
	iSubOpen(usbAudioEngineObject);
	#endif

	} 
	else 
	{
		debugIOLog ("? AppleUSBAudioEngine::iSubEnginePublished () - iSub is already open");
	}

Exit:
	return TRUE;
}

void AppleUSBAudioEngine::iSubOpen (AppleUSBAudioEngine * usbAudioEngineObject) {
	IOReturn						result;
    IOCommandGate *					cg;

	debugIOLog ("+ AppleUSBAudioEngine::iSubOpen (%p)", usbAudioEngineObject);

	FailIf (NULL == usbAudioEngineObject, Exit);

	cg = usbAudioEngineObject->getCommandGate ();
	if (NULL != cg) 
	{
		result = cg->runAction (iSubOpenAction);
	}

Exit:
	if (NULL != usbAudioEngineObject && NULL != usbAudioEngineObject->miSubOpenThreadCall) 
	{
		thread_call_free(usbAudioEngineObject->miSubOpenThreadCall);
	}

	debugIOLog ("- AppleUSBAudioEngine::iSubOpen (%p)", usbAudioEngineObject);
	return;
}

IOReturn AppleUSBAudioEngine::iSubOpenAction (OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4) {
	AppleUSBAudioEngine *			usbAudioEngineObject;
	const IOAudioStreamFormat *		streamFormat;
	IOReturn						result;
	bool							resultBool;
	UInt32							sampleRateWhole;
	UInt32							numTries;

	debugIOLog ("+ AppleUSBAudioEngine::iSubOpenAction (%p)", owner);

	result = kIOReturnError;

	usbAudioEngineObject = OSDynamicCast (AppleUSBAudioEngine, owner);
	FailIf (NULL == usbAudioEngineObject, Exit);
	FailIf (NULL == usbAudioEngineObject->miSubEngine, Exit);
	FailIf (NULL == usbAudioEngineObject->mMainStream, Exit);

	streamFormat = usbAudioEngineObject->mMainStream->getFormat ();
	// We only run the iSub if we're currently running at a sampling rate of 44.1kHz
    sampleRateWhole = usbAudioEngineObject->mCurSampleRate.whole;    
	// aml XXX iSub filters are set up for these sample rates, and only in stereo:
	if ((2 != streamFormat->fNumChannels) || ((8000 != usbAudioEngineObject->mCurSampleRate.whole) && (11025 != usbAudioEngineObject->mCurSampleRate.whole) &&
		(22050 != usbAudioEngineObject->mCurSampleRate.whole) && (44100 != usbAudioEngineObject->mCurSampleRate.whole) &&
		(48000 != usbAudioEngineObject->mCurSampleRate.whole) && (96000 != usbAudioEngineObject->mCurSampleRate.whole))) 
	{
		goto Exit;
	}

	// Allocate memory for filter function to write samples into
	usbAudioEngineObject->mLowFreqSamples = (float *)IOMallocAligned (usbAudioEngineObject->getSampleBufferSize () * 2, PAGE_SIZE);
	FailIf (NULL == usbAudioEngineObject->mLowFreqSamples, Cleanup);
	usbAudioEngineObject->mHighFreqSamples = (float *)IOMallocAligned (usbAudioEngineObject->getSampleBufferSize () * 2, PAGE_SIZE);
	FailIf (NULL == usbAudioEngineObject->mHighFreqSamples, Cleanup);

	usbAudioEngineObject->attach (usbAudioEngineObject->miSubEngine);
	//usbAudioEngineObject->miSubEngine->retain ();

	// Open the iSub which will cause it to create mute and volume controls
	numTries = 0;
	do 
	{
		// IOSleep (102);
		resultBool = usbAudioEngineObject->miSubEngine->openiSub (usbAudioEngineObject, &requestiSubClose);
		numTries++;
		if (!resultBool) IOSleep (102);
	} while (FALSE == resultBool && numTries < 5);

	FailWithAction (FALSE == resultBool, usbAudioEngineObject->detach (usbAudioEngineObject->miSubEngine), Cleanup);
	// FailWithAction (FALSE == resultBool, usbAudioEngineObject->miSubEngine->release (), Cleanup);

	result = kIOReturnSuccess;

// aml 7.17.02, can't clean up from first few FailIf's because usbAudioEngineObject will be NULL.
Cleanup:
	// remove our notifier because we only care about the first iSub
	// always remove it, even if we didn't successfully open the iSub.
	if (NULL != usbAudioEngineObject->miSubEngineNotifier) 
	{
		debugIOLog ("? AppleUSBAudioEngine::iSubOpenAction () - Removing miSubEngineNotifier because we found an iSub ...");
		usbAudioEngineObject->miSubEngineNotifier->remove ();
		usbAudioEngineObject->miSubEngineNotifier = NULL;
	}

	if (kIOReturnSuccess == result) 
	{
		debugIOLog ("? AppleUSBAudioEngine::iSubOpenAction () - Successfully opened the iSub");
		usbAudioEngineObject->miSubBufferMemory = usbAudioEngineObject->miSubEngine->GetSampleBuffer ();
		usbAudioEngineObject->miSubIsOpen = TRUE;
	} 
	else 
	{
		// We didn't actually open the iSub
		usbAudioEngineObject->miSubBufferMemory = NULL;
		usbAudioEngineObject->miSubEngine = NULL;
		usbAudioEngineObject->miSubIsOpen = FALSE;

		if (NULL != usbAudioEngineObject->mLowFreqSamples) 
		{
			IOFree (usbAudioEngineObject->mLowFreqSamples, usbAudioEngineObject->getSampleBufferSize () * 2);
		}

		if (NULL != usbAudioEngineObject->mHighFreqSamples) 
		{
			IOFree (usbAudioEngineObject->mHighFreqSamples, usbAudioEngineObject->getSampleBufferSize () * 2);
		}
	}

// aml 7.17.02, moved exit below because usbAudioEngineObject will be NULL from first few FailIf's
Exit:

	debugIOLog ("- AppleUSBAudioEngine::iSubOpenAction (%p) = 0x%lx", owner, result);

	return result;
}

// Runs on a thread to avoid waiting on the primary thread.
void AppleUSBAudioEngine::iSubTeardown (AppleUSBAudioEngine * usbAudioEngine, thread_call_t iSubTeardownThreadCall) {
	IOCommandGate *						cg;
	Boolean								streamWasRunning;

	debugIOLog ("+ AppleUSBAudioEngine::iSubTeardown ()");

	if (NULL != usbAudioEngine) 
	{
		// remember the state but lie about it running temporarily while we pause the engine to avoid USB deadlock
		// this shouldn't cause any problems with the USB stream on the main device, it's just for removing iSub
		// controls, which can only be done when the engine isn't running.
		streamWasRunning = usbAudioEngine->mUSBStreamRunning;
		usbAudioEngine->mUSBStreamRunning = FALSE;
		usbAudioEngine->pauseAudioEngine ();
		usbAudioEngine->beginConfigurationChange ();
		cg = usbAudioEngine->getCommandGate ();
		if (NULL != cg) 
		{
			cg->runAction (iSubCloseAction);
		}
		usbAudioEngine->completeConfigurationChange ();
		usbAudioEngine->resumeAudioEngine ();
		usbAudioEngine->mUSBStreamRunning = streamWasRunning;
	}

	if (NULL != iSubTeardownThreadCall) 
	{
		thread_call_free(iSubTeardownThreadCall);
	}

	// if the iSub is being torn down, we don't want to remove this notification! (rdar://3881790)
	/*
	if (NULL != usbAudioEngine->miSubEngineNotifier) {
		debugIOLog ("Removing miSubEngineNotifier ...");
		usbAudioEngine->miSubEngineNotifier->remove ();
		usbAudioEngine->miSubEngineNotifier = NULL;
	}
	*/

	debugIOLog ("- AppleUSBAudioEngine::iSubTeardown ()");
}

void AppleUSBAudioEngine::iSubTeardownConnection (void) {
	debugIOLog ("+ AppleUSBAudioEngine::iSubTeardownConnection ()");

	mOldiSubEngine = miSubEngine;

	miSubTeardownThreadCall = thread_call_allocate ((thread_call_func_t)iSubTeardown, (thread_call_param_t)this);

	if (NULL != miSubTeardownThreadCall) 
	{
		thread_call_enter1 (miSubTeardownThreadCall, miSubTeardownThreadCall);
	} 
	else 
	{
		// error - do something
	}

	debugIOLog ("- AppleUSBAudioEngine::iSubTeardownConnection ()");
	return;
}

bool AppleUSBAudioEngine::isiSubCompatibleFormat (const IOAudioStreamFormat *format, IOAudioSampleRate sampleRate)
{
	if (    (NULL != format)
		 && (2  == format->fNumChannels) 
		 && (16 == format->fBitDepth)
		 && (    (8000  == sampleRate.whole) 
			  || (11025 == sampleRate.whole) 
			  || (22050 == sampleRate.whole) 
			  || (44100 == sampleRate.whole) 
			  || (48000 == sampleRate.whole) 
			  || (96000 == sampleRate.whole))) 
	{
		debugIOLog ("? AppleUSBAudioEngine::isiSubCompatibleFormat () - TRUE");
		return TRUE;
	}
	else
	{
		debugIOLog ("? AppleUSBAudioEngine::isiSubCompatibleFormat () - FALSE");
		return FALSE;
	}
} // AppleUSBAudioEngine::isiSubCompatibleFormat

IOReturn AppleUSBAudioEngine::performAudioEngineStart () {
    IOReturn			resultCode;
	
    debugIOLog ("+ AppleUSBAudioEngine[%p]::performAudioEngineStart ()", this);

    resultCode = kIOReturnError;
	FailIf (NULL == mUSBAudioDevice, Exit);
	
	#if DEBUGTIMESTAMPS
		mLastTimeStamp_nanos = 0ull;
		mLastStampDifference = 0ull;
		mStampDrift = 0ll;
		mLastWrapFrame = 0ull;
	#endif
	
	if (mUSBAudioDevice->mNewReferenceUSBFrame == 0ull)
	{
		// We have to have an anchor frame and time before we can take a time stamp. Generate one now.
		debugIOLog ("! AppleUSBAudioEngine[%p]::performAudioEngineStart () - Getting an anchor for the first timestamp.", this);
		FailIf (kIOReturnSuccess != mUSBAudioDevice->getAnchorFrameAndTimeStamp (NULL, NULL), Exit);
	}
	
	/*
	if (mUSBAudioDevice->mNewReferenceUSBFrame > 0ull)
	{
		// Copy an initial anchor frame and time
		mReferenceUSBFrame = mUSBAudioDevice->mNewReferenceUSBFrame;
		mReferenceWallTime = mUSBAudioDevice->mNewReferenceWallTime;
	}
	else
	{
		// We have to have an anchor frame and time before we can take a time stamp. Generate one now.
		FailIf (kIOReturnSuccess != mUSBAudioDevice->getAnchorFrameAndTimeStamp (&mReferenceUSBFrame, &mReferenceWallTime), Exit);
	}
	*/
	
	resultCode = kIOReturnSuccess;
	
	if (mPlugin) 
	{
		mPlugin->pluginStart ();
	}

    if (NULL != miSubEngine) 
	{
		mStartiSub = TRUE;
		mNeedToSync = TRUE;
    }
	
	if (!mUSBStreamRunning) 
	{
		#if DEBUGLATENCY
			mHaveClipped = false;
			mLastFrameListSize = 0;
			mThisFrameListSize = 0;
		#endif
        resultCode = startUSBStream ();
    }

Exit:
	if (resultCode != kIOReturnSuccess) 
	{
		debugIOLog ("! AppleUSBAudioEngine[%p]::performAudioEngineStart () - NOT started, error = 0x%x", this, resultCode);
	} 
	else 
	{
		debugIOLog ("\n");
		debugIOLog ("  -------------------- Starting Stream (interface %d, alternate setting %d) --------------------", mInterfaceNumber, mAlternateSettingID);
		debugIOLog ("      format = %p", mMainStream->getFormat ());
		debugIOLog ("          fNumChannels = %d", mMainStream->getFormat ()->fNumChannels);
		debugIOLog ("          fBitDepth = %d", mMainStream->getFormat ()->fBitDepth);
		debugIOLog ("          fDriverTag = 0x%x", mMainStream->getFormat ()->fDriverTag);
		debugIOLog ("      sampleRate->whole = %lu", getSampleRate()->whole);
		debugIOLog ("\n");
	}

    debugIOLog ("- AppleUSBAudioEngine[%p]::performAudioEngineStart ()", this);
    return resultCode;
}

IOReturn AppleUSBAudioEngine::performAudioEngineStop() {
    debugIOLog("+ AppleUSBAudioEngine[%p]::performAudioEngineStop ()", this);
	
	if (mPlugin) 
	{
		mPlugin->pluginStop ();
	}

    if (NULL != miSubEngine) 
	{
		miSubEngine->StopiSub ();
		mNeedToSync = TRUE;
    }

    if (mUSBStreamRunning) 
	{
        stopUSBStream ();
    }

    debugIOLog("? AppleUSBAudioEngine[%p]::performAudioEngineStop() - stopped", this);

    debugIOLog("- AppleUSBAudioEngine[%p]::performAudioEngineStop()", this);
    return kIOReturnSuccess;
}

// This gets called when the HAL wants to select one of the different formats that we made available via mMainStream->addAvailableFormat
IOReturn AppleUSBAudioEngine::performFormatChange (IOAudioStream *audioStream, const IOAudioStreamFormat *newFormat, const IOAudioSampleRate *newSampleRate) {
    IOReturn							result;
	UInt32								controllerResult;
	bool								streamIsRunning = false;

	debugIOLog ("+ AppleUSBAudioEngine[%p]::performFormatChange (%p, %p, %p)", this, audioStream, newFormat, newSampleRate);
	result = kIOReturnError;
	FailIf (NULL == mUSBAudioDevice, Exit);
	streamIsRunning = mUSBStreamRunning;
	if (streamIsRunning)
	{
		pauseAudioEngine ();
	}
	controllerResult = mUSBAudioDevice->formatChangeController (this, audioStream, newFormat, newSampleRate);
	
	switch (controllerResult)
	{
		case kAUAFormatChangeNormal:
			result = kIOReturnSuccess;
			break;
		case kAUAFormatChangeForced:
			debugIOLog ("? AppleUSBAudioEngine[%p]::performFormatChange () - This request was forced.");
			result = kIOReturnSuccess;
			break;
		case kAUAFormatChangeForceFailure:
			debugIOLog ("! AppleUSBAudioEngine[%p]::performFormatChange () - Force of this request was attempted but failed.");
			result = kIOReturnSuccess;
			break;
		case kAUAFormatChangeError:
		default:
			debugIOLog ("! AppleUSBAudioEngine[%p]::performFormatChange () - Error encountered.");
			result = kIOReturnError;
	}

Exit:
	if (streamIsRunning)
	{
		resumeAudioEngine ();
	}
	debugIOLog ("- AppleUSBAudioEngine[%p]::performFormatChange () = 0x%x", this, result);
    return result;
}

IOReturn AppleUSBAudioEngine::PrepareWriteFrameList (UInt32 arrayIndex) {
	const IOAudioStreamFormat *			theFormat;
	IOReturn							result;
	UInt32								thisFrameListSize;
	#if DEBUGLATENCY
	UInt32								frameListByteCount;			// this is always updated, regardless of wrapping
	#endif
	UInt32								thisFrameSize;
	UInt32								firstFrame;
	UInt32								numBytesToBufferEnd;
	UInt32								lastPreparedByte;
	UInt32								numUSBFramesPrepared;
	UInt16								integerSamplesInFrame;
	UInt16								bytesAfterWrap = 0;			// for UHCI support
	Boolean								haveWrapped;

	result = kIOReturnError;		// assume failure
	haveWrapped = FALSE;
	firstFrame = arrayIndex * mNumUSBFramesPerList;
	mUSBCompletion[arrayIndex].target = (void *)this;
	mUSBCompletion[arrayIndex].action = writeHandler;
	mUSBCompletion[arrayIndex].parameter = 0;			// Set to number of bytes from the 0 wrap, 0 if this buffer didn't wrap

	theFormat = mMainStream->getFormat ();

	numBytesToBufferEnd = getSampleBufferSize () - mLastPreparedBufferOffset;
	lastPreparedByte = mLastPreparedBufferOffset;
	thisFrameListSize = 0;
	#if DEBUGLATENCY
		frameListByteCount = 0;
	#endif
	
	for (numUSBFramesPrepared = 0; numUSBFramesPrepared < mNumUSBFramesPerList; numUSBFramesPrepared++) 
	{
		integerSamplesInFrame = mAverageSampleRate / 1000;
		mFractionalSamplesLeft += mAverageSampleRate - (integerSamplesInFrame * 1000);
		if (mFractionalSamplesLeft >= 1000) 
		{
			integerSamplesInFrame++;
			mFractionalSamplesLeft -= 1000;
		}
		thisFrameSize = integerSamplesInFrame * mSampleSize;
		#if DEBUGLATENCY
			frameListByteCount += thisFrameSize;
		#endif
		if (thisFrameSize >= numBytesToBufferEnd) 
		{
			bytesAfterWrap = thisFrameSize - numBytesToBufferEnd;
			mNumFramesInFirstList = numUSBFramesPrepared + 1;
			mUSBCompletion[arrayIndex].parameter = (void *)((mNumFramesInFirstList << 16) | bytesAfterWrap);	// Number of bytes after wrap
			
			if (mUHCISupport)
			{
				
				#if DEBUGUHCI
				debugIOLog ("PrepareWriteFrameList: Wrapping because (thisFrameSize = ) %d >= (numBytesToBufferEnd = ) %d", thisFrameSize, numBytesToBufferEnd); 
				debugIOLog ("PrepareWriteFrameList: bytesAfterWrap = %d, numUSBFramesPrepared = %d, numUSBFramesPerList %d", bytesAfterWrap, numUSBFramesPrepared, mNumUSBFramesPerList);
				#endif
				
				mWrapDescriptors[0]->initSubRange (mUSBBufferDescriptor,
													mLastPreparedBufferOffset,
													getSampleBufferSize () + bytesAfterWrap  - mLastPreparedBufferOffset,
													kIODirectionOut);
				#if DEBUGUHCI
				debugIOLog ("PrepareWriteFrameList: initSubRange 0: %d to %d", mLastPreparedBufferOffset, getSampleBufferSize () + bytesAfterWrap);
				debugIOLog ("PrepareWriteFrameList: %d frames in first list", mNumFramesInFirstList);
				#endif
			}
			else
			{
				mWrapDescriptors[0]->initSubRange (mUSBBufferDescriptor, mLastPreparedBufferOffset, getSampleBufferSize () - mLastPreparedBufferOffset, kIODirectionOut);
			}
			
			numBytesToBufferEnd = getSampleBufferSize () - bytesAfterWrap;
			lastPreparedByte = bytesAfterWrap;
			haveWrapped = TRUE;
		} // if (thisFrameSize >= numBytesToBufferEnd)
		else 
		{
			thisFrameListSize += thisFrameSize;
			lastPreparedByte += thisFrameSize;
			numBytesToBufferEnd -= thisFrameSize;
		}
		mUSBIsocFrames[firstFrame + numUSBFramesPrepared].frStatus = -1;
		mUSBIsocFrames[firstFrame + numUSBFramesPrepared].frActCount = 0;
		mUSBIsocFrames[firstFrame + numUSBFramesPrepared].frReqCount = thisFrameSize;
	}  // for numUSBFramesPrepared
	
	if (TRUE == haveWrapped) 
	{
		mNeedTimeStamps = TRUE;
		if (mUHCISupport)
		{
			// debugIOLog("bytesAfterWrap = %d lastPreparedByte = %d numBytesToBufferEnd = %d  mNumFramesInFirstList = %d numUSBFramesPrepared = %d", bytesAfterWrap, lastPreparedByte, numBytesToBufferEnd, mNumFramesInFirstList, numUSBFramesPrepared );
			mWrapDescriptors[1]->initSubRange (mUSBBufferDescriptor, bytesAfterWrap, lastPreparedByte - bytesAfterWrap, kIODirectionOut);
			#if DEBUGUHCI
			debugIOLog ("PrepareWriteFrameList: initSubRange 1: %d to %d", bytesAfterWrap, lastPreparedByte);
			#endif
			if (lastPreparedByte != bytesAfterWrap)
			{
				// This is where we setup our extra completion for the second wrap write
				mExtraUSBCompletion.target = (void *)this;
				mExtraUSBCompletion.action = writeHandlerForUHCI;
				// mExtraUSBCompletion.parameter = (void *)((mNumFramesInFirstList << 16) | bytesAfterWrap);
			}
		}
		else
		{
			mWrapDescriptors[1]->initSubRange (mUSBBufferDescriptor, 0, lastPreparedByte, kIODirectionOut);

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

			mWrapRangeDescriptor = IOMultiMemoryDescriptor::withDescriptors ((IOMemoryDescriptor **)mWrapDescriptors, 2, kIODirectionOut, true);
		}
	} 
	else 
	{
		mSampleBufferDescriptors[arrayIndex]->initSubRange (mUSBBufferDescriptor, mLastPreparedBufferOffset, thisFrameListSize, kIODirectionOut);
		FailIf (NULL == mSampleBufferDescriptors[arrayIndex], Exit);
	}

	mSafeErasePoint = mLastSafeErasePoint;
	mLastSafeErasePoint = mLastPreparedBufferOffset;
	mLastPreparedBufferOffset = lastPreparedByte;
	#if DEBUGLATENCY
		mLastFrameListSize = mThisFrameListSize;
		mThisFrameListSize = frameListByteCount;
	#endif
	result = kIOReturnSuccess;

Exit:
	return result;
}

IOReturn AppleUSBAudioEngine::PrepareAndReadFrameLists (UInt8 sampleSize, UInt8 numChannels, UInt32 usbFrameListIndex) {
	IOReturn							result;
	UInt32								firstFrame;
	UInt32								numUSBFramesPrepared;
	UInt16								averageFrameSamples;
	UInt16								additionalSampleFrameFreq;
	UInt16								bytesToRead;

	#if DEBUGINPUT
	debugIOLog ("+ AppleUSBAudioEngine::PrepareAndReadFrameLists (%d, %d, %ld)", sampleSize, numChannels, usbFrameListIndex);
	#endif
	
	result = kIOReturnError;		// assume failure
	firstFrame = usbFrameListIndex * mNumUSBFramesPerList;
	mUSBCompletion[usbFrameListIndex].target = (void *)this;
	mUSBCompletion[usbFrameListIndex].action = readHandler;
	mUSBCompletion[usbFrameListIndex].parameter = (void *)usbFrameListIndex;	// what frame list index this buffer is

	CalculateSamplesPerFrame (mCurSampleRate.whole, &averageFrameSamples, &additionalSampleFrameFreq);
	mBytesPerSampleFrame = sampleSize * numChannels;
	bytesToRead = (averageFrameSamples + 1) * mBytesPerSampleFrame;
	
	// This number may be more than what we used for the pipe policy. If so, use the pipe policy number.
	bytesToRead = (bytesToRead > mReadUSBFrameSize) ? mReadUSBFrameSize : bytesToRead;

	for (numUSBFramesPrepared = 0; numUSBFramesPrepared < mNumUSBFramesPerList; numUSBFramesPrepared++) 
	{
		mUSBIsocFrames[firstFrame + numUSBFramesPrepared].frStatus = -1;
		mUSBIsocFrames[firstFrame + numUSBFramesPrepared].frActCount = 0;
		mUSBIsocFrames[firstFrame + numUSBFramesPrepared].frReqCount = bytesToRead;
		
		// This casting can be removed when AbsoluteTime is changed to UInt64 in Tiger.
		
		* (UInt64 *) ( & ( mUSBIsocFrames[firstFrame + numUSBFramesPrepared].frTimeStamp)) = 0ull;
	}

	if (NULL != mPipe) 
	{
		result = mPipe->Read (mSampleBufferDescriptors[usbFrameListIndex], mUSBFrameToQueue, mNumUSBFramesPerList, &mUSBIsocFrames[firstFrame], &mUSBCompletion[usbFrameListIndex], 1);	// Update timestamps every 1ms
		if (result != kIOReturnSuccess)
		{
			debugIOLog ("! AppleUSBAudioEngine[%p]::PrepareAndReadFrameLists () - Error 0x%x reading from pipe", this, result);
		}
		
		// keep track of this frame number for time stamping
		if (NULL != mFrameQueuedForList)
		{
			mFrameQueuedForList[usbFrameListIndex] = mUSBFrameToQueue;
		}
		mUSBFrameToQueue += mNumUSBFramesPerList;
	}
	else
	{
		debugIOLog ("! AppleUSBAudioEngine[%p]::PrepareAndReadFrameLists () - mPipe is NULL!", this);
	}

	#if DEBUGINPUT
	debugIOLog ("- AppleUSBAudioEngine::PrepareAndReadFrameLists ()");
	#endif
	return result;
}

#if PRIMEISOCINPUT
// This method starts an input isoc stream to a device and disregards kNumIsocFramesToPrime frames. 
void AppleUSBAudioEngine::primeInputPipe (IOUSBPipe * pipeToPrime, UInt32 bytesPerUSBFrame) {
	// IOUSBLowLatencyIsocFrame *	isocFrames = NULL;
	IOReturn					result;
	UInt32						bufferSize = 0;
	UInt8						i;
	bool						dataWrittenToPipe = false;
	
	FailIf (NULL == pipeToPrime, Exit);
	FailIf (0 == bytesPerUSBFrame, Exit);
	
	bufferSize = bytesPerUSBFrame * kNumIsocFramesToPrime;
	
	mPrimeInputIsocFrames = (IOUSBLowLatencyIsocFrame *)IOMalloc (kNumIsocFramesToPrime * sizeof (IOUSBLowLatencyIsocFrame));
	FailIf (NULL == mPrimeInputIsocFrames, Exit);
	
	mPrimeInputCompletion.target	= (void  *) this;
	mPrimeInputCompletion.action	= primeInputPipeHandler;
	mPrimeInputCompletion.parameter	= (void *) bufferSize;				// So we know how many bytes to free in the completion
	
	debugIOLog ("? AppleUSBAudioEngine::primeInputPipe () - bufferSize = %lu", bufferSize);
	
	// Initialize isoc frame list
	for (i = 0; i < kNumIsocFramesToPrime; i++)
	{
		(mPrimeInputIsocFrames + i)->frStatus = -1;
		(mPrimeInputIsocFrames + i)->frReqCount = bytesPerUSBFrame;
		(mPrimeInputIsocFrames + i)->frActCount = 0;
	}
	
	// attempt to read from USB pipe
	result = pipeToPrime->Read (mUSBBufferDescriptor, mUSBFrameToQueue, (UInt32) kNumIsocFramesToPrime, mPrimeInputIsocFrames, &mPrimeInputCompletion, 0);
	if (result == kIOReturnSuccess)
	{
		// Our request was queued. We can let the completion handle memory deallocation.
		dataWrittenToPipe = true;
		
		// Advance the frame to queue by the number of frames primed
		mUSBFrameToQueue += kNumIsocFramesToPrime;
		debugIOLog ("? AppleUSBAudioEngine::primeInputPipe (%p, %lu) - %d frames primed. mUSBFrameToQueue = %llu", pipeToPrime, bytesPerUSBFrame, kNumIsocFramesToPrime, mUSBFrameToQueue);
	}
	
Exit:
	if (false == dataWrittenToPipe)
	{
		if (mPrimeInputIsocFrames)
		{
			IOFree (mPrimeInputIsocFrames, kNumIsocFramesToPrime * sizeof (IOUSBLowLatencyIsocFrame));
			mPrimeInputIsocFrames = NULL;
		}
	}
}

void AppleUSBAudioEngine::primeInputPipeHandler (void * object, void * parameter, IOReturn result, IOUSBLowLatencyIsocFrame * pFrames)
{
	AppleUSBAudioEngine *	self;
	// AbsoluteTime			timeStamp;
	
	debugIOLog ("+ AppleUSBAudioEngine::primeInputPipeHandler (%p, %lu, 0x%x, %p)", object, (UInt32) parameter, result, pFrames); 
	self = (AppleUSBAudioEngine *)object;
	FailIf (NULL == self, Exit);
	
	// If any analysis of primed input is required, insert it after here.
	
	// If any analysis of primed input is required, insert it before here.
	
Exit:
	if (self->mPrimeInputIsocFrames)
	{
		IOFree (self->mPrimeInputIsocFrames, kNumIsocFramesToPrime * sizeof (IOUSBLowLatencyIsocFrame));
		self->mPrimeInputIsocFrames = NULL;
	}
	debugIOLog ("- AppleUSBAudioEngine::primeInputPipeHandler (%p, %lu, 0x%x, %p)", object, (UInt32) parameter, result, pFrames);
}

#endif

IOReturn AppleUSBAudioEngine::readFrameList (UInt32 frameListNum) {
	const IOAudioStreamFormat *			theFormat;
    IOReturn							result;

	#if DEBUGINPUT
	debugIOLog ("+ AppleUSBAudioEngine::readFrameList ()");
	#endif
	theFormat = mMainStream->getFormat ();

	result = PrepareAndReadFrameLists (theFormat->fBitWidth / 8, theFormat->fNumChannels, frameListNum);
	#if DEBUGINPUT
	debugIOLog ("- AppleUSBAudioEngine::readFrameList ()");
    #endif
	return result;
}

void AppleUSBAudioEngine::readHandler (void * object, void * parameter, IOReturn result, IOUSBLowLatencyIsocFrame * pFrames) {
	AppleUSBAudioEngine *			self;
	UInt64							currentUSBFrameNumber;
	UInt32							frameListToRead;
	UInt32							thisActCount = 0;
	UInt32							minimumUSBFrameSize = 0;
	UInt8							frameIndex;
	IOReturn						thisStatus = 0;

	#if DEBUGINPUT
	debugIOLog ("+ AppleUSBAudioEngine::readHandler ()");
	#endif
	self = (AppleUSBAudioEngine *)object;
	FailIf (TRUE == self->mInCompletion, Exit);
	self->mInCompletion = TRUE;
		
	if	(		(self->mUSBAudioDevice)
			&&	(false == self->mUSBAudioDevice->getSingleSampleRateDevice ())		// We didn't know this was a single sample rate device at this time
			&&	(kIOReturnOverrun == result))										// This is what IOUSBFamily should be reporting on an overrun
	{
		// This is a fatal error. Notify the AppleUSBAudioDevice to sync the sample rates when possible.
		self->mUSBAudioDevice->setShouldSyncSampleRates (self);
		
		debugIOLog ("! AppleUSBAudioEngine::readHandler () - Encountered fatal error 0x%x on frame list %d. Will attempt emergency format change.", result, self->mCurrentFrameList);
		goto Exit;
	}
	
	FailIf (NULL == self->mStreamInterface, Exit);
	currentUSBFrameNumber = self->mStreamInterface->GetDevice()->GetBus()->GetFrameNumber();
	
	if (kIOReturnAborted != result)
	{
		#if 1 // enabled for [3091812]
		if (0 == self->mShouldStop && (SInt32)(self->mUSBFrameToQueue - currentUSBFrameNumber) > (SInt32)(self->mNumUSBFramesPerList * (self->mNumUSBFrameListsToQueue - 1))) 
		{
			// The frame list that this would have queued has already been queued by convertInputSamples
			#if DEBUGLOADING
			debugIOLog ("Not queuing a frame list in readHandler (%ld)", (SInt32)(self->mUSBFrameToQueue - currentUSBFrameNumber));
			#endif
			goto Exit;
		}
		#endif
		
		// Comb the returned statuses for alarming statuses
		for (frameIndex = 0; frameIndex < self->mNumUSBFramesPerList && pFrames; frameIndex++)
		{
			thisStatus = (pFrames + frameIndex)->frStatus;
			thisActCount = (pFrames + frameIndex)->frActCount;
			minimumUSBFrameSize = (pFrames + frameIndex)->frReqCount - 2 * self->mSampleSize;
			#ifdef DEBUG
			if (		(!(self->mShouldStop))
					&&	(thisStatus != kIOReturnSuccess)
					&&	(		(thisStatus != kIOReturnUnderrun)
							||	(		(thisStatus == kIOReturnUnderrun)
									&&	(thisActCount < minimumUSBFrameSize))))
			{
				debugIOLog ("! AppleUSBAudioEngine::readHandler () - Frame list %d frame index %d returned error 0x%x (frActCount = %lu, result = 0x%x)", self->mCurrentFrameList, frameIndex, thisStatus, thisActCount, result);
			}
			#endif
			
			if (thisActCount < minimumUSBFrameSize)
			{
				// IOLog ("AppleUSBAudio: ERROR on input! Short packet of size %lu encountered when %lu bytes were requested.\n", thisActCount, (pFrames + frameIndex)->frReqCount);
			}
		}
	}
	
	if (kIOReturnSuccess != result && kIOReturnAborted != result && kIOReturnUnderrun != result) 
	{
		// skip ahead and see if that helps
		if (self->mUSBFrameToQueue <= currentUSBFrameNumber) 
		{
			self->mUSBFrameToQueue = currentUSBFrameNumber + kMinimumFrameOffset;
		}
	}

	if (kIOReturnAborted != result) 
	{
		self->CoalesceInputSamples (0, pFrames);
	}

	if (self->mShouldStop > 0) 
	{
		if (		(self->mShouldStop == 1)
				||	(self->mShouldStop == self->mNumUSBFrameListsToQueue))
		{
			// Only really care about the first and last stopped frame lists.
			debugIOLog("? AppleUSBAudioEngine::readHandler() - stopping: %d", self->mShouldStop);
		}
		self->mShouldStop++;
		if (self->mShouldStop == (self->mNumUSBFrameListsToQueue + 1) && TRUE == self->mTerminatingDriver) 
		{
			self->mStreamInterface->close (self);
			// [rdar://4287899] self->mStreamInterface = NULL;
		}
	} 
	else if (kIOReturnAborted != result)
	{
		if (self->mCurrentFrameList == self->mNumUSBFrameLists - 1) 
		{
			self->mCurrentFrameList = 0;
		} 
		else 
		{
			self->mCurrentFrameList++;
		}

		frameListToRead = (self->mCurrentFrameList - 1) + self->mNumUSBFrameListsToQueue;
		if (frameListToRead >= self->mNumUSBFrameLists) 
		{
			frameListToRead -= self->mNumUSBFrameLists;
		}
		self->readFrameList (frameListToRead);
	}

Exit:
	self->mInCompletion = FALSE;
	#if DEBUGINPUT
	debugIOLog ("- AppleUSBAudioEngine::readHandler ()");
	#endif
	return;
}

void AppleUSBAudioEngine::resetClipPosition (IOAudioStream *audioStream, UInt32 clipSampleFrame) {
	if (mPlugin) 
	{
		mPlugin->pluginReset ();
	}

	// aml 7.16.02, need to check for miSubEngine too [2999205]
    if (    (NULL != miSubBufferMemory) 
		 && (NULL != miSubEngine)) 
	{           	
		mSrcPhase = 1.0;		// aml 3.4.02
		mSrcState = 0.0;		// aml 3.4.02

		// start the filter over again since old filter state is invalid
		mFilterState.xl_1 = 0.0;
		mFilterState.xr_1 = 0.0;
		mFilterState.xl_2 = 0.0;
		mFilterState.xr_2 = 0.0;
		mFilterState.yl_1 = 0.0;
		mFilterState.yr_1 = 0.0;
		mFilterState.yl_2 = 0.0;
		mFilterState.yr_2 = 0.0;

		// aml 2.14.02 added for 4th order filter
		mFilterState2.xl_1 = 0.0;
		mFilterState2.xr_1 = 0.0;
		mFilterState2.xl_2 = 0.0;
		mFilterState2.xr_2 = 0.0;
		mFilterState2.yl_1 = 0.0;
		mFilterState2.yr_1 = 0.0;
		mFilterState2.yl_2 = 0.0;
		mFilterState2.yr_2 = 0.0;
            
		// aml 2.18.02 added for 4th order filter phase compensator
		mPhaseCompState.xl_1 = 0.0;
		mPhaseCompState.xr_1 = 0.0;
		mPhaseCompState.xl_2 = 0.0;
		mPhaseCompState.xr_2 = 0.0;
		mPhaseCompState.yl_1 = 0.0;
		mPhaseCompState.yr_1 = 0.0;
		mPhaseCompState.yl_2 = 0.0;
		mPhaseCompState.yr_2 = 0.0;

		#if DEBUGISUB
		debugIOLog ("+resetClipPosition, miSubBufferOffset=%ld, mLastClippedFrame=%ld, clipSampleFrame=%ld", miSubBufferOffset, mLastClippedFrame, clipSampleFrame);
		#endif
		UInt32 clipAdjustment;
		if (mLastClippedFrame < clipSampleFrame) 
		{
			// aml 3.13.02 added miSubEngine format instead of hard coded 2, changed divide by 4 to include channels
			// miSubBufferOffset -= (mLastClippedFrame + ((getSampleBufferSize () / (2 * miSubEngine->GetNumChannels())) - clipSampleFrame)) * miSubEngine->GetNumChannels();
			// aml 3.21.02, calc temp var
			clipAdjustment = (mLastClippedFrame + ((getSampleBufferSize () / (2 * miSubEngine->GetNumChannels())) - clipSampleFrame)) * miSubEngine->GetNumChannels();
		} 
		else 
		{
			// aml 3.13.02 added miSubEngine format instead of hard coded 2
			//miSubBufferOffset -= (mLastClippedFrame - clipSampleFrame) * miSubEngine->GetNumChannels();
			// aml 3.21.02, calc temp var
			clipAdjustment = (mLastClippedFrame - clipSampleFrame) * miSubEngine->GetNumChannels();		
		}
		// aml 3.21.02, adjust for different sample rates
		clipAdjustment = (clipAdjustment * 1000) / ((1000 * getSampleRate()->whole) / miSubEngine->GetSampleRate());  
		miSubBufferOffset -= clipAdjustment;

		if (miSubBufferOffset < 0) 
		{
			miSubBufferOffset += (miSubBufferMemory->getLength () / 2);
			miSubLoopCount--;			// aml 3.29.02 added to match AOA
		}
		mLastClippedFrame = clipSampleFrame;
		mJustResetClipPosition = TRUE;			// aml 3.29.02 added to match AOA
		#if DEBUGISUB
		debugIOLog ("-resetClipPosition, miSubBufferOffset=%ld, mLastClippedFrame=%ld", miSubBufferOffset, mLastClippedFrame);
		#endif
    }
}

void AppleUSBAudioEngine::requestiSubClose (IOAudioEngine * audioEngine) {
	/*	AppleUSBAudioEngine *				usbAudioEngine;

		usbAudioEngine = OSDynamicCast (AppleUSBAudioEngine, audioEngine);

		usbAudioEngine->iSubTeardownConnection ();

		// Set up notifier to run when iSub shows up again
		if (usbAudioEngine->miSubAttachToggle->getIntValue ()) 
		{
			usbAudioEngine->miSubEngineNotifier = addNotification (gIOPublishNotification, serviceMatching ("AppleiSubEngine"), (IOServiceNotificationHandler)&usbAudioEngine->iSubEnginePublished, usbAudioEngine);
		}
	*/
}

/*
	The purpose of this function is to deal with asynchronous synchronization of isochronous output streams.
	On devices that can lock their output clock to an external source, they can report that value to the driver
	so that the driver doesn't feed data too quickly or too slowly to the device (so that the device's FIFO's
	don't overrun or underrun).

	The device returns a 10.14 unsigned fixed point value in a 24 bit result or a 16.16 unsigned fixed point 
	value in a 32 bit result. This value says how many samples per frame the device wants for the current 
	sampling period.  The device reports the current sampling period in its feedback/synch endpoint, which 
	can be retrieved with the GetIsocAssociatedEndpointRefreshInt call (interpreted as 2^(10-x) frames where 
	x is the value returned by GetIsocAssociatedEndpointRefreshInt).

	The endpoint should not be read from more often than once every 2^(10-x) frames as the number isn't updated
	by the device any more often than that.  Because x can range from 1 to 9, the sample rate may need to be
	adjusted anywhere from once every 2 frames to once every 512 frames.

	If the number returned is larger than the last number returned, the device needs more data, if it is smaller
	than the previous value, the device needs less data.

	In typical usage, the value should not change by a large value (less than 1% of the clock value).
	A typical result would be be a value of 0x0b0667 which in 10.14 is 44.10004.  This is represented in the
	driver as 0x2c199c which is the 16.16 value for 44.10004.

	This number is then converted to a UInt32 representing the sampling rate in Hz, in this case 44100.
*/
void AppleUSBAudioEngine::sampleRateHandler (void * target, void * parameter, IOReturn result, IOUSBIsocFrame * pFrames) {
    AppleUSBAudioEngine	*			self;
	IOFixed							sampleRate;
	UInt16							fixed;
	IOFixed							fract;
	UInt32							oldSampleRate = 0;
	UInt32							newSampleRate = 0;

    self = (AppleUSBAudioEngine *)target;
	// debugIOLog ("entering sampleRateHandler (%p, %p, 0x%x, %p)", target, parameter, result, pFrames);

	if	(		(pFrames)
			&&	(		(kIOReturnSuccess == result)
					||	(kIOReturnUnderrun == result)))
	{
		newSampleRate = *(self->mAverageSampleRateBuffer);
		sampleRate = USBToHostLong (newSampleRate);
		oldSampleRate = self->mAverageSampleRate;
		
		switch (pFrames[0].frActCount)
		{
			case kFixedPoint10_14ByteSize:
				// change 10.14 value into a UInt32
				sampleRate = sampleRate << 2;
				fixed = sampleRate >> 16;
				newSampleRate = fixed * 1000;
				fract = IOFixedMultiply (sampleRate & 0x0000FFFF, 1000 << 16);
				newSampleRate += (fract & 0xFFFF0000) >> 16;
				break;
			case kFixedPoint16_16ByteSize:
				// change 16.16 value into a UInt32
				fixed = sampleRate >> 16;
				newSampleRate = fixed * 1000;
				fract = IOFixedMultiply (sampleRate & 0x0000FFFF, 1000 << 16);
				newSampleRate += (fract & 0xFFFF0000) >> 16;
				break;
			default:
				// We shouldn't get here. Set newSampleRate to 0 so that next check fails.
				newSampleRate = 0;
		}
			
		if (newSampleRate && newSampleRate != oldSampleRate) 
		{
			// The device has changed the sample rate that it needs, let's roll with the new sample rate
			self->mAverageSampleRate = newSampleRate;
			debugIOLog ("? AppleUSBAudioEngine::sampleRateHandler () - Sample rate changed, requestedFrameRate = %d", self->mAverageSampleRate);
		}
	}

	if (0 == self->mShouldStop) 
	{
		// Have to reset these parameters otherwise the read doesn't happen.
		self->mSampleRateFrame.frStatus = -1;
		self->mSampleRateFrame.frReqCount = self->mFeedbackPacketSize;	
		self->mSampleRateFrame.frActCount = 0;

		self->mNextSyncReadFrame = self->mNextSyncReadFrame + (1 << self->mRefreshInterval);
		// self->retain ();
		if (NULL != self->mAssociatedPipe) 
		{
			result = self->mAssociatedPipe->Read (self->mAssociatedEndpointMemoryDescriptor, self->mNextSyncReadFrame, 1, &(self->mSampleRateFrame), &(self->mSampleRateCompletion));
		}
	}

	//	self->release ();
	return;
}

IOReturn AppleUSBAudioEngine::SetSampleRate (USBAudioConfigObject *usbAudio, UInt32 sampleRate) {
	IOUSBDevRequest				devReq;
	UInt32						theSampleRate;
	IOReturn					result;

	result = kIOReturnError;
	if (usbAudio->IsocEndpointHasSampleFreqControl (mInterfaceNumber, mAlternateSettingID)) 
	{
		devReq.bmRequestType = USBmakebmRequestType (kUSBOut, kUSBClass, kUSBEndpoint);
		devReq.bRequest = SET_CUR;
		devReq.wValue = (SAMPLING_FREQ_CONTROL << 8) | 0;
		devReq.wIndex = usbAudio->GetIsocEndpointAddress (mInterfaceNumber, mAlternateSettingID, mDirection);
		devReq.wLength = 3;
		theSampleRate = OSSwapHostToLittleInt32 (sampleRate);
		devReq.pData = &theSampleRate;

		debugIOLog ("? AppleUSBAudioEngine[%p]::SetSampleRate () - Control interface %d, alt setting %d, endpoint address 0x%X, sample rate (little endian) 0x%X", this, mInterfaceNumber, mAlternateSettingID, devReq.wIndex, theSampleRate);
		result = mStreamInterface->GetDevice()->DeviceRequest (&devReq);
		FailIf (kIOReturnSuccess != result, Exit);
	} 
	else 
	{
		result = kIOReturnSuccess;
	}

Exit:
	if (kIOReturnSuccess != result)
	{
		debugIOLog ("! AppleUSBAudioEngine[%p]::SetSampleRate () = 0x%x", this, result);
	}
	return result;
}

IOReturn AppleUSBAudioEngine::startUSBStream () {
	const IOAudioStreamFormat *			theFormat;
	IOReturn							resultCode;
	IOReturn							interimResult;
	IOUSBFindEndpointRequest			audioIsochEndpoint;
	USBAudioConfigObject *				usbAudio;
	UInt64								currentUSBFrame;
	UInt32								frameListNum;
	UInt32								numQueued;
	UInt16								averageFrameSamples;
	UInt16								additionalSampleFrameFreq;
	UInt16								maxFrameSize;

    debugIOLog ("+ AppleUSBAudioEngine[%p]::startUSBStream ()", this);
    // Assume the entire method will fail.
	resultCode = kIOReturnError;
	numQueued = 0;

    // Start the IO audio engine
    // Enable interrupts for this stream
    // The interrupt should be triggered at the start of the sample buffer
    // The interrupt handler should increment the fCurrentLoopCount and fLastLoopTime fields

	mCurrentFrameList = 0;
	mSafeErasePoint = 0;
	mLastSafeErasePoint = 0;
	mBufferOffset = 0;
	mLastPreparedBufferOffset = 0;		// Start playing from the start of the buffer
	mFractionalSamplesLeft = 0;			// Reset our parital frame list info
	
    mShouldStop = 0;

    FailIf ((mNumUSBFrameLists < mNumUSBFrameListsToQueue), Exit);

	// Allocate the pipe now so that we don't keep it open when we're not streaming audio to the device.
	usbAudio = mUSBAudioDevice->GetUSBAudioConfigObject ();
	FailIf (NULL == usbAudio, Exit);
	FailIf (NULL == mStreamInterface, Exit);

	resultCode = mStreamInterface->SetAlternateInterface (this, mAlternateSettingID);
	debugIOLog ("? AppleUSBAudioEngine[%p]::startUSBStream () - mStreamInterface->SetAlternateInterface (this, %d) = 0x%X", this, mAlternateSettingID, resultCode);
	FailIf (kIOReturnSuccess != resultCode, Exit);

	SetSampleRate (usbAudio, mCurSampleRate.whole);

	// Acquire a PIPE for the isochronous stream.
	audioIsochEndpoint.type = kUSBIsoc;
	audioIsochEndpoint.direction = mDirection;

	mPipe = mStreamInterface->FindNextPipe (NULL, &audioIsochEndpoint);
	FailIf (NULL == mPipe, Exit);
    mPipe->retain ();

	if (getDirection () == kIOAudioStreamDirectionOutput) 
	{
		// Not concerned with errors in this function at this time.
		(void)CheckForAssociatedEndpoint (usbAudio);
	}

	// Make sure we have enough bandwidth (and give back any that we won't be using), RADAR 2750290
	CalculateSamplesPerFrame (mCurSampleRate.whole, &averageFrameSamples, &additionalSampleFrameFreq);
	theFormat = mMainStream->getFormat ();
	if (kUSBIn == mDirection) 
	{
		// debugIOLog ("Calculating maxFrameSize for new input format ...");
		// Try for one sample over, since 44.1k is going to send 45 sample packets every 10th packet, and 48k could send 47, 48, or 49 sample packets
		maxFrameSize = (averageFrameSamples + 1) * (theFormat->fNumChannels * (theFormat->fBitWidth / 8));
		// debugIOLog ("(averageFrameSamples + 1) = %d, fNumChannels = %d, (fBitWidth / 8) = %d", averageFrameSamples + 1, theFormat->fNumChannels, (theFormat->fBitWidth / 8));
		debugIOLog ("? AppleUSBAudioEngine[%p]::startUSBStream () - maxFrameSize = %d", this, maxFrameSize);
		resultCode = mPipe->SetPipePolicy (maxFrameSize, 0);
		// However, some devices say that they will never send a 49 sample packet.  Since they don't declare the bandwidth, there might be not enough pipe
		// bandwidth, so ask for the minimum (which would be 48 sample packets).
		if (kIOReturnSuccess != resultCode) 
		{
			maxFrameSize = averageFrameSamples * (theFormat->fNumChannels * (theFormat->fBitWidth / 8));
		}
		mReadUSBFrameSize = maxFrameSize;
	} 
	else 
	{
		if (0 == additionalSampleFrameFreq) 
		{
			maxFrameSize = averageFrameSamples * (theFormat->fNumChannels * (theFormat->fBitWidth / 8));
		} 
		else 
		{
			maxFrameSize = (averageFrameSamples + 1) * (theFormat->fNumChannels * (theFormat->fBitWidth / 8));
		}
	}

	debugIOLog ("? AppleUSBAudioEngine[%p]::startUSBStream () - calling SetPipePolicy (%d)", this, maxFrameSize);
	resultCode = mPipe->SetPipePolicy (maxFrameSize, 0);
	FailIf (kIOReturnSuccess != resultCode, Exit);

	currentUSBFrame = mStreamInterface->GetDevice()->GetBus()->GetFrameNumber();
	
	// The current frame is already in processing, and it may be nearly done. Must queue a minimum of two USB frames in the future to ensure
	// that our DMA occurs when we request it.
	mUSBFrameToQueue = currentUSBFrame + kMinimumFrameOffset;
	debugIOLog ("? AppleUSBAudioEngine[%p]::startUSBStream () - mUSBFrameToQueue = %llu", this, mUSBFrameToQueue);
	
	if (NULL != mAssociatedPipe) 
	{
		mNextSyncReadFrame = mUSBFrameToQueue;
		debugIOLog ("? AppleUSBAudioEngine[%p]::startUSBStream () - Starting associated endpoint stream at frame %llu", this, mNextSyncReadFrame);
		(void)mAssociatedPipe->Read (mAssociatedEndpointMemoryDescriptor, mNextSyncReadFrame, 1, &mSampleRateFrame, &mSampleRateCompletion);
	}

	// Note that we haven't taken our first time stamp yet. This will help us determine when we should take it.
	mHaveTakenFirstTimeStamp = false;
	
	if (getDirection () == kIOAudioStreamDirectionInput) 
	{
		#if PRIMEISOCINPUT
		// If successful, this operation will advance the first frame to queue, so this must be done prior to reading the frame lists.
		debugIOLog ("? AppleUSBAudioEngine[%p]::startUSBStream () - Priming input stream at frame %llu", this, mUSBFrameToQueue);
		primeInputPipe (mPipe, maxFrameSize);
		#endif
		debugIOLog ("? AppleUSBAudioEngine[%p]::startUSBStream () - Starting input stream at frame %llu", this, mUSBFrameToQueue);
		for (frameListNum = mCurrentFrameList; frameListNum < mNumUSBFrameListsToQueue; frameListNum++) 
		{
			interimResult = readFrameList (frameListNum);
			if (kIOReturnSuccess == interimResult)
			{
				numQueued++;
			}
			else
			{
				debugIOLog ("! AppleUSBAudioEngine[%p]::startUSBStream () - readFrameList (%d) failed with error 0x%x!", this, frameListNum, interimResult);
			}
		}
	} 
	else 
	{
		// This cast can be removed when AbsoluteTime is UInt64.
		* (UInt64 *) ( & (mUSBIsocFrames[0].frTimeStamp)) = 0xFFFFFFFFFFFFFFFFull;
		mUSBIsocFrames[0].frStatus = kUSBLowLatencyIsochTransferKey;
		for (frameListNum = mCurrentFrameList; frameListNum < mNumUSBFrameListsToQueue; frameListNum++) 
		{
			interimResult = writeFrameList (frameListNum);
			if (kIOReturnSuccess == interimResult)
			{
				numQueued++;
			}
			else
			{
				debugIOLog ("! AppleUSBAudioEngine[%p]::startUSBStream () - writeFrameList (%d) failed with error 0x%x!", this, frameListNum, interimResult);
			}
		}
	}

	if (0 != numQueued)
	{
		mUSBStreamRunning = TRUE;
		resultCode = kIOReturnSuccess;
	}
	else
	{
		debugIOLog ("! AppleUSBAudioEngine[%p]::startUSBStream () - Stream will *NOT* start because numQueued = 0", this);
		resultCode = kIOReturnError;
	}

Exit:
    debugIOLog ("- AppleUSBAudioEngine[%p]::startUSBStream () = %x", this, resultCode);
    return resultCode;
}

IOReturn AppleUSBAudioEngine::stopUSBStream () {
	debugIOLog ("+ AppleUSBAudioEngine[%p]::stopUSBStream ()", this);

	if (0 == mShouldStop) 
	{
		mShouldStop = 1;
	}

	if (NULL != mPipe) 
	{
		if (FALSE == mTerminatingDriver) 
		{
			// Don't call USB if we are being terminated because we could deadlock their workloop.
			mPipe->SetPipePolicy (0, 0);
		}

		// Have to close the current pipe so we can open a new one because changing the alternate interface will tear down the current pipe
		mPipe->release ();
		mPipe = NULL;
	}

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

	if (FALSE == mTerminatingDriver) 
	{
		// Don't call USB if we are being terminated because we could deadlock their workloop.
		if (NULL != mStreamInterface) 
		{	// if we don't have an interface, message() got called and we are being terminated
			(void)mStreamInterface->SetAlternateInterface (this, kRootAlternateSetting);
		}
	}

	mUSBStreamRunning = FALSE;

	debugIOLog ("- AppleUSBAudioEngine[%p]::stopUSBStream ()", this);
	return kIOReturnSuccess;
}

void AppleUSBAudioEngine::takeTimeStamp (bool incrementLoopCount, AbsoluteTime *timestamp)
{
	#if LOGTIMESTAMPS
	UInt64	time_nanos;
	
	absolutetime_to_nanoseconds (*timestamp, &time_nanos);
	// if (getDirection () == kIOAudioStreamDirectionInput)
	// if (getDirection () == kIOAudioStreamDirectionOutput)
	if (true)
	{
		debugIOLog ("? AppleUSBAudioEngine[%p]::takeTimeStamp (%d, %p) = %llu ns", this, mHaveTakenFirstTimeStamp, timestamp, time_nanos);
	}
	#endif
	if (false == mHaveTakenFirstTimeStamp)
	{
		super::takeTimeStamp (false, timestamp);
		debugIOLog ("? AppleUSBAudioEngine[%p]::takeTimeStamp (0, %p) - First timestamp taken", this, timestamp);
		mHaveTakenFirstTimeStamp = true;
	}
	else
	{
		super::takeTimeStamp (incrementLoopCount, timestamp);
	}
}

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

	if (miSubEngine == (AppleiSubEngine *)provider) 
	{
		debugIOLog ("? AppleUSBAudioEngine[%p]::willTerminate () - iSub requesting termination", this);

		iSubTeardownConnection ();

		// Set up notifier to run when iSub shows up again
		if (    (miSubAttachToggle->getIntValue ())
			 && (NULL == miSubEngineNotifier))		// rdar://3881790
		{
			debugIOLog ("? AppleUSBAudioEngine[%p]::willTerminate () - Adding miSubEngineNotifier because of iSub termination ...", this);
			miSubEngineNotifier = addNotification (gIOPublishNotification, serviceMatching ("AppleiSubEngine"), (IOServiceNotificationHandler)&iSubEnginePublished, this);
		}
	}

	if (mStreamInterface == provider) 
	{
		mTerminatingDriver = TRUE;
		if (FALSE == mUSBStreamRunning) 
		{
			// Close our stream interface and go away because we're not running.
			debugIOLog ("? AppleUSBAudioEngine[%p]::willTerminate () - Closing stream interface", this);
			mStreamInterface->close (this);
			// [rdar://4287899] mStreamInterface = NULL;
		} 
		else 
		{
			// Have the write completion routine clean everything up because we are running.
			debugIOLog ("? AppleUSBAudioEngine[%p]::willTerminate () - Checking mShouldStop = 0x%x", this, mShouldStop);
			if (0 == mShouldStop) 
			{
				mShouldStop++;
			}
		}
		mOldiSubEngine = miSubEngine;
		iSubCloseAction (this, NULL, NULL, NULL, NULL);
	}

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

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

IOReturn AppleUSBAudioEngine::writeFrameList (UInt32 frameListNum) {
    IOReturn							result;
	
	result = PrepareWriteFrameList (frameListNum);
	FailIf (kIOReturnSuccess != result, Exit);
	FailIf (NULL == mStreamInterface, Exit);
	result = kIOReturnError;		// reset the error in case mPipe is null

	FailIf (NULL == mPipe, Exit);

	if (mNeedTimeStamps) 
	{
		if (mUHCISupport)
		{
			// We might have to do two separate writes here. Do the first one, and then if necessary, do the second.
			#if DEBUGUHCI
			debugIOLog ("? AppleUSBAudioEngine::writeFrameList () - Writing mSampleBufferWrapDescriptors[0]");
			#endif
			result = mPipe->Write (mWrapDescriptors[0], mUSBFrameToQueue, mNumFramesInFirstList, &mUSBIsocFrames[frameListNum * mNumUSBFramesPerList], &mUSBCompletion[frameListNum], 1);
		
			// mNumFramesInFirstList must be less than mNumUSBFramesPerList if we wrapped.
			if ( mNumFramesInFirstList < mNumUSBFramesPerList )
			{
				#if DEBUGUHCI
				debugIOLog ("? AppleUSBAudioEngine::writeFrameList () - writeFrameList: Writing mSampleBufferWrapDescriptors[1] to frame %ld", mUSBFrameToQueue + mNumFramesInFirstList);
				#endif
				result = mPipe->Write (mWrapDescriptors[1], mUSBFrameToQueue + mNumFramesInFirstList, mNumUSBFramesPerList - mNumFramesInFirstList, &mUSBIsocFrames[frameListNum * mNumUSBFramesPerList + mNumFramesInFirstList], &mExtraUSBCompletion, 1);
				// result = thePipe->Write (mSampleBufferWrapDescriptors[1], usbFrameToQueueAt + mNumFramesInFirstList, numUSBFramesPerList - mNumFramesInFirstList, &theUSBIsocFrames[frameListNumber * numUSBFramesPerList + mNumFramesInFirstList], &extraUsbCompletion, 1);
			}
		}
		else
		{
			result = mPipe->Write (mWrapRangeDescriptor, mUSBFrameToQueue, mNumUSBFramesPerList, &mUSBIsocFrames[frameListNum * mNumUSBFramesPerList], &mUSBCompletion[frameListNum], 1);
		}
		mNeedTimeStamps = FALSE;
	} 
	else 
	{
		result = mPipe->Write (mSampleBufferDescriptors[frameListNum], mUSBFrameToQueue, mNumUSBFramesPerList, &mUSBIsocFrames[frameListNum * mNumUSBFramesPerList], &mUSBCompletion[frameListNum], 0);
	}
	FailIf (result != kIOReturnSuccess, Exit);
	
	// keep track of this frame number for time stamping
	if (NULL != mFrameQueuedForList)
	{
		mFrameQueuedForList[frameListNum] = mUSBFrameToQueue;
	}
		
	mUSBFrameToQueue += mNumUSBFramesPerList;

Exit:
	if (kIOReturnSuccess != result)
	{
		debugIOLog ("? AppleUSBAudioEngine[%p]::writeFrameList () - failed with error 0x%x", this, result);
	}
	return result;
}

void AppleUSBAudioEngine::writeHandler (void * object, void * parameter, IOReturn result, IOUSBLowLatencyIsocFrame * pFrames) {
    AppleUSBAudioEngine *   self;
    AbsoluteTime            time;
    UInt64                  curUSBFrameNumber;
    UInt32                  frameListToWrite;
    UInt32                  byteOffset;
	UInt32                  frameIndex;
    UInt32                  byteCount;
    UInt32                  preWrapBytes;
	UInt32					numberOfFramesToCheck;
    SInt64                  frameDifference;
    SInt32                  expectedFrames;

    self = (AppleUSBAudioEngine *)object;
    FailIf (TRUE == self->mInCompletion, Exit);
    self->mInCompletion = TRUE;
    FailIf (NULL == self->mStreamInterface, Exit);

    curUSBFrameNumber = self->mStreamInterface->GetDevice()->GetBus()->GetFrameNumber ();
    frameDifference = (SInt64)(self->mUSBFrameToQueue - curUSBFrameNumber);
    expectedFrames = (SInt32)(self->mNumUSBFramesPerList * (self->mNumUSBFrameListsToQueue / 2)) + 1;
	numberOfFramesToCheck = 0;
	
	#if DEBUGUHCI
	debugIOLog ("? AppleUSBAudioEngine::writeHandler () - writeHandler: curUSBFrameNumber = %llu parameter = 0x%x mUSBFrameToQueue = %llu", curUSBFrameNumber, (UInt32)parameter, self->mUSBFrameToQueue);
	debugIOLog ("? AppleUSBAudioEngine::writeHandler () - %llu ?> %lu", frameDifference, expectedFrames);
	#endif
	
	// This logical expression checks to see if IOUSBFamily fell behind. If so, we don't need to advance the frame list
    if (    (frameDifference > expectedFrames) 
         && (    (!(self->mUHCISupport))		// This is not a UHCI connection
              || (0 == parameter)))				// or this is a wrapping condition for a UHCI connection
    {
        debugIOLog ("? AppleUSBAudioEngine::writeHandler () - Not advancing frame list");
        goto Exit;
    }
    
    if (kIOReturnAborted != result) 
    {
        
		if (kIOReturnSuccess != result)
		{
			debugIOLog ("! AppleUSBAudioEngine::writeHandler () - Frame list %d write returned with error 0x%x", self->mCurrentFrameList, result);
        }
		
		numberOfFramesToCheck = ((self->mUHCISupport && (UInt32) parameter) ? self->mNumFramesInFirstList : self->mNumUSBFramesPerList);
		if	(		(!(self->mHaveTakenFirstTimeStamp))
				&&	(0 == self->mBufferOffset))
		{
			// Check to see if we should take our first time stamp in this frame list.
			for (UInt16 i = 0; i < numberOfFramesToCheck && pFrames; i++)
			{
				if (pFrames[i].frActCount)
				{
					// We should take our first time stamp here. Here, i represents the first frame in the framelist with a nonzero frActCount, i.e., our
					// first isoc data transfer.
					debugIOLog ("? AppleUSBAudioEngine::writeHandler () - Taking first time stamp on frame list %lu frame index %d", self->mCurrentFrameList, i); 
					debugIOLog ("     pFrames[%d].frStatus = %lu", i, pFrames[i].frStatus);
					debugIOLog ("     pFrames[%d].frReqCount = %lu", i, pFrames[i].frReqCount);
					debugIOLog ("     pFrames[%d].frActCount = %lu", i, pFrames[i].frActCount);
					debugIOLog ("     pFrames[%d].frTimeStamp = 0x%x", i, * (UInt64 *) &(pFrames[i].frTimeStamp));
					time = self->generateTimeStamp (i, 0, 0);
					self->takeTimeStamp (FALSE, &time);
					break;
				}
			}
		}
		#ifdef DEBUG
		// Comb the isoc frame list for alarming statuses.
		
		for (UInt16 i = 0; i < numberOfFramesToCheck && pFrames; i++) 
        {
            if	(		(kIOReturnSuccess != pFrames[i].frStatus)
					||	(pFrames[i].frActCount != pFrames[i].frReqCount))
            {
                debugIOLog ("! AppleUSBAudioEngine::writeHandler () - pFrames[%d].frStatus = 0x%x ", i, pFrames[i].frStatus);
				debugIOLog ("     pFrames[%d].frReqCount = %lu", i, pFrames[i].frReqCount);
				debugIOLog ("     pFrames[%d].frActCount = %lu", i, pFrames[i].frActCount);
				debugIOLog ("     pFrames[%d].frTimeStamp = 0x%x", i, * (UInt64 *) &(pFrames[i].frTimeStamp));
				// IOLog ("AppleUSBAudio: ERROR on output! Short packet of size %lu encountered when %lu bytes were requested.\n", pFrames[i].frActCount, pFrames[i].frReqCount);
            }
        }
		#endif
        
        // skip ahead and see if that helps
		
        if (self->mUSBFrameToQueue <= curUSBFrameNumber) 
        {
			debugIOLog ("! AppleUSBAudioEngine::writeHandler - Fell behind! mUSBFrameToQueue = %llu, curUSBFrameNumber = %llu", self->mUSBFrameToQueue, curUSBFrameNumber);
            self->mUSBFrameToQueue = curUSBFrameNumber + kMinimumFrameOffset;
        }
    }

    if (0 != parameter) 
    {
        // Take a timestamp
        byteOffset = (UInt32)parameter & 0x00FF;
		frameIndex = ((UInt32)parameter >> 16) - 1;     // zero-indexed frame index in mCurrentFrameList
		byteCount = pFrames[frameIndex].frActCount;     // number of bytes written
		preWrapBytes = byteCount - byteOffset;          // number of bytes written - wrapped bytes
		time = self->generateTimeStamp (frameIndex, preWrapBytes, byteCount);
        self->takeTimeStamp (TRUE, &time);
		
		// Now that we've taken the time stamp, if this is UHCI and the first of two writes, we need to exit now.
		// writeHandlerForUHCI will advance the frame list and queue the next write for us. If we do not let the
		// next write be queued by writeHandlerForUHCI, we will get intermittent artifacts after many minutes of
		// streaming.
		
		if (    (self->mUHCISupport)
			 &&	(frameDifference > expectedFrames - 1))
		{
			// Check to see if we should stop since we're about to skip the normal check
			if (self->mShouldStop > 0) 
			{
				debugIOLog ("? AppleUSBAudioEngine::writeHandler() - stopping: %d", self->mShouldStop);
				self->mShouldStop++;
				if (self->mShouldStop == (self->mNumUSBFrameListsToQueue + 1) && TRUE == self->mTerminatingDriver) 
				{
					self->mStreamInterface->close (self);
					self->mStreamInterface = NULL;
				}
			} // mShouldStop check
			
			// We're done for now. writeHandlerForUHCI will handle the rest.
			goto Exit;
			
		} // if this is the first of two UHCI write callbacks
		
    } // if we have wrapped

	if (self->mCurrentFrameList == self->mNumUSBFrameLists - 1) 
	{
		self->mCurrentFrameList = 0;
	} 
	else 
	{
		self->mCurrentFrameList++;
	}
    
    if (self->mShouldStop > 0) 
    {
        debugIOLog ("? AppleUSBAudioEngine::writeHandler() - stopping: %d", self->mShouldStop);
        self->mShouldStop++;
        if (self->mShouldStop == (self->mNumUSBFrameListsToQueue + 1) && TRUE == self->mTerminatingDriver) 
        {
            self->mStreamInterface->close (self);
            // [rdar://4287899] self->mStreamInterface = NULL;
        }
    } 
    else 
    {
		// Queue another write
        frameListToWrite = (self->mCurrentFrameList - 1) + self->mNumUSBFrameListsToQueue;
        if (frameListToWrite >= self->mNumUSBFrameLists) 
        {
            frameListToWrite -= self->mNumUSBFrameLists;
        }
        self->writeFrameList (frameListToWrite);
    }
	
Exit:
    self->mInCompletion = FALSE;
    return;
}

void AppleUSBAudioEngine::writeHandlerForUHCI (void * object, void * parameter, IOReturn result, IOUSBLowLatencyIsocFrame * pFrames) {
    AppleUSBAudioEngine *   self;
	UInt64                  curUSBFrameNumber;
	SInt64                  frameDifference;
    SInt32                  expectedFrames;
	UInt32                  frameListToWrite;
	UInt32					numberOfFramesToCheck;

    self = (AppleUSBAudioEngine *)object;
    FailIf (TRUE == self->mInCompletion, Exit);
    self->mInCompletion = TRUE;
    FailIf (NULL == self->mStreamInterface, Exit);

    curUSBFrameNumber = self->mStreamInterface->GetDevice()->GetBus()->GetFrameNumber ();
    frameDifference = (SInt64)(self->mUSBFrameToQueue - curUSBFrameNumber);
    expectedFrames = (SInt32)(self->mNumUSBFramesPerList * (self->mNumUSBFrameListsToQueue / 2)) + 1;
	numberOfFramesToCheck = 0;
	
	#if DEBUGUHCI
	debugIOLog ("? AppleUSBAudioEngine[%p]::writeHandlerForUHCI () - writeHandlerForUHCI: curUSBFrameNumber = %llu parameter = 0x%x mUSBFrameToQueue = %llu", curUSBFrameNumber, (UInt32)parameter, self->mUSBFrameToQueue);
	debugIOLog ("? AppleUSBAudioEngine[%p]::writeHandlerForUHCI () -  %llu ?> %lu", frameDifference, expectedFrames);
	#endif
	
	// This logical expression checks to see if IOUSBFamily fell behind. If so, we don't need to advance the frame list
    
	if (kIOReturnAborted != result) 
    {        
		if (kIOReturnSuccess != result)
		{
			debugIOLog ("! AppleUSBAudioEngine::writeHandlerForUHCI () - Frame list %d (split for UHCI) write returned with error 0x%x", self->mCurrentFrameList, result);
        }
		#ifdef DEBUG
		// Comb the isoc frame list for alarming statuses.
		numberOfFramesToCheck = (self->mUHCISupport ? (self->mNumUSBFramesPerList - self->mNumFramesInFirstList) : self->mNumUSBFramesPerList);
		
		for (UInt16 i = 0; i < numberOfFramesToCheck && pFrames; i++) 
        {
            if	(		(kIOReturnSuccess != pFrames[i].frStatus)
					||	(pFrames[i].frActCount != pFrames[i].frReqCount))
            {
                debugIOLog ("! AppleUSBAudioEngine::writeHandlerForUHCI () - pFrames[%d].frStatus = 0x%x ", i, pFrames[i].frStatus);
				debugIOLog ("     pFrames[%d].frReqCount = %lu", i, pFrames[i].frReqCount);
				debugIOLog ("     pFrames[%d].frActCount = %lu", i, pFrames[i].frActCount);
				debugIOLog ("     pFrames[%d].frTimeStamp = 0x%x", i, * (UInt64 *) &(pFrames[i].frTimeStamp));
				// IOLog ("AppleUSBAudio: ERROR on output! Short packet of size %lu encountered when %lu bytes were requested.\n", pFrames[i].frActCount, pFrames[i].frReqCount);
            }
        }
		#endif
        
        // skip ahead and see if that helps
        if (self->mUSBFrameToQueue <= curUSBFrameNumber) 
        {
			debugIOLog ("! AppleUSBAudioEngine[%p]::writeHandlerForUHCI () - Fell behind! mUSBFrameToQueue = %llu, curUSBFrameNumber = %llu", self->mUSBFrameToQueue, curUSBFrameNumber);
			debugIOLog ("! AppleUSBAudioEngine[%p]::writeHandlerForUHCI () - Skipping ahead ...");
            self->mUSBFrameToQueue = curUSBFrameNumber + kMinimumFrameOffset;
        }
    }

    // Advance the frame list
	
	if (self->mCurrentFrameList == self->mNumUSBFrameLists - 1) 
	{
		self->mCurrentFrameList = 0;
	} 
	else 
	{
		self->mCurrentFrameList++;
	}
    
	// Queue another write if we don't need to stop
	// self->mShouldStop is handled by writeHandler ()
	
    if (0 == self->mShouldStop) 
    {
		// Queue another write
        frameListToWrite = (self->mCurrentFrameList - 1) + self->mNumUSBFrameListsToQueue;
        if (frameListToWrite >= self->mNumUSBFrameLists) 
        {
            frameListToWrite -= self->mNumUSBFrameLists;
        }
        self->writeFrameList (frameListToWrite);
    }
	else
	{
		debugIOLog ("? AppleUSBAudioEngine[%p]::writeHandlerForUHCI () - Halting.", self);
	}

Exit:
    self->mInCompletion = FALSE;
    return;
}

