//--------------------------------------------------------------------------------
//
//	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 CONTINUOUS_STREAMING	FALSE
#define DEBUGTIMESTAMPS			FALSE
#define DEBUGZEROTIME			FALSE
#define FIXEDSIZEPACKETS		TRUE
#define TIMERSTREAM				FALSE
#define DOUBLEPARENT			TRUE

#include "AppleUSBAudioCommon.h"
#include "AppleUSBAudioEngine.h"
#include "AppleUSBAudioDevice.h"
#include "AppleUSBAudioLevelControl.h"
#include "AppleUSBAudioMuteControl.h"
#include "AppleUSBAudioClip.h"
#include "AppleiSubEngine.h"

#include <IOKit/IOLib.h>
#include <IOKit/IOSyncer.h>
#include <IOKit/IOService.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOMemoryCursor.h>
#include <IOKit/IOMemoryDescriptor.h>
#include <IOKit/audio/IOAudioDevice.h>
#include <IOKit/audio/IOAudioPort.h>
#include <IOKit/audio/IOAudioTypes.h>
#include <IOKit/audio/IOAudioDefines.h>
#include <IOKit/audio/IOAudioLevelControl.h>
#include <libkern/OSByteOrder.h>
#include <libkern/c++/OSCollectionIterator.h>

#define super IOAudioEngine

OSDefineMetaClassAndStructors(AppleUSBAudioEngine, IOAudioEngine)

#pragma mark -IOKit Routines-

void AppleUSBAudioEngine::free () {
	UInt32			i;

	debug2IOLog ("+AppleUSBAudioEngine[%p]::free ()\n", 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;
	} */

	for (i = 0; i < numFrameLists; i++) {
		if (NULL != soundBuffer) {
			if (soundBuffer[i]) {
				soundBuffer[i]->release ();
				soundBuffer[i] = NULL;
			}
			IOFree (soundBuffer, numFrameLists * sizeof (IOMemoryDescriptor *));
			soundBuffer = NULL;
		}
	}

	if (NULL != usbCompletion) {
		IOFree (usbCompletion, numFrameLists * sizeof (IOUSBIsocCompletion));
		usbCompletion = NULL;
	}

	if (NULL != theFrames) {
		IOFree (theFrames, numFramesPerList * numFrameListsToQueue * sizeof (IOUSBIsocFrame));
		theFrames = NULL;
	}

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

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

	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 (readBuffer) {
		IOFree (readBuffer, readFrameListSize * numFrameLists);
		readBuffer = NULL;
	}

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

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

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

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

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

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

	debug2IOLog("+AppleUSBAudioEngine[%p]::init ()\n", 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;

Exit:
	debug2IOLog("-AppleUSBAudioEngine[%p]::init ()\n", this);
	return result;
}

#if TIMERSTREAM
bool AppleUSBAudioEngine::interfacePublished (AppleUSBAudioEngine *audioEngine, void *ref, IOService *newService) {
    IOUSBInterface *				interface;
    bool							result;
    IOCommandGate *					cg;

    debugIOLog ("+AppleUSBAudioEngine::interfacePublished ()\n");
    result = TRUE;

    if (interface = OSDynamicCast (IOUSBInterface, newService)) {
        cg = audioEngine->getCommandGate ();
        if (cg) {
            cg->runAction (reinitWithInterfaceAction, interface, (void *)&result);
        }
    }

    debugIOLog ("-AppleUSBAudioEngine::interfacePublished ()\n");
    return result;
}
#endif

IOReturn AppleUSBAudioEngine::message (UInt32 type, IOService * provider, void * arg) {
	debug4IOLog("+AppleUSBAudioEngine[%p]::message (0x%x, %p)\n", this, type, provider);

	switch (type) {
		case kIOMessageServiceIsTerminated:
			if (iSubEngine == (AppleiSubEngine *)provider) {
				debugIOLog ("iSub requesting termination\n");

				iSubTeardownConnection ();

				// Set up notifier to run when iSub shows up again
				iSubEngineNotifier = addNotification (gIOPublishNotification, serviceMatching ("AppleiSubEngine"), (IOServiceNotificationHandler)&iSubEnginePublished, this);
			}
		case kIOMessageServiceIsRequestingClose:
			if ((streamInterface != NULL) && (streamInterface == provider)) {
				shouldStop = 1;
				streamInterface->close (this);
				streamInterface = NULL;
				gotRequestClose = TRUE;
			}
			break;
		default:
			;
	}

	debug4IOLog("-AppleUSBAudioEngine[%p]::message (0x%x, %p)\n", this, type, provider);
	return kIOReturnSuccess;
}

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

	debug4IOLog ("+AppleUSBAudioEngine[%p]::requestTerminate (%p, %x)\n", 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
	}

	debug4IOLog ("-AppleUSBAudioEngine[%p]::requestTerminate (%p, %x)\n", this, provider, options);
	return result;
}
#endif

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

	debug3IOLog ("+AppleUSBAudioEngine[%p]::start (%p)\n", this, provider);

	resultCode = FALSE;

	//	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);
    
	debug4IOLog ("-%d = AppleUSBAudioEngine[%p]::start (%p)\n", resultCode, this, provider);

Exit:    
	return resultCode;
}
    
void AppleUSBAudioEngine::stop (IOService * provider) {
#if TIMERSTREAM
    bool 				performStop;
#endif

    debug3IOLog("+AppleUSBAudioEngine[%p]::stop (%p)\n", this, provider);

#if TIMERSTREAM
    if (!didStop) {

        performStop = isInactive ();

        if (!performStop) {
            // It's possible that terminate() won't get called before stop()
            // If that's the case, we need to see if we should stick around
            if (!timerStreamThread) {	// We didn't terminate and we don't have a timer thread - no terminate() call
                IOCommandGate *cg;

                cg = getCommandGate ();

                performStop = TRUE;

                if (cg) {
                    bool timerStreamStarted;
                    if (cg->runAction (startTimerStreamIfNecessaryAction, (void *)&timerStreamStarted) == kIOReturnSuccess) {
                        performStop = !timerStreamStarted;
                    }
                }
            }
        }

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

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

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

#if TIMERSTREAM
            if (isUnattachedStreamRegistered ()) {
                unregisterUnattachedStream ();
                stopTimerStream ();
            }
#endif

#if CONTINUOUS_STREAMING
            if (usbStreamRunning) {
                stopUSBStream ();
            }
#endif

#if TIMERSTREAM
            if (!didStop) {
                didStop = TRUE;
#endif
                super::stop (provider);
#if TIMERSTREAM
            }
        }
#endif

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

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

        if (streamInterface) {
            streamInterface->close (this);
            streamInterface = NULL;
			gotStopCall = TRUE;
        }

#if TIMERSTREAM
        if (!performStop && !isUnattachedStreamRegistered ()) {
            debug2IOLog ("++AppleUSBAudioEngine[%p]::stop () - keeping the stream around\n", this);
            registerUnattachedStream ();
        }
    }
#endif

    debug3IOLog ("-AppleUSBAudioEngine[%p]::stop (%p)\n", this, provider);
}

bool AppleUSBAudioEngine::terminate (IOOptionBits options = 0) {
#if TIMERSTREAM
	IOCommandGate *					cg;
#endif
	bool							shouldTerminate;
	bool							result;

	result = TRUE;
	shouldTerminate = TRUE;

	debug2IOLog ("+AppleUSBAudioEngine[%p]::terminate ()\n", this);

#if TIMERSTREAM
	cg = getCommandGate ();

	if (cg) {
		bool timerStreamStarted;
		if (cg->runAction (startTimerStreamIfNecessaryAction, (void *)&timerStreamStarted) == kIOReturnSuccess) {
			shouldTerminate = !timerStreamStarted;
		}
	}
#endif

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

	debug2IOLog ("-AppleUSBAudioEngine[%p]::terminate ()\n", 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;
    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);
	debug3IOLog ("There are %d alternate stream interfaces on interface %d\n", 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);

		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 ("AC-3 audio format type!\n");
				streamFormat.fSampleFormat = 'AC-3';
				streamFormat.fNumericRepresentation = kIOAudioStreamNumericRepresentationSignedInt;
				streamFormat.fIsMixable = FALSE;
				break;
			case IEC1937_AC3:
				debugIOLog ("IEC1937 AC-3 audio format type!\n");
				streamFormat.fSampleFormat = 'IAC3';
				streamFormat.fNumericRepresentation = kIOAudioStreamNumericRepresentationSignedInt;
				streamFormat.fIsMixable = FALSE;
				break;
			default:
				debug2IOLog ("interface format = %x\n", usbAudio->GetFormat (ourInterfaceNumber, altInterfaceIndx));
				debugIOLog ("interface doesn't support a format that we can deal with, so we're not making it available\n");
				continue;	// skip this alternate interface
		}

		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;

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

		if (numSampleRates) {
			for (rateIndx = 0; rateIndx < numSampleRates; rateIndx++) {
				debug2IOLog (" %d", sampleRates[rateIndx]);
				lowSampleRate.whole = sampleRates[rateIndx];
				lowSampleRate.fraction = 0;
				mainStream->addAvailableFormat (&streamFormat, &lowSampleRate, &lowSampleRate);
			}
			debugIOLog ("\n");
		} else if (sampleRates) {
			debug3IOLog (" %d to %d\n", sampleRates[0], sampleRates[1]);
			lowSampleRate.whole = sampleRates[0];
			lowSampleRate.fraction = 0;
			highSampleRate.whole = sampleRates[1];
			highSampleRate.fraction = 0;
			mainStream->addAvailableFormat (&streamFormat, &lowSampleRate, &highSampleRate);
		}
	}

	result = kIOReturnSuccess;

Exit:
	return result;
}

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

	debug4IOLog ("+AppleUSBAudioEngine::audioDevicePublished (%p, %p, %p)\n", 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 ()) {
		debug4IOLog ("++AppleUSBAudioEngine[%p]: found device (%p) for Audio Engine (%p)\n", audioEngine, audioDevice, audioEngine);
		audioEngine->usbAudioDevice = audioDevice;
		audioEngine->signal->signal (kIOReturnSuccess, FALSE);
		resultCode = TRUE;	// Success!
	}

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

