//--------------------------------------------------------------------------------
//
//	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

#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);

/*	Moving to stop because the engine might be leaked and we need to remove the notifier even in that case.
	if (NULL != iSubEngineNotifier) {
		iSubEngineNotifier->remove ();
		iSubEngineNotifier = NULL;
	} */

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

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

	if (aveSampleRateBuf) {
		IOFree (aveSampleRateBuf, 4);
		aveSampleRateBuf = NULL;
	}

	if (signal) {
		signal->release ();
	}

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

	if (theWrapRangeDescriptor) {
		theWrapRangeDescriptor->release ();
		theWrapDescriptors[0]->release ();
		theWrapDescriptors[1]->release ();
		theWrapRangeDescriptor = NULL;
	}

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

	if (streamNotifier) {
		streamNotifier->remove ();
		streamNotifier = 0;
	}

	if (interfaceVendor) {
		interfaceVendor->release ();
		interfaceVendor = 0;
	}

	if (interfaceProduct) {
		interfaceProduct->release ();
		interfaceProduct = 0;
	}

	if (deviceReleaseNumber) {
		deviceReleaseNumber->release ();
		deviceReleaseNumber = 0;
	}

	if (configurationValue) {
		configurationValue->release ();
		configurationValue = 0;
	}

	if (interfaceNumber) {
		interfaceNumber->release ();
		interfaceNumber = 0;
	}

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

		IOFree (soundBufferDescriptors, numUSBFrameLists * sizeof (IOSubMemoryDescriptor *));
		soundBufferDescriptors = NULL;
	}

	if (NULL != theUSBIsocFrames) {
		IOFree (theUSBIsocFrames, numUSBFrameLists * numUSBFramesPerList * sizeof (IOUSBLowLatencyIsocFrame));
		theUSBIsocFrames = NULL;
	}

	if (NULL != usbCompletion) {
		IOFree (usbCompletion, numUSBFrameLists * sizeof (IOUSBLowLatencyIsocCompletion));
		usbCompletion = NULL;
	}

	if (NULL != getSampleBuffer ()) {
		IOFree (getSampleBuffer (), getSampleBufferSize ());
	}

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

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

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

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

	if (mainStream) {
		mainStream->release ();
		mainStream = 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");

	signal = IOSyncer::create (FALSE);
	result = TRUE;
        
	srcPhase = 1.0;		// aml 3.4.02
	srcState = 0.0;		// aml 3.6.02
	justResetClipPosition = 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 (usbAudioDevice == provider || streamInterface == provider) {
		result = TRUE;		// it is OK to terminate us
	} else {
		result = FALSE;		// don't terminate us
	}

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

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

	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.
	streamInterface = OSDynamicCast (IOUSBInterface, provider);
	FailIf (NULL == streamInterface, Exit);
	ourInterfaceNumber = streamInterface->GetInterfaceNumber ();
	debugIOLog ("ourInterfaceNumber = %d", ourInterfaceNumber);

	//	This snippet of code goes with the audiodevicePublished function
	//	we wait until the Stream interface finds a partner.
	audioDeviceNotifier = addNotification (gIOPublishNotification,
											serviceMatching ("AppleUSBAudioDevice"),
											(IOServiceNotificationHandler)&audioDevicePublished,
											this,
											NULL);

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

	FailIf (NULL == usbAudioDevice, Exit);

	// If this is an iSub, we need to not go any further because we don't support it in this driver
	usbAudio = usbAudioDevice->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, usbAudioDevice);
    
	debugIOLog ("-%d = AppleUSBAudioEngine[%p]::start (%p)", resultCode, this, provider);

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

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

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

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

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

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

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

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

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

	if (streamInterface) {
		streamInterface->close (this);
		streamInterface = 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-

/*
IOAudioEngineState AppleUSBAudioEngine::_setState(IOAudioEngineState newState) {
    IOAudioEngineState oldState;

    oldState = super::setState(newState);

    if (notifyDeviceOfStop && (newState != oldState) && (newState == kIOAudioEngineStopped)) {
        FailIf (NULL == usbAudioDevice, Exit);

        usbAudioDevice->streamStopped(this);
    }

Exit:
    return oldState;
}
*/
IOReturn AppleUSBAudioEngine::AddAvailableFormatsFromDevice (USBAudioConfigObject *usbAudio) {
    IOAudioStreamFormat					streamFormat;
    IOAudioStreamFormatExtension		streamFormatExtension;
    IOAudioSampleRate					lowSampleRate;
    IOAudioSampleRate					highSampleRate;
	UInt32 *							sampleRates;
	IOReturn							result;
    UInt16								numAltInterfaces;
    UInt8								numSampleRates;
    UInt8								altInterfaceIndx;
	UInt8								rateIndx;

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

	numAltInterfaces = usbAudio->GetNumAltStreamInterfaces (ourInterfaceNumber);
	debugIOLog ("There are %d alternate stream interfaces on interface %d", numAltInterfaces, ourInterfaceNumber);

	// Find all of the available formats on the device.
	for (altInterfaceIndx = 1; altInterfaceIndx < numAltInterfaces; altInterfaceIndx++) {
		numSampleRates = usbAudio->GetNumSampleRates (ourInterfaceNumber, altInterfaceIndx);
		sampleRates = usbAudio->GetSampleRates (ourInterfaceNumber, altInterfaceIndx);

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

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

		switch (usbAudio->GetFormat (ourInterfaceNumber, altInterfaceIndx)) {
			case PCM:
				streamFormat.fSampleFormat = kIOAudioStreamSampleFormatLinearPCM;
				streamFormat.fNumericRepresentation = kIOAudioStreamNumericRepresentationSignedInt;
				streamFormat.fIsMixable = TRUE;
				break;
			case AC3:	// just starting to stub something in for AC-3 support
				debugIOLog ("variable bit rate AC-3 audio format type!");
				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 (ourInterfaceNumber, altInterfaceIndx));
//				streamFormatExtension.fFramesPerPacket = usbAudio->GetSamplesPerFrame (ourInterfaceNumber, altInterfaceIndx);
				streamFormatExtension.fFramesPerPacket = 1536;
//				streamFormatExtension.fBytesPerPacket = ((usbAudio->GetMaxBitRate (ourInterfaceNumber, altInterfaceIndx) * 1024 / 8) + 500) / 1000;
				streamFormatExtension.fBytesPerPacket = streamFormatExtension.fFramesPerPacket * streamFormat.fNumChannels * usbAudio->GetSubframeSize (ourInterfaceNumber, altInterfaceIndx);
				break;
			case IEC1937_AC3:
				debugIOLog ("IEC1937 AC-3 audio format type!");
				streamFormat.fSampleFormat = kIOAudioStreamSampleFormat1937AC3;
				streamFormat.fNumericRepresentation = kIOAudioStreamNumericRepresentationSignedInt;
				streamFormat.fIsMixable = FALSE;

				streamFormatExtension.fFramesPerPacket = 1536;
				streamFormatExtension.fBytesPerPacket = streamFormatExtension.fFramesPerPacket * streamFormat.fNumChannels * usbAudio->GetSubframeSize (ourInterfaceNumber, altInterfaceIndx);
				break;
			default:
				debugIOLog ("interface format = %x", usbAudio->GetFormat (ourInterfaceNumber, altInterfaceIndx));
				debugIOLog ("interface doesn't support a format that we can deal with, so we're not making it available");
				continue;	// skip this alternate interface
		}

		debugIOLog ("Interface %d, Alt %d has a ", ourInterfaceNumber, altInterfaceIndx);
		debugIOLog ("%d bit interface, ", streamFormat.fBitDepth);
		debugIOLog ("%d channels, and ", streamFormat.fNumChannels);
		debugIOLog ("%d sample rates, which are:", numSampleRates);

		if (numSampleRates) {
			for (rateIndx = 0; rateIndx < numSampleRates; rateIndx++) {
				debugIOLog (" %d", sampleRates[rateIndx]);
				lowSampleRate.whole = sampleRates[rateIndx];
				lowSampleRate.fraction = 0;
				mainStream->addAvailableFormat (&streamFormat, &streamFormatExtension, &lowSampleRate, &lowSampleRate);
				if (kIOAudioStreamSampleFormatLinearPCM == streamFormat.fSampleFormat) {
					streamFormat.fIsMixable = FALSE;
					mainStream->addAvailableFormat (&streamFormat, &streamFormatExtension, &lowSampleRate, &lowSampleRate);
				}
			}
			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;
			mainStream->addAvailableFormat (&streamFormat, &streamFormatExtension, &lowSampleRate, &highSampleRate);
			if (kIOAudioStreamSampleFormatLinearPCM == streamFormat.fSampleFormat) {
				streamFormat.fIsMixable = FALSE;
				mainStream->addAvailableFormat (&streamFormat, &streamFormatExtension, &lowSampleRate, &highSampleRate);
			}
		}
	}

	result = kIOReturnSuccess;

Exit:
	return result;
}

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->ourInterfaceNumber)) {
			debugIOLog ("++AppleUSBAudioEngine[%p]: found device (%p) for Audio Engine (%p)", audioEngine, audioDevice, audioEngine);
			audioEngine->usbAudioDevice = audioDevice;
			audioEngine->signal->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;

	result = kIOReturnSuccess;
	theAssociatedPipe = NULL;
	address = usbAudio->GetIsocEndpointAddress (ourInterfaceNumber, alternateInterfaceID, direction);
	syncType = usbAudio->GetIsocEndpointSyncType (ourInterfaceNumber, alternateInterfaceID, address);
	if (kAsynchSyncType == syncType) {
		debugIOLog ("checking endpoint %d for an associated endpoint", address);
		assocEndpoint = usbAudio->GetIsocAssociatedEndpointAddress (ourInterfaceNumber, alternateInterfaceID, address);
		if (assocEndpoint != 0) {
			debugIOLog ("This endpoint has an associated synch endpoint!");
			refreshInterval = usbAudio->GetIsocAssociatedEndpointRefreshInt (ourInterfaceNumber, alternateInterfaceID, assocEndpoint);
			debugIOLog ("The refresh interval is %d", refreshInterval);
			framesUntilRefresh = 1 << refreshInterval;		// the same as 2^refreshInterval
			// 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 (framesUntilRefresh < numUSBFramesPerList) {
				debugIOLog ("Need to adjust numUSBFramesPerList, %ld < %ld", framesUntilRefresh, numUSBFramesPerList);
				if (NULL != theUSBIsocFrames) {
					debugIOLog ("Disposing of current theUSBIsocFrames [%p]", theUSBIsocFrames);
					IOFree (theUSBIsocFrames, numUSBFrameLists * numUSBFramesPerList * sizeof (IOUSBLowLatencyIsocFrame));
					theUSBIsocFrames = NULL;
				}
				numUSBFramesPerList = framesUntilRefresh;		// It needs to be updated more often, so run as the device requests.
				theUSBIsocFrames = (IOUSBLowLatencyIsocFrame *)IOMalloc (numUSBFrameLists * numUSBFramesPerList * sizeof (IOUSBLowLatencyIsocFrame));
				debugIOLog ("theUSBIsocFrames is now %p", theUSBIsocFrames);
				FailIf (NULL == theUSBIsocFrames, Exit);
			}
			associatedEndpoint.type = kUSBIsoc;
			associatedEndpoint.direction = kUSBIn;	// The associated endpoint always goes "in"
			associatedEndpoint.maxPacketSize = 3;	// The sample rate is always 3 bytes
			associatedEndpoint.interval = 0xFF;		// don't care
			theAssociatedPipe = streamInterface->FindNextPipe (NULL, &associatedEndpoint);
			FailWithAction (NULL == theAssociatedPipe, result = kIOReturnError, Exit);

			if (NULL == neededSampleRateDescriptor) {
				aveSampleRateBuf = (UInt32 *)IOMalloc (sizeof (UInt32));
				FailIf (NULL == aveSampleRateBuf, Exit);
				bzero (aveSampleRateBuf, 4);
				neededSampleRateDescriptor = IOMemoryDescriptor::withAddress (aveSampleRateBuf, 4, kIODirectionIn);
				FailIf (NULL == neededSampleRateDescriptor, Exit);
			}
			theSampleRateFrame.frStatus = -1;
			theSampleRateFrame.frReqCount = 3;
			theSampleRateFrame.frActCount = 0;
			sampleRateCompletion.target = (void *)this;
			sampleRateCompletion.action = sampleRateHandler;
			sampleRateCompletion.parameter = 0;

			theAssociatedPipe->retain ();
		} else {
			debugIOLog ("Couldn't find the associated synch endpoint!");
		}
	} else {
		debugIOLog ("This endpoint does not have an associated synch endpoint");
	}

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 == shouldStop && TRUE != inCompletion) {
		curUSBFrameNumber = streamInterface->GetDevice()->GetBus()->GetFrameNumber ();
		framesLeftInQueue = usbFrameToQueueAt - curUSBFrameNumber;
		if (framesLeftInQueue < (numUSBFramesPerList * (numUSBFrameListsToQueue / 2)) / 2) {
			debugIOLog ("queue a write from clipOutputSamples: framesLeftInQueue = %ld", (UInt32)framesLeftInQueue);
//			debugIOLog ("0=0x%x, 1=0x%x, this=0x%x", usbCompletion[0].parameter, usbCompletion[1].parameter, usbCompletion[currentFrameList].parameter);
			writeHandler (this, usbCompletion[currentFrameList].parameter, kIOReturnSuccess, &theUSBIsocFrames[currentFrameList * numUSBFramesPerList]);
		}
	}

