/* Copyright (c) 2012-2014, 2016, 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.
 *
 */

// To remove
#include <cutils/properties.h>

// System dependencies
#include <pthread.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#define IOCTL_H <SYSTEM_HEADER_PREFIX/ioctl.h>
#include IOCTL_H

// Camera dependencies
#include "cam_semaphore.h"
#include "mm_camera_dbg.h"
#include "mm_camera_sock.h"
#include "mm_camera_interface.h"
#include "mm_camera.h"

#define SET_PARM_BIT32(parm, parm_arr) \
    (parm_arr[parm/32] |= (1<<(parm%32)))

#define GET_PARM_BIT32(parm, parm_arr) \
    ((parm_arr[parm/32]>>(parm%32))& 0x1)

/* internal function declare */
int32_t mm_camera_evt_sub(mm_camera_obj_t * my_obj,
                          uint8_t reg_flag);
int32_t mm_camera_enqueue_evt(mm_camera_obj_t *my_obj,
                              mm_camera_event_t *event);

/*===========================================================================
 * FUNCTION   : mm_camera_util_get_channel_by_handler
 *
 * DESCRIPTION: utility function to get a channel object from its handle
 *
 * PARAMETERS :
 *   @cam_obj: ptr to a camera object
 *   @handler: channel handle
 *
 * RETURN     : ptr to a channel object.
 *              NULL if failed.
 *==========================================================================*/
mm_channel_t * mm_camera_util_get_channel_by_handler(
                                    mm_camera_obj_t * cam_obj,
                                    uint32_t handler)
{
    int i;
    mm_channel_t *ch_obj = NULL;
    for(i = 0; i < MM_CAMERA_CHANNEL_MAX; i++) {
        if (handler == cam_obj->ch[i].my_hdl) {
            ch_obj = &cam_obj->ch[i];
            break;
        }
    }
    return ch_obj;
}

/*===========================================================================
 * FUNCTION   : mm_camera_util_chip_is_a_family
 *
 * DESCRIPTION: utility function to check if the host is A family chip
 *
 * PARAMETERS :
 *
 * RETURN     : TRUE if A family.
 *              FALSE otherwise.
 *==========================================================================*/
uint8_t mm_camera_util_chip_is_a_family(void)
{
#ifdef USE_A_FAMILY
    return TRUE;
#else
    return FALSE;
#endif
}

/*===========================================================================
 * FUNCTION   : mm_camera_dispatch_app_event
 *
 * DESCRIPTION: dispatch event to apps who regitster for event notify
 *
 * PARAMETERS :
 *   @cmd_cb: ptr to a struct storing event info
 *   @user_data: user data ptr (camera object)
 *
 * RETURN     : none
 *==========================================================================*/
static void mm_camera_dispatch_app_event(mm_camera_cmdcb_t *cmd_cb,
                                         void* user_data)
{
    int i;
    mm_camera_event_t *event = &cmd_cb->u.evt;
    mm_camera_obj_t * my_obj = (mm_camera_obj_t *)user_data;
    if (NULL != my_obj) {
        mm_camera_cmd_thread_name(my_obj->evt_thread.threadName);
        pthread_mutex_lock(&my_obj->cb_lock);
        for(i = 0; i < MM_CAMERA_EVT_ENTRY_MAX; i++) {
            if(my_obj->evt.evt[i].evt_cb) {
                my_obj->evt.evt[i].evt_cb(
                    my_obj->my_hdl,
                    event,
                    my_obj->evt.evt[i].user_data);
            }
        }
        pthread_mutex_unlock(&my_obj->cb_lock);
    }
}

/*===========================================================================
 * FUNCTION   : mm_camera_event_notify
 *
 * DESCRIPTION: callback to handle event notify from kernel. This call will
 *              dequeue event from kernel.
 *
 * PARAMETERS :
 *   @user_data: user data ptr (camera object)
 *
 * RETURN     : none
 *==========================================================================*/
static void mm_camera_event_notify(void* user_data)
{
    struct v4l2_event ev;
    struct msm_v4l2_event_data *msm_evt = NULL;
    int rc;
    mm_camera_event_t evt;
    memset(&evt, 0, sizeof(mm_camera_event_t));

    mm_camera_obj_t *my_obj = (mm_camera_obj_t*)user_data;
    if (NULL != my_obj) {
        /* read evt */
        memset(&ev, 0, sizeof(ev));
        rc = ioctl(my_obj->ctrl_fd, VIDIOC_DQEVENT, &ev);

        if (rc >= 0 && ev.id == MSM_CAMERA_MSM_NOTIFY) {
            msm_evt = (struct msm_v4l2_event_data *)ev.u.data;
            switch (msm_evt->command) {
            case CAM_EVENT_TYPE_DAEMON_PULL_REQ:
                evt.server_event_type = CAM_EVENT_TYPE_DAEMON_PULL_REQ;
                mm_camera_enqueue_evt(my_obj, &evt);
                break;
            case CAM_EVENT_TYPE_MAP_UNMAP_DONE:
                pthread_mutex_lock(&my_obj->evt_lock);
                my_obj->evt_rcvd.server_event_type = msm_evt->command;
                my_obj->evt_rcvd.status = msm_evt->status;
                pthread_cond_signal(&my_obj->evt_cond);
                pthread_mutex_unlock(&my_obj->evt_lock);
                break;
            case CAM_EVENT_TYPE_INT_TAKE_JPEG:
            case CAM_EVENT_TYPE_INT_TAKE_RAW:
                {
                    evt.server_event_type = msm_evt->command;
                    mm_camera_enqueue_evt(my_obj, &evt);
                }
                break;
            case MSM_CAMERA_PRIV_SHUTDOWN:
                {
                    LOGE("Camera Event DAEMON DIED received");
                    evt.server_event_type = CAM_EVENT_TYPE_DAEMON_DIED;
                    mm_camera_enqueue_evt(my_obj, &evt);
                }
                break;
            case CAM_EVENT_TYPE_CAC_DONE:
                {
                    evt.server_event_type = CAM_EVENT_TYPE_CAC_DONE;
                    mm_camera_enqueue_evt(my_obj, &evt);
                }
                break;
            default:
                break;
            }
        }
    }
}