UInt32 AppleUSBAudioEngine::CalculateNumSamplesPerFrameList (UInt32 sampleRate, UInt32 numFrames, UInt32 theNumFrameLists) {
	UInt32					numSamples;
	UInt32					i;
	UInt32					sampleNum;
	UInt32					sum;
	UInt16					samplesThisFrame;
	IOFixed					fixedSampleRate;

	numSamples = 0;
	fixedSampleRate = IOUFixedDivide (sampleRate << 16, 1000 << 16);		// we want a number like 44.101 to represent 44101Hz
	samplesThisFrame = fixedSampleRate >> 16;
	sampleNum = 2;
	sum = samplesThisFrame;

	for (i = 0; i < numFrames * theNumFrameLists; i++) {
		numSamples += samplesThisFrame;
		samplesThisFrame = (IOUFixedMultiply (fixedSampleRate, sampleNum << 16) >> 16) - sum;
		sum += samplesThisFrame;
		sampleNum++;
		if (1000 == sampleNum) {
			samplesThisFrame = fixedSampleRate >> 16;
			sampleNum = 2;
			sum = samplesThisFrame;
		}
	}

	return numSamples;
}

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) {
		debug2IOLog ("checking endpoint %d for an associated endpoint\n", address);
		assocEndpoint = usbAudio->GetIsocAssociatedEndpointAddress (ourInterfaceNumber, alternateInterfaceID, address);
		if (assocEndpoint != 0) {
			debugIOLog ("This endpoint has an associated synch endpoint!\n");
			refreshInterval = usbAudio->GetIsocAssociatedEndpointRefreshInt (ourInterfaceNumber, alternateInterfaceID, assocEndpoint);
			debug2IOLog ("The refresh interval is %d\n", refreshInterval);
			framesUntilRefresh = 1 << (10 - refreshInterval);		// the same as 2^(10-refreshInterval)
			// The hardware might not need to be updated as often as we were planning on (currently we queue 10 lists with 64ms of audio each).
			// If they don't need to be updated that often, then just keep everything at 64ms intervals to keep things standard.
			if (framesUntilRefresh < numFramesPerList)
				numFramesPerList = framesUntilRefresh;		// It needs to be updated more often, so run as the device requests.
			associatedEndpoint.type = kUSBIsoc;
			associatedEndpoint.direction = direction == kUSBOut ? kUSBIn : kUSBOut;		// The associated endpoint goes the opposite direction of the primary endpoint
			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);
	
			theAssociatedPipe->retain ();
		} else {
			debugIOLog ("Couldn't find the associated synch endpoint!\n");
		}
	} else {
		debugIOLog ("This endpoint does not have an associated synch endpoint\n");
	}

Exit:
	return result;
}

IOReturn AppleUSBAudioEngine::clipOutputSamples (const void *mixBuf,
                                                    void *sampleBuf,
                                                    UInt32 firstSampleFrame,
                                                    UInt32 numSampleFrames,
                                                    const IOAudioStreamFormat *streamFormat,
                                                    IOAudioStream *audioStream) {
	void *						iSubBuffer = NULL;
	UInt32						iSubBufferLen = 0;
	UInt32						sampleRate;
//	UInt32						iSubLead;
//	static UInt32				initialiSubLead = 0;
	SInt32						offsetDelta;
	IOReturn					result;

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

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

	if (TRUE == streamFormat->fIsMixable) {
		if (iSubBufferMemory) {
			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;

			if (FALSE == needToSync && previousClippedToFrame != firstSampleFrame && !(previousClippedToFrame == getNumSampleFramesPerBuffer () && firstSampleFrame == 0)) {
#if DEBUGLOG
				IOLog ("iSubBufferOffset was %ld\n", 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
					offsetDelta = (getNumSampleFramesPerBuffer () - previousClippedToFrame + firstSampleFrame) * streamFormat->fNumChannels;
				} else {
					offsetDelta = (firstSampleFrame - previousClippedToFrame) * streamFormat->fNumChannels;
				}
				iSubBufferOffset += offsetDelta;
#if DEBUGLOG
				IOLog ("clip to point was %ld, now %ld (delta = %ld)\n", previousClippedToFrame, firstSampleFrame, offsetDelta);
				IOLog ("iSubBufferOffset is now %ld\n", iSubBufferOffset);
#endif
				if (iSubBufferOffset > (SInt32)iSubBufferLen) {
					// Our calculated spot has actually wrapped around the iSub's buffer.
					iSubLoopCount += iSubBufferOffset / iSubBufferLen;
					iSubBufferOffset = iSubBufferOffset % iSubBufferLen;
#if DEBUGLOG
					IOLog ("iSubBufferOffset > iSubBufferLen, iSubBufferOffset is now %ld\n", iSubBufferOffset);
#endif
				} else if (iSubBufferOffset < 0) {
					iSubBufferOffset += iSubBufferLen;
#if DEBUGLOG
					IOLog ("iSubBufferOffset < 0, iSubBufferOffset is now %ld\n", iSubBufferOffset);
#endif
				}
			}
			previousClippedToFrame = firstSampleFrame + numSampleFrames;

			// Detect being out of sync with the iSub
			if (needToSync == FALSE && 0xFFFFFFFF != iSubEngine->GetCurrentLoopCount ()) {
			    if (iSubLoopCount > (iSubEngine->GetCurrentLoopCount () + 1) && !(iSubBufferOffset < (SInt32)(iSubEngine->GetCurrentByteCount () / 2))) {
#if DEBUGLOG
				IOLog ("****looped more than the iSub iSubLoopCount = %d, iSubEngine->GetCurrentLoopCount () = %d\n", iSubLoopCount, iSubEngine->GetCurrentLoopCount ());
#endif
				needToSync = TRUE;
				startiSub = TRUE;
			    } else if (iSubLoopCount < iSubEngine->GetCurrentLoopCount ()) {
#if DEBUGLOG
				IOLog ("****iSub is ahead of us iSubLoopCount = %d, iSubEngine->GetCurrentLoopCount () = %d\n", iSubLoopCount, iSubEngine->GetCurrentLoopCount ());
#endif
				needToSync = TRUE;
				startiSub = TRUE;
			    } else if (iSubLoopCount == iSubEngine->GetCurrentLoopCount () && iSubBufferOffset < (SInt32)(iSubEngine->GetCurrentByteCount () / 2)) {
#if DEBUGLOG
				IOLog ("****iSub is in front of our write head iSubBufferOffset = %d, (iSubEngine->GetCurrentByteCount () / 2) = %d\n", iSubBufferOffset, iSubEngine->GetCurrentByteCount () / 2);
#endif
				needToSync = TRUE;
				startiSub = TRUE;
			    }
			}

			// sync up with iSub
			if (TRUE == needToSync) {
				needToSync = FALSE;
				// 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;
				iSubLoopCount = iSubEngine->GetCurrentLoopCount ();
				iSubBufferOffset = (firstSampleFrame - getCurrentSampleFrame ()) * streamFormat->fNumChannels;
#if DEBUGLOG
				IOLog ("starting iSubBufferOffset = %ld, iSubLoopCount = %ld\n", iSubBufferOffset, iSubLoopCount);
#endif
				iSubBufferOffset -= 88 * 5;
			}

			if (iSubBufferOffset > (SInt32)iSubBufferLen) {
				// Our calculated spot has actually wrapped around the iSub's buffer.
				iSubLoopCount += iSubBufferOffset / iSubBufferLen;
				iSubBufferOffset = iSubBufferOffset % iSubBufferLen;
#if DEBUGLOG
				IOLog ("iSubBufferOffset > iSubBufferLen, iSubBufferOffset is now %ld\n", iSubBufferOffset);
#endif
			} else if (iSubBufferOffset < 0) {
				iSubBufferOffset += iSubBufferLen;
#if DEBUGLOG
				IOLog ("iSubBufferOffset < 0, iSubBufferOffset is now %ld\n", iSubBufferOffset);
#endif
			}
// aml 2.21.02 added extra filter states for 4th order with phase compensation
// iSub crossover
			result = clipAppleUSBAudioToOutputStreamiSub (mixBuf, sampleBuf, &filterState, &filterState2, &phaseCompState, lowFreqSamples, highFreqSamples, firstSampleFrame, numSampleFrames, sampleRate, streamFormat, (SInt16*)iSubBuffer, &iSubLoopCount, &iSubBufferOffset, iSubBufferLen);
			if (TRUE == startiSub) {
				iSubEngine->StartiSub ();
				startiSub = FALSE;
				iSubLoopCount = 0;
			}
		} else {
			result = clipAppleUSBAudioToOutputStream (mixBuf, sampleBuf, firstSampleFrame, numSampleFrames, streamFormat);
		}
	} else {
		memcpy (sampleBuf, (UInt8 *)mixBuf + (firstSampleFrame * streamFormat->fNumChannels), numSampleFrames * streamFormat->fNumChannels * (streamFormat->fBitWidth / 8));
		result = kIOReturnSuccess;
	}

	return result;
}

