[plain text]
#include "AppleiSubEngine.h"
#include "AppleUSBAudioCommon.h"
#include "AppleUSBAudioLevelControl.h"
#include "AppleUSBAudioMuteControl.h"
#include "USBAudioObject.h"
#include <IOKit/audio/IOAudioStream.h>
#include <IOKit/audio/IOAudioEngine.h>
#include <IOKit/pwr_mgt/RootDomain.h>
#define kiSubFeatureUnitID 2
#define kiSubMuteControlChannelNum 0
#define kiSubVolumeControlMasterChannelNum 1
#define NUM_POWER_STATES 2
#define super IOService
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;
debug3IOLog ("+AppleiSubEngine[%p]::closeiSub (%p)\n", this, forClient);
if (forClient == audioEngine) {
if ((NULL != muteControl || NULL != iSubVolumeControl) && NULL != audioEngine) {
debugIOLog ("Removing iSub audio controls\n");
if (NULL != iSubVolumeControl) {
result = audioEngine->removeDefaultAudioControl (iSubVolumeControl);
iSubVolumeControl = NULL;
#if DEBUGLOG
if (kIOReturnSuccess != result) IOLog ("Error 0x%x removing left iSub control\n", result);
#endif
}
if (NULL != muteControl) {
result = audioEngine->removeDefaultAudioControl (muteControl);
muteControl = NULL;
#if DEBUGLOG
if (kIOReturnSuccess != result) IOLog ("Error 0x%x removing mute iSub control\n", result);
#endif
}
release (); audioEngine = NULL;
#if DEBUGLOG
} else {
if (NULL == muteControl) {IOLog ("NULL == muteControl\n");}
if (NULL == iSubVolumeControl) {IOLog ("NULL == iSubVolumeControl\n");}
if (NULL == audioEngine) {IOLog ("NULL == audioEngine\n");}
#endif
}
} else {
debugIOLog ("the wrong client tried to close us\n");
}
debug3IOLog ("-AppleiSubEngine[%p]::closeiSub (%p)\n", this, forClient);
}
void AppleiSubEngine::free (void) {
UInt32 frameIndex;
debug2IOLog ("+AppleiSubEngine[%p]::free ()\n", this);
if (NULL != thePipe) {
thePipe->release ();
thePipe = NULL;
}
if (NULL != sampleBufferDescriptor) {
sampleBufferDescriptor->release ();
sampleBufferDescriptor = NULL;
}
if (NULL != sampleBuffer) {
IOFree (sampleBuffer, bufferSize);
sampleBuffer = NULL;
}
for (frameIndex = 0; frameIndex < NUM_ISUB_FRAME_LISTS; frameIndex++) {
if (NULL != soundBuffer[frameIndex]) {
soundBuffer[frameIndex]->release ();
soundBuffer[frameIndex] = NULL;
}
}
super::free ();
debug2IOLog ("-AppleiSubEngine[%p]::free ()\n", this);
return;
}
bool AppleiSubEngine::openiSub (IOService * forClient) {
bool resultCode;
resultCode = FALSE;
FailIf (NULL != audioEngine, Exit);
audioEngine = OSDynamicCast (IOAudioEngine, forClient);
FailIf (NULL == audioEngine, Exit);
audioEngine->pauseAudioEngine ();
audioEngine->beginConfigurationChange ();
retain ();
iSubVolumeControl = AppleUSBAudioLevelControl::create (kiSubFeatureUnitID,
controlInterface->GetInterfaceNumber (),
VOLUME_CONTROL,
kiSubVolumeControlMasterChannelNum,
FALSE,
(USBDeviceRequest)&deviceRequest,
this,
kIOAudioLevelControlSubTypeLFEVolume,
kIOAudioControlUsageOutput);
FailIf (NULL == iSubVolumeControl, Exit);
audioEngine->addDefaultAudioControl (iSubVolumeControl);
muteControl = AppleUSBAudioMuteControl::create (kiSubFeatureUnitID,
controlInterface->GetInterfaceNumber (),
0,
(USBDeviceRequest)&deviceRequest,
this,
kIOAudioControlUsageOutput,
kIOAudioToggleControlSubTypeLFEMute);
FailIf (NULL == muteControl, Exit);
audioEngine->addDefaultAudioControl (muteControl);
audioEngine->completeConfigurationChange ();
audioEngine->resumeAudioEngine ();
resultCode = TRUE;
Exit:
debug4IOLog ("-AppleiSubEngine[%p]::handleOpen (%p, 0x%lx, %p) = %d\n", this, forClient, resultCode);
return resultCode;
}
bool AppleiSubEngine::init (OSDictionary * properties) {
Boolean resultCode;
debug3IOLog ("+AppleiSubEngine[%p]::init (%p)\n", this, properties);
resultCode = FALSE;
FailIf (FALSE == super::init (properties), Exit);
resultCode = TRUE;
mFormat.altInterface = kDefaultiSubAltInterface;
mFormat.numChannels = kDefaultNumChannels;
mFormat.bytesPerSample = kDefaultBytesPerSample;
mFormat.outputSampleRate = kDefaultOutputSampleRate;
Exit:
debug4IOLog ("-AppleiSubEngine[%p]::init (%p) = %d\n", 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}
};
debug3IOLog ("+AppleiSubEngine[%p]::start (%p)\n", this, provider);
resultBool = FALSE;
FailIf (FALSE == super::start (provider), Exit);
streamInterface = OSDynamicCast (IOUSBInterface, provider);
FailIf (NULL == streamInterface, Exit);
ourInterfaceNumber = streamInterface->GetInterfaceNumber ();
debug2IOLog ("AppleiSubEngine->ourInterfaceNumber = %d\n", ourInterfaceNumber);
alternateInterfaceID = mFormat.altInterface;
PMinit ();
streamInterface->joinPMtree (this);
if (pm_vars != NULL) {
registerPowerDriver (this, iSubPowerStates, NUM_POWER_STATES);
changePowerStateTo (1);
}
frameListSize = CalculateNumSamplesPerBuffer (mFormat.outputSampleRate, NUM_ISUB_FRAMES_PER_LIST) * mFormat.numChannels * mFormat.bytesPerSample;
debug4IOLog ("format = %ldHz, %ld channels, %ld bits\n", mFormat.outputSampleRate, mFormat.numChannels, mFormat.bytesPerSample * 8);
#if AML_DEBUGLOG
IOLog ("AppleiSubEngine::handleOpen: frameListSize = %d\n", frameListSize);
#endif
debug2IOLog ("frameListSize = %d\n", frameListSize);
bufferSize = CalculateNumSamplesPerBuffer (mFormat.outputSampleRate, NUM_ISUB_FRAMES_PER_LIST, NUM_ISUB_FRAME_LISTS) * mFormat.numChannels * mFormat.bytesPerSample;
debug2IOLog ("bufferSize = %d\n", bufferSize);
sampleBuffer = IOMallocAligned (round_page (bufferSize), PAGE_SIZE);
FailIf (NULL == sampleBuffer, Exit);
for (i = 0; i < bufferSize / sizeof(UInt16); i++) {
((UInt16*)sampleBuffer)[i] = 0;
}
sampleBufferDescriptor = IOMemoryDescriptor::withAddress (sampleBuffer, bufferSize, kIODirectionNone);
FailIf (NULL == sampleBufferDescriptor, Exit);
FailIf (kIOReturnSuccess != PrepareFrameLists (frameListSize), Exit);
shouldCloseStream = FALSE;
FailIf (FALSE == streamInterface->open (this), Exit);
streamOpened = TRUE;
resultIOReturn = streamInterface->SetAlternateInterface (this, kRootAlternateSetting);
FailIf (kIOReturnSuccess != resultIOReturn, Exit);
resultIOReturn = streamInterface->SetAlternateInterface (this, mFormat.altInterface);
FailIf (kIOReturnSuccess != resultIOReturn, Exit);
audioIsochEndpoint.type = kUSBIsoc;
audioIsochEndpoint.direction = kUSBOut;
thePipe = streamInterface->FindNextPipe (NULL, &audioIsochEndpoint);
FailIf (NULL == thePipe, Exit);
thePipe->retain ();
loopCount = 0;
numUSBFrameListsNotOutstanding = 0;
numQueued = 0;
currentFrameList = 0;
currentByteOffset = 0;
theFirstFrame = streamInterface->GetDevice()->GetBus()->GetFrameNumber () + kMinimumiSubFrameOffset;
for (frameListNum = currentFrameList; numQueued < NUM_ISUB_FRAME_LISTS_TO_QUEUE; frameListNum++) {
resultIOReturn = WriteFrameList (frameListNum);
FailIf (kIOReturnSuccess != resultIOReturn, Exit);
numQueued++;
}
debug2IOLog ("writeFrameList resultIOReturn = 0x%lx\n", resultIOReturn);
findRequest.bInterfaceClass = 1;
findRequest.bInterfaceSubClass = 1;
findRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
findRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare;
controlInterface = streamInterface->GetDevice()->FindNextInterface (NULL, &findRequest);
FailIf (NULL == controlInterface, Exit);
registerService ();
resultBool = TRUE;
Exit:
debug4IOLog ("-AppleiSubEngine[%p]::start (%p) = %d\n", this, provider, resultBool);
return resultBool;
}
bool AppleiSubEngine::willTerminate (IOService * provider, IOOptionBits options) {
bool resultCode;
debug5IOLog ("+AppleiSubEngine[%p]::willTerminate (%p, 0x%x) rc =\n", this, provider, options, getRetainCount ());
if (FALSE == iSubUSBRunning) {
if ((streamInterface != NULL) && (streamInterface == provider)) {
debugIOLog ("willTerminate closing iSub down\n");
if (NULL != thePipe) {
thePipe->release ();
thePipe = NULL;
}
streamInterface->close (this);
streamOpened = FALSE;
}
} else {
shouldCloseStream = TRUE;
}
resultCode = super::willTerminate (provider, options);
debug6IOLog ("-AppleiSubEngine[%p]::willTerminate (%p, 0x%x) = %d, rc=%d\n", this, provider, options, resultCode, getRetainCount ());
return resultCode;
}
IOReturn AppleiSubEngine::setPowerState (unsigned long powerStateOrdinal, IOService *device) {
IOReturn result;
debug4IOLog("AppleiSubEngine[%p]::setPowerState (%lu, %p)\n", this, powerStateOrdinal, device);
if (device == this) {
switch (powerStateOrdinal) {
case 0: debugIOLog ("sleeping...\n");
sleeping = TRUE;
StopiSub ();
result = IOPMNoErr;
break;
case 1: debugIOLog ("waking...\n");
sleeping = FALSE;
result = IOPMNoErr;
break;
default:
result = IOPMNoSuchState;
}
}
return result;
}
#pragma mark -iSub Routines-
UInt32 AppleiSubEngine::CalculateNumSamplesPerBuffer (UInt32 sampleRate, UInt32 theNumFramesPerList, UInt32 theNumFrameLists) {
UInt32 numSamplesPerFrameList;
UInt32 totalFrames;
UInt32 numAlternateFrames;
UInt32 numAverageFrames;
UInt16 averageSamplesPerFrame;
UInt16 additionalSampleFrameFreq;
CalculateSamplesPerFrame (sampleRate, &averageSamplesPerFrame, &additionalSampleFrameFreq);
if (0 == additionalSampleFrameFreq) {
numSamplesPerFrameList = averageSamplesPerFrame * theNumFramesPerList * theNumFrameLists;
} else {
totalFrames = theNumFramesPerList * theNumFrameLists;
numAlternateFrames = totalFrames / additionalSampleFrameFreq;
numAverageFrames = totalFrames - numAlternateFrames;
numSamplesPerFrameList = (numAverageFrames * averageSamplesPerFrame) + (numAlternateFrames * (averageSamplesPerFrame + 1));
}
return numSamplesPerFrameList;
}
void AppleiSubEngine::CalculateSamplesPerFrame (UInt32 sampleRate, UInt16 * averageSamplesPerFrame, UInt16 * additionalSampleFrameFreq) {
UInt32 divisor;
*averageSamplesPerFrame = sampleRate / 1000;
divisor = (sampleRate % 1000);
if (divisor)
*additionalSampleFrameFreq = 1000 / divisor;
else
*additionalSampleFrameFreq = 0;
}
IOReturn AppleiSubEngine::deviceRequest (IOUSBDevRequest * request, AppleiSubEngine * self, IOUSBCompletion * completion) {
IOReturn result;
debug4IOLog ("+AppleiSubEngine[%p]::deviceRequest (%p, %p)\n", self, request, completion);
result = kIOReturnSuccess;
if (self->streamInterface && request) {
result = self->streamInterface->DeviceRequest (request, completion);
}
debug4IOLog ("-AppleiSubEngine[%p]::deviceRequest (%p, %p)\n", self, request, completion);
return result;
}
UInt32 AppleiSubEngine::GetCurrentByteCount (void) {
return currentByteOffset;
}
UInt32 AppleiSubEngine::GetCurrentLoopCount (void) {
return loopCount;
}
IOMemoryDescriptor * AppleiSubEngine::GetSampleBuffer (void) {
return sampleBufferDescriptor;
}
IOReturn AppleiSubEngine::PrepareFrameLists (UInt32 frameListSize) {
IOReturn result;
UInt32 frameIndex;
result = kIOReturnError;
for (frameIndex = 0; frameIndex < NUM_ISUB_FRAME_LISTS; frameIndex++) {
usbCompletion[frameIndex].target = (void *)this;
usbCompletion[frameIndex].parameter = (void *)((UInt8 *)sampleBuffer + (frameIndex * frameListSize)); usbCompletion[frameIndex].action = WriteHandler;
soundBuffer[frameIndex] = IOMemoryDescriptor::withAddress ((UInt8 *)usbCompletion[frameIndex].parameter, frameListSize, kIODirectionNone);
FailIf (NULL == soundBuffer[frameIndex], Exit);
}
result = kIOReturnSuccess;
Exit:
return result;
}
IOReturn AppleiSubEngine::StartiSub (void) {
UInt32 frameListNum;
UInt16 numQueued = 0; IOReturn resultCode;
debug2IOLog ("+AppleiSubEngine[%p]::StartiSub ()\n", this);
FailWithAction (TRUE == sleeping, resultCode = kIOReturnOffline, Exit);
resultCode = kIOReturnError;
iSubRunning = TRUE;
#if ABORT_PIPE_ON_START
FailIf (NULL == thePipe, Exit);
thePipe->Abort (); iSubUSBRunning = FALSE;
#else
loopCount = 0xFFFFFFFF; currentFrameList = NUM_ISUB_FRAME_LISTS - 1;
#endif // ABORT_PIPE_ON_START
resultCode = kIOReturnSuccess;
if (FALSE == iSubUSBRunning) {
loopCount = 0;
numUSBFrameListsNotOutstanding = 0;
numQueued = 0;
currentFrameList = 0;
currentByteOffset = 0;
FailIf (NULL == streamInterface, Exit);
theFirstFrame = streamInterface->GetDevice()->GetBus()->GetFrameNumber () + kMinimumiSubFrameOffset;
for (frameListNum = currentFrameList; numQueued < NUM_ISUB_FRAME_LISTS_TO_QUEUE; frameListNum++) {
resultCode = WriteFrameList (frameListNum);
FailIf (kIOReturnSuccess != resultCode, Exit);
numQueued++;
}
}
Exit:
debug3IOLog ("-AppleiSubEngine[%p]::StartiSub (), result = %d\n", this, resultCode);
return resultCode;
}
IOReturn AppleiSubEngine::StopiSub (void) {
debug2IOLog ("+AppleiSubEngine[%p]::StopiSub ()\n", this);
iSubRunning = FALSE;
debug2IOLog ("-AppleiSubEngine[%p]::StopiSub ()\n", 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;
CalculateSamplesPerFrame (mFormat.outputSampleRate, &averageFrameSamples, &additionalSampleFrameFreq);
averageFrameSize = averageFrameSamples * mFormat.numChannels * mFormat.bytesPerSample;
alternateFrameSize = (averageFrameSamples + 1) * mFormat.numChannels * mFormat.bytesPerSample;
if (additionalSampleFrameFreq) {
for (frameIndex = 0; frameIndex < NUM_ISUB_FRAMES_PER_LIST; frameIndex++) {
theFrames[firstFrame + frameIndex].frStatus = -1;
if ((frameIndex % additionalSampleFrameFreq) == (UInt16)(additionalSampleFrameFreq - 1)) {
theFrames[firstFrame + frameIndex].frReqCount = alternateFrameSize;
} else {
theFrames[firstFrame + frameIndex].frReqCount = averageFrameSize;
}
theFrames[firstFrame + frameIndex].frActCount = 0;
}
} else {
for (frameIndex = 0; frameIndex < NUM_ISUB_FRAMES_PER_LIST; frameIndex++) {
theFrames[firstFrame + frameIndex].frStatus = -1;
theFrames[firstFrame + frameIndex].frReqCount = averageFrameSize;
theFrames[firstFrame + frameIndex].frActCount = 0;
}
}
FailIf (NULL == thePipe, Exit);
result = thePipe->Write (soundBuffer[frameListNum], theFirstFrame, NUM_ISUB_FRAMES_PER_LIST, &theFrames[firstFrame], &usbCompletion[frameListNum]);
if (result != kIOReturnSuccess) {
if (streamInterface) {
debug6IOLog ("++AppleiSubEngine[%p]::WriteFrameList (%d) - error writing to pipe at frame %lu - current = %lu: 0x%x\n", this, frameListNum, (UInt32)theFirstFrame, (UInt32)streamInterface->GetDevice()->GetBus()->GetFrameNumber(), result);
}
iSubUSBRunning = FALSE;
} else {
theFirstFrame += NUM_ISUB_FRAMES_PER_LIST;
iSubUSBRunning = TRUE;
}
Exit:
return result;
}
void AppleiSubEngine::WriteHandler (void * object, void * buffer, IOReturn result, IOUSBIsocFrame * pFrames) {
AppleiSubEngine * self;
UInt64 currentUSBFrame;
UInt32 frameListToWrite;
UInt32 i;
self = (AppleiSubEngine *)object;
FailIf (TRUE == self->sleeping, Exit);
if (result != kIOReturnSuccess) {
#if DEBUGLOG
IOLog ("++AppleiSubEngine::WriteHandler () - error 0x%x\n", result);
#endif
FailIf (NULL == self->streamInterface, Exit);
currentUSBFrame = self->streamInterface->GetDevice()->GetBus()->GetFrameNumber ();
switch (result) {
#if ABORT_PIPE_ON_START
case kIOReturnAborted:
#if AML_DEBUGLOG
IOLog ("AppleiSubEngine::WriteHandler() aborted.\n");
#endif
goto Exit; break;
case kIOReturnOverrun:
#else
case kIOReturnOverrun:
case kIOReturnAborted:
#endif
default:
if (self->theFirstFrame <= currentUSBFrame) {
self->theFirstFrame = currentUSBFrame + kMinimumiSubFrameOffset;
#if DEBUGLOG
IOLog ("+++AppleiSubEngine::skipping ahead to frame %ld\n", self->theFirstFrame);
#endif
}
break;
}
}
for (i = 0; i < self->frameListSize / sizeof(UInt16); i++) {
((UInt16*)buffer)[i] = 0;
}
if ((NUM_ISUB_FRAME_LISTS - 1) == self->currentFrameList) {
if (TRUE == self->iSubRunning) {
self->loopCount++;
self->currentByteOffset = 0;
}
self->currentFrameList = 0;
} else {
self->currentFrameList++;
if (TRUE == self->iSubRunning) {
self->currentByteOffset = self->currentFrameList * self->frameListSize;
}
}
if (FALSE == self->shouldCloseStream) {
frameListToWrite = self->currentFrameList + NUM_ISUB_FRAME_LISTS_TO_QUEUE - 1;
if (frameListToWrite >= NUM_ISUB_FRAME_LISTS) {
frameListToWrite -= NUM_ISUB_FRAME_LISTS;
}
self->WriteFrameList (frameListToWrite);
}
Exit:
if (TRUE == self->shouldCloseStream) {
#if DEBUGLOG
IOLog ("++AppleiSubEngine[%p]::WriteHandler () - stopping: %d\n", self, self->numUSBFrameListsNotOutstanding);
#endif
self->numUSBFrameListsNotOutstanding++;
if (self->numUSBFrameListsNotOutstanding == NUM_ISUB_FRAME_LISTS_TO_QUEUE) {
#if DEBUGLOG
IOLog ("iSub last write completed, closing streamInterface\n");
#endif
if (NULL != self->thePipe) {
self->thePipe->release ();
self->thePipe = NULL;
}
self->streamInterface->close (self);
self->streamOpened = FALSE;
self->iSubUSBRunning = FALSE;
}
}
return;
}
Generated by GNU enscript 1.6.4.