Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ecc03733 authored by Eric Laurent's avatar Eric Laurent Committed by Android (Google) Code Review
Browse files

Merge "audioflinger: first patch panel implementation."

parents 5e497806 1c333e25
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ LOCAL_SRC_FILES:= \
    Tracks.cpp                  \
    Effects.cpp                 \
    AudioMixer.cpp.arm          \
    PatchPanel.cpp

LOCAL_SRC_FILES += StateQueue.cpp

+3 −0
Original line number Diff line number Diff line
@@ -177,6 +177,7 @@ AudioFlinger::AudioFlinger()
    if (doLog) {
        mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters", MemoryHeapBase::READ_ONLY);
    }

#ifdef TEE_SINK
    (void) property_get("ro.debuggable", value, "0");
    int debuggable = atoi(value);
@@ -218,6 +219,8 @@ void AudioFlinger::onFirstRef()
        }
    }

    mPatchPanel = new PatchPanel(this);

    mMode = AUDIO_MODE_NORMAL;
}

+13 −24
Original line number Diff line number Diff line
@@ -225,41 +225,24 @@ public:

    /* List available audio ports and their attributes */
    virtual status_t listAudioPorts(unsigned int *num_ports,
                                    struct audio_port *ports)
    {
        return INVALID_OPERATION;
    }
                                    struct audio_port *ports);

    /* Get attributes for a given audio port */
    virtual status_t getAudioPort(struct audio_port *port)
    {
        return INVALID_OPERATION;
    }
    virtual status_t getAudioPort(struct audio_port *port);

    /* Create an audio patch between several source and sink ports */
    virtual status_t createAudioPatch(const struct audio_patch *patch,
                                       audio_patch_handle_t *handle)
    {
        return INVALID_OPERATION;
    }
                                       audio_patch_handle_t *handle);

    /* Release an audio patch */
    virtual status_t releaseAudioPatch(audio_patch_handle_t handle)
    {
        return INVALID_OPERATION;
    }
    virtual status_t releaseAudioPatch(audio_patch_handle_t handle);

    /* List existing audio patches */
    virtual status_t listAudioPatches(unsigned int *num_patches,
                                      struct audio_patch *patches)
    {
        return INVALID_OPERATION;
    }
                                      struct audio_patch *patches);

    /* Set audio port configuration */
    virtual status_t setAudioPortConfig(const struct audio_port_config *config)
    {
        return INVALID_OPERATION;
    }
    virtual status_t setAudioPortConfig(const struct audio_port_config *config);

    virtual     status_t    onTransact(
                                uint32_t code,
@@ -435,6 +418,8 @@ private:

#include "Effects.h"

#include "PatchPanel.h"

    // server side of the client's IAudioTrack
    class TrackHandle : public android::BnAudioTrack {
    public:
@@ -542,6 +527,8 @@ private:

        const char *moduleName() const { return mModuleName; }
        audio_hw_device_t *hwDevice() const { return mHwDevice; }
        uint32_t version() const { return mHwDevice->common.version; }

    private:
        const char * const mModuleName;
        audio_hw_device_t * const mHwDevice;
@@ -702,6 +689,8 @@ private:
    bool    mIsLowRamDevice;
    bool    mIsDeviceTypeKnown;
    nsecs_t mGlobalEffectEnableTime;  // when a global effect was last enabled

    sp<PatchPanel> mPatchPanel;
};

#undef INCLUDING_FROM_AUDIOFLINGER_H
+409 −0
Original line number Diff line number Diff line
/*
**
** Copyright 2014, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/


#define LOG_TAG "AudioFlinger::PatchPanel"
//#define LOG_NDEBUG 0

#include "Configuration.h"
#include <utils/Log.h>
#include <audio_utils/primitives.h>

#include "AudioFlinger.h"
#include "ServiceUtilities.h"
#include <media/AudioParameter.h>

// ----------------------------------------------------------------------------

// Note: the following macro is used for extremely verbose logging message.  In
// order to run with ALOG_ASSERT turned on, we need to have LOG_NDEBUG set to
// 0; but one side effect of this is to turn all LOGV's as well.  Some messages
// are so verbose that we want to suppress them even when we have ALOG_ASSERT
// turned on.  Do not uncomment the #def below unless you really know what you
// are doing and want to see all of the extremely verbose messages.
//#define VERY_VERY_VERBOSE_LOGGING
#ifdef VERY_VERY_VERBOSE_LOGGING
#define ALOGVV ALOGV
#else
#define ALOGVV(a...) do { } while(0)
#endif

namespace android {

/* List connected audio ports and their attributes */
status_t AudioFlinger::listAudioPorts(unsigned int *num_ports,
                                struct audio_port *ports)
{
    Mutex::Autolock _l(mLock);
    if (mPatchPanel != 0) {
        return mPatchPanel->listAudioPorts(num_ports, ports);
    }
    return NO_INIT;
}

/* Get supported attributes for a given audio port */
status_t AudioFlinger::getAudioPort(struct audio_port *port)
{
    Mutex::Autolock _l(mLock);
    if (mPatchPanel != 0) {
        return mPatchPanel->getAudioPort(port);
    }
    return NO_INIT;
}


/* Connect a patch between several source and sink ports */
status_t AudioFlinger::createAudioPatch(const struct audio_patch *patch,
                                   audio_patch_handle_t *handle)
{
    Mutex::Autolock _l(mLock);
    if (mPatchPanel != 0) {
        return mPatchPanel->createAudioPatch(patch, handle);
    }
    return NO_INIT;
}

/* Disconnect a patch */
status_t AudioFlinger::releaseAudioPatch(audio_patch_handle_t handle)
{
    Mutex::Autolock _l(mLock);
    if (mPatchPanel != 0) {
        return mPatchPanel->releaseAudioPatch(handle);
    }
    return NO_INIT;
}


/* List connected audio ports and they attributes */
status_t AudioFlinger::listAudioPatches(unsigned int *num_patches,
                                  struct audio_patch *patches)
{
    Mutex::Autolock _l(mLock);
    if (mPatchPanel != 0) {
        return mPatchPanel->listAudioPatches(num_patches, patches);
    }
    return NO_INIT;
}

/* Set audio port configuration */
status_t AudioFlinger::setAudioPortConfig(const struct audio_port_config *config)
{
    Mutex::Autolock _l(mLock);
    if (mPatchPanel != 0) {
        return mPatchPanel->setAudioPortConfig(config);
    }
    return NO_INIT;
}


AudioFlinger::PatchPanel::PatchPanel(const sp<AudioFlinger>& audioFlinger)
                                   : mAudioFlinger(audioFlinger)
{
}

AudioFlinger::PatchPanel::~PatchPanel()
{
}

/* List connected audio ports and their attributes */
status_t AudioFlinger::PatchPanel::listAudioPorts(unsigned int *num_ports __unused,
                                struct audio_port *ports __unused)
{
    ALOGV("listAudioPorts");
    return NO_ERROR;
}

/* Get supported attributes for a given audio port */
status_t AudioFlinger::PatchPanel::getAudioPort(struct audio_port *port __unused)
{
    ALOGV("getAudioPort");
    return NO_ERROR;
}


/* Connect a patch between several source and sink ports */
status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *patch,
                                   audio_patch_handle_t *handle)
{
    ALOGV("createAudioPatch() num_sources %d num_sinks %d handle %d",
          patch->num_sources, patch->num_sinks, *handle);
    status_t status = NO_ERROR;

    audio_patch_handle_t halHandle = AUDIO_PATCH_HANDLE_NONE;

    sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
    if (audioflinger == 0) {
        return NO_INIT;
    }
    if (handle == NULL || patch == NULL) {
        return BAD_VALUE;
    }
    // limit number of sources to 1 for now
    if (patch->num_sources == 0 || patch->num_sources > 1 ||
            patch->num_sinks == 0 || patch->num_sinks > AUDIO_PATCH_PORTS_MAX) {
        return BAD_VALUE;
    }

    for (size_t index = 0; *handle != 0 && index < mPatches.size(); index++) {
        if (*handle == mPatches[index]->mHandle) {
            ALOGV("createAudioPatch() removing patch handle %d", *handle);
            halHandle = mPatches[index]->mHalHandle;
            mPatches.removeAt(index);
            break;
        }
    }

    switch (patch->sources[0].type) {
        case AUDIO_PORT_TYPE_DEVICE: {
            // limit number of sinks to 1 for now
            if (patch->num_sinks > 1) {
                return BAD_VALUE;
            }
            audio_module_handle_t src_module = patch->sources[0].ext.device.hw_module;
            ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module);
            if (index < 0) {
                ALOGW("createAudioPatch() bad src hw module %d", src_module);
                return BAD_VALUE;
            }
            for (unsigned int i = 0; i < patch->num_sinks; i++) {
                // limit to connections between devices and output streams
                if (patch->sinks[i].type != AUDIO_PORT_TYPE_MIX) {
                    ALOGW("createAudioPatch() invalid sink type %d for device source",
                          patch->sinks[i].type);
                    return BAD_VALUE;
                }
                // limit to connections between sinks and sources on same HW module
                if (patch->sinks[i].ext.mix.hw_module != src_module) {
                    ALOGW("createAudioPatch() cannot connect source on module %d to"
                            "sink on module %d", src_module, patch->sinks[i].ext.mix.hw_module);
                    return BAD_VALUE;
                }
            }

            AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
            if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
                if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
                    sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
                                                                    patch->sinks[0].ext.mix.handle);
                    if (thread == 0) {
                        ALOGW("createAudioPatch() bad capture I/O handle %d",
                                                                  patch->sinks[0].ext.mix.handle);
                        return BAD_VALUE;
                    }
                    status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
                } else {
                    audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
                    status = hwDevice->create_audio_patch(hwDevice,
                                                           patch->num_sources,
                                                           patch->sources,
                                                           patch->num_sinks,
                                                           patch->sinks,
                                                           &halHandle);
                }
            } else {
                sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
                                                                    patch->sinks[0].ext.mix.handle);
                if (thread == 0) {
                    ALOGW("createAudioPatch() bad capture I/O handle %d",
                                                                  patch->sinks[0].ext.mix.handle);
                    return BAD_VALUE;
                }
                AudioParameter param;
                param.addInt(String8(AudioParameter::keyRouting),
                             (int)patch->sources[0].ext.device.type);
                param.addInt(String8(AudioParameter::keyInputSource),
                                                     (int)patch->sinks[0].ext.mix.usecase.source);

                ALOGW("createAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s",
                                                                      param.toString().string());
                status = thread->setParameters(param.toString());
            }
        } break;
        case AUDIO_PORT_TYPE_MIX: {
            audio_module_handle_t src_module =  patch->sources[0].ext.mix.hw_module;
            ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module);
            if (index < 0) {
                ALOGW("createAudioPatch() bad src hw module %d", src_module);
                return BAD_VALUE;
            }
            // limit to connections between devices and output streams
            for (unsigned int i = 0; i < patch->num_sinks; i++) {
                if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
                    ALOGW("createAudioPatch() invalid sink type %d for bus source",
                          patch->sinks[i].type);
                    return BAD_VALUE;
                }
                // limit to connections between sinks and sources on same HW module
                if (patch->sinks[i].ext.device.hw_module != src_module) {
                    return BAD_VALUE;
                }
            }
            AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
            sp<ThreadBase> thread =
                            audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle);
            if (thread == 0) {
                ALOGW("createAudioPatch() bad playback I/O handle %d",
                          patch->sources[0].ext.mix.handle);
                return BAD_VALUE;
            }
            if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
                status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
            } else {
                audio_devices_t type = AUDIO_DEVICE_NONE;
                for (unsigned int i = 0; i < patch->num_sinks; i++) {
                    type |= patch->sinks[i].ext.device.type;
                }
                AudioParameter param;
                param.addInt(String8(AudioParameter::keyRouting), (int)type);
                status = thread->setParameters(param.toString());
            }

        } break;
        default:
            return BAD_VALUE;
    }
    ALOGV("createAudioPatch() status %d", status);
    if (status == NO_ERROR) {
        *handle = audioflinger->nextUniqueId();
        Patch *newPatch = new Patch(patch);
        newPatch->mHandle = *handle;
        newPatch->mHalHandle = halHandle;
        mPatches.add(newPatch);
        ALOGV("createAudioPatch() added new patch handle %d halHandle %d", *handle, halHandle);
    }
    return status;
}

