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