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 (size_t)0 /* bufferSize */, 478 AUDIO_INPUT_FLAG_NONE); 479 if (patch->mPatchRecord == 0) { 480 return NO_MEMORY; 481 } 482 status = patch->mPatchRecord->initCheck(); 483 if (status != NO_ERROR) { 484 return status; 485 } 486 patch->mRecordThread->addPatchRecord(patch->mPatchRecord); 487 488 // create a special playback track to render to playback thread. 489 // this track is given the same buffer as the PatchRecord buffer 490 patch->mPatchTrack = new PlaybackThread::PatchTrack( 491 patch->mPlaybackThread.get(), 492 audioPatch->sources[1].ext.mix.usecase.stream, 493 sampleRate, 494 outChannelMask, 495 format, 496 frameCount, 497 patch->mPatchRecord->buffer(), 498 patch->mPatchRecord->bufferSize(), 499 AUDIO_OUTPUT_FLAG_NONE); 500 status = patch->mPatchTrack->initCheck(); 501 if (status != NO_ERROR) { 502 return status; 503 } 504 patch->mPlaybackThread->addPatchTrack(patch->mPatchTrack); 505 506 // tie playback and record tracks together 507 patch->mPatchRecord->setPeerProxy(patch->mPatchTrack.get()); 508 patch->mPatchTrack->setPeerProxy(patch->mPatchRecord.get()); 509 510 // start capture and playback 511 patch->mPatchRecord->start(AudioSystem::SYNC_EVENT_NONE, AUDIO_SESSION_NONE); 512 patch->mPatchTrack->start(); 513 514 return status; 515} 516 517void AudioFlinger::PatchPanel::clearPatchConnections(Patch *patch) 518{ 519 sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); 520 if (audioflinger == 0) { 521 return; 522 } 523 524 ALOGV("clearPatchConnections() patch->mRecordPatchHandle %d patch->mPlaybackPatchHandle %d", 525 patch->mRecordPatchHandle, patch->mPlaybackPatchHandle); 526 527 if (patch->mPatchRecord != 0) { 528 patch->mPatchRecord->stop(); 529 } 530 if (patch->mPatchTrack != 0) { 531 patch->mPatchTrack->stop(); 532 } 533 if (patch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE) { 534 releaseAudioPatch(patch->mRecordPatchHandle); 535 patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; 536 } 537 if (patch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) { 538 releaseAudioPatch(patch->mPlaybackPatchHandle); 539 patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; 540 } 541 if (patch->mRecordThread != 0) { 542 if (patch->mPatchRecord != 0) { 543 patch->mRecordThread->deletePatchRecord(patch->mPatchRecord); 544 } 545 audioflinger->closeInputInternal_l(patch->mRecordThread); 546 } 547 if (patch->mPlaybackThread != 0) { 548 if (patch->mPatchTrack != 0) { 549 patch->mPlaybackThread->deletePatchTrack(patch->mPatchTrack); 550 } 551 // if num sources == 2 we are reusing an existing playback thread so we do not close it 552 if (patch->mAudioPatch.num_sources != 2) { 553 audioflinger->closeOutputInternal_l(patch->mPlaybackThread); 554 } 555 } 556 if (patch->mRecordThread != 0) { 557 if (patch->mPatchRecord != 0) { 558 patch->mPatchRecord.clear(); 559 } 560 patch->mRecordThread.clear(); 561 } 562 if (patch->mPlaybackThread != 0) { 563 if (patch->mPatchTrack != 0) { 564 patch->mPatchTrack.clear(); 565 } 566 patch->mPlaybackThread.clear(); 567 } 568 569} 570 571/* Disconnect a patch */ 572status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle) 573{ 574 ALOGV("releaseAudioPatch handle %d", handle); 575 status_t status = NO_ERROR; 576 size_t index; 577 578 sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); 579 if (audioflinger == 0) { 580 return NO_INIT; 581 } 582 583 for (index = 0; index < mPatches.size(); index++) { 584 if (handle == mPatches[index]->mHandle) { 585 break; 586 } 587 } 588 if (index == mPatches.size()) { 589 return BAD_VALUE; 590 } 591 Patch *removedPatch = mPatches[index]; 592 mPatches.removeAt(index); 593 594 struct audio_patch *patch = &removedPatch->mAudioPatch; 595 596 switch (patch->sources[0].type) { 597 case AUDIO_PORT_TYPE_DEVICE: { 598 audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module; 599 ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); 600 if (index < 0) { 601 ALOGW("releaseAudioPatch() bad src hw module %d", srcModule); 602 status = BAD_VALUE; 603 break; 604 } 605 606 if (removedPatch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE || 607 removedPatch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) { 608 clearPatchConnections(removedPatch); 609 break; 610 } 611 612 if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { 613 sp<ThreadBase> thread = audioflinger->checkRecordThread_l( 614 patch->sinks[0].ext.mix.handle); 615 if (thread == 0) { 616 thread = audioflinger->checkMmapThread_l(patch->sinks[0].ext.mix.handle); 617 if (thread == 0) { 618 ALOGW("releaseAudioPatch() bad capture I/O handle %d", 619 patch->sinks[0].ext.mix.handle); 620 status = BAD_VALUE; 621 break; 622 } 623 } 624 status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle); 625 } else { 626 AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); 627 sp<DeviceHalInterface> hwDevice = audioHwDevice->hwDevice(); 628 status = hwDevice->releaseAudioPatch(removedPatch->mHalHandle); 629 } 630 } break; 631 case AUDIO_PORT_TYPE_MIX: { 632 audio_module_handle_t srcModule = patch->sources[0].ext.mix.hw_module; 633 ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); 634 if (index < 0) { 635 ALOGW("releaseAudioPatch() bad src hw module %d", srcModule); 636 status = BAD_VALUE; 637 break; 638 } 639 sp<ThreadBase> thread = 640 audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle); 641 if (thread == 0) { 642 thread = audioflinger->checkMmapThread_l(patch->sources[0].ext.mix.handle); 643 if (thread == 0) { 644 ALOGW("releaseAudioPatch() bad playback I/O handle %d", 645 patch->sources[0].ext.mix.handle); 646 status = BAD_VALUE; 647 break; 648 } 649 } 650 status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle); 651 } break; 652 default: 653 status = BAD_VALUE; 654 break; 655 } 656 657 delete removedPatch; 658 return status; 659} 660 661 662/* List connected audio ports and they attributes */ 663status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __unused, 664 struct audio_patch *patches __unused) 665{ 666 ALOGV("listAudioPatches"); 667 return NO_ERROR; 668} 669 670/* Set audio port configuration */ 671status_t AudioFlinger::PatchPanel::setAudioPortConfig(const struct audio_port_config *config) 672{ 673 ALOGV("setAudioPortConfig"); 674 675 sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); 676 if (audioflinger == 0) { 677 return NO_INIT; 678 } 679 680 audio_module_handle_t module; 681 if (config->type == AUDIO_PORT_TYPE_DEVICE) { 682 module = config->ext.device.hw_module; 683 } else { 684 module = config->ext.mix.hw_module; 685 } 686 687 ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(module); 688 if (index < 0) { 689 ALOGW("setAudioPortConfig() bad hw module %d", module); 690 return BAD_VALUE; 691 } 692 693 AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); 694 return audioHwDevice->hwDevice()->setAudioPortConfig(config); 695} 696 697} // namespace android 698