AppleAC97AudioDriver.cpp [plain text]
#include <sys/systm.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOWorkLoop.h>
#include <IOKit/audio/IOAudioPort.h>
#include "AppleAC97AudioDriver.h"
#include "IOAC97Debug.h"
#define kIdleTimerSeconds 63
#define CLASS AppleAC97AudioDriver
#define super IOAudioDevice
OSDefineMetaClassAndStructors( AppleAC97AudioDriver, IOAudioDevice )
IOService * CLASS::probe( IOService * provider, SInt32 * score )
{
IOAC97AudioCodec * codec;
if (super::probe(provider, score) == 0)
return 0;
codec = OSDynamicCast( IOAC97AudioCodec, provider );
if (codec && (codec->getCodecID() == 0))
{
return this;
}
return 0;
}
bool CLASS::initHardware( IOService * provider )
{
const OSString * rootName;
const OSString * codecName;
DebugLog("%s::%s(%p)\n", getName(), __FUNCTION__, provider);
if (super::initHardware(provider) == false)
{
goto fail;
}
fPowerChangeThreadCall = thread_call_allocate(
(thread_call_func_t) powerChangeThreadHandler,
(thread_call_param_t) this );
if (fPowerChangeThreadCall == 0)
goto fail;
currentPowerState = kIOAudioDeviceActive;
fAudioCodec = OSDynamicCast( IOAC97AudioCodec, provider );
if ( fAudioCodec == 0 )
{
goto fail;
}
fAudioRoot = fAudioCodec->getAudioController();
if ( fAudioRoot == 0 )
{
goto fail;
}
setDeviceTransportType(kIOAudioDeviceTransportTypeBuiltIn);
setDeviceName("AC97 Built-in Audio");
setDeviceShortName("AC97Audio");
rootName = OSDynamicCast(OSString,
fAudioRoot->getProperty(kIOAC97HardwareNameKey));
codecName = OSDynamicCast(OSString,
fAudioCodec->getProperty(kIOAC97HardwareNameKey));
if (rootName && codecName)
{
char * nameBuf;
int nameLength = rootName->getLength() + codecName->getLength() +
strlen(" / ") + 1;
if ((nameBuf = (char *)IOMalloc(nameLength)))
{
snprintf(nameBuf, nameLength, "%s / %s",
rootName->getCStringNoCopy(),
codecName->getCStringNoCopy());
setManufacturerName(nameBuf);
IOFree(nameBuf, nameLength);
}
}
if ( fAudioCodec->open( this ) == false )
{
DebugLog("%s: codec open error\n", getName());
goto fail;
}
if ( createAudioEngines() == false )
{
DebugLog("%s: createAudioEngine() error\n", getName());
goto fail;
}
if ( createAudioPorts() == false )
{
DebugLog("%s: createAudioPorts() error\n", getName());
goto fail;
}
if ( engageAudioEngines() == false )
{
DebugLog("%s: engageAudioEngines() error\n", getName());
goto fail;
}
setIdleAudioSleepTime( kIdleTimerSeconds * 1000000000ULL );
return true;
fail:
if ( fAudioCodec )
{
fAudioCodec->close( this );
fAudioCodec = 0;
}
return false;
}
#ifndef RELEASE
#define RELEASE(x) do { if (x) { (x)->release(); (x) = 0; } } while (0)
#endif
void CLASS::free()
{
RELEASE( fEnginePCMOut );
RELEASE( fEnginePCMIn );
RELEASE( fEngineSPDIF );
RELEASE( fInputAudioPort );
RELEASE( fOutputAudioPort );
if ( fPowerChangeThreadCall )
{
thread_call_free( fPowerChangeThreadCall );
fPowerChangeThreadCall = 0;
}
fAudioCodec = 0;
fAudioRoot = 0;
super::free();
}
IOReturn CLASS::message( UInt32 type,
IOService * provider,
void * arg )
{
DebugLog("%s::%s type=%lx provider=%p arg=%p\n", getName(), __FUNCTION__,
type, provider, arg);
if ( ( provider )
&& ( provider == fAudioCodec )
&& ( type == kIOMessageServiceIsTerminated ) )
{
provider->close( this );
return kIOReturnSuccess;
}
return super::message( type, provider, arg );
}
bool CLASS::createAudioEngines( void )
{
DebugLog("%s::%s\n", getName(), __FUNCTION__);
fEnginePCMOut = new AppleAC97AudioEnginePCMOut;
if (fEnginePCMOut && !fEnginePCMOut->init(this, fAudioCodec))
{
fEnginePCMOut->release();
fEnginePCMOut = 0;
}
fEnginePCMIn = new AppleAC97AudioEnginePCMIn;
if (fEnginePCMIn && !fEnginePCMIn->init(this, fAudioCodec))
{
fEnginePCMIn->release();
fEnginePCMIn = 0;
}
fEngineSPDIF = new AppleAC97AudioEngineSPDIF;
if (fEngineSPDIF && !fEngineSPDIF->init(this, fAudioCodec))
{
fEngineSPDIF->release();
fEngineSPDIF = 0;
}
return (fEnginePCMOut && fEnginePCMIn);
}
bool CLASS::createAudioPorts( void )
{
bool success = false;
DebugLog("%s::%s\n", getName(), __FUNCTION__);
do {
fOutputAudioPort = IOAudioPort::withAttributes(
kIOAudioPortTypeOutput,
"Master Output Port" );
if ( fOutputAudioPort == 0 )
break;
fInputAudioPort = IOAudioPort::withAttributes(
kIOAudioPortTypeInput,
"Master Input Port" );
if ( fInputAudioPort == 0 )
break;
success = true;
}
while ( false );
return success;
}
bool CLASS::engageAudioEngines( void )
{
DebugLog("%s::%s\n", getName(), __FUNCTION__);
IOAC97Assert(fEnginePCMOut != 0);
IOAC97Assert(fEnginePCMIn != 0);
if (activateAudioEngine( fEnginePCMOut ) != kIOReturnSuccess)
{
DebugLog("%s: activateAudioEngine PCMOut error\n", getName());
return false;
}
else
{
attachAudioPort( fOutputAudioPort, fEnginePCMOut, 0 );
}
if (activateAudioEngine( fEnginePCMIn ) != kIOReturnSuccess)
{
DebugLog("%s: activateAudioEngine PCMIn error\n", getName());
fEnginePCMIn->release();
fEnginePCMIn = 0;
}
else
{
attachAudioPort( fInputAudioPort, 0, fEnginePCMIn );
}
if (fEngineSPDIF)
{
if (activateAudioEngine( fEngineSPDIF ) != kIOReturnSuccess)
{
DebugLog("%s: activateAudioEngine SPDIF error\n", getName());
fEngineSPDIF->release();
fEngineSPDIF = 0;
}
else
{
attachAudioPort( fOutputAudioPort, fEngineSPDIF, 0 );
}
}
return true;
}
#pragma mark -
#pragma mark еее Power Management еее
#pragma mark -
IOReturn CLASS::performPowerStateChange(
IOAudioDevicePowerState oldPowerState,
IOAudioDevicePowerState newPowerState,
UInt32 * microsecondsUntilComplete )
{
DebugLog("%s::%s old=%d new=%d\n", getName(), __FUNCTION__,
oldPowerState, newPowerState);
*microsecondsUntilComplete = 15 * 1000 * 1000;
retain();
if ( thread_call_enter( fPowerChangeThreadCall ) == TRUE )
{
release(); }
return kIOReturnSuccess;
}
void CLASS::powerChangeThreadHandler( thread_call_param_t param0,
thread_call_param_t param1 )
{
IOWorkLoop * workLoop;
CLASS * driver = (CLASS *) param0;
if (( workLoop = driver->getWorkLoop() ))
{
workLoop->runAction( powerChangeThreadAction, driver );
}
driver->completePowerStateChange();
driver->release();
}
IOReturn CLASS::powerChangeThreadAction( OSObject * target,
void * arg0, void * arg1,
void * arg2, void * arg3 )
{
CLASS * driver = (CLASS *) target;
int oldPowerState = (int) driver->getPowerState();
int newPowerState = (int) driver->getPendingPowerState();
DebugLog("%s::%s old=%d new=%d\n", driver->getName(), __FUNCTION__,
oldPowerState, newPowerState);
if ( newPowerState > oldPowerState )
{
while ( oldPowerState++ != newPowerState )
{
driver->raisePowerState( oldPowerState );
}
}
else if ( newPowerState < oldPowerState )
{
while ( oldPowerState-- != newPowerState )
{
driver->lowerPowerState( oldPowerState );
}
}
return kIOReturnSuccess;
}
void CLASS::lowerPowerState( int newPowerState )
{
DebugLog("%s::%s (%d)\n", getName(), __FUNCTION__, newPowerState);
switch ( newPowerState )
{
case kIOAudioDeviceIdle:
fAudioCodec->lowerPowerState(IOAC97AudioCodec::kPowerStateIdle);
break;
case kIOAudioDeviceSleep:
if (fEnginePCMOut)
fEnginePCMOut->handlePowerStateChange(
AppleAC97AudioEngine::kPowerStateSleep);
if (fEnginePCMIn)
fEnginePCMIn->handlePowerStateChange(
AppleAC97AudioEngine::kPowerStateSleep);
if (fEngineSPDIF)
fEngineSPDIF->handlePowerStateChange(
AppleAC97AudioEngine::kPowerStateSleep);
fAudioCodec->lowerPowerState(IOAC97AudioCodec::kPowerStateSleep);
break;
default:
IOAC97Assert(false);
}
}
void CLASS::raisePowerState( int newPowerState )
{
DebugLog("%s::%s (%d)\n", getName(), __FUNCTION__, newPowerState);
switch ( newPowerState )
{
case kIOAudioDeviceIdle:
fAudioCodec->raisePowerState(IOAC97AudioCodec::kPowerStateIdle);
if (fEnginePCMOut)
fEnginePCMOut->handlePowerStateChange(
AppleAC97AudioEngine::kPowerStateActive);
if (fEnginePCMIn)
fEnginePCMIn->handlePowerStateChange(
AppleAC97AudioEngine::kPowerStateActive);
if (fEngineSPDIF)
fEngineSPDIF->handlePowerStateChange(
AppleAC97AudioEngine::kPowerStateActive);
flushAudioControls();
break;
case kIOAudioDeviceActive:
fAudioCodec->raisePowerState(IOAC97AudioCodec::kPowerStateActive);
break;
default:
IOAC97Assert(false);
}
}
IOReturn CLASS::acknowledgePowerChange(
IOService * whichDriver )
{
DebugLog("%s::%s [ %d->%d ]\n", getName(), __FUNCTION__,
getPowerState(), getPendingPowerState());
if ( getPendingPowerState() != kIOAudioDeviceSleep &&
getPowerState() != kIOAudioDeviceSleep )
{
DebugLog("%s: ** Dropped ack for non-sleep state transition **\n",
getName());
return kIOReturnSuccess;
}
return acknowledgeSetPowerState();
}
IOReturn CLASS::protectedSetPowerState(
unsigned long powerStateOrdinal, IOService * device )
{
IOReturn ret = super::protectedSetPowerState(powerStateOrdinal, device);
DebugLog("%s::%s state = %ld, ret = %lx\n",
getName(), __FUNCTION__, powerStateOrdinal, ret);
return ret;
}