AppleiSubEngine.cpp [plain text]
#include "AppleiSubEngine.h"
#include "AppleUSBAudioCommon.h"
#include "AppleUSBAudioLevelControl.h"
#include "AppleUSBAudioMuteControl.h"
#include "USBAudioObject.h"

#include <IOKit/audio/IOAudioStream.h>
#include <IOKit/audio/IOAudioEngine.h>
#include <IOKit/pwr_mgt/RootDomain.h>

#define kiSubFeatureUnitID					2
#define kiSubMuteControlChannelNum			0
#define kiSubVolumeControlMasterChannelNum	1

// Has to be true or else the iSub looses the beginning of sounds if it has auto powered down
#define NUM_POWER_STATES					2

#define super IOService

// aml 02.14.02 mandatory definitions of static constants
const UInt32 AppleiSubEngine::kDefaultOutputSampleRate;		
const UInt32 AppleiSubEngine::kDefaultNumChannels;		
const UInt32 AppleiSubEngine::kDefaultBytesPerSample;		
const iSubAltInterfaceType AppleiSubEngine::kDefaultiSubAltInterface;		

OSDefineMetaClassAndStructors (AppleiSubEngine, super)

#pragma mark -IOKit Routines-

void AppleiSubEngine::closeiSub (IOService * forClient) {
	IOReturn				result;

	debugIOLog ("+AppleiSubEngine[%p]::closeiSub (%p)", this, forClient);

	if (forClient == audioEngine) {
		if ((NULL != muteControl || NULL != iSubVolumeControl) && NULL != audioEngine) {
			debugIOLog ("Removing iSub audio controls");
	
			if (NULL != iSubVolumeControl) {
				result = audioEngine->removeDefaultAudioControl (iSubVolumeControl);
				iSubVolumeControl->release ();
				iSubVolumeControl = NULL;
				#if DEBUGLOGGING
				if (kIOReturnSuccess != result) debugIOLog ("Error 0x%x removing left iSub control", result);
				#endif
			}
	
			if (NULL != muteControl) {
				result = audioEngine->removeDefaultAudioControl (muteControl);
				muteControl->release ();
				muteControl = NULL;
				#if DEBUGLOGGING
				if (kIOReturnSuccess != result) debugIOLog ("Error 0x%x removing mute iSub control", result);
				#endif
			}
	
			release ();		// It's safe to release us now, there are no more controls that might be able to call into us.
			// Don't try to use our audio engine host anymore
			audioEngine = NULL;
#if DEBUGLOGGING
		} else {
			if (NULL == muteControl) {debugIOLog ("NULL == muteControl");}
			if (NULL == iSubVolumeControl) {debugIOLog ("NULL == iSubVolumeControl");}
			if (NULL == audioEngine) {debugIOLog ("NULL == audioEngine");}
#endif
		}
	} else {
		debugIOLog ("the wrong client tried to close us");
	}

	debugIOLog ("-AppleiSubEngine[%p]::closeiSub (%p)", this, forClient);
}

void AppleiSubEngine::free (void) {
	UInt32							frameIndex;

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

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

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

	if (NULL != sampleBuffer) {
		IOFree (sampleBuffer, bufferSize);
		sampleBuffer = NULL;
	}

	for (frameIndex = 0; frameIndex < NUM_ISUB_FRAME_LISTS; frameIndex++) {
		if (NULL != soundBuffer[frameIndex]) {
			soundBuffer[frameIndex]->release ();
			soundBuffer[frameIndex] = NULL;
		}
	}

	super::free ();

	debugIOLog ("-AppleiSubEngine[%p]::free ()", this);
	return;
}