IOReturn AppleUSBAudioEngine::convertInputSamples (const void *sampleBuf,
                                                                void *destBuf,
                                                                UInt32 firstSampleFrame,
                                                                UInt32 numSampleFrames,
                                                                const IOAudioStreamFormat *streamFormat,
                                                                IOAudioStream *audioStream) {
#if 0
	#if DEBUGLOG
	UInt32 lastSampleFrame = firstSampleFrame + numSampleFrames;
	UInt32 numSampleFramesPerBuffer, currentSampleFrame;
	numSampleFramesPerBuffer = getNumSampleFramesPerBuffer();
	if (lastSampleFrame >= numSampleFramesPerBuffer) {
		lastSampleFrame -= numSampleFramesPerBuffer;
	}

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

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

UInt32 AppleUSBAudioEngine::getCurrentSampleFrame () {
	const IOAudioStreamFormat			*theFormat;
	UInt32								currentSampleFrame;
	UInt32								i;
	UInt32								offset;
	UInt32								thisFrameListSize;
	UInt16								averageFrameSamples;
	UInt16								additionalSampleFrameFreq;
#if 0
	AbsoluteTime						uptime;
	static AbsoluteTime					lastUptime = {0, 0};
	AbsoluteTime						delta;
	UInt64								nanos;
#endif

	currentSampleFrame = 0;
	FailIf (NULL == mainStream, Exit);
	theFormat = mainStream->getFormat ();
	if (getDirection () == kIOAudioStreamDirectionOutput) {
		CalculateSamplesPerFrame (curSampleRate.whole, &averageFrameSamples, &additionalSampleFrameFreq);
		if (additionalSampleFrameFreq) {
			offset = 0;
			// This will have to be changed if numFramesPerList is ever larger than additionalSampleFrameFreq
			if (additionalSampleFrameFreq == numFramesPerList) {
				thisFrameListSize = frameListSize;
			} else {
				thisFrameListSize = frameListSize - ((theFormat->fBitWidth / 8) * theFormat->fNumChannels);
			}

			for (i = 0; i < currentFrameList; i++) {
				offset += thisFrameListSize;
				if (i % (additionalSampleFrameFreq / numFramesPerList) == 0) {
					thisFrameListSize = frameListSize;
				} else {
					thisFrameListSize = frameListSize - ((theFormat->fBitWidth / 8) * theFormat->fNumChannels);
				}
			}
			currentSampleFrame = offset;
		} else {
			currentSampleFrame = currentFrameList * frameListSize;
		}
	} else {
		currentSampleFrame = bufferOffset == bufferSize ? 0 : bufferOffset;
	}
	currentSampleFrame /= (theFormat->fNumChannels * (theFormat->fBitWidth / 8));
#if 0
	clock_get_uptime (&uptime);
	delta = uptime;
	SUB_ABSOLUTETIME (&delta, &lastUptime);
	absolutetime_to_nanoseconds (delta, &nanos);
	debug3IOLog ("getCurrentSampleFrame = %ld, delta = %ld\n", currentSampleFrame, (UInt32)(nanos / (1000 * 1000)));
	lastUptime = uptime;
#endif

Exit:
#if 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 ();
	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 = (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
		debug2IOLog ("device has a serial number = %s\n", 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);
		debug2IOLog ("getGlobalUniqueID = %s\n", 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) {
    USBAudioConfigObject *				usbAudio;
    IOAudioStreamFormat					streamFormat;
	Boolean								resultCode;

    debug3IOLog ("+AppleUSBAudioEngine[%p]::initHardware (%p)\n", this, provider);

    resultCode = FALSE;

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

	streamInterface = OSDynamicCast (IOUSBInterface, provider);
	FailIf (NULL == streamInterface, Exit);
	previousInterface = streamInterface;
	numFrameLists = NUM_FRAME_LISTS;
	numFramesPerList = NUM_FRAMES_PER_LIST;
	numFrameListsToQueue = NUM_FRAME_LISTS_TO_QUEUE;

	// 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.
	ourInterfaceNumber = streamInterface->GetInterfaceNumber ();
	debug2IOLog ("ourInterfaceNumber = %d\n", ourInterfaceNumber);

    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);
	}
	debug2IOLog ("default sample rate is %d\n", 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.)\n");
	} else if (kUSBOut == direction) {
		debugIOLog ("This is an output type endpoint (speaker, etc.)\n");
	} else {
		FailMessage ("Couldn't get the endpoint direction!\n", Exit);
	}

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

	curSampleRate = sampleRate;
	setSampleRate (&sampleRate);

	// Tell the IOAudioFamily what format we are going to be running in.
	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 = 'AC-3';
			streamFormat.fNumericRepresentation = kIOAudioStreamNumericRepresentationSignedInt;
			streamFormat.fIsMixable = FALSE;
			break;
		case IEC1937_AC3:
			streamFormat.fSampleFormat = 'IAC3';
			streamFormat.fNumericRepresentation = kIOAudioStreamNumericRepresentationSignedInt;
			streamFormat.fIsMixable = FALSE;
		default:
			FailMessage ("!!!!interface doesn't support a format that we can deal with!!!!\n", Exit);
	}
	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;

	FailIf (FALSE == streamInterface->open (this), Exit);		// Have to open the interface because calling setFormat will call performFormatChange, which expects the interface to be open.
	FailIf (kIOReturnSuccess != mainStream->setFormat (&streamFormat), Exit);
	FailIf (kIOReturnSuccess != addAudioStream (mainStream), 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.

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

#if CONTINUOUS_STREAMING
    if (!usbStreamRunning) {
		resetStatusBuffer ();
        resultCode = (startUSBStream () == kIOReturnSuccess);
    } else {
        resultCode = TRUE;
    }
#else
    resultCode = TRUE;
#endif

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

	if (kUSBOut == direction) {
		// Set up notifier to run when iSub shows up
		iSubBufferMemory = NULL;
		iSubEngineNotifier = addNotification (gIOPublishNotification, serviceMatching ("AppleiSubEngine"), (IOServiceNotificationHandler)&iSubEnginePublished, this);
		if (NULL != 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
			iSubEngineNotifier->remove ();
			iSubEngineNotifier = NULL;
		}
	}

Exit:
    debug4IOLog("-%d = AppleUSBAudioEngine[%p]::initHardware(%p)\n", resultCode, this, provider);
    return resultCode;
}

// 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) {
    if (NULL != arg1) {
        AppleUSBAudioEngine *audioEngine = OSDynamicCast (AppleUSBAudioEngine, owner);

        if (NULL != audioEngine) {
			// To avoid a possible race with the clip routine being called at the same time we are disposing of the
			// buffers it is using, we will pause the engine and then restart it around disposing of these buffers.
			audioEngine->pauseAudioEngine ();

			((AppleiSubEngine *)arg1)->close (audioEngine);

			audioEngine->iSubEngine = NULL;
			audioEngine->iSubBufferMemory = NULL;

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

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

			audioEngine->resumeAudioEngine ();
        }
    }

	return kIOReturnSuccess;
}

bool AppleUSBAudioEngine::iSubEnginePublished (AppleUSBAudioEngine * usbAudioEngineObject, void * refCon, IOService * newService) {
	IOReturn						result;
	bool							resultCode;
	OSCollectionIterator *			collectionIterator;
	AppleUSBAudioMuteControl *		ourMuteControl;
	AppleUSBAudioLevelControl *		masterVolumeControl;
	AppleUSBAudioLevelControl *		volumeControl;
	AppleUSBAudioLevelControl *		leftVolumeControl;
	AppleUSBAudioLevelControl *		rightVolumeControl;
    IOCommandGate *					cg;
	UInt32							i;
	UInt32							numControls;

	debug4IOLog ("+AppleUSBAudioEngine::iSubEnginePublished (%p, %p, %p)\n", usbAudioEngineObject, (UInt32*)refCon, newService);

	resultCode = false;

	FailIf (NULL == usbAudioEngineObject, Exit);
	FailIf (NULL == newService, Exit);
	// We only run the iSub if we're currently running at a sampling rate of 44.1kHz
	FailIf (44100 != usbAudioEngineObject->curSampleRate.whole, Exit);

	usbAudioEngineObject->iSubEngine = (AppleiSubEngine *)newService;
	FailIf (NULL == usbAudioEngineObject->iSubEngine, Exit);

	// Set the initial volume of the iSub by finding our initial volume level
	debugIOLog ("Looking for our volume controls to set the initial iSub volume\n");
	collectionIterator = OSCollectionIterator::withCollection (usbAudioEngineObject->defaultAudioControls);
	FailIf (NULL == collectionIterator, Exit);

	i = 0;
	numControls = usbAudioEngineObject->defaultAudioControls->getCount ();
	volumeControl = NULL;
	masterVolumeControl = NULL;
	leftVolumeControl = NULL;
	rightVolumeControl = NULL;
	while (i <  numControls) {
		volumeControl = OSDynamicCast (AppleUSBAudioLevelControl, collectionIterator->getNextObject ());
		if (NULL != volumeControl && volumeControl->getUsage () == kIOAudioControlUsageOutput) {
			if (volumeControl->getChannelID () == 1) {
				leftVolumeControl = volumeControl;
				debugIOLog ("Got our left volume control\n");
			} else if (volumeControl->getChannelID () == 2) {
				rightVolumeControl = volumeControl;
				debugIOLog ("Got our right volume control\n");
			} else if (volumeControl->getChannelID () == 0) {
				masterVolumeControl = volumeControl;
				debugIOLog ("Got our master volume control\n");
			}
		}
		i++;
	}
	collectionIterator->release ();

	// Get the initial mute state of our control so that we can set the iSub's mute state
	debugIOLog ("Looking for our mute control to set the initial iSub mute\n");
	collectionIterator = OSCollectionIterator::withCollection (usbAudioEngineObject->defaultAudioControls);
	i = 0;
	ourMuteControl = NULL;
	while (i <  numControls && NULL == ourMuteControl) {
		ourMuteControl = OSDynamicCast (AppleUSBAudioMuteControl, collectionIterator->getNextObject ());
		i++;
	}
	collectionIterator->release ();

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

	// Open the iSub which will cause it to create mute and volume controls
	usbAudioEngineObject->attach (usbAudioEngineObject->iSubEngine);
	cg = usbAudioEngineObject->getCommandGate ();
	FailIf (NULL == cg, Exit);
	result = cg->runAction (iSubOpenAction, usbAudioEngineObject);
	FailIf (kIOReturnSuccess != result, Exit);
	usbAudioEngineObject->iSubBufferMemory = usbAudioEngineObject->iSubEngine->GetSampleBuffer ();

	// Set the volume and mute state of the iSub
	// Since the iSub takes its volume from our volume, just set our volume and the iSub will pick it up.
	if (NULL != leftVolumeControl && NULL != rightVolumeControl) {
		debug3IOLog ("setting initial iSub volumes to L:%ld R:%ld\n", leftVolumeControl->getIntValue (), rightVolumeControl->getIntValue ());
		leftVolumeControl->flushValue ();
		rightVolumeControl->flushValue ();
	} else if (NULL != masterVolumeControl) {
		debug2IOLog ("setting initial iSub volume using master control to %ld\n", masterVolumeControl->getIntValue ());
		masterVolumeControl->flushValue ();
	}

	if (NULL != ourMuteControl) {
		debug2IOLog ("setting initial iSub mute state to %ld\n", ourMuteControl->getIntValue ());
		ourMuteControl->flushValue ();
	}

	resultCode = true;

Exit:
	// 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;
	}

	debug5IOLog ("- %d = AppleUSBAudioEngine::iSubEnginePublished (%p, %p, %p)\n", resultCode, usbAudioEngineObject, (UInt32 *)refCon, newService);
	return resultCode;
}

IOReturn AppleUSBAudioEngine::iSubOpenAction (OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4) {
	IOReturn				result;
	bool					resultBool;

	result = kIOReturnError;
	resultBool = FALSE;

    if (NULL != owner && NULL != arg1) {
        AppleUSBAudioEngine *audioEngine = OSDynamicCast (AppleUSBAudioEngine, owner);

        if (audioEngine) {
            resultBool = audioEngine->iSubEngine->open ((IOService *)arg1);
        }
    }

	if (TRUE == resultBool) {
		result = kIOReturnSuccess;
	}

	return result;
}

void AppleUSBAudioEngine::iSubTeardown (AppleUSBAudioEngine *usbAudioEngine, thread_call_t iSubTeardownThreadCall) {
	IOCommandGate *						cg;

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

	if (NULL != usbAudioEngine) {
		cg = usbAudioEngine->getCommandGate ();
		if (NULL != cg) {
			cg->runAction (iSubCloseAction, usbAudioEngine->oldiSubEngine);
		}
		usbAudioEngine->detach (usbAudioEngine->oldiSubEngine);
	}

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

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

void AppleUSBAudioEngine::iSubTeardownConnection (void) {
	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
	}

	return;
}

#if TIMERSTREAM
bool AppleUSBAudioEngine::isUnattachedStreamRegistered () {
    debugIOLog ("+AppleUSBAudioEngine::isUnattachedStreamRegistered ()\n");
    debugIOLog ("-AppleUSBAudioEngine::isUnattachedStreamRegistered ()\n");

    return (streamNotifier != NULL);
}
#endif

