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 // 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 sp<DeviceHalInterface> hwDevice = 206 audioflinger->mAudioHwDevs.valueAt(index)->hwDevice(); 207 hwDevice->releaseAudioPatch(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 does not support audio patches creation 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->supportsAudioPatches()))) { 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 sp<ThreadBase> thread = 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 newPatch->mPlaybackThread = (PlaybackThread *)thread.get(); 285 ALOGV("audioflinger->openOutput_l() returned %p", 286 newPatch->mPlaybackThread.get()); 287 if (newPatch->mPlaybackThread == 0) { 288 status = NO_MEMORY; 289 goto exit; 290 } 291 } 292 audio_devices_t device = patch->sources[0].ext.device.type; 293 String8 address = String8(patch->sources[0].ext.device.address); 294 audio_config_t config = AUDIO_CONFIG_INITIALIZER; 295 // open input stream with source device audio properties if provided or 296 // default to peer output stream properties otherwise. 297 if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { 298 config.sample_rate = patch->sources[0].sample_rate; 299 } else { 300 config.sample_rate = newPatch->mPlaybackThread->sampleRate(); 301 } 302 if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { 303 config.channel_mask = patch->sources[0].channel_mask; 304 } else { 305 config.channel_mask = 306 audio_channel_in_mask_from_count(newPatch->mPlaybackThread->channelCount()); 307 } 308 if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_FORMAT) { 309 config.format = patch->sources[0].format; 310 } else { 311 config.format = newPatch->mPlaybackThread->format(); 312 } 313 audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; 314 sp<ThreadBase> thread = audioflinger->openInput_l(srcModule, 315 &input, 316 &config, 317 device, 318 address, 319 AUDIO_SOURCE_MIC, 320 AUDIO_INPUT_FLAG_NONE); 321 newPatch->mRecordThread = (RecordThread *)thread.get(); 322 ALOGV("audioflinger->openInput_l() returned %p inChannelMask %08x", 323 newPatch->mRecordThread.get(), config.channel_mask); 324 if (newPatch->mRecordThread == 0) { 325 status = NO_MEMORY; 326 goto exit; 327 } 328 status = createPatchConnections(newPatch, patch); 329 if (status != NO_ERROR) { 330 goto exit; 331 } 332 } else { 333 if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { 334 sp<ThreadBase> thread = audioflinger->checkRecordThread_l( 335 patch->sinks[0].ext.mix.handle); 336 if (thread == 0) { 337 thread = audioflinger->checkMmapThread_l(patch->sinks[0].ext.mix.handle); 338 if (thread == 0) { 339 ALOGW("createAudioPatch() bad capture I/O handle %d", 340 patch->sinks[0].ext.mix.handle); 341 status = BAD_VALUE; 342 goto exit; 343 } 344 } 345 status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); 346 } else { 347 sp<DeviceHalInterface> hwDevice = audioHwDevice->hwDevice(); 348 status = hwDevice->createAudioPatch(patch->num_sources, 349 patch->sources, 350 patch->num_sinks, 351 patch->sinks, 352 &halHandle); 353 if (status == INVALID_OPERATION) goto exit; 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 thread = audioflinger->checkMmapThread_l(patch->sources[0].ext.mix.handle); 385 if (thread == 0) { 386 ALOGW("createAudioPatch() bad playback I/O handle %d", 387 patch->sources[0].ext.mix.handle); 388 status = BAD_VALUE; 389 goto exit; 390 } 391 } 392 if (thread == audioflinger->primaryPlaybackThread_l()) { 393 AudioParameter param = AudioParameter(); 394 param.addInt(String8(AudioParameter::keyRouting), (int)type); 395 396 audioflinger->broacastParametersToRecordThreads_l(param.toString()); 397 } 398 399 status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); 400 } break; 401 default: 402 status = BAD_VALUE; 403 goto exit; 404 } 405exit: 406 ALOGV("createAudioPatch() status %d", status); 407 if (status == NO_ERROR) { 408 *handle = (audio_patch_handle_t) audioflinger->nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH); 409 newPatch->mHandle = *handle; 410 newPatch->mHalHandle = halHandle; 411 mPatches.add(newPatch); 412 ALOGV("createAudioPatch() added new patch handle %d halHandle %d", *handle, halHandle); 413 } else { 414 clearPatchConnections(newPatch); 415 delete newPatch; 416 } 417 return status; 418} 419 420status_t AudioFlinger::PatchPanel::createPatchConnections(Patch *patch, 421 const struct audio_patch *audioPatch) 422{ 423 // create patch from source device to record thread input 424 struct audio_patch subPatch; 425 subPatch.num_sources = 1; 426 subPatch.sources[0] = audioPatch->sources[0]; 427 subPatch.num_sinks = 1; 428 429 patch->mRecordThread->getAudioPortConfig(&subPatch.sinks[0]); 430 subPatch.sinks[0].ext.mix.usecase.source = AUDIO_SOURCE_MIC; 431 432 status_t status = createAudioPatch(&subPatch, &patch->mRecordPatchHandle); 433 if (status != NO_ERROR) { 434 patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; 435 return status; 436 } 437 438 // create patch from playback thread output to sink device 439 if (audioPatch->num_sinks != 0) { 440 patch->mPlaybackThread->getAudioPortConfig(&subPatch.sources[0]); 441 subPatch.sinks[0] = audioPatch->sinks[0]; 442 status = createAudioPatch(&subPatch, &patch->mPlaybackPatchHandle); 443 if (status != NO_ERROR) { 444 patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; 445 return status; 446 } 447 } else { 448 patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; 449 } 450 451 // use a pseudo LCM between input and output framecount 452 size_t playbackFrameCount = patch->mPlaybackThread->frameCount(); 453 int playbackShift = __builtin_ctz(playbackFrameCount); 454 size_t recordFramecount = patch->mRecordThread->frameCount(); 455 int shift = __builtin_ctz(recordFramecount); 456 if (playbackShift < shift) { 457 shift = playbackShift; 458 } 459 size_t frameCount = (playbackFrameCount * recordFramecount) >> shift; 460 ALOGV("createPatchConnections() playframeCount %zu recordFramecount %zu frameCount %zu", 461 playbackFrameCount, recordFramecount, frameCount); 462 463 // create a special record track to capture from record thread 464 uint32_t channelCount = patch->mPlaybackThread->channelCount(); 465 audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount); 466 audio_channel_mask_t outChannelMask = patch->mPlaybackThread->channelMask(); 467 uint32_t sampleRate = patch->mPlaybackThread->sampleRate(); 468 audio_format_t format = patch->mPlaybackThread->format(); 469 470 patch->mPatchRecord = new RecordThread::PatchRecord( 471 patch->mRecordThread.get(), 472 sampleRate, 473 inChannelMask, 474 format, 475 frameCount, 476 NULL, 477 AUDIO_INPUT_FLAG_NONE); 478 if (patch->mPatchRecord == 0) { 479 return NO_MEMORY; 480 } 481 status = patch->mPatchRecord->initCheck(); 482 if (status != NO_ERROR) { 483 return status; 484 } 485 patch->mRecordThread->addPatchRecord(patch->mPatchRecord); 486 487 // create a special playback track to render to playback thread. 488 // this track is given the same buffer as the PatchRecord buffer 489 patch->mPatchTrack = new PlaybackThread::PatchTrack( 490 patch->mPlaybackThread.get(), 491 audioPatch->sources[1].ext.mix.usecase.stream, 492 sampleRate, 493 outChannelMask, 494 format, 495 frameCount, 496 patch->mPatchRecord->buffer(), 497 AUDIO_OUTPUT_FLAG_NONE); 498 if (patch->mPatchTrack == 0) { 499 return NO_MEMORY; 500 } 501 status = patch->mPatchTrack->initCheck(); 502 if (status != NO_ERROR) { 503 return status; 504 } 505 patch->mPlaybackThread->addPatchTrack(patch->mPatchTrack); 506 507 // tie playback and record tracks together 508 patch->mPatchRecord->setPeerProxy(patch->mPatchTrack.get()); 509 patch->mPatchTrack->setPeerProxy(patch->mPatchRecord.get()); 510 511 // start capture and playback 512 patch->mPatchRecord->start(AudioSystem::SYNC_EVENT_NONE, AUDIO_SESSION_NONE); 513 patch->mPatchTrack->start(); 514 515 return status; 516} 517 518void AudioFlinger::PatchPanel::clearPatchConnections(Patch *patch) 519{ 520 sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); 521 if (audioflinger == 0) { 522 return; 523 } 524 525 ALOGV("clearPatchConnections() patch->mRecordPatchHandle %d patch->mPlaybackPatchHandle %d", 526 patch->mRecordPatchHandle, patch->mPlaybackPatchHandle); 527 528 if (patch->mPatchRecord != 0) { 529 patch->mPatchRecord->stop(); 530 } 531 if (patch->mPatchTrack != 0) { 532 patch->mPatchTrack->stop(); 533 } 534 if (patch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE) { 535 releaseAudioPatch(patch->mRecordPatchHandle); 536 patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; 537 } 538 if (patch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) { 539 releaseAudioPatch(patch->mPlaybackPatchHandle); 540 patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; 541 } 542 if (patch->mRecordThread != 0) { 543 if (patch->mPatchRecord != 0) { 544 patch->mRecordThread->deletePatchRecord(patch->mPatchRecord); 545 } 546 audioflinger->closeInputInternal_l(patch->mRecordThread); 547 } 548 if (patch->mPlaybackThread != 0) { 549 if (patch->mPatchTrack != 0) { 550 patch->mPlaybackThread->deletePatchTrack(patch->mPatchTrack); 551 } 552 // if num sources == 2 we are reusing an existing playback thread so we do not close it 553 if (patch->mAudioPatch.num_sources != 2) { 554 audioflinger->closeOutputInternal_l(patch->mPlaybackThread); 555 } 556 } 557 if (patch->mRecordThread != 0) { 558 if (patch->mPatchRecord != 0) { 559 patch->mPatchRecord.clear(); 560 } 561 patch->mRecordThread.clear(); 562 } 563 if (patch->mPlaybackThread != 0) { 564 if (patch->mPatchTrack != 0) { 565 patch->mPatchTrack.clear(); 566 } 567 patch->mPlaybackThread.clear(); 568 } 569 570} 571 572/* Disconnect a patch */ 573status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle) 574{ 575 ALOGV("releaseAudioPatch handle %d", handle); 576 status_t status = NO_ERROR; 577 size_t index; 578 579 sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); 580 if (audioflinger == 0) { 581 return NO_INIT; 582 } 583 584 for (index = 0; index < mPatches.size(); index++) { 585 if (handle == mPatches[index]->mHandle) { 586 break; 587 } 588 } 589 if (index == mPatches.size()) { 590 return BAD_VALUE; 591 } 592 Patch *removedPatch = mPatches[index]; 593 mPatches.removeAt(index); 594 595 struct audio_patch *patch = &removedPatch->mAudioPatch; 596 597 switch (patch->sources[0].type) { 598 case AUDIO_PORT_TYPE_DEVICE: { 599 audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module; 600 ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); 601 if (index < 0) { 602 ALOGW("releaseAudioPatch() bad src hw module %d", srcModule); 603 status = BAD_VALUE; 604 break; 605 } 606 607 if (removedPatch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE || 608 removedPatch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) { 609 clearPatchConnections(removedPatch); 610 break; 611 } 612 613 if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { 614 sp<ThreadBase> thread = audioflinger->checkRecordThread_l( 615 patch->sinks[0].ext.mix.handle); 616 if (thread == 0) { 617 thread = audioflinger->checkMmapThread_l(patch->sinks[0].ext.mix.handle); 618 if (thread == 0) { 619 ALOGW("releaseAudioPatch() bad capture I/O handle %d", 620 patch->sinks[0].ext.mix.handle); 621 status = BAD_VALUE; 622 break; 623 } 624 } 625 status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle); 626 } else { 627 AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); 628 sp<DeviceHalInterface> hwDevice = audioHwDevice->hwDevice(); 629 status = hwDevice->releaseAudioPatch(removedPatch->mHalHandle); 630 } 631 } break; 632 case AUDIO_PORT_TYPE_MIX: { 633 audio_module_handle_t srcModule = patch->sources[0].ext.mix.hw_module; 634 ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); 635 if (index < 0) { 636 ALOGW("releaseAudioPatch() bad src hw module %d", srcModule); 637 status = BAD_VALUE; 638 break; 639 } 640 sp<ThreadBase> thread = 641 audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle); 642 if (thread == 0) { 643 thread = audioflinger->checkMmapThread_l(patch->sources[0].ext.mix.handle); 644 if (thread == 0) { 645 ALOGW("releaseAudioPatch() bad playback I/O handle %d", 646 patch->sources[0].ext.mix.handle); 647 status = BAD_VALUE; 648 break; 649 } 650 } 651 status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle); 652 } break; 653 default: 654 status = BAD_VALUE; 655 break; 656 } 657 658 delete removedPatch; 659 return status; 660} 661 662 663/* List connected audio ports and they attributes */ 664status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __unused, 665 struct audio_patch *patches __unused) 666{ 667 ALOGV("listAudioPatches"); 668 return NO_ERROR; 669} 670 671/* Set audio port configuration */ 672status_t AudioFlinger::PatchPanel::setAudioPortConfig(const struct audio_port_config *config) 673{ 674 ALOGV("setAudioPortConfig"); 675 676 sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); 677 if (audioflinger == 0) { 678 return NO_INIT; 679 } 680 681 audio_module_handle_t module; 682 if (config->type == AUDIO_PORT_TYPE_DEVICE) { 683 module = config->ext.device.hw_module; 684 } else { 685 module = config->ext.mix.hw_module; 686 } 687 688 ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(module); 689 if (index < 0) { 690 ALOGW("setAudioPortConfig() bad hw module %d", module); 691 return BAD_VALUE; 692 } 693 694 AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); 695 return audioHwDevice->hwDevice()->setAudioPortConfig(config); 696} 697 698} // namespace android 699