//-------------------------------------------------------------------------------- // // File: AppleUSBAudioDevice.cpp // // Contains: Support for the USB Audio Class Control Interface. // This includes support for exporting device controls // to the Audio HAL such as Volume, Bass, Treble and // Mute. // // Future support will include parsing of the device // topology and exporting of all appropriate device // control functions through the Audio HAL. // // Technology: OS X // //-------------------------------------------------------------------------------- #define TIMERSTREAM FALSE #define DOUBLEPARENT TRUE #include "AppleUSBAudioCommon.h" #include "AppleUSBAudioDevice.h" #include "AppleUSBAudioEngine.h" #include "AppleUSBAudioLevelControl.h" #include "AppleUSBAudioMuteControl.h" #include "USBAudioObject.h" #include #include #include #include #include #include #include #include #define super IOAudioDevice OSDefineMetaClassAndStructors (AppleUSBAudioDevice, super) bool AppleUSBAudioDevice::init (OSDictionary * properties) { bool resultCode; debug2IOLog ("+AppleUSBAudioDevice[%p]::init ()\n", this); resultCode = FALSE; // Assume failure // get the IOAudioDevice generic initialization FailIf (FALSE == super::init (properties), Exit); resultCode = TRUE; Exit: debug2IOLog ("-AppleUSBAudioDevice[%p]::init ()\n", this); return resultCode; } void AppleUSBAudioDevice::free () { debug2IOLog ("+AppleUSBAudioDevice[%p]::free ()\n", this); #if DEBUG if (interfaceVendor && interfaceVendor->getRetainCount () == 0) Debugger ("interfaceVendor = 0"); if (interfaceProduct && interfaceProduct->getRetainCount () == 0) Debugger ("interfaceProduct = 0"); if (deviceReleaseNumber && deviceReleaseNumber->getRetainCount () == 0) Debugger ("deviceReleaseNumber = 0"); if (configurationValue && configurationValue->getRetainCount () == 0) Debugger ("configurationValue = 0"); if (interfaceNumber && interfaceNumber->getRetainCount () == 0) Debugger ("interfaceNumber = 0"); if (usbAudio && usbAudio->getRetainCount () == 0) Debugger ("usbAudio = 0"); #endif if (interfaceLock) { IORecursiveLockFree (interfaceLock); interfaceLock = NULL; } #if TIMERSTREAM if (deviceNotifier) { deviceNotifier->remove (); deviceNotifier = 0; } #endif if (interfaceVendor) { interfaceVendor->release (); interfaceVendor = 0; } if (interfaceProduct) { interfaceProduct->release (); interfaceProduct = 0; } if (deviceReleaseNumber) { deviceReleaseNumber->release(); deviceReleaseNumber = 0; } if (configurationValue) { configurationValue->release(); configurationValue = 0; } if (interfaceNumber) { interfaceNumber->release(); interfaceNumber = 0; } if (usbAudio) { usbAudio->release (); usbAudio = 0; } super::free (); debug2IOLog ("-AppleUSBAudioDevice[%p]::free ()\n", this); } #if DOUBLEPARENT bool AppleUSBAudioDevice::requestTerminate (IOService * provider, IOOptionBits options) { debug4IOLog ("+AppleUSBAudioDevice[%p]::requestTerminate (%p, %x)\n", this, provider, options); debug4IOLog ("-AppleUSBAudioDevice[%p]::requestTerminate (%p, %x)\n", this, provider, options); return TRUE; // it is OK to terminate us } #endif bool AppleUSBAudioDevice::start (IOService * provider) { OSObject * obj; UInt8 stringIndex; char string[kStringBufferSize]; IOReturn err; Boolean resultCode; debug3IOLog ("+AppleUSBAudioDevice[%p]::start (%p)\n", this, provider); resultCode = FALSE; controlInterface = OSDynamicCast (IOUSBInterface, provider); FailIf (NULL == controlInterface, Exit); previousInterface = controlInterface; debug2IOLog ("There are %d configurations on this device\n", controlInterface->GetDevice()->GetNumConfigurations ()); usbAudio = USBAudioConfigObject::create (controlInterface->GetDevice()->GetFullConfigurationDescriptor (0)); FailIf (NULL == usbAudio, Exit); // If this is an iSub, we need to not go any further because we don't support it in this driver // This will cause the driver to not load on any device that has _only_ a low frequency effect output terminal FailIf (usbAudio->GetNumOutputTerminals (0, 0) == 1 && usbAudio->GetIndexedOutputTerminalType (0, 0, 0) == OUTPUT_LOW_FREQUENCY_EFFECTS_SPEAKER, Exit); // IOLog ("This isn't an iSub\n"); err = kIOReturnError; string[0] = 0; stringIndex = controlInterface->GetDevice()->GetProductStringIndex (); if (0 != stringIndex) { err = controlInterface->GetDevice()->GetStringDescriptor (stringIndex, string, kStringBufferSize); } if (0 == string[0] || kIOReturnSuccess != err) { strcpy (string, "Unknown USB Audio Device"); } setDeviceName (string); err = kIOReturnError; string[0] = 0; stringIndex = controlInterface->GetDevice()->GetManufacturerStringIndex (); if (0 != stringIndex) { err = controlInterface->GetDevice()->GetStringDescriptor (stringIndex, string, kStringBufferSize); } if (0 == string[0] || kIOReturnSuccess != err) { strcpy (string, "Unknown Manufacturer"); } setManufacturerName (string); FailIf (FALSE == controlInterface->open (this), Exit); // get the IOAudioDevice generic start routine FailIf (FALSE == super::start (provider), Exit); interfaceLock = IORecursiveLockAlloc (); FailIf (NULL == interfaceLock, Exit); if (obj = controlInterface->getProperty (kUSBVendorName)) { obj->retain (); interfaceVendor = obj; } if (obj = controlInterface->getProperty (kUSBProductName)) { obj->retain (); interfaceProduct = obj; } if (obj = controlInterface->getProperty (kUSBDeviceReleaseNumber)) { obj->retain (); deviceReleaseNumber = obj; } if (obj = controlInterface->getProperty (kUSBConfigurationValue)) { obj->retain(); configurationValue = obj; } if (obj = controlInterface->getProperty (kUSBInterfaceNumber)) { obj->retain (); interfaceNumber = obj; } resultCode = TRUE; Exit: debug3IOLog ("-AppleUSBAudioDevice[%p]::start (%p)\n", this, provider); return resultCode; } #if 0 UInt32 AppleUSBAudioDevice::getNumAttachedAudioDevices () { OSDictionary * audioDeviceMatchingDict; UInt32 numAudioDevices; debugIOLog ("+AppleUSBAudioDevice::getNumAttachedAudioDevices ()\n"); numAudioDevices = 0; audioDeviceMatchingDict = serviceMatching ("IOAudioDevice"); if (audioDeviceMatchingDict) { OSIterator * deviceIterator; deviceIterator = getMatchingServices (audioDeviceMatchingDict); if (deviceIterator) { IOAudioDevice *device; while (device = (IOAudioDevice *)deviceIterator->getNextObject ()) { if (device != this) { numAudioDevices++; } } deviceIterator->release (); } else { debugIOLog ("++AppleUSBAudioDevice::getNumAttachedAudioDevices () - no device iterator created\n"); } audioDeviceMatchingDict->release (); } else { debugIOLog ("++AppleUSBAudioDevice::getNumAttachedAudioDevices () - no matching dict created\n"); } debugIOLog ("-AppleUSBAudioDevice::getNumAttachedAudioDevices ()\n"); return numAudioDevices; } #endif void AppleUSBAudioDevice::streamStopped (IOAudioEngine *audioEngine) { // This must be called from within the workLoop // The AppleUSBAudioEngine does so from _setState() // which has the same requirement debugIOLog ("+AppleUSBAudioDevice::streamStopped ()\n"); if (allStreamsStopped()) { terminate(); } debugIOLog ("-AppleUSBAudioDevice::streamStopped ()\n"); } #if TIMERSTREAM bool AppleUSBAudioDevice::isUnattachedDeviceRegistered () { debugIOLog ("AppleUSBAudioDevice::isUnattachedDeviceRegistered ()\n"); return (deviceNotifier != NULL); } void AppleUSBAudioDevice::registerUnattachedDevice () { debugIOLog ("+AppleUSBAudioDevice::registerUnattachedDevice ()\n"); if (!deviceNotifier) { OSDictionary * matchingDict; matchingDict = serviceMatching ("IOUSBInterface"); matchingDict->setObject (kUSBVendorName, interfaceVendor); matchingDict->setObject (kUSBProductName, interfaceProduct); matchingDict->setObject (kUSBDeviceReleaseNumber, deviceReleaseNumber); matchingDict->setObject (kUSBConfigurationValue, configurationValue); matchingDict->setObject (kUSBInterfaceNumber, interfaceNumber); matchingDict->setObject (gIOMatchCategoryKey, (OSSymbol *)gIODefaultMatchCategoryKey); deviceNotifier = addNotification(gIOPublishNotification, matchingDict, (IOServiceNotificationHandler) &interfacePublished, this, NULL, 1000); // matchingDict->release(); } else { debugIOLog ("++AppleUSBAudioDevice::registerUnattachedDevice () - error - already have a device notifier - inconsistent state.\n"); } debugIOLog ("-AppleUSBAudioDevice::registerUnattachedDevice ()\n"); } void AppleUSBAudioDevice::unregisterUnattachedDevice () { debugIOLog("+AppleUSBAudioDevice::unregisterUnattachedDevice ()\n"); if (deviceNotifier) { deviceNotifier->remove (); deviceNotifier = 0; } else { debugIOLog("++AppleUSBAudioDevice::unregisterUnattachedDevice () - error - don't have a device notifier - inconsistent state.\n"); } debugIOLog("-AppleUSBAudioDevice::unregisterUnattachedDevice ()\n"); } bool AppleUSBAudioDevice::interfacePublished (AppleUSBAudioDevice *audioDevice, void *ref, IOService *newService) { IOUSBInterface * interface; bool result; IOCommandGate *cg; result = TRUE; debugIOLog ("+AppleUSBAudioDevice::interfacePublished ()\n"); if (interface = OSDynamicCast (IOUSBInterface, newService)) { cg = audioDevice->getCommandGate(); if (cg) { cg->runAction(reinitWithInterfaceAction, interface, (void *)&result); } } debugIOLog ("-AppleUSBAudioDevice::interfacePublished ()\n"); return result; } IOReturn AppleUSBAudioDevice::reinitWithInterfaceAction(OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4) { IOReturn result = kIOReturnBadArgument; if (owner && arg1 && arg2) { AppleUSBAudioDevice *audioDevice = OSDynamicCast(AppleUSBAudioDevice, owner); IOUSBInterface *interface = OSDynamicCast(IOUSBInterface, (OSObject *)arg1); if (audioDevice) { *(bool *)arg2 = audioDevice->reinitWithInterface(interface); result = kIOReturnSuccess; } } return result; } bool AppleUSBAudioDevice::reinitWithInterface (IOUSBInterface *interface) { OSCollectionIterator * audioEngineIterator; AppleUSBAudioEngine * audioEngine; OSCollectionIterator * portIterator; IOAudioPort * audioPort; OSCollectionIterator * controlIterator; IOAudioControl * audioControl; AppleUSBAudioLevelControl * levelControl; AppleUSBAudioMuteControl * muteControl; bool result; debug3IOLog("+AppleUSBAudioDevice[%p]::reinitWithInterface(%p)\n", this, interface); result = FALSE; FailIf (interface == previousInterface, Exit); FailIf (kIOReturnSuccess != interface->open (this), Exit); debug3IOLog("+AppleUSBAudioDevice[%p]::reinitWithInterface(%p) - 1\n", this, interface); controlInterface = interface; previousInterface = interface; this->attach (controlInterface); unregisterUnattachedDevice (); if (audioEngines) { // We need to do this stuff on the IOWorkLoop // need to tell streams not to notify us when stopped - there's a potential race here // because we've already committed to the new interface, but haven't told the streams // yet - we could still get a notification which could cause us to stop // probably need another lock to make sure that doesn't happen audioEngineIterator = OSCollectionIterator::withCollection (audioEngines); if (audioEngineIterator) { while (audioEngine = (AppleUSBAudioEngine *)audioEngineIterator->getNextObject ()) { if (OSDynamicCast (AppleUSBAudioEngine, audioEngine)) { audioEngine->notifyDeviceOfStop = FALSE; } } audioEngineIterator->release (); } } debug3IOLog("+AppleUSBAudioDevice[%p]::reinitWithInterface(%p) - 4\n", this, interface); // Force control updates so that the device is set to the correct volume, etc... if (audioPorts) { // No portsLock anymore - need to be on IOWorkLoop portIterator = OSCollectionIterator::withCollection (audioPorts); if (portIterator) { while (audioPort = (IOAudioPort *)portIterator->getNextObject ()) { if (OSDynamicCast (IOAudioPort, audioPort)) { if (audioPort->audioControls) { controlIterator = OSCollectionIterator::withCollection (audioPort->audioControls); if (controlIterator) { while (audioControl = (IOAudioControl *)controlIterator->getNextObject ()) { if (levelControl = OSDynamicCast (AppleUSBAudioLevelControl, audioControl)) { levelControl->updateUSBValue (); } else if (muteControl = OSDynamicCast (AppleUSBAudioMuteControl, audioControl)) { muteControl->updateUSBValue (); } } controlIterator->release (); } } } } portIterator->release (); } } debug3IOLog ("++AppleUSBAudioDevice[%p]::reinitWithInterface (%p) - successfully attached to new interface\n", this, interface); result = TRUE; Exit: debug3IOLog ("-AppleUSBAudioDevice[%p]::reinitWithInterface (%p)\n", this, interface); return result; } #endif bool AppleUSBAudioDevice::allStreamsStopped () { OSCollectionIterator * audioEngineIterator; IOAudioEngine * audioEngine; bool result; debug2IOLog ("+AppleUSBAudioDevice[%p]::allStreamsStopped ()\n", this); result = TRUE; FailIf (NULL == audioEngines, Exit); // Need to do this on the IOWorkLoop audioEngineIterator = OSCollectionIterator::withCollection (audioEngines); if (audioEngineIterator) { while (audioEngine = (IOAudioEngine *)audioEngineIterator->getNextObject ()) { if (OSDynamicCast (AppleUSBAudioEngine, audioEngine)) { // We only care that the AppleUSBAudioEngines are stopped if (audioEngine->getState () != kIOAudioEngineStopped) { result = FALSE; break; } } } audioEngineIterator->release (); } Exit: debug2IOLog ("-AppleUSBAudioDevice[%p]::allStreamsStopped ()\n", this); return result; } IOReturn AppleUSBAudioDevice::performPowerStateChange (IOAudioDevicePowerState oldPowerState, IOAudioDevicePowerState newPowerState, UInt32 *microSecsUntilComplete) { IOReturn result; debug4IOLog ("+AppleUSBAudioDevice::performPowerStateChange (%d, %d, %p)\n", oldPowerState, newPowerState, microSecsUntilComplete); result = super::performPowerStateChange (oldPowerState, newPowerState, microSecsUntilComplete); if (oldPowerState == kIOAudioDeviceSleep) { debugIOLog ("Waking from sleep - flushing controls to the device.\n"); flushAudioControls (); } return result; } #if TIMERSTREAM IOReturn AppleUSBAudioDevice::performUnattachedDeviceRegistrationAction (OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4) { IOReturn result; AppleUSBAudioDevice * audioDevice; result = kIOReturnBadArgument; if (owner && arg1) { audioDevice = OSDynamicCast (AppleUSBAudioDevice, owner); if (audioDevice) { *(bool *)arg1 = audioDevice->performUnattachedDeviceRegistration (); result = kIOReturnSuccess; } } return result; } bool AppleUSBAudioDevice::performUnattachedDeviceRegistration () { OSCollectionIterator *audioEngineIterator; AppleUSBAudioEngine *audioEngine; bool isRegistered; isRegistered = FALSE; // We should stick around if we have a running audio engine if (audioEngines) { debugIOLog("++AppleUSBAudioDevice::performUnattachedDeviceRegistration () - we're the only device - checking to see if any audioEngines are running.\n"); // We're stopping - do we need to lock this stuff? on IOWorkLoop? audioEngineIterator = OSCollectionIterator::withCollection (audioEngines); if (audioEngineIterator) { while (audioEngine = (AppleUSBAudioEngine *)audioEngineIterator->getNextObject ()) { if (OSDynamicCast (AppleUSBAudioEngine, audioEngine)) { // We only care about runninng AppleUSBAudioEngine objects // Nothing more to lock here - definitely need to be on the IOWorkLoop if (audioEngine->getState () == kIOAudioEngineRunning) { debug2IOLog ("+++AppleUSBAudioDevice::performUnattachedDeviceRegistration () - found a running audio engine: %p - sticking around until a re-attach or all audio engines stop.\n", audioEngine); isRegistered = TRUE; audioEngine->notifyDeviceOfStop = TRUE; if (!isUnattachedDeviceRegistered ()) { // register for new USB device notification registerUnattachedDevice (); if (!hasBeenUnattached) { retain (); hasBeenUnattached = TRUE; } } break; } } } audioEngineIterator->release (); } } return isRegistered; } #endif void AppleUSBAudioDevice::stop (IOService *provider) { bool shouldStop; #if TIMERSTREAM debug6IOLog ("+AppleUSBAudioDevice[%p]::stop (%p) - audioEngines = %p - deviceNotifier = %p - rc=%d\n", this, provider, audioEngines, deviceNotifier, getRetainCount()); #else debug5IOLog ("+AppleUSBAudioDevice[%p]::stop (%p) - audioEngines = %p - rc=%d\n", this, provider, audioEngines, getRetainCount()); #endif shouldStop = TRUE; #if TIMERSTREAM if (!isInactive()) { // We haven't been terminated, so we need to check to see if we are still running IOCommandGate *cg; cg = getCommandGate(); if (cg) { bool isRegistered; if (cg->runAction(performUnattachedDeviceRegistrationAction, (void *)&isRegistered) == kIOReturnSuccess) { shouldStop = !isRegistered; } } } #endif if (shouldStop) { performStop (provider); } debug2IOLog("-AppleUSBAudioDevice[%p]::stop ()\n", this); } // Return FALSE if you don't want PRAM updated on a volume change, TRUE if you want it updated. // Only update PRAM if we're on a Cube and the speakers are Cube, SoundSticks, or Mirconas (somethings). Boolean AppleUSBAudioDevice::ShouldUpdatePRAM (void) { const IORegistryPlane * usbPlane; IORegistryEntry * usbRegEntry; OSObject * obj; OSNumber * number; UInt32 locationID; UInt16 productID; UInt16 vendorID; Boolean speakersGood; Boolean connectionGood; Boolean result; // Assume failure result = FALSE; speakersGood = FALSE; connectionGood = FALSE; // Make sure they're speakers that can support boot beep vendorID = controlInterface->GetDevice()->GetVendorID (); debug2IOLog ("+ ShouldUpdatePRAM\nspeaker's vendorID = 0x%x\n", vendorID); if (kIOUSBVendorIDAppleComputer == vendorID || kIOUSBVendorIDHaronKardon == vendorID || kIOUSBVendorMicronas == vendorID) { speakersGood = TRUE; } debug2IOLog ("speakersGood = %d\n", speakersGood); // They have to be plugged into a root hub or a hub in monitor that can support boot beep if (TRUE == speakersGood) { usbPlane = getPlane (kIOUSBPlane); FailIf (NULL == usbPlane, Exit); usbRegEntry = controlInterface->GetDevice()->getParentEntry (usbPlane); FailIf (NULL == usbRegEntry, Exit); obj = usbRegEntry->getProperty (kUSBVendorID); number = OSDynamicCast (OSNumber, obj); FailIf (NULL == number, Exit); vendorID = number->unsigned32BitValue (); debug2IOLog ("hub's vendorID = 0x%x\n", vendorID); if (kIOUSBVendorIDAppleComputer == vendorID) { obj = usbRegEntry->getProperty (kUSBDevicePropertyLocationID); number = OSDynamicCast (OSNumber, obj); FailIf (NULL == number, Exit); locationID = number->unsigned32BitValue (); debug2IOLog ("speaker's locationID = 0x%lx\n", locationID); if (0x18000000 == locationID || 0x19000000 == locationID) { // It's connected to the root hub connectionGood = TRUE; } else { obj = usbRegEntry->getProperty (kUSBProductID); number = OSDynamicCast (OSNumber, obj); FailIf (NULL == number, Exit); productID = number->unsigned32BitValue (); debug2IOLog ("hub's productID = 0x%x\n", productID); if (kStudioDisplay15CRT == productID || kStudioDisplay17CRT == productID || kCinemaDisplay == productID || kStudioDisplay17FP == productID) { // It's connected to a good monitor connectionGood = TRUE; } } } } debug2IOLog ("connectionGood = %d\n", speakersGood); // And there CANNOT be a "sound" node in the device tree so that OF will boot beep through them if (TRUE == connectionGood && FALSE == FindSoundNode ()) { result = TRUE; } Exit: debug2IOLog ("- ShouldUpdatePRAM result = %d", result); return result; } Boolean AppleUSBAudioDevice::FindSoundNode (void) { const IORegistryPlane * dtPlane; IORegistryEntry * regEntry; IORegistryIterator * iterator; Boolean found; Boolean done; const char * name; found = FALSE; dtPlane = IORegistryEntry::getPlane (kIODeviceTreePlane); FailIf (NULL == dtPlane, Exit); iterator = IORegistryIterator::iterateOver (dtPlane, kIORegistryIterateRecursively); FailIf (NULL == iterator, Exit); done = FALSE; regEntry = iterator->getNextObject (); while (NULL != regEntry && FALSE == done) { name = regEntry->getName (); if (0 == strcmp (name, "mac-io")) { // This is where we want to start the search iterator->release (); // release the current iterator and make a new one rooted at "mac-io" iterator = IORegistryIterator::iterateOver (regEntry, dtPlane, kIORegistryIterateRecursively); done = TRUE; } regEntry = iterator->getNextObject (); } // Now the real search begins... regEntry = iterator->getNextObject (); while (NULL != regEntry && FALSE == found) { name = regEntry->getName (); if (0 == strcmp (name, "sound")) { found = TRUE; } regEntry = iterator->getNextObject (); } iterator->release (); Exit: return found; } void AppleUSBAudioDevice::performStop (IOService *provider) { debug3IOLog("+AppleUSBAudioDevice[%p]::performStop (%p)\n", this, provider); #if TIMERSTREAM // remove from new USB device notification if (isUnattachedDeviceRegistered ()) { unregisterUnattachedDevice (); } #endif super::stop (provider); // call the IOAudioDevice generic stop routine if (controlInterface) { controlInterface->close (this); controlInterface = NULL; } #if TIMERSTREAM if (hasBeenUnattached) { // we need to schedule this for later release (); } #endif debug3IOLog ("-AppleUSBAudioDevice[%p]::performStop (%p)\n", this, provider); } bool AppleUSBAudioDevice::terminate (IOOptionBits options = 0) { #if TIMERSTREAM IOCommandGate * cg; bool isRegistered; #endif bool shouldTerminate; bool result; shouldTerminate = TRUE; result = TRUE; debug3IOLog ("+AppleUSBAudioDevice[%p]::terminate () - rc=%d\n", this, getRetainCount ()); #if TIMERSTREAM cg = getCommandGate(); if (cg) { if (cg->runAction (performUnattachedDeviceRegistrationAction, (void *)&isRegistered) == kIOReturnSuccess) { shouldTerminate = !isRegistered; } } #endif if (shouldTerminate) { result = super::terminate (options); } debug3IOLog ("-AppleUSBAudioDevice[%p]::terminate () - rc=%d\n", this, getRetainCount ()); return result; } bool AppleUSBAudioDevice::finalize (IOOptionBits options) { bool result; debug4IOLog ("+AppleUSBAudioDevice[%p]::finalize (%p) - rc=%d\n", this, options, getRetainCount ()); result = super::finalize (options); #if TIMERSTREAM if (isUnattachedDeviceRegistered ()) { stop (0); } #endif debug4IOLog ("-AppleUSBAudioDevice[%p]::finalize (%p) - rc=%d\n", this, options, getRetainCount ()); return result; } IOReturn AppleUSBAudioDevice::message (UInt32 type, IOService * provider, void * arg) { debug5IOLog ("+AppleUSBAudioDevice[%p]::message (0x%x, %p) - rc=%d\n", this, type, provider, getRetainCount ()); switch (type) { case kIOMessageServiceIsTerminated: case kIOMessageServiceIsRequestingClose: if (controlInterface != NULL && controlInterface == provider) { controlInterface->close (this); controlInterface = NULL; } default: ; } debug5IOLog ("-AppleUSBAudioDevice[%p]::message (0x%x, %p) - rc=%d\n", this, type, provider, getRetainCount ()); return kIOReturnSuccess; } IOReturn AppleUSBAudioDevice::activateAudioEngine (IOAudioEngine *audioEngine, bool shouldStartAudioEngine, UInt8 interfaceNum, UInt8 altInterfaceNum) { AppleUSBAudioEngine * usbAudioEngine; UInt8 channelNum; AppleUSBAudioLevelControl * speakerControl; AppleUSBAudioLevelControl * micControl; AppleUSBAudioMuteControl * muteControl; IOReturn result; UInt16 terminalType; UInt8 numTerminals; UInt8 featureUnitID; UInt8 terminalIndex; UInt8 controlInterfaceNum; Boolean shouldUpdatePRAM; debug5IOLog ("+AppleUSBAudioDevice[%p]::activateAudioEngine (%p, %d) - rc=%d\n", this, audioEngine, shouldStartAudioEngine, getRetainCount()); result = kIOReturnError; FailIf (NULL == controlInterface, Exit); usbAudioEngine = OSDynamicCast (AppleUSBAudioEngine, audioEngine); FailIf (NULL == usbAudioEngine, Exit); controlInterfaceNum = controlInterface->GetInterfaceNumber (); if (usbAudioEngine->getDirection () == kIOAudioStreamDirectionOutput) { // It's an output device (e.g., a speaker). featureUnitID = 0; // look for a streaming input terminal that's connected to a non-streaming output terminal // for the moment we'll just look for a feature unit connected to a non-streaming output terminal numTerminals = usbAudio->GetNumOutputTerminals (controlInterfaceNum, 0); debug2IOLog ("num output terminals = %d\n", numTerminals); for (terminalIndex = 0; terminalIndex < numTerminals; terminalIndex++) { debug2IOLog ("terminalIndex = 0x%X\n", terminalIndex); terminalType = usbAudio->GetIndexedOutputTerminalType (controlInterfaceNum, 0, terminalIndex); debug2IOLog ("terminalType = 0x%X\n", terminalType); if (terminalType != 0x0101) { // Only look for output terminals that output audio (things we can play to) featureUnitID = usbAudio->GetFeatureUnitIDConnectedToOutputTerminal (controlInterfaceNum, 0, usbAudio->GetIndexedOutputTerminalID (controlInterfaceNum, 0, terminalIndex)); debug2IOLog ("featureUnitID = %d\n", featureUnitID); break; // get out of for loop } } FailWithAction (0 == featureUnitID, result = kIOReturnSuccess, Finish); // There isn't a feature unit connected to this input terminal // The interface was opened in AppleUSBAudioEngine::initHardware controlInterface->SetAlternateInterface (this, kRootAlternateSetting); shouldUpdatePRAM = ShouldUpdatePRAM (); for (channelNum = 0; channelNum <= usbAudio->GetNumChannels (interfaceNum, altInterfaceNum); channelNum++) { if (usbAudio->ChannelHasVolumeControl (controlInterfaceNum, 0, featureUnitID, channelNum)) { speakerControl = AppleUSBAudioLevelControl::create (featureUnitID, controlInterfaceNum, VOLUME_CONTROL, channelNum, shouldUpdatePRAM, (USBDeviceRequest)&deviceRequest, this, kIOAudioLevelControlSubTypeVolume, kIOAudioControlUsageOutput); if (NULL != speakerControl) { audioEngine->addDefaultAudioControl (speakerControl); speakerControl->release (); } else { debug2IOLog ("++AppleUSBAudioDevice::activateAudioEngine () - error creating volume control for channelNum %d\n", channelNum); } } } muteControl = NULL; for (channelNum = 0; channelNum <= usbAudio->GetNumChannels (interfaceNum, altInterfaceNum); channelNum++) { if (usbAudio->ChannelHasMuteControl (controlInterfaceNum, 0, featureUnitID, channelNum)) { muteControl = AppleUSBAudioMuteControl::create (featureUnitID, controlInterfaceNum, channelNum, (USBDeviceRequest)&deviceRequest, this, kIOAudioControlUsageOutput); if (NULL != muteControl) { audioEngine->addDefaultAudioControl (muteControl); muteControl->release (); } else { debug2IOLog ("++AppleUSBAudioDevice::activateAudioEngine () - error creating mute control for channelNum %d\n", channelNum); } } } } else { // It's an input device (e.g., a microphone). featureUnitID = 0; // look for a streaming input terminal that's connected to a non-streaming output terminal // for the moment we'll just look for a feature unit connected to a non-streaming output terminal numTerminals = usbAudio->GetNumOutputTerminals (controlInterfaceNum, 0); debug2IOLog ("num output terminals = %d\n", numTerminals); for (terminalIndex = 0; terminalIndex < numTerminals; terminalIndex++) { debug2IOLog ("terminalIndex = 0x%X\n", terminalIndex); terminalType = usbAudio->GetIndexedOutputTerminalType (controlInterfaceNum, 0, terminalIndex); debug2IOLog ("terminalType = 0x%X\n", terminalType); if (terminalType == 0x0101) { // Only look for output terminals that output digital audio data (things we can record from) featureUnitID = usbAudio->GetFeatureUnitIDConnectedToOutputTerminal (controlInterfaceNum, 0, usbAudio->GetIndexedOutputTerminalID (controlInterfaceNum, 0, terminalIndex)); debug2IOLog ("featureUnitID = %d\n", featureUnitID); break; // get out of for loop } } FailWithAction (0 == featureUnitID, result = kIOReturnSuccess, Finish); // There isn't a feature unit connected to this output terminal // The interface was opened in AppleUSBAudioEngine::initHardware controlInterface->SetAlternateInterface (this, kRootAlternateSetting); for (channelNum = 0; channelNum <= usbAudio->GetNumChannels (interfaceNum, altInterfaceNum); channelNum++) { if (usbAudio->ChannelHasVolumeControl (controlInterfaceNum, 0, featureUnitID, channelNum)) { micControl = AppleUSBAudioLevelControl::create (featureUnitID, controlInterfaceNum, VOLUME_CONTROL, channelNum, FALSE, (USBDeviceRequest)&deviceRequest, this, kIOAudioLevelControlSubTypeVolume, kIOAudioControlUsageInput); if (NULL != micControl) { audioEngine->addDefaultAudioControl (micControl); micControl->release (); } else { debug2IOLog ("++AppleUSBAudioDevice::activateAudioEngine () - error creating volume control for channelNum %d\n", channelNum); } } } muteControl = NULL; for (channelNum = 0; channelNum <= usbAudio->GetNumChannels (interfaceNum, altInterfaceNum); channelNum++) { if (usbAudio->ChannelHasMuteControl (controlInterfaceNum, 0, featureUnitID, channelNum)) { muteControl = AppleUSBAudioMuteControl::create (featureUnitID, controlInterfaceNum, channelNum, (USBDeviceRequest)&deviceRequest, this, kIOAudioControlUsageInput); if (NULL != muteControl) { audioEngine->addDefaultAudioControl (muteControl); muteControl->release (); } else { debug2IOLog ("++AppleUSBAudioDevice::activateAudioEngine () - error creating mute control for channelNum %d\n", channelNum); } } } } Finish: FailIf (kIOReturnSuccess != super::activateAudioEngine (audioEngine, shouldStartAudioEngine), Exit); result = TRUE; Exit: debug4IOLog ("-AppleUSBAudioDevice[%p]::activateAudioEngine (%p) - rc=%d\n", this, audioEngine, getRetainCount ()); return result; } IOReturn AppleUSBAudioDevice::deviceRequest (IOUSBDevRequest *request, AppleUSBAudioDevice * self, IOUSBCompletion *completion) { IOReturn result; debug4IOLog ("+AppleUSBAudioDevice[%p]::deviceRequest (%p, %p)\n", self, request, completion); result = kIOReturnSuccess; if (self->controlInterface) { FailIf (NULL == self->interfaceLock, Exit); IORecursiveLockLock (self->interfaceLock); result = self->controlInterface->DeviceRequest (request, completion); IORecursiveLockUnlock (self->interfaceLock); } debug4IOLog ("-AppleUSBAudioDevice[%p]::deviceRequest (%p, %p)\n", self, request, completion); Exit: return result; } #ifdef DEBUG void AppleUSBAudioDevice::retain() const { // debug3IOLog("AppleUSBAudioDevice(%p)::retain() - rc=%d\n", this, getRetainCount()); super::retain(); } void AppleUSBAudioDevice::release() const { // debug3IOLog("AppleUSBAudioDevice(%p)::release() - rc=%d\n", this, getRetainCount()); super::release(); } #endif