[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>
#define kiSubFeatureUnitID 2
#define kiSubMuteControlChannelNum 0
#define kiSubVolumeControlLeftChannelNum 1
#define kiSubVolumeControlRightChannelNum 2
#define CONTINUOUS_STREAMING TRUE
#define DEBUGTIMESTAMPS FALSE
#define super IOService
OSDefineMetaClassAndStructors (AppleiSubEngine, super)
#pragma mark -IOKit Routines-
void AppleiSubEngine::close (IOService * forClient, IOOptionBits options) {
debug4IOLog ("+AppleiSubEngine[%p]::close (%p, 0x%lx)\n", this, forClient, options);
if (NULL != audioEngine) {
debugIOLog ("removing iSub audio controls\n");
audioEngine->pauseAudioEngine ();
audioEngine->beginConfigurationChange ();
(void)audioEngine->removeDefaultAudioControl (leftVolumeControl);
(void)audioEngine->removeDefaultAudioControl (rightVolumeControl);
(void)audioEngine->removeDefaultAudioControl (muteControl);
audioEngine->completeConfigurationChange ();
audioEngine->resumeAudioEngine ();
audioEngine = NULL;
}
super::close (forClient, options);
debug4IOLog ("-AppleiSubEngine[%p]::close (%p, 0x%lx)\n", this, forClient, options);
}
bool AppleiSubEngine::finalize (IOOptionBits options) {
Boolean resultCode;
debug4IOLog ("+AppleiSubEngine[%p]::finalize (%p), rc=%d\n", this, options, getRetainCount ());
resultCode = super::finalize (options);
debug5IOLog ("-AppleiSubEngine[%p]::finalize (%p) = %d, rc=%d\n", this, options, resultCode, getRetainCount ());
return resultCode;
}
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::handleOpen (IOService * forClient, IOOptionBits options, void * arg) {
IOUSBFindInterfaceRequest findRequest;
IOUSBFindEndpointRequest audioIsochEndpoint;
IOReturn resultIOReturn;
UInt32 i;
#if CONTINUOUS_STREAMING
AbsoluteTime currentTime;
AbsoluteTime initialTimestampOffset;
UInt32 frameListNum;
UInt16 numQueued;
#endif
bool resultCode;
debug5IOLog ("+AppleiSubEngine[%p]::handleOpen (%p, 0x%lx, %p)\n", this, forClient, options, arg);
resultCode = FALSE;
FailIf (FALSE == super::handleOpen (forClient, options, arg), Exit);
FailIf (NULL == forClient, Exit);
audioEngine = (IOAudioEngine *)forClient;
frameListSize = CalculateNumSamplesPerBuffer (44100, NUM_ISUB_FRAMES_PER_LIST) * 4;
debug2IOLog ("frameListSize = %d\n", frameListSize);
bufferSize = CalculateNumSamplesPerBuffer (44100, NUM_ISUB_FRAMES_PER_LIST, NUM_ISUB_FRAME_LISTS) * 4;
debug2IOLog ("bufferSize = %d\n", bufferSize);
sampleBuffer = IOMallocAligned (round_page (bufferSize), PAGE_SIZE);
FailIf (NULL == sampleBuffer, Exit);
for (i = 0; i < bufferSize / 4; i++) {
((UInt32*)sampleBuffer)[i] = 0;
}
sampleBufferDescriptor = IOMemoryDescriptor::withAddress (sampleBuffer, bufferSize, kIODirectionNone);
FailIf (NULL == sampleBufferDescriptor, Exit);
FailIf (kIOReturnSuccess != PrepareFrameLists (frameListSize), Exit);
FailIf (FALSE == streamInterface->open (this), Exit);
resultIOReturn = streamInterface->SetAlternateInterface (this, kRootAlternateSetting);
FailIf (kIOReturnSuccess != resultIOReturn, Exit);
resultIOReturn = streamInterface->SetAlternateInterface (this, 4); FailIf (kIOReturnSuccess != resultIOReturn, Exit);
audioIsochEndpoint.type = kUSBIsoc;
audioIsochEndpoint.direction = kUSBOut;
thePipe = streamInterface->FindNextPipe (NULL, &audioIsochEndpoint);
FailIf (NULL == thePipe, Exit);
thePipe->retain ();
#if CONTINUOUS_STREAMING
loopCount = 0;
nanoseconds_to_absolutetime ((kMinimumiSubFrameOffset) * 1000 * 1000, &initialTimestampOffset);
clock_get_uptime (¤tTime);
ADD_ABSOLUTETIME (¤tTime, &initialTimestampOffset);
lastLoopTime.hi = currentTime.hi;
lastLoopTime.lo = currentTime.lo;
shouldStop = 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++;
}
#endif
findRequest.bInterfaceClass = 1;
findRequest.bInterfaceSubClass = 1;
findRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
findRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare;
controlInterface = streamInterface->GetDevice()->FindNextInterface (NULL, &findRequest);
FailIf (NULL == controlInterface, Exit);
audioEngine->pauseAudioEngine ();
audioEngine->beginConfigurationChange ();
leftVolumeControl = AppleUSBAudioLevelControl::create (kiSubFeatureUnitID,
controlInterface->GetInterfaceNumber (),
VOLUME_CONTROL,
kiSubVolumeControlLeftChannelNum,
(USBDeviceRequest)&deviceRequest,
this,
'subL',
kIOAudioControlUsageOutput);
FailIf (NULL == leftVolumeControl, Exit);
audioEngine->addDefaultAudioControl (leftVolumeControl);
leftVolumeControl->release ();
rightVolumeControl = AppleUSBAudioLevelControl::create (kiSubFeatureUnitID,
controlInterface->GetInterfaceNumber (),
VOLUME_CONTROL,
kiSubVolumeControlRightChannelNum,
(USBDeviceRequest)&deviceRequest,
this,
'subR',
kIOAudioControlUsageOutput);
FailIf (NULL == rightVolumeControl, Exit);
audioEngine->addDefaultAudioControl (rightVolumeControl);
rightVolumeControl->release ();
muteControl = AppleUSBAudioMuteControl::create (kiSubFeatureUnitID,
controlInterface->GetInterfaceNumber (),
0,
(USBDeviceRequest)&deviceRequest,
this,
kIOAudioControlUsageOutput,
'subM');
FailIf (NULL == muteControl, Exit);
audioEngine->addDefaultAudioControl (muteControl);
muteControl->release ();
audioEngine->completeConfigurationChange ();
audioEngine->resumeAudioEngine ();
resultCode = TRUE;
Exit:
debug5IOLog ("-AppleiSubEngine[%p]::handleOpen (%p, 0x%lx, %p)\n", this, forClient, options, arg);
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;
Exit:
debug4IOLog ("-AppleiSubEngine[%p]::init (%p) = %d\n", this, properties, resultCode);
return resultCode;
}
IOReturn AppleiSubEngine::message (UInt32 type, IOService * provider, void * arg) {
IOReturn resultCode;
debug4IOLog ("+AppleiSubEngine[%p]::message (0x%x, %p)\n", this, type, provider);
resultCode = kIOReturnSuccess;
switch (type) {
case kIOMessageServiceIsRequestingClose:
debugIOLog ("kIOMessageServiceIsRequestingClose\n");
case kIOMessageServiceIsTerminated:
if (kIOMessageServiceIsRequestingClose != type) debugIOLog ("kIOMessageServiceIsTerminated\n");
if ((NULL != streamInterface) && (streamInterface == provider)) {
debugIOLog ("stopping iSub\n");
shouldStop = 1;
streamInterface->close (this);
streamInterface = NULL;
}
break;
default:
;
}
debug4IOLog ("-AppleiSubEngine[%p]::message (0x%x, %p)\n", this, type, provider);
return resultCode;
}
bool AppleiSubEngine::start (IOService * provider) {
Boolean resultBool;
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 = 4;
interfaceLock = IORecursiveLockAlloc ();
FailIf (NULL == interfaceLock, Exit);
registerService ();
resultBool = TRUE;
Exit:
debug4IOLog ("-AppleiSubEngine[%p]::start (%p) = %d\n", this, provider, resultBool);
return resultBool;
}
void AppleiSubEngine::stop (IOService * provider) {
debug2IOLog("+AppleiSubEngine[%p]::stop ()\n", this);
shouldStop = 1;
super::stop (provider);
debug2IOLog("-AppleiSubEngine[%p]::stop ()\n", this);
return;
}
bool AppleiSubEngine::terminate (IOOptionBits options) {
Boolean resultCode;
debug4IOLog ("+AppleiSubEngine[%p]::terminate (0x%x), rc=%d\n", this, options, getRetainCount ());
resultCode = super::terminate (options);
debug5IOLog ("-AppleiSubEngine[%p]::terminate (0x%x) = %d, rc=%d\n", this, options, resultCode, getRetainCount ());
return resultCode;
}
#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) {
FailIf (NULL == self->interfaceLock, Exit);
IORecursiveLockLock (self->interfaceLock);
result = self->streamInterface->DeviceRequest (request, completion);
IORecursiveLockUnlock (self->interfaceLock);
}
debug4IOLog ("-AppleiSubEngine[%p]::deviceRequest (%p, %p)\n", self, request, completion);
Exit:
return result;
}
UInt32 AppleiSubEngine::GetCurrentByteCount (void) {
return currentByteOffset;
}
UInt16 AppleiSubEngine::GetCurrentFrameList (void) {
return currentFrameList;
}
UInt32 AppleiSubEngine::GetCurrentLoopCount (void) {
return loopCount;
}
IOMemoryDescriptor * AppleiSubEngine::GetSampleBuffer (void) {
return sampleBufferDescriptor;
}
volatile AbsoluteTime * AppleiSubEngine::GetLoopTime (void) {
return &lastLoopTime;
}
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) {
AbsoluteTime currentTime;
AbsoluteTime initialTimestampOffset;
UInt32 frameListNum;
UInt16 numQueued;
IOReturn resultCode;
debug2IOLog ("+AppleiSubEngine[%p]::StartiSub ()\n", this);
resultCode = kIOReturnError;
#if !CONTINUOUS_STREAMING
loopCount = 0;
nanoseconds_to_absolutetime ((kMinimumiSubFrameOffset) * 1000 * 1000, &initialTimestampOffset);
clock_get_uptime (¤tTime);
ADD_ABSOLUTETIME (¤tTime, &initialTimestampOffset);
lastLoopTime.hi = currentTime.hi;
lastLoopTime.lo = currentTime.lo;
shouldStop = 0;
numQueued = 0;
currentFrameList = 0;
currentByteOffset = 0;
theFirstFrame = streamInterface->GetDevice()->GetBus()->GetFrameNumber () + kMinimumiSubFrameOffset;
for (frameListNum = currentFrameList; numQueued < NUM_ISUB_FRAME_LISTS_TO_QUEUE; frameListNum++) {
resultCode = WriteFrameList (frameListNum);
FailIf (kIOReturnSuccess != resultCode, Exit);
numQueued++;
}
#else
iSubRunning = TRUE;
loopCount = 0xFFFFFFFF; clock_get_uptime (¤tTime);
lastLoopTime.hi = currentTime.hi;
lastLoopTime.lo = currentTime.lo;
currentFrameList = NUM_ISUB_FRAME_LISTS - 1;
currentByteOffset = 0;
resultCode = kIOReturnSuccess;
if (FALSE == iSubUSBRunning) {
#if DEBUGLOG
IOLog ("!!!iSub USB transport isn't running!!!\n");
#endif
loopCount = 0;
nanoseconds_to_absolutetime ((kMinimumiSubFrameOffset) * 1000 * 1000, &initialTimestampOffset);
clock_get_uptime (¤tTime);
ADD_ABSOLUTETIME (¤tTime, &initialTimestampOffset);
lastLoopTime.hi = currentTime.hi;
lastLoopTime.lo = currentTime.lo;
shouldStop = 0;
numQueued = 0;
currentFrameList = 0;
currentByteOffset = 0;
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++;
}
}
#endif
Exit:
debug3IOLog ("-AppleiSubEngine[%p]::StartiSub (), result = %d\n", this, resultCode);
return resultCode;
}
IOReturn AppleiSubEngine::StopiSub (void) {
debug2IOLog ("+AppleiSubEngine[%p]::StopiSub ()\n", this);
iSubRunning = FALSE;
#if !CONTINUOUS_STREAMING
shouldStop = 1;
#endif
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 (44100, &averageFrameSamples, &additionalSampleFrameFreq);
averageFrameSize = averageFrameSamples * 4;
alternateFrameSize = (averageFrameSamples + 1) * 4;
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;
}
}
retain (); result = thePipe->Write (soundBuffer[frameListNum], theFirstFrame, NUM_ISUB_FRAMES_PER_LIST, &theFrames[firstFrame], &usbCompletion[frameListNum]);
if (result != kIOReturnSuccess) {
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;
}
return result;
}
void AppleiSubEngine::WriteHandler (AppleiSubEngine * self, UInt32 * buffer, IOReturn result, IOUSBIsocFrame * pFrames) {
AbsoluteTime currentTime;
UInt64 currentUSBFrame;
UInt32 frameListToWrite;
UInt32 i;
#if DEBUGTIMESTAMPS
static AbsoluteTime lastLoopTime = {0, 0};
AbsoluteTime diff;
UInt64 nanos;
#endif
for (i = 0; i < self->frameListSize / 4; i++) {
buffer[i] = 0;
}
if (result != kIOReturnSuccess) {
#if DEBUGLOG
if (result != kIOReturnOverrun) {
IOLog ("++AppleiSubEngine::WriteHandler () - error 0x%x\n", result);
}
#endif
FailIf (NULL == self->streamInterface, Exit);
currentUSBFrame = self->streamInterface->GetDevice()->GetBus()->GetFrameNumber ();
switch (result) {
case kIOReturnOverrun:
case kIOReturnAborted:
default:
if (self->theFirstFrame <= currentUSBFrame) {
self->theFirstFrame = currentUSBFrame + kMinimumiSubFrameOffset;
#if DEBUGLOG
IOLog ("+++AppleiSubEngine::skipping ahead to frame %ld\n", self->theFirstFrame);
#endif
}
break;
}
}
if ((NUM_ISUB_FRAME_LISTS - 1) == self->currentFrameList) {
clock_get_uptime (¤tTime);
#if DEBUGTIMESTAMPS
diff.hi = currentTime.hi;
diff.lo = currentTime.lo;
SUB_ABSOLUTETIME (&diff, &lastLoopTime);
lastLoopTime.hi = currentTime.hi;
lastLoopTime.lo = currentTime.lo;
absolutetime_to_nanoseconds (diff, &nanos);
IOLog ("delta = %ld\n", (UInt32)(nanos / (1000 * 1000)));
#endif
self->lastLoopTime.hi = currentTime.hi;
self->lastLoopTime.lo = currentTime.lo;
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 (self->shouldStop > 0) {
debug3IOLog ("++AppleiSubEngine[%p]::WriteHandler () - stopping: %d\n", self, self->shouldStop);
self->shouldStop++;
} else {
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:
self->release ();
return;
}
Generated by GNU enscript 1.6.4.