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