| Eric Laurent | 1c333e2 | 2014-05-20 10:48:17 -0700 | [diff] [blame^] | 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 |  | 
 | 45 | namespace android { | 
 | 46 |  | 
 | 47 | /* List connected audio ports and their attributes */ | 
 | 48 | status_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 */ | 
 | 59 | status_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 */ | 
 | 70 | status_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 */ | 
 | 81 | status_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 */ | 
 | 92 | status_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 */ | 
 | 103 | status_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 |  | 
 | 113 | AudioFlinger::PatchPanel::PatchPanel(const sp<AudioFlinger>& audioFlinger) | 
 | 114 |                                    : mAudioFlinger(audioFlinger) | 
 | 115 | { | 
 | 116 | } | 
 | 117 |  | 
 | 118 | AudioFlinger::PatchPanel::~PatchPanel() | 
 | 119 | { | 
 | 120 | } | 
 | 121 |  | 
 | 122 | /* List connected audio ports and their attributes */ | 
 | 123 | status_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 */ | 
 | 131 | status_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 */ | 
 | 139 | status_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 |  | 
 | 146 |     audio_patch_handle_t halHandle = AUDIO_PATCH_HANDLE_NONE; | 
 | 147 |  | 
 | 148 |     sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); | 
 | 149 |     if (audioflinger == 0) { | 
 | 150 |         return NO_INIT; | 
 | 151 |     } | 
 | 152 |     if (handle == NULL || patch == NULL) { | 
 | 153 |         return BAD_VALUE; | 
 | 154 |     } | 
 | 155 |     // limit number of sources to 1 for now | 
 | 156 |     if (patch->num_sources == 0 || patch->num_sources > 1 || | 
 | 157 |             patch->num_sinks == 0 || patch->num_sinks > AUDIO_PATCH_PORTS_MAX) { | 
 | 158 |         return BAD_VALUE; | 
 | 159 |     } | 
 | 160 |  | 
 | 161 |     for (size_t index = 0; *handle != 0 && index < mPatches.size(); index++) { | 
 | 162 |         if (*handle == mPatches[index]->mHandle) { | 
 | 163 |             ALOGV("createAudioPatch() removing patch handle %d", *handle); | 
 | 164 |             halHandle = mPatches[index]->mHalHandle; | 
 | 165 |             mPatches.removeAt(index); | 
 | 166 |             break; | 
 | 167 |         } | 
 | 168 |     } | 
 | 169 |  | 
 | 170 |     switch (patch->sources[0].type) { | 
 | 171 |         case AUDIO_PORT_TYPE_DEVICE: { | 
 | 172 |             // limit number of sinks to 1 for now | 
 | 173 |             if (patch->num_sinks > 1) { | 
 | 174 |                 return BAD_VALUE; | 
 | 175 |             } | 
 | 176 |             audio_module_handle_t src_module = patch->sources[0].ext.device.hw_module; | 
 | 177 |             ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module); | 
 | 178 |             if (index < 0) { | 
 | 179 |                 ALOGW("createAudioPatch() bad src hw module %d", src_module); | 
 | 180 |                 return BAD_VALUE; | 
 | 181 |             } | 
 | 182 |             for (unsigned int i = 0; i < patch->num_sinks; i++) { | 
 | 183 |                 // limit to connections between devices and output streams | 
 | 184 |                 if (patch->sinks[i].type != AUDIO_PORT_TYPE_MIX) { | 
 | 185 |                     ALOGW("createAudioPatch() invalid sink type %d for device source", | 
 | 186 |                           patch->sinks[i].type); | 
 | 187 |                     return BAD_VALUE; | 
 | 188 |                 } | 
 | 189 |                 // limit to connections between sinks and sources on same HW module | 
 | 190 |                 if (patch->sinks[i].ext.mix.hw_module != src_module) { | 
 | 191 |                     ALOGW("createAudioPatch() cannot connect source on module %d to" | 
 | 192 |                             "sink on module %d", src_module, patch->sinks[i].ext.mix.hw_module); | 
 | 193 |                     return BAD_VALUE; | 
 | 194 |                 } | 
 | 195 |             } | 
 | 196 |  | 
 | 197 |             AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); | 
 | 198 |             if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { | 
 | 199 |                 if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { | 
 | 200 |                     sp<ThreadBase> thread = audioflinger->checkRecordThread_l( | 
 | 201 |                                                                     patch->sinks[0].ext.mix.handle); | 
 | 202 |                     if (thread == 0) { | 
 | 203 |                         ALOGW("createAudioPatch() bad capture I/O handle %d", | 
 | 204 |                                                                   patch->sinks[0].ext.mix.handle); | 
 | 205 |                         return BAD_VALUE; | 
 | 206 |                     } | 
 | 207 |                     status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); | 
 | 208 |                 } else { | 
 | 209 |                     audio_hw_device_t *hwDevice = audioHwDevice->hwDevice(); | 
 | 210 |                     status = hwDevice->create_audio_patch(hwDevice, | 
 | 211 |                                                            patch->num_sources, | 
 | 212 |                                                            patch->sources, | 
 | 213 |                                                            patch->num_sinks, | 
 | 214 |                                                            patch->sinks, | 
 | 215 |                                                            &halHandle); | 
 | 216 |                 } | 
 | 217 |             } else { | 
 | 218 |                 sp<ThreadBase> thread = audioflinger->checkRecordThread_l( | 
 | 219 |                                                                     patch->sinks[0].ext.mix.handle); | 
 | 220 |                 if (thread == 0) { | 
 | 221 |                     ALOGW("createAudioPatch() bad capture I/O handle %d", | 
 | 222 |                                                                   patch->sinks[0].ext.mix.handle); | 
 | 223 |                     return BAD_VALUE; | 
 | 224 |                 } | 
 | 225 |                 AudioParameter param; | 
 | 226 |                 param.addInt(String8(AudioParameter::keyRouting), | 
 | 227 |                              (int)patch->sources[0].ext.device.type); | 
 | 228 |                 param.addInt(String8(AudioParameter::keyInputSource), | 
 | 229 |                                                      (int)patch->sinks[0].ext.mix.usecase.source); | 
 | 230 |  | 
 | 231 |                 ALOGW("createAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s", | 
 | 232 |                                                                       param.toString().string()); | 
 | 233 |                 status = thread->setParameters(param.toString()); | 
 | 234 |             } | 
 | 235 |         } break; | 
 | 236 |         case AUDIO_PORT_TYPE_MIX: { | 
 | 237 |             audio_module_handle_t src_module =  patch->sources[0].ext.mix.hw_module; | 
 | 238 |             ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module); | 
 | 239 |             if (index < 0) { | 
 | 240 |                 ALOGW("createAudioPatch() bad src hw module %d", src_module); | 
 | 241 |                 return BAD_VALUE; | 
 | 242 |             } | 
 | 243 |             // limit to connections between devices and output streams | 
 | 244 |             for (unsigned int i = 0; i < patch->num_sinks; i++) { | 
 | 245 |                 if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) { | 
 | 246 |                     ALOGW("createAudioPatch() invalid sink type %d for bus source", | 
 | 247 |                           patch->sinks[i].type); | 
 | 248 |                     return BAD_VALUE; | 
 | 249 |                 } | 
 | 250 |                 // limit to connections between sinks and sources on same HW module | 
 | 251 |                 if (patch->sinks[i].ext.device.hw_module != src_module) { | 
 | 252 |                     return BAD_VALUE; | 
 | 253 |                 } | 
 | 254 |             } | 
 | 255 |             AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); | 
 | 256 |             sp<ThreadBase> thread = | 
 | 257 |                             audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle); | 
 | 258 |             if (thread == 0) { | 
 | 259 |                 ALOGW("createAudioPatch() bad playback I/O handle %d", | 
 | 260 |                           patch->sources[0].ext.mix.handle); | 
 | 261 |                 return BAD_VALUE; | 
 | 262 |             } | 
 | 263 |             if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { | 
 | 264 |                 status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); | 
 | 265 |             } else { | 
 | 266 |                 audio_devices_t type = AUDIO_DEVICE_NONE; | 
 | 267 |                 for (unsigned int i = 0; i < patch->num_sinks; i++) { | 
 | 268 |                     type |= patch->sinks[i].ext.device.type; | 
 | 269 |                 } | 
 | 270 |                 AudioParameter param; | 
 | 271 |                 param.addInt(String8(AudioParameter::keyRouting), (int)type); | 
 | 272 |                 status = thread->setParameters(param.toString()); | 
 | 273 |             } | 
 | 274 |  | 
 | 275 |         } break; | 
 | 276 |         default: | 
 | 277 |             return BAD_VALUE; | 
 | 278 |     } | 
 | 279 |     ALOGV("createAudioPatch() status %d", status); | 
 | 280 |     if (status == NO_ERROR) { | 
 | 281 |         *handle = audioflinger->nextUniqueId(); | 
 | 282 |         Patch *newPatch = new Patch(patch); | 
 | 283 |         newPatch->mHandle = *handle; | 
 | 284 |         newPatch->mHalHandle = halHandle; | 
 | 285 |         mPatches.add(newPatch); | 
 | 286 |         ALOGV("createAudioPatch() added new patch handle %d halHandle %d", *handle, halHandle); | 
 | 287 |     } | 
 | 288 |     return status; | 
 | 289 | } | 
 | 290 |  | 
 | 291 | /* Disconnect a patch */ | 
 | 292 | status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle) | 
 | 293 | { | 
 | 294 |     ALOGV("releaseAudioPatch handle %d", handle); | 
 | 295 |     status_t status = NO_ERROR; | 
 | 296 |     size_t index; | 
 | 297 |  | 
 | 298 |     sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); | 
 | 299 |     if (audioflinger == 0) { | 
 | 300 |         return NO_INIT; | 
 | 301 |     } | 
 | 302 |  | 
 | 303 |     for (index = 0; index < mPatches.size(); index++) { | 
 | 304 |         if (handle == mPatches[index]->mHandle) { | 
 | 305 |             break; | 
 | 306 |         } | 
 | 307 |     } | 
 | 308 |     if (index == mPatches.size()) { | 
 | 309 |         return BAD_VALUE; | 
 | 310 |     } | 
 | 311 |  | 
 | 312 |     struct audio_patch *patch = &mPatches[index]->mAudioPatch; | 
 | 313 |  | 
 | 314 |     switch (patch->sources[0].type) { | 
 | 315 |         case AUDIO_PORT_TYPE_DEVICE: { | 
 | 316 |             audio_module_handle_t src_module = patch->sources[0].ext.device.hw_module; | 
 | 317 |             ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module); | 
 | 318 |             if (index < 0) { | 
 | 319 |                 ALOGW("releaseAudioPatch() bad src hw module %d", src_module); | 
 | 320 |                 status = BAD_VALUE; | 
 | 321 |                 break; | 
 | 322 |             } | 
 | 323 |             AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); | 
 | 324 |             if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { | 
 | 325 |                 if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { | 
 | 326 |                     sp<ThreadBase> thread = audioflinger->checkRecordThread_l( | 
 | 327 |                                                                     patch->sinks[0].ext.mix.handle); | 
 | 328 |                     if (thread == 0) { | 
 | 329 |                         ALOGW("createAudioPatch() bad capture I/O handle %d", | 
 | 330 |                                                                   patch->sinks[0].ext.mix.handle); | 
 | 331 |                         status = BAD_VALUE; | 
 | 332 |                         break; | 
 | 333 |                     } | 
 | 334 |                     status = thread->sendReleaseAudioPatchConfigEvent(mPatches[index]->mHalHandle); | 
 | 335 |                 } else { | 
 | 336 |                     audio_hw_device_t *hwDevice = audioHwDevice->hwDevice(); | 
 | 337 |                     status = hwDevice->release_audio_patch(hwDevice, mPatches[index]->mHalHandle); | 
 | 338 |                 } | 
 | 339 |             } else { | 
 | 340 |                 sp<ThreadBase> thread = audioflinger->checkRecordThread_l( | 
 | 341 |                                                                     patch->sinks[0].ext.mix.handle); | 
 | 342 |                 if (thread == 0) { | 
 | 343 |                     ALOGW("releaseAudioPatch() bad capture I/O handle %d", | 
 | 344 |                                                                   patch->sinks[0].ext.mix.handle); | 
 | 345 |                     status = BAD_VALUE; | 
 | 346 |                     break; | 
 | 347 |                 } | 
 | 348 |                 AudioParameter param; | 
 | 349 |                 param.addInt(String8(AudioParameter::keyRouting), 0); | 
 | 350 |                 ALOGW("releaseAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s", | 
 | 351 |                                                                       param.toString().string()); | 
 | 352 |                 status = thread->setParameters(param.toString()); | 
 | 353 |             } | 
 | 354 |         } break; | 
 | 355 |         case AUDIO_PORT_TYPE_MIX: { | 
 | 356 |             audio_module_handle_t src_module =  patch->sources[0].ext.mix.hw_module; | 
 | 357 |             ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module); | 
 | 358 |             if (index < 0) { | 
 | 359 |                 ALOGW("releaseAudioPatch() bad src hw module %d", src_module); | 
 | 360 |                 status = BAD_VALUE; | 
 | 361 |                 break; | 
 | 362 |             } | 
 | 363 |             sp<ThreadBase> thread = | 
 | 364 |                             audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle); | 
 | 365 |             if (thread == 0) { | 
 | 366 |                 ALOGW("releaseAudioPatch() bad playback I/O handle %d", | 
 | 367 |                                                               patch->sources[0].ext.mix.handle); | 
 | 368 |                 status = BAD_VALUE; | 
 | 369 |                 break; | 
 | 370 |             } | 
 | 371 |             AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); | 
 | 372 |             if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { | 
 | 373 |                 status = thread->sendReleaseAudioPatchConfigEvent(mPatches[index]->mHalHandle); | 
 | 374 |             } else { | 
 | 375 |                 AudioParameter param; | 
 | 376 |                 param.addInt(String8(AudioParameter::keyRouting), (int)0); | 
 | 377 |                 status = thread->setParameters(param.toString()); | 
 | 378 |             } | 
 | 379 |         } break; | 
 | 380 |         default: | 
 | 381 |             status = BAD_VALUE; | 
 | 382 |             break; | 
 | 383 |     } | 
 | 384 |  | 
 | 385 |     delete (mPatches[index]); | 
 | 386 |     mPatches.removeAt(index); | 
 | 387 |     return status; | 
 | 388 | } | 
 | 389 |  | 
 | 390 |  | 
 | 391 | /* List connected audio ports and they attributes */ | 
 | 392 | status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __unused, | 
 | 393 |                                   struct audio_patch *patches __unused) | 
 | 394 | { | 
 | 395 |     ALOGV("listAudioPatches"); | 
 | 396 |     return NO_ERROR; | 
 | 397 | } | 
 | 398 |  | 
 | 399 | /* Set audio port configuration */ | 
 | 400 | status_t AudioFlinger::PatchPanel::setAudioPortConfig( | 
 | 401 |         const struct audio_port_config *config __unused) | 
 | 402 | { | 
 | 403 |     ALOGV("setAudioPortConfig"); | 
 | 404 |     return NO_ERROR; | 
 | 405 | } | 
 | 406 |  | 
 | 407 |  | 
 | 408 |  | 
 | 409 | }; // namespace android |