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