/*
* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above
*       copyright notice, this list of conditions and the following
*       disclaimer in the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of The Linux Foundation nor the names of its
*       contributors may be used to endorse or promote products derived
*       from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#define LOG_TAG "qahw"
/*#define LOG_NDEBUG 0*/
#define LOG_NDDEBUG 0

#include <dlfcn.h>
#include <utils/Log.h>
#include <stdlib.h>
#include <cutils/list.h>

#include <hardware/audio.h>
#include "qahw_api.h"

#define NO_ERROR 0
#define MAX_MODULE_NAME_LENGTH  100

/*
 * The current HAL API version.
 */
#define QAHW_MODULE_API_VERSION_CURRENT QAHW_MODULE_API_VERSION_0_0


typedef uint64_t (*qahwi_out_write_v2_t)(audio_stream_out_t *out, const void* buffer,
                                       size_t bytes, int64_t* timestamp);

typedef int (*qahwi_get_param_data_t) (const audio_hw_device_t *,
                              qahw_param_id, qahw_param_payload *);

typedef int (*qahwi_set_param_data_t) (audio_hw_device_t *,
                              qahw_param_id, qahw_param_payload *);

typedef uint64_t (*qahwi_in_read_v2_t)(audio_stream_in_t *in, void* buffer,
                                       size_t bytes, int64_t *timestamp);

typedef int (*qahwi_out_set_param_data_t)(struct audio_stream_out *out,
                                      qahw_param_id param_id,
                                      qahw_param_payload *payload);

typedef int (*qahwi_out_get_param_data_t)(struct audio_stream_out *out,
                                      qahw_param_id param_id,
                                      qahw_param_payload *payload);

typedef struct {
    audio_hw_device_t *audio_device;
    char module_name[MAX_MODULE_NAME_LENGTH];
    struct listnode module_list;
    struct listnode in_list;
    struct listnode out_list;
    pthread_mutex_t lock;
    uint32_t ref_count;
    const hw_module_t* module;
    qahwi_get_param_data_t qahwi_get_param_data;
    qahwi_set_param_data_t qahwi_set_param_data;
} qahw_module_t;

typedef struct {
    qahw_module_t *module;
    struct listnode module_list;
    pthread_mutex_t lock;
} qahw_module_instances_t;

typedef struct {
    audio_stream_out_t *stream;
    qahw_module_t *module;
    struct listnode list;
    pthread_mutex_t lock;
    qahwi_out_set_param_data_t qahwi_out_get_param_data;
    qahwi_out_get_param_data_t qahwi_out_set_param_data;
    qahwi_out_write_v2_t qahwi_out_write_v2;
} qahw_stream_out_t;

typedef struct {
    audio_stream_in_t *stream;
    qahw_module_t *module;
    struct listnode list;
    pthread_mutex_t lock;
    qahwi_in_read_v2_t qahwi_in_read_v2;
} qahw_stream_in_t;

typedef enum {
    STREAM_DIR_IN,
    STREAM_DIR_OUT,
} qahw_stream_direction_t;

static struct listnode qahw_module_list;
static int qahw_list_count;
static pthread_mutex_t qahw_module_init_lock = PTHREAD_MUTEX_INITIALIZER;


/** Start of internal functions */
/******************************************************************************/

/* call this function without anylock held */
static bool is_valid_qahw_stream(void *qahw_stream,
                                 qahw_stream_direction_t dir)
{

    int is_valid = false;
    struct listnode *module_node = NULL;
    struct listnode *stream_node = NULL;
    struct listnode *list_node = NULL;
    void  *stream = NULL;
    qahw_module_t *qahw_module = NULL;

    if (qahw_stream == NULL) {
        ALOGE("%s:: Invalid stream", __func__);
        goto exit;
    }

    if ((dir != STREAM_DIR_OUT) && (dir != STREAM_DIR_IN)) {
        ALOGE("%s:: Invalid stream direction %d", __func__, dir);
        goto exit;
    }

    /* go through all the modules and check for valid stream */
    pthread_mutex_lock(&qahw_module_init_lock);
    list_for_each(module_node, &qahw_module_list) {
        qahw_module = node_to_item(module_node, qahw_module_t, module_list);
        pthread_mutex_lock(&qahw_module->lock);
        if(dir == STREAM_DIR_OUT)
            list_node = &qahw_module->out_list;
        else
            list_node = &qahw_module->in_list;
        list_for_each(stream_node, list_node) {
            if(dir == STREAM_DIR_OUT)
                stream = (void *)node_to_item(stream_node,
                                              qahw_stream_out_t,
                                              list);
            else
                stream = (void *)node_to_item(stream_node,
                                              qahw_stream_in_t,
                                              list);
            if(stream == qahw_stream) {
                is_valid = true;
                break;
            }
        }
        pthread_mutex_unlock(&qahw_module->lock);
        if(is_valid)
            break;
    }
    pthread_mutex_unlock(&qahw_module_init_lock);

exit:
    return is_valid;
}

/* call this fucntion with ahw_module_init_lock held*/
static qahw_module_t* get_qahw_module_by_ptr(qahw_module_t *qahw_module)
{
    struct listnode *node = NULL;
    qahw_module_t *module = NULL, *module_temp = NULL;

    if (qahw_module == NULL)
        goto exit;

    list_for_each(node, &qahw_module_list) {
        module_temp = node_to_item(node, qahw_module_t, module_list);
        if (module_temp == qahw_module) {
            module = module_temp;
            break;
        }
    }
exit:
    return module;
}

/* call this function with qahw_module_init_lock held*/
static qahw_module_t* get_qahw_module_by_name(const char *qahw_name)
{
    struct listnode *node = NULL;
    qahw_module_t *module = NULL, *module_temp = NULL;

    if (qahw_name == NULL)
        goto exit;

    list_for_each(node, &qahw_module_list) {
        module_temp = node_to_item(node, qahw_module_t, module_list);
        if(!strncmp(qahw_name, module_temp->module_name, MAX_MODULE_NAME_LENGTH)) {
            module = module_temp;
            break;
        }
    }
exit:
    return module;
}
/* End of of internal functions */

