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