diff options
Diffstat (limited to 'services/audioflinger/PatchPanel.cpp')
-rw-r--r-- | services/audioflinger/PatchPanel.cpp | 695 |
1 files changed, 695 insertions, 0 deletions
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp new file mode 100644 index 0000000..7544052 --- /dev/null +++ b/services/audioflinger/PatchPanel.cpp @@ -0,0 +1,695 @@ +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + +#define LOG_TAG "AudioFlinger::PatchPanel" +//#define LOG_NDEBUG 0 + +#include "Configuration.h" +#include <utils/Log.h> +#include <audio_utils/primitives.h> + +#include "AudioFlinger.h" +#include "ServiceUtilities.h" +#include <media/AudioParameter.h> + +// ---------------------------------------------------------------------------- + +// Note: the following macro is used for extremely verbose logging message. In +// order to run with ALOG_ASSERT turned on, we need to have LOG_NDEBUG set to +// 0; but one side effect of this is to turn all LOGV's as well. Some messages +// are so verbose that we want to suppress them even when we have ALOG_ASSERT +// turned on. Do not uncomment the #def below unless you really know what you +// are doing and want to see all of the extremely verbose messages. +//#define VERY_VERY_VERBOSE_LOGGING +#ifdef VERY_VERY_VERBOSE_LOGGING +#define ALOGVV ALOGV +#else +#define ALOGVV(a...) do { } while(0) +#endif + +namespace android { + +/* List connected audio ports and their attributes */ +status_t AudioFlinger::listAudioPorts(unsigned int *num_ports, + struct audio_port *ports) +{ + Mutex::Autolock _l(mLock); + if (mPatchPanel != 0) { + return mPatchPanel->listAudioPorts(num_ports, ports); + } + return NO_INIT; +} + +/* Get supported attributes for a given audio port */ +status_t AudioFlinger::getAudioPort(struct audio_port *port) +{ + Mutex::Autolock _l(mLock); + if (mPatchPanel != 0) { + return mPatchPanel->getAudioPort(port); + } + return NO_INIT; +} + + +/* Connect a patch between several source and sink ports */ +status_t AudioFlinger::createAudioPatch(const struct audio_patch *patch, + audio_patch_handle_t *handle) +{ + Mutex::Autolock _l(mLock); + if (mPatchPanel != 0) { + return mPatchPanel->createAudioPatch(patch, handle); + } + return NO_INIT; +} + +/* Disconnect a patch */ +status_t AudioFlinger::releaseAudioPatch(audio_patch_handle_t handle) +{ + Mutex::Autolock _l(mLock); + if (mPatchPanel != 0) { + return mPatchPanel->releaseAudioPatch(handle); + } + return NO_INIT; +} + + +/* List connected audio ports and they attributes */ +status_t AudioFlinger::listAudioPatches(unsigned int *num_patches, + struct audio_patch *patches) +{ + Mutex::Autolock _l(mLock); + if (mPatchPanel != 0) { + return mPatchPanel->listAudioPatches(num_patches, patches); + } + return NO_INIT; +} + +/* Set audio port configuration */ +status_t AudioFlinger::setAudioPortConfig(const struct audio_port_config *config) +{ + Mutex::Autolock _l(mLock); + if (mPatchPanel != 0) { + return mPatchPanel->setAudioPortConfig(config); + } + return NO_INIT; +} + + +AudioFlinger::PatchPanel::PatchPanel(const sp<AudioFlinger>& audioFlinger) + : mAudioFlinger(audioFlinger) +{ +} + +AudioFlinger::PatchPanel::~PatchPanel() +{ +} + +/* List connected audio ports and their attributes */ +status_t AudioFlinger::PatchPanel::listAudioPorts(unsigned int *num_ports __unused, + struct audio_port *ports __unused) +{ + ALOGV("listAudioPorts"); + return NO_ERROR; +} + +/* Get supported attributes for a given audio port */ +status_t AudioFlinger::PatchPanel::getAudioPort(struct audio_port *port __unused) +{ + ALOGV("getAudioPort"); + return NO_ERROR; +} + + +/* Connect a patch between several source and sink ports */ +status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *patch, + audio_patch_handle_t *handle) +{ + ALOGV("createAudioPatch() num_sources %d num_sinks %d handle %d", + patch->num_sources, patch->num_sinks, *handle); + status_t status = NO_ERROR; + audio_patch_handle_t halHandle = AUDIO_PATCH_HANDLE_NONE; + sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); + if (audioflinger == 0) { + return NO_INIT; + } + + if (handle == NULL || patch == NULL) { + return BAD_VALUE; + } + if (patch->num_sources == 0 || patch->num_sources > AUDIO_PATCH_PORTS_MAX || + patch->num_sinks == 0 || patch->num_sinks > AUDIO_PATCH_PORTS_MAX) { + return BAD_VALUE; + } + // limit number of sources to 1 for now or 2 sources for special cross hw module case. + // only the audio policy manager can request a patch creation with 2 sources. + if (patch->num_sources > 2) { + return INVALID_OPERATION; + } + + if (*handle != AUDIO_PATCH_HANDLE_NONE) { + for (size_t index = 0; *handle != 0 && index < mPatches.size(); index++) { + if (*handle == mPatches[index]->mHandle) { + ALOGV("createAudioPatch() removing patch handle %d", *handle); + halHandle = mPatches[index]->mHalHandle; + mPatches.removeAt(index); + break; + } + } + } + + Patch *newPatch = new Patch(patch); + + switch (patch->sources[0].type) { + case AUDIO_PORT_TYPE_DEVICE: { + audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module; + ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); + if (index < 0) { + ALOGW("createAudioPatch() bad src hw module %d", srcModule); + status = BAD_VALUE; + goto exit; + } + AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); + for (unsigned int i = 0; i < patch->num_sinks; i++) { + // support only one sink if connection to a mix or across HW modules + if ((patch->sinks[i].type == AUDIO_PORT_TYPE_MIX || + patch->sinks[i].ext.mix.hw_module != srcModule) && + patch->num_sinks > 1) { + status = INVALID_OPERATION; + goto exit; + } + // reject connection to different sink types + if (patch->sinks[i].type != patch->sinks[0].type) { + ALOGW("createAudioPatch() different sink types in same patch not supported"); + status = BAD_VALUE; + goto exit; + } + // limit to connections between devices and input streams for HAL before 3.0 + if (patch->sinks[i].ext.mix.hw_module == srcModule && + (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) && + (patch->sinks[i].type != AUDIO_PORT_TYPE_MIX)) { + ALOGW("createAudioPatch() invalid sink type %d for device source", + patch->sinks[i].type); + status = BAD_VALUE; + goto exit; + } + } + + if (patch->sinks[0].ext.device.hw_module != srcModule) { + // limit to device to device connection if not on same hw module + if (patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) { + ALOGW("createAudioPatch() invalid sink type for cross hw module"); + status = INVALID_OPERATION; + goto exit; + } + // special case num sources == 2 -=> reuse an exiting output mix to connect to the + // sink + if (patch->num_sources == 2) { + if (patch->sources[1].type != AUDIO_PORT_TYPE_MIX || + patch->sinks[0].ext.device.hw_module != + patch->sources[1].ext.mix.hw_module) { + ALOGW("createAudioPatch() invalid source combination"); + status = INVALID_OPERATION; + goto exit; + } + + sp<ThreadBase> thread = + audioflinger->checkPlaybackThread_l(patch->sources[1].ext.mix.handle); + newPatch->mPlaybackThread = (MixerThread *)thread.get(); + if (thread == 0) { + ALOGW("createAudioPatch() cannot get playback thread"); + status = INVALID_OPERATION; + goto exit; + } + } else { + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + audio_devices_t device = patch->sinks[0].ext.device.type; + String8 address = String8(patch->sinks[0].ext.device.address); + audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; + newPatch->mPlaybackThread = audioflinger->openOutput_l( + patch->sinks[0].ext.device.hw_module, + &output, + &config, + device, + address, + AUDIO_OUTPUT_FLAG_NONE); + ALOGV("audioflinger->openOutput_l() returned %p", + newPatch->mPlaybackThread.get()); + if (newPatch->mPlaybackThread == 0) { + status = NO_MEMORY; + goto exit; + } + } + uint32_t channelCount = newPatch->mPlaybackThread->channelCount(); + audio_devices_t device = patch->sources[0].ext.device.type; + String8 address = String8(patch->sources[0].ext.device.address); + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount); + config.sample_rate = newPatch->mPlaybackThread->sampleRate(); + config.channel_mask = inChannelMask; + config.format = newPatch->mPlaybackThread->format(); + audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; + newPatch->mRecordThread = audioflinger->openInput_l(srcModule, + &input, + &config, + device, + address, + AUDIO_SOURCE_MIC, + AUDIO_INPUT_FLAG_NONE); + ALOGV("audioflinger->openInput_l() returned %p inChannelMask %08x", + newPatch->mRecordThread.get(), inChannelMask); + if (newPatch->mRecordThread == 0) { + status = NO_MEMORY; + goto exit; + } + status = createPatchConnections(newPatch, patch); + if (status != NO_ERROR) { + goto exit; + } + } else { + if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { + if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { + sp<ThreadBase> thread = audioflinger->checkRecordThread_l( + patch->sinks[0].ext.mix.handle); + if (thread == 0) { + ALOGW("createAudioPatch() bad capture I/O handle %d", + patch->sinks[0].ext.mix.handle); + status = BAD_VALUE; + goto exit; + } + status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); + } else { + audio_hw_device_t *hwDevice = audioHwDevice->hwDevice(); + status = hwDevice->create_audio_patch(hwDevice, + patch->num_sources, + patch->sources, + patch->num_sinks, + patch->sinks, + &halHandle); + } + } else { + sp<ThreadBase> thread = audioflinger->checkRecordThread_l( + patch->sinks[0].ext.mix.handle); + if (thread == 0) { + ALOGW("createAudioPatch() bad capture I/O handle %d", + patch->sinks[0].ext.mix.handle); + status = BAD_VALUE; + goto exit; + } + char *address; + if (strcmp(patch->sources[0].ext.device.address, "") != 0) { + address = audio_device_address_to_parameter( + patch->sources[0].ext.device.type, + patch->sources[0].ext.device.address); + } else { + address = (char *)calloc(1, 1); + } + AudioParameter param = AudioParameter(String8(address)); + free(address); + param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), + (int)patch->sources[0].ext.device.type); + param.addInt(String8(AUDIO_PARAMETER_STREAM_INPUT_SOURCE), + (int)patch->sinks[0].ext.mix.usecase.source); + ALOGV("createAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s", + param.toString().string()); + status = thread->setParameters(param.toString()); + } + } + } break; + case AUDIO_PORT_TYPE_MIX: { + audio_module_handle_t srcModule = patch->sources[0].ext.mix.hw_module; + ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); + if (index < 0) { + ALOGW("createAudioPatch() bad src hw module %d", srcModule); + status = BAD_VALUE; + goto exit; + } + // limit to connections between devices and output streams + for (unsigned int i = 0; i < patch->num_sinks; i++) { + if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) { + ALOGW("createAudioPatch() invalid sink type %d for mix source", + patch->sinks[i].type); + status = BAD_VALUE; + goto exit; + } + // limit to connections between sinks and sources on same HW module + if (patch->sinks[i].ext.device.hw_module != srcModule) { + status = BAD_VALUE; + goto exit; + } + } + AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); + sp<ThreadBase> thread = + audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle); + if (thread == 0) { + ALOGW("createAudioPatch() bad playback I/O handle %d", + patch->sources[0].ext.mix.handle); + status = BAD_VALUE; + goto exit; + } + if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { + status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); + } else { + audio_devices_t type = AUDIO_DEVICE_NONE; + for (unsigned int i = 0; i < patch->num_sinks; i++) { + type |= patch->sinks[i].ext.device.type; + } + char *address; + if (strcmp(patch->sinks[0].ext.device.address, "") != 0) { + //FIXME: we only support address on first sink with HAL version < 3.0 + address = audio_device_address_to_parameter( + patch->sinks[0].ext.device.type, + patch->sinks[0].ext.device.address); + } else { + address = (char *)calloc(1, 1); + } + AudioParameter param = AudioParameter(String8(address)); + free(address); + param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), (int)type); + status = thread->setParameters(param.toString()); + } + + } break; + default: + status = BAD_VALUE; + goto exit; + } +exit: + ALOGV("createAudioPatch() status %d", status); + if (status == NO_ERROR) { + *handle = audioflinger->nextUniqueId(); + newPatch->mHandle = *handle; + newPatch->mHalHandle = halHandle; + mPatches.add(newPatch); + ALOGV("createAudioPatch() added new patch handle %d halHandle %d", *handle, halHandle); + } else { + clearPatchConnections(newPatch); + delete newPatch; + } + return status; +} + +status_t AudioFlinger::PatchPanel::createPatchConnections(Patch *patch, + const struct audio_patch *audioPatch) +{ + // create patch from source device to record thread input + struct audio_patch subPatch; + subPatch.num_sources = 1; + subPatch.sources[0] = audioPatch->sources[0]; + subPatch.num_sinks = 1; + + patch->mRecordThread->getAudioPortConfig(&subPatch.sinks[0]); + subPatch.sinks[0].ext.mix.usecase.source = AUDIO_SOURCE_MIC; + + status_t status = createAudioPatch(&subPatch, &patch->mRecordPatchHandle); + if (status != NO_ERROR) { + patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; + return status; + } + + // create patch from playback thread output to sink device + patch->mPlaybackThread->getAudioPortConfig(&subPatch.sources[0]); + subPatch.sinks[0] = audioPatch->sinks[0]; + status = createAudioPatch(&subPatch, &patch->mPlaybackPatchHandle); + if (status != NO_ERROR) { + patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; + return status; + } + + // use a pseudo LCM between input and output framecount + size_t playbackFrameCount = patch->mPlaybackThread->frameCount(); + int playbackShift = __builtin_ctz(playbackFrameCount); + size_t recordFramecount = patch->mRecordThread->frameCount(); + int shift = __builtin_ctz(recordFramecount); + if (playbackShift < shift) { + shift = playbackShift; + } + size_t frameCount = (playbackFrameCount * recordFramecount) >> shift; + ALOGV("createPatchConnections() playframeCount %d recordFramecount %d frameCount %d ", + playbackFrameCount, recordFramecount, frameCount); + + // create a special record track to capture from record thread + uint32_t channelCount = patch->mPlaybackThread->channelCount(); + audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount); + audio_channel_mask_t outChannelMask = patch->mPlaybackThread->channelMask(); + uint32_t sampleRate = patch->mPlaybackThread->sampleRate(); + audio_format_t format = patch->mPlaybackThread->format(); + + patch->mPatchRecord = new RecordThread::PatchRecord( + patch->mRecordThread.get(), + sampleRate, + inChannelMask, + format, + frameCount, + NULL, + IAudioFlinger::TRACK_DEFAULT); + if (patch->mPatchRecord == 0) { + return NO_MEMORY; + } + status = patch->mPatchRecord->initCheck(); + if (status != NO_ERROR) { + return status; + } + patch->mRecordThread->addPatchRecord(patch->mPatchRecord); + + // create a special playback track to render to playback thread. + // this track is given the same buffer as the PatchRecord buffer + patch->mPatchTrack = new PlaybackThread::PatchTrack( + patch->mPlaybackThread.get(), + sampleRate, + outChannelMask, + format, + frameCount, + patch->mPatchRecord->buffer(), + IAudioFlinger::TRACK_DEFAULT); + if (patch->mPatchTrack == 0) { + return NO_MEMORY; + } + status = patch->mPatchTrack->initCheck(); + if (status != NO_ERROR) { + return status; + } + patch->mPlaybackThread->addPatchTrack(patch->mPatchTrack); + + // tie playback and record tracks together + patch->mPatchRecord->setPeerProxy(patch->mPatchTrack.get()); + patch->mPatchTrack->setPeerProxy(patch->mPatchRecord.get()); + + // start capture and playback + patch->mPatchRecord->start(AudioSystem::SYNC_EVENT_NONE, 0); + patch->mPatchTrack->start(); + + return status; +} + +void AudioFlinger::PatchPanel::clearPatchConnections(Patch *patch) +{ + sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); + if (audioflinger == 0) { + return; + } + + ALOGV("clearPatchConnections() patch->mRecordPatchHandle %d patch->mPlaybackPatchHandle %d", + patch->mRecordPatchHandle, patch->mPlaybackPatchHandle); + + if (patch->mPatchRecord != 0) { + patch->mPatchRecord->stop(); + } + if (patch->mPatchTrack != 0) { + patch->mPatchTrack->stop(); + } + if (patch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE) { + releaseAudioPatch(patch->mRecordPatchHandle); + patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; + } + if (patch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) { + releaseAudioPatch(patch->mPlaybackPatchHandle); + patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; + } + if (patch->mRecordThread != 0) { + if (patch->mPatchRecord != 0) { + patch->mRecordThread->deletePatchRecord(patch->mPatchRecord); + patch->mPatchRecord.clear(); + } + audioflinger->closeInputInternal_l(patch->mRecordThread); + patch->mRecordThread.clear(); + } + if (patch->mPlaybackThread != 0) { + if (patch->mPatchTrack != 0) { + patch->mPlaybackThread->deletePatchTrack(patch->mPatchTrack); + patch->mPatchTrack.clear(); + } + // if num sources == 2 we are reusing an existing playback thread so we do not close it + if (patch->mAudioPatch.num_sources != 2) { + audioflinger->closeOutputInternal_l(patch->mPlaybackThread); + } + patch->mPlaybackThread.clear(); + } +} + +/* Disconnect a patch */ +status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle) +{ + ALOGV("releaseAudioPatch handle %d", handle); + status_t status = NO_ERROR; + size_t index; + + sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); + if (audioflinger == 0) { + return NO_INIT; + } + + for (index = 0; index < mPatches.size(); index++) { + if (handle == mPatches[index]->mHandle) { + break; + } + } + if (index == mPatches.size()) { + return BAD_VALUE; + } + Patch *removedPatch = mPatches[index]; + mPatches.removeAt(index); + + struct audio_patch *patch = &removedPatch->mAudioPatch; + + switch (patch->sources[0].type) { + case AUDIO_PORT_TYPE_DEVICE: { + audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module; + ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); + if (index < 0) { + ALOGW("releaseAudioPatch() bad src hw module %d", srcModule); + status = BAD_VALUE; + break; + } + + if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE && + patch->sinks[0].ext.device.hw_module != srcModule) { + clearPatchConnections(removedPatch); + break; + } + + AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); + if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { + if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { + sp<ThreadBase> thread = audioflinger->checkRecordThread_l( + patch->sinks[0].ext.mix.handle); + if (thread == 0) { + ALOGW("releaseAudioPatch() bad capture I/O handle %d", + patch->sinks[0].ext.mix.handle); + status = BAD_VALUE; + break; + } + status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle); + } else { + audio_hw_device_t *hwDevice = audioHwDevice->hwDevice(); + status = hwDevice->release_audio_patch(hwDevice, removedPatch->mHalHandle); + } + } else { + sp<ThreadBase> thread = audioflinger->checkRecordThread_l( + patch->sinks[0].ext.mix.handle); + if (thread == 0) { + ALOGW("releaseAudioPatch() bad capture I/O handle %d", + patch->sinks[0].ext.mix.handle); + status = BAD_VALUE; + break; + } + AudioParameter param; + param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), 0); + ALOGV("releaseAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s", + param.toString().string()); + status = thread->setParameters(param.toString()); + } + } break; + case AUDIO_PORT_TYPE_MIX: { + audio_module_handle_t srcModule = patch->sources[0].ext.mix.hw_module; + ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); + if (index < 0) { + ALOGW("releaseAudioPatch() bad src hw module %d", srcModule); + status = BAD_VALUE; + break; + } + sp<ThreadBase> thread = + audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle); + if (thread == 0) { + ALOGW("releaseAudioPatch() bad playback I/O handle %d", + patch->sources[0].ext.mix.handle); + status = BAD_VALUE; + break; + } + AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); + if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { + status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle); + } else { + AudioParameter param; + param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), 0); + status = thread->setParameters(param.toString()); + } + } break; + default: + status = BAD_VALUE; + break; + } + + delete removedPatch; + return status; +} + + +/* List connected audio ports and they attributes */ +status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __unused, + struct audio_patch *patches __unused) +{ + ALOGV("listAudioPatches"); + return NO_ERROR; +} + +/* Set audio port configuration */ +status_t AudioFlinger::PatchPanel::setAudioPortConfig(const struct audio_port_config *config) +{ + ALOGV("setAudioPortConfig"); + status_t status = NO_ERROR; + + sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); + if (audioflinger == 0) { + return NO_INIT; + } + + audio_module_handle_t module; + if (config->type == AUDIO_PORT_TYPE_DEVICE) { + module = config->ext.device.hw_module; + } else { + module = config->ext.mix.hw_module; + } + + ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(module); + if (index < 0) { + ALOGW("setAudioPortConfig() bad hw module %d", module); + return BAD_VALUE; + } + + AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); + if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { + audio_hw_device_t *hwDevice = audioHwDevice->hwDevice(); + return hwDevice->set_audio_port_config(hwDevice, config); + } else { + return INVALID_OPERATION; + } + return NO_ERROR; +} + + +}; // namespace android |