/* Disconnect a patch */
status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle)
{
    ALOGV("releaseAudioPatch handle %d", handle);
    status_t status = NO_ERROR;
    size_t index;

    sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
    if (audioflinger == 0) {
        return NO_INIT;
    }

    for (index = 0; index < mPatches.size(); index++) {
        if (handle == mPatches[index]->mHandle) {
            break;
        }
    }
    if (index == mPatches.size()) {
        return BAD_VALUE;
    }

    struct audio_patch *patch = &mPatches[index]->mAudioPatch;

    switch (patch->sources[0].type) {
        case AUDIO_PORT_TYPE_DEVICE: {
            audio_module_handle_t src_module = patch->sources[0].ext.device.hw_module;
            ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module);
            if (index < 0) {
                ALOGW("releaseAudioPatch() bad src hw module %d", src_module);
                status = BAD_VALUE;
                break;
            }
            AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
            if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
                if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
                    sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
                                                                    patch->sinks[0].ext.mix.handle);
                    if (thread == 0) {
                        ALOGW("createAudioPatch() bad capture I/O handle %d",
                                                                  patch->sinks[0].ext.mix.handle);
                        status = BAD_VALUE;
                        break;
                    }
                    status = thread->sendReleaseAudioPatchConfigEvent(mPatches[index]->mHalHandle);
                } else {
                    audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
                    status = hwDevice->release_audio_patch(hwDevice, mPatches[index]->mHalHandle);
                }
            } else {
                sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
                                                                    patch->sinks[0].ext.mix.handle);
                if (thread == 0) {
                    ALOGW("releaseAudioPatch() bad capture I/O handle %d",
                                                                  patch->sinks[0].ext.mix.handle);
                    status = BAD_VALUE;
                    break;
                }
                AudioParameter param;
                param.addInt(String8(AudioParameter::keyRouting), 0);
                ALOGW("releaseAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s",
                                                                      param.toString().string());
                status = thread->setParameters(param.toString());
            }
        } break;
        case AUDIO_PORT_TYPE_MIX: {
            audio_module_handle_t src_module =  patch->sources[0].ext.mix.hw_module;
            ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module);
            if (index < 0) {
                ALOGW("releaseAudioPatch() bad src hw module %d", src_module);
                status = BAD_VALUE;
                break;
            }
            sp<ThreadBase> thread =
                            audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle);
            if (thread == 0) {
                ALOGW("releaseAudioPatch() bad playback I/O handle %d",
                                                              patch->sources[0].ext.mix.handle);
                status = BAD_VALUE;
                break;
            }
            AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
            if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
                status = thread->sendReleaseAudioPatchConfigEvent(mPatches[index]->mHalHandle);
            } else {
                AudioParameter param;
                param.addInt(String8(AudioParameter::keyRouting), (int)0);
                status = thread->setParameters(param.toString());
            }
        } break;
        default:
            status = BAD_VALUE;
            break;
    }

    delete (mPatches[index]);
    mPatches.removeAt(index);
    return status;
}


