#define DEBUGTIMESTAMPS FALSE #include "AppleDBDMAAudioDMAEngine.h" #include "AppleOnboardAudio.h" #include #include #include #include #include "AudioHardwareUtilities.h" #include "AppleiSubEngine.h" extern "C" { extern vm_offset_t phystokv(vm_offset_t pa); }; #define super IOAudioEngine OSDefineMetaClassAndStructors(AppleDBDMAAudioDMAEngine, super) const int AppleDBDMAAudioDMAEngine::kDBDMADeviceIndex = 0; const int AppleDBDMAAudioDMAEngine::kDBDMAOutputIndex = 1; const int AppleDBDMAAudioDMAEngine::kDBDMAInputIndex = 2; bool AppleDBDMAAudioDMAEngine::init(OSDictionary *properties, IOService *theDeviceProvider, bool hasInput, UInt32 nBlocks, UInt32 bSize, UInt32 rate, UInt16 bitDepth, UInt16 numChannels) { IOAudioSampleRate initialSampleRate; IOMemoryMap *map; Boolean result; CLOG("+ AppleDBDMAAudioDMAEngine::init\n"); result = FALSE; // Ususal check FailIf (FALSE == super::init (properties), Exit); FailIf (NULL == theDeviceProvider, Exit); // create the memory places for DMA map = theDeviceProvider->mapDeviceMemoryWithIndex(AppleDBDMAAudioDMAEngine::kDBDMAOutputIndex); FailIf (NULL == map, Exit); ioBaseDMAOutput = (IODBDMAChannelRegisters *) map->getVirtualAddress(); if(hasInput) { map = theDeviceProvider->mapDeviceMemoryWithIndex(AppleDBDMAAudioDMAEngine::kDBDMAInputIndex); FailIf (NULL == map, Exit); ioBaseDMAInput = (IODBDMAChannelRegisters *) map->getVirtualAddress(); } else { ioBaseDMAInput = 0; } dmaCommandBufferIn = 0; dmaCommandBufferOut = 0; commandBufferSize = 0; interruptEventSource = 0; numBlocks = nBlocks; blockSize = bSize; setSampleOffset(kMinimumLatency); setNumSampleFramesPerBuffer(numBlocks * blockSize / sizeof (float)); initialSampleRate.whole = rate; initialSampleRate.fraction = 0; setSampleRate(&initialSampleRate); mInputDualMonoMode = e_Mode_Disabled; // aml 6.17.02 srcPhase = 1.0; // aml 3.5.02 srcState = 0.0; // aml 3.6.02 // aml 5.10.02 mUseSoftwareInputGain = false; mInputGainLPtr = NULL; mInputGainRPtr = NULL; result = TRUE; Exit: CLOG("- AppleDBDMAAudioDMAEngine::init\n"); return result; } void AppleDBDMAAudioDMAEngine::setSampleLatencies (UInt32 outputLatency, UInt32 inputLatency) { setOutputSampleLatency (outputLatency); setInputSampleLatency (inputLatency); } void AppleDBDMAAudioDMAEngine::free() { if (interruptEventSource) { interruptEventSource->release(); interruptEventSource = 0; } if (NULL != dmaCommandBufferInMemDescriptor) { dmaCommandBufferInMemDescriptor->release (); dmaCommandBufferInMemDescriptor = NULL; } if (NULL != dmaCommandBufferOutMemDescriptor) { dmaCommandBufferOutMemDescriptor->release (); dmaCommandBufferOutMemDescriptor = NULL; } if (NULL != sampleBufferInMemDescriptor) { sampleBufferInMemDescriptor->release (); sampleBufferInMemDescriptor = NULL; } if (NULL != sampleBufferOutMemDescriptor) { sampleBufferOutMemDescriptor->release (); sampleBufferOutMemDescriptor = NULL; } if (NULL != stopCommandMemDescriptor) { stopCommandMemDescriptor->release (); stopCommandMemDescriptor = NULL; } if (dmaCommandBufferOut && (commandBufferSize > 0)) { IOFreeAligned(dmaCommandBufferOut, commandBufferSize); dmaCommandBufferOut = 0; } if (dmaCommandBufferIn && (commandBufferSize > 0)) { IOFreeAligned(dmaCommandBufferIn, commandBufferSize); dmaCommandBufferOut = 0; } if (NULL != iSubEngineNotifier) { iSubEngineNotifier->remove (); iSubEngineNotifier = NULL; } if (NULL != iSubAttach) { iSubAttach->release (); iSubAttach = NULL; } if (NULL != lowFreqSamples) { IOFree (lowFreqSamples, (numBlocks * blockSize) * sizeof (float)); } if (NULL != highFreqSamples) { IOFree (highFreqSamples, (numBlocks * blockSize) * sizeof (float)); } super::free(); } bool AppleDBDMAAudioDMAEngine::initHardware(IOService *provider) { vm_offset_t offset; vm_offset_t sampleBufOut; vm_offset_t sampleBufIn; IOPhysicalAddress commandBufferPhys; IOPhysicalAddress sampleBufferPhys; IOPhysicalAddress stopCommandPhys; UInt32 blockNum; UInt32 dmaCommand = 0; Boolean doInterrupt = false; UInt32 interruptIndex; IOWorkLoop * workLoop; IOAudioStream * stream; Boolean result; result = FALSE; sampleBufIn = NULL; IOAudioStreamFormat format = { 2, kIOAudioStreamSampleFormatLinearPCM, kIOAudioStreamNumericRepresentationSignedInt, 16, 16, kIOAudioStreamAlignmentHighByte, kIOAudioStreamByteOrderBigEndian, true }; // rbm 7.15.2002 keep a copy for user client dbdmaFormat.fNumChannels = format.fNumChannels; dbdmaFormat.fSampleFormat = format.fSampleFormat; dbdmaFormat.fNumericRepresentation = format.fNumericRepresentation; dbdmaFormat.fBitDepth = format.fBitDepth; dbdmaFormat.fBitWidth = format.fBitWidth; dbdmaFormat.fAlignment = format.fAlignment; dbdmaFormat.fIsMixable = format.fIsMixable; dbdmaFormat.fDriverTag = format.fDriverTag; DEBUG_IOLOG("+ AppleDBDMAAudioDMAEngine::initHardware()\n"); ourProvider = provider; fBadCmd = 0; fBadResult = 0; FailIf (!super::initHardware(provider), Exit); // allocate the memory for the buffer sampleBufOut = (vm_offset_t)IOMallocAligned(round_page(numBlocks * blockSize), PAGE_SIZE); if(ioBaseDMAInput) sampleBufIn = (vm_offset_t)IOMallocAligned(round_page(numBlocks * blockSize), PAGE_SIZE); // create the streams stream = new IOAudioStream; if (stream) { const IOAudioSampleRate *rate; rate = getSampleRate(); stream->initWithAudioEngine(this, kIOAudioStreamDirectionOutput, 1, 0, 0); stream->setSampleBuffer((void *)sampleBufOut, numBlocks * blockSize); stream->addAvailableFormat(&format, rate, rate); stream->setFormat(&format); addAudioStream(stream); stream->release(); } if(ioBaseDMAInput) { stream = new IOAudioStream; if (stream) { const IOAudioSampleRate *rate; rate = getSampleRate(); stream->initWithAudioEngine(this, kIOAudioStreamDirectionInput, 1, 0, 0); stream->setSampleBuffer((void *)sampleBufIn, numBlocks * blockSize); stream->addAvailableFormat(&format, rate, rate); stream->setFormat(&format); addAudioStream(stream); stream->release(); } } FailIf (!status || !sampleBufOut, Exit); if(ioBaseDMAInput) FailIf (!sampleBufIn, Exit); // create the DMA output part commandBufferSize = (numBlocks + 1) * sizeof(IODBDMADescriptor); dmaCommandBufferOut = (IODBDMADescriptor *)IOMallocAligned(commandBufferSize, 32); // needs to be more than 4 byte aligned FailIf (!dmaCommandBufferOut, Exit); dmaCommandBufferOutMemDescriptor = IOMemoryDescriptor::withAddress (dmaCommandBufferOut, commandBufferSize, kIODirectionOut); FailIf (NULL == dmaCommandBufferOutMemDescriptor, Exit); sampleBufferOutMemDescriptor = IOMemoryDescriptor::withAddress ((void *)sampleBufOut, round_page (numBlocks * blockSize), kIODirectionOut); FailIf (NULL == sampleBufferOutMemDescriptor, Exit); stopCommandMemDescriptor = IOMemoryDescriptor::withAddress (&dmaCommandBufferOut[numBlocks], sizeof (IODBDMADescriptor *), kIODirectionOut); FailIf (NULL == stopCommandMemDescriptor, Exit); commandBufferPhys = dmaCommandBufferOutMemDescriptor->getPhysicalAddress (); FailIf (NULL == commandBufferPhys, Exit); sampleBufferPhys = sampleBufferOutMemDescriptor->getPhysicalAddress (); FailIf (NULL == sampleBufferPhys, Exit); stopCommandPhys = stopCommandMemDescriptor->getPhysicalAddress (); FailIf (NULL == stopCommandPhys, Exit); offset = 0; dmaCommand = kdbdmaOutputMore; interruptIndex = kDBDMAOutputIndex; // install an interrupt handler only on the Ouput size of it workLoop = getWorkLoop(); FailIf (!workLoop, Exit); interruptEventSource = IOFilterInterruptEventSource::filterInterruptEventSource(this, AppleDBDMAAudioDMAEngine::interruptHandler, AppleDBDMAAudioDMAEngine::interruptFilter, audioDevice->getProvider(), interruptIndex); FailIf (!interruptEventSource, Exit); workLoop->addEventSource(interruptEventSource); // create the DMA program for (blockNum = 0; blockNum < numBlocks; blockNum++) { IOPhysicalAddress cmdDest; if (offset >= PAGE_SIZE) { sampleBufferPhys = sampleBufferOutMemDescriptor->getPhysicalSegment (blockNum * blockSize, 0); FailIf (NULL == sampleBufferPhys, Exit); offset = 0; } // This code assumes that the size of the IODBDMADescriptor divides evenly into the page size // If this is the last block, branch to the first block if (blockNum == (numBlocks - 1)) { cmdDest = commandBufferPhys; doInterrupt = true; // Else if the next block starts on a page boundry, branch to it } else if ((((blockNum + 1) * sizeof(IODBDMADescriptor)) % PAGE_SIZE) == 0) { cmdDest = dmaCommandBufferOutMemDescriptor->getPhysicalSegment ((blockNum + 1) * sizeof(IODBDMADescriptor), 0); FailIf (NULL == cmdDest, Exit); // No branch in the common case } else { cmdDest = 0; } if (cmdDest) { IOMakeDBDMADescriptorDep(&dmaCommandBufferOut[blockNum], dmaCommand, kdbdmaKeyStream0, doInterrupt ? kdbdmaIntAlways : kdbdmaIntNever, kdbdmaBranchAlways, kdbdmaWaitNever, blockSize, sampleBufferPhys + offset, cmdDest); } else { IOMakeDBDMADescriptorDep(&dmaCommandBufferOut[blockNum], dmaCommand, kdbdmaKeyStream0, kdbdmaIntNever, kdbdmaBranchIfTrue, kdbdmaWaitNever, blockSize, sampleBufferPhys + offset, stopCommandPhys); } offset += blockSize; } IOMakeDBDMADescriptor(&dmaCommandBufferOut[blockNum], kdbdmaStop, kdbdmaKeyStream0, kdbdmaIntNever, kdbdmaBranchNever, kdbdmaWaitNever, 0, 0); // create the DMA input code if(ioBaseDMAInput) { dmaCommandBufferIn = (IODBDMADescriptor *)IOMallocAligned(commandBufferSize, 32); // needs to be more than 4 byte aligned FailIf (!dmaCommandBufferIn, Exit); dmaCommandBufferInMemDescriptor = IOMemoryDescriptor::withAddress (dmaCommandBufferIn, commandBufferSize, kIODirectionOut); FailIf (NULL == dmaCommandBufferInMemDescriptor, Exit); sampleBufferInMemDescriptor = IOMemoryDescriptor::withAddress ((void *)sampleBufIn, round_page (numBlocks * blockSize), kIODirectionIn); FailIf (NULL == sampleBufferInMemDescriptor, Exit); stopCommandMemDescriptor = IOMemoryDescriptor::withAddress (&dmaCommandBufferIn[numBlocks], sizeof (IODBDMADescriptor *), kIODirectionOut); FailIf (NULL == stopCommandMemDescriptor, Exit); commandBufferPhys = dmaCommandBufferInMemDescriptor->getPhysicalAddress (); FailIf (NULL == commandBufferPhys, Exit); sampleBufferPhys = sampleBufferInMemDescriptor->getPhysicalAddress (); FailIf (NULL == sampleBufferPhys, Exit); stopCommandPhys = stopCommandMemDescriptor->getPhysicalAddress (); FailIf (NULL == stopCommandPhys, Exit); doInterrupt = false; offset = 0; dmaCommand = kdbdmaInputMore; for (blockNum = 0; blockNum < numBlocks; blockNum++) { IOPhysicalAddress cmdDest; if (offset >= PAGE_SIZE) { sampleBufferPhys = sampleBufferInMemDescriptor->getPhysicalSegment (blockNum * blockSize, 0); FailIf (NULL == sampleBufferPhys, Exit); offset = 0; } // This code assumes that the size of the IODBDMADescriptor // divides evenly into the page size // If this is the last block, branch to the first block if (blockNum == (numBlocks - 1)) { cmdDest = commandBufferPhys; // doInterrupt = true; // Else if the next block starts on a page boundry, branch to it } else if ((((blockNum + 1) * sizeof(IODBDMADescriptor)) % PAGE_SIZE) == 0) { cmdDest = dmaCommandBufferInMemDescriptor->getPhysicalSegment ((blockNum + 1) * sizeof(IODBDMADescriptor), 0); FailIf (NULL == cmdDest, Exit); // No branch in the common case } else { cmdDest = 0; } if (cmdDest) { IOMakeDBDMADescriptorDep(&dmaCommandBufferIn[blockNum], dmaCommand, kdbdmaKeyStream0, doInterrupt ? kdbdmaIntAlways : kdbdmaIntNever, kdbdmaBranchAlways, kdbdmaWaitNever, blockSize, sampleBufferPhys + offset, cmdDest); } else { IOMakeDBDMADescriptorDep(&dmaCommandBufferIn[blockNum], dmaCommand, kdbdmaKeyStream0, kdbdmaIntNever, kdbdmaBranchIfTrue, kdbdmaWaitNever, blockSize, sampleBufferPhys + offset, stopCommandPhys); } offset += blockSize; } IOMakeDBDMADescriptor(&dmaCommandBufferIn[blockNum], kdbdmaStop, kdbdmaKeyStream0, kdbdmaIntNever, kdbdmaBranchNever, kdbdmaWaitNever, 0, 0); } iSubBufferMemory = NULL; iSubEngine = NULL; // Set up a control that sound prefs can set to tell us if we should install our notifier or not iSubAttach = IOAudioToggleControl::create (FALSE, kIOAudioControlChannelIDAll, kIOAudioControlChannelNameAll, 0, kIOAudioToggleControlSubTypeiSubAttach, kIOAudioControlUsageOutput); if (NULL != iSubAttach) { addDefaultAudioControl (iSubAttach); iSubAttach->setValueChangeHandler ((IOAudioControl::IntValueChangeHandler)iSubAttachChangeHandler, this); iSubAttach->release (); } result = TRUE; Exit: DEBUG_IOLOG("- AppleDBDMAAudioDMAEngine::initHardware()\n"); return result; } void AppleDBDMAAudioDMAEngine::stop(IOService *provider) { IOWorkLoop *workLoop; DEBUG3_IOLOG(" + AppleDBDMAAudioDMAEngine[%p]::stop(%p)\n", this, provider); if (interruptEventSource) { workLoop = getWorkLoop(); if (workLoop) { workLoop->removeEventSource(interruptEventSource); } } super::stop(provider); stopAudioEngine(); DEBUG3_IOLOG(" - AppleDBDMAAudioDMAEngine[%p]::stop(%p)\n", this, provider); } IOReturn AppleDBDMAAudioDMAEngine::performAudioEngineStart() { IOPhysicalAddress commandBufferPhys; IOReturn result; debugIOLog(" + AppleDBDMAAudioDMAEngine::performAudioEngineStart()\n"); result = kIOReturnError; FailIf (!ioBaseDMAOutput || !dmaCommandBufferOut || !status || !interruptEventSource, Exit); flush_dcache((vm_offset_t)dmaCommandBufferOut, commandBufferSize, false); if(ioBaseDMAInput) flush_dcache((vm_offset_t)dmaCommandBufferIn, commandBufferSize, false); filterState.xl_1 = 0.0; filterState.xr_1 = 0.0; filterState.xl_2 = 0.0; filterState.xr_2 = 0.0; filterState.yl_1 = 0.0; filterState.yr_1 = 0.0; filterState.yl_2 = 0.0; filterState.yr_2 = 0.0; // aml 2.14.02 added for 4th order filter filterState2.xl_1 = 0.0; filterState2.xr_1 = 0.0; filterState2.xl_2 = 0.0; filterState2.xr_2 = 0.0; filterState2.yl_1 = 0.0; filterState2.yr_1 = 0.0; filterState2.yl_2 = 0.0; filterState2.yr_2 = 0.0; // aml 2.18.02 added for 4th order filter phase compensator phaseCompState.xl_1 = 0.0; phaseCompState.xr_1 = 0.0; phaseCompState.xl_2 = 0.0; phaseCompState.xr_2 = 0.0; phaseCompState.yl_1 = 0.0; phaseCompState.yr_1 = 0.0; phaseCompState.yl_2 = 0.0; phaseCompState.yr_2 = 0.0; if (NULL != iSubEngine) { startiSub = TRUE; needToSync = TRUE; } interruptEventSource->enable(); // add the time stamp take to test takeTimeStamp(false); // start the input DMA first if(ioBaseDMAInput) { IOSetDBDMAChannelControl(ioBaseDMAInput, IOClearDBDMAChannelControlBits(kdbdmaS0)); IOSetDBDMABranchSelect(ioBaseDMAInput, IOSetDBDMAChannelControlBits(kdbdmaS0)); commandBufferPhys = dmaCommandBufferInMemDescriptor->getPhysicalAddress (); FailIf (NULL == commandBufferPhys, Exit); IODBDMAStart(ioBaseDMAInput, (IODBDMADescriptor *)commandBufferPhys); } IOSetDBDMAChannelControl(ioBaseDMAOutput, IOClearDBDMAChannelControlBits(kdbdmaS0)); IOSetDBDMABranchSelect(ioBaseDMAOutput, IOSetDBDMAChannelControlBits(kdbdmaS0)); commandBufferPhys = dmaCommandBufferOutMemDescriptor->getPhysicalAddress (); FailIf (NULL == commandBufferPhys, Exit); IODBDMAStart(ioBaseDMAOutput, (IODBDMADescriptor *)commandBufferPhys); dmaRunState = TRUE; // rbm 7.12.02 added for user client support result = kIOReturnSuccess; debugIOLog(" - AppleDBDMAAudioDMAEngine::performAudioEngineStart()\n"); Exit: return result; } IOReturn AppleDBDMAAudioDMAEngine::restartOutputIfFailure(){ IOPhysicalAddress commandBufferPhys; IOReturn result; result = kIOReturnError; FailIf (!ioBaseDMAOutput || !dmaCommandBufferOut || !status || !interruptEventSource, Exit); #if DEBUGLOG IOLog ("Restarting DMA\n"); #endif flush_dcache((vm_offset_t)dmaCommandBufferOut, commandBufferSize, false); if (NULL != iSubEngine) { needToSync = TRUE; startiSub = TRUE; restartedDMA = TRUE; } interruptEventSource->enable(); // add the time stamp take to test takeTimeStamp(false); IOSetDBDMAChannelControl(ioBaseDMAOutput, IOClearDBDMAChannelControlBits(kdbdmaS0)); IOSetDBDMABranchSelect(ioBaseDMAOutput, IOSetDBDMAChannelControlBits(kdbdmaS0)); commandBufferPhys = dmaCommandBufferOutMemDescriptor->getPhysicalAddress (); FailIf (NULL == commandBufferPhys, Exit); IODBDMAStart(ioBaseDMAOutput, (IODBDMADescriptor *)commandBufferPhys); result = kIOReturnSuccess; Exit: return result; } IOReturn AppleDBDMAAudioDMAEngine::performAudioEngineStop() { UInt16 attemptsToStop = 1000; debugIOLog("+ AppleDBDMAAudioDMAEngine::performAudioEngineStop()\n"); if (NULL != iSubEngine) { iSubEngine->StopiSub (); needToSync = TRUE; } if (!interruptEventSource) { return kIOReturnError; } interruptEventSource->disable(); // stop the output IOSetDBDMAChannelControl(ioBaseDMAOutput, IOSetDBDMAChannelControlBits(kdbdmaS0)); while ((IOGetDBDMAChannelStatus(ioBaseDMAOutput) & kdbdmaActive) && (attemptsToStop--)) { eieio(); IOSleep(1); } IODBDMAStop(ioBaseDMAOutput); IODBDMAReset(ioBaseDMAOutput); // stop the input if(ioBaseDMAInput){ IOSetDBDMAChannelControl(ioBaseDMAInput, IOSetDBDMAChannelControlBits(kdbdmaS0)); while ((IOGetDBDMAChannelStatus(ioBaseDMAInput) & kdbdmaActive) && (attemptsToStop--)) { eieio(); IOSleep(1); } IODBDMAStop(ioBaseDMAInput); IODBDMAReset(ioBaseDMAInput); } dmaRunState = FALSE; // rbm 7.12.02 added for user client support interruptEventSource->enable(); DEBUG_IOLOG("- AppleDBDMAAudioDMAEngine::performAudioEngineStop()\n"); return kIOReturnSuccess; } bool AppleDBDMAAudioDMAEngine::filterInterrupt(int index) { // check to see if this interupt is because the DMA went bad UInt32 result = IOGetDBDMAChannelStatus(ioBaseDMAOutput); UInt32 cmd = IOGetDBDMACommandPtr(ioBaseDMAOutput); if (!(result & kdbdmaActive)) { fBadResult = result; fBadCmd = cmd; } if (status) { // test the takeTimeStamp :it will increment the fCurrentLoopCount and time stamp it with the time now takeTimeStamp(); } return false; } bool AppleDBDMAAudioDMAEngine::interruptFilter(OSObject *owner, IOFilterInterruptEventSource *source) { register AppleDBDMAAudioDMAEngine *dmaEngine = (AppleDBDMAAudioDMAEngine *)owner; bool result = true; if (dmaEngine) { result = dmaEngine->filterInterrupt(source->getIntIndex()); } return result; } void AppleDBDMAAudioDMAEngine::interruptHandler(OSObject *owner, IOInterruptEventSource *source, int count) { return; } UInt32 AppleDBDMAAudioDMAEngine::getCurrentSampleFrame() { UInt32 currentBlock = 0; if (ioBaseDMAOutput) { vm_offset_t currentDMACommandPhys, currentDMACommand; currentDMACommandPhys = (vm_offset_t)IOGetDBDMAChannelRegister(ioBaseDMAOutput, commandPtrLo); currentDMACommand = phystokv(currentDMACommandPhys); if ((UInt32)currentDMACommand > (UInt32)dmaCommandBufferOut) { currentBlock = ((UInt32)currentDMACommand - (UInt32)dmaCommandBufferOut) / sizeof(IODBDMADescriptor); } } return currentBlock * blockSize / 4; // 4 bytes per frame - 2 per sample * 2 channels - BIG HACK } // This gets called when a new audio stream needs to be mixed into an already playing audio stream void AppleDBDMAAudioDMAEngine::resetClipPosition (IOAudioStream *audioStream, UInt32 clipSampleFrame) { if ((NULL != iSubBufferMemory) && (NULL != iSubEngine)) { srcPhase = 1.0; // aml 3.5.02 srcState = 0.0; // aml 3.6.02 // start the filter over again since old filter state is invalid filterState.xl_1 = 0.0; filterState.xr_1 = 0.0; filterState.xl_2 = 0.0; filterState.xr_2 = 0.0; filterState.yl_1 = 0.0; filterState.yr_1 = 0.0; filterState.yl_2 = 0.0; filterState.yr_2 = 0.0; // aml 2.14.02 added for 4th order filter filterState2.xl_1 = 0.0; filterState2.xr_1 = 0.0; filterState2.xl_2 = 0.0; filterState2.xr_2 = 0.0; filterState2.yl_1 = 0.0; filterState2.yr_1 = 0.0; filterState2.yl_2 = 0.0; filterState2.yr_2 = 0.0; // aml 2.18.02 added for 4th order filter phase compensator phaseCompState.xl_1 = 0.0; phaseCompState.xr_1 = 0.0; phaseCompState.xl_2 = 0.0; phaseCompState.xr_2 = 0.0; phaseCompState.yl_1 = 0.0; phaseCompState.yr_1 = 0.0; phaseCompState.yl_2 = 0.0; phaseCompState.yr_2 = 0.0; #if DEBUGLOG IOLog ("+resetClipPosition: iSubBufferOffset=%ld, previousClippedToFrame=%ld, clipSampleFrame=%ld\n", iSubBufferOffset, previousClippedToFrame, clipSampleFrame); #endif if (previousClippedToFrame < clipSampleFrame) { // Resetting the clip point backwards around the end of the buffer // aml 3.12.02 changed to iSub num channels clipAdjustment = (getNumSampleFramesPerBuffer () - clipSampleFrame + previousClippedToFrame) * iSubEngine->GetNumChannels(); } else { // aml 3.12.02 changed to iSub num channels clipAdjustment = (previousClippedToFrame - clipSampleFrame) * iSubEngine->GetNumChannels(); } #if DEBUGLOG if (clipAdjustment < kMinimumLatency) { IOLog ("resetClipPosition: 44.1 clipAdjustment < min, clipAdjustment=%ld\n", clipAdjustment); } #endif // aml 3.21.02, adjust for new sample rate clipAdjustment = (clipAdjustment * 1000) / ((1000 * getSampleRate()->whole) / iSubEngine->GetSampleRate()); iSubBufferOffset -= clipAdjustment; #if DEBUGLOG if (clipAdjustment > (iSubBufferMemory->getLength () / 2)) { IOLog ("resetClipPosition: clipAdjustment > iSub buffer size, clipAdjustment=%ld\n", clipAdjustment); } #endif if (iSubBufferOffset < 0) { iSubBufferOffset += (iSubBufferMemory->getLength () / 2); iSubLoopCount--; } previousClippedToFrame = clipSampleFrame; justResetClipPosition = TRUE; #if DEBUGLOG IOLog ("-resetClipPosition: iSubBufferOffset=%ld, previousClippedToFrame=%ld\n", iSubBufferOffset, previousClippedToFrame); #endif } } extern "C" { UInt32 CalculateOffset (UInt64 nanoseconds, UInt32 sampleRate); IOReturn clipAppleDBDMAToOutputStream(const void *mixBuf, void *sampleBuf, UInt32 firstSampleFrame, UInt32 numSampleFrames, const IOAudioStreamFormat *streamFormat); IOReturn clipAppleDBDMAToOutputStreamInvertRightChannel(const void *mixBuf, void *sampleBuf, UInt32 firstSampleFrame, UInt32 numSampleFrames, const IOAudioStreamFormat *streamFormat); IOReturn clipAppleDBDMAToOutputStreamMixRightChannel(const void *mixBuf, void *sampleBuf, UInt32 firstSampleFrame, UInt32 numSampleFrames, const IOAudioStreamFormat *streamFormat); // aml 2.18.02 changed to 4th order phase compensator version // aml 3.1.02 added iSub format struct // aml 3.5.02 added src phase // aml 3.5.02 added src state IOReturn clipAppleDBDMAToOutputStreamiSub(const void *mixBuf, void *sampleBuf, PreviousValues *filterState, PreviousValues *filterState2, PreviousValues *phaseCompState, float *low, float *high, UInt32 firstSampleFrame, UInt32 numSampleFrames, UInt32 sampleRate, const IOAudioStreamFormat *streamFormat, SInt16 * iSubBufferMemory, UInt32 *loopCount, SInt32 *iSubBufferOffset, UInt32 iSubBufferLen, iSubAudioFormatType* iSubFormat, float* srcPhase, float* srcState); // aml 2.18.02 changed to 4th order phase compensator version // aml 3.1.02 added iSub format struct // aml 3.5.02 added src phase // aml 3.5.02 added src state IOReturn clipAppleDBDMAToOutputStreamiSubInvertRightChannel(const void *mixBuf, void *sampleBuf, PreviousValues *filterState, PreviousValues *filterState2, PreviousValues *phaseCompState, float *low, float *high, UInt32 firstSampleFrame, UInt32 numSampleFrames, UInt32 sampleRate, const IOAudioStreamFormat *streamFormat, SInt16 * iSubBufferMemory, UInt32 *loopCount, SInt32 *iSubBufferOffset, UInt32 iSubBufferLen, iSubAudioFormatType* iSubFormat, float* srcPhase, float* srcState); // aml 2.18.02 changed to 4th order phase compensator version // aml 3.1.02 added iSub format struct // aml 3.5.02 added src phase // aml 3.5.02 added src state IOReturn clipAppleDBDMAToOutputStreamiSubMixRightChannel(const void *mixBuf, void *sampleBuf, PreviousValues *filterState, PreviousValues *filterState2, PreviousValues *phaseCompState, float *low, float *high, UInt32 firstSampleFrame, UInt32 numSampleFrames, UInt32 sampleRate, const IOAudioStreamFormat *streamFormat, SInt16 * iSubBufferMemory, UInt32 *loopCount, SInt32 *iSubBufferOffset, UInt32 iSubBufferLen, iSubAudioFormatType* iSubFormat, float* srcPhase, float* srcState); // aml 6.17.02, added dual mono mode parameter IOReturn convertAppleDBDMAFromInputStream(const void *sampleBuf, void *destBuf, UInt32 firstSampleFrame, UInt32 numSampleFrames, const IOAudioStreamFormat *streamFormat, DualMonoModeType inDualMonoMode); // aml 5.10.02, adding input clip routine with software gain control // aml 6.17.02, added dual mono mode parameter IOReturn convertAppleDBDMAFromInputStreamWithGain(const void *sampleBuf, void *destBuf, UInt32 firstSampleFrame, UInt32 numSampleFrames, const IOAudioStreamFormat *streamFormat, float* inGainL, float* inGainR, DualMonoModeType inDualMonoMode); // aml 5.10.02 added utility functions void dBfixed2float(UInt32 indBfixed, float* ioGainPtr); void inputGainConverter(UInt32 inGainIndex, float* ioGainPtr); }; // aml 5.10.02 void AppleDBDMAAudioDMAEngine::setUseSoftwareInputGain(bool inUseSoftwareInputGain) { mUseSoftwareInputGain = inUseSoftwareInputGain; #ifdef _AML_LOG_INPUT_GAIN // aml XXX testing if (mUseSoftwareInputGain) IOLog("AppleDBDMAAudioDMAEngine::setUseSoftwareInputGain, use SW input gain = TRUE.\n"); else IOLog("AppleDBDMAAudioDMAEngine::setUseSoftwareInputGain, use SW input gain = FALSE.\n"); #endif return; } // aml 5.10.02 void AppleDBDMAAudioDMAEngine::setInputGainL(UInt32 inGainL) { if (mInputGainLPtr == NULL) { mInputGainLPtr = (float *)IOMalloc(sizeof(float)); #ifdef _AML_LOG_INPUT_GAIN // aml XXX testing IOLog("AppleDBDMAAudioDMAEngine::setInputGainL - allocating mInputGainLPtr (0x%x).\n", mInputGainLPtr); #endif } inputGainConverter(inGainL, mInputGainLPtr); return; } // aml 5.10.02 void AppleDBDMAAudioDMAEngine::setInputGainR(UInt32 inGainR) { if (mInputGainRPtr == NULL) { mInputGainRPtr = (float *)IOMalloc(sizeof(float)); #ifdef _AML_LOG_INPUT_GAIN // aml XXX testing IOLog("AppleDBDMAAudioDMAEngine::setInputGainR - allocating mInputGainRPtr (0x%x).\n", mInputGainRPtr); #endif } inputGainConverter(inGainR, mInputGainRPtr); return; } IOReturn AppleDBDMAAudioDMAEngine::clipOutputSamples(const void *mixBuf, void *sampleBuf, UInt32 firstSampleFrame, UInt32 numSampleFrames, const IOAudioStreamFormat *streamFormat, IOAudioStream *audioStream) { IOReturn result; void * iSubBuffer = NULL; SInt32 offsetDelta; SInt32 safetyOffset; UInt32 iSubBufferLen = 0; UInt32 sampleRate; iSubAudioFormatType iSubFormat; // aml 3.1.02 // if the DMA went bad restart it if (fBadCmd && fBadResult) { fBadCmd = 0; fBadResult = 0; restartOutputIfFailure(); } if ((NULL != iSubBufferMemory) && (NULL != iSubEngine)) { iSubBufferLen = iSubBufferMemory->getLength (); iSubBuffer = (void*)iSubBufferMemory->getVirtualSegment (0, &iSubBufferLen); // (iSubBufferLen / 2) is because iSubBufferOffset is in UInt16s so convert iSubBufferLen to UInt16 length iSubBufferLen = iSubBufferLen / 2; sampleRate = getSampleRate()->whole; // aml 3.1.02 iSubFormat.altInterface = iSubEngine->GetAltInterface(); iSubFormat.numChannels = iSubEngine->GetNumChannels(); iSubFormat.bytesPerSample = iSubEngine->GetBytesPerSample(); iSubFormat.outputSampleRate = iSubEngine->GetSampleRate(); // Detect being out of sync with the iSub #if ABORT_PIPE_ON_START if (needToSync == FALSE && previousClippedToFrame == firstSampleFrame && 0x0 != iSubEngine->GetCurrentLoopCount ()) { #else if (needToSync == FALSE && previousClippedToFrame == firstSampleFrame && 0xFFFFFFFF != iSubEngine->GetCurrentLoopCount ()) { #endif // aml - make the reader/writer check more strict - this helps get rid of long term crunchy iSub audio // the reader is now not allowed within one frame (one millisecond of audio) of the writer safetyOffset = iSubBufferOffset - ((iSubFormat.outputSampleRate) / 1000); // 6 samples at 6kHz if (safetyOffset < 0) { safetyOffset += iSubBufferLen; } if (iSubLoopCount == iSubEngine->GetCurrentLoopCount () && safetyOffset < (SInt32)(iSubEngine->GetCurrentByteCount () / 2)) { #if DEBUGLOG IOLog ("****iSub is in front of write head safetyOffset = %ld, iSubEngine->GetCurrentByteCount () / 2 = %ld\n", safetyOffset, iSubEngine->GetCurrentByteCount () / 2); #endif needToSync = TRUE; startiSub = TRUE; } else if (iSubLoopCount > (iSubEngine->GetCurrentLoopCount () + 1)) { #if DEBUGLOG IOLog ("****looped more than the iSub iSubLoopCount = %ld, iSubEngine->GetCurrentLoopCount () = %ld\n", iSubLoopCount, iSubEngine->GetCurrentLoopCount ()); #endif needToSync = TRUE; startiSub = TRUE; } else if (iSubLoopCount < iSubEngine->GetCurrentLoopCount ()) { #if DEBUGLOG IOLog ("****iSub is ahead of us iSubLoopCount = %ld, iSubEngine->GetCurrentLoopCount () = %ld\n", iSubLoopCount, iSubEngine->GetCurrentLoopCount ()); #endif needToSync = TRUE; startiSub = TRUE; } else if (iSubLoopCount == iSubEngine->GetCurrentLoopCount () && iSubBufferOffset > ((SInt32)( (iSubEngine->GetCurrentByteCount() + (((iSubFormat.outputSampleRate)/1000 * NUM_ISUB_FRAME_LISTS_TO_QUEUE * NUM_ISUB_FRAMES_PER_LIST) * iSubFormat.bytesPerSample * iSubFormat.numChannels) ) / 2))) { // aml 3.27.02, this is the right number here (buffersize was 2x too large). This number should come eventually from the iSub engine reporting it's maximum number of queued bytes. #if DEBUGLOG IOLog ("****iSub is too far behind write head iSubBufferOffset = %ld, (iSubEngine->GetCurrentByteCount () / 2 + max queued data) = %ld\n", iSubBufferOffset, (iSubEngine->GetCurrentByteCount() / 2 + iSubBufferLen/2)); #endif needToSync = TRUE; startiSub = TRUE; } } if (FALSE == needToSync && previousClippedToFrame != firstSampleFrame && !(previousClippedToFrame == getNumSampleFramesPerBuffer () && firstSampleFrame == 0)) { #if DEBUGLOG IOLog ("clipOutput: no sync: iSubBufferOffset was %ld\n", iSubBufferOffset); #endif if (firstSampleFrame < previousClippedToFrame) { #if DEBUGLOG IOLog ("clipOutput: no sync: firstSampleFrame < previousClippedToFrame (delta = %ld)\n", previousClippedToFrame-firstSampleFrame); #endif // We've wrapped around the buffer // aml 2.28.02 replaced input stream num channels with iSub output num channels offsetDelta = (getNumSampleFramesPerBuffer () - firstSampleFrame + previousClippedToFrame) * iSubEngine->GetNumChannels(); } else { #if DEBUGLOG IOLog ("clipOutput: no sync: previousClippedToFrame < firstSampleFrame (delta = %ld)\n", firstSampleFrame - previousClippedToFrame); #endif // aml 2.28.02 replaced input stream num channels with iSub output num channels offsetDelta = (firstSampleFrame - previousClippedToFrame) * iSubEngine->GetNumChannels(); } // aml 3.21.02, adjust for new sample rate offsetDelta = (offsetDelta * 1000) / ((sampleRate * 1000) / iSubFormat.outputSampleRate); iSubBufferOffset += offsetDelta; #if DEBUGLOG IOLog ("clipOutput: no sync: clip to point was %ld, now %ld (delta = %ld)\n", previousClippedToFrame, firstSampleFrame, offsetDelta); IOLog ("clipOutput: no sync: iSubBufferOffset is now %ld\n", iSubBufferOffset); #endif if (iSubBufferOffset > (SInt32)iSubBufferLen) { #if DEBUGLOG IOLog ("clipOutput: no sync: iSubBufferOffset > iSubBufferLen, iSubBufferOffset = %ld\n", iSubBufferOffset); #endif // Our calculated spot has actually wrapped around the iSub's buffer. iSubLoopCount += iSubBufferOffset / iSubBufferLen; iSubBufferOffset = iSubBufferOffset % iSubBufferLen; #if DEBUGLOG IOLog ("clipOutput: no sync: iSubBufferOffset > iSubBufferLen, iSubBufferOffset is now %ld\n", iSubBufferOffset); #endif } else if (iSubBufferOffset < 0) { iSubBufferOffset += iSubBufferLen; #if DEBUGLOG IOLog ("clipOutput: no sync: iSubBufferOffset < 0, iSubBufferOffset is now %ld\n", iSubBufferOffset); #endif } } if (TRUE == justResetClipPosition) { justResetClipPosition = FALSE; needToSync = FALSE; startiSub = FALSE; } // sync up with iSub only if everything is proceeding normally. if (TRUE == needToSync) { UInt32 curSampleFrame; needToSync = FALSE; srcPhase = 1.0; // aml 3.5.02 srcState = 0.0; // aml 3.6.02 // start the filter over again since old filter state is invalid filterState.xl_1 = 0.0; filterState.xr_1 = 0.0; filterState.xl_2 = 0.0; filterState.xr_2 = 0.0; filterState.yl_1 = 0.0; filterState.yr_1 = 0.0; filterState.yl_2 = 0.0; filterState.yr_2 = 0.0; // aml 2.14.02 added for 4th order filter filterState2.xl_1 = 0.0; filterState2.xr_1 = 0.0; filterState2.xl_2 = 0.0; filterState2.xr_2 = 0.0; filterState2.yl_1 = 0.0; filterState2.yr_1 = 0.0; filterState2.yl_2 = 0.0; filterState2.yr_2 = 0.0; // aml 2.18.02 added for 4th order phase compensator phaseCompState.xl_1 = 0.0; phaseCompState.xr_1 = 0.0; phaseCompState.xl_2 = 0.0; phaseCompState.xr_2 = 0.0; phaseCompState.yl_1 = 0.0; phaseCompState.yr_1 = 0.0; phaseCompState.yl_2 = 0.0; phaseCompState.yr_2 = 0.0; #if ABORT_PIPE_ON_START // aml 4.25.02 wipe out the iSub buffer, changed due to moving zeroing of iSub buffer in AUA write handler when aborting the pipe bzero(iSubBuffer, iSubBufferLen); #endif curSampleFrame = getCurrentSampleFrame (); if (TRUE == restartedDMA) { iSubBufferOffset = initialiSubLead; restartedDMA = FALSE; } else { if (firstSampleFrame < curSampleFrame) { // aml 2.28.02 replaced input stream num channels with iSub output num channels // aml 3.21.02, moved to temp variable offsetDelta = (getNumSampleFramesPerBuffer () - curSampleFrame + firstSampleFrame) * iSubEngine->GetNumChannels(); } else { // aml 2.28.02 replaced input stream num channels with iSub output num channels // aml 3.21.02, moved to temp variable offsetDelta = (firstSampleFrame - curSampleFrame) * iSubEngine->GetNumChannels(); } #if DEBUGLOG IOLog ("clipOutput: need to sync: 44.1kHz offsetDelta = %ld\n", offsetDelta); #endif #if DEBUGLOG if (offsetDelta < kMinimumLatency) { IOLog ("clipOutput: no sync: 44.1 offsetDelta < min, offsetDelta=%ld\n", offsetDelta); } #endif // aml 3.21.02, adjust for new sample rate offsetDelta = (offsetDelta * 1000) / ((sampleRate * 1000) / iSubFormat.outputSampleRate); #if DEBUGLOG IOLog ("clipOutput: need to sync: iSubBufferOffset = %ld, offsetDelta = %ld\n", iSubBufferOffset, offsetDelta); #endif // aml 4.24.02 this was supposed to set the offset, not add it! Looks like a typo from case above. iSubBufferOffset = offsetDelta; #if DEBUGLOG IOLog ("clipOutput: need to sync: offsetDelta = %ld\n", offsetDelta); IOLog ("clipOutput: need to sync: firstSampleFrame = %ld, curSampleFrame = %ld\n", firstSampleFrame, curSampleFrame); IOLog ("clipOutput: need to sync: starting iSubBufferOffset = %ld, numSampleFrames = %ld\n", iSubBufferOffset, numSampleFrames); #endif if (iSubBufferOffset > (SInt32)iSubBufferLen) { needToSync = TRUE; // aml 4.24.02, requests larger than our buffer size = bad! #if DEBUGLOG IOLog ("clipOutput: need to sync: SubBufferOffset too big (%ld) RESYNC!\n", iSubBufferOffset); #endif // Our calculated spot has actually wrapped around the iSub's buffer. iSubLoopCount += iSubBufferOffset / iSubBufferLen; iSubBufferOffset = iSubBufferOffset % iSubBufferLen; #if DEBUGLOG IOLog ("clipOutput: need to sync: iSubBufferOffset > iSubBufferLen (%ld), iSubBufferOffset is now %ld\n", iSubBufferLen, iSubBufferOffset); #endif } else if (iSubBufferOffset < 0) { iSubBufferOffset += iSubBufferLen; #if DEBUGLOG IOLog ("clipOutput: need to sync: iSubBufferOffset < 0, iSubBufferOffset is now %ld\n", iSubBufferOffset); #endif } initialiSubLead = iSubBufferOffset; } } // aml 2.14.02 added filterState2 for 4th order filter in all clip calls below // aml 2.18.02 added phaseCompState for 4th order filter phase compensator in all clip calls below // aml 3.1.02 added iSub format struct to calls below // aml 3.5.02 added src phase to calls below // aml 3.6.02 added src state to calls below // this will be true on a slot load iMac that has the built in speakers enabled if (TRUE == fNeedsPhaseInversion) { result = clipAppleDBDMAToOutputStreamiSubInvertRightChannel (mixBuf, sampleBuf, &filterState, &filterState2, &phaseCompState, lowFreqSamples, highFreqSamples, firstSampleFrame, numSampleFrames, sampleRate, streamFormat, (SInt16*)iSubBuffer, &iSubLoopCount, &iSubBufferOffset, iSubBufferLen, &iSubFormat, &srcPhase, &srcState); } else if (TRUE == fNeedsRightChanMixed) { result = clipAppleDBDMAToOutputStreamiSubMixRightChannel (mixBuf, sampleBuf, &filterState, &filterState2, &phaseCompState, lowFreqSamples, highFreqSamples, firstSampleFrame, numSampleFrames, sampleRate, streamFormat, (SInt16*)iSubBuffer, &iSubLoopCount, &iSubBufferOffset, iSubBufferLen, &iSubFormat, &srcPhase, &srcState); } else { result = clipAppleDBDMAToOutputStreamiSub (mixBuf, sampleBuf, &filterState, &filterState2, &phaseCompState, lowFreqSamples, highFreqSamples, firstSampleFrame, numSampleFrames, sampleRate, streamFormat, (SInt16*)iSubBuffer, &iSubLoopCount, &iSubBufferOffset, iSubBufferLen, &iSubFormat, &srcPhase, &srcState); } if (TRUE == startiSub) { iSubEngine->StartiSub (); startiSub = FALSE; iSubLoopCount = 0; } previousClippedToFrame = firstSampleFrame + numSampleFrames; } else { // this will be true on a slot load iMac that has the built in speakers enabled if (TRUE == fNeedsPhaseInversion) { result = clipAppleDBDMAToOutputStreamInvertRightChannel(mixBuf, sampleBuf, firstSampleFrame, numSampleFrames, streamFormat); } else if (TRUE == fNeedsRightChanMixed) { result = clipAppleDBDMAToOutputStreamMixRightChannel(mixBuf, sampleBuf, firstSampleFrame, numSampleFrames, streamFormat); } else { result = clipAppleDBDMAToOutputStream(mixBuf, sampleBuf, firstSampleFrame, numSampleFrames, streamFormat); } } return result; } IOReturn AppleDBDMAAudioDMAEngine::convertInputSamples(const void *sampleBuf, void *destBuf, UInt32 firstSampleFrame, UInt32 numSampleFrames, const IOAudioStreamFormat *streamFormat, IOAudioStream *audioStream) { if (mUseSoftwareInputGain) { return convertAppleDBDMAFromInputStreamWithGain(sampleBuf, destBuf, firstSampleFrame, numSampleFrames, streamFormat, mInputGainLPtr, mInputGainRPtr, mInputDualMonoMode); } else { return convertAppleDBDMAFromInputStream(sampleBuf, destBuf, firstSampleFrame, numSampleFrames, streamFormat, mInputDualMonoMode); } } IOReturn AppleDBDMAAudioDMAEngine::performFormatChange(IOAudioStream *audioStream, const IOAudioStreamFormat *newFormat, const IOAudioSampleRate *newSampleRate) { if ( NULL != newFormat ) { // rbm 7.15.2002 keep a copy for user client dbdmaFormat.fNumChannels = newFormat->fNumChannels; dbdmaFormat.fSampleFormat = newFormat->fSampleFormat; dbdmaFormat.fNumericRepresentation = newFormat->fNumericRepresentation; dbdmaFormat.fBitDepth = newFormat->fBitDepth; dbdmaFormat.fBitWidth = newFormat->fBitWidth; dbdmaFormat.fAlignment = newFormat->fAlignment; dbdmaFormat.fByteOrder = newFormat->fByteOrder; dbdmaFormat.fIsMixable = newFormat->fIsMixable; dbdmaFormat.fDriverTag = newFormat->fDriverTag; } return kIOReturnSuccess; } IOReturn AppleDBDMAAudioDMAEngine::iSubAttachChangeHandler (IOService *target, IOAudioControl *attachControl, SInt32 oldValue, SInt32 newValue) { IOReturn result; AppleDBDMAAudioDMAEngine * audioDMAEngine; IOCommandGate * cg; debug5IOLog ("+ AppleDBDMAAudioDMAEngine::iSubAttachChangeHandler (%p, %p, 0x%lx, 0x%lx)\n", target, attachControl, oldValue, newValue); result = kIOReturnSuccess; FailIf (oldValue == newValue, Exit); audioDMAEngine = OSDynamicCast (AppleDBDMAAudioDMAEngine, target); FailIf (NULL == audioDMAEngine, Exit); if (newValue) { debugIOLog ("try to connect to an iSub\n"); // Set up notifier to run when iSub shows up audioDMAEngine->iSubEngineNotifier = addNotification (gIOPublishNotification, serviceMatching ("AppleiSubEngine"), (IOServiceNotificationHandler)&iSubEnginePublished, audioDMAEngine); if (NULL != audioDMAEngine->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 debugIOLog ("iSub was already attached\n"); audioDMAEngine->iSubEngineNotifier->remove (); audioDMAEngine->iSubEngineNotifier = NULL; } } else { debugIOLog ("do not try to connect to iSub\n"); if (NULL != audioDMAEngine->iSubBufferMemory) { debugIOLog ("disconnect from iSub\n"); // We're already attached to an iSub, so detach cg = audioDMAEngine->getCommandGate (); if (NULL != cg) { cg->runAction (iSubCloseAction); } } // We're not attached to the iSub, so just remove our notifier if (NULL != audioDMAEngine->iSubEngineNotifier) { debugIOLog ("remove iSub notifier\n"); audioDMAEngine->iSubEngineNotifier->remove (); audioDMAEngine->iSubEngineNotifier = NULL; } } Exit: debugIOLog ("- AppleDBDMAAudioDMAEngine::iSubAttachChangeHandler\n"); return result; } bool AppleDBDMAAudioDMAEngine::iSubEnginePublished (AppleDBDMAAudioDMAEngine * dbdmaEngineObject, void * refCon, IOService * newService) { IOReturn result; bool resultCode; IOCommandGate * cg; debug4IOLog ("+AppleDBDMAAudioDMAEngine::iSubEnginePublished (%p, %p, %p)\n", dbdmaEngineObject, (UInt32*)refCon, newService); resultCode = false; FailIf (NULL == dbdmaEngineObject, Exit); FailIf (NULL == newService, Exit); dbdmaEngineObject->iSubEngine = (AppleiSubEngine *)newService; FailIf (NULL == dbdmaEngineObject->iSubEngine, Exit); // Create the memory for the high/low samples to go into dbdmaEngineObject->lowFreqSamples = (float *)IOMallocAligned (round_page((dbdmaEngineObject->numBlocks * dbdmaEngineObject->blockSize) * sizeof (float)), PAGE_SIZE); FailIf (NULL == dbdmaEngineObject->lowFreqSamples, Exit); dbdmaEngineObject->highFreqSamples = (float *)IOMallocAligned (round_page((dbdmaEngineObject->numBlocks * dbdmaEngineObject->blockSize) * sizeof (float)), PAGE_SIZE); FailIf (NULL == dbdmaEngineObject->highFreqSamples, Exit); // Open the iSub which will cause it to create mute and volume controls dbdmaEngineObject->attach (dbdmaEngineObject->iSubEngine); cg = dbdmaEngineObject->getCommandGate (); FailWithAction (NULL == cg, dbdmaEngineObject->detach (dbdmaEngineObject->iSubEngine), Exit); dbdmaEngineObject->setSampleOffset(kMinimumLatencyiSub); // HAL should notice this when iSub adds it's controls and sends out update IOSleep (102); result = cg->runAction (iSubOpenAction); FailWithAction (kIOReturnSuccess != result, dbdmaEngineObject->detach (dbdmaEngineObject->iSubEngine), Exit); dbdmaEngineObject->iSubBufferMemory = dbdmaEngineObject->iSubEngine->GetSampleBuffer (); debug2IOLog ("iSubBuffer length = %ld\n", dbdmaEngineObject->iSubBufferMemory->getLength ()); // remove our notifier because we only care about the first iSub if (NULL != dbdmaEngineObject->iSubEngineNotifier) { dbdmaEngineObject->iSubEngineNotifier->remove (); dbdmaEngineObject->iSubEngineNotifier = NULL; } resultCode = true; dbdmaEngineObject->iSubOpen = TRUE; Exit: if (FALSE == resultCode) { // We didn't actually open the iSub dbdmaEngineObject->iSubBufferMemory = NULL; dbdmaEngineObject->iSubEngine = NULL; dbdmaEngineObject->iSubOpen = FALSE; dbdmaEngineObject->setSampleOffset(kMinimumLatency); if (NULL != dbdmaEngineObject->lowFreqSamples) { IOFree (dbdmaEngineObject->lowFreqSamples, (dbdmaEngineObject->numBlocks * dbdmaEngineObject->blockSize) * sizeof (float)); } if (NULL != dbdmaEngineObject->highFreqSamples) { IOFree (dbdmaEngineObject->highFreqSamples, (dbdmaEngineObject->numBlocks * dbdmaEngineObject->blockSize) * sizeof (float)); } } debug5IOLog ("-AppleDBDMAAudioDMAEngine::iSubEnginePublished (%p, %p, %p), result = %d\n", dbdmaEngineObject, (UInt32 *)refCon, newService, resultCode); return resultCode; } IOReturn AppleDBDMAAudioDMAEngine::iSubCloseAction (OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4) { if (NULL != owner) { AppleDBDMAAudioDMAEngine * audioEngine; debugIOLog ("+AppleDBDMAAudioDMAEngine::iSubCloseAction\n"); audioEngine = OSDynamicCast (AppleDBDMAAudioDMAEngine, owner); if (NULL != audioEngine && NULL != audioEngine->iSubEngine && TRUE == audioEngine->iSubOpen) { audioEngine->pauseAudioEngine (); audioEngine->beginConfigurationChange (); audioEngine->iSubEngine->closeiSub (audioEngine); audioEngine->completeConfigurationChange (); audioEngine->resumeAudioEngine (); audioEngine->detach (audioEngine->iSubEngine); audioEngine->iSubEngine = NULL; audioEngine->iSubBufferMemory = NULL; if (NULL != audioEngine->lowFreqSamples) { IOFree (audioEngine->lowFreqSamples, (audioEngine->numBlocks * audioEngine->blockSize) * sizeof (float)); audioEngine->lowFreqSamples = NULL; } if (NULL != audioEngine->highFreqSamples) { IOFree (audioEngine->highFreqSamples, (audioEngine->numBlocks * audioEngine->blockSize) * sizeof (float)); audioEngine->highFreqSamples = NULL; } #if DEBUGLOG IOLog ("iSub connections terminated\n"); #endif } else { #if DEBUGLOG IOLog ("didn't terminate the iSub connections because we didn't have an audioEngine\n"); #endif } } else { #if DEBUGLOG IOLog ("didn't terminate the iSub connections owner = %p, arg1 = %p\n", owner, arg1); #endif } debugIOLog ("-AppleDBDMAAudioDMAEngine::iSubCloseAction\n"); return kIOReturnSuccess; } IOReturn AppleDBDMAAudioDMAEngine::iSubOpenAction (OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4) { IOReturn result; bool resultBool; debugIOLog ("+AppleDBDMAAudioDMAEngine::iSubOpenAction\n"); result = kIOReturnError; resultBool = FALSE; if (NULL != owner) { AppleDBDMAAudioDMAEngine * audioEngine; audioEngine = OSDynamicCast (AppleDBDMAAudioDMAEngine, owner); resultBool = audioEngine->iSubEngine->openiSub (audioEngine); } if (resultBool) { result = kIOReturnSuccess; } debugIOLog ("-AppleDBDMAAudioDMAEngine::iSubOpenAction\n"); return result; } bool AppleDBDMAAudioDMAEngine::willTerminate (IOService * provider, IOOptionBits options) { IOCommandGate * cg; debug3IOLog ("+AppleDBDMAAudioDMAEngine[%p]::willTerminate (%p)\n", this, provider); if (iSubEngine == (AppleiSubEngine *)provider) { debugIOLog ("iSub requesting termination\n"); cg = getCommandGate (); if (NULL != cg) { cg->runAction (iSubCloseAction); } // Set up notifier to run when iSub shows up again if (iSubAttach->getIntValue ()) { iSubEngineNotifier = addNotification (gIOPublishNotification, serviceMatching ("AppleiSubEngine"), (IOServiceNotificationHandler)&iSubEnginePublished, this); } } debug2IOLog ("-AppleDBDMAAudioDMAEngine[%p]::willTerminate - about to call super::willTerminate ()\n", this); return super::willTerminate (provider, options); } bool AppleDBDMAAudioDMAEngine::getDmaState (void ) { return dmaRunState; } IOReturn AppleDBDMAAudioDMAEngine::getAudioStreamFormat( IOAudioStreamFormat * streamFormatPtr ) { if ( NULL != streamFormatPtr ) { streamFormatPtr->fNumChannels = dbdmaFormat.fNumChannels; streamFormatPtr->fSampleFormat = dbdmaFormat.fSampleFormat; streamFormatPtr->fNumericRepresentation = dbdmaFormat.fNumericRepresentation; streamFormatPtr->fBitDepth = dbdmaFormat.fBitDepth; streamFormatPtr->fBitWidth = dbdmaFormat.fBitWidth; streamFormatPtr->fAlignment = dbdmaFormat.fAlignment; streamFormatPtr->fByteOrder = dbdmaFormat.fByteOrder; streamFormatPtr->fIsMixable = dbdmaFormat.fIsMixable; streamFormatPtr->fDriverTag = dbdmaFormat.fDriverTag; } return kIOReturnSuccess; }