// This has to be called on the audio engine's work loop so that we don't interrupt the IOAudioFamily
bool AppleiSubEngine::openiSub (IOService * forClient, iSubRequestCloseRoutine theiSubRequestCloseRoutine) {
    bool						resultCode;

	resultCode = FALSE;

	// Only one client at a time!
	FailIf (NULL != audioEngine, Exit);
	audioEngine = OSDynamicCast (IOAudioEngine, forClient);
	FailIf (NULL == audioEngine, Exit);

	FailIf (NULL == theiSubRequestCloseRoutine, Exit);
	miSubRequestCloseRoutine = theiSubRequestCloseRoutine;

//	audioEngine->joinPMtree (this);
//	if (pm_vars != NULL) {
//		registerPowerDriver (this, iSubPowerStates, NUM_POWER_STATES);
//		changePowerStateTo (1);
//	}

	audioEngine->pauseAudioEngine ();
	audioEngine->beginConfigurationChange ();
	retain ();		// As long as there are controls out there, don't let ourselves be released.

	// Only need a master volume control now that the iSub is running mono.
	iSubVolumeControl = AppleUSBAudioLevelControl::create (kiSubFeatureUnitID,
													controlInterface->GetInterfaceNumber (),
													VOLUME_CONTROL,
													kiSubVolumeControlMasterChannelNum,
													FALSE,
													(USBDeviceRequest)&deviceRequest,
													this,
													kIOAudioLevelControlSubTypeLFEVolume,
													kIOAudioControlUsageOutput);
	FailIf (NULL == iSubVolumeControl, Exit);

	audioEngine->addDefaultAudioControl (iSubVolumeControl);

	muteControl = AppleUSBAudioMuteControl::create (kiSubFeatureUnitID,
													controlInterface->GetInterfaceNumber (),
													0,
													(USBDeviceRequest)&deviceRequest,
													this,
													kIOAudioControlUsageOutput,
													kIOAudioToggleControlSubTypeLFEMute);
	FailIf (NULL == muteControl, Exit);

	audioEngine->addDefaultAudioControl (muteControl);

	audioEngine->completeConfigurationChange ();
	audioEngine->resumeAudioEngine ();

	mNeedToSync = false;	// aml [3095619]
	
	resultCode = TRUE;

Exit:
	debugIOLog ("-AppleiSubEngine[%p]::handleOpen (%p, 0x%lx, %p) = %d", this, forClient, resultCode);
	return resultCode;
}

bool AppleiSubEngine::init (OSDictionary * properties) {
	Boolean							resultCode;

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

	resultCode = FALSE;
	terminatingDriver = FALSE;

	FailIf (FALSE == super::init (properties), Exit);

	resultCode = TRUE;

	// aml 2.28.02 initialize member vars
	mFormat.altInterface = kDefaultiSubAltInterface;		
	mFormat.numChannels = kDefaultNumChannels;		
	mFormat.bytesPerSample = kDefaultBytesPerSample;		
	mFormat.outputSampleRate = kDefaultOutputSampleRate;		

Exit:
	debugIOLog ("-AppleiSubEngine[%p]::init (%p) = %d", this, properties, resultCode);
	return resultCode;
}