#if 0
	#if DEBUGLOG
	UInt32 currentLocation = getCurrentSampleFrame ();

	if (firstSampleFrame <= currentLocation && (firstSampleFrame + numSampleFrames) > currentLocation) {
		debugIOLog ("!!!!!!!!!!Buffer problems!!!!!!!!!");
		debugIOLog ("currentLocation = 0x%lX, firstSampleFrame = 0x%lX, numSampleFrames = 0x%lX", currentLocation, firstSampleFrame, numSampleFrames);
	} else if (((currentLocation + 96) - firstSampleFrame) < numSampleFrames) {
		debugIOLog("!!!!!! Possible buffer problem !!!!!!");
		debugIOLog ("currentLocation = 0x%lX, firstSampleFrame = 0x%lX, numSampleFrames = 0x%lX", currentLocation, firstSampleFrame, numSampleFrames);
		debugIOLog ("overlap = 0x%X [%d]", (currentLocation + 96) - firstSampleFrame, (currentLocation + 96) - firstSampleFrame);
	}
	#endif
#endif

	if (TRUE == streamFormat->fIsMixable) {
		if (NULL != iSubBufferMemory && NULL != iSubEngine) {
			iSubBufferLen = iSubBufferMemory->getLength ();
			iSubBuffer = (void*)iSubBufferMemory->getVirtualSegment (0, &iSubBufferLen);
			// (iSubBufferLen / 2) is because iSubBufferOffset is in UInt16s so convert iSubBufferLen to UInt16 length
			iSubBufferLen = iSubBufferLen / 2;

			sampleRate = getSampleRate()->whole;

			// aml 3.1.02
			iSubFormat.altInterface = iSubEngine->GetAltInterface();
			iSubFormat.numChannels = iSubEngine->GetNumChannels();
			iSubFormat.bytesPerSample = iSubEngine->GetBytesPerSample();
			iSubFormat.outputSampleRate = iSubEngine->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 (needToSync == FALSE && previousClippedToFrame == firstSampleFrame && 0x0 != iSubEngine->GetCurrentLoopCount ()) {
#else
			if (needToSync == FALSE && previousClippedToFrame == firstSampleFrame && 0xFFFFFFFF != iSubEngine->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 = iSubBufferOffset - ((iSubFormat.outputSampleRate) / 1000);		// 6 samples at 6kHz
			if (safetyOffset < 0) {
				safetyOffset += iSubBufferLen;
				}
			if (iSubLoopCount == iSubEngine->GetCurrentLoopCount () && safetyOffset < (SInt32)(iSubEngine->GetCurrentByteCount () / 2)) {
#if DEBUGISUB
				debugIOLog ("****iSub is in front of write head iSubBufferOffset = %ld, iSubEngine->GetCurrentByteCount () / 2 = %ld", iSubBufferOffset, iSubEngine->GetCurrentByteCount () / 2);
#endif
				needToSync = TRUE;
				startiSub = TRUE;
			} else if (iSubLoopCount > (iSubEngine->GetCurrentLoopCount () + 1)) {
#if DEBUGISUB
				debugIOLog ("****looped more than the iSub iSubLoopCount = %ld, iSubEngine->GetCurrentLoopCount () = %ld", iSubLoopCount, iSubEngine->GetCurrentLoopCount ());
#endif
				needToSync = TRUE;
				startiSub = TRUE;
		    } else if (iSubLoopCount < iSubEngine->GetCurrentLoopCount ()) {
#if DEBUGISUB
				debugIOLog ("****iSub is ahead of us iSubLoopCount = %ld, iSubEngine->GetCurrentLoopCount () = %ld", iSubLoopCount, iSubEngine->GetCurrentLoopCount ());
#endif
				needToSync = TRUE;
				startiSub = TRUE;
		    } else if (iSubLoopCount == iSubEngine->GetCurrentLoopCount () && iSubBufferOffset > ((SInt32)((iSubEngine->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 iSubBufferOffset = %ld, (iSubEngine->GetCurrentByteCount () / 2 + iSubBufferLen) = %ld", iSubBufferOffset, (iSubEngine->GetCurrentByteCount() / 2 + iSubBufferLen));					
#endif
				needToSync = TRUE;
				startiSub = TRUE;
				}
			}

// aml 3.29.02 end move

			if (FALSE == needToSync && previousClippedToFrame != firstSampleFrame && !(previousClippedToFrame == getNumSampleFramesPerBuffer () && firstSampleFrame == 0)) {
#if DEBUGISUB
				debugIOLog ("iSubBufferOffset was %ld", iSubBufferOffset);
#endif
				if (firstSampleFrame < previousClippedToFrame) {
					// We've wrapped around the buffer
					// don't multiply by bit width because iSubBufferOffset is a UInt16 buffer pointer, not a UInt8 buffer pointer
// aml 3.29.02 flipped +/- to match AOA
					offsetDelta = (getNumSampleFramesPerBuffer () - firstSampleFrame + previousClippedToFrame) * iSubEngine->GetNumChannels();
				} else {
					offsetDelta = (firstSampleFrame - previousClippedToFrame) * iSubEngine->GetNumChannels();

				}
				// aml 3.21.02, adjust for new sample rate
				offsetDelta = (offsetDelta * 1000) / ((sampleRate * 1000) / iSubFormat.outputSampleRate);
				iSubBufferOffset += offsetDelta;
#if DEBUGISUB
				debugIOLog ("clip to point was %ld, now %ld (delta = %ld)", previousClippedToFrame, firstSampleFrame, offsetDelta);
				debugIOLog ("iSubBufferOffset is now %ld", iSubBufferOffset);
#endif
				if (iSubBufferOffset > (SInt32)iSubBufferLen) {
					// Our calculated spot has actually wrapped around the iSub's buffer.
					iSubLoopCount += iSubBufferOffset / iSubBufferLen;
					iSubBufferOffset = iSubBufferOffset % iSubBufferLen;
#if DEBUGISUB
					debugIOLog ("iSubBufferOffset > iSubBufferLen, iSubBufferOffset is now %ld", iSubBufferOffset);
#endif
				} else if (iSubBufferOffset < 0) {
					iSubBufferOffset += iSubBufferLen;
#if DEBUGISUB
					debugIOLog ("iSubBufferOffset < 0, iSubBufferOffset is now %ld", iSubBufferOffset);
#endif
				}
			}

// aml 3.29.02 added this check to match AOA
		if (TRUE == justResetClipPosition) {
			justResetClipPosition = FALSE;
			needToSync = FALSE;
			startiSub = FALSE;
		}

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

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

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

				// aml 2.18.02 added for 4th order filter phase compensator
				phaseCompState.xl_1 = 0.0;
				phaseCompState.xr_1 = 0.0;
				phaseCompState.xl_2 = 0.0;
				phaseCompState.xr_2 = 0.0;
				phaseCompState.yl_1 = 0.0;
				phaseCompState.yr_1 = 0.0;
				phaseCompState.yl_2 = 0.0;
				phaseCompState.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) * iSubEngine->GetNumChannels();
				} else {
					offsetDelta = (firstSampleFrame - curSampleFrame) * iSubEngine->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.
				iSubBufferOffset = offsetDelta;

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

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

				// Our calculated spot has actually wrapped around the iSub's buffer.
				iSubLoopCount += iSubBufferOffset / iSubBufferLen;
				iSubBufferOffset = iSubBufferOffset % iSubBufferLen;
#if DEBUGISUB
				debugIOLog ("iSubBufferOffset > iSubBufferLen, iSubBufferOffset is now %ld", iSubBufferOffset);
#endif
			} else if (iSubBufferOffset < 0) {
				iSubBufferOffset += iSubBufferLen;
#if DEBUGISUB
				debugIOLog ("iSubBufferOffset < 0, iSubBufferOffset is now %ld", iSubBufferOffset);
#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 srcPhase
// aml 3.6.02 added srcState
			if (mPlugin) {
				mPlugin->pluginProcess ((Float32*)mixBuf + (firstSampleFrame * streamFormat->fNumChannels), numSampleFrames, streamFormat->fNumChannels);
			}
			result = clipAppleUSBAudioToOutputStreamiSub (mixBuf, sampleBuf, &filterState, &filterState2, &phaseCompState, lowFreqSamples, highFreqSamples, firstSampleFrame, numSampleFrames, sampleRate, streamFormat, (SInt16*)iSubBuffer, &iSubLoopCount, &iSubBufferOffset, iSubBufferLen, &iSubFormat, &srcPhase, &srcState);
			if (TRUE == startiSub) {
				iSubEngine->StartiSub ();
				startiSub = FALSE;
				iSubLoopCount = 0;
			}
// aml 3.29.02 moved here to match AOA
			previousClippedToFrame = firstSampleFrame + numSampleFrames;
		} else {
			if (mPlugin) {
				mPlugin->pluginProcess ((Float32*)mixBuf + (firstSampleFrame * streamFormat->fNumChannels), numSampleFrames, streamFormat->fNumChannels);
			}
			result = clipAppleUSBAudioToOutputStream (mixBuf, sampleBuf, firstSampleFrame, numSampleFrames, streamFormat);
		}
	} else {
		UInt32			offset;

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

		memcpy ((UInt8 *)sampleBuf + offset, (UInt8 *)mixBuf, numSampleFrames * streamFormat->fNumChannels * (streamFormat->fBitWidth / 8));
		previousClippedToFrame = 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 currentFrameList, 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 currentFrameList.
// 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 currentFrameList.
void AppleUSBAudioEngine::CoalesceInputSamples (UInt32 numBytesToCoalesce, IOUSBLowLatencyIsocFrame * pFrames, UInt32 startingFrameList) {
#if DEBUGZEROTIME
	AbsoluteTime					timeNow;
#endif
	AbsoluteTime					time;
	AbsoluteTime					timeOffset;
	UInt64							nanos;
	UInt32							usbFrameIndex;
	UInt32							numFramesChecked;
	UInt32							numBytesToCopy;
	UInt32							numBytesToEnd;
	UInt32							numBytesCopied;
	UInt32							originalBufferOffset;
	SInt32							numBytesLeft;
	UInt8 *							source;
	UInt8 *							dest;
	Boolean							done;
#if DEBUGUSB
	UInt32							i;
#endif

	originalBufferOffset = 0;
	if (0 != numBytesToCoalesce) {
		originalBufferOffset = bufferOffset;		// So that when we later get called from the readHandler that we'll put everything in the right spot
		debugIOLog ("coalesce from %ld %ld bytes", originalBufferOffset, numBytesToCoalesce);
		debugIOLog ("%ld %ld", currentFrameList, numBytesToCoalesce);
	}

	if (NULL == pFrames) {
		pFrames = &theUSBIsocFrames[currentFrameList * numUSBFramesPerList];
	}

	dest = (UInt8 *)getSampleBuffer () + bufferOffset;
	source = (UInt8 *)readBuffer + (startingFrameList * readUSBFrameListSize);

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

	while (FALSE == done) {
#if DEBUGUSB
		if (pFrames[usbFrameIndex].frActCount < (pFrames[usbFrameIndex].frReqCount - bytesPerSampleFrame)) {
			debugIOLog ("ERROR! short read packet %d req = %d", pFrames[usbFrameIndex].frActCount, pFrames[usbFrameIndex].frReqCount);
		}
		if (kIOReturnSuccess != pFrames[usbFrameIndex].frStatus && kIOReturnUnderrun != pFrames[usbFrameIndex].frStatus) {
			debugIOLog ("Err:0x%lx fl:%ld, fr:%ld", pFrames[usbFrameIndex].frStatus, currentFrameList, usbFrameIndex);
		}
#endif

		numBytesToEnd = getSampleBufferSize () - bufferOffset;
		if ((UInt32)(pFrames[usbFrameIndex].frActCount) > numBytesToEnd) {
			numBytesToCopy = numBytesToEnd;
		} else {
			numBytesToCopy = pFrames[usbFrameIndex].frActCount;
#if !DEBUGUSB
			if (0 == numBytesToCoalesce) {
				pFrames[usbFrameIndex].frActCount = 0;
			}
#endif
		}

#if DEBUGUSB
//		for (i = pFrames[usbFrameIndex].frActCount == 176 ? 172 / 2 : 176 / 2; i < pFrames[usbFrameIndex].frActCount / 2; i++) {
//			if (((UInt16 *)source)[i] == 0) {
//				debugIOLog ("0x0 sample: %ld, %lX", currentFrameList, pFrames[usbFrameIndex].frActCount, i, source);
//			}
//			((UInt16 *)dest)[i] = ((UInt16 *)source)[i];
//		}
		memcpy (dest, source, numBytesToCopy);
		
		if ((UInt32)(pFrames[usbFrameIndex].frActCount) <= numBytesToEnd) {
			if (0 == numBytesToCoalesce) {
				pFrames[usbFrameIndex].frActCount = 0;
			}
		}
#else
		memcpy (dest, source, numBytesToCopy);
#endif

		bufferOffset 	+= numBytesToCopy;
		numBytesCopied 	= numBytesToCopy;
		numBytesLeft 	-= numBytesToCopy;

		if ((UInt32)(pFrames[usbFrameIndex].frActCount) > numBytesToEnd) {
			numBytesToCopy = (pFrames[usbFrameIndex].frActCount) - numBytesToEnd;
			dest = (UInt8 *)getSampleBuffer ();
			memcpy (dest, source + numBytesCopied, numBytesToCopy);
			bufferOffset = numBytesToCopy;
			numBytesLeft -= numBytesToCopy;

			if (0 == numBytesToCoalesce) {
				// we have wrapped and we were called by the completion routine -- take timestamp
				time = pFrames[usbFrameIndex].frTimeStamp;
/*
				// Need to figure out how long a USB frame is, so check with the one before or after this one, depending on if this is the last frame in the list or not
				if (usbFrameIndex == numUSBFramesPerList - 1) {
					tempTime = time;
					timeOther = pFrames[usbFrameIndex - 1].frTimeStamp;
					SUB_ABSOLUTETIME (&tempTime, &timeOther);
					absolutetime_to_nanoseconds (tempTime, &nanos);
				} else {
					timeOther = pFrames[usbFrameIndex + 1].frTimeStamp;
					SUB_ABSOLUTETIME (&timeOther, &time);
					absolutetime_to_nanoseconds (timeOther, &nanos);
				}
*/
				// Calculate the fractional part of this sample frame after the 0 point
//				nanos = (numBytesToCopy * nanos) / pFrames[usbFrameIndex].frActCount;
				nanos = (numBytesToCopy * 1000000) / pFrames[usbFrameIndex].frActCount;
				pFrames[usbFrameIndex].frActCount = 0;
				nanoseconds_to_absolutetime (nanos, &timeOffset);
				SUB_ABSOLUTETIME (&time, &timeOffset);								// time -= timeOffset
#if DEBUGZEROTIME
				timeNow = time;
				SUB_ABSOLUTETIME (&timeNow, &status->fLastLoopTime);
				absolutetime_to_nanoseconds (timeNow, &nanos);
				debugIOLog ("d=%ld", (UInt32)(nanos / (1000 * 1000)));
#endif

				takeTimeStamp (TRUE, &time);
			}
		}

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

#if DEBUGUSB
	if (0 != numBytesToCoalesce && numBytesLeft > 0) {
		debugIOLog ("!!!!Not enough data yet!!!!");
	} else {
		debugIOLog ("did %ld bytes", numBytesToCoalesce - numBytesLeft);
	}
#endif

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

	return;
}

// 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 * numUSBFrameListsToQueue) 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 (currentFrameList + 1) to (currentFrameList + numFramesListsToQueue) which is at most
// readUSBFrameListSize * numUSBFrameListsToQueue 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;

#if DEBUGLOG
	#if 0
	UInt32 lastSampleFrame = firstSampleFrame + numSampleFrames;
	UInt32 numSampleFramesPerBuffer, currentSampleFrame;
	numSampleFramesPerBuffer = getNumSampleFramesPerBuffer ();
	if (lastSampleFrame >= numSampleFramesPerBuffer) {
		lastSampleFrame -= numSampleFramesPerBuffer;
	}

	currentSampleFrame = getCurrentSampleFrame ();
	if (firstSampleFrame < lastSampleFrame) {
		if ((currentSampleFrame > firstSampleFrame) && (currentSampleFrame < lastSampleFrame)) {
			debugIOLog ("input error - samples not input yet! (%lx, %lx, curr = %lx)", firstSampleFrame, numSampleFrames, currentSampleFrame);
		}
	} else {
		if ((currentSampleFrame > firstSampleFrame) || (currentSampleFrame < lastSampleFrame)) {
			debugIOLog ("input error - samples not input yet! (%lx, %lx, curr = %lx)", firstSampleFrame, numSampleFrames, currentSampleFrame);
		}
	}
	#endif
#endif

#if 1	// enabled for [3091812]
	if (0 == shouldStop && TRUE != inCompletion) {
		curUSBFrameNumber = streamInterface->GetDevice()->GetBus()->GetFrameNumber ();
		framesLeftInQueue = usbFrameToQueueAt - curUSBFrameNumber;

		if (framesLeftInQueue < (numUSBFramesPerList * (numUSBFrameListsToQueue / 2)) / 2) {
			while (framesLeftInQueue < numUSBFramesPerList * (numUSBFrameListsToQueue - 1)) {
#if DEBUGLOADING
				debugIOLog ("queue a read from convertInputSamples: framesLeftInQueue = %ld", (UInt32)framesLeftInQueue);
#endif
				readHandler (this, usbCompletion[currentFrameList].parameter, kIOReturnSuccess, &theUSBIsocFrames[currentFrameList * numUSBFramesPerList]);

				curUSBFrameNumber = streamInterface->GetDevice()->GetBus()->GetFrameNumber ();
				framesLeftInQueue = usbFrameToQueueAt - 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 (bufferOffset + 1 > getSampleBufferSize ()) {
		windowStartByte = 0;
	} else {
		windowStartByte = bufferOffset + 1;
	}
	windowEndByte = windowStartByte + (numUSBFrameListsToQueue * readUSBFrameListSize);
	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, currentFrameList, bufferOffset, windowStartByte, windowEndByte);
		if (bufferOffset < lastSampleByte) {
			CoalesceInputSamples (lastSampleByte - bufferOffset, NULL, currentFrameList);
#if DEBUGLOADING
			debugIOLog ("Coalesce from convert %d bytes", lastSampleByte - bufferOffset);
#endif
		} else {
			// Have to wrap around the buffer.
			UInt32		numBytesToCoalesce = getSampleBufferSize () - bufferOffset + lastSampleByte;
			CoalesceInputSamples (numBytesToCoalesce, NULL, currentFrameList);
#if DEBUGLOADING
			debugIOLog ("Coalesce from convert %d bytes (wrapping)", numBytesToCoalesce);
#endif
		}
	}
#endif

    return convertFromAppleUSBAudioInputStream_NoWrap (sampleBuf, destBuf, firstSampleFrame, numSampleFrames, streamFormat);
}

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

	currentSampleFrame = 0;
	FailIf (NULL == mainStream, Exit);
	theFormat = mainStream->getFormat ();
	if (getDirection () == kIOAudioStreamDirectionOutput) {
		currentSampleFrame = safeToEraseTo;
	} else {
		currentSampleFrame = (bufferOffset == bufferSize ? 0 : bufferOffset);
	}
	currentSampleFrame /= (theFormat->fNumChannels * (theFormat->fBitWidth / 8));

Exit:
#if 0	// DEBUGLOG
	if (currentSampleFrame > getNumSampleFramesPerBuffer ()) {
		char							panicString[255];

		sprintf (panicString, "currentSampleFrame = %d, getNumSampleFramesPerBuffer () = %d, currentFrameList = %d, frameListSize = %d, bufferOffset = %d", currentSampleFrame, getNumSampleFramesPerBuffer (), currentFrameList, frameListSize, bufferOffset);
		panic (panicString);
	}
#endif
	return currentSampleFrame;
}

void AppleUSBAudioEngine::GetDeviceInfo (void) {
    OSObject						*obj;

    if (obj = streamInterface->getProperty (kUSBVendorName)) {
        obj->retain();
        interfaceVendor = obj;
    }

    if (obj = streamInterface->getProperty (kUSBProductName)) {
        obj->retain();
        interfaceProduct = obj;
    }

    if (obj = streamInterface->getProperty (kUSBDeviceReleaseNumber)) {
        obj->retain();
        deviceReleaseNumber = obj;
    }

    if (obj = streamInterface->getProperty (kUSBConfigurationValue)) {
        obj->retain();
        configurationValue = obj;
    }

    if (obj = streamInterface->getProperty (kUSBInterfaceNumber)) {
        obj->retain();
        interfaceNumber = obj;
    }
}

IOAudioStreamDirection AppleUSBAudioEngine::getDirection () {
	IOAudioStreamDirection		direction;

	direction = kIOAudioStreamDirectionOutput;
	FailIf (NULL == mainStream, Exit);
	direction = mainStream->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 = streamInterface->GetDevice()->GetManufacturerStringIndex ();
	if (0 != stringIndex) {
		err = streamInterface->GetDevice()->GetStringDescriptor (stringIndex, manufacturerString, kStringBufferSize);
	}

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

	err = kIOReturnSuccess;
	productString[0] = 0;
	stringIndex = streamInterface->GetDevice()->GetProductStringIndex ();
	if (0 != stringIndex) {
		err = streamInterface->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 = streamInterface->GetDevice()->GetSerialNumberStringIndex ();
	stringIndex = 0;
	if (0 != stringIndex) {
		err = streamInterface->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, streamInterface->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 ("device has a serial number = %s", serialNumberString);
		uniqueIDSize += strlen (serialNumberString);
	}

	interfaceNumber = streamInterface->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 ("getGlobalUniqueID = %s", uniqueIDStr);
		IOFree (uniqueIDStr, uniqueIDSize);
	}

	return uniqueID;
}

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

	sampleBuffer = NULL;
	FailIf  (NULL == mainStream, Exit);
	sampleBuffer = mainStream->getSampleBuffer ();

Exit:
	return sampleBuffer;
}

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

	bufferSize = 0;
	FailIf (NULL == mainStream, Exit);
	bufferSize = mainStream->getSampleBufferSize ();

Exit:
	return bufferSize;
}

//--------------------------------------------------------------------------------
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;
	terminatingDriver = FALSE;

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

	streamInterface = OSDynamicCast (IOUSBInterface, provider);
	FailIf (NULL == streamInterface, Exit);
	previousInterface = streamInterface;

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

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

	sampleRate.whole = kDefaultSamplingRate;
	sampleRate.fraction = 0;

	// We, as the driver, have to pick a default sample rate, size, and number of channels, so pick 16-bit stereo 44.1kHz
	alternateInterfaceID = usbAudio->FindAltInterfaceWithSettings (ourInterfaceNumber, kChannelDepth_STEREO, kBitDepth_16bits, sampleRate.whole);
	if (255 == alternateInterfaceID) {
		// Didn't have stereo, so let's hope it has mono
		alternateInterfaceID = usbAudio->FindAltInterfaceWithSettings (ourInterfaceNumber, kChannelDepth_MONO, kBitDepth_16bits, sampleRate.whole);
	}
	if (255 == alternateInterfaceID) {
		// They don't have a mono or stereo 16-bit 44.1kHz interface, so let's try for a stereo 16-bit interface with any sample rate
		alternateInterfaceID = usbAudio->FindAltInterfaceWithSettings (ourInterfaceNumber, kChannelDepth_STEREO, kBitDepth_16bits);
		sampleRate.whole = usbAudio->GetHighestSampleRate (ourInterfaceNumber, alternateInterfaceID);			// we'll run at the highest sample rate that the device has
	}
	if (255 == alternateInterfaceID) {
		// They don't have a stereo 16-bit interface, so let's try for a mono 16-bit interface with any sample rate
		alternateInterfaceID = usbAudio->FindAltInterfaceWithSettings (ourInterfaceNumber, kChannelDepth_MONO, kBitDepth_16bits);
		sampleRate.whole = usbAudio->GetHighestSampleRate (ourInterfaceNumber, alternateInterfaceID);			// we'll run at the highest sample rate that the device has
	}
	if (255 == alternateInterfaceID) {
		// 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.
		alternateInterfaceID = 1;
		sampleRate.whole = usbAudio->GetHighestSampleRate (ourInterfaceNumber, alternateInterfaceID);
	}
	debugIOLog ("default sample rate is %d", sampleRate.whole);
	FailIf (0 == sampleRate.whole, Exit);

	direction = usbAudio->GetIsocEndpointDirection (ourInterfaceNumber, alternateInterfaceID);
	mainStream->initWithAudioEngine (this, (IOAudioStreamDirection)direction, 1);
	if (kUSBIn == direction) {
		// look for a streaming output terminal that's connected to a non-streaming input terminal
		debugIOLog ("This is an input type endpoint (mic, etc.)");
		index = 0;
		do {
			terminalType = usbAudio->GetIndexedInputTerminalType (usbAudioDevice->controlInterface->GetInterfaceNumber (), 0, index++);
		} while (terminalType == INPUT_UNDEFINED && index < 256);

		numUSBFrameLists = RECORD_NUM_USB_FRAME_LISTS;
		numUSBFramesPerList = RECORD_NUM_USB_FRAMES_PER_LIST;
		numUSBFrameListsToQueue = RECORD_NUM_USB_FRAME_LISTS_TO_QUEUE;
	} else if (kUSBOut == direction) {
		debugIOLog ("This is an output type endpoint (speaker, etc.)");
		index = 0;
		do {
			terminalType = usbAudio->GetIndexedOutputTerminalType (usbAudioDevice->controlInterface->GetInterfaceNumber (), 0, index++);
		} while (terminalType == OUTPUT_UNDEFINED && index < 256);

		numUSBFrameLists = PLAY_NUM_USB_FRAME_LISTS;
		numUSBFramesPerList = PLAY_NUM_USB_FRAMES_PER_LIST;
		numUSBFrameListsToQueue = PLAY_NUM_USB_FRAME_LISTS_TO_QUEUE;
	} else {
		FailMessage ("Couldn't get the endpoint direction!", Exit);
	}
	mainStream->setTerminalType (terminalType);

	theUSBIsocFrames = (IOUSBLowLatencyIsocFrame *)IOMalloc (numUSBFrameLists * numUSBFramesPerList * sizeof (IOUSBLowLatencyIsocFrame));
	usbCompletion = (IOUSBLowLatencyIsocCompletion *)IOMalloc (numUSBFrameLists * sizeof (IOUSBLowLatencyIsocCompletion));
	soundBufferDescriptors = (IOSubMemoryDescriptor **)IOMalloc (numUSBFrameLists * sizeof (IOMemoryDescriptor *));
	bzero (soundBufferDescriptors, numUSBFrameLists * sizeof (IOMemoryDescriptor *));
	theWrapDescriptors[0] = OSTypeAlloc (IOSubMemoryDescriptor);
	theWrapDescriptors[1] = OSTypeAlloc (IOSubMemoryDescriptor);
	FailIf (NULL == theWrapDescriptors[0], Exit);
	FailIf (NULL == theWrapDescriptors[1], Exit);
	FailIf (NULL == theUSBIsocFrames, Exit);
	FailIf (NULL == usbCompletion, Exit);
	FailIf (NULL == soundBufferDescriptors, Exit);

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

	curSampleRate = sampleRate;
	setSampleRate (&sampleRate);

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

	switch (usbAudio->GetFormat (ourInterfaceNumber, alternateInterfaceID)) {
		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 support a format that we can deal with!!!!\n", Exit);
	}
	FailIf (FALSE == streamInterface->open (this), Exit);		// Have to open the interface because calling setFormat will call performFormatChange, which expects the interface to be open.
	resultCode = streamInterface->SetAlternateInterface (this, kRootAlternateSetting);		// Select the first alternate interface to init the hardware
	FailIf (kIOReturnSuccess != resultCode, Exit);
	resultCode = mainStream->setFormat (&streamFormat);
	FailIf (kIOReturnSuccess != resultCode, Exit);
	resultCode = addAudioStream (mainStream);
	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 (ourInterfaceNumber, alternateInterfaceID), Exit);
    FailIf (kUSBAudioStreamInterfaceSubclass != usbAudio->GetInterfaceSubClass (ourInterfaceNumber, alternateInterfaceID), Exit);

	GetDeviceInfo ();				// Figure out what this device is so if it is unplugged we can tell if it's replugged.

	if (kUSBOut == direction) {
		iSubBufferMemory = NULL;
		iSubEngine = NULL;

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

	startTimer = IOTimerEventSource::timerEventSource (this, waitForFirstUSBFrameCompletion);
	FailIf (NULL == startTimer, Exit);
	workLoop->addEventSource (startTimer);

    usbAudioDevice->activateAudioEngine (this, FALSE, ourInterfaceNumber, alternateInterfaceID);

    resultBool = TRUE;

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

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

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

Exit:
    debugIOLog("-%d = AppleUSBAudioEngine[%p]::initHardware(%p), resultCode = %x", resultBool, this, provider, resultCode);
    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->streamInterface->GetDevice()->GetVendorID (), usbAudioEngineObject->streamInterface->GetDevice()->GetProductID ());
		if (result == kIOReturnSuccess) {
			debugIOLog ("success initing the plugin");
			usbAudioEngineObject->mPlugin->pluginSetFormat (usbAudioEngineObject->mainStream->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 = usbAudioDevice->deviceRequest (request, usbAudioDevice, completion);
	}

	return result;
}

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);
    audioEngine = OSDynamicCast (AppleUSBAudioEngine, target);
	FailIf (NULL == audioEngine, Exit);

	if (newValue) {
		// Set up notifier to run when iSub shows up
		audioEngine->iSubEngineNotifier = addNotification (gIOPublishNotification, serviceMatching ("AppleiSubEngine"), (IOServiceNotificationHandler)&iSubEnginePublished, audioEngine);
		if (NULL != audioEngine->iSubBufferMemory) {
			// it looks like the notifier could be called before iSubEngineNotifier is set, 
			// so if it was called, then iSubBufferMemory would no longer be NULL and we can remove the notifier
			audioEngine->iSubEngineNotifier->remove ();
			audioEngine->iSubEngineNotifier = NULL;
		}
    } else {
		if (NULL != audioEngine->iSubBufferMemory && NULL != audioEngine->iSubEngine) {
			// 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->iSubEngineNotifier) {
			audioEngine->iSubEngineNotifier->remove ();
			audioEngine->iSubEngineNotifier = NULL;
		}
	}