/*
 * Return the sampling rate in Hz - eg. 44100.
 */
uint32_t qahw_out_get_sample_rate(const qahw_stream_handle_t *out_handle)
{
    uint32_t rate = 0;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGV("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (out->common.get_sample_rate)
        rate = out->common.get_sample_rate(&out->common);
    else
        ALOGW("%s not supported", __func__);
    pthread_mutex_unlock(&qahw_stream_out->lock);

exit:
     return rate;
}

/*
 * currently unused - use set_parameters with key
 *    AUDIO_PARAMETER_STREAM_SAMPLING_RATE
 */
int qahw_out_set_sample_rate(qahw_stream_handle_t *out_handle, uint32_t rate)
{
    int32_t rc = -EINVAL;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }
    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (out->common.set_sample_rate) {
        rc = out->common.set_sample_rate(&out->common, rate);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);
exit:
    return rc;
}

size_t qahw_out_get_buffer_size(const qahw_stream_handle_t *out_handle)
{
    size_t buf_size = 0;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }
    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (out->common.get_buffer_size) {
        buf_size = out->common.get_buffer_size(&out->common);
    } else {
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);

exit:
    return buf_size;
}

audio_channel_mask_t qahw_out_get_channels(const qahw_stream_handle_t *out_handle)
{
    audio_channel_mask_t ch_mask = 0;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }
    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (out->common.get_channels) {
        ch_mask = out->common.get_channels(&out->common);
    } else {
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);

exit:
    return ch_mask;
}

audio_format_t qahw_out_get_format(const qahw_stream_handle_t *out_handle)
{
    audio_format_t format = AUDIO_FORMAT_INVALID;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }
    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (out->common.get_format) {
        format = out->common.get_format(&out->common);
    } else {
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);

exit:
    return format;
}

int qahw_out_standby(qahw_stream_handle_t *out_handle)
{
    int32_t rc = -EINVAL;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (out->common.standby) {
        rc = out->common.standby(&out->common);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);

exit:
    return rc;
}

int qahw_out_set_parameters(qahw_stream_handle_t *out_handle, const char *kv_pairs)
{
    int rc = NO_ERROR;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        rc = -EINVAL;
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (out->common.set_parameters) {
        rc = out->common.set_parameters(&out->common, kv_pairs);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);

exit:
    return rc;
}

char *qahw_out_get_parameters(const qahw_stream_handle_t *out_handle,
                               const char *keys)
{
    char *str_param = NULL;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (out->common.get_parameters) {
        str_param = out->common.get_parameters(&out->common, keys);
    } else {
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);

exit:
    return str_param;
}

/* API to get playback stream specific config parameters */
int qahw_out_set_param_data(qahw_stream_handle_t *out_handle,
                            qahw_param_id param_id,
                            qahw_param_payload *payload)
{
    int rc = -EINVAL;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!payload) {
        ALOGE("%s::Invalid param", __func__);
        goto exit;
    }

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (qahw_stream_out->qahwi_out_set_param_data) {
        rc = qahw_stream_out->qahwi_out_set_param_data(out, param_id, payload);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);

exit:
    return rc;
}

/* API to get playback stream specific config parameters */
int qahw_out_get_param_data(qahw_stream_handle_t *out_handle,
                            qahw_param_id param_id,
                            qahw_param_payload *payload)
{
    int rc = -EINVAL;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (qahw_stream_out->qahwi_out_get_param_data) {
        rc = qahw_stream_out->qahwi_out_get_param_data(out, param_id, payload);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);

exit:
    return rc;
}

uint32_t qahw_out_get_latency(const qahw_stream_handle_t *out_handle)
{
    uint32_t latency = 0;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (out->get_latency) {
        latency = out->get_latency(out);
    } else {
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);

exit:
    return latency;
}

int qahw_out_set_volume(qahw_stream_handle_t *out_handle, float left, float right)
{
    int rc = -EINVAL;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (out->set_volume) {
        rc = out->set_volume(out, left, right);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);

exit:
   return rc;
}

ssize_t qahw_out_write(qahw_stream_handle_t *out_handle,
        qahw_out_buffer_t *out_buf)
{
    int rc = -EINVAL;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if ((out_buf == NULL) || (out_buf->buffer == NULL)) {
        ALOGE("%s::Invalid meta data %p", __func__, out_buf);
        goto exit;
    }

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }

    /*TBD:: validate other meta data parameters */
    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (qahw_stream_out->qahwi_out_write_v2) {
        rc = qahw_stream_out->qahwi_out_write_v2(out, out_buf->buffer,
                                         out_buf->bytes, out_buf->timestamp);
        out_buf->offset = 0;
    } else if (out->write) {
        rc = out->write(out, out_buf->buffer, out_buf->bytes);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);
exit:
    return rc;
}

int qahw_out_get_render_position(const qahw_stream_handle_t *out_handle,
                                 uint32_t *dsp_frames)
{
    int rc = -EINVAL;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (out->get_render_position) {
        rc = out->get_render_position(out, dsp_frames);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);
exit:
    return rc;
}

int qahw_out_set_callback(qahw_stream_handle_t *out_handle,
                          qahw_stream_callback_t callback,
                          void *cookie)
{
    /*TBD:load hal func pointer and call */
    int rc = -EINVAL;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (out->set_callback) {
        rc = out->set_callback(out, (stream_callback_t)callback, cookie);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);

exit:
    return rc;
}

int qahw_out_pause(qahw_stream_handle_t *out_handle)
{
    /*TBD:load hal func pointer and call */
    int rc = -EINVAL;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (out->pause) {
        rc = out->pause(out);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);

exit:
    return rc;
}

int qahw_out_resume(qahw_stream_handle_t *out_handle)
{
    /*TBD:load hal func pointer and call */
    int rc = -EINVAL;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (out->resume) {
        rc = out->resume(out);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);

exit:
    return rc;
}

int qahw_out_drain(qahw_stream_handle_t *out_handle, qahw_drain_type_t type )
{
    /*TBD:load hal func pointer and call */
    int rc = -EINVAL;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (out->drain) {
        rc = out->drain(out,(audio_drain_type_t)type);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);

exit:
    return rc;
}