/*===========================================================================
 * FUNCTION   : mm_camera_enqueue_evt
 *
 * DESCRIPTION: enqueue received event into event queue to be processed by
 *              event thread.
 *
 * PARAMETERS :
 *   @my_obj   : ptr to a camera object
 *   @event    : event to be queued
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_enqueue_evt(mm_camera_obj_t *my_obj,
                              mm_camera_event_t *event)
{
    int32_t rc = 0;
    mm_camera_cmdcb_t *node = NULL;

    node = (mm_camera_cmdcb_t *)malloc(sizeof(mm_camera_cmdcb_t));
    if (NULL != node) {
        memset(node, 0, sizeof(mm_camera_cmdcb_t));
        node->cmd_type = MM_CAMERA_CMD_TYPE_EVT_CB;
        node->u.evt = *event;

        /* enqueue to evt cmd thread */
        cam_queue_enq(&(my_obj->evt_thread.cmd_queue), node);
        /* wake up evt cmd thread */
        cam_sem_post(&(my_obj->evt_thread.cmd_sem));
    } else {
        LOGE("No memory for mm_camera_node_t");
        rc = -1;
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_open
 *
 * DESCRIPTION: open a camera
 *
 * PARAMETERS :
 *   @my_obj   : ptr to a camera object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_open(mm_camera_obj_t *my_obj)
{
    char dev_name[MM_CAMERA_DEV_NAME_LEN];
    int32_t rc = 0;
    int8_t n_try=MM_CAMERA_DEV_OPEN_TRIES;
    uint8_t sleep_msec=MM_CAMERA_DEV_OPEN_RETRY_SLEEP;
    int cam_idx = 0;
    const char *dev_name_value = NULL;
    int l_errno = 0;

    LOGD("begin\n");

    if (NULL == my_obj) {
        goto on_error;
    }
    dev_name_value = mm_camera_util_get_dev_name(my_obj->my_hdl);
    if (NULL == dev_name_value) {
        goto on_error;
    }
    snprintf(dev_name, sizeof(dev_name), "/dev/%s",
             dev_name_value);
    sscanf(dev_name, "/dev/video%d", &cam_idx);
    LOGD("dev name = %s, cam_idx = %d", dev_name, cam_idx);

    do{
        n_try--;
        errno = 0;
        my_obj->ctrl_fd = open(dev_name, O_RDWR | O_NONBLOCK);
        l_errno = errno;
        LOGD("ctrl_fd = %d, errno == %d", my_obj->ctrl_fd, l_errno);
        if((my_obj->ctrl_fd >= 0) || (errno != EIO && errno != ETIMEDOUT) || (n_try <= 0 )) {
            LOGH("opened, break out while loop");
            break;
        }
        LOGE("Failed with %s error, retrying after %d milli-seconds",
              strerror(errno), sleep_msec);
        usleep(sleep_msec * 1000U);
    }while (n_try > 0);

    if (my_obj->ctrl_fd < 0) {
        LOGE("cannot open control fd of '%s' (%s)\n",
                  dev_name, strerror(l_errno));
        if (l_errno == EBUSY)
            rc = -EUSERS;
        else
            rc = -1;
        goto on_error;
    }

    /* open domain socket*/
    n_try = MM_CAMERA_DEV_OPEN_TRIES;
    do {
        n_try--;
        my_obj->ds_fd = mm_camera_socket_create(cam_idx, MM_CAMERA_SOCK_TYPE_UDP);
        l_errno = errno;
        LOGD("ds_fd = %d, errno = %d", my_obj->ds_fd, l_errno);
        if((my_obj->ds_fd >= 0) || (n_try <= 0 )) {
            LOGD("opened, break out while loop");
            break;
        }
        LOGD("failed with I/O error retrying after %d milli-seconds",
              sleep_msec);
        usleep(sleep_msec * 1000U);
    } while (n_try > 0);

    if (my_obj->ds_fd < 0) {
        LOGE("cannot open domain socket fd of '%s'(%s)\n",
                  dev_name, strerror(l_errno));
        rc = -1;
        goto on_error;
    }
    pthread_mutex_init(&my_obj->msg_lock, NULL);

    pthread_mutex_init(&my_obj->cb_lock, NULL);
    pthread_mutex_init(&my_obj->evt_lock, NULL);
    pthread_cond_init(&my_obj->evt_cond, NULL);

    LOGD("Launch evt Thread in Cam Open");
    snprintf(my_obj->evt_thread.threadName, THREAD_NAME_SIZE, "CAM_Dispatch");
    mm_camera_cmd_thread_launch(&my_obj->evt_thread,
                                mm_camera_dispatch_app_event,
                                (void *)my_obj);

    /* launch event poll thread
     * we will add evt fd into event poll thread upon user first register for evt */
    LOGD("Launch evt Poll Thread in Cam Open");
    snprintf(my_obj->evt_poll_thread.threadName, THREAD_NAME_SIZE, "CAM_evntPoll");
    mm_camera_poll_thread_launch(&my_obj->evt_poll_thread,
                                 MM_CAMERA_POLL_TYPE_EVT);
    mm_camera_evt_sub(my_obj, TRUE);

    /* unlock cam_lock, we need release global intf_lock in camera_open(),
     * in order not block operation of other Camera in dual camera use case.*/
    pthread_mutex_unlock(&my_obj->cam_lock);
    LOGD("end (rc = %d)\n", rc);
    return rc;

on_error:

    if (NULL == dev_name_value) {
        LOGE("Invalid device name\n");
        rc = -1;
    }

    if (NULL == my_obj) {
        LOGE("Invalid camera object\n");
        rc = -1;
    } else {
        if (my_obj->ctrl_fd >= 0) {
            close(my_obj->ctrl_fd);
            my_obj->ctrl_fd = -1;
        }
        if (my_obj->ds_fd >= 0) {
            mm_camera_socket_close(my_obj->ds_fd);
            my_obj->ds_fd = -1;
        }
    }

    /* unlock cam_lock, we need release global intf_lock in camera_open(),
     * in order not block operation of other Camera in dual camera use case.*/
    pthread_mutex_unlock(&my_obj->cam_lock);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_close
 *
 * DESCRIPTION: enqueue received event into event queue to be processed by
 *              event thread.
 *
 * PARAMETERS :
 *   @my_obj   : ptr to a camera object
 *   @event    : event to be queued
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_close(mm_camera_obj_t *my_obj)
{
    LOGD("unsubscribe evt");
    mm_camera_evt_sub(my_obj, FALSE);

    LOGD("Close evt Poll Thread in Cam Close");
    mm_camera_poll_thread_release(&my_obj->evt_poll_thread);

    LOGD("Close evt cmd Thread in Cam Close");
    mm_camera_cmd_thread_release(&my_obj->evt_thread);

    if(my_obj->ctrl_fd >= 0) {
        close(my_obj->ctrl_fd);
        my_obj->ctrl_fd = -1;
    }
    if(my_obj->ds_fd >= 0) {
        mm_camera_socket_close(my_obj->ds_fd);
        my_obj->ds_fd = -1;
    }
    pthread_mutex_destroy(&my_obj->msg_lock);

    pthread_mutex_destroy(&my_obj->cb_lock);
    pthread_mutex_destroy(&my_obj->evt_lock);
    pthread_cond_destroy(&my_obj->evt_cond);

    pthread_mutex_unlock(&my_obj->cam_lock);
    return 0;
}

/*===========================================================================
 * FUNCTION   : mm_camera_register_event_notify_internal
 *
 * DESCRIPTION: internal implementation for registering callback for event notify.
 *
 * PARAMETERS :
 *   @my_obj   : ptr to a camera object
 *   @evt_cb   : callback to be registered to handle event notify
 *   @user_data: user data ptr
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_register_event_notify_internal(mm_camera_obj_t *my_obj,
                                                 mm_camera_event_notify_t evt_cb,
                                                 void * user_data)
{
    int i;
    int rc = -1;
    mm_camera_evt_obj_t *evt_array = NULL;

    pthread_mutex_lock(&my_obj->cb_lock);
    evt_array = &my_obj->evt;
    if(evt_cb) {
        /* this is reg case */
        for(i = 0; i < MM_CAMERA_EVT_ENTRY_MAX; i++) {
            if(evt_array->evt[i].user_data == NULL) {
                evt_array->evt[i].evt_cb = evt_cb;
                evt_array->evt[i].user_data = user_data;
                evt_array->reg_count++;
                rc = 0;
                break;
            }
        }
    } else {
        /* this is unreg case */
        for(i = 0; i < MM_CAMERA_EVT_ENTRY_MAX; i++) {
            if(evt_array->evt[i].user_data == user_data) {
                evt_array->evt[i].evt_cb = NULL;
                evt_array->evt[i].user_data = NULL;
                evt_array->reg_count--;
                rc = 0;
                break;
            }
        }
    }

    pthread_mutex_unlock(&my_obj->cb_lock);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_register_event_notify
 *
 * DESCRIPTION: registering a callback for event notify.
 *
 * PARAMETERS :
 *   @my_obj   : ptr to a camera object
 *   @evt_cb   : callback to be registered to handle event notify
 *   @user_data: user data ptr
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_register_event_notify(mm_camera_obj_t *my_obj,
                                        mm_camera_event_notify_t evt_cb,
                                        void * user_data)
{
    int rc = -1;
    rc = mm_camera_register_event_notify_internal(my_obj,
                                                  evt_cb,
                                                  user_data);
    pthread_mutex_unlock(&my_obj->cam_lock);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_qbuf
 *
 * DESCRIPTION: enqueue buffer back to kernel
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *   @buf          : buf ptr to be enqueued
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_qbuf(mm_camera_obj_t *my_obj,
                       uint32_t ch_id,
                       mm_camera_buf_def_t *buf)
{
    int rc = -1;
    mm_channel_t * ch_obj = NULL;
    ch_obj = mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    pthread_mutex_unlock(&my_obj->cam_lock);

    /* we always assume qbuf will be done before channel/stream is fully stopped
     * because qbuf is done within dataCB context
     * in order to avoid deadlock, we are not locking ch_lock for qbuf */
    if (NULL != ch_obj) {
        rc = mm_channel_qbuf(ch_obj, buf);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_get_queued_buf_count
 *
 * DESCRIPTION: return queued buffer count
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *   @stream_id : stream id
 *
 * RETURN     : queued buffer count
 *==========================================================================*/
int32_t mm_camera_get_queued_buf_count(mm_camera_obj_t *my_obj,
        uint32_t ch_id, uint32_t stream_id)
{
    int rc = -1;
    mm_channel_t * ch_obj = NULL;
    uint32_t payload;
    ch_obj = mm_camera_util_get_channel_by_handler(my_obj, ch_id);
    payload = stream_id;

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);
        rc = mm_channel_fsm_fn(ch_obj,
                MM_CHANNEL_EVT_GET_STREAM_QUEUED_BUF_COUNT,
                (void *)&payload,
                NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_query_capability
 *
 * DESCRIPTION: query camera capability
 *
 * PARAMETERS :
 *   @my_obj: camera object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_query_capability(mm_camera_obj_t *my_obj)
{
    int32_t rc = 0;
    struct v4l2_capability cap;

    /* get camera capabilities */
    memset(&cap, 0, sizeof(cap));
    rc = ioctl(my_obj->ctrl_fd, VIDIOC_QUERYCAP, &cap);
    if (rc != 0) {
        LOGE("cannot get camera capabilities, rc = %d, errno %d",
                 rc, errno);
    }

    pthread_mutex_unlock(&my_obj->cam_lock);
    return rc;

}

/*===========================================================================
 * FUNCTION   : mm_camera_set_parms
 *
 * DESCRIPTION: set parameters per camera
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @parms        : ptr to a param struct to be set to server
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * NOTE       : Assume the parms struct buf is already mapped to server via
 *              domain socket. Corresponding fields of parameters to be set
 *              are already filled in by upper layer caller.
 *==========================================================================*/
int32_t mm_camera_set_parms(mm_camera_obj_t *my_obj,
                            parm_buffer_t *parms)
{
    int32_t rc = -1;
    int32_t value = 0;
    if (parms !=  NULL) {
        rc = mm_camera_util_s_ctrl(my_obj->ctrl_fd, CAM_PRIV_PARM, &value);
    }
    pthread_mutex_unlock(&my_obj->cam_lock);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_get_parms
 *
 * DESCRIPTION: get parameters per camera
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @parms        : ptr to a param struct to be get from server
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * NOTE       : Assume the parms struct buf is already mapped to server via
 *              domain socket. Parameters to be get from server are already
 *              filled in by upper layer caller. After this call, corresponding
 *              fields of requested parameters will be filled in by server with
 *              detailed information.
 *==========================================================================*/
int32_t mm_camera_get_parms(mm_camera_obj_t *my_obj,
                            parm_buffer_t *parms)
{
    int32_t rc = -1;
    int32_t value = 0;
    if (parms != NULL) {
        rc = mm_camera_util_g_ctrl(my_obj->ctrl_fd, CAM_PRIV_PARM, &value);
    }
    pthread_mutex_unlock(&my_obj->cam_lock);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_do_auto_focus
 *
 * DESCRIPTION: performing auto focus
 *
 * PARAMETERS :
 *   @camera_handle: camera handle
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * NOTE       : if this call success, we will always assume there will
 *              be an auto_focus event following up.
 *==========================================================================*/
int32_t mm_camera_do_auto_focus(mm_camera_obj_t *my_obj)
{
    int32_t rc = -1;
    int32_t value = 0;
    rc = mm_camera_util_s_ctrl(my_obj->ctrl_fd, CAM_PRIV_DO_AUTO_FOCUS, &value);
    pthread_mutex_unlock(&my_obj->cam_lock);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_cancel_auto_focus
 *
 * DESCRIPTION: cancel auto focus
 *
 * PARAMETERS :
 *   @camera_handle: camera handle
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_cancel_auto_focus(mm_camera_obj_t *my_obj)
{
    int32_t rc = -1;
    int32_t value = 0;
    rc = mm_camera_util_s_ctrl(my_obj->ctrl_fd, CAM_PRIV_CANCEL_AUTO_FOCUS, &value);
    pthread_mutex_unlock(&my_obj->cam_lock);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_prepare_snapshot
 *
 * DESCRIPTION: prepare hardware for snapshot
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @do_af_flag   : flag indicating if AF is needed
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_prepare_snapshot(mm_camera_obj_t *my_obj,
                                   int32_t do_af_flag)
{
    int32_t rc = -1;
    int32_t value = do_af_flag;
    rc = mm_camera_util_s_ctrl(my_obj->ctrl_fd, CAM_PRIV_PREPARE_SNAPSHOT, &value);
    pthread_mutex_unlock(&my_obj->cam_lock);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_start_zsl_snapshot
 *
 * DESCRIPTION: start zsl snapshot
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_start_zsl_snapshot(mm_camera_obj_t *my_obj)
{
    int32_t rc = -1;
    int32_t value = 0;

    rc = mm_camera_util_s_ctrl(my_obj->ctrl_fd,
             CAM_PRIV_START_ZSL_SNAPSHOT, &value);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_stop_zsl_snapshot
 *
 * DESCRIPTION: stop zsl capture
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_stop_zsl_snapshot(mm_camera_obj_t *my_obj)
{
    int32_t rc = -1;
    int32_t value;
    rc = mm_camera_util_s_ctrl(my_obj->ctrl_fd,
             CAM_PRIV_STOP_ZSL_SNAPSHOT, &value);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_flush
 *
 * DESCRIPTION: flush the current camera state and buffers
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_flush(mm_camera_obj_t *my_obj)
{
    int32_t rc = -1;
    int32_t value;
    rc = mm_camera_util_s_ctrl(my_obj->ctrl_fd,
            CAM_PRIV_FLUSH, &value);
    pthread_mutex_unlock(&my_obj->cam_lock);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_add_channel
 *
 * DESCRIPTION: add a channel
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @attr         : bundle attribute of the channel if needed
 *   @channel_cb   : callback function for bundle data notify
 *   @userdata     : user data ptr
 *
 * RETURN     : uint32_t type of channel handle
 *              0  -- invalid channel handle, meaning the op failed
 *              >0 -- successfully added a channel with a valid handle
 * NOTE       : if no bundle data notify is needed, meaning each stream in the
 *              channel will have its own stream data notify callback, then
 *              attr, channel_cb, and userdata can be NULL. In this case,
 *              no matching logic will be performed in channel for the bundling.
 *==========================================================================*/
uint32_t mm_camera_add_channel(mm_camera_obj_t *my_obj,
                               mm_camera_channel_attr_t *attr,
                               mm_camera_buf_notify_t channel_cb,
                               void *userdata)
{
    mm_channel_t *ch_obj = NULL;
    uint8_t ch_idx = 0;
    uint32_t ch_hdl = 0;

    for(ch_idx = 0; ch_idx < MM_CAMERA_CHANNEL_MAX; ch_idx++) {
        if (MM_CHANNEL_STATE_NOTUSED == my_obj->ch[ch_idx].state) {
            ch_obj = &my_obj->ch[ch_idx];
            break;
        }
    }

    if (NULL != ch_obj) {
        /* initialize channel obj */
        memset(ch_obj, 0, sizeof(mm_channel_t));
        ch_hdl = mm_camera_util_generate_handler(ch_idx);
        ch_obj->my_hdl = ch_hdl;
        ch_obj->state = MM_CHANNEL_STATE_STOPPED;
        ch_obj->cam_obj = my_obj;
        pthread_mutex_init(&ch_obj->ch_lock, NULL);
        ch_obj->sessionid = my_obj->sessionid;
        mm_channel_init(ch_obj, attr, channel_cb, userdata);
    }

    pthread_mutex_unlock(&my_obj->cam_lock);

    return ch_hdl;
}

/*===========================================================================
 * FUNCTION   : mm_camera_del_channel
 *
 * DESCRIPTION: delete a channel by its handle
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * NOTE       : all streams in the channel should be stopped already before
 *              this channel can be deleted.
 *==========================================================================*/
int32_t mm_camera_del_channel(mm_camera_obj_t *my_obj,
                              uint32_t ch_id)
{
    int32_t rc = -1;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        rc = mm_channel_fsm_fn(ch_obj,
                               MM_CHANNEL_EVT_DELETE,
                               NULL,
                               NULL);

        pthread_mutex_destroy(&ch_obj->ch_lock);
        memset(ch_obj, 0, sizeof(mm_channel_t));
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_get_bundle_info
 *
 * DESCRIPTION: query bundle info of the channel
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *   @bundle_info  : bundle info to be filled in
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * NOTE       : all streams in the channel should be stopped already before
 *              this channel can be deleted.
 *==========================================================================*/
int32_t mm_camera_get_bundle_info(mm_camera_obj_t *my_obj,
                                  uint32_t ch_id,
                                  cam_bundle_config_t *bundle_info)
{
    int32_t rc = -1;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        rc = mm_channel_fsm_fn(ch_obj,
                               MM_CHANNEL_EVT_GET_BUNDLE_INFO,
                               (void *)bundle_info,
                               NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_link_stream
 *
 * DESCRIPTION: link a stream into a channel
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *   @stream_id    : stream that will be linked
 *   @linked_ch_id : channel in which the stream will be linked
 *
 * RETURN     : uint32_t type of stream handle
 *              0  -- invalid stream handle, meaning the op failed
 *              >0 -- successfully linked a stream with a valid handle
 *==========================================================================*/
uint32_t mm_camera_link_stream(mm_camera_obj_t *my_obj,
        uint32_t ch_id,
        uint32_t stream_id,
        uint32_t linked_ch_id)
{
    uint32_t s_hdl = 0;
    mm_channel_t * ch_obj =
            mm_camera_util_get_channel_by_handler(my_obj, linked_ch_id);
    mm_channel_t * owner_obj =
            mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if ((NULL != ch_obj) && (NULL != owner_obj)) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        mm_camera_stream_link_t stream_link;
        memset(&stream_link, 0, sizeof(mm_camera_stream_link_t));
        stream_link.ch = owner_obj;
        stream_link.stream_id = stream_id;
        mm_channel_fsm_fn(ch_obj,
                          MM_CHANNEL_EVT_LINK_STREAM,
                          (void*)&stream_link,
                          (void*)&s_hdl);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return s_hdl;
}

/*===========================================================================
 * FUNCTION   : mm_camera_add_stream
 *
 * DESCRIPTION: add a stream into a channel
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *
 * RETURN     : uint32_t type of stream handle
 *              0  -- invalid stream handle, meaning the op failed
 *              >0 -- successfully added a stream with a valid handle
 *==========================================================================*/
uint32_t mm_camera_add_stream(mm_camera_obj_t *my_obj,
                              uint32_t ch_id)
{
    uint32_t s_hdl = 0;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        mm_channel_fsm_fn(ch_obj,
                          MM_CHANNEL_EVT_ADD_STREAM,
                          NULL,
                          (void *)&s_hdl);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return s_hdl;
}

/*===========================================================================
 * FUNCTION   : mm_camera_del_stream
 *
 * DESCRIPTION: delete a stream by its handle
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *   @stream_id    : stream handle
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * NOTE       : stream should be stopped already before it can be deleted.
 *==========================================================================*/
int32_t mm_camera_del_stream(mm_camera_obj_t *my_obj,
                             uint32_t ch_id,
                             uint32_t stream_id)
{
    int32_t rc = -1;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        rc = mm_channel_fsm_fn(ch_obj,
                               MM_CHANNEL_EVT_DEL_STREAM,
                               (void *)&stream_id,
                               NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_start_zsl_snapshot_ch
 *
 * DESCRIPTION: starts zsl snapshot for specific channel
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_start_zsl_snapshot_ch(mm_camera_obj_t *my_obj,
        uint32_t ch_id)
{
    int32_t rc = -1;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        rc = mm_channel_fsm_fn(ch_obj,
                               MM_CHANNEL_EVT_START_ZSL_SNAPSHOT,
                               NULL,
                               NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_stop_zsl_snapshot_ch
 *
 * DESCRIPTION: stops zsl snapshot for specific channel
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_stop_zsl_snapshot_ch(mm_camera_obj_t *my_obj,
        uint32_t ch_id)
{
    int32_t rc = -1;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        rc = mm_channel_fsm_fn(ch_obj,
                               MM_CHANNEL_EVT_STOP_ZSL_SNAPSHOT,
                               NULL,
                               NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_config_stream
 *
 * DESCRIPTION: configure a stream
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *   @stream_id    : stream handle
 *   @config       : stream configuration
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_config_stream(mm_camera_obj_t *my_obj,
                                uint32_t ch_id,
                                uint32_t stream_id,
                                mm_camera_stream_config_t *config)
{
    int32_t rc = -1;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);
    mm_evt_paylod_config_stream_t payload;

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        memset(&payload, 0, sizeof(mm_evt_paylod_config_stream_t));
        payload.stream_id = stream_id;
        payload.config = config;
        rc = mm_channel_fsm_fn(ch_obj,
                               MM_CHANNEL_EVT_CONFIG_STREAM,
                               (void *)&payload,
                               NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_start_channel
 *
 * DESCRIPTION: start a channel, which will start all streams in the channel
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_start_channel(mm_camera_obj_t *my_obj, uint32_t ch_id)
{
    int32_t rc = -1;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        rc = mm_channel_fsm_fn(ch_obj,
                               MM_CHANNEL_EVT_START,
                               NULL,
                               NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_stop_channel
 *
 * DESCRIPTION: stop a channel, which will stop all streams in the channel
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_stop_channel(mm_camera_obj_t *my_obj,
                               uint32_t ch_id)
{
    int32_t rc = 0;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        rc = mm_channel_fsm_fn(ch_obj,
                               MM_CHANNEL_EVT_STOP,
                               NULL,
                               NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_request_super_buf
 *
 * DESCRIPTION: for burst mode in bundle, reuqest certain amount of matched
 *              frames from superbuf queue
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *   @num_buf_requested : number of matched frames needed
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_request_super_buf(mm_camera_obj_t *my_obj,
        uint32_t ch_id, mm_camera_req_buf_t *buf)
{
    int32_t rc = -1;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if ((NULL != ch_obj) && (buf != NULL)) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        rc = mm_channel_fsm_fn(ch_obj, MM_CHANNEL_EVT_REQUEST_SUPER_BUF,
                (void *)buf, NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_cancel_super_buf_request
 *
 * DESCRIPTION: for burst mode in bundle, cancel the reuqest for certain amount
 *              of matched frames from superbuf queue
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_cancel_super_buf_request(mm_camera_obj_t *my_obj, uint32_t ch_id)
{
    int32_t rc = -1;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        rc = mm_channel_fsm_fn(ch_obj,
                               MM_CHANNEL_EVT_CANCEL_REQUEST_SUPER_BUF,
                               NULL,
                               NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_flush_super_buf_queue
 *
 * DESCRIPTION: flush out all frames in the superbuf queue
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_flush_super_buf_queue(mm_camera_obj_t *my_obj, uint32_t ch_id,
                                                             uint32_t frame_idx)
{
    int32_t rc = -1;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        rc = mm_channel_fsm_fn(ch_obj,
                               MM_CHANNEL_EVT_FLUSH_SUPER_BUF_QUEUE,
                               (void *)&frame_idx,
                               NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_config_channel_notify
 *
 * DESCRIPTION: configures the channel notification mode
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *   @notify_mode  : notification mode
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_config_channel_notify(mm_camera_obj_t *my_obj,
                                        uint32_t ch_id,
                                        mm_camera_super_buf_notify_mode_t notify_mode)
{
    int32_t rc = -1;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        rc = mm_channel_fsm_fn(ch_obj,
                               MM_CHANNEL_EVT_CONFIG_NOTIFY_MODE,
                               (void *)&notify_mode,
                               NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_set_stream_parms
 *
 * DESCRIPTION: set parameters per stream
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *   @s_id         : stream handle
 *   @parms        : ptr to a param struct to be set to server
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * NOTE       : Assume the parms struct buf is already mapped to server via
 *              domain socket. Corresponding fields of parameters to be set
 *              are already filled in by upper layer caller.
 *==========================================================================*/
int32_t mm_camera_set_stream_parms(mm_camera_obj_t *my_obj,
                                   uint32_t ch_id,
                                   uint32_t s_id,
                                   cam_stream_parm_buffer_t *parms)
{
    int32_t rc = -1;
    mm_evt_paylod_set_get_stream_parms_t payload;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        memset(&payload, 0, sizeof(payload));
        payload.stream_id = s_id;
        payload.parms = parms;

        rc = mm_channel_fsm_fn(ch_obj,
                               MM_CHANNEL_EVT_SET_STREAM_PARM,
                               (void *)&payload,
                               NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_get_stream_parms
 *
 * DESCRIPTION: get parameters per stream
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *   @s_id         : stream handle
 *   @parms        : ptr to a param struct to be get from server
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * NOTE       : Assume the parms struct buf is already mapped to server via
 *              domain socket. Parameters to be get from server are already
 *              filled in by upper layer caller. After this call, corresponding
 *              fields of requested parameters will be filled in by server with
 *              detailed information.
 *==========================================================================*/
int32_t mm_camera_get_stream_parms(mm_camera_obj_t *my_obj,
                                   uint32_t ch_id,
                                   uint32_t s_id,
                                   cam_stream_parm_buffer_t *parms)
{
    int32_t rc = -1;
    mm_evt_paylod_set_get_stream_parms_t payload;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        memset(&payload, 0, sizeof(payload));
        payload.stream_id = s_id;
        payload.parms = parms;

        rc = mm_channel_fsm_fn(ch_obj,
                               MM_CHANNEL_EVT_GET_STREAM_PARM,
                               (void *)&payload,
                               NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_do_stream_action
 *
 * DESCRIPTION: request server to perform stream based action. Maybe removed later
 *              if the functionality is included in mm_camera_set_parms
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *   @s_id         : stream handle
 *   @actions      : ptr to an action struct buf to be performed by server
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * NOTE       : Assume the action struct buf is already mapped to server via
 *              domain socket. Actions to be performed by server are already
 *              filled in by upper layer caller.
 *==========================================================================*/
int32_t mm_camera_do_stream_action(mm_camera_obj_t *my_obj,
                                   uint32_t ch_id,
                                   uint32_t stream_id,
                                   void *actions)
{
    int32_t rc = -1;
    mm_evt_paylod_do_stream_action_t payload;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        memset(&payload, 0, sizeof(payload));
        payload.stream_id = stream_id;
        payload.actions = actions;

        rc = mm_channel_fsm_fn(ch_obj,
                               MM_CHANNEL_EVT_DO_STREAM_ACTION,
                               (void*)&payload,
                               NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_map_stream_buf
 *
 * DESCRIPTION: mapping stream buffer via domain socket to server
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *   @s_id         : stream handle
 *   @buf_type     : type of buffer to be mapped. could be following values:
 *                   CAM_MAPPING_BUF_TYPE_STREAM_BUF
 *                   CAM_MAPPING_BUF_TYPE_STREAM_INFO
 *                   CAM_MAPPING_BUF_TYPE_OFFLINE_INPUT_BUF
 *   @buf_idx      : index of buffer within the stream buffers, only valid if
 *                   buf_type is CAM_MAPPING_BUF_TYPE_STREAM_BUF or
 *                   CAM_MAPPING_BUF_TYPE_OFFLINE_INPUT_BUF
 *   @plane_idx    : plane index. If all planes share the same fd,
 *                   plane_idx = -1; otherwise, plean_idx is the
 *                   index to plane (0..num_of_planes)
 *   @fd           : file descriptor of the buffer
 *   @size         : size of the buffer
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_map_stream_buf(mm_camera_obj_t *my_obj,
                                 uint32_t ch_id,
                                 uint32_t stream_id,
                                 uint8_t buf_type,
                                 uint32_t buf_idx,
                                 int32_t plane_idx,
                                 int fd,
                                 size_t size)
{
    int32_t rc = -1;
    cam_buf_map_type payload;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        memset(&payload, 0, sizeof(payload));
        payload.stream_id = stream_id;
        payload.type = buf_type;
        payload.frame_idx = buf_idx;
        payload.plane_idx = plane_idx;
        payload.fd = fd;
        payload.size = size;
        rc = mm_channel_fsm_fn(ch_obj,
                               MM_CHANNEL_EVT_MAP_STREAM_BUF,
                               (void*)&payload,
                               NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_map_stream_bufs
 *
 * DESCRIPTION: mapping stream buffers via domain socket to server
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *   @buf_map_list : list of buffers to be mapped
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_map_stream_bufs(mm_camera_obj_t *my_obj,
                                  uint32_t ch_id,
                                  const cam_buf_map_type_list *buf_map_list)
{
    int32_t rc = -1;
    cam_buf_map_type_list payload;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        memcpy(&payload, buf_map_list, sizeof(payload));
        rc = mm_channel_fsm_fn(ch_obj,
                               MM_CHANNEL_EVT_MAP_STREAM_BUFS,
                               (void*)&payload,
                               NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_unmap_stream_buf
 *
 * DESCRIPTION: unmapping stream buffer via domain socket to server
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
 *   @s_id         : stream handle
 *   @buf_type     : type of buffer to be mapped. could be following values:
 *                   CAM_MAPPING_BUF_TYPE_STREAM_BUF
 *                   CAM_MAPPING_BUF_TYPE_STREAM_INFO
 *                   CAM_MAPPING_BUF_TYPE_OFFLINE_INPUT_BUF
 *   @buf_idx      : index of buffer within the stream buffers, only valid if
 *                   buf_type is CAM_MAPPING_BUF_TYPE_STREAM_BUF or
 *                   CAM_MAPPING_BUF_TYPE_OFFLINE_INPUT_BUF
 *   @plane_idx    : plane index. If all planes share the same fd,
 *                   plane_idx = -1; otherwise, plean_idx is the
 *                   index to plane (0..num_of_planes)
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_unmap_stream_buf(mm_camera_obj_t *my_obj,
                                   uint32_t ch_id,
                                   uint32_t stream_id,
                                   uint8_t buf_type,
                                   uint32_t buf_idx,
                                   int32_t plane_idx)
{
    int32_t rc = -1;
    cam_buf_unmap_type payload;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        memset(&payload, 0, sizeof(payload));
        payload.stream_id = stream_id;
        payload.type = buf_type;
        payload.frame_idx = buf_idx;
        payload.plane_idx = plane_idx;
        rc = mm_channel_fsm_fn(ch_obj,
                               MM_CHANNEL_EVT_UNMAP_STREAM_BUF,
                               (void*)&payload,
                               NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_evt_sub
 *
 * DESCRIPTION: subscribe/unsubscribe event notify from kernel
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @reg_flag     : 1 -- subscribe ; 0 -- unsubscribe
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_evt_sub(mm_camera_obj_t * my_obj,
                          uint8_t reg_flag)
{
    int32_t rc = 0;
    struct v4l2_event_subscription sub;

    memset(&sub, 0, sizeof(sub));
    sub.type = MSM_CAMERA_V4L2_EVENT_TYPE;
    sub.id = MSM_CAMERA_MSM_NOTIFY;
    if(FALSE == reg_flag) {
        /* unsubscribe */
        rc = ioctl(my_obj->ctrl_fd, VIDIOC_UNSUBSCRIBE_EVENT, &sub);
        if (rc < 0) {
            LOGE("unsubscribe event rc = %d, errno %d",
                     rc, errno);
            return rc;
        }
        /* remove evt fd from the polling thraed when unreg the last event */
        rc = mm_camera_poll_thread_del_poll_fd(&my_obj->evt_poll_thread,
                                               my_obj->my_hdl,
                                               mm_camera_sync_call);
    } else {
        rc = ioctl(my_obj->ctrl_fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
        if (rc < 0) {
            LOGE("subscribe event rc = %d, errno %d",
             rc, errno);
            return rc;
        }
        /* add evt fd to polling thread when subscribe the first event */
        rc = mm_camera_poll_thread_add_poll_fd(&my_obj->evt_poll_thread,
                                               my_obj->my_hdl,
                                               my_obj->ctrl_fd,
                                               mm_camera_event_notify,
                                               (void*)my_obj,
                                               mm_camera_sync_call);
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_util_wait_for_event
 *
 * DESCRIPTION: utility function to wait for certain events
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @evt_mask     : mask for events to be waited. Any of event in the mask would
 *                   trigger the wait to end
 *   @status       : status of the event
 *
 * RETURN     : none
 *==========================================================================*/
void mm_camera_util_wait_for_event(mm_camera_obj_t *my_obj,
                                   uint32_t evt_mask,
                                   uint32_t *status)
{
    int32_t rc = 0;
    struct timespec ts;

    pthread_mutex_lock(&my_obj->evt_lock);
    while (!(my_obj->evt_rcvd.server_event_type & evt_mask)) {
        clock_gettime(CLOCK_REALTIME, &ts);
        ts.tv_sec += WAIT_TIMEOUT;
        rc = pthread_cond_timedwait(&my_obj->evt_cond, &my_obj->evt_lock, &ts);
        if (rc) {
            LOGE("pthread_cond_timedwait of evt_mask 0x%x failed %d",
                     evt_mask, rc);
            break;
        }
    }
    if (!rc) {
        *status = my_obj->evt_rcvd.status;
    } else {
        *status = MSM_CAMERA_STATUS_FAIL;
    }
    /* reset local storage for recieved event for next event */
    memset(&my_obj->evt_rcvd, 0, sizeof(mm_camera_event_t));
    pthread_mutex_unlock(&my_obj->evt_lock);
}

/*===========================================================================
 * FUNCTION   : mm_camera_util_bundled_sendmsg
 *
 * DESCRIPTION: utility function to send bundled msg via domain socket
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @msg          : message to be sent
 *   @buf_size     : size of the message to be sent
 *   @sendfds      : array of file descriptors to be sent
 *   @numfds       : number of file descriptors to be sent
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_util_bundled_sendmsg(mm_camera_obj_t *my_obj,
                                       void *msg,
                                       size_t buf_size,
                                       int sendfds[CAM_MAX_NUM_BUFS_PER_STREAM],
                                       int numfds)
{
    int32_t rc = -1;
    uint32_t status;

    /* need to lock msg_lock, since sendmsg until response back is deemed as one operation*/
    pthread_mutex_lock(&my_obj->msg_lock);
    if(mm_camera_socket_bundle_sendmsg(my_obj->ds_fd, msg, buf_size, sendfds, numfds) > 0) {
        /* wait for event that mapping/unmapping is done */
        mm_camera_util_wait_for_event(my_obj, CAM_EVENT_TYPE_MAP_UNMAP_DONE, &status);
        if (MSM_CAMERA_STATUS_SUCCESS == status) {
            rc = 0;
        }
    }
    pthread_mutex_unlock(&my_obj->msg_lock);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_util_sendmsg
 *
 * DESCRIPTION: utility function to send msg via domain socket
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @msg          : message to be sent
 *   @buf_size     : size of the message to be sent
 *   @sendfd       : >0 if any file descriptor need to be passed across process
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_util_sendmsg(mm_camera_obj_t *my_obj,
                               void *msg,
                               size_t buf_size,
                               int sendfd)
{
    int32_t rc = -1;
    uint32_t status;

    /* need to lock msg_lock, since sendmsg until reposonse back is deemed as one operation*/
    pthread_mutex_lock(&my_obj->msg_lock);
    if(mm_camera_socket_sendmsg(my_obj->ds_fd, msg, buf_size, sendfd) > 0) {
        /* wait for event that mapping/unmapping is done */
        mm_camera_util_wait_for_event(my_obj, CAM_EVENT_TYPE_MAP_UNMAP_DONE, &status);
        if (MSM_CAMERA_STATUS_SUCCESS == status) {
            rc = 0;
        }
    }
    pthread_mutex_unlock(&my_obj->msg_lock);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_map_buf
 *
 * DESCRIPTION: mapping camera buffer via domain socket to server
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @buf_type     : type of buffer to be mapped. could be following values:
 *                   CAM_MAPPING_BUF_TYPE_CAPABILITY
 *                   CAM_MAPPING_BUF_TYPE_SETPARM_BUF
 *                   CAM_MAPPING_BUF_TYPE_GETPARM_BUF
 *   @fd           : file descriptor of the buffer
 *   @size         : size of the buffer
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_map_buf(mm_camera_obj_t *my_obj,
                          uint8_t buf_type,
                          int fd,
                          size_t size)
{
    int32_t rc = 0;
    cam_sock_packet_t packet;
    memset(&packet, 0, sizeof(cam_sock_packet_t));
    packet.msg_type = CAM_MAPPING_TYPE_FD_MAPPING;
    packet.payload.buf_map.type = buf_type;
    packet.payload.buf_map.fd = fd;
    packet.payload.buf_map.size = size;
    rc = mm_camera_util_sendmsg(my_obj,
                                &packet,
                                sizeof(cam_sock_packet_t),
                                fd);
    pthread_mutex_unlock(&my_obj->cam_lock);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_map_bufs
 *
 * DESCRIPTION: mapping camera buffers via domain socket to server
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @buf_map_list : list of buffers to be mapped
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_map_bufs(mm_camera_obj_t *my_obj,
                           const cam_buf_map_type_list* buf_map_list)
{
    int32_t rc = 0;
    cam_sock_packet_t packet;
    memset(&packet, 0, sizeof(cam_sock_packet_t));
    packet.msg_type = CAM_MAPPING_TYPE_FD_BUNDLED_MAPPING;

    memcpy(&packet.payload.buf_map_list, buf_map_list,
           sizeof(packet.payload.buf_map_list));

    int sendfds[CAM_MAX_NUM_BUFS_PER_STREAM];
    uint32_t numbufs = packet.payload.buf_map_list.length;
    uint32_t i;
    for (i = 0; i < numbufs; i++) {
        sendfds[i] = packet.payload.buf_map_list.buf_maps[i].fd;
    }

    for (i = numbufs; i < CAM_MAX_NUM_BUFS_PER_STREAM; i++) {
        packet.payload.buf_map_list.buf_maps[i].fd = -1;
        sendfds[i] = -1;
    }

    rc = mm_camera_util_bundled_sendmsg(my_obj,
                                        &packet,
                                        sizeof(cam_sock_packet_t),
                                        sendfds,
                                        numbufs);

    pthread_mutex_unlock(&my_obj->cam_lock);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_unmap_buf
 *
 * DESCRIPTION: unmapping camera buffer via domain socket to server
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @buf_type     : type of buffer to be mapped. could be following values:
 *                   CAM_MAPPING_BUF_TYPE_CAPABILITY
 *                   CAM_MAPPING_BUF_TYPE_SETPARM_BUF
 *                   CAM_MAPPING_BUF_TYPE_GETPARM_BUF
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_unmap_buf(mm_camera_obj_t *my_obj,
                            uint8_t buf_type)
{
    int32_t rc = 0;
    cam_sock_packet_t packet;
    memset(&packet, 0, sizeof(cam_sock_packet_t));
    packet.msg_type = CAM_MAPPING_TYPE_FD_UNMAPPING;
    packet.payload.buf_unmap.type = buf_type;
    rc = mm_camera_util_sendmsg(my_obj,
                                &packet,
                                sizeof(cam_sock_packet_t),
                                -1);
    pthread_mutex_unlock(&my_obj->cam_lock);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_util_s_ctrl
 *
 * DESCRIPTION: utility function to send v4l2 ioctl for s_ctrl
 *
 * PARAMETERS :
 *   @fd      : file descritpor for sending ioctl
 *   @id      : control id
 *   @value   : value of the ioctl to be sent
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_util_s_ctrl(int32_t fd,  uint32_t id, int32_t *value)
{
    int rc = 0;
    struct v4l2_control control;

    memset(&control, 0, sizeof(control));
    control.id = id;
    if (value != NULL) {
        control.value = *value;
    }
    rc = ioctl(fd, VIDIOC_S_CTRL, &control);

    LOGD("fd=%d, S_CTRL, id=0x%x, value = %p, rc = %d\n",
          fd, id, value, rc);
    if (rc < 0) {
        LOGE("ioctl failed %d, errno %d", rc, errno);
    } else if (value != NULL) {
        *value = control.value;
    }
    return (rc >= 0)? 0 : -1;
}

/*===========================================================================
 * FUNCTION   : mm_camera_util_g_ctrl
 *
 * DESCRIPTION: utility function to send v4l2 ioctl for g_ctrl
 *
 * PARAMETERS :
 *   @fd      : file descritpor for sending ioctl
 *   @id      : control id
 *   @value   : value of the ioctl to be sent
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_util_g_ctrl( int32_t fd, uint32_t id, int32_t *value)
{
    int rc = 0;
    struct v4l2_control control;

    memset(&control, 0, sizeof(control));
    control.id = id;
    if (value != NULL) {
        control.value = *value;
    }
    rc = ioctl(fd, VIDIOC_G_CTRL, &control);
    LOGD("fd=%d, G_CTRL, id=0x%x, rc = %d\n", fd, id, rc);
    if (value != NULL) {
        *value = control.value;
    }
    return (rc >= 0)? 0 : -1;
}

/*===========================================================================
 * FUNCTION   : mm_camera_channel_advanced_capture
 *
 * DESCRIPTION: sets the channel advanced capture
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @ch_id        : channel handle
  *   @type : advanced capture type.
 *   @start_flag  : flag to indicate start/stop
  *   @in_value  : input configaration
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_camera_channel_advanced_capture(mm_camera_obj_t *my_obj,
            uint32_t ch_id, mm_camera_advanced_capture_t type,
            uint32_t trigger, void *in_value)
{
    LOGD("E type = %d", type);
    int32_t rc = -1;
    mm_channel_t * ch_obj =
        mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);
        switch (type) {
            case MM_CAMERA_AF_BRACKETING:
                rc = mm_channel_fsm_fn(ch_obj,
                                       MM_CHANNEL_EVT_AF_BRACKETING,
                                       (void *)&trigger,
                                       NULL);
                break;
            case MM_CAMERA_AE_BRACKETING:
                rc = mm_channel_fsm_fn(ch_obj,
                                       MM_CHANNEL_EVT_AE_BRACKETING,
                                       (void *)&trigger,
                                       NULL);
                break;
            case MM_CAMERA_FLASH_BRACKETING:
                rc = mm_channel_fsm_fn(ch_obj,
                                       MM_CHANNEL_EVT_FLASH_BRACKETING,
                                       (void *)&trigger,
                                       NULL);
                break;
            case MM_CAMERA_ZOOM_1X:
                rc = mm_channel_fsm_fn(ch_obj,
                                       MM_CHANNEL_EVT_ZOOM_1X,
                                       (void *)&trigger,
                                       NULL);
                break;
            case MM_CAMERA_FRAME_CAPTURE:
                rc = mm_channel_fsm_fn(ch_obj,
                                       MM_CAMERA_EVT_CAPTURE_SETTING,
                                       (void *)in_value,
                                       NULL);
                break;
            default:
                break;
        }

    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }

    LOGD("X");
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_get_session_id
 *
 * DESCRIPTION: get the session identity
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @sessionid: pointer to the output session id
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * NOTE       : if this call succeeds, we will get a valid session id
 *==========================================================================*/
int32_t mm_camera_get_session_id(mm_camera_obj_t *my_obj,
        uint32_t* sessionid)
{
    int32_t rc = -1;
    int32_t value = 0;
    if(sessionid != NULL) {
        rc = mm_camera_util_g_ctrl(my_obj->ctrl_fd,
                MSM_CAMERA_PRIV_G_SESSION_ID, &value);
        LOGD("fd=%d, get_session_id, id=0x%x, value = %d, rc = %d\n",
                 my_obj->ctrl_fd, MSM_CAMERA_PRIV_G_SESSION_ID,
                value, rc);
        *sessionid = value;
        my_obj->sessionid = value;
    }

    pthread_mutex_unlock(&my_obj->cam_lock);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_sync_related_sensors
 *
 * DESCRIPTION: send sync cmd
 *
 * PARAMETERS :
 *   @my_obj       : camera object
 *   @parms        : ptr to the related cam info to be sent to server
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * NOTE       : Assume the sync struct buf is already mapped to server via
 *              domain socket. Corresponding fields of parameters to be set
 *              are already filled in by upper layer caller.
 *==========================================================================*/
int32_t mm_camera_sync_related_sensors(mm_camera_obj_t *my_obj,
        cam_sync_related_sensors_event_info_t* parms)
{
    int32_t rc = -1;
    int32_t value = 0;
    if (parms !=  NULL) {
        rc = mm_camera_util_s_ctrl(my_obj->ctrl_fd,
                CAM_PRIV_SYNC_RELATED_SENSORS, &value);
    }
    pthread_mutex_unlock(&my_obj->cam_lock);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_camera_reg_stream_buf_cb
 *
 * DESCRIPTION: Register callback for stream buffer
 *
 * PARAMETERS :
 *   @my_obj    : camera object
 *   @ch_id     : channel handle
 *   @stream_id : stream that will be linked
 *   @buf_cb    : special callback needs to be registered for stream buffer
 *   @cb_type   : Callback type SYNC/ASYNC
 *   @userdata  : user data pointer
 *
 * RETURN    : int32_t type of status
 *             0  -- success
 *             1 --  failure
 *==========================================================================*/
int32_t mm_camera_reg_stream_buf_cb(mm_camera_obj_t *my_obj,
        uint32_t ch_id, uint32_t stream_id, mm_camera_buf_notify_t stream_cb,
        mm_camera_stream_cb_type cb_type, void *userdata)
{
    int rc = 0;
    mm_stream_data_cb_t buf_cb;
    mm_channel_t * ch_obj =
            mm_camera_util_get_channel_by_handler(my_obj, ch_id);

    if (NULL != ch_obj) {
        pthread_mutex_lock(&ch_obj->ch_lock);
        pthread_mutex_unlock(&my_obj->cam_lock);

        memset(&buf_cb, 0, sizeof(mm_stream_data_cb_t));
        buf_cb.cb = stream_cb;
        buf_cb.cb_count = -1;
        buf_cb.cb_type = cb_type;
        buf_cb.user_data = userdata;

        mm_evt_paylod_reg_stream_buf_cb payload;
        memset(&payload, 0, sizeof(mm_evt_paylod_reg_stream_buf_cb));
        payload.buf_cb = buf_cb;
        payload.stream_id = stream_id;
        mm_channel_fsm_fn(ch_obj,
                MM_CHANNEL_EVT_REG_STREAM_BUF_CB,
                (void*)&payload, NULL);
    } else {
        pthread_mutex_unlock(&my_obj->cam_lock);
    }
    return rc;
}

#ifdef QCAMERA_REDEFINE_LOG

/*===========================================================================
 * DESCRIPTION: mm camera debug interface
 *
 *==========================================================================*/
pthread_mutex_t dbg_log_mutex;

#undef LOG_TAG
#define LOG_TAG "QCamera"
#define CDBG_MAX_STR_LEN 1024
#define CDBG_MAX_LINE_LENGTH 256

/* current trace loggin permissions
   * {NONE, ERR, WARN, HIGH, DEBUG, LOW, INFO} */
int g_cam_log[CAM_LAST_MODULE][CAM_GLBL_DBG_INFO + 1] = {
    {0, 1, 0, 0, 0, 0, 1}, /* CAM_NO_MODULE     */
    {0, 1, 0, 0, 0, 0, 1}, /* CAM_HAL_MODULE    */
    {0, 1, 0, 0, 0, 0, 1}, /* CAM_MCI_MODULE    */
    {0, 1, 0, 0, 0, 0, 1}, /* CAM_JPEG_MODULE   */
};

/* string representation for logging level */
static const char *cam_dbg_level_to_str[] = {
     "",        /* CAM_GLBL_DBG_NONE  */
     "<ERROR>", /* CAM_GLBL_DBG_ERR   */
     "<WARN>", /* CAM_GLBL_DBG_WARN  */
     "<HIGH>", /* CAM_GLBL_DBG_HIGH  */
     "<DBG>", /* CAM_GLBL_DBG_DEBUG */
     "<LOW>", /* CAM_GLBL_DBG_LOW   */
     "<INFO>"  /* CAM_GLBL_DBG_INFO  */
};

/* current trace logging configuration */
typedef struct {
   cam_global_debug_level_t  level;
   int                       initialized;
   const char               *name;
   const char               *prop;
} module_debug_t;

static module_debug_t cam_loginfo[(int)CAM_LAST_MODULE] = {
  {CAM_GLBL_DBG_ERR, 1,
      "",         "persist.camera.global.debug"     }, /* CAM_NO_MODULE     */
  {CAM_GLBL_DBG_ERR, 1,
      "<HAL>", "persist.camera.hal.debug"        }, /* CAM_HAL_MODULE    */
  {CAM_GLBL_DBG_ERR, 1,
      "<MCI>", "persist.camera.mci.debug"        }, /* CAM_MCI_MODULE    */
  {CAM_GLBL_DBG_ERR, 1,
      "<JPEG>", "persist.camera.mmstill.logs"     }, /* CAM_JPEG_MODULE   */
};

/** cam_get_dbg_level
 *
 *    @module: module name
 *    @level:  module debug logging level
 *
 *  Maps debug log string to value.
 *
 *  Return: logging level
 **/
__unused
static cam_global_debug_level_t cam_get_dbg_level(const char *module,
  char *pValue) {

  cam_global_debug_level_t rc = CAM_GLBL_DBG_NONE;

  if (!strcmp(pValue, "none")) {
    rc = CAM_GLBL_DBG_NONE;
  } else if (!strcmp(pValue, "warn")) {
    rc = CAM_GLBL_DBG_WARN;
  } else if (!strcmp(pValue, "debug")) {
    rc = CAM_GLBL_DBG_DEBUG;
  } else if (!strcmp(pValue, "error")) {
    rc = CAM_GLBL_DBG_ERR;
  } else if (!strcmp(pValue, "low")) {
    rc = CAM_GLBL_DBG_LOW;
  } else if (!strcmp(pValue, "high")) {
    rc = CAM_GLBL_DBG_HIGH;
  } else if (!strcmp(pValue, "info")) {
    rc = CAM_GLBL_DBG_INFO;
  } else {
    ALOGE("Invalid %s debug log level %s\n", module, pValue);
  }

  ALOGD("%s debug log level: %s\n", module, cam_dbg_level_to_str[rc]);

  return rc;
}

/** cam_vsnprintf
 *    @pdst:   destination buffer pointer
 *    @size:   size of destination b uffer
 *    @pfmt:   string format
 *    @argptr: variabkle length argument list
 *
 *  Processes variable length argument list to a formatted string.
 *
 *  Return: n/a
 **/
static void cam_vsnprintf(char* pdst, unsigned int size,
                          const char* pfmt, va_list argptr) {
  int num_chars_written = 0;

  pdst[0] = '\0';
  num_chars_written = vsnprintf(pdst, size, pfmt, argptr);

  if ((num_chars_written >= (int)size) && (size > 0)) {
     /* Message length exceeds the buffer limit size */
     num_chars_written = size - 1;
     pdst[size - 1] = '\0';
  }
}

/** mm_camera_debug_log
 *    @module: origin or log message
 *    @level:  logging level
 *    @func:   caller function name
 *    @line:   caller line number
 *    @fmt:    log message formatting string
 *    @...:    variable argument list
 *
 *  Generig logger method.
 *
 *  Return: N/A
 **/
void mm_camera_debug_log(const cam_modules_t module,
                   const cam_global_debug_level_t level,
                   const char *func, const int line, const char *fmt, ...) {
  char    str_buffer[CDBG_MAX_STR_LEN];
  va_list args;

  va_start(args, fmt);
  cam_vsnprintf(str_buffer, CDBG_MAX_STR_LEN, fmt, args);
  va_end(args);

  switch (level) {
  case CAM_GLBL_DBG_WARN:
    ALOGW("%s%s %s: %d: %s", cam_loginfo[module].name,
      cam_dbg_level_to_str[level], func, line, str_buffer);
    break;
  case CAM_GLBL_DBG_ERR:
    ALOGE("%s%s %s: %d: %s", cam_loginfo[module].name,
      cam_dbg_level_to_str[level], func, line, str_buffer);
    break;
  case CAM_GLBL_DBG_INFO:
    ALOGI("%s%s %s: %d: %s", cam_loginfo[module].name,
      cam_dbg_level_to_str[level], func, line, str_buffer);
    break;
  case CAM_GLBL_DBG_HIGH:
  case CAM_GLBL_DBG_DEBUG:
  case CAM_GLBL_DBG_LOW:
  default:
    ALOGD("%s%s %s: %d: %s", cam_loginfo[module].name,
      cam_dbg_level_to_str[level], func, line, str_buffer);
  }
}

 /** mm_camera_set_dbg_log_properties
 *
 *  Set global and module log level properties.
 *
 *  Return: N/A
 **/
void mm_camera_set_dbg_log_properties(void) {
  int          i;
  unsigned int j;
  static int   boot_init = 1;
  char         property_value[PROPERTY_VALUE_MAX] = {0};
  char         default_value[PROPERTY_VALUE_MAX]  = {0};

  if (boot_init) {
      boot_init = 0;
      pthread_mutex_init(&dbg_log_mutex, 0);
  }

  /* set global and individual module logging levels */
  pthread_mutex_lock(&dbg_log_mutex);
  for (i = CAM_NO_MODULE; i < CAM_LAST_MODULE; i++) {
    cam_global_debug_level_t log_level;
    snprintf(default_value, PROPERTY_VALUE_MAX, "%d", (int)cam_loginfo[i].level);
    property_get(cam_loginfo[i].prop, property_value, default_value);
    log_level = (cam_global_debug_level_t)atoi(property_value);

    /* fix KW warnings */
    if (log_level > CAM_GLBL_DBG_INFO) {
       log_level = CAM_GLBL_DBG_INFO;
    }

    cam_loginfo[i].level = log_level;

    /* The logging macros will produce a log message when logging level for
     * a module is less or equal to the level specified in the property for
     * the module, or less or equal the level specified by the global logging
     * property. Currently we don't allow INFO logging to be turned off */
    for (j = CAM_GLBL_DBG_ERR; j <= CAM_GLBL_DBG_LOW; j++) {
      g_cam_log[i][j] = (cam_loginfo[CAM_NO_MODULE].level != CAM_GLBL_DBG_NONE)     &&
                        (cam_loginfo[i].level             != CAM_GLBL_DBG_NONE)     &&
                        ((j                                <= cam_loginfo[i].level) ||
                         (j                                <= cam_loginfo[CAM_NO_MODULE].level));
    }
  }
  pthread_mutex_unlock(&dbg_log_mutex);
}

#endif