bool AppleiSubEngine::start (IOService * provider) {
	Boolean							resultBool;
	IOUSBFindInterfaceRequest		findRequest;
	IOUSBFindEndpointRequest		audioIsochEndpoint;
	IOReturn						resultIOReturn;
	UInt32							i;
	UInt32							frameListNum;
	UInt16							numQueued;
	static IOPMPowerState			iSubPowerStates[2] = {
		{kIOPMPowerStateVersion1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{kIOPMPowerStateVersion1, kIOPMDeviceUsable, kIOPMPowerOn, kIOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0}
	};

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

	resultBool = FALSE;
	FailIf (FALSE == super::start (provider), Exit);

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

	ourInterfaceNumber = streamInterface->GetInterfaceNumber ();
	debugIOLog ("AppleiSubEngine->ourInterfaceNumber = %d", ourInterfaceNumber);
	// aml 1.18.02 replaced constant with member
	alternateInterfaceID = mFormat.altInterface;		

	PMinit ();
	streamInterface->joinPMtree (this);
	if (pm_vars != NULL) {
		registerPowerDriver (this, iSubPowerStates, NUM_POWER_STATES);
		changePowerStateTo (1);
	}

	// aml 1.18.02 replaced sample rate with member
	// aml 2.13.02 replaced kInputSampleRate with mOutputSampleRate
	// aml 2.13.02 added num channels and bit depth constants, instead of '4'
	frameListSize = CalculateNumSamplesPerBuffer (mFormat.outputSampleRate, NUM_ISUB_FRAMES_PER_LIST) * mFormat.numChannels * mFormat.bytesPerSample;
	debugIOLog ("format = %ldHz, %ld channels, %ld bits", mFormat.outputSampleRate, mFormat.numChannels, mFormat.bytesPerSample * 8);
#if AML_DEBUGLOG
	debugIOLog ("AppleiSubEngine::handleOpen: frameListSize = %d", frameListSize);
#endif
	debugIOLog ("frameListSize = %d", frameListSize);
	// aml 2.13.02 replaced kInputSampleRate with mOutputSampleRate
	// aml 2.13.02 added num channels and bit depth members, instead of '4'
	bufferSize = CalculateNumSamplesPerBuffer (mFormat.outputSampleRate, NUM_ISUB_FRAMES_PER_LIST, NUM_ISUB_FRAME_LISTS) * mFormat.numChannels * mFormat.bytesPerSample;
	debugIOLog ("bufferSize = %d", bufferSize);
	sampleBuffer = IOMallocAligned (bufferSize, PAGE_SIZE);
	FailIf (NULL == sampleBuffer, Exit);
	// aml 2.13.02 changed from clearing by longs to words
	for (i = 0; i < bufferSize / sizeof(UInt16); i++) {
		((UInt16*)sampleBuffer)[i] = 0;
	}
	sampleBufferDescriptor = IOMemoryDescriptor::withAddress (sampleBuffer, bufferSize, kIODirectionNone);
	FailIf (NULL == sampleBufferDescriptor, Exit);

	FailIf (kIOReturnSuccess != PrepareFrameLists (frameListSize), Exit);
//	FailIf (TRUE == streamOpened, Exit);
//	debugIOLog ("after TRUE == streamOpened");
	shouldCloseStream = FALSE;
	FailIf (FALSE == streamInterface->open (this), Exit);
	streamOpened = TRUE;

	resultIOReturn = streamInterface->SetAlternateInterface (this, kRootAlternateSetting);
	FailIf (kIOReturnSuccess != resultIOReturn, Exit);

	// aml 1.18.02 replaced 4 (16 bit stereo alternate interface) with member
 	resultIOReturn = streamInterface->SetAlternateInterface (this, mFormat.altInterface);
	FailIf (kIOReturnSuccess != resultIOReturn, Exit);

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

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

	// The iSub is a little funky and reseting it improves (but doesn't totally eliminate) funky behavior.
//	streamInterface->GetDevice()->ResetDevice ();

	// Start the iSub playing silence now
	loopCount = 0;

	numUSBFrameListsNotOutstanding = 0;
	numQueued = 0;
	currentFrameList = 0;
	currentByteOffset = 0;
	theFirstFrame = streamInterface->GetDevice()->GetBus()->GetFrameNumber () + kMinimumiSubFrameOffset;
	for (frameListNum = currentFrameList; numQueued < NUM_ISUB_FRAME_LISTS_TO_QUEUE; frameListNum++) {
		resultIOReturn = WriteFrameList (frameListNum);
		FailIf (kIOReturnSuccess != resultIOReturn, Exit);
		numQueued++;
	}
	debugIOLog ("writeFrameList resultIOReturn = 0x%lx", resultIOReturn);

	// Find control interface on iSub device
	findRequest.bInterfaceClass = 1;
	findRequest.bInterfaceSubClass = 1;
	findRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
	findRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare;
	controlInterface = streamInterface->GetDevice()->FindNextInterface (NULL, &findRequest);
	FailIf (NULL == controlInterface, Exit);

	registerService ();
	resultBool = TRUE;

Exit:
	debugIOLog ("-AppleiSubEngine[%p]::start (%p) = %d", this, provider, resultBool);
	return resultBool;
}

void AppleiSubEngine::stop (IOService *provider) {
	debugIOLog("+AppleiSubEngine::stop");

	PMstop();

	super::stop(provider);

	debugIOLog("-AppleiSubEngine::stop");
}

bool AppleiSubEngine::willTerminate (IOService * provider, IOOptionBits options) {
	bool					resultCode;

	debugIOLog ("+AppleiSubEngine[%p]::willTerminate (%p, 0x%x) rc =", this, provider, options, getRetainCount ());

	terminatingDriver = TRUE;

	if (NULL != audioEngine) {
		(miSubRequestCloseRoutine)(audioEngine);
	}

	if (FALSE == iSubUSBRunning) {
		// close our stream interface and go away
		if ((streamInterface != NULL) && (streamInterface == provider)) {
			debugIOLog ("willTerminate closing iSub down");
			if (NULL != thePipe) {
				thePipe->release ();
				thePipe = NULL;
			}
			streamInterface->close (this);
			streamOpened = FALSE;
		}
	} else {
		shouldCloseStream = TRUE;
	}

	resultCode = super::willTerminate (provider, options);

	debugIOLog ("-AppleiSubEngine[%p]::willTerminate (%p, 0x%x) = %d, rc=%d", this, provider, options, resultCode, getRetainCount ());
	return resultCode;
}

IOReturn AppleiSubEngine::setPowerState (unsigned long powerStateOrdinal, IOService *device) {
	IOReturn					result;

	debugIOLog("AppleiSubEngine[%p]::setPowerState (%lu, %p)", this, powerStateOrdinal, device);

	result = kIOReturnError;

	if (device == this) {
		switch (powerStateOrdinal) {
			case 0:		// Power off state
				debugIOLog ("sleeping...");
				sleeping = TRUE;
				StopiSub ();
				result = IOPMNoErr;
				break;
			case 1:		// Power on state
				debugIOLog ("waking...");
				sleeping = FALSE;	
				result = IOPMNoErr;
				break;
			default:
				result = IOPMNoSuchState;
		}
	}

	return result;
}

#pragma mark -iSub Routines-

UInt32 AppleiSubEngine::CalculateNumSamplesPerBuffer (UInt32 sampleRate, UInt32 theNumFramesPerList, UInt32 theNumFrameLists) {
	UInt32							numSamplesPerFrameList;
	UInt32 							totalFrames;
	UInt32							numAlternateFrames;
	UInt32							numAverageFrames;
	UInt16							averageSamplesPerFrame;
	UInt16							additionalSampleFrameFreq;

	CalculateSamplesPerFrame (sampleRate, &averageSamplesPerFrame, &additionalSampleFrameFreq);
	if (0 == additionalSampleFrameFreq) {
		numSamplesPerFrameList = averageSamplesPerFrame * theNumFramesPerList * theNumFrameLists;
	} else {
		totalFrames = theNumFramesPerList * theNumFrameLists;

		numAlternateFrames = totalFrames / additionalSampleFrameFreq;
		numAverageFrames = totalFrames - numAlternateFrames;
		numSamplesPerFrameList = (numAverageFrames * averageSamplesPerFrame) + (numAlternateFrames * (averageSamplesPerFrame + 1));
	}

	return numSamplesPerFrameList;
}

void AppleiSubEngine::CalculateSamplesPerFrame (UInt32 sampleRate, UInt16 * averageSamplesPerFrame, UInt16 * additionalSampleFrameFreq) {
	UInt32							divisor;

	*averageSamplesPerFrame = sampleRate / 1000;

	divisor = (sampleRate % 1000);

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

IOReturn AppleiSubEngine::deviceRequest (IOUSBDevRequest * request, AppleiSubEngine * self, IOUSBCompletion * completion) {
	IOReturn						result;

	debugIOLog ("+AppleiSubEngine[%p]::deviceRequest (%p, %p)", self, request, completion);
	result = kIOReturnSuccess;
	if (self->streamInterface && request && (FALSE == self->terminatingDriver)) {
		result = self->streamInterface->DeviceRequest (request, completion);
	}

	debugIOLog ("-AppleiSubEngine[%p]::deviceRequest (%p, %p)", self, request, completion);
	return result;
}

UInt32 AppleiSubEngine::GetCurrentByteCount (void) {
	return currentByteOffset;
}

UInt32 AppleiSubEngine::GetCurrentLoopCount (void) {
	return loopCount;
}

IOMemoryDescriptor * AppleiSubEngine::GetSampleBuffer (void) {
	return sampleBufferDescriptor;
}

IOReturn AppleiSubEngine::PrepareFrameLists (UInt32 frameListSize) {
	IOReturn						result;
	UInt32							frameIndex;

	result = kIOReturnError;

	for (frameIndex = 0; frameIndex < NUM_ISUB_FRAME_LISTS; frameIndex++) {
		usbCompletion[frameIndex].target = (void *)this;
		usbCompletion[frameIndex].parameter = (void *)((UInt8 *)sampleBuffer + (frameIndex * frameListSize));	// pointer into the buffer that is the start of this frame list
		usbCompletion[frameIndex].action = WriteHandler;

		soundBuffer[frameIndex] = IOMemoryDescriptor::withAddress ((UInt8 *)usbCompletion[frameIndex].parameter, frameListSize, kIODirectionNone);
		FailIf (NULL == soundBuffer[frameIndex], Exit);
	}

	result = kIOReturnSuccess;

Exit:
	return result;
}

IOReturn AppleiSubEngine::StartiSub (void) {
	UInt32							frameListNum;
	UInt16							numQueued = 0;	// aml this was unititialized and the for loop below never ran sometimes!
    IOReturn						resultCode;

	debugIOLog ("+AppleiSubEngine[%p]::StartiSub ()", this);

	FailWithAction (TRUE == sleeping, resultCode = kIOReturnOffline, Exit);
	resultCode = kIOReturnError;
	iSubRunning = TRUE;

#if ABORT_PIPE_ON_START
	FailIf (NULL == thePipe, Exit);
	thePipe->Abort ();		// let's kill all outstanding IO and start right back at the beginning
	iSubUSBRunning = FALSE;
//	FailIf (NULL == streamInterface, Exit);
//	theFirstFrame = streamInterface->GetDevice()->GetBus()->GetFrameNumber () + kMinimumiSubFrameOffset;
//	for (frameListNum = currentFrameList; numQueued < NUM_ISUB_FRAME_LISTS_TO_QUEUE; frameListNum++) {
//		resultCode = WriteFrameList (frameListNum);
//		FailIf (kIOReturnSuccess != resultCode, Exit);
//		numQueued++;
//	}
#else
	loopCount = 0xFFFFFFFF;			// so that it will go to 0 in the write completion routine when the frames are aborted
	currentFrameList = NUM_ISUB_FRAME_LISTS - 1;
//	theFirstFrame = 0;		// force the completion routine to calculate the correct starting frame
//	FailIf (NULL == thePipe, Exit);
//	thePipe->Abort ();		// let's kill all outstanding IO and start right back at the beginning
//	iSubUSBRunning = FALSE;
//	currentByteOffset = 0;
#endif // ABORT_PIPE_ON_START

	resultCode = kIOReturnSuccess;

	if (FALSE == iSubUSBRunning) {
		loopCount = 0;

		numUSBFrameListsNotOutstanding = 0;
		numQueued = 0;
		currentFrameList = 0;
		currentByteOffset = 0;
		FailIf (NULL == streamInterface, Exit);
		theFirstFrame = streamInterface->GetDevice()->GetBus()->GetFrameNumber () + kMinimumiSubFrameOffset;
		for (frameListNum = currentFrameList; numQueued < NUM_ISUB_FRAME_LISTS_TO_QUEUE; frameListNum++) {
			resultCode = WriteFrameList (frameListNum);
			FailIf (kIOReturnSuccess != resultCode, Exit);
			numQueued++;
		}
	}

Exit:
	debugIOLog ("-AppleiSubEngine[%p]::StartiSub (), result = %d", this, resultCode);

	return resultCode;
}

IOReturn AppleiSubEngine::StopiSub (void) {

	debugIOLog ("+AppleiSubEngine[%p]::StopiSub ()", this);

	iSubRunning = FALSE;

	debugIOLog ("-AppleiSubEngine[%p]::StopiSub ()", this);

	return kIOReturnSuccess;
}

IOReturn AppleiSubEngine::WriteFrameList (UInt32 frameListNum) {
    UInt32							frameIndex;
	UInt32							firstFrame;
	UInt16							averageFrameSamples;
	UInt16							averageFrameSize;
	UInt16							alternateFrameSize;
	UInt16							additionalSampleFrameFreq;
    IOReturn						result;

	result = kIOReturnError;

	firstFrame = (frameListNum % NUM_ISUB_FRAME_LISTS_TO_QUEUE) * NUM_ISUB_FRAMES_PER_LIST;

	// aml 2.13.02 replaced kInputSampleRate with kOutputSampleRate
	CalculateSamplesPerFrame (mFormat.outputSampleRate, &averageFrameSamples, &additionalSampleFrameFreq);
	// aml 2.13.02 added num channels and bit depth constants, instead of '4'
	averageFrameSize = averageFrameSamples * mFormat.numChannels * mFormat.bytesPerSample;
	alternateFrameSize = (averageFrameSamples + 1) * mFormat.numChannels * mFormat.bytesPerSample;

	if (additionalSampleFrameFreq) {
		for (frameIndex = 0; frameIndex < NUM_ISUB_FRAMES_PER_LIST; frameIndex++) {
			theFrames[firstFrame + frameIndex].frStatus = -1;
			if ((frameIndex % additionalSampleFrameFreq) == (UInt16)(additionalSampleFrameFreq - 1)) {
				theFrames[firstFrame + frameIndex].frReqCount = alternateFrameSize;
			} else {
				theFrames[firstFrame + frameIndex].frReqCount = averageFrameSize;
			}
			theFrames[firstFrame + frameIndex].frActCount = 0;
		}
	} else {
		for (frameIndex = 0; frameIndex < NUM_ISUB_FRAMES_PER_LIST; frameIndex++) {
			theFrames[firstFrame + frameIndex].frStatus = -1;
			theFrames[firstFrame + frameIndex].frReqCount = averageFrameSize;
			theFrames[firstFrame + frameIndex].frActCount = 0;
		}
	}

    FailIf (NULL == thePipe, Exit);
//	retain ();		// Don't want the driver being terminated until our completion routine runs.
    result = thePipe->Write (soundBuffer[frameListNum], theFirstFrame, NUM_ISUB_FRAMES_PER_LIST, &theFrames[firstFrame], &usbCompletion[frameListNum]);

	if (result != kIOReturnSuccess) {
		if (streamInterface) {
			debugIOLog ("++AppleiSubEngine[%p]::WriteFrameList (%d) - error writing to pipe at frame %lu - current = %lu: 0x%x", this, frameListNum, (UInt32)theFirstFrame, (UInt32)streamInterface->GetDevice()->GetBus()->GetFrameNumber(), result);
		}
		iSubUSBRunning = FALSE;
	} else {
		theFirstFrame += NUM_ISUB_FRAMES_PER_LIST;
		iSubUSBRunning = TRUE;
	}

Exit:
	return result;
}

void AppleiSubEngine::WriteHandler (void * object, void * buffer, IOReturn result, IOUSBIsocFrame * pFrames) {
	AppleiSubEngine *				self;
	UInt64							currentUSBFrame;
	UInt32							frameListToWrite;
	UInt32							i;

	self = (AppleiSubEngine *)object;

	FailIf (TRUE == self->sleeping, Exit);

	if (result != kIOReturnSuccess) {
		debugIOLog ("++AppleiSubEngine::WriteHandler () - error 0x%x", result);
		FailIf (NULL == self->streamInterface, Exit);
		currentUSBFrame = self->streamInterface->GetDevice()->GetBus()->GetFrameNumber ();
		switch (result) {
#if ABORT_PIPE_ON_START
			case kIOReturnAborted:
#if AML_DEBUGLOG
				debugIOLog ("AppleiSubEngine::WriteHandler() aborted.");
#endif
				goto Exit;			// do nothing
				break;
			case kIOReturnOverrun:
#else
			case kIOReturnOverrun:
			case kIOReturnAborted:
#endif
			default:
				// skip ahead and see if that helps
				if (self->theFirstFrame <= currentUSBFrame) {
					self->theFirstFrame = currentUSBFrame + kMinimumiSubFrameOffset;
					debugIOLog ("+++AppleiSubEngine::skipping ahead to frame %ld", self->theFirstFrame);
				}
				break;
		}
	}

	// aml 4.25.02 moved below error checking!

	// Zero the data in the buffer so that this buffer just contains silence
	// aml 2.13.02 changed from clearing by longs to words
	for (i = 0; i < self->frameListSize / sizeof(UInt16); i++) {
		((UInt16*)buffer)[i] = 0;
	}

	if ((NUM_ISUB_FRAME_LISTS - 1) == self->currentFrameList) {
		if (TRUE == self->iSubRunning) {
			self->loopCount++;
			self->currentByteOffset = 0;
		}
		self->currentFrameList = 0;
	} else {
		self->currentFrameList++;
		if (TRUE == self->iSubRunning) {
			self->currentByteOffset = self->currentFrameList * self->frameListSize;
		}
	}

	if (FALSE == self->shouldCloseStream) {
		frameListToWrite = self->currentFrameList + NUM_ISUB_FRAME_LISTS_TO_QUEUE - 1;
		if (frameListToWrite >= NUM_ISUB_FRAME_LISTS) {
			frameListToWrite -= NUM_ISUB_FRAME_LISTS;
		}
		self->WriteFrameList (frameListToWrite);
	}

	// aml - added for [3095619].  Keep track of time differences between calls to monitor scheduling latency
	// and set the need to sync flag if we are held off for more time than all of our queued data.
	AbsoluteTime					t_now;
	AbsoluteTime					t_old;
	UInt64							nanos_del;
	t_old.lo = self->mLastTime.lo;
	t_old.hi = self->mLastTime.hi;
	clock_get_uptime (&t_now);
	self->mLastTime.lo = t_now.lo;
	self->mLastTime.hi = t_now.hi;
	SUB_ABSOLUTETIME (&t_now, &t_old);		// t_now -= t_old;
	absolutetime_to_nanoseconds (t_now, &nanos_del);

#if DEBUGLOGGING
	nanos_del = nanos_del / (1000);
	if (nanos_del > 11000) {
		debugIOLog ("iSubEngine::WriteHandler - delta = %ld microseconds", (UInt32)(nanos_del));
	} else 	if (nanos_del < 9000) {
		debugIOLog ("iSubEngine::WriteHandler - delta = %ld microseconds", (UInt32)(nanos_del));
	}
#endif

	if (nanos_del > ((NUM_ISUB_FRAME_LISTS_TO_QUEUE * NUM_ISUB_FRAMES_PER_LIST + 1) * 1000 * 1000)) {
		self->mNeedToSync = true;
	}	
	// end added for [3095619]
	
Exit:
	if (TRUE == self->shouldCloseStream) {
		debugIOLog ("++AppleiSubEngine[%p]::WriteHandler () - stopping: %d", self, self->numUSBFrameListsNotOutstanding);
		self->numUSBFrameListsNotOutstanding++;
		if (self->numUSBFrameListsNotOutstanding == NUM_ISUB_FRAME_LISTS_TO_QUEUE) {
			debugIOLog ("iSub last write completed, closing streamInterface");
			if (NULL != self->thePipe) {
				self->thePipe->release ();
				self->thePipe = NULL;
			}

			self->streamInterface->close (self);
			self->streamOpened = FALSE;
			self->iSubUSBRunning = FALSE;
		}
	}
//	self->release ();
	return;
}

Generated by GNU enscript 1.6.4.