/* List connected audio ports and they attributes */
status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __unused,
                                  struct audio_patch *patches __unused)
{
    ALOGV("listAudioPatches");
    return NO_ERROR;
}

/* Set audio port configuration */
status_t AudioFlinger::PatchPanel::setAudioPortConfig(
        const struct audio_port_config *config __unused)
{
    ALOGV("setAudioPortConfig");
    return NO_ERROR;
}



}; // namespace android
+60 −0
Original line number Diff line number Diff line
/*
**
** Copyright 2014, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#ifndef INCLUDING_FROM_AUDIOFLINGER_H
    #error This header file should only be included from AudioFlinger.h
#endif

class PatchPanel : public RefBase {
public:
    PatchPanel(const sp<AudioFlinger>& audioFlinger);
    virtual ~PatchPanel();

    /* List connected audio ports and their attributes */
    status_t listAudioPorts(unsigned int *num_ports,
                                    struct audio_port *ports);

    /* Get supported attributes for a given audio port */
    status_t getAudioPort(struct audio_port *port);

    /* Create a patch between several source and sink ports */
    status_t createAudioPatch(const struct audio_patch *patch,
                                       audio_patch_handle_t *handle);

    /* Release a patch */
    status_t releaseAudioPatch(audio_patch_handle_t handle);

    /* List connected audio devices and they attributes */
    status_t listAudioPatches(unsigned int *num_patches,
                                      struct audio_patch *patches);

    /* Set audio port configuration */
    status_t setAudioPortConfig(const struct audio_port_config *config);

    class Patch {
    public:
        Patch(const struct audio_patch *patch) :
            mAudioPatch(*patch), mHandle(0), mHalHandle(0) {}

        struct audio_patch mAudioPatch;
        audio_patch_handle_t mHandle;
        audio_patch_handle_t mHalHandle;
    };
private:
    const wp<AudioFlinger>  mAudioFlinger;
    SortedVector <Patch *> mPatches;
};
Loading