Exit:
	debugIOLog("- AppleUSBAudioEngine::iSubAttachChangeHandler");
    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->iSubIsOpen && NULL != audioEngine->oldiSubEngine) {
		audioEngine->oldiSubEngine->closeiSub (audioEngine);
		debugIOLog ("iSub closed");
		audioEngine->detach (audioEngine->oldiSubEngine);
		audioEngine->iSubIsOpen = FALSE;
//		audioEngine->iSubEngine->release ();
		audioEngine->iSubEngine = NULL;
		audioEngine->iSubBufferMemory = NULL;
#if DEBUGISUB
	} else {
		if (TRUE != audioEngine->iSubIsOpen) {debugIOLog ("TRUE != audioEngine->iSubIsOpen");}
		if (NULL == audioEngine->oldiSubEngine) {debugIOLog ("NULL == audioEngine->oldiSubEngine");}
#endif
	}

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

	if (NULL != audioEngine->highFreqSamples) {
		IOFree (audioEngine->highFreqSamples, 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->iSubIsOpen) {
		usbAudioEngineObject->iSubEngine = (AppleiSubEngine *)newService;

		usbAudioEngineObject->iSubOpenThreadCall = thread_call_allocate ((thread_call_func_t)usbAudioEngineObject->iSubOpen, (thread_call_param_t)usbAudioEngineObject);

		if (NULL != usbAudioEngineObject->iSubOpenThreadCall) {
			thread_call_enter (usbAudioEngineObject->iSubOpenThreadCall);
		} else {
			// error - do something
		}
	} else {
		debugIOLog ("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->iSubOpenThreadCall) {
		thread_call_free(usbAudioEngineObject->iSubOpenThreadCall);
	}

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

IOReturn AppleUSBAudioEngine::iSubOpenAction (OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4) {
	AppleUSBAudioEngine *			usbAudioEngineObject;
	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->iSubEngine, Exit);

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

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

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

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

	FailWithAction (FALSE == resultBool, usbAudioEngineObject->detach (usbAudioEngineObject->iSubEngine), Cleanup);
//	FailWithAction (FALSE == resultBool, usbAudioEngineObject->iSubEngine->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->iSubEngineNotifier) {
		usbAudioEngineObject->iSubEngineNotifier->remove ();
		usbAudioEngineObject->iSubEngineNotifier = NULL;
	}

	if (kIOReturnSuccess == result) {
		debugIOLog ("successfully opened the iSub");
		usbAudioEngineObject->iSubBufferMemory = usbAudioEngineObject->iSubEngine->GetSampleBuffer ();
		usbAudioEngineObject->iSubIsOpen = TRUE;
	} else {
		// We didn't actually open the iSub
		usbAudioEngineObject->iSubBufferMemory = NULL;
		usbAudioEngineObject->iSubEngine = NULL;
		usbAudioEngineObject->iSubIsOpen = FALSE;

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

		if (NULL != usbAudioEngineObject->highFreqSamples) {
			IOFree (usbAudioEngineObject->highFreqSamples, 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) {
		// remeber 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->usbStreamRunning;
		usbAudioEngine->usbStreamRunning = FALSE;
		usbAudioEngine->pauseAudioEngine ();
		usbAudioEngine->beginConfigurationChange ();
		cg = usbAudioEngine->getCommandGate ();
		if (NULL != cg) {
			cg->runAction (iSubCloseAction);
		}
		usbAudioEngine->completeConfigurationChange ();
		usbAudioEngine->resumeAudioEngine ();
		usbAudioEngine->usbStreamRunning = streamWasRunning;
	}

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

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

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

	oldiSubEngine = iSubEngine;

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

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

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

IOReturn AppleUSBAudioEngine::performAudioEngineStart () {
    IOReturn			resultCode;

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

    resultCode = kIOReturnSuccess;

	if (mPlugin) {
		mPlugin->pluginStart ();
	}

    if (NULL != iSubEngine) {
		startiSub = TRUE;
		needToSync = TRUE;
    }

    if (!usbStreamRunning) {
        resultCode = startUSBStream ();
    }

	if (resultCode != kIOReturnSuccess) {
		debugIOLog ("++AppleUSBAudioEngine[%p]::performAudioEngineStart () -- NOT started, error = 0x%x", resultCode);
	} else {
		debugIOLog ("++AppleUSBAudioEngine[%p]::performAudioEngineStart () - started.", this);
	}

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

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

	if (mPlugin) {
		mPlugin->pluginStop ();
	}

    if (NULL != iSubEngine) {
		iSubEngine->StopiSub ();
		needToSync = TRUE;
    }

    if (usbStreamRunning) {
        stopUSBStream ();
    }

/* We can't do this here any more because the device depends on having our status already
 * changed and the _setStatus() call is now made after the stopAudioEngine() call
    if (notifyDeviceOfStop) {
        usbAudioDevice->streamStopped(this);
    }
*/
    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 mainStream->addAvailableFormat
IOReturn AppleUSBAudioEngine::performFormatChange (IOAudioStream *audioStream, const IOAudioStreamFormat *newFormat, const IOAudioSampleRate *newSampleRate) {
    USBAudioConfigObject *				usbAudio;
    IOReturn							result;
    void *								sampleBuffer;
	UInt32								i;
	UInt32								oldBufferSize;
	UInt32								numSamplesInBuffer;
	UInt16								averageFrameSamples;
	UInt16								averageFrameSize;
	UInt16								alternateFrameSize;
	UInt16								additionalSampleFrameFreq;
	UInt16								oldReadUSBFrameListSize;
	UInt8								ourInterfaceNumber;
	UInt8								newAlternateInterfaceID;
	UInt8								newDirection;
	bool								needToChangeChannels;

	debugIOLog ("+AppleUSBAudioEngine::performFormatChange");

	result = kIOReturnError;
	oldBufferSize = bufferSize;
	FailIf (NULL == streamInterface, Exit);
	FailIf (NULL == usbAudioDevice, Exit);

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

	if (newFormat == NULL) {
		result = kIOReturnSuccess;
		goto Exit;
	}
	ourInterfaceNumber = (UInt8)(newFormat->fDriverTag >> 16);
	newAlternateInterfaceID = (UInt8)(newFormat->fDriverTag);
	if (newFormat->fNumChannels != audioStream->format.fNumChannels) {
		needToChangeChannels = TRUE;
		debugIOLog ("Need to adjust channel controls, cur = %d, new = %d", audioStream->format.fNumChannels, newFormat->fNumChannels);
	} else {
		needToChangeChannels = FALSE;
		debugIOLog ("No need to adjust channel controls");
	}

	if (newSampleRate) {
		if (FALSE == usbAudio->VerifySampleRateIsSupported (ourInterfaceNumber, newAlternateInterfaceID, newSampleRate->whole)) {
			newAlternateInterfaceID = usbAudio->FindAltInterfaceWithSettings (ourInterfaceNumber, newFormat->fNumChannels, newFormat->fBitDepth, newSampleRate->whole);
			FailIf (255 == newAlternateInterfaceID, Exit);
		}
	}

	newDirection = usbAudio->GetIsocEndpointDirection (ourInterfaceNumber, newAlternateInterfaceID);
	FailIf (newDirection != direction, Exit);
	debugIOLog ("++about to set: ourInterfaceNumber = %d & newAlternateInterfaceID = %d", ourInterfaceNumber, newAlternateInterfaceID);
	alternateInterfaceID = newAlternateInterfaceID;

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

	if (newSampleRate) {
		debugIOLog ("changing sampling rate to %d", newSampleRate->whole);
		curSampleRate = *newSampleRate;
	} else {
		debugIOLog ("keeping the existing sampling rate of %d", curSampleRate.whole);
	}

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

	averageSampleRate = curSampleRate.whole;	// Set this as the default until we are told otherwise
	debugIOLog ("averageSampleRate = %d", averageSampleRate);
	CalculateSamplesPerFrame (curSampleRate.whole, &averageFrameSamples, &additionalSampleFrameFreq);
	debugIOLog ("averageFrameSamples = %d, additionalSampleFrameFreq = %d", averageFrameSamples, additionalSampleFrameFreq);

	oldReadUSBFrameListSize = readUSBFrameListSize;

	averageFrameSize = averageFrameSamples * (newFormat->fNumChannels * (newFormat->fBitWidth / 8));
	alternateFrameSize = (averageFrameSamples + 1) * (newFormat->fNumChannels * (newFormat->fBitWidth / 8));
	debugIOLog ("averageFrameSize = %d, alternateFrameSize = %d", averageFrameSize, alternateFrameSize);

	// You have to make the read buffer the size of the alternate frame size because we have to ask for alternateFrameSize 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 == direction) {
		readUSBFrameListSize = alternateFrameSize * numUSBFramesPerList;
	}

	numSamplesInBuffer = averageSampleRate / 4;
	numSamplesInBuffer += (PAGE_SIZE - 1);
	numSamplesInBuffer /= PAGE_SIZE;
	numSamplesInBuffer *= PAGE_SIZE;
	bufferSize = numSamplesInBuffer * newFormat->fNumChannels * (newFormat->fBitWidth / 8);

	// aml 7.16.02 iSub filters are set up for these sample rates:
	if (NULL != iSubBufferMemory) {
		if ((2 != newFormat->fNumChannels) || ((8000 != curSampleRate.whole) && (11025 != curSampleRate.whole) && (22050 != curSampleRate.whole) &&
			(44100 != curSampleRate.whole) && (48000 != curSampleRate.whole) && (96000 != curSampleRate.whole))) {

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

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

			// Close our connection to the iSub because we can't use it if we're running at a rate other than 44.1kHz
			iSubTeardownConnection ();
		}
	}

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

	if (kUSBIn == direction) {
		if (NULL != readBuffer) {
			mUSBBufferDescriptor->release ();
		}
		mUSBBufferDescriptor = IOBufferMemoryDescriptor::withOptions (kIODirectionInOut, numUSBFrameLists * readUSBFrameListSize, PAGE_SIZE);
		FailIf (NULL == mUSBBufferDescriptor, Exit);

		readBuffer = mUSBBufferDescriptor->getBytesNoCopy ();
		FailIf (NULL == readBuffer, Exit);

		for (i = 0; i < numUSBFrameLists; i++) {
			soundBufferDescriptors[i] = OSTypeAlloc (IOSubMemoryDescriptor);
			soundBufferDescriptors[i]->initSubRange (mUSBBufferDescriptor, i * readUSBFrameListSize, readUSBFrameListSize, kIODirectionInOut);
			FailIf (NULL == soundBufferDescriptors[i], Exit);
		}

		if (NULL != sampleBufferMemoryDescriptor) {
			mainStream->setSampleBuffer (NULL, 0);
			setNumSampleFramesPerBuffer (0);
			sampleBufferMemoryDescriptor->release ();
		}
		sampleBufferMemoryDescriptor = IOBufferMemoryDescriptor::withOptions (kIODirectionInOut, bufferSize, PAGE_SIZE);
		FailIf (NULL == sampleBufferMemoryDescriptor, Exit);
		sampleBuffer = sampleBufferMemoryDescriptor->getBytesNoCopy ();
	} else {
		if (NULL != iSubBufferMemory) {
			if (NULL != lowFreqSamples) {
				IOFree (lowFreqSamples, getSampleBufferSize () * 2);
			}

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

			// aml XXX iSub filters are set up for these sample rates:
			if ((2 == newFormat->fNumChannels) && ((8000 == curSampleRate.whole) || (11025 == curSampleRate.whole) || (22050 == curSampleRate.whole) ||
				(44100 == curSampleRate.whole) || (48000 == curSampleRate.whole) || (96000 == curSampleRate.whole))) {

				lowFreqSamples = (float *)IOMallocAligned (bufferSize * 2, PAGE_SIZE);
				FailIf (NULL == lowFreqSamples, Exit);
				highFreqSamples = (float *)IOMallocAligned (bufferSize * 2, PAGE_SIZE);
				FailIf (NULL == highFreqSamples, Exit);
			}
		}

		if (NULL != mUSBBufferDescriptor) {
			mainStream->setSampleBuffer (NULL, 0);
			setNumSampleFramesPerBuffer (0);
			mUSBBufferDescriptor->release ();
		}
		mUSBBufferDescriptor = IOBufferMemoryDescriptor::withOptions (kIODirectionInOut, bufferSize, PAGE_SIZE);
		FailIf (NULL == mUSBBufferDescriptor, Exit);

		for (i = 0; i < numUSBFrameLists; i++) {
			if (NULL != soundBufferDescriptors[i]) {
				soundBufferDescriptors[i]->release ();
			}
			soundBufferDescriptors[i] = OSTypeAlloc (IOSubMemoryDescriptor);
			soundBufferDescriptors[i]->initSubRange (mUSBBufferDescriptor, 0, bufferSize, kIODirectionInOut);
			FailIf (NULL == soundBufferDescriptors[i], Exit);

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

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

	if (kUSBIn == direction) {
		// Set the latency for input devices (microphones, etc.)
		setSampleOffset ((numUSBFramesPerList + 1) * (averageFrameSamples + 2));
//		setSampleOffset (5 * (averageFrameSamples + 2));
		// difference from wrap to timestamp
		setSampleLatency (averageFrameSamples * (kMinimumFrameOffset + 1));
	} else {
		// Set the latency for output devices (speakers, etc.)
		setSampleOffset (3 * (additionalSampleFrameFreq ? averageFrameSamples + 1 : averageFrameSamples));
		setSampleLatency (averageFrameSamples * kMinimumFrameOffset);
	}

	if (TRUE == needToChangeChannels) {
		beginConfigurationChange ();
		removeAllDefaultAudioControls ();
		usbAudioDevice->createControlsForInterface (this, ourInterfaceNumber, alternateInterfaceID);
		completeConfigurationChange ();
	}

	debugIOLog ("called setNumSampleFramesPerBuffer with %d", bufferSize / (newFormat->fNumChannels * (newFormat->fBitWidth / 8)));
	debugIOLog ("newFormat->fNumChannels = %d, newFormat->fBitWidth %d", newFormat->fNumChannels, newFormat->fBitWidth);
	debugIOLog ("called setSampleOffset with %d", numUSBFramesPerList);

	result = kIOReturnSuccess;

Exit:
	debugIOLog ("-AppleUSBAudioEngine::performFormatChange, result = %x", result);
    return result;
}

IOReturn AppleUSBAudioEngine::PrepareWriteFrameList (UInt32 arrayIndex) {
	const IOAudioStreamFormat *			theFormat;
	IOReturn							result;
	UInt32								thisFrameListSize;
	UInt32								thisFrameSize;
	UInt32								firstFrame;
	UInt32								numBytesToBufferEnd;
	UInt32								lastPreparedByte;
	UInt32								numUSBFramesPrepared;
	UInt16								integerSamplesInFrame;
	Boolean								haveWrapped;

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

	theFormat = mainStream->getFormat ();

	numBytesToBufferEnd = getSampleBufferSize () - previouslyPreparedBufferOffset;
	lastPreparedByte = previouslyPreparedBufferOffset;
	thisFrameListSize = 0;
	for (numUSBFramesPrepared = 0; numUSBFramesPrepared < numUSBFramesPerList; numUSBFramesPrepared++) {
		integerSamplesInFrame = averageSampleRate / 1000;
		fractionalSamplesRemaining += averageSampleRate - (integerSamplesInFrame * 1000);
		if (fractionalSamplesRemaining >= 1000) {
			integerSamplesInFrame++;
			fractionalSamplesRemaining -= 1000;
		}
		thisFrameSize = integerSamplesInFrame * (theFormat->fBitWidth / 8) * theFormat->fNumChannels;
		if (thisFrameSize >= numBytesToBufferEnd) {
			usbCompletion[arrayIndex].parameter = (void *)(((numUSBFramesPrepared + 1) << 16) | (thisFrameSize - numBytesToBufferEnd));
			theWrapDescriptors[0]->initSubRange (mUSBBufferDescriptor, previouslyPreparedBufferOffset, getSampleBufferSize () - previouslyPreparedBufferOffset, kIODirectionInOut);
			lastPreparedByte = thisFrameSize - numBytesToBufferEnd;
			numBytesToBufferEnd = getSampleBufferSize () - lastPreparedByte;
			haveWrapped = TRUE;
		} else {
			thisFrameListSize += thisFrameSize;
			lastPreparedByte += thisFrameSize;
			numBytesToBufferEnd -= thisFrameSize;
		}
		theUSBIsocFrames[firstFrame + numUSBFramesPrepared].frStatus = -1;
		theUSBIsocFrames[firstFrame + numUSBFramesPrepared].frActCount = 0;
		theUSBIsocFrames[firstFrame + numUSBFramesPrepared].frReqCount = thisFrameSize;
	}
	
	if (TRUE == haveWrapped) {
		needTimeStamps = TRUE;
		theWrapDescriptors[1]->initSubRange (mUSBBufferDescriptor, 0, lastPreparedByte, kIODirectionInOut);

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

		theWrapRangeDescriptor = IOMultiMemoryDescriptor::withDescriptors ((IOMemoryDescriptor **)theWrapDescriptors, 2, kIODirectionInOut, true);
	} else {
		soundBufferDescriptors[arrayIndex]->initSubRange (mUSBBufferDescriptor, previouslyPreparedBufferOffset, thisFrameListSize, kIODirectionInOut);
		FailIf (NULL == soundBufferDescriptors[arrayIndex], Exit);
	}

	safeToEraseTo = lastSafeErasePoint;
	lastSafeErasePoint = previouslyPreparedBufferOffset;
	previouslyPreparedBufferOffset = lastPreparedByte;
	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;

//	debugIOLog ("+AppleUSBAudioEngine::PrepareAndReadFrameLists (%d, %d, %ld)", sampleSize, numChannels, usbFrameListIndex);

	result = kIOReturnError;		// assume failure
	firstFrame = usbFrameListIndex * numUSBFramesPerList;
	usbCompletion[usbFrameListIndex].target = (void *)this;
	usbCompletion[usbFrameListIndex].action = readHandler;
	usbCompletion[usbFrameListIndex].parameter = (void *)usbFrameListIndex;	// what frame list index this buffer is

	CalculateSamplesPerFrame (curSampleRate.whole, &averageFrameSamples, &additionalSampleFrameFreq);
	bytesPerSampleFrame = sampleSize * numChannels;
	bytesToRead = (averageFrameSamples + 1) * bytesPerSampleFrame;
//	bytesToRead = thePipe->GetEndpoint()->maxPacketSize;

	for (numUSBFramesPrepared = 0; numUSBFramesPrepared < numUSBFramesPerList; numUSBFramesPrepared++) {
		theUSBIsocFrames[firstFrame + numUSBFramesPrepared].frStatus = -1;
		theUSBIsocFrames[firstFrame + numUSBFramesPrepared].frActCount = 0;
		theUSBIsocFrames[firstFrame + numUSBFramesPrepared].frReqCount = bytesToRead;
		theUSBIsocFrames[firstFrame + numUSBFramesPrepared].frTimeStamp.hi = 0;
		theUSBIsocFrames[firstFrame + numUSBFramesPrepared].frTimeStamp.lo = 0;
	}

	FailIf (NULL == thePipe, Exit);
	result = thePipe->Read (soundBufferDescriptors[usbFrameListIndex], usbFrameToQueueAt, numUSBFramesPerList, &theUSBIsocFrames[firstFrame], &usbCompletion[usbFrameListIndex], 1);	// Update timestamps every 1ms
	usbFrameToQueueAt += numUSBFramesPerList;

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

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

	theFormat = mainStream->getFormat ();

	result = PrepareAndReadFrameLists (theFormat->fBitWidth / 8, theFormat->fNumChannels, frameListNum);

    return result;
}

void AppleUSBAudioEngine::readHandler (void * object, void * parameter, IOReturn result, IOUSBLowLatencyIsocFrame * pFrames) {
	AppleUSBAudioEngine *			self;
	UInt64							currentUSBFrameNumber;
	UInt32							frameListToRead;
//	UInt32							i;

	self = (AppleUSBAudioEngine *)object;
	FailIf (TRUE == self->inCompletion, Exit);
	self->inCompletion = TRUE;

#if DEBUGUSB
	if (0 == self->shouldStop && (UInt32)parameter != self->currentFrameList) {
		debugIOLog ("%ld != %ld", (UInt32)parameter, self->currentFrameList);
	}
#endif

	FailIf (NULL == self->streamInterface, Exit);

	if (TRUE == self->startingEngine) {
		self->startingEngine = FALSE;	// The engine is fully started (it's no longer starting... it's running)
#if 0
		time = pFrames[0].frTimeStamp;
		nanoseconds_to_absolutetime (1000000, &timeOffset);
		SUB_ABSOLUTETIME (&time, &timeOffset);								// time -= timeOffset
		self->takeTimeStamp (FALSE, &time);
#endif
	}
	
#if 0 // logs differences in time between completion routine calls, to use add member variable mLastTime of type AbsoluteTime
	AbsoluteTime					t_now;
	AbsoluteTime					t_old;
	UInt64							nanos_del;
	t_old.lo = self->mLastTime.lo;
	t_old.hi = self->mLastTime.hi;
	clock_get_uptime (&t_now);
	self->mLastTime.lo = t_now.lo;
	self->mLastTime.hi = t_now.hi;
	SUB_ABSOLUTETIME (&t_now, &t_old);		// t_now -= t_old;
	absolutetime_to_nanoseconds (t_now, &nanos_del);
	nanos_del = nanos_del / (1000);
	if (nanos_del > 3000) {
		debugIOLog ("Read handler delta = %ld microseconds", (UInt32)(nanos_del));
	}
#endif	
	
#if 1 // enabled for [3091812]
	currentUSBFrameNumber = self->streamInterface->GetDevice()->GetBus()->GetFrameNumber ();
	if (0 == self->shouldStop && (SInt32)(self->usbFrameToQueueAt - currentUSBFrameNumber) > (SInt32)(self->numUSBFramesPerList * (self->numUSBFrameListsToQueue - 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->usbFrameToQueueAt - currentUSBFrameNumber));
#endif
		goto Exit;
	}
#endif

	if (kIOReturnSuccess != result && kIOReturnAborted != result && kIOReturnUnderrun != result) {
		#if DEBUGUSB
		debugIOLog ("Error 0x%X from USB read", result);
		#endif

		// skip ahead and see if that helps
		if (self->usbFrameToQueueAt <= currentUSBFrameNumber) {
			#if DEBUGUSB
			debugIOLog ("skipping ahead %ld frames", currentUSBFrameNumber - self->usbFrameToQueueAt);
			#endif
			self->usbFrameToQueueAt = currentUSBFrameNumber + kMinimumFrameOffset;
		}
	}

	self->CoalesceInputSamples (0, pFrames, (UInt32)parameter);
#if DEBUGUSB
	bzero ((UInt8 *)self->readBuffer + (self->currentFrameList * self->readUSBFrameListSize), self->readUSBFrameListSize);
//	for (i = 0; i < self->readUSBFrameListSize / sizeof (UInt16); i++) {
//		((UInt16 *)self->readBuffer)[i] = 0x7BBB;
//	}
#endif

	if (self->shouldStop > 0) {
		debugIOLog("++AppleUSBAudioEngine::readHandler() - stopping: %d", self->shouldStop);
		self->shouldStop++;
		if (self->shouldStop == (self->numUSBFrameListsToQueue + 1) && TRUE == self->terminatingDriver) {
			self->streamInterface->close (self);
			self->streamInterface = NULL;
		}
	} else {
		if (self->currentFrameList == self->numUSBFrameLists - 1) {
			self->currentFrameList = 0;
		} else {
			self->currentFrameList++;
		}

		frameListToRead = (self->currentFrameList - 1) + self->numUSBFrameListsToQueue;
		if (frameListToRead >= self->numUSBFrameLists) {
			frameListToRead -= self->numUSBFrameLists;
		}
		self->readFrameList (frameListToRead);
	}

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

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

	// aml 7.16.02, need to check for iSubEngine too [2999205]
    if ((NULL != iSubBufferMemory) && (NULL != iSubEngine)) {           	
		srcPhase = 1.0;		// aml 3.4.02
		srcState = 0.0;		// aml 3.4.02

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

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

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

		if (iSubBufferOffset < 0) {
			iSubBufferOffset += (iSubBufferMemory->getLength () / 2);
			iSubLoopCount--;			// aml 3.29.02 added to match AOA
		}
		previousClippedToFrame = clipSampleFrame;
		justResetClipPosition = TRUE;			// aml 3.29.02 added to match AOA
#if DEBUGISUB
		debugIOLog ("-resetClipPosition, iSubBufferOffset=%ld, previousClippedToFrame=%ld", iSubBufferOffset, previousClippedToFrame);
#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->iSubAttach->getIntValue ()) {
		usbAudioEngine->iSubEngineNotifier = 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.  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 alternate endpoint, which can be retrieved with the GetIsocAssociatedEndpointRefreshInt call (which
	is 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.

	I don't really like working with fixed point values, so this number is then converted to a normal integer
	representing the sampling rate (instead of the samples per millisecond), 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;
	UInt32							newSampleRate;

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

	if (kIOReturnSuccess != result) {
		newSampleRate = *(self->aveSampleRateBuf);
		sampleRate = USBToHostLong (newSampleRate);
		oldSampleRate = self->averageSampleRate;
		// turn the 10.14 value returned by the device into a normal UInt32
		sampleRate = sampleRate << 2;
		fixed = sampleRate >> 16;
		newSampleRate = fixed * 1000;
		fract = IOFixedMultiply (sampleRate & 0x0000FFFF, 1000 << 16);
		newSampleRate += (fract & 0xFFFF0000) >> 16;
		if (newSampleRate && newSampleRate != oldSampleRate) {
			// The device has changed the sample rate that it needs, let's roll with the new sample rate
			self->averageSampleRate = newSampleRate;
			debugIOLog ("!!!!sample rate changed, requestedFrameRate = %d", self->averageSampleRate);
		}
	}

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

		self->nextSynchReadFrame = self->nextSynchReadFrame + (1 << self->refreshInterval);
//		self->retain ();
		if (NULL != self->theAssociatedPipe) {
			result = self->theAssociatedPipe->Read (self->neededSampleRateDescriptor, self->nextSynchReadFrame, 1, &(self->theSampleRateFrame), &(self->sampleRateCompletion));
		}
	}

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

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

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

		debugIOLog ("Set freq control interface %d, alt interface %d, endpoint address 0x%X, sample rate (little endian) 0x%X", ourInterfaceNumber, alternateInterfaceID, devReq.wIndex, theSampleRate);
		result = streamInterface->GetDevice()->DeviceRequest (&devReq);
		FailIf (kIOReturnSuccess != result, Exit);
	} else {
		result = kIOReturnSuccess;
		debugIOLog ("Device doesn't have a sample freq control!");
	}

Exit:
	return result;
}

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

    debugIOLog ("+AppleUSBAudioEngine[%p]::startUSBStream ()", this);
    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

	currentFrameList = 0;
	safeToEraseTo = 0;
	lastSafeErasePoint = 0;
	bufferOffset = 0;
	startingEngine = TRUE;
	previouslyPreparedBufferOffset = 0;		// Start playing from the start of the buffer
	fractionalSamplesRemaining = 0;			// Reset our parital frame list info

    shouldStop = 0;

    FailIf ((numUSBFrameLists < numUSBFrameListsToQueue), Exit);

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

	resultCode = streamInterface->SetAlternateInterface (this, alternateInterfaceID);
	debugIOLog ("streamInterface->SetAlternateInterface (this, %d) = 0x%X", alternateInterfaceID, resultCode);
	FailIf (kIOReturnSuccess != resultCode, Exit);

	SetSampleRate (usbAudio, curSampleRate.whole);

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

	thePipe = streamInterface->FindNextPipe (NULL, &audioIsochEndpoint);
	FailIf (NULL == thePipe, Exit);
    thePipe->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 (curSampleRate.whole, &averageFrameSamples, &additionalSampleFrameFreq);
	theFormat = mainStream->getFormat ();
	if (kUSBIn == direction) {
		// 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));
		resultCode = thePipe->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));
		}
	} else {
		if (0 == additionalSampleFrameFreq) {
			maxFrameSize = averageFrameSamples * (theFormat->fNumChannels * (theFormat->fBitWidth / 8));
		} else {
			maxFrameSize = (averageFrameSamples + 1) * (theFormat->fNumChannels * (theFormat->fBitWidth / 8));
		}
	}

	debugIOLog ("calling SetPipePolicy (%d)", maxFrameSize);
	resultCode = thePipe->SetPipePolicy (maxFrameSize, 0);
	FailIf (kIOReturnSuccess != resultCode, Exit);

	usbFrameToQueueAt = streamInterface->GetDevice()->GetBus()->GetFrameNumber() + kMinimumFrameOffset;		// Can't start on the current frame, so start on the next available USB frame
	if (NULL != theAssociatedPipe) {
		nextSynchReadFrame = usbFrameToQueueAt;
		(void)theAssociatedPipe->Read (neededSampleRateDescriptor, nextSynchReadFrame, 1, &theSampleRateFrame, &sampleRateCompletion);
	}

	if (getDirection () == kIOAudioStreamDirectionInput) {
		clock_get_uptime (&uptime);
		nanoseconds_to_absolutetime (kMinimumFrameOffset * 1000 * 1000, &initialTimestampOffset);
		ADD_ABSOLUTETIME (&uptime, &initialTimestampOffset);
		takeTimeStamp (FALSE, &uptime);
		for (frameListNum = currentFrameList; frameListNum < numUSBFrameListsToQueue; frameListNum++) {
			readFrameList (frameListNum);
		}
	} else {
		theUSBIsocFrames[0].frTimeStamp.hi = 0xFFFFFFFF;
		theUSBIsocFrames[0].frTimeStamp.lo = 0xFFFFFFFF;
		for (frameListNum = currentFrameList; frameListNum < numUSBFrameListsToQueue; frameListNum++) {
			writeFrameList (frameListNum);
		}
		// Spawn a thread to look for the first frame completing so we can take our 0 time stamp
		startTimer->enable ();
		startTimer->setTimeoutMS (kMinimumFrameOffset);		// will call waitForFirstUSBFrameCompletion
	}

    usbStreamRunning = TRUE;
    resultCode = kIOReturnSuccess;

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

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

	if (NULL != streamInterface) {	// if we don't have an interface, message() got called and we are being terminated
		shouldStop = 1;
	}

	if (NULL != thePipe) {
		if (FALSE == terminatingDriver) {
			// Don't call USB if we are being terminated because we could deadlock their workloop.
			thePipe->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
		thePipe->release ();
		thePipe = NULL;
	}

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

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

	usbStreamRunning = FALSE;

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

void AppleUSBAudioEngine::waitForFirstUSBFrameCompletion (OSObject * owner, IOTimerEventSource * sender) {
	AppleUSBAudioEngine *				usbAudioEngineObject;
	AbsoluteTime						timeOffset;
	static UInt32						timeout = 60;

	usbAudioEngineObject = OSDynamicCast (AppleUSBAudioEngine, owner);
	FailIf (NULL == usbAudioEngineObject, Exit);
	FailIf (usbAudioEngineObject->isInactive (), Exit);
	FailIf (0 != usbAudioEngineObject->shouldStop, Exit);

	if (0 == timeout || usbAudioEngineObject->theUSBIsocFrames[0].frTimeStamp.lo != 0xFFFFFFFF) {
		nanoseconds_to_absolutetime (1 * 1000 * 1000, &timeOffset);
		SUB_ABSOLUTETIME (&usbAudioEngineObject->theUSBIsocFrames[0].frTimeStamp, &timeOffset);				// subtract 1ms since the time stamp was taken at the end of the transfer
		if (0 == timeout) {
			usbAudioEngineObject->takeTimeStamp (FALSE);
		} else {
		usbAudioEngineObject->takeTimeStamp (FALSE, &usbAudioEngineObject->theUSBIsocFrames[0].frTimeStamp);
		}
		timeout = 60;
		usbAudioEngineObject->startingEngine = FALSE;			// It's started now.
		usbAudioEngineObject->startTimer->disable ();
		debugIOLog ("audio engine starting playing now");
	} else {
		// Requeue timer to check for the first frame completing
		usbAudioEngineObject->startTimer->setTimeoutUS (50);		// will call us back in 50 micoseconds
		timeout--;
		debugIOLog ("audio not running, requeuing timer (%ld)", timeout);
	}

Exit:
	return;
}

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

	if (iSubEngine == (AppleiSubEngine *)provider) {
		debugIOLog ("iSub requesting termination");

		iSubTeardownConnection ();

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

	if (streamInterface == provider) {
		terminatingDriver = TRUE;
		if (FALSE == usbStreamRunning) {
			// Close our stream interface and go away because we're not running.
			debugIOLog ("willTerminate closing down");
			streamInterface->close (this);
			streamInterface = NULL;
		} else {
			// Have the write completion routine clean everything up because we are running.
			debugIOLog ("willTerminate checking shouldStop = 0x%x", shouldStop);
			if (0 == shouldStop) {
				shouldStop++;
			}
		}
		iSubCloseAction (this, NULL, NULL, NULL, NULL);
	}

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

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

IOReturn AppleUSBAudioEngine::writeFrameList (UInt32 frameListNum) {
    IOReturn							result;
	UInt32								arrayIndex;

	arrayIndex = frameListNum;

	result = PrepareWriteFrameList (arrayIndex);
	FailIf (kIOReturnSuccess != result, Exit);
	result = kIOReturnError;		// reset the error in case thePipe is null

	FailIf (NULL == thePipe, Exit);
	
	if (needTimeStamps) {
		result = thePipe->Write (theWrapRangeDescriptor, usbFrameToQueueAt, numUSBFramesPerList, &theUSBIsocFrames[arrayIndex * numUSBFramesPerList], &usbCompletion[arrayIndex], 1);
		needTimeStamps = FALSE;
	} else {
		if (TRUE == startingEngine) {
			result = thePipe->Write (soundBufferDescriptors[arrayIndex], usbFrameToQueueAt, numUSBFramesPerList, &theUSBIsocFrames[arrayIndex * numUSBFramesPerList], &usbCompletion[arrayIndex], 1);
		} else {
		result = thePipe->Write (soundBufferDescriptors[arrayIndex], usbFrameToQueueAt, numUSBFramesPerList, &theUSBIsocFrames[arrayIndex * numUSBFramesPerList], &usbCompletion[arrayIndex], 0);
		}
	}
	FailIf (result != kIOReturnSuccess, Exit);
	usbFrameToQueueAt += numUSBFramesPerList;

Exit:
	return result;
}

void AppleUSBAudioEngine::writeHandler (void * object, void * parameter, IOReturn result, IOUSBLowLatencyIsocFrame * pFrames) {
	AppleUSBAudioEngine *				self;
	AbsoluteTime						time;
	AbsoluteTime						timeOffset;
	UInt64								curUSBFrameNumber;
	UInt64								nanos;
	UInt32								frameListToWrite;
	UInt32								byteOffset;
#if DEBUGZEROTIME
	UInt32								ms;
	AbsoluteTime						diff;
#endif
#if DEBUGUSB
	UInt32								i;
#endif

	self = (AppleUSBAudioEngine *)object;
	FailIf (TRUE == self->inCompletion, Exit);
	self->inCompletion = TRUE;
	FailIf (NULL == self->streamInterface, Exit);

	curUSBFrameNumber = self->streamInterface->GetDevice()->GetBus()->GetFrameNumber ();
//	if (self->usbFrameToQueueAt - curUSBFrameNumber > (self->numUSBFramesPerList + 1)) {
	if ((SInt64)(self->usbFrameToQueueAt - curUSBFrameNumber) > (SInt32)(self->numUSBFramesPerList * (self->numUSBFrameListsToQueue / 2) + 1)) {
		// The frame list that this would have queued has already been queued by clipOutputSamples
#if DEBUGLOADING
		debugIOLog ("Not queuing a frame list in writeHandler");
		if (parameter) {
			debugIOLog ("parameter = 0x%x", parameter);
			debugIOLog ("0x%x%x - 0x%x%x = 0x%x", (UInt32)(self->usbFrameToQueueAt >> 32), (UInt32)(self->usbFrameToQueueAt), (UInt32)(curUSBFrameNumber >> 32), (UInt32)(curUSBFrameNumber), (UInt32)(self->usbFrameToQueueAt - curUSBFrameNumber));
	}
#endif
		goto Exit;
	}

    if (kIOReturnSuccess != result && kIOReturnAborted != result) {
#if DEBUGUSB
		debugIOLog ("++AppleUSBAudioEngine[%p]::writeHandler () - error 0x%x on USB frame 0x%lx%lx", self, result, (UInt32)(curUSBFrameNumber >> 32), (UInt32)curUSBFrameNumber);
		debugIOLog ("current frame list = %d", self->currentFrameList);
		for (i = 0; i < self->numUSBFramesPerList; i++) {
			if (kIOReturnSuccess != pFrames[i].frStatus) {
				debugIOLog ("fr %d: 0x%lx ", i, pFrames[i].frStatus);
			}
		}
		debugIOLog ("");
#endif
		// skip ahead and see if that helps
		if (self->usbFrameToQueueAt <= curUSBFrameNumber) {
			self->usbFrameToQueueAt = curUSBFrameNumber + kMinimumFrameOffset;
		}
    }

#if DEBUGUSB
	if (self->usbFrameToQueueAt < curUSBFrameNumber) {
		debugIOLog ("fell behind 0x%x%x < 0x%x%x", (UInt32)(self->usbFrameToQueueAt >> 32), (UInt32)(self->usbFrameToQueueAt), (UInt32)(curUSBFrameNumber >> 32), (UInt32)(curUSBFrameNumber));
		self->usbFrameToQueueAt = curUSBFrameNumber + kMinimumFrameOffset;
	}
#endif

	if (0 != parameter) {
#if DEBUGZEROTIME
//		debugIOLog ("%d:%d", (UInt32)parameter >> 16, (UInt32)parameter & 0x00FF);
#endif
		// Take a timestamp
		byteOffset = (UInt32)parameter & 0x00FF;
		// Need to figure out how long a USB frame is, so check with the one before or after this one, depending on if this is the last frame in the list or not
		time = pFrames[((UInt32)parameter >> 16) - 1].frTimeStamp;
//		if (((UInt32)parameter >> 16) == self->numUSBFramesPerList) {
//			debugIOLog ("Have to use previous time stamp");
//			tempTime = time;
//			timeOther = pFrames[((UInt32)parameter >> 16) - 2].frTimeStamp;
//			SUB_ABSOLUTETIME (&tempTime, &timeOther);
//			absolutetime_to_nanoseconds (tempTime, &nanos);
//		} else {
//			timeOther = pFrames[(UInt32)parameter >> 16].frTimeStamp;
//			SUB_ABSOLUTETIME (&timeOther, &time);
//			absolutetime_to_nanoseconds (timeOther, &nanos);
//		}
#if DEBUGZEROTIME
//		debugIOLog ("to = %lx %lx, n = %lx", timeOther.hi, timeOther.lo, (UInt32)nanos);
#endif
		// Calculate the fractional part of this sample frame after the 0 point
//		nanos = (byteOffset * nanos) / pFrames[((UInt32)parameter >> 16) - 1].frActCount;
		nanos = (byteOffset * 1000000) / pFrames[((UInt32)parameter >> 16) - 1].frActCount;
#if DEBUGZEROTIME
		debugIOLog ("so = %ld, n = %lx", byteOffset, (UInt32)nanos);
#endif
		nanoseconds_to_absolutetime (nanos, &timeOffset);
		SUB_ABSOLUTETIME (&time, &timeOffset);								// time -= timeOffset

#if DEBUGZEROTIME
		diff = time;
		SUB_ABSOLUTETIME (&diff, &self->status->fLastLoopTime);						// diff -= fLastLoopTime;
		absolutetime_to_nanoseconds (diff, &nanos);
		ms = nanos / (1000 * 1000);
		debugIOLog ("%ld", ms);
		if (ms > 280 || ms < 275) {
			debugIOLog ("%ldms, parameter=0x%x", ms, parameter);
		}
		if (ms < 277 || ms > 279) {
			if (((UInt32)parameter >> 16) == self->numUSBFramesPerList) {
				debugIOLog ("bad delta %lx %lx, %lx %lx", pFrames[(UInt32)parameter >> 16 - 2].frTimeStamp.hi, pFrames[(UInt32)parameter >> 16 - 2].frTimeStamp.lo, pFrames[(UInt32)parameter >> 16 - 1].frTimeStamp.hi, pFrames[(UInt32)parameter >> 16 - 1].frTimeStamp.lo);
			} else {
				debugIOLog ("bad delta %lx %lx, %lx %lx", pFrames[(UInt32)parameter >> 16 - 1].frTimeStamp.hi, pFrames[(UInt32)parameter >> 16 - 1].frTimeStamp.lo, pFrames[(UInt32)parameter >> 16].frTimeStamp.hi, pFrames[(UInt32)parameter >> 16].frTimeStamp.lo);
			}
		}
#endif
		self->takeTimeStamp (TRUE, &time);
	}

	if (self->currentFrameList == self->numUSBFrameLists - 1) {
		self->currentFrameList = 0;
	} else {
		self->currentFrameList++;
	}

	if (self->shouldStop > 0) {
		debugIOLog ("++AppleUSBAudioEngine::writeHandler() - stopping: %d", self->shouldStop);
		self->shouldStop++;
		if (self->shouldStop == (self->numUSBFrameListsToQueue + 1) && TRUE == self->terminatingDriver) {
			self->streamInterface->close (self);
			self->streamInterface = NULL;
		}
	} else {
		frameListToWrite = (self->currentFrameList - 1) + self->numUSBFrameListsToQueue;
		if (frameListToWrite >= self->numUSBFrameLists) {
			frameListToWrite -= self->numUSBFrameLists;
		}
		self->writeFrameList (frameListToWrite);
	}

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