int qahw_out_flush(qahw_stream_handle_t *out_handle)
{
    int rc = -EINVAL;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (out->flush) {
        rc = out->flush(out);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);

exit:
    return rc;
}

int qahw_out_get_presentation_position(const qahw_stream_handle_t *out_handle,
                           uint64_t *frames, struct timespec *timestamp)
{
    int rc = -EINVAL;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    audio_stream_out_t *out = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_out->lock);
    out = qahw_stream_out->stream;
    if (out->get_presentation_position) {
        rc = out->get_presentation_position(out, frames, timestamp);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_out->lock);

exit:
    return rc;
}

/* Input stream specific APIs */
uint32_t qahw_in_get_sample_rate(const qahw_stream_handle_t *in_handle)
{
    uint32_t rate = 0;
    qahw_stream_in_t *qahw_stream_in = (qahw_stream_in_t *)in_handle;
    audio_stream_in_t *in = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_in, STREAM_DIR_IN)) {
        ALOGV("%s::Invalid in handle %p", __func__, in_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_in->lock);
    in = qahw_stream_in->stream;
    if (in->common.get_sample_rate) {
        rate = in->common.get_sample_rate(&in->common);
    } else {
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_in->lock);

exit:
    return rate;
}

/*
 * currently unused - use set_parameters with key
 *    AUDIO_PARAMETER_STREAM_SAMPLING_RATE
 */
int qahw_in_set_sample_rate(qahw_stream_handle_t *in_handle, uint32_t rate)
{
    int rc = -EINVAL;
    qahw_stream_in_t *qahw_stream_in = (qahw_stream_in_t *)in_handle;
    audio_stream_in_t *in = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_in, STREAM_DIR_IN)) {
        ALOGV("%s::Invalid in handle %p", __func__, in_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_in->lock);
    in = qahw_stream_in->stream;
    if (in->common.set_sample_rate) {
        rc = in->common.set_sample_rate(&in->common, rate);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_in->lock);

exit:
    return rc;
}

size_t qahw_in_get_buffer_size(const qahw_stream_handle_t *in_handle)
{
    size_t buf_size = 0;
    qahw_stream_in_t *qahw_stream_in = (qahw_stream_in_t *)in_handle;
    audio_stream_in_t *in = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_in, STREAM_DIR_IN)) {
        ALOGV("%s::Invalid in handle %p", __func__, in_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_in->lock);
    in = qahw_stream_in->stream;
    if (in->common.get_sample_rate) {
        buf_size = in->common.get_buffer_size(&in->common);
    } else {
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_in->lock);

exit:
    return buf_size;
}


audio_channel_mask_t qahw_in_get_channels(const qahw_stream_handle_t *in_handle)
{
    audio_channel_mask_t ch_mask = 0;;
    qahw_stream_in_t *qahw_stream_in = (qahw_stream_in_t *)in_handle;
    audio_stream_in_t *in = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_in, STREAM_DIR_IN)) {
        ALOGV("%s::Invalid in handle %p", __func__, in_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_in->lock);
    in = qahw_stream_in->stream;
    if (in->common.get_channels) {
        ch_mask = in->common.get_channels(&in->common);
    } else {
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_in->lock);

exit:
    return ch_mask;
}

audio_format_t qahw_in_get_format(const qahw_stream_handle_t *in_handle)
{
    audio_format_t format = AUDIO_FORMAT_INVALID;
    qahw_stream_in_t *qahw_stream_in = (qahw_stream_in_t *)in_handle;
    audio_stream_in_t *in = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_in, STREAM_DIR_IN)) {
        ALOGV("%s::Invalid in handle %p", __func__, in_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_in->lock);
    in = qahw_stream_in->stream;
    if (in->common.get_format) {
        format = in->common.get_format(&in->common);
    } else {
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_in->lock);

exit:
    return format;
}

/*
 * currently unused - use set_parameters with key
 *     AUDIO_PARAMETER_STREAM_FORMAT
 */
int qahw_in_set_format(qahw_stream_handle_t *in_handle, audio_format_t format)
{
    int rc = -EINVAL;
    qahw_stream_in_t *qahw_stream_in = (qahw_stream_in_t *)in_handle;
    audio_stream_in_t *in = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_in, STREAM_DIR_IN)) {
        ALOGV("%s::Invalid in handle %p", __func__, in_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_in->lock);
    in = qahw_stream_in->stream;
    if (in->common.set_format) {
        rc = in->common.set_format(&in->common, format);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_in->lock);

exit:
    return rc;
}

int qahw_in_standby(qahw_stream_handle_t *in_handle)
{
    int rc = -EINVAL;
    qahw_stream_in_t *qahw_stream_in = (qahw_stream_in_t *)in_handle;
    audio_stream_in_t *in = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_in, STREAM_DIR_IN)) {
        ALOGV("%s::Invalid in handle %p", __func__, in_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_in->lock);
    in = qahw_stream_in->stream;
    if (in->common.standby) {
        rc = in->common.standby(&in->common);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_in->lock);

exit:
    return rc;
}

/*
 * set/get audio stream parameters. The function accepts a list of
 * parameter key value pairs in the form: key1=value1;key2=value2;...
 *
 * Some keys are reserved for standard parameters (See AudioParameter class)
 *
 * If the implementation does not accept a parameter change while
 * the output is active but the parameter is acceptable otherwise, it must
 * return -ENOSYS.
 *
 * The audio flinger will put the stream in standby and then change the
 * parameter value.
 */
int qahw_in_set_parameters(qahw_stream_handle_t *in_handle, const char *kv_pairs)
{
    int rc = -EINVAL;
    qahw_stream_in_t *qahw_stream_in = (qahw_stream_in_t *)in_handle;
    audio_stream_in_t *in = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_in, STREAM_DIR_IN)) {
        ALOGV("%s::Invalid in handle %p", __func__, in_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_in->lock);
    in = qahw_stream_in->stream;
    if (in->common.set_parameters) {
        rc = in->common.set_parameters(&in->common, kv_pairs);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_in->lock);
exit:
    return rc;
}

