PatchPanel.cpp revision d60560af7cb559762593161c8202459cc01fb0f5
1/* 2** 3** Copyright 2014, The Android Open Source Project 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17 18 19#define LOG_TAG "AudioFlinger::PatchPanel" 20//#define LOG_NDEBUG 0 21 22#include "Configuration.h" 23#include <utils/Log.h> 24#include <audio_utils/primitives.h> 25 26#include "AudioFlinger.h" 27#include "ServiceUtilities.h" 28#include <media/AudioParameter.h> 29 30// ---------------------------------------------------------------------------- 31 32// Note: the following macro is used for extremely verbose logging message. In 33// order to run with ALOG_ASSERT turned on, we need to have LOG_NDEBUG set to 34// 0; but one side effect of this is to turn all LOGV's as well. Some messages 35// are so verbose that we want to suppress them even when we have ALOG_ASSERT 36// turned on. Do not uncomment the #def below unless you really know what you 37// are doing and want to see all of the extremely verbose messages. 38//#define VERY_VERY_VERBOSE_LOGGING 39#ifdef VERY_VERY_VERBOSE_LOGGING 40#define ALOGVV ALOGV 41#else 42#define ALOGVV(a...) do { } while(0) 43#endif 44 45namespace android { 46 47/* List connected audio ports and their attributes */ 48status_t AudioFlinger::listAudioPorts(unsigned int *num_ports, 49 struct audio_port *ports) 50{ 51 Mutex::Autolock _l(mLock); 52 if (mPatchPanel != 0) { 53 return mPatchPanel->listAudioPorts(num_ports, ports); 54 } 55 return NO_INIT; 56} 57 58/* Get supported attributes for a given audio port */ 59status_t AudioFlinger::getAudioPort(struct audio_port *port) 60{ 61 Mutex::Autolock _l(mLock); 62 if (mPatchPanel != 0) { 63 return mPatchPanel->getAudioPort(port); 64 } 65 return NO_INIT; 66} 67 68 69/* Connect a patch between several source and sink ports */ 70status_t AudioFlinger::createAudioPatch(const struct audio_patch *patch, 71 audio_patch_handle_t *handle) 72{ 73 Mutex::Autolock _l(mLock); 74 if (mPatchPanel != 0) { 75 return mPatchPanel->createAudioPatch(patch, handle); 76 } 77 return NO_INIT; 78} 79 80/* Disconnect a patch */ 81status_t AudioFlinger::releaseAudioPatch(audio_patch_handle_t handle) 82{ 83 Mutex::Autolock _l(mLock); 84 if (mPatchPanel != 0) { 85 return mPatchPanel->releaseAudioPatch(handle); 86 } 87 return NO_INIT; 88} 89 90 91/* List connected audio ports and they attributes */ 92status_t AudioFlinger::listAudioPatches(unsigned int *num_patches, 93 struct audio_patch *patches) 94{ 95 Mutex::Autolock _l(mLock); 96 if (mPatchPanel != 0) { 97 return mPatchPanel->listAudioPatches(num_patches, patches); 98 } 99 return NO_INIT; 100} 101 102/* Set audio port configuration */ 103status_t AudioFlinger::setAudioPortConfig(const struct audio_port_config *config) 104{ 105 Mutex::Autolock _l(mLock); 106 if (mPatchPanel != 0) { 107 return mPatchPanel->setAudioPortConfig(config); 108 } 109 return NO_INIT; 110} 111 112 113AudioFlinger::PatchPanel::PatchPanel(const sp<AudioFlinger>& audioFlinger) 114 : mAudioFlinger(audioFlinger) 115{ 116} 117 118AudioFlinger::PatchPanel::~PatchPanel() 119{ 120} 121 122/* List connected audio ports and their attributes */ 123status_t AudioFlinger::PatchPanel::listAudioPorts(unsigned int *num_ports __unused, 124 struct audio_port *ports __unused) 125{ 126 ALOGV("listAudioPorts"); 127 return NO_ERROR; 128} 129 130/* Get supported attributes for a given audio port */ 131status_t AudioFlinger::PatchPanel::getAudioPort(struct audio_port *port __unused) 132{ 133 ALOGV("getAudioPort"); 134 return NO_ERROR; 135} 136 137 138/* Connect a patch between several source and sink ports */ 139status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *patch, 140 audio_patch_handle_t *handle) 141{ 142 ALOGV("createAudioPatch() num_sources %d num_sinks %d handle %d", 143 patch->num_sources, patch->num_sinks, *handle); 144 status_t status = NO_ERROR; 145 audio_patch_handle_t halHandle = AUDIO_PATCH_HANDLE_NONE; 146 sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); 147 if (audioflinger == 0) { 148 return NO_INIT; 149 } 150 151 if (handle == NULL || patch == NULL) { 152 return BAD_VALUE; 153 } 154 if (patch->num_sources == 0 || patch->num_sources > AUDIO_PATCH_PORTS_MAX || 155 (patch->num_sinks == 0 && patch->num_sources != 2) || 156 patch->num_sinks > AUDIO_PATCH_PORTS_MAX) { 157 return BAD_VALUE; 158 } 159 // limit number of sources to 1 for now or 2 sources for special cross hw module case. 160 // only the audio policy manager can request a patch creation with 2 sources. 161 if (patch->num_sources > 2) { 162 return INVALID_OPERATION; 163 } 164 165 if (*handle != AUDIO_PATCH_HANDLE_NONE) { 166 for (size_t index = 0; *handle != 0 && index < mPatches.size(); index++) { 167 if (*handle == mPatches[index]->mHandle) { 168 ALOGV("createAudioPatch() removing patch handle %d", *handle); 169 halHandle = mPatches[index]->mHalHandle; 170 Patch *removedPatch = mPatches[index]; 171 mPatches.removeAt(index); 172 delete removedPatch; 173 break; 174 } 175 } 176 } 177 178 Patch *newPatch = new Patch(patch); 179 180 switch (patch->sources[0].type) { 181 case AUDIO_PORT_TYPE_DEVICE: { 182 audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module; 183 ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); 184 if (index < 0) { 185 ALOGW("createAudioPatch() bad src hw module %d", srcModule); 186 status = BAD_VALUE; 187 goto exit; 188 } 189 AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); 190 for (unsigned int i = 0; i < patch->num_sinks; i++) { 191 // support only one sink if connection to a mix or across HW modules 192 if ((patch->sinks[i].type == AUDIO_PORT_TYPE_MIX || 193 patch->sinks[i].ext.mix.hw_module != srcModule) && 194 patch->num_sinks > 1) { 195 status = INVALID_OPERATION; 196 goto exit; 197 } 198 // reject connection to different sink types 199 if (patch->sinks[i].type != patch->sinks[0].type) { 200 ALOGW("createAudioPatch() different sink types in same patch not supported"); 201 status = BAD_VALUE; 202 goto exit; 203 } 204 } 205 206 // manage patches requiring a software bridge 207 // - special patch request with 2 sources (reuse one existing output mix) OR 208 // - Device to device AND 209 // - source HW module != destination HW module OR 210 // - audio HAL version < 3.0 211 if ((patch->num_sources == 2) || 212 ((patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) && 213 ((patch->sinks[0].ext.device.hw_module != srcModule) || 214 (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0)))) { 215 if (patch->num_sources == 2) { 216 if (patch->sources[1].type != AUDIO_PORT_TYPE_MIX || 217 (patch->num_sinks != 0 && patch->sinks[0].ext.device.hw_module != 218 patch->sources[1].ext.mix.hw_module)) { 219 ALOGW("createAudioPatch() invalid source combination"); 220 status = INVALID_OPERATION; 221 goto exit; 222 } 223 224 sp<ThreadBase> thread = 225 audioflinger->checkPlaybackThread_l(patch->sources[1].ext.mix.handle); 226 newPatch->mPlaybackThread = (MixerThread *)thread.get(); 227 if (thread == 0) { 228 ALOGW("createAudioPatch() cannot get playback thread"); 229 status = INVALID_OPERATION; 230 goto exit; 231 } 232 } else { 233 audio_config_t config = AUDIO_CONFIG_INITIALIZER; 234 audio_devices_t device = patch->sinks[0].ext.device.type; 235 String8 address = String8(patch->sinks[0].ext.device.address); 236 audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; 237 newPatch->mPlaybackThread = audioflinger->openOutput_l( 238 patch->sinks[0].ext.device.hw_module, 239 &output, 240 &config, 241 device, 242 address, 243 AUDIO_OUTPUT_FLAG_NONE); 244 ALOGV("audioflinger->openOutput_l() returned %p", 245 newPatch->mPlaybackThread.get()); 246 if (newPatch->mPlaybackThread == 0) { 247 status = NO_MEMORY; 248 goto exit; 249 } 250 } 251 uint32_t channelCount = newPatch->mPlaybackThread->channelCount(); 252 audio_devices_t device = patch->sources[0].ext.device.type; 253 String8 address = String8(patch->sources[0].ext.device.address); 254 audio_config_t config = AUDIO_CONFIG_INITIALIZER; 255 audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount); 256 config.sample_rate = newPatch->mPlaybackThread->sampleRate(); 257 config.channel_mask = inChannelMask; 258 config.format = newPatch->mPlaybackThread->format(); 259 audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; 260 newPatch->mRecordThread = audioflinger->openInput_l(srcModule, 261 &input, 262 &config, 263 device, 264 address, 265 AUDIO_SOURCE_MIC, 266 AUDIO_INPUT_FLAG_NONE); 267 ALOGV("audioflinger->openInput_l() returned %p inChannelMask %08x", 268 newPatch->mRecordThread.get(), inChannelMask); 269 if (newPatch->mRecordThread == 0) { 270 status = NO_MEMORY; 271 goto exit; 272 } 273 status = createPatchConnections(newPatch, patch); 274 if (status != NO_ERROR) { 275 goto exit; 276 } 277 } else { 278 if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { 279 sp<ThreadBase> thread = audioflinger->checkRecordThread_l( 280 patch->sinks[0].ext.mix.handle); 281 if (thread == 0) { 282 ALOGW("createAudioPatch() bad capture I/O handle %d", 283 patch->sinks[0].ext.mix.handle); 284 status = BAD_VALUE; 285 goto exit; 286 } 287 status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); 288 } else { 289 if (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) { 290 status = INVALID_OPERATION; 291 goto exit; 292 } 293 294 audio_hw_device_t *hwDevice = audioHwDevice->hwDevice(); 295 status = hwDevice->create_audio_patch(hwDevice, 296 patch->num_sources, 297 patch->sources, 298 patch->num_sinks, 299 patch->sinks, 300 &halHandle); 301 } 302 } 303 } break; 304 case AUDIO_PORT_TYPE_MIX: { 305 audio_module_handle_t srcModule = patch->sources[0].ext.mix.hw_module; 306 ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); 307 if (index < 0) { 308 ALOGW("createAudioPatch() bad src hw module %d", srcModule); 309 status = BAD_VALUE; 310 goto exit; 311 } 312 // limit to connections between devices and output streams 313 audio_devices_t type = AUDIO_DEVICE_NONE; 314 for (unsigned int i = 0; i < patch->num_sinks; i++) { 315 if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) { 316 ALOGW("createAudioPatch() invalid sink type %d for mix source", 317 patch->sinks[i].type); 318 status = BAD_VALUE; 319 goto exit; 320 } 321 // limit to connections between sinks and sources on same HW module 322 if (patch->sinks[i].ext.device.hw_module != srcModule) { 323 status = BAD_VALUE; 324 goto exit; 325 } 326 type |= patch->sinks[i].ext.device.type; 327 } 328 sp<ThreadBase> thread = 329 audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle); 330 if (thread == 0) { 331 ALOGW("createAudioPatch() bad playback I/O handle %d", 332 patch->sources[0].ext.mix.handle); 333 status = BAD_VALUE; 334 goto exit; 335 } 336 if (thread == audioflinger->primaryPlaybackThread_l()) { 337 AudioParameter param = AudioParameter(); 338 param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), (int)type); 339 340 audioflinger->broacastParametersToRecordThreads_l(param.toString()); 341 } 342 343 status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); 344 } break; 345 default: 346 status = BAD_VALUE; 347 goto exit; 348 } 349exit: 350 ALOGV("createAudioPatch() status %d", status); 351 if (status == NO_ERROR) { 352 *handle = audioflinger->nextUniqueId(); 353 newPatch->mHandle = *handle; 354 newPatch->mHalHandle = halHandle; 355 mPatches.add(newPatch); 356 ALOGV("createAudioPatch() added new patch handle %d halHandle %d", *handle, halHandle); 357 } else { 358 clearPatchConnections(newPatch); 359 delete newPatch; 360 } 361 return status; 362} 363 364status_t AudioFlinger::PatchPanel::createPatchConnections(Patch *patch, 365 const struct audio_patch *audioPatch) 366{ 367 // create patch from source device to record thread input 368 struct audio_patch subPatch; 369 subPatch.num_sources = 1; 370 subPatch.sources[0] = audioPatch->sources[0]; 371 subPatch.num_sinks = 1; 372 373 patch->mRecordThread->getAudioPortConfig(&subPatch.sinks[0]); 374 subPatch.sinks[0].ext.mix.usecase.source = AUDIO_SOURCE_MIC; 375 376 status_t status = createAudioPatch(&subPatch, &patch->mRecordPatchHandle); 377 if (status != NO_ERROR) { 378 patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; 379 return status; 380 } 381 382 // create patch from playback thread output to sink device 383 if (audioPatch->num_sinks != 0) { 384 patch->mPlaybackThread->getAudioPortConfig(&subPatch.sources[0]); 385 subPatch.sinks[0] = audioPatch->sinks[0]; 386 status = createAudioPatch(&subPatch, &patch->mPlaybackPatchHandle); 387 if (status != NO_ERROR) { 388 patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; 389 return status; 390 } 391 } else { 392 patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; 393 } 394 395 // use a pseudo LCM between input and output framecount 396 size_t playbackFrameCount = patch->mPlaybackThread->frameCount(); 397 int playbackShift = __builtin_ctz(playbackFrameCount); 398 size_t recordFramecount = patch->mRecordThread->frameCount(); 399 int shift = __builtin_ctz(recordFramecount); 400 if (playbackShift < shift) { 401 shift = playbackShift; 402 } 403 size_t frameCount = (playbackFrameCount * recordFramecount) >> shift; 404 ALOGV("createPatchConnections() playframeCount %d recordFramecount %d frameCount %d ", 405 playbackFrameCount, recordFramecount, frameCount); 406 407 // create a special record track to capture from record thread 408 uint32_t channelCount = patch->mPlaybackThread->channelCount(); 409 audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount); 410 audio_channel_mask_t outChannelMask = patch->mPlaybackThread->channelMask(); 411 uint32_t sampleRate = patch->mPlaybackThread->sampleRate(); 412 audio_format_t format = patch->mPlaybackThread->format(); 413 414 patch->mPatchRecord = new RecordThread::PatchRecord( 415 patch->mRecordThread.get(), 416 sampleRate, 417 inChannelMask, 418 format, 419 frameCount, 420 NULL, 421 IAudioFlinger::TRACK_DEFAULT); 422 if (patch->mPatchRecord == 0) { 423 return NO_MEMORY; 424 } 425 status = patch->mPatchRecord->initCheck(); 426 if (status != NO_ERROR) { 427 return status; 428 } 429 patch->mRecordThread->addPatchRecord(patch->mPatchRecord); 430 431 // create a special playback track to render to playback thread. 432 // this track is given the same buffer as the PatchRecord buffer 433 patch->mPatchTrack = new PlaybackThread::PatchTrack( 434 patch->mPlaybackThread.get(), 435 audioPatch->sources[1].ext.mix.usecase.stream, 436 sampleRate, 437 outChannelMask, 438 format, 439 frameCount, 440 patch->mPatchRecord->buffer(), 441 IAudioFlinger::TRACK_DEFAULT); 442 if (patch->mPatchTrack == 0) { 443 return NO_MEMORY; 444 } 445 status = patch->mPatchTrack->initCheck(); 446 if (status != NO_ERROR) { 447 return status; 448 } 449 patch->mPlaybackThread->addPatchTrack(patch->mPatchTrack); 450 451 // tie playback and record tracks together 452 patch->mPatchRecord->setPeerProxy(patch->mPatchTrack.get()); 453 patch->mPatchTrack->setPeerProxy(patch->mPatchRecord.get()); 454 455 // start capture and playback 456 patch->mPatchRecord->start(AudioSystem::SYNC_EVENT_NONE, 0); 457 patch->mPatchTrack->start(); 458 459 return status; 460} 461 462void AudioFlinger::PatchPanel::clearPatchConnections(Patch *patch) 463{ 464 sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); 465 if (audioflinger == 0) { 466 return; 467 } 468 469 ALOGV("clearPatchConnections() patch->mRecordPatchHandle %d patch->mPlaybackPatchHandle %d", 470 patch->mRecordPatchHandle, patch->mPlaybackPatchHandle); 471 472 if (patch->mPatchRecord != 0) { 473 patch->mPatchRecord->stop(); 474 } 475 if (patch->mPatchTrack != 0) { 476 patch->mPatchTrack->stop(); 477 } 478 if (patch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE) { 479 releaseAudioPatch(patch->mRecordPatchHandle); 480 patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; 481 } 482 if (patch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) { 483 releaseAudioPatch(patch->mPlaybackPatchHandle); 484 patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; 485 } 486 if (patch->mRecordThread != 0) { 487 if (patch->mPatchRecord != 0) { 488 patch->mRecordThread->deletePatchRecord(patch->mPatchRecord); 489 } 490 audioflinger->closeInputInternal_l(patch->mRecordThread); 491 } 492 if (patch->mPlaybackThread != 0) { 493 if (patch->mPatchTrack != 0) { 494 patch->mPlaybackThread->deletePatchTrack(patch->mPatchTrack); 495 } 496 // if num sources == 2 we are reusing an existing playback thread so we do not close it 497 if (patch->mAudioPatch.num_sources != 2) { 498 audioflinger->closeOutputInternal_l(patch->mPlaybackThread); 499 } 500 } 501 if (patch->mRecordThread != 0) { 502 if (patch->mPatchRecord != 0) { 503 patch->mPatchRecord.clear(); 504 } 505 patch->mRecordThread.clear(); 506 } 507 if (patch->mPlaybackThread != 0) { 508 if (patch->mPatchTrack != 0) { 509 patch->mPatchTrack.clear(); 510 } 511 patch->mPlaybackThread.clear(); 512 } 513 514} 515 516/* Disconnect a patch */ 517status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle) 518{ 519 ALOGV("releaseAudioPatch handle %d", handle); 520 status_t status = NO_ERROR; 521 size_t index; 522 523 sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); 524 if (audioflinger == 0) { 525 return NO_INIT; 526 } 527 528 for (index = 0; index < mPatches.size(); index++) { 529 if (handle == mPatches[index]->mHandle) { 530 break; 531 } 532 } 533 if (index == mPatches.size()) { 534 return BAD_VALUE; 535 } 536 Patch *removedPatch = mPatches[index]; 537 mPatches.removeAt(index); 538 539 struct audio_patch *patch = &removedPatch->mAudioPatch; 540 541 switch (patch->sources[0].type) { 542 case AUDIO_PORT_TYPE_DEVICE: { 543 audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module; 544 ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); 545 if (index < 0) { 546 ALOGW("releaseAudioPatch() bad src hw module %d", srcModule); 547 status = BAD_VALUE; 548 break; 549 } 550 551 if (removedPatch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE || 552 removedPatch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) { 553 clearPatchConnections(removedPatch); 554 break; 555 } 556 557 if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { 558 sp<ThreadBase> thread = audioflinger->checkRecordThread_l( 559 patch->sinks[0].ext.mix.handle); 560 if (thread == 0) { 561 ALOGW("releaseAudioPatch() bad capture I/O handle %d", 562 patch->sinks[0].ext.mix.handle); 563 status = BAD_VALUE; 564 break; 565 } 566 status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle); 567 } else { 568 AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); 569 if (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) { 570 status = INVALID_OPERATION; 571 break; 572 } 573 audio_hw_device_t *hwDevice = audioHwDevice->hwDevice(); 574 status = hwDevice->release_audio_patch(hwDevice, removedPatch->mHalHandle); 575 } 576 } break; 577 case AUDIO_PORT_TYPE_MIX: { 578 audio_module_handle_t srcModule = patch->sources[0].ext.mix.hw_module; 579 ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); 580 if (index < 0) { 581 ALOGW("releaseAudioPatch() bad src hw module %d", srcModule); 582 status = BAD_VALUE; 583 break; 584 } 585 sp<ThreadBase> thread = 586 audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle); 587 if (thread == 0) { 588 ALOGW("releaseAudioPatch() bad playback I/O handle %d", 589 patch->sources[0].ext.mix.handle); 590 status = BAD_VALUE; 591 break; 592 } 593 status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle); 594 } break; 595 default: 596 status = BAD_VALUE; 597 break; 598 } 599 600 delete removedPatch; 601 return status; 602} 603 604 605/* List connected audio ports and they attributes */ 606status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __unused, 607 struct audio_patch *patches __unused) 608{ 609 ALOGV("listAudioPatches"); 610 return NO_ERROR; 611} 612 613/* Set audio port configuration */ 614status_t AudioFlinger::PatchPanel::setAudioPortConfig(const struct audio_port_config *config) 615{ 616 ALOGV("setAudioPortConfig"); 617 status_t status = NO_ERROR; 618 619 sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); 620 if (audioflinger == 0) { 621 return NO_INIT; 622 } 623 624 audio_module_handle_t module; 625 if (config->type == AUDIO_PORT_TYPE_DEVICE) { 626 module = config->ext.device.hw_module; 627 } else { 628 module = config->ext.mix.hw_module; 629 } 630 631 ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(module); 632 if (index < 0) { 633 ALOGW("setAudioPortConfig() bad hw module %d", module); 634 return BAD_VALUE; 635 } 636 637 AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); 638 if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { 639 audio_hw_device_t *hwDevice = audioHwDevice->hwDevice(); 640 return hwDevice->set_audio_port_config(hwDevice, config); 641 } else { 642 return INVALID_OPERATION; 643 } 644 return NO_ERROR; 645} 646 647} // namespace android 648