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 status_t status = NO_ERROR; 143 audio_patch_handle_t halHandle = AUDIO_PATCH_HANDLE_NONE; 144 sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); 145 if (handle == NULL || patch == NULL) { 146 return BAD_VALUE; 147 } 148 ALOGV("createAudioPatch() num_sources %d num_sinks %d handle %d", 149 patch->num_sources, patch->num_sinks, *handle); 150 if (audioflinger == 0) { 151 return NO_INIT; 152 } 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 if ((removedPatch->mRecordPatchHandle 172 != AUDIO_PATCH_HANDLE_NONE) || 173 (removedPatch->mPlaybackPatchHandle != 174 AUDIO_PATCH_HANDLE_NONE)) { 175 clearPatchConnections(removedPatch); 176 } 177 mPatches.removeAt(index); 178 delete removedPatch; 179 break; 180 } 181 } 182 } 183 184 Patch *newPatch = new Patch(patch); 185 186 switch (patch->sources[0].type) { 187 case AUDIO_PORT_TYPE_DEVICE: { 188 audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module; 189 ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); 190 if (index < 0) { 191 ALOGW("createAudioPatch() bad src hw module %d", srcModule); 192 status = BAD_VALUE; 193 goto exit; 194 } 195 AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); 196 for (unsigned int i = 0; i < patch->num_sinks; i++) { 197 // support only one sink if connection to a mix or across HW modules 198 if ((patch->sinks[i].type == AUDIO_PORT_TYPE_MIX || 199 patch->sinks[i].ext.mix.hw_module != srcModule) && 200 patch->num_sinks > 1) { 201 status = INVALID_OPERATION; 202 goto exit; 203 } 204 // reject connection to different sink types 205 if (patch->sinks[i].type != patch->sinks[0].type) { 206 ALOGW("createAudioPatch() different sink types in same patch not supported"); 207 status = BAD_VALUE; 208 goto exit; 209 } 210 } 211 212 // manage patches requiring a software bridge 213 // - special patch request with 2 sources (reuse one existing output mix) OR 214 // - Device to device AND 215 // - source HW module != destination HW module OR 216 // - audio HAL version < 3.0 217 if ((patch->num_sources == 2) || 218 ((patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) && 219 ((patch->sinks[0].ext.device.hw_module != srcModule) || 220 (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0)))) { 221 if (patch->num_sources == 2) { 222 if (patch->sources[1].type != AUDIO_PORT_TYPE_MIX || 223 (patch->num_sinks != 0 && patch->sinks[0].ext.device.hw_module != 224 patch->sources[1].ext.mix.hw_module)) { 225 ALOGW("createAudioPatch() invalid source combination"); 226 status = INVALID_OPERATION; 227 goto exit; 228 } 229 230 sp<ThreadBase> thread = 231 audioflinger->checkPlaybackThread_l(patch->sources[1].ext.mix.handle); 232 newPatch->mPlaybackThread = (MixerThread *)thread.get(); 233 if (thread == 0) { 234 ALOGW("createAudioPatch() cannot get playback thread"); 235 status = INVALID_OPERATION; 236 goto exit; 237 } 238 } else { 239 audio_config_t config = AUDIO_CONFIG_INITIALIZER; 240 audio_devices_t device = patch->sinks[0].ext.device.type; 241 String8 address = String8(patch->sinks[0].ext.device.address); 242 audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; 243 newPatch->mPlaybackThread = audioflinger->openOutput_l( 244 patch->sinks[0].ext.device.hw_module, 245 &output, 246 &config, 247 device, 248 address, 249 AUDIO_OUTPUT_FLAG_NONE); 250 ALOGV("audioflinger->openOutput_l() returned %p", 251 newPatch->mPlaybackThread.get()); 252 if (newPatch->mPlaybackThread == 0) { 253 status = NO_MEMORY; 254 goto exit; 255 } 256 } 257 audio_devices_t device = patch->sources[0].ext.device.type; 258 String8 address = String8(patch->sources[0].ext.device.address); 259 audio_config_t config = AUDIO_CONFIG_INITIALIZER; 260 // open input stream with source device audio properties if provided or 261 // default to peer output stream properties otherwise. 262 if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { 263 config.sample_rate = patch->sources[0].sample_rate; 264 } else { 265 config.sample_rate = newPatch->mPlaybackThread->sampleRate(); 266 } 267 if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { 268 config.channel_mask = patch->sources[0].channel_mask; 269 } else { 270 config.channel_mask = 271 audio_channel_in_mask_from_count(newPatch->mPlaybackThread->channelCount()); 272 } 273 if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_FORMAT) { 274 config.format = patch->sources[0].format; 275 } else { 276 config.format = newPatch->mPlaybackThread->format(); 277 } 278 audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; 279 newPatch->mRecordThread = audioflinger->openInput_l(srcModule, 280 &input, 281 &config, 282 device, 283 address, 284 AUDIO_SOURCE_MIC, 285 AUDIO_INPUT_FLAG_NONE); 286 ALOGV("audioflinger->openInput_l() returned %p inChannelMask %08x", 287 newPatch->mRecordThread.get(), config.channel_mask); 288 if (newPatch->mRecordThread == 0) { 289 status = NO_MEMORY; 290 goto exit; 291 } 292 status = createPatchConnections(newPatch, patch); 293 if (status != NO_ERROR) { 294 goto exit; 295 } 296 } else { 297 if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { 298 sp<ThreadBase> thread = audioflinger->checkRecordThread_l( 299 patch->sinks[0].ext.mix.handle); 300 if (thread == 0) { 301 ALOGW("createAudioPatch() bad capture I/O handle %d", 302 patch->sinks[0].ext.mix.handle); 303 status = BAD_VALUE; 304 goto exit; 305 } 306 status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); 307 } else { 308 if (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) { 309 status = INVALID_OPERATION; 310 goto exit; 311 } 312 313 audio_hw_device_t *hwDevice = audioHwDevice->hwDevice(); 314 status = hwDevice->create_audio_patch(hwDevice, 315 patch->num_sources, 316 patch->sources, 317 patch->num_sinks, 318 patch->sinks, 319 &halHandle); 320 } 321 } 322 } break; 323 case AUDIO_PORT_TYPE_MIX: { 324 audio_module_handle_t srcModule = patch->sources[0].ext.mix.hw_module; 325 ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); 326 if (index < 0) { 327 ALOGW("createAudioPatch() bad src hw module %d", srcModule); 328 status = BAD_VALUE; 329 goto exit; 330 } 331 // limit to connections between devices and output streams 332 audio_devices_t type = AUDIO_DEVICE_NONE; 333 for (unsigned int i = 0; i < patch->num_sinks; i++) { 334 if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) { 335 ALOGW("createAudioPatch() invalid sink type %d for mix source", 336 patch->sinks[i].type); 337 status = BAD_VALUE; 338 goto exit; 339 } 340 // limit to connections between sinks and sources on same HW module 341 if (patch->sinks[i].ext.device.hw_module != srcModule) { 342 status = BAD_VALUE; 343 goto exit; 344 } 345 type |= patch->sinks[i].ext.device.type; 346 } 347 sp<ThreadBase> thread = 348 audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle); 349 if (thread == 0) { 350 ALOGW("createAudioPatch() bad playback I/O handle %d", 351 patch->sources[0].ext.mix.handle); 352 status = BAD_VALUE; 353 goto exit; 354 } 355 if (thread == audioflinger->primaryPlaybackThread_l()) { 356 AudioParameter param = AudioParameter(); 357 param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), (int)type); 358 359 audioflinger->broacastParametersToRecordThreads_l(param.toString()); 360 } 361 362 status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); 363 } break; 364 default: 365 status = BAD_VALUE; 366 goto exit; 367 } 368exit: 369 ALOGV("createAudioPatch() status %d", status); 370 if (status == NO_ERROR) { 371 *handle = (audio_patch_handle_t) audioflinger->nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH); 372 newPatch->mHandle = *handle; 373 newPatch->mHalHandle = halHandle; 374 mPatches.add(newPatch); 375 ALOGV("createAudioPatch() added new patch handle %d halHandle %d", *handle, halHandle); 376 } else { 377 clearPatchConnections(newPatch); 378 delete newPatch; 379 } 380 return status; 381} 382 383status_t AudioFlinger::PatchPanel::createPatchConnections(Patch *patch, 384 const struct audio_patch *audioPatch) 385{ 386 // create patch from source device to record thread input 387 struct audio_patch subPatch; 388 subPatch.num_sources = 1; 389 subPatch.sources[0] = audioPatch->sources[0]; 390 subPatch.num_sinks = 1; 391 392 patch->mRecordThread->getAudioPortConfig(&subPatch.sinks[0]); 393 subPatch.sinks[0].ext.mix.usecase.source = AUDIO_SOURCE_MIC; 394 395 status_t status = createAudioPatch(&subPatch, &patch->mRecordPatchHandle); 396 if (status != NO_ERROR) { 397 patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; 398 return status; 399 } 400 401 // create patch from playback thread output to sink device 402 if (audioPatch->num_sinks != 0) { 403 patch->mPlaybackThread->getAudioPortConfig(&subPatch.sources[0]); 404 subPatch.sinks[0] = audioPatch->sinks[0]; 405 status = createAudioPatch(&subPatch, &patch->mPlaybackPatchHandle); 406 if (status != NO_ERROR) { 407 patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; 408 return status; 409 } 410 } else { 411 patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; 412 } 413 414 // use a pseudo LCM between input and output framecount 415 size_t playbackFrameCount = patch->mPlaybackThread->frameCount(); 416 int playbackShift = __builtin_ctz(playbackFrameCount); 417 size_t recordFramecount = patch->mRecordThread->frameCount(); 418 int shift = __builtin_ctz(recordFramecount); 419 if (playbackShift < shift) { 420 shift = playbackShift; 421 } 422 size_t frameCount = (playbackFrameCount * recordFramecount) >> shift; 423 ALOGV("createPatchConnections() playframeCount %zu recordFramecount %zu frameCount %zu", 424 playbackFrameCount, recordFramecount, frameCount); 425 426 // create a special record track to capture from record thread 427 uint32_t channelCount = patch->mPlaybackThread->channelCount(); 428 audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount); 429 audio_channel_mask_t outChannelMask = patch->mPlaybackThread->channelMask(); 430 uint32_t sampleRate = patch->mPlaybackThread->sampleRate(); 431 audio_format_t format = patch->mPlaybackThread->format(); 432 433 patch->mPatchRecord = new RecordThread::PatchRecord( 434 patch->mRecordThread.get(), 435 sampleRate, 436 inChannelMask, 437 format, 438 frameCount, 439 NULL, 440 IAudioFlinger::TRACK_DEFAULT); 441 if (patch->mPatchRecord == 0) { 442 return NO_MEMORY; 443 } 444 status = patch->mPatchRecord->initCheck(); 445 if (status != NO_ERROR) { 446 return status; 447 } 448 patch->mRecordThread->addPatchRecord(patch->mPatchRecord); 449 450 // create a special playback track to render to playback thread. 451 // this track is given the same buffer as the PatchRecord buffer 452 patch->mPatchTrack = new PlaybackThread::PatchTrack( 453 patch->mPlaybackThread.get(), 454 audioPatch->sources[1].ext.mix.usecase.stream, 455 sampleRate, 456 outChannelMask, 457 format, 458 frameCount, 459 patch->mPatchRecord->buffer(), 460 IAudioFlinger::TRACK_DEFAULT); 461 if (patch->mPatchTrack == 0) { 462 return NO_MEMORY; 463 } 464 status = patch->mPatchTrack->initCheck(); 465 if (status != NO_ERROR) { 466 return status; 467 } 468 patch->mPlaybackThread->addPatchTrack(patch->mPatchTrack); 469 470 // tie playback and record tracks together 471 patch->mPatchRecord->setPeerProxy(patch->mPatchTrack.get()); 472 patch->mPatchTrack->setPeerProxy(patch->mPatchRecord.get()); 473 474 // start capture and playback 475 patch->mPatchRecord->start(AudioSystem::SYNC_EVENT_NONE, AUDIO_SESSION_NONE); 476 patch->mPatchTrack->start(); 477 478 return status; 479} 480 481void AudioFlinger::PatchPanel::clearPatchConnections(Patch *patch) 482{ 483 sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); 484 if (audioflinger == 0) { 485 return; 486 } 487 488 ALOGV("clearPatchConnections() patch->mRecordPatchHandle %d patch->mPlaybackPatchHandle %d", 489 patch->mRecordPatchHandle, patch->mPlaybackPatchHandle); 490 491 if (patch->mPatchRecord != 0) { 492 patch->mPatchRecord->stop(); 493 } 494 if (patch->mPatchTrack != 0) { 495 patch->mPatchTrack->stop(); 496 } 497 if (patch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE) { 498 releaseAudioPatch(patch->mRecordPatchHandle); 499 patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; 500 } 501 if (patch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) { 502 releaseAudioPatch(patch->mPlaybackPatchHandle); 503 patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; 504 } 505 if (patch->mRecordThread != 0) { 506 if (patch->mPatchRecord != 0) { 507 patch->mRecordThread->deletePatchRecord(patch->mPatchRecord); 508 } 509 audioflinger->closeInputInternal_l(patch->mRecordThread); 510 } 511 if (patch->mPlaybackThread != 0) { 512 if (patch->mPatchTrack != 0) { 513 patch->mPlaybackThread->deletePatchTrack(patch->mPatchTrack); 514 } 515 // if num sources == 2 we are reusing an existing playback thread so we do not close it 516 if (patch->mAudioPatch.num_sources != 2) { 517 audioflinger->closeOutputInternal_l(patch->mPlaybackThread); 518 } 519 } 520 if (patch->mRecordThread != 0) { 521 if (patch->mPatchRecord != 0) { 522 patch->mPatchRecord.clear(); 523 } 524 patch->mRecordThread.clear(); 525 } 526 if (patch->mPlaybackThread != 0) { 527 if (patch->mPatchTrack != 0) { 528 patch->mPatchTrack.clear(); 529 } 530 patch->mPlaybackThread.clear(); 531 } 532 533} 534 535/* Disconnect a patch */ 536status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle) 537{ 538 ALOGV("releaseAudioPatch handle %d", handle); 539 status_t status = NO_ERROR; 540 size_t index; 541 542 sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); 543 if (audioflinger == 0) { 544 return NO_INIT; 545 } 546 547 for (index = 0; index < mPatches.size(); index++) { 548 if (handle == mPatches[index]->mHandle) { 549 break; 550 } 551 } 552 if (index == mPatches.size()) { 553 return BAD_VALUE; 554 } 555 Patch *removedPatch = mPatches[index]; 556 mPatches.removeAt(index); 557 558 struct audio_patch *patch = &removedPatch->mAudioPatch; 559 560 switch (patch->sources[0].type) { 561 case AUDIO_PORT_TYPE_DEVICE: { 562 audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module; 563 ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); 564 if (index < 0) { 565 ALOGW("releaseAudioPatch() bad src hw module %d", srcModule); 566 status = BAD_VALUE; 567 break; 568 } 569 570 if (removedPatch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE || 571 removedPatch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) { 572 clearPatchConnections(removedPatch); 573 break; 574 } 575 576 if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { 577 sp<ThreadBase> thread = audioflinger->checkRecordThread_l( 578 patch->sinks[0].ext.mix.handle); 579 if (thread == 0) { 580 ALOGW("releaseAudioPatch() bad capture I/O handle %d", 581 patch->sinks[0].ext.mix.handle); 582 status = BAD_VALUE; 583 break; 584 } 585 status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle); 586 } else { 587 AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); 588 if (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) { 589 status = INVALID_OPERATION; 590 break; 591 } 592 audio_hw_device_t *hwDevice = audioHwDevice->hwDevice(); 593 status = hwDevice->release_audio_patch(hwDevice, removedPatch->mHalHandle); 594 } 595 } break; 596 case AUDIO_PORT_TYPE_MIX: { 597 audio_module_handle_t srcModule = patch->sources[0].ext.mix.hw_module; 598 ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); 599 if (index < 0) { 600 ALOGW("releaseAudioPatch() bad src hw module %d", srcModule); 601 status = BAD_VALUE; 602 break; 603 } 604 sp<ThreadBase> thread = 605 audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle); 606 if (thread == 0) { 607 ALOGW("releaseAudioPatch() bad playback I/O handle %d", 608 patch->sources[0].ext.mix.handle); 609 status = BAD_VALUE; 610 break; 611 } 612 status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle); 613 } break; 614 default: 615 status = BAD_VALUE; 616 break; 617 } 618 619 delete removedPatch; 620 return status; 621} 622 623 624/* List connected audio ports and they attributes */ 625status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __unused, 626 struct audio_patch *patches __unused) 627{ 628 ALOGV("listAudioPatches"); 629 return NO_ERROR; 630} 631 632/* Set audio port configuration */ 633status_t AudioFlinger::PatchPanel::setAudioPortConfig(const struct audio_port_config *config) 634{ 635 ALOGV("setAudioPortConfig"); 636 637 sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); 638 if (audioflinger == 0) { 639 return NO_INIT; 640 } 641 642 audio_module_handle_t module; 643 if (config->type == AUDIO_PORT_TYPE_DEVICE) { 644 module = config->ext.device.hw_module; 645 } else { 646 module = config->ext.mix.hw_module; 647 } 648 649 ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(module); 650 if (index < 0) { 651 ALOGW("setAudioPortConfig() bad hw module %d", module); 652 return BAD_VALUE; 653 } 654 655 AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); 656 if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { 657 audio_hw_device_t *hwDevice = audioHwDevice->hwDevice(); 658 return hwDevice->set_audio_port_config(hwDevice, config); 659 } else { 660 return INVALID_OPERATION; 661 } 662 return NO_ERROR; 663} 664 665} // namespace android 666