/*
 * Returns a pointer to a heap allocated string. The caller is responsible
 * for freeing the memory for it using free().
 */
char * qahw_in_get_parameters(const qahw_stream_handle_t *in_handle,
                              const char *keys)
{
    char *str_param = NULL;
    qahw_stream_in_t *qahw_stream_in = (qahw_stream_in_t *)in_handle;
    audio_stream_in_t *in = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_in, STREAM_DIR_IN)) {
        ALOGV("%s::Invalid in handle %p", __func__, in_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_in->lock);
    in = qahw_stream_in->stream;
    if (in->common.get_parameters) {
        str_param = in->common.get_parameters(&in->common, keys);
    } else {
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_in->lock);

exit:
    return str_param;
}

/*
 * Read audio buffer in from audio driver. Returns number of bytes read, or a
 *  negative status_t. If at least one frame was read prior to the error,
 *  read should return that byte count and then return an error in the subsequent call.
 */
ssize_t qahw_in_read(qahw_stream_handle_t *in_handle,
                     qahw_in_buffer_t *in_buf)
{
    int rc = -EINVAL;
    qahw_stream_in_t *qahw_stream_in = (qahw_stream_in_t *)in_handle;
    audio_stream_in_t *in = NULL;

    if ((in_buf == NULL) || (in_buf->buffer == NULL)) {
        ALOGE("%s::Invalid meta data %p", __func__, in_buf);
        goto exit;
    }

    if (!is_valid_qahw_stream((void *)qahw_stream_in, STREAM_DIR_IN)) {
        ALOGV("%s::Invalid in handle %p", __func__, in_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_in->lock);
    in = qahw_stream_in->stream;
    if (qahw_stream_in->qahwi_in_read_v2) {
        rc = qahw_stream_in->qahwi_in_read_v2(in, in_buf->buffer,
                                         in_buf->bytes, in_buf->timestamp);
        in_buf->offset = 0;
    } else if (in->read) {
        rc = in->read(in, in_buf->buffer, in_buf->bytes);
        in_buf->offset = 0;
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_in->lock);

exit:
    return rc;
}

/*
 * Return the amount of input frames lost in the audio driver since the
 * last call of this function.
 * Audio driver is expected to reset the value to 0 and restart counting
 * upon returning the current value by this function call.
 * Such loss typically occurs when the user space process is blocked
 * longer than the capacity of audio driver buffers.
 *
 * Unit: the number of input audio frames
 */
uint32_t qahw_in_get_input_frames_lost(qahw_stream_handle_t *in_handle)
{
    uint32_t rc = 0;
    qahw_stream_in_t *qahw_stream_in = (qahw_stream_in_t *)in_handle;
    audio_stream_in_t *in = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_in, STREAM_DIR_IN)) {
        ALOGV("%s::Invalid in handle %p", __func__, in_handle);
        goto exit;
    }

    pthread_mutex_lock(&qahw_stream_in->lock);
    in = qahw_stream_in->stream;
    if (in->get_input_frames_lost) {
        rc = in->get_input_frames_lost(in);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_stream_in->lock);

exit:
    return rc;
}

/*
 * Return a recent count of the number of audio frames received and
 * the clock time associated with that frame count.
 *
 * frames is the total frame count received. This should be as early in
 *     the capture pipeline as possible. In general,
 *     frames should be non-negative and should not go "backwards".
 *
 * time is the clock MONOTONIC time when frames was measured. In general,
 *     time should be a positive quantity and should not go "backwards".
 *
 * The status returned is 0 on success, -ENOSYS if the device is not
 * ready/available, or -EINVAL if the arguments are null or otherwise invalid.
 */
int qahw_in_get_capture_position(const qahw_stream_handle_t *in_handle __unused,
                                 int64_t *frames __unused, int64_t *time __unused)
{
    /*TBD:: do we need this*/
    return -ENOSYS;
}

/*
 * check to see if the audio hardware interface has been initialized.
 * returns 0 on success, -ENODEV on failure.
 */
int qahw_init_check(const qahw_module_handle_t *hw_module)
{
    int rc = -EINVAL;
    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
    qahw_module_t *qahw_module_temp;

    pthread_mutex_lock(&qahw_module_init_lock);
    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
    pthread_mutex_unlock(&qahw_module_init_lock);
    if (qahw_module_temp == NULL) {
        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
        goto exit;
    }

    pthread_mutex_lock(&qahw_module->lock);
    if (qahw_module->audio_device->init_check) {
        rc = qahw_module->audio_device->init_check(qahw_module->audio_device);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_module->lock);

exit:
    return rc;
}
/* set the audio volume of a voice call. Range is between 0.0 and 1.0 */
int qahw_set_voice_volume(qahw_module_handle_t *hw_module, float volume)
{
    int rc = -EINVAL;
    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
    qahw_module_t *qahw_module_temp;

    pthread_mutex_lock(&qahw_module_init_lock);
    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
    pthread_mutex_unlock(&qahw_module_init_lock);
    if (qahw_module_temp == NULL) {
        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
        goto exit;
    }

    pthread_mutex_lock(&qahw_module->lock);
    if (qahw_module->audio_device->set_voice_volume) {
        rc = qahw_module->audio_device->set_voice_volume(qahw_module->audio_device,
                                                         volume);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_module->lock);

exit:
    return rc;
}

/*
 * set_mode is called when the audio mode changes. AUDIO_MODE_NORMAL mode
 * is for standard audio playback, AUDIO_MODE_RINGTONE when a ringtone is
 * playing, and AUDIO_MODE_IN_CALL when a call is in progress.
 */
int qahw_set_mode(qahw_module_handle_t *hw_module, audio_mode_t mode)
{
    int rc = -EINVAL;
    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
    qahw_module_t *qahw_module_temp;

    pthread_mutex_lock(&qahw_module_init_lock);
    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
    pthread_mutex_unlock(&qahw_module_init_lock);
    if (qahw_module_temp == NULL) {
        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
        goto exit;
    }

    pthread_mutex_lock(&qahw_module->lock);
    if (qahw_module->audio_device->set_mode) {
        rc = qahw_module->audio_device->set_mode(qahw_module->audio_device,
                                                 mode);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_module->lock);

exit:
    return rc;
}

