AppleDBDMAAudioDMAEngine.cpp [plain text]
#define DEBUGTIMESTAMPS FALSE
#include "AppleDBDMAAudioDMAEngine.h"
#include "AppleOnboardAudio.h"
#include <IOKit/IOMemoryDescriptor.h>
#include <IOKit/audio/IOAudioDevice.h>
#include <IOKit/audio/IOAudioTypes.h>
#include <IOKit/audio/IOAudioDefines.h>
#include <IOKit/audio/IOAudioDebug.h>
#include <IOKit/audio/IOAudioLevelControl.h>
#include <IOKit/IOFilterInterruptEventSource.h>
#include <IOKit/IOWorkLoop.h>
#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;
FailIf (FALSE == super::init (properties), Exit);
FailIf (NULL == theDeviceProvider, Exit);
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);
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 (dmaCommandBufferOut && (commandBufferSize > 0))
{
IOFreeAligned(dmaCommandBufferOut, commandBufferSize);
dmaCommandBufferOut = 0;
}
if (dmaCommandBufferIn && (commandBufferSize > 0))
{
IOFreeAligned(dmaCommandBufferIn, commandBufferSize);
dmaCommandBufferOut = 0;
}
if (NULL != iSubEngineNotifier) {
iSubEngineNotifier->remove ();
}
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 commandBufferPhys, sampleBufferPhys, stopCommandPhys, offset, sampleBufOut, sampleBufIn;
UInt32 blockNum, dmaCommand = 0;
bool doInterrupt = false;
int interruptIndex;
IOWorkLoop *workLoop;
IOAudioStream *stream;
sampleBufIn = NULL;
IOAudioStreamFormat format = {
2,
kIOAudioStreamSampleFormatLinearPCM,
kIOAudioStreamNumericRepresentationSignedInt,
16,
16,
kIOAudioStreamAlignmentHighByte,
kIOAudioStreamByteOrderBigEndian,
true
};
DEBUG_IOLOG("+ AppleDBDMAAudioDMAEngine::initHardware()\n");
ourProvider = provider;
fBadCmd = 0;
fBadResult = 0;
if (!super::initHardware(provider)) {return false;}
sampleBufOut = (vm_offset_t)IOMallocAligned(round_page(numBlocks * blockSize), PAGE_SIZE);
if(ioBaseDMAInput)
sampleBufIn = (vm_offset_t)IOMallocAligned(round_page(numBlocks * blockSize), PAGE_SIZE);
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();
}
}
if (!status || !sampleBufOut) {return false;}
if(ioBaseDMAInput)
if(!sampleBufIn) {return false;}
commandBufferSize = (numBlocks + 1) * sizeof(IODBDMADescriptor);
dmaCommandBufferOut = (IODBDMADescriptor *)IOMallocAligned(commandBufferSize, 32);
if (!dmaCommandBufferOut) {return false;}
commandBufferPhys = pmap_extract(kernel_pmap, (vm_address_t)dmaCommandBufferOut);
sampleBufferPhys = pmap_extract(kernel_pmap, (vm_address_t)sampleBufOut);
stopCommandPhys = pmap_extract(kernel_pmap, (vm_address_t) (&dmaCommandBufferOut[numBlocks]));
offset = 0;
dmaCommand = kdbdmaOutputMore;
interruptIndex = kDBDMAOutputIndex;
workLoop = getWorkLoop();
if (!workLoop) {return false;}
interruptEventSource = IOFilterInterruptEventSource::filterInterruptEventSource(this,
AppleDBDMAAudioDMAEngine::interruptHandler,
AppleDBDMAAudioDMAEngine::interruptFilter,
audioDevice->getProvider(),
interruptIndex);
if (!interruptEventSource) {return false;}
workLoop->addEventSource(interruptEventSource);
for (blockNum = 0; blockNum < numBlocks; blockNum++) {
vm_offset_t cmdDest;
if (offset >= PAGE_SIZE) {
sampleBufferPhys = pmap_extract(kernel_pmap, (vm_address_t)(sampleBufOut + (blockNum * blockSize)));
offset = 0;
}
if (blockNum == (numBlocks - 1)) {
cmdDest = commandBufferPhys;
doInterrupt = true;
} else if ((((blockNum + 1) * sizeof(IODBDMADescriptor)) % PAGE_SIZE) == 0) {
cmdDest = pmap_extract(kernel_pmap, (vm_address_t) (dmaCommandBufferOut + (blockNum + 1)));
} 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);
if(ioBaseDMAInput) {
dmaCommandBufferIn = (IODBDMADescriptor *)IOMallocAligned(commandBufferSize, 32);
if (!dmaCommandBufferIn) {return false;}
commandBufferPhys = pmap_extract(kernel_pmap, (vm_address_t)dmaCommandBufferIn);
sampleBufferPhys = pmap_extract(kernel_pmap, (vm_address_t)sampleBufIn);
stopCommandPhys = pmap_extract(kernel_pmap, (vm_address_t) (&dmaCommandBufferIn[numBlocks]));
doInterrupt = false;
offset = 0;
dmaCommand = kdbdmaInputMore;
for (blockNum = 0; blockNum < numBlocks; blockNum++) {
vm_offset_t cmdDest;
if (offset >= PAGE_SIZE) {
sampleBufferPhys = pmap_extract(kernel_pmap, (vm_address_t)(sampleBufIn + (blockNum * blockSize)));
offset = 0;
}
if (blockNum == (numBlocks - 1)) {
cmdDest = commandBufferPhys;
} else if ((((blockNum + 1) * sizeof(IODBDMADescriptor)) % PAGE_SIZE) == 0) {
cmdDest = pmap_extract(kernel_pmap, (vm_address_t) (dmaCommandBufferIn + (blockNum + 1)));
} 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;
iSubEngineNotifier = addNotification (gIOPublishNotification, serviceMatching ("AppleiSubEngine"),
(IOServiceNotificationHandler)&iSubEnginePublished, this);
if (NULL != iSubBufferMemory) {
iSubEngineNotifier->remove ();
}
DEBUG_IOLOG("- AppleDBDMAAudioDMAEngine::initHardware()\n");
return true;
}
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::message (UInt32 type, IOService * provider, void * arg) {
bool resultCode;
IOCommandGate * cg;
debug4IOLog ("+AppleDBDMAAudioDMAEngine[%p]::message (0x%lx, %p)\n", this, type, provider);
resultCode = kIOReturnSuccess;
switch (type) {
case kIOMessageServiceIsTerminated:
if (iSubEngine == (AppleiSubEngine *)provider) {
debugIOLog ("iSub requesting termination\n");
pauseAudioEngine ();
cg = getCommandGate ();
if (NULL != cg) {
cg->runAction (iSubCloseAction, this);
}
resumeAudioEngine ();
iSubEngineNotifier = addNotification (gIOPublishNotification, serviceMatching ("AppleiSubEngine"), (IOServiceNotificationHandler)&iSubEnginePublished, this);
}
break;
default:
;
}
debug4IOLog ("-AppleDBDMAAudioDMAEngine[%p]::message (0x%lx, %p)\n", this, type, provider);
return resultCode;
}
IOReturn AppleDBDMAAudioDMAEngine::performAudioEngineStart()
{
debugIOLog(" + AppleDBDMAAudioDMAEngine::performAudioEngineStart()\n");
if (!ioBaseDMAOutput || !dmaCommandBufferOut || !status || !interruptEventSource) {
return kIOReturnError;
}
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;
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;
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();
takeTimeStamp(false);
if(ioBaseDMAInput) {
IOSetDBDMAChannelControl(ioBaseDMAInput, IOClearDBDMAChannelControlBits(kdbdmaS0));
IOSetDBDMABranchSelect(ioBaseDMAInput, IOSetDBDMAChannelControlBits(kdbdmaS0));
IODBDMAStart(ioBaseDMAInput, (IODBDMADescriptor *)pmap_extract(kernel_pmap, (vm_address_t)(dmaCommandBufferIn)));
}
IOSetDBDMAChannelControl(ioBaseDMAOutput, IOClearDBDMAChannelControlBits(kdbdmaS0));
IOSetDBDMABranchSelect(ioBaseDMAOutput, IOSetDBDMAChannelControlBits(kdbdmaS0));
IODBDMAStart(ioBaseDMAOutput, (IODBDMADescriptor *)pmap_extract(kernel_pmap, (vm_address_t)(dmaCommandBufferOut)));
debugIOLog(" - AppleDBDMAAudioDMAEngine::performAudioEngineStart()\n");
return kIOReturnSuccess;
}
IOReturn AppleDBDMAAudioDMAEngine::restartOutputIfFailure(){
if (!ioBaseDMAOutput || !dmaCommandBufferOut || !status || !interruptEventSource) {
return kIOReturnError;
}
#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();
takeTimeStamp(false);
IOSetDBDMAChannelControl(ioBaseDMAOutput, IOClearDBDMAChannelControlBits(kdbdmaS0));
IOSetDBDMABranchSelect(ioBaseDMAOutput, IOSetDBDMAChannelControlBits(kdbdmaS0));
IODBDMAStart(ioBaseDMAOutput, (IODBDMADescriptor *)pmap_extract(kernel_pmap,
(vm_address_t)(dmaCommandBufferOut)));
return kIOReturnSuccess;
}
IOReturn AppleDBDMAAudioDMAEngine::performAudioEngineStop()
{
UInt16 attemptsToStop = 1000;
debugIOLog("+ AppleDBDMAAudioDMAEngine::performAudioEngineStop()\n");
if (NULL != iSubEngine) {
iSubEngine->StopiSub ();
needToSync = TRUE;
}
if (!interruptEventSource) {
return kIOReturnError;
}
interruptEventSource->disable();
IOSetDBDMAChannelControl(ioBaseDMAOutput, IOSetDBDMAChannelControlBits(kdbdmaS0));
while ((IOGetDBDMAChannelStatus(ioBaseDMAOutput) & kdbdmaActive) && (attemptsToStop--)) {
eieio();
IOSleep(1);
}
IODBDMAStop(ioBaseDMAOutput);
IODBDMAReset(ioBaseDMAOutput);
if(ioBaseDMAInput){
IOSetDBDMAChannelControl(ioBaseDMAInput, IOSetDBDMAChannelControlBits(kdbdmaS0));
while ((IOGetDBDMAChannelStatus(ioBaseDMAInput) & kdbdmaActive) && (attemptsToStop--)) {
eieio();
IOSleep(1);
}
IODBDMAStop(ioBaseDMAInput);
IODBDMAReset(ioBaseDMAInput);
}
interruptEventSource->enable();
DEBUG_IOLOG("- AppleDBDMAAudioDMAEngine::performAudioEngineStop()\n");
return kIOReturnSuccess;
}
bool AppleDBDMAAudioDMAEngine::filterInterrupt(int index)
{
UInt32 result = IOGetDBDMAChannelStatus(ioBaseDMAOutput);
UInt32 cmd = IOGetDBDMACommandPtr(ioBaseDMAOutput);
if (!(result & kdbdmaActive)) {
fBadResult = result;
fBadCmd = cmd;
}
if (status)
{
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; }
void AppleDBDMAAudioDMAEngine::resetClipPosition (IOAudioStream *audioStream, UInt32 clipSampleFrame) {
if (NULL != iSubBufferMemory) {
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;
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;
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
#endif
if (previousClippedToFrame < clipSampleFrame) {
clipAdjustment = (getNumSampleFramesPerBuffer () - clipSampleFrame + previousClippedToFrame) * audioStream->format.fNumChannels;
} else {
clipAdjustment = (previousClippedToFrame - clipSampleFrame) * audioStream->format.fNumChannels;
}
iSubBufferOffset -= clipAdjustment;
if (iSubBufferOffset < 0) {
iSubBufferOffset += (iSubBufferMemory->getLength () / 2);
iSubLoopCount--;
}
previousClippedToFrame = clipSampleFrame;
justResetClipPosition = TRUE;
#if DEBUGLOG
#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);
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);
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);
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);
IOReturn convertAppleDBDMAFromInputStream(const void *sampleBuf, void *destBuf, UInt32 firstSampleFrame, UInt32 numSampleFrames, const IOAudioStreamFormat *streamFormat);
};
IOReturn AppleDBDMAAudioDMAEngine::clipOutputSamples(const void *mixBuf, void *sampleBuf, UInt32 firstSampleFrame, UInt32 numSampleFrames, const IOAudioStreamFormat *streamFormat, IOAudioStream *audioStream)
{
IOReturn result;
void * iSubBuffer = NULL;
SInt32 offsetDelta;
UInt32 iSubBufferLen = 0;
UInt32 sampleRate;
if (fBadCmd && fBadResult)
{
fBadCmd = 0;
fBadResult = 0;
restartOutputIfFailure();
}
if (iSubBufferMemory) {
iSubBufferLen = iSubBufferMemory->getLength ();
iSubBuffer = (void*)iSubBufferMemory->getVirtualSegment (0, &iSubBufferLen);
iSubBufferLen = iSubBufferLen / 2;
sampleRate = getSampleRate()->whole;
if (needToSync == FALSE && previousClippedToFrame == firstSampleFrame && 0xFFFFFFFF != iSubEngine->GetCurrentLoopCount ()) {
if (iSubLoopCount == iSubEngine->GetCurrentLoopCount () && iSubBufferOffset < (SInt32)(iSubEngine->GetCurrentByteCount () / 2)) {
#if DEBUGLOG
IOLog ("****iSub is in front of write head iSubBufferOffset = %ld, iSubEngine->GetCurrentByteCount () / 2 = %ld\n", iSubBufferOffset, 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 () + 17640) / 2)) { #if DEBUGLOG
IOLog ("****iSub is too far behind write head iSubBufferOffset = %ld, ((iSubEngine->GetCurrentByteCount () + 17640) / 2) = %ld\n", iSubBufferOffset, (iSubEngine->GetCurrentByteCount () + 17640) / 2);
#endif
needToSync = TRUE;
startiSub = TRUE;
}
}
if (FALSE == needToSync && previousClippedToFrame != firstSampleFrame && !(previousClippedToFrame == getNumSampleFramesPerBuffer () && firstSampleFrame == 0)) {
#if DEBUGLOG
IOLog ("iSubBufferOffset was %ld\n", iSubBufferOffset);
#endif
if (firstSampleFrame < previousClippedToFrame) {
offsetDelta = (getNumSampleFramesPerBuffer () - firstSampleFrame + previousClippedToFrame) * 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) {
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
}
}
if (TRUE == justResetClipPosition) {
justResetClipPosition = FALSE;
needToSync = FALSE;
startiSub = FALSE;
}
if (TRUE == needToSync) {
UInt32 curSampleFrame;
needToSync = 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;
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;
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;
curSampleFrame = getCurrentSampleFrame ();
if (TRUE == restartedDMA) {
iSubBufferOffset = initialiSubLead;
restartedDMA = FALSE;
} else {
if (firstSampleFrame < curSampleFrame) {
iSubBufferOffset = (getNumSampleFramesPerBuffer () - curSampleFrame + firstSampleFrame) * streamFormat->fNumChannels;
} else {
iSubBufferOffset = (firstSampleFrame - curSampleFrame) * streamFormat->fNumChannels;
}
iSubBufferOffset -= 88 * 5;
#if DEBUGLOG
IOLog ("firstSampleFrame = %ld, curSampleFrame = %ld\n", firstSampleFrame, curSampleFrame);
IOLog ("starting iSubBufferOffset = %ld, numSampleFrames = %ld\n", iSubBufferOffset, numSampleFrames);
#endif
if (iSubBufferOffset > (SInt32)iSubBufferLen) {
iSubLoopCount += iSubBufferOffset / iSubBufferLen;
iSubBufferOffset = iSubBufferOffset % iSubBufferLen;
#if DEBUGLOG
IOLog ("iSubBufferOffset > iSubBufferLen (%ld), iSubBufferOffset is now %ld\n", iSubBufferLen, iSubBufferOffset);
#endif
} else if (iSubBufferOffset < 0) {
iSubBufferOffset += iSubBufferLen;
#if DEBUGLOG
IOLog ("iSubBufferOffset < 0, iSubBufferOffset is now %ld\n", iSubBufferOffset);
#endif
}
initialiSubLead = iSubBufferOffset;
}
}
if (TRUE == fNeedsPhaseInversion) {
result = clipAppleDBDMAToOutputStreamiSubInvertRightChannel (mixBuf, sampleBuf, &filterState, &filterState2, &phaseCompState, lowFreqSamples, highFreqSamples, firstSampleFrame, numSampleFrames, sampleRate, streamFormat, (SInt16*)iSubBuffer, &iSubLoopCount, &iSubBufferOffset, iSubBufferLen);
} else if (TRUE == fNeedsRightChanMixed) {
result = clipAppleDBDMAToOutputStreamiSubMixRightChannel (mixBuf, sampleBuf, &filterState, &filterState2, &phaseCompState, lowFreqSamples, highFreqSamples, firstSampleFrame, numSampleFrames, sampleRate, streamFormat, (SInt16*)iSubBuffer, &iSubLoopCount, &iSubBufferOffset, iSubBufferLen);
} else {
result = clipAppleDBDMAToOutputStreamiSub (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;
}
previousClippedToFrame = firstSampleFrame + numSampleFrames;
} else {
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)
{
return convertAppleDBDMAFromInputStream(sampleBuf, destBuf, firstSampleFrame, numSampleFrames, streamFormat);
}
IOReturn AppleDBDMAAudioDMAEngine::performFormatChange(IOAudioStream *audioStream, const IOAudioStreamFormat *newFormat, const IOAudioSampleRate *newSampleRate)
{
return kIOReturnSuccess;
}
bool AppleDBDMAAudioDMAEngine::iSubEnginePublished (AppleDBDMAAudioDMAEngine * dbdmaEngineObject, void * refCon, IOService * newService) {
IOReturn result;
bool resultCode;
OSCollectionIterator * collectionIterator;
IOAudioToggleControl * ourMuteControl;
IOAudioLevelControl * masterVolumeControl;
IOAudioLevelControl * volumeControl;
IOAudioLevelControl * leftVolumeControl;
IOAudioLevelControl * rightVolumeControl;
IOCommandGate * cg;
UInt32 i;
UInt32 numControls;
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);
debugIOLog ("Looking for our volume controls to set the initial iSub volume\n");
collectionIterator = OSCollectionIterator::withCollection (dbdmaEngineObject->defaultAudioControls);
FailIf (NULL == collectionIterator, Exit);
i = 0;
numControls = dbdmaEngineObject->defaultAudioControls->getCount ();
volumeControl = NULL;
masterVolumeControl = NULL;
leftVolumeControl = NULL;
rightVolumeControl = NULL;
while (i < numControls) {
volumeControl = OSDynamicCast (IOAudioLevelControl, 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 ();
debugIOLog ("Looking for our mute control to set the initial iSub mute\n");
collectionIterator = OSCollectionIterator::withCollection (dbdmaEngineObject->defaultAudioControls);
i = 0;
ourMuteControl = NULL;
while (i < numControls && NULL == ourMuteControl) {
ourMuteControl = OSDynamicCast (IOAudioToggleControl, collectionIterator->getNextObject ());
i++;
}
collectionIterator->release ();
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);
dbdmaEngineObject->attach (dbdmaEngineObject->iSubEngine);
cg = dbdmaEngineObject->getCommandGate ();
FailIf (NULL == cg, Exit);
result = cg->runAction (iSubOpenAction, dbdmaEngineObject);
FailIf (kIOReturnSuccess != result, Exit);
dbdmaEngineObject->iSubBufferMemory = dbdmaEngineObject->iSubEngine->GetSampleBuffer ();
debug2IOLog ("iSubBuffer length = %ld\n", dbdmaEngineObject->iSubBufferMemory->getLength ());
if (NULL != leftVolumeControl && NULL != rightVolumeControl) {
debug3IOLog ("setting initial iSub volumes to L:%ld R:%ld\n", leftVolumeControl->getIntValue (), rightVolumeControl->getIntValue ());
resultCode = leftVolumeControl->flushValue ();
resultCode = rightVolumeControl->flushValue ();
} else if (NULL != masterVolumeControl) {
debug2IOLog ("setting initial iSub volume using master control to %ld\n", masterVolumeControl->getIntValue ());
resultCode = masterVolumeControl->flushValue ();
}
if (NULL != ourMuteControl) {
debug2IOLog ("setting initial iSub mute state to %ld\n", ourMuteControl->getIntValue ());
resultCode = ourMuteControl->flushValue ();
}
if (NULL != dbdmaEngineObject->iSubEngineNotifier)
dbdmaEngineObject->iSubEngineNotifier->remove ();
resultCode = true;
Exit:
debug4IOLog ("-AppleDBDMAAudioDMAEngine::iSubEnginePublished (%p, %p, %p)\n", dbdmaEngineObject, (UInt32 *)refCon, newService);
return resultCode;
}
IOReturn AppleDBDMAAudioDMAEngine::iSubCloseAction (OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4) {
if (owner && arg1) {
AppleDBDMAAudioDMAEngine *audioEngine = OSDynamicCast (AppleDBDMAAudioDMAEngine, owner);
if (audioEngine) {
audioEngine->iSubEngine->close ((IOService *)arg1);
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
}
return kIOReturnSuccess;
}
IOReturn AppleDBDMAAudioDMAEngine::iSubOpenAction (OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4) {
IOReturn result;
bool resultBool;
result = kIOReturnError;
resultBool = FALSE;
if (owner && arg1) {
AppleDBDMAAudioDMAEngine *audioEngine = OSDynamicCast (AppleDBDMAAudioDMAEngine, owner);
if (audioEngine) {
resultBool = audioEngine->iSubEngine->open ((IOService *)arg1);
}
}
if (TRUE == resultBool) {
result = kIOReturnSuccess;
}
return result;
}