#include "AppleiSubEngine.h" #include "AppleUSBAudioCommon.h" #include "AppleUSBAudioLevelControl.h" #include "AppleUSBAudioMuteControl.h" #include "USBAudioObject.h" #include #include #include #define kiSubFeatureUnitID 2 #define kiSubMuteControlChannelNum 0 #define kiSubVolumeControlMasterChannelNum 1 // Has to be true or else the iSub loses the beginning of sounds if it has auto powered down #define NUM_POWER_STATES 2 #define LOGISUBDELAY FALSE #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 == mAudioEngine) { if ( ( (NULL != mMuteControl) || (NULL != miSubVolumeControl)) && (NULL != mAudioEngine)) { debugIOLog ("? AppleiSubEngine[%p]::closeiSub () - Removing iSub audio controls", this); if (NULL != miSubVolumeControl) { result = mAudioEngine->removeDefaultAudioControl (miSubVolumeControl); miSubVolumeControl->release (); miSubVolumeControl = NULL; #if DEBUGLOGGING if (kIOReturnSuccess != result) debugIOLog ("! AppleiSubEngine[%p]::closeiSub () - Error 0x%x removing left iSub control", this, result); #endif } if (NULL != mMuteControl) { result = mAudioEngine->removeDefaultAudioControl (mMuteControl); mMuteControl->release (); mMuteControl = NULL; #if DEBUGLOGGING if (kIOReturnSuccess != result) debugIOLog ("! AppleiSubEngine[%p]::closeiSub () - Error 0x%x removing mute iSub control", this, 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 mAudioEngine = NULL; #if DEBUGLOGGING } else { if (NULL == mMuteControl) {debugIOLog ("? AppleiSubEngine[%p]::closeiSub () - NULL == mMuteControl", this);} if (NULL == miSubVolumeControl) {debugIOLog ("? AppleiSubEngine[%p]::closeiSub () - NULL == miSubVolumeControl", this);} if (NULL == mAudioEngine) {debugIOLog ("? AppleiSubEngine[%p]::closeiSub () - NULL == mAudioEngine", this);} #endif } } else { debugIOLog ("! AppleiSubEngine[%p]::closeiSub () - The wrong client tried to close the iSub.", this); } debugIOLog ("- AppleiSubEngine[%p]::closeiSub (%p)", this, forClient); } void AppleiSubEngine::free (void) { UInt32 frameIndex; debugIOLog ("+ AppleiSubEngine[%p]::free ()", this); if (NULL != mPipe) { mPipe->release (); mPipe = NULL; } if (NULL != mSampleBufferDescriptor) { mSampleBufferDescriptor->release (); mSampleBufferDescriptor = NULL; } if (NULL != mSampleBuffer) { IOFree (mSampleBuffer, mBufferSize); mSampleBuffer = NULL; } for (frameIndex = 0; frameIndex < NUM_ISUB_FRAME_LISTS; frameIndex++) { if (NULL != mSoundBuffer[frameIndex]) { mSoundBuffer[frameIndex]->release (); mSoundBuffer[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 != mAudioEngine, Exit); mAudioEngine = OSDynamicCast (IOAudioEngine, forClient); FailIf (NULL == mAudioEngine, Exit); FailIf (NULL == theiSubRequestCloseRoutine, Exit); miSubRequestCloseRoutine = theiSubRequestCloseRoutine; // mAudioEngine->joinPMtree (this); // if (pm_vars != NULL) // { // registerPowerDriver (this, iSubPowerStates, NUM_POWER_STATES); // changePowerStateTo (1); // } mAudioEngine->pauseAudioEngine (); mAudioEngine->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. miSubVolumeControl = AppleUSBAudioLevelControl::create (kiSubFeatureUnitID, mControlInterface->GetInterfaceNumber (), VOLUME_CONTROL, kiSubVolumeControlMasterChannelNum, FALSE, (USBDeviceRequest)&deviceRequest, this, kIOAudioLevelControlSubTypeLFEVolume, kIOAudioControlUsageOutput); FailIf (NULL == miSubVolumeControl, Exit); mAudioEngine->addDefaultAudioControl (miSubVolumeControl); mMuteControl = AppleUSBAudioMuteControl::create (kiSubFeatureUnitID, mControlInterface->GetInterfaceNumber (), 0, (USBDeviceRequest)&deviceRequest, this, kIOAudioControlUsageOutput, kIOAudioToggleControlSubTypeLFEMute); FailIf (NULL == mMuteControl, Exit); mAudioEngine->addDefaultAudioControl (mMuteControl); mAudioEngine->completeConfigurationChange (); mAudioEngine->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; mTerminatingDriver = 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); mStreamInterface = OSDynamicCast (IOUSBInterface, provider); FailIf (NULL == mStreamInterface, Exit); mThisInterfaceNumber = mStreamInterface->GetInterfaceNumber (); debugIOLog ("? AppleiSubEngine[%p]::start () - AppleiSubEngine->mThisInterfaceNumber = %d", this, mThisInterfaceNumber); // aml 1.18.02 replaced constant with member mAltInterfaceID = mFormat.altInterface; PMinit (); mStreamInterface->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' mFrameListSize = CalculateNumSamplesPerBuffer (mFormat.outputSampleRate, NUM_ISUB_FRAMES_PER_LIST) * mFormat.numChannels * mFormat.bytesPerSample; debugIOLog ("? AppleiSubEngine[%p]::start () - format = %ldHz, %ld channels, %ld bits", this, mFormat.outputSampleRate, mFormat.numChannels, mFormat.bytesPerSample * 8); #if AML_DEBUGLOG debugIOLog ("? AppleiSubEngine[%p]::start () - handleOpen: mFrameListSize = %d", mFrameListSize, this); #endif debugIOLog ("? AppleiSubEngine[%p]::start () - mFrameListSize = %d", this, mFrameListSize); // aml 2.13.02 replaced kInputSampleRate with mOutputSampleRate // aml 2.13.02 added num channels and bit depth members, instead of '4' mBufferSize = CalculateNumSamplesPerBuffer (mFormat.outputSampleRate, NUM_ISUB_FRAMES_PER_LIST, NUM_ISUB_FRAME_LISTS) * mFormat.numChannels * mFormat.bytesPerSample; debugIOLog ("? AppleiSubEngine[%p]::start () - mBufferSize = %d", this, mBufferSize); mSampleBuffer = IOMallocAligned (mBufferSize, PAGE_SIZE); FailIf (NULL == mSampleBuffer, Exit); // aml 2.13.02 changed from clearing by longs to words for (i = 0; i < mBufferSize / sizeof(UInt16); i++) { ((UInt16*)mSampleBuffer)[i] = 0; } mSampleBufferDescriptor = IOMemoryDescriptor::withAddress (mSampleBuffer, mBufferSize, kIODirectionNone); FailIf (NULL == mSampleBufferDescriptor, Exit); FailIf (kIOReturnSuccess != PrepareFrameLists (mFrameListSize), Exit); // FailIf (TRUE == mStreamOpened, Exit); // debugIOLog ("after TRUE == mStreamOpened"); mShouldCloseStream = FALSE; FailIf (FALSE == mStreamInterface->open (this), Exit); mStreamOpened = TRUE; resultIOReturn = mStreamInterface->SetAlternateInterface (this, kRootAlternateSetting); FailIf (kIOReturnSuccess != resultIOReturn, Exit); // aml 1.18.02 replaced 4 (16 bit stereo alternate interface) with member resultIOReturn = mStreamInterface->SetAlternateInterface (this, mFormat.altInterface); FailIf (kIOReturnSuccess != resultIOReturn, Exit); // Acquire a PIPE for the isochronous stream. audioIsochEndpoint.type = kUSBIsoc; audioIsochEndpoint.direction = kUSBOut; mPipe = mStreamInterface->FindNextPipe (NULL, &audioIsochEndpoint); FailIf (NULL == mPipe, Exit); mPipe->retain (); // The iSub is a little funky and reseting it improves (but doesn't totally eliminate) funky behavior. // mStreamInterface->GetDevice()->ResetDevice (); // Start the iSub playing silence now mLoopCount = 0; mNumUSBFrameListsNotOutstanding = 0; numQueued = 0; mCurrentFrameList = 0; mCurrentByteOffset = 0; mFirstFrame = mStreamInterface->GetDevice()->GetBus()->GetFrameNumber () + kMinimumiSubFrameOffset; for (frameListNum = mCurrentFrameList; numQueued < NUM_ISUB_FRAME_LISTS_TO_QUEUE; frameListNum++) { resultIOReturn = WriteFrameList (frameListNum); FailIf (kIOReturnSuccess != resultIOReturn, Exit); numQueued++; } debugIOLog ("? AppleiSubEngine[%p]::start () - writeFrameList resultIOReturn = 0x%lx", this, resultIOReturn); // Find control interface on iSub device findRequest.bInterfaceClass = 1; findRequest.bInterfaceSubClass = 1; findRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; findRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare; mControlInterface = mStreamInterface->GetDevice()->FindNextInterface (NULL, &findRequest); FailIf (NULL == mControlInterface, 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 ()); mTerminatingDriver = TRUE; if (NULL != mAudioEngine) { (miSubRequestCloseRoutine)(mAudioEngine); } if (FALSE == miSubUSBRunning) { // close our stream interface and go away if ( (mStreamInterface != NULL) && (mStreamInterface == provider)) { debugIOLog ("? AppleiSubEngine[%p]::willTerminate () - Closing iSub down", this); if (NULL != mPipe) { mPipe->release (); mPipe = NULL; } mStreamInterface->close (this); mStreamOpened = FALSE; } } else { mShouldCloseStream = 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 ("? AppleiSubEngine[%p]::setPowerState () - iSub is going to sleep. Setting mIsSleeping = TRUE ...", this); mIsSleeping = TRUE; StopiSub (); result = IOPMNoErr; break; case 1: // Power on state if (mIsSleeping) { debugIOLog ("? AppleiSubEngine[%p]::setPowerState () - iSub is waking..."); } mIsSleeping = FALSE; result = IOPMNoErr; break; default: result = IOPMNoSuchState; } } debugIOLog("- AppleiSubEngine[%p]::setPowerState (%lu, %p) = 0x%x", this, powerStateOrdinal, device, result); 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->mStreamInterface) && (request) && (FALSE == self->mTerminatingDriver)) { result = self->mStreamInterface->DeviceRequest (request, completion); } debugIOLog ("- AppleiSubEngine[%p]::deviceRequest (%p, %p) = 0x%x", self, request, completion, result); return result; } UInt32 AppleiSubEngine::GetCurrentByteCount (void) { return mCurrentByteOffset; } UInt32 AppleiSubEngine::GetCurrentLoopCount (void) { return mLoopCount; } IOMemoryDescriptor * AppleiSubEngine::GetSampleBuffer (void) { return mSampleBufferDescriptor; } IOReturn AppleiSubEngine::PrepareFrameLists (UInt32 frameListSize) { IOReturn result; UInt32 frameIndex; result = kIOReturnError; for (frameIndex = 0; frameIndex < NUM_ISUB_FRAME_LISTS; frameIndex++) { mUSBCompletion[frameIndex].target = (void *)this; mUSBCompletion[frameIndex].parameter = (void *)((UInt8 *)mSampleBuffer + (frameIndex * frameListSize)); // pointer into the buffer that is the start of this frame list mUSBCompletion[frameIndex].action = WriteHandler; mSoundBuffer[frameIndex] = IOMemoryDescriptor::withAddress ((UInt8 *)mUSBCompletion[frameIndex].parameter, frameListSize, kIODirectionNone); FailIf (NULL == mSoundBuffer[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); // [rdar://4206816] If sleeping, give the power thread a chance to wake it up (maximum of 500 ms). /thw if (mIsSleeping) { for (UInt8 i = 0; i < 5 && mIsSleeping; i++) { IOSleep (100); } } FailWithAction (TRUE == mIsSleeping, resultCode = kIOReturnOffline, Exit); resultCode = kIOReturnError; miSubRunning = TRUE; #if ABORT_PIPE_ON_START FailIf (NULL == mPipe, Exit); mPipe->Abort (); // let's kill all outstanding IO and start right back at the beginning miSubUSBRunning = FALSE; #else mLoopCount = 0xFFFFFFFF; // so that it will go to 0 in the write completion routine when the frames are aborted mCurrentFrameList = NUM_ISUB_FRAME_LISTS - 1; #endif // ABORT_PIPE_ON_START resultCode = kIOReturnSuccess; if (FALSE == miSubUSBRunning) { mLoopCount = 0; mNumUSBFrameListsNotOutstanding = 0; numQueued = 0; mCurrentFrameList = 0; mCurrentByteOffset = 0; FailIf (NULL == mStreamInterface, Exit); mFirstFrame = mStreamInterface->GetDevice()->GetBus()->GetFrameNumber () + kMinimumiSubFrameOffset; for (frameListNum = mCurrentFrameList; numQueued < NUM_ISUB_FRAME_LISTS_TO_QUEUE; frameListNum++) { resultCode = WriteFrameList (frameListNum); FailIf (kIOReturnSuccess != resultCode, Exit); numQueued++; } } Exit: debugIOLog ("- AppleiSubEngine[%p]::StartiSub (), result = 0x%x", this, resultCode); return resultCode; } IOReturn AppleiSubEngine::StopiSub (void) { debugIOLog ("+ AppleiSubEngine[%p]::StopiSub ()", this); miSubRunning = 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++) { mFrames[firstFrame + frameIndex].frStatus = -1; if ((frameIndex % additionalSampleFrameFreq) == (UInt16)(additionalSampleFrameFreq - 1)) { mFrames[firstFrame + frameIndex].frReqCount = alternateFrameSize; } else { mFrames[firstFrame + frameIndex].frReqCount = averageFrameSize; } mFrames[firstFrame + frameIndex].frActCount = 0; } } else { for (frameIndex = 0; frameIndex < NUM_ISUB_FRAMES_PER_LIST; frameIndex++) { mFrames[firstFrame + frameIndex].frStatus = -1; mFrames[firstFrame + frameIndex].frReqCount = averageFrameSize; mFrames[firstFrame + frameIndex].frActCount = 0; } } FailIf (NULL == mPipe, Exit); // retain (); // Don't want the driver being terminated until our completion routine runs. result = mPipe->Write (mSoundBuffer[frameListNum], mFirstFrame, NUM_ISUB_FRAMES_PER_LIST, &mFrames[firstFrame], &mUSBCompletion[frameListNum]); if (result != kIOReturnSuccess) { if (mStreamInterface) { debugIOLog ("! AppleiSubEngine[%p]::WriteFrameList (%d) - error writing to pipe at frame %lu - current = %lu: 0x%x", this, frameListNum, (UInt32)mFirstFrame, (UInt32)mStreamInterface->GetDevice()->GetBus()->GetFrameNumber(), result); } miSubUSBRunning = FALSE; } else { mFirstFrame += NUM_ISUB_FRAMES_PER_LIST; miSubUSBRunning = 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->mIsSleeping, Exit); if (result != kIOReturnSuccess) { if (result != kIOReturnAborted) { // Currently AppleUSBAudio aborts the pipe any time the stream is started. We don't need to see the error for this, as it is expected. debugIOLog ("! AppleiSubEngine::WriteHandler () - error 0x%x", result); } FailIf (NULL == self->mStreamInterface, Exit); currentUSBFrame = self->mStreamInterface->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->mFirstFrame <= currentUSBFrame) { self->mFirstFrame = currentUSBFrame + kMinimumiSubFrameOffset; debugIOLog ("! AppleiSubEngine::WriteHandler () - Skipping ahead to frame %ld", self->mFirstFrame); } 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->mFrameListSize / sizeof(UInt16); i++) { ((UInt16*)buffer)[i] = 0; } if ((NUM_ISUB_FRAME_LISTS - 1) == self->mCurrentFrameList) { if (TRUE == self->miSubRunning) { self->mLoopCount++; self->mCurrentByteOffset = 0; } self->mCurrentFrameList = 0; } else { self->mCurrentFrameList++; if (TRUE == self->miSubRunning) { self->mCurrentByteOffset = self->mCurrentFrameList * self->mFrameListSize; } } if (FALSE == self->mShouldCloseStream) { frameListToWrite = self->mCurrentFrameList + 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 = self->mLastTime; clock_get_uptime (&t_now); self->mLastTime = t_now; SUB_ABSOLUTETIME (&t_now, &t_old); // t_now -= t_old; absolutetime_to_nanoseconds (t_now, &nanos_del); #if LOGISUBDELAY nanos_del = nanos_del / (1000); if (nanos_del > 11000) { debugIOLog ("? AppleiSubEngine::WriteHandler () - delta = %ld microseconds", (UInt32)(nanos_del)); } else if (nanos_del < 9000) { debugIOLog ("? AppleiSubEngine::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->mShouldCloseStream) { debugIOLog ("? AppleiSubEngine[%p]::WriteHandler () - stopping: %d", self, self->mNumUSBFrameListsNotOutstanding); self->mNumUSBFrameListsNotOutstanding++; if (self->mNumUSBFrameListsNotOutstanding == NUM_ISUB_FRAME_LISTS_TO_QUEUE) { debugIOLog ("? AppleiSubEngine[%p]::WriteHandler () - iSub last write completed, closing mStreamInterface", self); if (NULL != self->mPipe) { self->mPipe->release (); self->mPipe = NULL; } self->mStreamInterface->close (self); self->mStreamOpened = FALSE; self->miSubUSBRunning = FALSE; } } // self->release (); return; }