int qahw_set_mic_mute(qahw_module_handle_t *hw_module, bool state)
{
    int rc = -EINVAL;
    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
    qahw_module_t *qahw_module_temp;

    pthread_mutex_lock(&qahw_module_init_lock);
    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
    pthread_mutex_unlock(&qahw_module_init_lock);
    if (qahw_module_temp == NULL) {
        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
        goto exit;
    }

    pthread_mutex_lock(&qahw_module->lock);
    if (qahw_module->audio_device->set_mic_mute) {
        rc = qahw_module->audio_device->set_mic_mute(qahw_module->audio_device,
                                                 state);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_module->lock);

exit:
    return rc;
}

int qahw_get_mic_mute(qahw_module_handle_t *hw_module, bool *state)
{
    size_t rc = 0;
    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
    qahw_module_t *qahw_module_temp;
    audio_hw_device_t *audio_device;

    pthread_mutex_lock(&qahw_module_init_lock);
    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
    pthread_mutex_unlock(&qahw_module_init_lock);
    if (qahw_module_temp == NULL) {
        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
        goto exit;
    }

    pthread_mutex_lock(&qahw_module->lock);
    audio_device = qahw_module->audio_device;
    if (qahw_module->audio_device->get_mic_mute) {
        rc = audio_device->get_mic_mute(qahw_module->audio_device, state);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_module->lock);

exit:
    return rc;
}

/* set/get global audio parameters */
int qahw_set_parameters(qahw_module_handle_t *hw_module, const char *kv_pairs)
{
    int rc = -EINVAL;
    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
    qahw_module_t *qahw_module_temp;
    audio_hw_device_t *audio_device;

    pthread_mutex_lock(&qahw_module_init_lock);
    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
    pthread_mutex_unlock(&qahw_module_init_lock);
    if (qahw_module_temp == NULL) {
        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
        goto exit;
    }

    pthread_mutex_lock(&qahw_module->lock);
    audio_device = qahw_module->audio_device;
    if (qahw_module->audio_device->set_parameters) {
        rc = audio_device->set_parameters(qahw_module->audio_device, kv_pairs);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_module->lock);

exit:
    return rc;
}

/*
 * Returns a pointer to a heap allocated string. The caller is responsible
 * for freeing the memory for it using free().
 */
char * qahw_get_parameters(const qahw_module_handle_t *hw_module,
                           const char *keys)
{
    char *str_param = NULL;
    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
    qahw_module_t *qahw_module_temp;
    audio_hw_device_t *audio_device;

    pthread_mutex_lock(&qahw_module_init_lock);
    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
    pthread_mutex_unlock(&qahw_module_init_lock);
    if (qahw_module_temp == NULL) {
        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
        goto exit;
    }

    pthread_mutex_lock(&qahw_module->lock);
    audio_device = qahw_module->audio_device;
    if (qahw_module->audio_device->get_parameters) {
        str_param = audio_device->get_parameters(qahw_module->audio_device, keys);
    } else {
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_module->lock);

exit:
    return str_param;
}

/* Api to implement get parameters  based on keyword param_id
 * and store data in payload.
 */
int qahw_get_param_data(const qahw_module_handle_t *hw_module,
                        qahw_param_id param_id,
                        qahw_param_payload *payload)
{
    int ret = 0;
    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
    qahw_module_t *qahw_module_temp;

    pthread_mutex_lock(&qahw_module_init_lock);
    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
    pthread_mutex_unlock(&qahw_module_init_lock);
    if (qahw_module_temp == NULL) {
        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
        goto exit;
    }

    pthread_mutex_lock(&qahw_module->lock);

    if (qahw_module->qahwi_get_param_data){
        ret = qahw_module->qahwi_get_param_data (qahw_module->audio_device,
                                   param_id, payload);
    } else {
         ret = -ENOSYS;
         ALOGE("%s not supported\n",__func__);
    }
    pthread_mutex_unlock(&qahw_module->lock);

exit:
     return ret;
}

/* Api to implement set parameters  based on keyword param_id
 * and data present in payload.
 */
int qahw_set_param_data(const qahw_module_handle_t *hw_module,
                        qahw_param_id param_id,
                        qahw_param_payload *payload)
{
    int ret = 0;
    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
    qahw_module_t *qahw_module_temp;

    pthread_mutex_lock(&qahw_module_init_lock);
    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
    pthread_mutex_unlock(&qahw_module_init_lock);
    if (qahw_module_temp == NULL) {
        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
        goto exit;
    }

    pthread_mutex_lock(&qahw_module->lock);

    if (qahw_module->qahwi_set_param_data){
        ret = qahw_module->qahwi_set_param_data (qahw_module->audio_device,
                                   param_id, payload);
    } else {
         ret = -ENOSYS;
         ALOGE("%s not supported\n",__func__);
    }
    pthread_mutex_unlock(&qahw_module->lock);

exit:
     return ret;
}

/* Creates an audio patch between several source and sink ports.
 * The handle is allocated by the HAL and should be unique for this
 * audio HAL module.
 */
int qahw_create_audio_patch(qahw_module_handle_t *hw_module,
                        unsigned int num_sources,
                        const struct audio_port_config *sources,
                        unsigned int num_sinks,
                        const struct audio_port_config *sinks,
                        audio_patch_handle_t *handle)
{
    int ret = 0;
    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
    qahw_module_t *qahw_module_temp;

    pthread_mutex_lock(&qahw_module_init_lock);
    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
    pthread_mutex_unlock(&qahw_module_init_lock);
    if (qahw_module_temp == NULL) {
        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
        goto exit;
    }

    pthread_mutex_lock(&qahw_module->lock);
    if (qahw_module->audio_device->create_audio_patch) {
        ret = qahw_module->audio_device->create_audio_patch(
                        qahw_module->audio_device,
                        num_sources,
                        sources,
                        num_sinks,
                        sinks,
                        handle);
    } else {
         ret = -ENOSYS;
         ALOGE("%s not supported\n",__func__);
    }
    pthread_mutex_unlock(&qahw_module->lock);

exit:
     return ret;
}