IOReturn AppleUSBAudioEngine::performAudioEngineStart () {
    IOReturn			resultCode;

    debug2IOLog ("+AppleUSBAudioEngine[%p]::performAudioEngineStart ()\n", this);

    resultCode = kIOReturnSuccess;

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

#if !CONTINUOUS_STREAMING
    if (!usbStreamRunning) {
        resultCode = startUSBStream ();
    }
#endif

	if (resultCode != kIOReturnSuccess) {
		debug2IOLog ("++AppleUSBAudioEngine[%p]::performAudioEngineStart () -- NOT started, error = %d", resultCode);
	} else {
		debug2IOLog ("++AppleUSBAudioEngine[%p]::performAudioEngineStart () - started.\n", this);
	}

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

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

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

#if !CONTINUOUS_STREAMING
    if (usbStreamRunning) {
        stopUSBStream();
    }
#endif

/* 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);
    }
*/
    debug2IOLog("++AppleUSBAudioEngine[%p]::performAudioEngineStop() - stopped\n", this);

    debug2IOLog("-AppleUSBAudioEngine[%p]::performAudioEngineStop()\n", 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 								totalFrames;
	UInt32								numAlternateFrames;
	UInt32								numAverageFrames;
	UInt16								averageFrameSamples;
	UInt16								averageFrameSize;
	UInt16								alternateFrameSize;
	UInt16								additionalSampleFrameFreq;
	UInt16								oldNumFrameLists;
	UInt16								oldNumFramesPerList;
	UInt16								oldReadFrameListSize;
	UInt8								ourInterfaceNumber;
	UInt8								newAlternateInterfaceID;
	UInt8								newDirection;

	debugIOLog ("+AppleUSBAudioEngine::performFormatChange\n");

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

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

	FailIf (NULL == newFormat, Exit);
	ourInterfaceNumber = (UInt8)(newFormat->fDriverTag >> 16);
	newAlternateInterfaceID = (UInt8)(newFormat->fDriverTag);

	if (newSampleRate) {
		FailWithAction (FALSE == usbAudio->VerifySampleRateIsSupported (ourInterfaceNumber, newAlternateInterfaceID, newSampleRate->whole), result = kIOReturnError, Exit);
	}

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

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

	// Set the sampling rate on the device
	result = SetSampleRate (usbAudio, curSampleRate.whole);
	FailIf (kIOReturnSuccess != result, Exit);
	result = kIOReturnError;						// reset error in case something goes wrong

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

	oldNumFrameLists = numFrameLists;
	oldNumFramesPerList = numFramesPerList;
	oldReadFrameListSize = readFrameListSize;

	if (additionalSampleFrameFreq) {
		numFramesPerList = additionalSampleFrameFreq;
	} else {
		numFramesPerList = NUM_FRAMES_PER_LIST;
	}

	if (additionalSampleFrameFreq) {
		while (numFrameLists % additionalSampleFrameFreq != 0) {
			// We need more frame lists so that the additional samples always fit into the buffer
			numFrameLists++;
		}
		debug2IOLog ("numFramelists = %d\n", numFrameLists);

		while (additionalSampleFrameFreq % numFramesPerList != 0) {
			// We need to make sure that the frame list that has the additional sample does not shift
			numFramesPerList++;
		}
		debug2IOLog ("numFramesPerList = %d\n", numFramesPerList);
	}

	averageFrameSize = averageFrameSamples * (newFormat->fNumChannels * (newFormat->fBitWidth / 8));
	alternateFrameSize = (averageFrameSamples + 1) * (newFormat->fNumChannels * (newFormat->fBitWidth / 8));
	debug3IOLog ("averageFrameSize = %d, alternateFrameSize = %d\n", averageFrameSize, alternateFrameSize);
	if (additionalSampleFrameFreq) {
		if (kUSBIn == direction) {
			// 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...
			readFrameListSize = alternateFrameSize * numFramesPerList;
			debug2IOLog ("readFrameListSize = %d\n", readFrameListSize);
		}
		// one second of audio is packetized into packets of size n and n+1 (maybe n-1, n, and n+1, but so far we haven't needed to do that)
		// n*p + (n+1)q = sampleRate
		// p + q = 1000, where p is number of small packets (average size) and q is number of large packets (alternate size)

		// **** Can't calculate frameListSize like this because it doesn't work if the numFramesPerList doesn't contain exactly one additional sample frame ****

		// frameListSize = ((averageFrameSize * (numFramesPerList / additionalSampleFrameFreq * (additionalSampleFrameFreq - 1))) + (alternateFrameSize * (numFramesPerList / additionalSampleFrameFreq)));
		// frameListSize = ((numFramesPerList - 1) * (averageFrameSamples * newFormat->fNumChannels * (newFormat->fBitWidth / 8))) + ((averageFrameSamples + 1) * newFormat->fNumChannels * (newFormat->fBitWidth / 8));
		debug3IOLog ("newFormat->fNumChannels = %d, averageFrameSize = %d\n", newFormat->fNumChannels, averageFrameSize);
		
		totalFrames = numFrameLists * numFramesPerList;

		numAlternateFrames = totalFrames / additionalSampleFrameFreq;
		numAverageFrames = totalFrames - numAlternateFrames;

		bufferSize = (averageFrameSize * numAverageFrames) + (alternateFrameSize * numAlternateFrames);
		frameListSize = bufferSize / numFrameLists;
		// bufferSize = CalculateNumSamplesPerFrameList (curSampleRate.whole, numFramesPerList, numFrameLists) * (newFormat->fNumChannels * (newFormat->fBitWidth / 8));
		// frameListSize = CalculateNumSamplesPerFrameList (curSampleRate.whole, numFramesPerList) * (newFormat->fNumChannels * (newFormat->fBitWidth / 8));
		debug3IOLog ("the frameListSize is %d, the bufferSize is %d\n", frameListSize, bufferSize);
	} else {
                if (kUSBIn == direction) {
                    // 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.
                    frameListSize = (averageFrameSize + 1) * numFramesPerList;
                    bufferSize = averageFrameSize * numFramesPerList * numFrameLists;
                    readFrameListSize = frameListSize;
                    debug2IOLog ("frameListSize is (averageFrameSize + 1) * numFramesPerList = %d\n", frameListSize);
                } else {
                    frameListSize = averageFrameSize * numFramesPerList;
                    bufferSize = frameListSize * numFrameLists;
                    readFrameListSize = frameListSize;
                    debug2IOLog ("frameListSize is averageFrameSize * numFramesPerList = %d\n", frameListSize);
                }
	}

	// Dispose of the old frame list buffers and make new ones with the new size
	if (NULL != soundBuffer) {
		for (i = 0; i < oldNumFrameLists; i++) {
			if (NULL != soundBuffer[i]) {
				soundBuffer[i]->release ();
				soundBuffer[i] = NULL;
			}
		}
	}

	if (oldNumFrameLists != numFrameLists) {
		if (NULL != usbCompletion) {
			IOFree (usbCompletion, oldNumFrameLists * sizeof (IOUSBIsocCompletion));
			usbCompletion = NULL;
		}
	}

	if (oldNumFramesPerList != numFramesPerList) {
		if (NULL != theFrames) {
			IOFree (theFrames, oldNumFramesPerList * numFrameListsToQueue * sizeof (IOUSBIsocFrame));
			theFrames = NULL;
		}
	}

	// Make everything again at the right size for the new format
	if (NULL == usbCompletion) {
		usbCompletion = (IOUSBIsocCompletion *)IOMalloc (numFrameLists * sizeof (IOUSBIsocCompletion));
		FailIf (NULL == usbCompletion, Exit);
	}

	if (NULL == theFrames) {
		theFrames = (IOUSBIsocFrame *)IOMalloc (numFramesPerList * numFrameListsToQueue * sizeof (IOUSBIsocFrame));
		FailIf (NULL == theFrames, Exit);
	}

	if (NULL != iSubBufferMemory && 44100 != 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 (oldBufferSize != bufferSize) {
		if (NULL != soundBuffer) {
			IOFree (soundBuffer, oldNumFrameLists * sizeof (IOMemoryDescriptor *));
		}
		soundBuffer = (IOMemoryDescriptor **)IOMalloc (numFrameLists * sizeof (IOMemoryDescriptor *));
		FailIf (NULL == soundBuffer, Exit);

		if (kUSBIn == direction) {
			if (NULL != readBuffer) {
				IOFree (readBuffer, oldNumFrameLists * oldReadFrameListSize);
			}
			readBuffer = IOMallocAligned (round_page (numFrameLists * readFrameListSize), PAGE_SIZE);
			FailIf (NULL == readBuffer, Exit);
		}

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

			if (44100 == curSampleRate.whole) {
				lowFreqSamples = (float *)IOMallocAligned (round_page (bufferSize * 2), PAGE_SIZE);
				FailIf (NULL == lowFreqSamples, Exit);
				highFreqSamples = (float *)IOMallocAligned (round_page (bufferSize * 2), PAGE_SIZE);
				FailIf (NULL == highFreqSamples, Exit);
			}
		}

		if (NULL != getSampleBuffer ()) {
			IOFree (getSampleBuffer (), getSampleBufferSize ());
		}
		sampleBuffer = IOMallocAligned (round_page (bufferSize), PAGE_SIZE);
		FailIf (NULL == sampleBuffer, Exit);

		mainStream->setSampleBuffer (sampleBuffer, bufferSize);
	}

	setNumSampleFramesPerBuffer (bufferSize / (newFormat->fNumChannels * (newFormat->fBitWidth / 8)));
	if (kUSBIn == direction) {
		// Set the latency for input devices (microphones, etc.)
		// You have to wait for an entire frame list to be recorded so that we can copy the data to the HAL in our completion routine.
		setSampleOffset (frameListSize / (newFormat->fNumChannels * (newFormat->fBitWidth / 8)));
		setSampleLatency (averageFrameSamples * kMinimumFrameOffset);
	} else {
		// Set the latency for output devices (speakers, etc.)
		// This might be just a frame instead of an entire list depending on if you can modify samples after they have been queued to the USB controller.
		setSampleOffset (numFramesPerList);
		setSampleLatency (averageFrameSamples * kMinimumFrameOffset);
	}

	debug2IOLog ("called setNumSampleFramesPerBuffer with %d\n", bufferSize / (newFormat->fNumChannels * (newFormat->fBitWidth / 8)));
	debug4IOLog ("frameListSize = %d, newFormat->fNumChannels = %d, newFormat->fBitWidth %d\n", frameListSize, newFormat->fNumChannels, newFormat->fBitWidth);
	debug2IOLog ("called setSampleOffset with %d\n", numFramesPerList);

	result = PrepareFrameLists (numFrameLists, (newFormat->fBitWidth / 8), newFormat->fNumChannels);
	FailIf (kIOReturnSuccess != result, Exit);

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

IOReturn AppleUSBAudioEngine::PrepareFrameLists (UInt32 numFrameLists, UInt8 sampleSize, UInt8 numChannels) {
	IOReturn							result;
	UInt32								i;
	UInt32								offset;
	UInt32								thisFrameListSize;
	UInt16								averageFrameSamples;
	UInt16								additionalSampleFrameFreq;

	debug4IOLog ("+AppleUSBAudioEngine::PrepareFrameLists (%ld, %d, %d)\n", numFrameLists, sampleSize, numChannels);

	result = kIOReturnError;		// assume failure
	for (i = 0; i < numFrameLists; i++) {
		usbCompletion[i].target = (void *)this;
		usbCompletion[i].parameter = (void *)i;	// what frame list index this buffer is
	}

	CalculateSamplesPerFrame (curSampleRate.whole, &averageFrameSamples, &additionalSampleFrameFreq);
	if (getDirection () == kIOAudioStreamDirectionOutput) {
		if (NULL != theAssociatedPipe) {
			sampleRateCompletion.target = (void *)this;
			sampleRateCompletion.parameter = 0;
			sampleRateCompletion.action = sampleRateHandler;
			neededSampleRate = IOMemoryDescriptor::withAddress (&aveSampleRateBuf, 4, kIODirectionNone);
			FailIf (NULL == neededSampleRate, Exit);
		}
		offset = 0;
		if (additionalSampleFrameFreq == numFramesPerList) {
			thisFrameListSize = frameListSize;
		} else {
			thisFrameListSize = frameListSize - (sampleSize * numChannels);
		}

		for (i = 0; i < numFrameLists; i++) {
			usbCompletion[i].action = writeHandler;
			if (additionalSampleFrameFreq) {
				soundBuffer[i] = IOMemoryDescriptor::withAddress (((UInt8 *)getSampleBuffer ()) + offset, thisFrameListSize, kIODirectionNone);
				FailIf (NULL == soundBuffer[i], Exit);
				offset += thisFrameListSize;
				if (i % (additionalSampleFrameFreq / numFramesPerList) == 0) {
					thisFrameListSize = frameListSize;
				} else {
					thisFrameListSize = frameListSize - (sampleSize * numChannels);
				}
			} else {
				soundBuffer[i] = IOMemoryDescriptor::withAddress (((UInt8 *)getSampleBuffer ()) + (i * frameListSize), frameListSize, kIODirectionNone);
			}
		}
	} else if (getDirection () == kIOAudioStreamDirectionInput) {
		for (i = 0; i < numFrameLists; i++) {
			usbCompletion[i].action = readHandler;
			soundBuffer[i] = IOMemoryDescriptor::withAddress (((UInt8 *)readBuffer) + (i * readFrameListSize), readFrameListSize, kIODirectionNone);
			FailIf (NULL == soundBuffer[i], Exit);
		}
	}

	result = kIOReturnSuccess;

Exit:
	debugIOLog ("-AppleUSBAudioEngine::PrepareFrameLists ()\n");
	return result;
}

IOReturn AppleUSBAudioEngine::readFrameList (UInt32 frameListNum) {
	const IOAudioStreamFormat *			theFormat;
    UInt32								i,
										firstFrame;
	UInt16								averageFrameSamples;
	UInt16								additionalSampleFrameFreq;
	UInt16								bytesToRead;
    IOReturn							result;

	result = kIOReturnError;
    firstFrame = (frameListNum % numFrameListsToQueue) * numFramesPerList;

	theFormat = mainStream->getFormat ();
//	IOLog ("getSampleRate = %d\n", getSampleRate()->whole);
//	IOLog ("curSampleRate.whole = %d\n", curSampleRate.whole);
	CalculateSamplesPerFrame (curSampleRate.whole, &averageFrameSamples, &additionalSampleFrameFreq);
	bytesPerSampleFrame = theFormat->fNumChannels * (theFormat->fBitWidth / 8);
//	IOLog ("averageFrameSamples = %d, additionalSampleFrameFreq = %d\n", averageFrameSamples, additionalSampleFrameFreq);
//	IOLog ("theFormat->fNumChannels =%d, theFormat->fBitWidth = %d\n", theFormat->fNumChannels, theFormat->fBitWidth);
	if (0 != additionalSampleFrameFreq) {
		bytesToRead = (averageFrameSamples + 1) * bytesPerSampleFrame;
	} else {
		bytesToRead = averageFrameSamples * bytesPerSampleFrame;
	}

//	IOLog ("bytesToRead = %d\n", bytesToRead);
    for (i = 0; i < numFramesPerList; i++) {
        theFrames[firstFrame + i].frStatus = -1;
        theFrames[firstFrame + i].frReqCount = bytesToRead;
        theFrames[firstFrame + i].frActCount = 0;
    }

	FailIf (NULL == thePipe, Exit);
	retain ();		// Don't terminate the driver until the completion routine has run.
    result = thePipe->Read (soundBuffer[frameListNum], theFirstFrame, numFramesPerList, &theFrames[firstFrame], &usbCompletion[frameListNum]);
	if (result) debug2IOLog ("error from thePipe->Read 0x%X\n", result);
	theFirstFrame += numFramesPerList;

Exit:
    return result;
}

void AppleUSBAudioEngine::readHandler (AppleUSBAudioEngine * self, void * parameter, IOReturn result, IOUSBIsocFrame * pFrames) {
	AbsoluteTime					timestampOffset;
	AbsoluteTime					uptime;
#if DEBUGTIMESTAMPS
	static AbsoluteTime				lastTimeCalled = {0, 0};
	AbsoluteTime					timeAdded;
#endif
#if DEBUGTIMESTAMPS || DEBUGZEROTIME
	AbsoluteTime					diff;
	UInt64							nanos;
#endif
	UInt64							currentUSBFrame;
	UInt32							frameIndex;
	UInt32							frameListIndex;
	UInt32							frameListToRead;
	UInt32							numBytesToCopy;
	UInt32							numBytesToEnd;
	UInt32							numBytesCopied;
	UInt8 *							source;
	UInt8 *							dest;
	Boolean							haveWrapped;

	frameListIndex = (UInt32)parameter;

#if DEBUGTIMESTAMPS
	clock_get_uptime (&diff);
    if (lastTimeCalled.hi == 0 && lastTimeCalled.lo == 0) {
		lastTimeCalled.hi = self->status->fLastLoopTime.hi;
		lastTimeCalled.lo = self->status->fLastLoopTime.lo;
		nanoseconds_to_absolutetime ((self->numFramesPerList) * (1000 * 1000), &timeAdded);
		SUB_ABSOLUTETIME (&lastTimeCalled, &timeAdded);
	}
	SUB_ABSOLUTETIME (&diff, &lastTimeCalled);
	absolutetime_to_nanoseconds (diff, &nanos);
	IOLog ("%ld\n", (UInt32)(nanos / (1000 * 1000)));
	clock_get_uptime (&diff);
	lastTimeCalled = diff;
#endif

	if (kIOReturnSuccess != result) {
		#if DEBUGLOG
		IOLog ("Error 0x%X from USB read\n", result);
		#endif
		currentUSBFrame = self->streamInterface->GetDevice()->GetBus()->GetFrameNumber ();
		// skip ahead and see if that helps
		FailIf (NULL == self->streamInterface, Exit);
		if (self->theFirstFrame <= currentUSBFrame) {
			self->theFirstFrame = currentUSBFrame + kMinimumFrameOffset;
		}
	}

//	debug2IOLog ("bufferOffset = %ld\n", self->bufferOffset);
	haveWrapped = FALSE;
	dest = (UInt8 *)(self->getSampleBuffer ()) + self->bufferOffset;
	source = ((UInt8 *)self->readBuffer) + (frameListIndex * self->readFrameListSize);
	if (self->bufferOffset == 0 && !self->startingEngine) {
		haveWrapped = TRUE;
	}

	self->startingEngine = FALSE;	// The engine is fully started (it's no longer starting... it's running)
	for (frameIndex = 0; frameIndex < self->numFramesPerList; frameIndex++) {
		if (pFrames[frameIndex].frActCount < (pFrames[frameIndex].frReqCount - self->bytesPerSampleFrame)) {
			#if DEBUGLOG
			IOLog ("short read packet!!! %d\n", (UInt32)(pFrames[frameIndex].frActCount));
			#endif
		}

		numBytesToEnd = self->getSampleBufferSize () - self->bufferOffset;
		if ((UInt32)(pFrames[frameIndex].frActCount) > numBytesToEnd)
			numBytesToCopy = numBytesToEnd;
		else
			numBytesToCopy = pFrames[frameIndex].frActCount;

//		IOLog ("numBytesToCopy = %d, numBytesToEnd = %d\n", numBytesToCopy, numBytesToEnd);
//		IOLog ("copying %d bytes from 0x%X to 0x%X\n", numBytesToCopy, source, dest);

		memcpy (dest, source, numBytesToCopy);
		self->bufferOffset += numBytesToCopy;
		numBytesCopied = numBytesToCopy;

		if ((UInt32)(pFrames[frameIndex].frActCount) > numBytesToEnd) {
			numBytesToCopy = (pFrames[frameIndex].frActCount) - numBytesToEnd;
			dest = (UInt8 *)self->getSampleBuffer ();
//			IOLog ("buffer wapped, copying %d bytes from 0x%X to 0x%X\n", numBytesToCopy, &source[i], dest);
			memcpy (dest, source + numBytesCopied, numBytesToCopy);
			self->bufferOffset = numBytesToCopy;
			haveWrapped = TRUE;
		}

		if (haveWrapped) {
			// we wrapped - take timestamp
			nanoseconds_to_absolutetime (self->numFramesPerList * 1000 * 1000, &timestampOffset);
			clock_get_uptime (&uptime);
			ADD_ABSOLUTETIME (&uptime, &timestampOffset);
#if DEBUGZEROTIME
			diff = uptime;
//			diff.lo = uptime.lo;
//			IOLog ("diff.hi = %d, diff.lo = %d, fLastLoopTime.hi = %d, fLastLoopTime.lo = %d\n", diff.hi, diff.lo, self->status->fLastLoopTime.hi, self->status->fLastLoopTime.lo);
			SUB_ABSOLUTETIME (&diff, &self->status->fLastLoopTime);
			absolutetime_to_nanoseconds (diff, &nanos);
			IOLog ("delta=%ld, frameListIndex = %d\n", (UInt32)(nanos / (1000 * 1000)), frameListIndex);
#endif
			self->status->fLastLoopTime.hi = uptime.hi;
			self->status->fLastLoopTime.lo = uptime.lo;
			++self->status->fCurrentLoopCount;
//			self->takeTimeStamp ();
//			self->takeTimeStamp (TRUE, &uptime);
			haveWrapped = FALSE;
		}

		dest += numBytesToCopy;
		if (self->bufferOffset >= self->getSampleBufferSize ()) {
			haveWrapped = TRUE;
			if (self->bufferOffset > self->getSampleBufferSize ()) {
				#if DEBUGLOG
				IOLog ("readjusting bufferOffset, shouldn't be here.\n");
				#endif
			}
			self->bufferOffset -= self->getSampleBufferSize ();
		}
		source += pFrames[frameIndex].frReqCount;
	}

	if (self->shouldStop > 0) {
		#if DEBUGLOG
		IOLog("++AppleUSBAudioEngine::readHandler() - stopping: %d\n", self->shouldStop);
		#endif
		self->shouldStop++;
		if (self->shouldStop > self->numFrameListsToQueue) {
			FailIf (NULL == self->signal, Exit);
			self->signal->signal (kIOReturnSuccess, FALSE);
		}
	} else {
		if (((UInt32)self->numFrameLists - 1) == self->currentFrameList) {
			self->currentFrameList = 0;
		} else {
			self->currentFrameList++;
		}

		frameListToRead = self->currentFrameList + self->numFrameListsToQueue - 1;
		if (frameListToRead >= self->numFrameLists) {
			frameListToRead -= self->numFrameLists;
		}
		self->readFrameList (frameListToRead);
	}

Exit:
	self->release ();		// It's safe to terminate the driver now.
	return;
}

#if TIMERSTREAM
void AppleUSBAudioEngine::registerUnattachedStream () {
    OSDictionary *						matchingDict;

    debugIOLog ("+AppleUSBAudioEngine::registerUnattachedStream()\n");

    if (!streamNotifier) {
        matchingDict = serviceMatching ("IOUSBInterface");
        matchingDict->setObject (kUSBVendorName, interfaceVendor);
        matchingDict->setObject (kUSBProductName, interfaceProduct);
        matchingDict->setObject (kUSBDeviceReleaseNumber, deviceReleaseNumber);
        matchingDict->setObject (kUSBConfigurationValue, configurationValue);
        matchingDict->setObject (kUSBInterfaceNumber, interfaceNumber);

        streamNotifier = addNotification (gIOPublishNotification,
                                         matchingDict,
                                         (IOServiceNotificationHandler)&interfacePublished,
                                         this,
                                         NULL,
                                         1000);
    } else {
        debug2IOLog ("++AppleUSBAudioEngine[%p]::registerUnattachedStream () - error - already have a stream notifier - inconsistent state.\n", this);
    }

    debugIOLog ("-AppleUSBAudioEngine::registerUnattachedStream ()\n");
}

bool AppleUSBAudioEngine::reinitWithInterface (IOUSBInterface *interface) {
    IOUSBFindEndpointRequest			audioIsochEndpoint;
    bool								result;

    debugIOLog ("+AppleUSBAudioEngine::reinitWithInterface ()\n");
    result = FALSE;

    FailIf (NULL == interface, Exit);
    FailIf (interface == previousInterface, Exit);
    FailIf (kIOReturnSuccess != interface->open (this), Exit);

    interface->SetAlternateInterface (this, kRootAlternateSetting);
    interface->SetAlternateInterface (this, alternateInterfaceID);

    streamInterface = interface;
    previousInterface = interface;
    this->attach (streamInterface);

    audioIsochEndpoint.type = kUSBIsoc;
    if (getDirection () == kIOAudioStreamDirectionOutput) {
        audioIsochEndpoint.direction = kUSBOut;
    } else {	// kIOAudioStreamDirectionInput
        audioIsochEndpoint.direction = kUSBIn;
    }

	audioIsochEndpoint.maxPacketSize = 0xFF;	// don't care
	audioIsochEndpoint.interval = 0xFF;		// don't care

    thePipe = streamInterface->FindNextPipe (NULL, &audioIsochEndpoint);
    FailIf (NULL == thePipe, Exit);

    thePipe->retain ();

    FailIf (usbStreamRunning, Exit);
    stopTimerStream ();
    if (startUSBStream () == kIOReturnSuccess) {
        debugIOLog ("++AppleUSBAudioEngine::reinitWithInterface () - successfully re-attached to interface.\n");
        result = TRUE;
    }

Exit:
    debugIOLog ("-AppleUSBAudioEngine::reinitWithInterface ()\n");
    return result;
}

IOReturn AppleUSBAudioEngine::reinitWithInterfaceAction (OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4) {
    IOReturn result = kIOReturnBadArgument;

    if (owner && arg1 && arg2) {
        AppleUSBAudioEngine *audioEngine = OSDynamicCast (AppleUSBAudioEngine, owner);
        IOUSBInterface *interface = OSDynamicCast (IOUSBInterface, (OSObject *)arg1);

        if (audioEngine) {
            *(bool *)arg2 = audioEngine->reinitWithInterface (interface);
            result = kIOReturnSuccess;
        }
    }

    return result;
}
#endif

// This gets called when a new audio stream needs to be mixed into an already playing audio stream
void AppleUSBAudioEngine::resetClipPosition (IOAudioStream *audioStream, UInt32 clipSampleFrame) {
    if (NULL != iSubBufferMemory) {
		// 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;

#if DEBUGLOG
		IOLog ("resetClipPosition, iSubBufferOffset=%ld, previousClippedToFrame=%ld, clipSampleFrame=%ld\n", iSubBufferOffset, previousClippedToFrame, clipSampleFrame);
#endif
		if (previousClippedToFrame < clipSampleFrame) {
			iSubBufferOffset -= (previousClippedToFrame + ((getSampleBufferSize () / 4) - clipSampleFrame)) * 2;
		} else {
			iSubBufferOffset -= (previousClippedToFrame - clipSampleFrame) * 2;		// should be * streamFormat->fNumChannels, but that's not readily available
		}

		if (iSubBufferOffset < 0) {
			iSubBufferOffset += (iSubBufferMemory->getLength () / 2);
		}
		previousClippedToFrame = clipSampleFrame;
#if DEBUGLOG
		IOLog ("resetClipPosition, iSubBufferOffset=%ld, previousClippedToFrame=%ld\n", iSubBufferOffset, previousClippedToFrame);
#endif
    }
}

#if RETRY_WRITES
void AppleUSBAudioEngine::retryWriteFrameList (void *arg) {
    FrameListWriteInfo *			info;

    debug2IOLog ("+AppleUSBAudioEngine::retryWriteFrameList (%p)\n", (UInt32*)arg);

    info = (FrameListWriteInfo *)arg;
    if (info->audioEngine) {
        info->audioEngine->writeFrameList (info->frameListNum);
    }

    debug2IOLog ("-AppleUSBAudioEngine::retryWriteFrameList (%p)\n", (UInt32*)arg);
}
#endif

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

    self = (AppleUSBAudioEngine *)target;

	sampleRate = USBToHostLong (self->aveSampleRateBuf);
	oldSampleRate = self->averageSampleRate;
	// turn the 10.14 value returned by the device into a normal UInt32
	sampleRate = sampleRate << 2;
	fixed = sampleRate >> 16;
	self->averageSampleRate = fixed * 1000;
	fract = IOFixedMultiply (sampleRate & 0x0000FFFF, 1000 << 16);
	self->averageSampleRate += (fract & 0xFFFF0000) >> 16;
	if (self->averageSampleRate != oldSampleRate) {
		// The device has changed the sample rate that it needs, let's roll with the new sample rate
		self->sampleNum = 0;		// This will cause us to recalculate the samples per packet the next time we queue up some packets.
		debugIOLog ("sample rate changed!!!!!\n");
		debug2IOLog ("requestedFrameRate = %d\n", self->averageSampleRate);
	}

	self->theSampleRateFrame.frStatus = -1;
	self->theSampleRateFrame.frReqCount = 3;
	self->theSampleRateFrame.frActCount = 0;

	if (0 == self->shouldStop) {
		self->nextSynchReadFrame += (1 << (10 - self->refreshInterval));
		self->retain ();		// Don't dispose of the driver until the completion routine runs.
		result = self->theAssociatedPipe->Read (self->neededSampleRate, self->nextSynchReadFrame, 1, &self->theSampleRateFrame, &self->sampleRateCompletion);
	}

	self->release ();		// It's safe to terminate the driver now (there is a retain from the orignal read outside of this completion as well).
	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;

		debug5IOLog ("Set freq control interface %d, alt interface %d, endpoint address 0x%X, sample rate (little endian) 0x%X\n", 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!\n");
	}

Exit:
	return result;
}

#if TIMERSTREAM
void AppleUSBAudioEngine::startTimerStream() {
	debug2IOLog("+AppleUSBAudioEngine[%p]::startTimerStream()\n", this);

	if (!timerStreamThread) {
		if (getDirection () == kIOAudioStreamDirectionInput) {
			FailIf (NULL == mainStream, Exit);
			mainStream->clearSampleBuffer ();
		}
		timerStreamThread = IOCreateThread (timerStreamMain, this);
	}

Exit:
	debug2IOLog("-AppleUSBAudioEngine[%p]::startTimerStream()\n", this);
}

bool AppleUSBAudioEngine::startTimerStreamIfNecessary() {
    bool timerStreamStarted;

    timerStreamStarted = FALSE;

    if (usbAudioDevice && !usbAudioDevice->allStreamsStopped ()) {
        timerStreamStarted = TRUE;

        debug2IOLog("++AppleUSBAudioEngine[%p]::startTimerStreamIfNecessary () - not all streams are stopped and this is the only audio device.\n", this);

        if (usbStreamRunning) {
            stopUSBStream ();
            startTimerStream ();
        }
    }

    return timerStreamStarted;
}

IOReturn AppleUSBAudioEngine::startTimerStreamIfNecessaryAction(OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4) {
    IOReturn result;

    result = kIOReturnBadArgument;

    if (owner && arg1) {
        AppleUSBAudioEngine *audioEngine = OSDynamicCast(AppleUSBAudioEngine, owner);
        if (audioEngine) {
            *(bool *)arg1 = audioEngine->startTimerStreamIfNecessary();
            result = kIOReturnSuccess;
        }
    }

    return result;
}
#endif

IOReturn AppleUSBAudioEngine::startUSBStream () {
    IOReturn							resultCode;
    IOUSBFindEndpointRequest			audioIsochEndpoint;
    USBAudioConfigObject *				usbAudio;
    AbsoluteTime						uptime;
	AbsoluteTime						initialTimestampOffset;
    UInt32								frameListNum;
    UInt32								numQueued;
    UInt32								numRetries;

    debug2IOLog ("+AppleUSBAudioEngine[%p]::startUSBStream ()\n", 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

    // We may need to restart at block 0 here temporarily
    // It shouldn't be necessary, but I don't think the HAL is flushing the stream
    // Which causes extra sample data to be left over in the buffer when it disconnects
    // Those samples get played the next time the stream is opened

    if (status->fLastLoopTime.hi == 0 && status->fLastLoopTime.lo == 0) {
		if (getDirection() == kIOAudioStreamDirectionOutput) {
//			nanoseconds_to_absolutetime ((kMinimumFrameOffset - numFramesPerList) * 1000 * 1000, &initialTimestampOffset);
			nanoseconds_to_absolutetime ((kMinimumFrameOffset) * 1000 * 1000, &initialTimestampOffset);
		} else if (getDirection() == kIOAudioStreamDirectionInput) {
//			nanoseconds_to_absolutetime ((numFramesPerList + kMinimumFrameOffset) * 1000 * 1000, &initialTimestampOffset);
			nanoseconds_to_absolutetime ((kMinimumFrameOffset) * 1000 * 1000, &initialTimestampOffset);
		}
        clock_get_uptime (&uptime);
		ADD_ABSOLUTETIME (&uptime, &initialTimestampOffset);
        status->fLastLoopTime.hi = uptime.hi;
        status->fLastLoopTime.lo = uptime.lo;
        currentFrameList = 0;
		bufferOffset = 0;
		previous = 0;
		startingEngine = TRUE;
    }

    shouldStop = 0;

    FailIf ((numFrameLists < numFrameListsToQueue), Exit);

    numRetries = 0;

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

	// It is important to configure the root interface (0) prior to configuring the
	// target interface to guarantee that the device will stream correctly.
	// We need to set the alternate interface that supports the format that we will be sending it.
	resultCode = streamInterface->SetAlternateInterface (this, kRootAlternateSetting);
	FailIf (kIOReturnSuccess != resultCode, Exit);
	debug2IOLog ("streamInterface->SetAlternateInterface (this, 0) = 0x%X\n", resultCode);

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

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

	// Not concerned with errors in this function, and I think the emi 2|6 triggers a false error anyway
	(void)CheckForAssociatedEndpoint (usbAudio);

    theMaxPacket = thePipe->GetEndpoint()->maxPacketSize;
    FailIf (0 == theMaxPacket, Exit);	// Make sure we have an endpoint that accepts data.

	FailIf (NULL == streamInterface, Exit);
    theFirstFrame = streamInterface->GetDevice()->GetBus()->GetFrameNumber() + kMinimumFrameOffset;

	if (NULL != theAssociatedPipe) {
		nextSynchReadFrame = theFirstFrame;
		retain ();		// Don't dispose of the driver until the completion routine runs.
		(void)theAssociatedPipe->Read (neededSampleRate, nextSynchReadFrame, 1, &theSampleRateFrame, &sampleRateCompletion);
	}

    for (frameListNum = currentFrameList; (frameListNum < numFrameLists) && (numQueued < numFrameListsToQueue); frameListNum++) {
        while ((getDirection () == kIOAudioStreamDirectionOutput ? (writeFrameList (frameListNum) != kIOReturnSuccess) : (readFrameList (frameListNum) != kIOReturnSuccess)) && (numRetries < MAX_WRITE_RETRIES)) {
            numRetries++;
        }
        FailIf ((numRetries == MAX_WRITE_RETRIES), Exit);
        numQueued++;
    }

    numRetries = 0;
    for (frameListNum = 0; numQueued < numFrameListsToQueue; frameListNum++) {
        while ((getDirection () == kIOAudioStreamDirectionOutput ? (writeFrameList (frameListNum) != kIOReturnSuccess) : (readFrameList (frameListNum) != kIOReturnSuccess)) && (numRetries < MAX_WRITE_RETRIES)) {
            numRetries++;
        }
        FailIf ((numRetries == MAX_WRITE_RETRIES), Exit);
        numQueued++;
    }

    usbStreamRunning = TRUE;
    resultCode = kIOReturnSuccess;

Exit:
    debug2IOLog ("-AppleUSBAudioEngine[%p]::startUSBStream ()\n", this);
    return resultCode;
}

#if TIMERSTREAM
void AppleUSBAudioEngine::stopTimerStream() {
	debug2IOLog("+AppleUSBAudioEngine[%p]::stopTimerStream()\n", this);

	if (timerStreamThread) {
		timerStreamThread = NULL;

		signal->wait(FALSE);
		signal->reinit();
	}

	debug2IOLog("-AppleUSBAudioEngine[%p]::stopTimerStream()\n", this);
}
#endif

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

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

	if (NULL != thePipe) {
		// 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 (NULL != signal) {
		signal->wait (FALSE);	// wait for any running streams to finish
		signal->reinit ();
	}

	usbStreamRunning = FALSE;

	if (NULL != streamInterface) {	// if we don't have an interface, message() got called and we are being terminated
		(void)streamInterface->SetAlternateInterface (this, kRootAlternateSetting);
	}

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

#if TIMERSTREAM
void AppleUSBAudioEngine::timerStreamMain(void *audioEngine) {
	AppleUSBAudioEngine *				self;
//	AbsoluteTime						uptime;
//	AbsoluteTime						offset;

	debugIOLog("+AppleUSBAudioEngine::timerStreamMain ()\n");

	self = (AppleUSBAudioEngine *)audioEngine;
	while (self->timerStreamThread) {
		IOSleep(self->numFramesPerList);

		if (self->timerStreamThread) {
			if (((UInt32)self->numFrameLists - 1) == self->currentFrameList) {
//				nanoseconds_to_absolutetime (self->numFramesPerList * 1000 * 1000, &offset);
				// take timestamp
				self->takeTimeStamp ();
//				clock_get_uptime(&uptime);

//				if (self->getDirection() == kIOAudioStreamDirectionOutput) {
//					SUB_ABSOLUTETIME (&uptime, &offset);
//				} else {	// kIOAudioStreamDirectionInput
//					ADD_ABSOLUTETIME (&uptime, &offset);
//				}

//				self->status->fLastLoopTime.hi = uptime.hi;
//				self->status->fLastLoopTime.lo = uptime.lo;
//				++self->status->fCurrentLoopCount;
				self->currentFrameList = 0;
			} else {
				self->currentFrameList++;
			}
		}
	}

	self->signal->signal(kIOReturnSuccess, FALSE);
	IOExitThread();
	debugIOLog("-AppleUSBAudioEngine::timerStreamMain ()\n");
}

void AppleUSBAudioEngine::unregisterUnattachedStream() {
    debugIOLog ("+AppleUSBAudioEngine::unregisterUnattachedStream ()\n");

    if (streamNotifier) {
        streamNotifier->remove ();
        streamNotifier = 0;
    } else {
        debug2IOLog ("++AppleUSBAudioEngine[%p]::unregisterUnattachedStream () - error - don't have a stream notifier - inconsistent state.\n", this);
    }

    debugIOLog ("-AppleUSBAudioEngine::unregisterUnattachedStream ()\n");
}
#endif

IOReturn AppleUSBAudioEngine::writeFrameList (UInt32 frameListNum) {
	const IOAudioStreamFormat *			theFormat;
    UInt32								i,
										firstFrame;
#if FIXEDSIZEPACKETS || DEBUGLOG
	UInt16								averageFrameSamples;
	UInt16								averageFrameSize;
	UInt16								alternateFrameSize;
	UInt16								additionalSampleFrameFreq;
#endif
    IOReturn							result;
#if RETRY_WRITES
    AbsoluteTime						uptime;
    UInt64								nanos;
#endif
#if !FIXEDSIZEPACKETS
	UInt32								sampleRate;
	UInt32								bytesPerSample;
	UInt32								bytesThisFrameList;
	IOFixed								fixedSampleRate;
#endif

//	IOLog ("+writeFrameList\n");
	result = kIOReturnError;
#if RETRY_WRITES
	if (retrySync) {	// We have a pending retry
		FailIf (NULL == pendingFrameListCall, Exit);
		FailIf (NULL == pendingFrameListWriteInfo, Exit);

		if (pendingFrameListWriteInfo->frameListNum != frameListNum) {	// Not for this frame
			retrySync->wait ();	// wait for pending retry to complete
			retrySync = 0;
			thread_call_free (pendingFrameListCall);	// free thread_call used for successful retry
			pendingFrameListCall = NULL;
			IOFree (pendingFrameListWriteInfo, sizeof (FrameListWriteInfo));
			pendingFrameListWriteInfo = NULL;
		} else {	// Retry for this frame
			debug4IOLog ("++AppleUSBAudioEngine[%p]::writeFrameList (%d) - retry %d\n", this, frameListNum, ++pendingFrameListWriteInfo->retryCount);
		}
	}
#endif

	firstFrame = (frameListNum % numFrameListsToQueue) * numFramesPerList;

	theFormat = mainStream->getFormat ();

#if FIXEDSIZEPACKETS || DEBUGLOG
	CalculateSamplesPerFrame (curSampleRate.whole, &averageFrameSamples, &additionalSampleFrameFreq);
	averageFrameSize = averageFrameSamples * (theFormat->fNumChannels * (theFormat->fBitWidth / 8));
#endif

#if FIXEDSIZEPACKETS
	alternateFrameSize = (averageFrameSamples + 1) * (theFormat->fNumChannels * (theFormat->fBitWidth / 8));
	if (additionalSampleFrameFreq) {
		if (additionalSampleFrameFreq == numFramesPerList) {
//			IOLog ("additionalSampleFrameFreq == numFramesPerList\n");
			for (i = 0; i < numFramesPerList; i++) {
				theFrames[firstFrame + i].frStatus = -1;
				if ((i % additionalSampleFrameFreq) == (UInt16)(additionalSampleFrameFreq - 1)) {
					theFrames[firstFrame + i].frReqCount = alternateFrameSize;
//					IOLog ("%d ", alternateFrameSize);
				} else {
					theFrames[firstFrame + i].frReqCount = averageFrameSize;
//					IOLog ("%d ", averageFrameSize);
				}
			}
		} else {
			for (i = 0; i < numFramesPerList; i++) {
				if (((i + previous++) % additionalSampleFrameFreq) == (UInt16)(additionalSampleFrameFreq - 1)) {
					theFrames[firstFrame + i].frReqCount = alternateFrameSize;
				} else {
					theFrames[firstFrame + i].frReqCount = averageFrameSize;
				}
				if (previous == additionalSampleFrameFreq) {
					previous = 0;
				}
			}
			theFrames[firstFrame + i].frActCount = 0;
		}
	} else {
		for (i = 0; i < numFramesPerList; i++) {
			theFrames[firstFrame + i].frStatus = -1;
			theFrames[firstFrame + i].frReqCount = averageFrameSize;
			theFrames[firstFrame + i].frActCount = 0;
		}
	}
#else
	sampleRate = averageSampleRate;		// we want to make packets based on what the hardware is telling us it _really_ needs, not it's overall average sample rate

	bytesPerSample = theFormat->fNumChannels * (theFormat->fBitWidth / 8);
	fixedSampleRate = IOUFixedDivide (sampleRate << 16, 1000 << 16);		// we want a number like 44.101 to represent 44101Hz
	if (1000 == sampleNum || 0 == sampleNum) {
		samplesThisFrame = fixedSampleRate >> 16;
		sampleNum = 2;
		sum = samplesThisFrame;
	}

	for (i = 0; i < numFramesPerList; i++) {
		theFrames[firstFrame + i].frStatus = -1;
		theFrames[firstFrame + i].frActCount = 0;
		theFrames[firstFrame + i].frReqCount = samplesThisFrame * bytesPerSample;
		bytesThisFrameList += theFrames[firstFrame + i].frReqCount;
		bytesQueued += bytesThisFrameList;
		samplesThisFrame = (IOUFixedMultiply (fixedSampleRate, sampleNum << 16) >> 16) - sum;
	#if DEBUGLOG
		if (samplesThisFrame < averageFrameSamples || samplesThisFrame > (averageFrameSamples + 1)) {
			IOLog ("error !!! samplesThisFrame = %d\n", samplesThisFrame);
			IOLog ("sum = %d\n", sum);
			IOLog ("sampleNum = %d\n", sampleNum);
		}
	#endif
		sum += samplesThisFrame;
		sampleNum++;
		if (1000 == sampleNum) {
			samplesThisFrame = fixedSampleRate >> 16;
			sampleNum = 2;
			sum = samplesThisFrame;
		}
	}
#endif

/*
	if ((frameListNum % 20) == 0) {
		debug4IOLog("AppleUSBAudioEngine::writeFrameList(%d) to frame: %lu - at frame: %lu\n", frameListNum, (UInt32)theFirstFrame, (UInt32)streamInterface->GetFrameNumber());
	}
*/

/*
	// test frame lists
	{
		UInt32						i, j, numSegs, offset;
		IOPhysicalSegment			segs[2];
		IONaturalMemoryCursor *		_isoCursor;

		offset = 0;
        _isoCursor = IONaturalMemoryCursor::withSpecification (1023, 1023);
		if (NULL == _isoCursor) {
			IOLog ("********couldn't allocate the memory cursor*********\n");
		} else {
			for (j = 0; j < numFrameLists; j++) {
				for (i = 0; i < numFramesPerList; i++) {
					numSegs = _isoCursor->getPhysicalSegments (soundBuffer[j], offset, segs, 2, theFrames[firstFrame + i].frReqCount);
					offset += segs[0].length;
					if (numSegs == 0) {
						Debugger ("numSegs is 0!");
					}
				}
				offset = 0;
			}
		}
	}
*/

	FailIf (NULL == thePipe, Exit);
	// Just in case the device is unplugged while our write is outstanding, we need to make sure that the driver isn't torn down until we complete the write
	retain ();		// Don't nobody go no where...
	result = thePipe->Write (soundBuffer[frameListNum], theFirstFrame, numFramesPerList, &theFrames[firstFrame], &usbCompletion[frameListNum]);
//	IOLog ("++after thePipe->Write\n");

	if (result != kIOReturnSuccess) {
		debug6IOLog ("++AppleUSBAudioEngine[%p]::writeFrameList(%d) - error writing to pipe at frame %lu - current = %lu: 0x%x\n", this, frameListNum, (UInt32)theFirstFrame, (UInt32)streamInterface->GetDevice()->GetBus()->GetFrameNumber(), result);
#if RETRY_WRITES
		if (!retrySync) {	// No retrys pending
			FailIf (pendingFrameListWriteInfo, Exit);
			FailIf (pendingFrameListCall, Exit);

			retrySync = IOSyncer::create (TRUE);
			if (!retrySync) {
				return kIOReturnNoMemory;
			}
			pendingFrameListWriteInfo = (FrameListWriteInfo *)IOMalloc (sizeof (FrameListWriteInfo));
			if (!pendingFrameListWriteInfo) {
				return kIOReturnNoMemory;
			}
			pendingFrameListWriteInfo->audioEngine = this;
			pendingFrameListWriteInfo->frameListNum = frameListNum;
			pendingFrameListWriteInfo->retryCount = 0;

			pendingFrameListCall = thread_call_allocate ((thread_call_func_t)retryWriteFrameList, (thread_call_param_t)pendingFrameListWriteInfo);
			if (!pendingFrameListCall) {
				return kIOReturnNoMemory;
			}
		} else {
			FailIf (NULL == pendingFrameListWriteInfo, Exit);
			FailIf (NULL == pendingFrameListCall, Exit);
			FailIf (pendingFrameListWriteInfo->frameListNum != frameListNum, Exit);
		}

		clock_get_uptime (&uptime);
		absolutetime_to_nanoseconds (uptime, &nanos);
		nanos += 1000000;	// Schedule 1ms in the future for retry
		nanoseconds_to_absolutetime (nanos, &uptime);
		thread_call_enter_delayed (pendingFrameListCall, uptime);
#endif
	} else {
		theFirstFrame += numFramesPerList;
#if RETRY_WRITES
		if (retrySync) {
			retrySync->signal ();
			debug3IOLog ("++AppleUSBAudioEngine[%p]::writeFrameList (%d) - retry successful.\n", this, frameListNum);
		}
#endif
	}

Exit:
//	IOLog ("-writeFrameList\n");
	return result;
}

void AppleUSBAudioEngine::writeHandler (AppleUSBAudioEngine * self, void * parameter, IOReturn result, IOUSBIsocFrame * pFrames) {
//	AbsoluteTime						offset;
	UInt32								frameListToWrite;
	UInt64								currentUSBFrame;
//	static UInt64						lastScheduledUSBFrame = 0;
#if DEBUGTIMESTAMPS
	static AbsoluteTime					lastTimeCalled = {0, 0};
	AbsoluteTime						timeAdded;
#endif
#if DEBUGTIMESTAMPS || DEBUGZEROTIME
	AbsoluteTime						uptime;
	AbsoluteTime						diff;
	UInt64								nanos;
#endif
#if DEBUGLOG
	UInt64								USBFrameNumber;
	UInt32								i;
#endif

//	IOLog ("+writeHandler\n");

#if DEBUGTIMESTAMPS
	clock_get_uptime (&diff);
    if (lastTimeCalled.hi == 0 && lastTimeCalled.lo == 0) {
		lastTimeCalled.hi = self->status->fLastLoopTime.hi;
		lastTimeCalled.lo = self->status->fLastLoopTime.lo;
		nanoseconds_to_absolutetime ((self->numFramesPerList) * (1000 * 1000), &timeAdded);
		SUB_ABSOLUTETIME (&lastTimeCalled, &timeAdded);
	}
	SUB_ABSOLUTETIME (&diff, &lastTimeCalled);
	absolutetime_to_nanoseconds (diff, &nanos);
	IOLog ("%ld\n", (UInt32)(nanos / (1000 * 1000)));
	clock_get_uptime (&diff);
	lastTimeCalled = diff;
#endif

    if (result != kIOReturnSuccess) {
#if DEBUGLOG
		USBFrameNumber = self->streamInterface->GetDevice()->GetBus()->GetFrameNumber ();
		IOLog ("++AppleUSBAudioEngine[%p]::writeHandler () - error 0x%x on USB frame 0x%lx%lx\n", self, result, (UInt32)(USBFrameNumber >> 32), (UInt32)USBFrameNumber);
		IOLog ("current frame list = %d\n", self->currentFrameList);
		for (i = 0; i < self->numFramesPerList; i++) {
			if (kIOReturnSuccess != pFrames->frStatus) {
				IOLog ("fr %d: 0x%lx ", i, pFrames->frStatus);
			}
		}
		IOLog ("\n");
#endif
		currentUSBFrame = self->streamInterface->GetDevice()->GetBus()->GetFrameNumber ();
		switch (result) {
			case kIOReturnOverrun:
				// skip ahead and see if that helps
				FailIf (NULL == self->streamInterface, Exit);
				if (self->theFirstFrame <= currentUSBFrame) {
					self->theFirstFrame = currentUSBFrame + kMinimumFrameOffset;
				}
//				if (lastScheduledUSBFrame != currentUSBFrame) {
//					lastScheduledUSBFrame = currentUSBFrame;
//				} else {
//					lastScheduledUSBFrame++;
//				}
//				self->theFirstFrame = lastScheduledUSBFrame + (self->numFramesPerList * self->numFrameListsToQueue);

//				if (self->currentFrameList + (self->numFramesPerList * self->numFrameListsToQueue + kMinimumFrameOffset) >= self->numFrameLists)
//					self->currentFrameList = self->currentFrameList + (self->numFramesPerList * self->numFrameListsToQueue + kMinimumFrameOffset) - self->numFrameLists - 1;
//				else
//					self->currentFrameList += (self->numFramesPerList * self->numFrameListsToQueue + kMinimumFrameOffset);
				break;
			default:
				;
		}
    }

    if (((UInt32)self->numFrameLists - 1) == self->currentFrameList) {
//		nanoseconds_to_absolutetime (self->numFramesPerList * 1000 * 1000, &offset);
		// take timestamp
//		SUB_ABSOLUTETIME (&uptime, &offset);
#if DEBUGZEROTIME
		clock_get_uptime (&uptime);
		diff = uptime;
//		diff.lo = uptime.lo;
//		IOLog ("diff.hi = %d, diff.lo = %d, fLastLoopTime.hi = %d, fLastLoopTime.lo = %d\n", diff.hi, diff.lo, self->status->fLastLoopTime.hi, self->status->fLastLoopTime.lo);
		SUB_ABSOLUTETIME (&diff, &self->status->fLastLoopTime);
		absolutetime_to_nanoseconds (diff, &nanos);
		IOLog ("delta=%ld\n", (UInt32)(nanos / (1000 * 1000)));
#endif
//		self->status->fLastLoopTime.hi = uptime.hi;
//		self->status->fLastLoopTime.lo = uptime.lo;
//		++self->status->fCurrentLoopCount;
		self->takeTimeStamp ();
		self->currentFrameList = 0;
    } else {
		self->currentFrameList++;
    }

	if (self->shouldStop > 0) {
		debug2IOLog ("++AppleUSBAudioEngine::writeHandler() - stopping: %d\n", self->shouldStop);
		self->shouldStop++;
		if (self->shouldStop > self->numFrameListsToQueue) {
			FailIf (NULL == self->signal, Exit);
			self->signal->signal (kIOReturnSuccess, FALSE);
		}
	} else {
		frameListToWrite = self->currentFrameList + self->numFrameListsToQueue - 1;
		if (frameListToWrite >= self->numFrameLists) {
			frameListToWrite -= self->numFrameLists;
		}
		self->writeFrameList (frameListToWrite);
	}

Exit:
//	IOLog ("-writeHandler\n");
	self->release ();		// It's safe to be terminated now.
	return;
}