/* Release an audio patch */
int qahw_release_audio_patch(qahw_module_handle_t *hw_module,
                        audio_patch_handle_t handle)
{
    int ret = 0;
    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
    qahw_module_t *qahw_module_temp;

    pthread_mutex_lock(&qahw_module_init_lock);
    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
    pthread_mutex_unlock(&qahw_module_init_lock);
    if (qahw_module_temp == NULL) {
        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
        goto exit;
    }

    pthread_mutex_lock(&qahw_module->lock);
    if (qahw_module->audio_device->release_audio_patch) {
        ret = qahw_module->audio_device->release_audio_patch(
                        qahw_module->audio_device,
                        handle);
    } else {
         ret = -ENOSYS;
         ALOGE("%s not supported\n",__func__);
    }
    pthread_mutex_unlock(&qahw_module->lock);

exit:
     return ret;
}

/* Fills the list of supported attributes for a given audio port.
 * As input, "port" contains the information (type, role, address etc...)
 * needed by the HAL to identify the port.
 * As output, "port" contains possible attributes (sampling rates, formats,
 * channel masks, gain controllers...) for this port.
 */
int qahw_get_audio_port(qahw_module_handle_t *hw_module,
                      struct audio_port *port)
{
    int ret = 0;
    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
    qahw_module_t *qahw_module_temp;

    pthread_mutex_lock(&qahw_module_init_lock);
    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
    pthread_mutex_unlock(&qahw_module_init_lock);
    if (qahw_module_temp == NULL) {
        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
        goto exit;
    }

    pthread_mutex_lock(&qahw_module->lock);
    if (qahw_module->audio_device->get_audio_port) {
        ret = qahw_module->audio_device->get_audio_port(
                    qahw_module->audio_device,
                    port);
    } else {
         ret = -ENOSYS;
         ALOGE("%s not supported\n",__func__);
    }
    pthread_mutex_unlock(&qahw_module->lock);

exit:
     return ret;
}

/* Set audio port configuration */
int qahw_set_audio_port_config(qahw_module_handle_t *hw_module,
                     const struct audio_port_config *config)
{
    int ret = 0;
    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
    qahw_module_t *qahw_module_temp;

    pthread_mutex_lock(&qahw_module_init_lock);
    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
    pthread_mutex_unlock(&qahw_module_init_lock);
    if (qahw_module_temp == NULL) {
        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
        goto exit;
    }

    pthread_mutex_lock(&qahw_module->lock);
    if (qahw_module->audio_device->set_audio_port_config) {
        ret = qahw_module->audio_device->set_audio_port_config(
                    qahw_module->audio_device,
                        config);
    } else {
         ret = -ENOSYS;
         ALOGE("%s not supported\n",__func__);
    }
    pthread_mutex_unlock(&qahw_module->lock);

exit:
     return ret;
}

/* Returns audio input buffer size according to parameters passed or
 * 0 if one of the parameters is not supported.
 * See also get_buffer_size which is for a particular stream.
 */
size_t qahw_get_input_buffer_size(const qahw_module_handle_t *hw_module,
                                  const struct audio_config *config)
{
    size_t rc = 0;
    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
    qahw_module_t *qahw_module_temp;
    audio_hw_device_t *audio_device;

    pthread_mutex_lock(&qahw_module_init_lock);
    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
    pthread_mutex_unlock(&qahw_module_init_lock);
    if (qahw_module_temp == NULL) {
        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
        goto exit;
    }

    pthread_mutex_lock(&qahw_module->lock);
    audio_device = qahw_module->audio_device;
    if (qahw_module->audio_device->get_input_buffer_size) {
        rc = audio_device->get_input_buffer_size(qahw_module->audio_device,
                                                 config);
    } else {
        rc = -ENOSYS;
        ALOGW("%s not supported", __func__);
    }
    pthread_mutex_unlock(&qahw_module->lock);

exit:
    return rc;
}

/*
 * This method creates and opens the audio hardware output stream.
 * The "address" parameter qualifies the "devices" audio device type if needed.
 * The format format depends on the device type:
 * - Bluetooth devices use the MAC address of the device in the form "00:11:22:AA:BB:CC"
 * - USB devices use the ALSA card and device numbers in the form  "card=X;device=Y"
 * - Other devices may use a number or any other string.
 */
int qahw_open_output_stream(qahw_module_handle_t *hw_module,
                            audio_io_handle_t handle,
                            audio_devices_t devices,
                            audio_output_flags_t flags,
                            struct audio_config *config,
                            qahw_stream_handle_t **out_handle,
                            const char *address)
{
    int rc = -EINVAL;
    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
    qahw_module_t *qahw_module_temp = NULL;
    audio_hw_device_t *audio_device = NULL;
    qahw_stream_out_t *qahw_stream_out = NULL;

    pthread_mutex_lock(&qahw_module_init_lock);
    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
    pthread_mutex_unlock(&qahw_module_init_lock);
    if (qahw_module_temp == NULL) {
        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
        return rc;
    }

    pthread_mutex_lock(&qahw_module->lock);
    audio_device = qahw_module->audio_device;
    qahw_stream_out = (qahw_stream_out_t *)calloc(1, sizeof(qahw_stream_out_t));
    if (qahw_stream_out == NULL) {
        ALOGE("%s:: calloc failed for out stream_out_t",__func__);
        rc = -ENOMEM;
        goto exit;
    }

    rc = audio_device->open_output_stream(audio_device,
                                          handle,
                                          devices,
                                          flags,
                                          config,
                                          &qahw_stream_out->stream,
                                          address);
    if (rc) {
        ALOGE("%s::open output stream failed %d",__func__, rc);
        free(qahw_stream_out);
    } else {
        qahw_stream_out->module = hw_module;
        *out_handle = (void *)qahw_stream_out;
        pthread_mutex_init(&qahw_stream_out->lock, (const pthread_mutexattr_t *)NULL);
        list_add_tail(&qahw_module->out_list, &qahw_stream_out->list);

        /* clear any existing errors */
        const char *error;
        dlerror();
        qahw_stream_out->qahwi_out_get_param_data = (qahwi_out_get_param_data_t)
                                                 dlsym(qahw_module->module->dso,
                                                 "qahwi_out_get_param_data");
        if ((error = dlerror()) != NULL) {
            ALOGI("%s: dlsym error %s for qahwi_out_get_param_data",
                   __func__, error);
            qahw_stream_out->qahwi_out_get_param_data = NULL;
        }

        dlerror();
        qahw_stream_out->qahwi_out_set_param_data = (qahwi_out_set_param_data_t)
                                                 dlsym(qahw_module->module->dso,
                                                 "qahwi_out_set_param_data");
        if ((error = dlerror()) != NULL) {
            ALOGI("%s: dlsym error %s for qahwi_out_set_param_data",
                   __func__, error);
            qahw_stream_out->qahwi_out_set_param_data = NULL;
        }
}

    /* dlsym qahwi_out_write_v2 */
    if (!rc) {
        const char *error;

        /* clear any existing errors */
        dlerror();
        qahw_stream_out->qahwi_out_write_v2 = (qahwi_out_write_v2_t)dlsym(qahw_module->module->dso, "qahwi_out_write_v2");
        if ((error = dlerror()) != NULL) {
            ALOGI("%s: dlsym error %s for qahwi_out_write_v2", __func__, error);
            qahw_stream_out->qahwi_out_write_v2 = NULL;
        }
    }

exit:
    pthread_mutex_unlock(&qahw_module->lock);
    return rc;
}

int qahw_close_output_stream(qahw_stream_handle_t *out_handle)
{

    int rc = 0;
    qahw_stream_out_t *qahw_stream_out = (qahw_stream_out_t *)out_handle;
    qahw_module_t *qahw_module = NULL;
    audio_hw_device_t *audio_device = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_out, STREAM_DIR_OUT)) {
        ALOGE("%s::Invalid out handle %p", __func__, out_handle);
        rc = -EINVAL;
        goto exit;
    }

    ALOGV("%s::calling device close_output_stream %p", __func__, out_handle);
    pthread_mutex_lock(&qahw_stream_out->lock);
    qahw_module = qahw_stream_out->module;
    audio_device = qahw_module->audio_device;
    audio_device->close_output_stream(audio_device,
                                      qahw_stream_out->stream);

    pthread_mutex_lock(&qahw_module->lock);
    list_remove(&qahw_stream_out->list);
    pthread_mutex_unlock(&qahw_module->lock);

    pthread_mutex_unlock(&qahw_stream_out->lock);

    pthread_mutex_destroy(&qahw_stream_out->lock);
    free(qahw_stream_out);

exit:
    return rc;
}

/* This method creates and opens the audio hardware input stream */
int qahw_open_input_stream(qahw_module_handle_t *hw_module,
                           audio_io_handle_t handle,
                           audio_devices_t devices,
                           struct audio_config *config,
                           qahw_stream_handle_t **in_handle,
                           audio_input_flags_t flags,
                           const char *address,
                           audio_source_t source)
{
    int rc = -EINVAL;
    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
    qahw_module_t *qahw_module_temp = NULL;
    audio_hw_device_t *audio_device = NULL;
    qahw_stream_in_t *qahw_stream_in = NULL;

    pthread_mutex_lock(&qahw_module_init_lock);
    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
    pthread_mutex_unlock(&qahw_module_init_lock);
    if (qahw_module_temp == NULL) {
        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
        return rc;
    }

    pthread_mutex_lock(&qahw_module->lock);
    audio_device = qahw_module->audio_device;
    qahw_stream_in = (qahw_stream_in_t *)calloc(1, sizeof(qahw_stream_in_t));
    if (qahw_stream_in == NULL) {
        ALOGE("%s:: calloc failed for in stream_in_t",__func__);
        rc = -ENOMEM;
        goto exit;
    }

    rc = audio_device->open_input_stream(audio_device,
                                          handle,
                                          devices,
                                          config,
                                          &qahw_stream_in->stream,
                                          flags,
                                          address,
                                          source);
    if (rc) {
        ALOGE("%s::open input stream failed %d",__func__, rc);
        free(qahw_stream_in);
    } else {
        qahw_stream_in->module = hw_module;
        *in_handle = (void *)qahw_stream_in;
        pthread_mutex_init(&qahw_stream_in->lock, (const pthread_mutexattr_t *)NULL);
        list_add_tail(&qahw_module->in_list, &qahw_stream_in->list);
    }

    /* dlsym qahwi_in_read_v2 if timestamp flag is used */
    if (!rc && (flags & QAHW_INPUT_FLAG_TIMESTAMP)) {
        const char *error;

        /* clear any existing errors */
        dlerror();
        qahw_stream_in->qahwi_in_read_v2 = (qahwi_in_read_v2_t)
                          dlsym(qahw_module->module->dso, "qahwi_in_read_v2");
        if ((error = dlerror()) != NULL) {
            ALOGI("%s: dlsym error %s for qahwi_in_read_v2", __func__, error);
            qahw_stream_in->qahwi_in_read_v2 = NULL;
        }
    }

exit:
    pthread_mutex_unlock(&qahw_module->lock);
    return rc;
}

int qahw_close_input_stream(qahw_stream_handle_t *in_handle)
{
    int rc = 0;
    qahw_stream_in_t *qahw_stream_in = (qahw_stream_in_t *)in_handle;
    qahw_module_t *qahw_module = NULL;
    audio_hw_device_t *audio_device = NULL;

    if (!is_valid_qahw_stream((void *)qahw_stream_in, STREAM_DIR_IN)) {
        ALOGV("%s::Invalid in handle %p", __func__, in_handle);
        rc = -EINVAL;
        goto exit;
    }

    ALOGV("%s:: calling device close_input_stream %p", __func__, in_handle);
    pthread_mutex_lock(&qahw_stream_in->lock);
    qahw_module = qahw_stream_in->module;
    audio_device = qahw_module->audio_device;
    audio_device->close_input_stream(audio_device,
                                     qahw_stream_in->stream);

    pthread_mutex_lock(&qahw_module->lock);
    list_remove(&qahw_stream_in->list);
    pthread_mutex_unlock(&qahw_module->lock);

    pthread_mutex_unlock(&qahw_stream_in->lock);

    pthread_mutex_destroy(&qahw_stream_in->lock);
    free(qahw_stream_in);

exit:
    return rc;
}

/*returns current QTI HAL verison */
int qahw_get_version() {
    return QAHW_MODULE_API_VERSION_CURRENT;
}

/* convenience API for opening and closing an audio HAL module */

qahw_module_handle_t *qahw_load_module(const char *hw_module_id)
{
    int rc = -EINVAL;
    qahw_module_handle_t *qahw_mod_handle = NULL;
    qahw_module_t *qahw_module = NULL;
    char *ahal_name = NULL;
    const hw_module_t* module = NULL;
    audio_hw_device_t* audio_device = NULL;

    if (hw_module_id == NULL) {
        ALOGE("%s::module id is NULL",__func__);
        goto exit;
    }

    if (!strcmp(hw_module_id, QAHW_MODULE_ID_PRIMARY)) {
        ahal_name = "primary";
    } else if (!strcmp(hw_module_id, QAHW_MODULE_ID_A2DP)) {
        ahal_name = "a2dp";
    } else if (!strcmp(hw_module_id, QAHW_MODULE_ID_USB)) {
        ahal_name = "usb";
    } else {
        ALOGE("%s::Invalid Module id %s", __func__, hw_module_id);
        goto exit;
    }

    /* return exiting module ptr if already loaded */
    pthread_mutex_lock(&qahw_module_init_lock);
    if (qahw_list_count > 0) {
        qahw_module = get_qahw_module_by_name(hw_module_id);
        if(qahw_module != NULL) {
            qahw_mod_handle = (void *)qahw_module;
            pthread_mutex_lock(&qahw_module->lock);
            qahw_module->ref_count++;
            pthread_mutex_unlock(&qahw_module->lock);
            goto error_exit;
        }
    }

    rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, ahal_name, &module);
    if(rc) {
        ALOGE("%s::HAL Loading failed %d", __func__, rc);
        goto error_exit;
    }

    rc = audio_hw_device_open(module, &audio_device);
    if(rc) {
        ALOGE("%s::HAL Device open failed %d", __func__, rc);
        goto error_exit;
    }

    qahw_module = (qahw_module_t *)calloc(1, sizeof(qahw_module_t));
    if(qahw_module == NULL) {
        ALOGE("%s::calloc failed", __func__);
        audio_hw_device_close(audio_device);
        goto error_exit;
    }
    qahw_module->module = module;
    ALOGD("%s::Loaded HAL %s module %p", __func__, ahal_name, qahw_module);

    qahw_module->qahwi_get_param_data = (qahwi_get_param_data_t) dlsym (module->dso,
                            "qahwi_get_param_data");
    if (!qahw_module->qahwi_get_param_data)
         ALOGD("%s::qahwi_get_param_data api is not defined\n",__func__);

    qahw_module->qahwi_set_param_data = (qahwi_set_param_data_t) dlsym (module->dso,
                            "qahwi_set_param_data");
    if (!qahw_module->qahwi_set_param_data)
         ALOGD("%s::qahwi_set_param_data api is not defined\n",__func__);

    if (!qahw_list_count)
        list_init(&qahw_module_list);
    qahw_list_count++;

    pthread_mutex_init(&qahw_module->lock, (const pthread_mutexattr_t *) NULL);
    pthread_mutex_lock(&qahw_module->lock);
    qahw_module->ref_count++;
    pthread_mutex_unlock(&qahw_module->lock);

    list_init(&qahw_module->out_list);
    list_init(&qahw_module->in_list);

    /* update qahw_module */
    qahw_module->audio_device = audio_device;
    strlcpy(&qahw_module->module_name[0], hw_module_id, MAX_MODULE_NAME_LENGTH);

    qahw_mod_handle = (void *)qahw_module;

    /* Add module list to global module list */
    list_add_tail(&qahw_module_list, &qahw_module->module_list);


error_exit:
    pthread_mutex_unlock(&qahw_module_init_lock);

exit:
    return qahw_mod_handle;
}

int qahw_unload_module(qahw_module_handle_t *hw_module)
{
    int rc = -EINVAL;
    bool is_empty = false;
    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
    qahw_module_t *qahw_module_temp = NULL;

    /* close HW device if its valid and all the streams on
     * it is closed
    */
    pthread_mutex_lock(&qahw_module_init_lock);
    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
    if (qahw_module_temp == NULL) {
        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
        goto error_exit;
    }

    pthread_mutex_lock(&qahw_module->lock);
    qahw_module->ref_count--;
    if (qahw_module->ref_count > 0) {
        rc = 0;
        ALOGE("%s:: skipping module unload of %p count %d", __func__,
              qahw_module,
              qahw_module->ref_count);
        pthread_mutex_unlock(&qahw_module->lock);
        goto error_exit;
    }

    is_empty = (list_empty(&qahw_module->out_list) &&
             list_empty(&qahw_module->in_list));
    if (is_empty) {
        rc = audio_hw_device_close(qahw_module->audio_device);
        if(rc) {
            ALOGE("%s::HAL Device close failed Error %d Module %p",__func__,
                    rc, qahw_module);
            rc = 0;
        }
        qahw_list_count--;
        list_remove(&qahw_module->module_list);
        pthread_mutex_unlock(&qahw_module->lock);
        pthread_mutex_destroy(&qahw_module->lock);
        free(qahw_module);
    } else {
        pthread_mutex_unlock(&qahw_module->lock);
        ALOGE("%s::failed as all the streams on this module"
               "is not closed", __func__);
        rc = -EINVAL;
    }

error_exit:
    pthread_mutex_unlock(&qahw_module_init_lock);

    return rc;
}

__END_DECLS
