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

// System dependencies
#include <pthread.h>
#include <fcntl.h>

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

extern mm_camera_obj_t* mm_camera_util_get_camera_by_handler(uint32_t cam_handler);
extern mm_channel_t * mm_camera_util_get_channel_by_handler(mm_camera_obj_t * cam_obj,
                                                            uint32_t handler);
/* Static frame sync info used between different camera channels*/
static mm_channel_frame_sync_info_t fs = { .num_cam =0, .pos = 0};
/* Frame sync info access lock */
static pthread_mutex_t fs_lock = PTHREAD_MUTEX_INITIALIZER;

/* internal function declare goes here */
int32_t mm_channel_qbuf(mm_channel_t *my_obj,
                        mm_camera_buf_def_t *buf);
int32_t mm_channel_init(mm_channel_t *my_obj,
                        mm_camera_channel_attr_t *attr,
                        mm_camera_buf_notify_t channel_cb,
                        void *userdata);
void mm_channel_release(mm_channel_t *my_obj);
uint32_t mm_channel_add_stream(mm_channel_t *my_obj);
int32_t mm_channel_del_stream(mm_channel_t *my_obj,
                                   uint32_t stream_id);
uint32_t mm_channel_link_stream(mm_channel_t *my_obj,
        mm_camera_stream_link_t *stream_link);
int32_t mm_channel_config_stream(mm_channel_t *my_obj,
                                 uint32_t stream_id,
                                 mm_camera_stream_config_t *config);
int32_t mm_channel_get_bundle_info(mm_channel_t *my_obj,
                                   cam_bundle_config_t *bundle_info);
int32_t mm_channel_start(mm_channel_t *my_obj);
int32_t mm_channel_stop(mm_channel_t *my_obj);
int32_t mm_channel_request_super_buf(mm_channel_t *my_obj,
        mm_camera_req_buf_t *buf);
int32_t mm_channel_cancel_super_buf_request(mm_channel_t *my_obj);
int32_t mm_channel_flush_super_buf_queue(mm_channel_t *my_obj,
                                         uint32_t frame_idx,
                                         cam_stream_type_t stream_type);
int32_t mm_channel_config_notify_mode(mm_channel_t *my_obj,
                                      mm_camera_super_buf_notify_mode_t notify_mode);
int32_t mm_channel_start_zsl_snapshot(mm_channel_t *my_obj);
int32_t mm_channel_stop_zsl_snapshot(mm_channel_t *my_obj);
int32_t mm_channel_superbuf_flush(mm_channel_t* my_obj,
        mm_channel_queue_t * queue, cam_stream_type_t cam_type);
int32_t mm_channel_set_stream_parm(mm_channel_t *my_obj,
                                   mm_evt_paylod_set_get_stream_parms_t *payload);
int32_t mm_channel_get_queued_buf_count(mm_channel_t *my_obj,
        uint32_t stream_id);

int32_t mm_channel_get_stream_parm(mm_channel_t *my_obj,
                                   mm_evt_paylod_set_get_stream_parms_t *payload);
int32_t mm_channel_do_stream_action(mm_channel_t *my_obj,
                                    mm_evt_paylod_do_stream_action_t *payload);
int32_t mm_channel_map_stream_buf(mm_channel_t *my_obj,
                                  cam_buf_map_type *payload);
int32_t mm_channel_map_stream_bufs(mm_channel_t *my_obj,
                                   cam_buf_map_type_list *payload);
int32_t mm_channel_unmap_stream_buf(mm_channel_t *my_obj,
                                    cam_buf_unmap_type *payload);

/* state machine function declare */
int32_t mm_channel_fsm_fn_notused(mm_channel_t *my_obj,
                          mm_channel_evt_type_t evt,
                          void * in_val,
                          void * out_val);
int32_t mm_channel_fsm_fn_stopped(mm_channel_t *my_obj,
                          mm_channel_evt_type_t evt,
                          void * in_val,
                          void * out_val);
int32_t mm_channel_fsm_fn_active(mm_channel_t *my_obj,
                          mm_channel_evt_type_t evt,
                          void * in_val,
                          void * out_val);
int32_t mm_channel_fsm_fn_paused(mm_channel_t *my_obj,
                          mm_channel_evt_type_t evt,
                          void * in_val,
                          void * out_val);

/* channel super queue functions */
int32_t mm_channel_superbuf_queue_init(mm_channel_queue_t * queue);
int32_t mm_channel_superbuf_queue_deinit(mm_channel_queue_t * queue);
int32_t mm_channel_superbuf_comp_and_enqueue(mm_channel_t *ch_obj,
                                             mm_channel_queue_t * queue,
                                             mm_camera_buf_info_t *buf);
mm_channel_queue_node_t* mm_channel_superbuf_dequeue(
        mm_channel_queue_t * queue, mm_channel_t *ch_obj);
int32_t mm_channel_superbuf_bufdone_overflow(mm_channel_t *my_obj,
                                             mm_channel_queue_t *queue);
int32_t mm_channel_superbuf_skip(mm_channel_t *my_obj,
                                 mm_channel_queue_t *queue);

static int32_t mm_channel_proc_general_cmd(mm_channel_t *my_obj,
                                           mm_camera_generic_cmd_t *p_gen_cmd);
int32_t mm_channel_superbuf_flush_matched(mm_channel_t* my_obj,
                                          mm_channel_queue_t * queue);

/* Start of Frame Sync util methods */
void mm_frame_sync_reset();
int32_t mm_frame_sync_register_channel(mm_channel_t *ch_obj);
int32_t mm_frame_sync_unregister_channel(mm_channel_t *ch_obj);
int32_t mm_frame_sync_add(uint32_t frame_id, mm_channel_t *ch_obj);
int32_t mm_frame_sync_remove(uint32_t frame_id);
uint32_t mm_frame_sync_find_matched(uint8_t oldest);
int8_t mm_frame_sync_find_frame_index(uint32_t frame_id);
void mm_frame_sync_lock_queues();
void mm_frame_sync_unlock_queues();
void mm_channel_node_qbuf(mm_channel_t *ch_obj, mm_channel_queue_node_t *node);
/* End of Frame Sync Util methods */
void mm_channel_send_super_buf(mm_channel_node_info_t *info);
mm_channel_queue_node_t* mm_channel_superbuf_dequeue_frame_internal(
        mm_channel_queue_t * queue, uint32_t frame_idx);

/*===========================================================================
 * FUNCTION   : mm_channel_util_get_stream_by_handler
 *
 * DESCRIPTION: utility function to get a stream object from its handle
 *
 * PARAMETERS :
 *   @cam_obj: ptr to a channel object
 *   @handler: stream handle
 *
 * RETURN     : ptr to a stream object.
 *              NULL if failed.
 *==========================================================================*/
mm_stream_t * mm_channel_util_get_stream_by_handler(
                                    mm_channel_t * ch_obj,
                                    uint32_t handler)
{
    int i;
    mm_stream_t *s_obj = NULL;
    for(i = 0; i < MAX_STREAM_NUM_IN_BUNDLE; i++) {
        if ((MM_STREAM_STATE_NOTUSED != ch_obj->streams[i].state) &&
            (handler == ch_obj->streams[i].my_hdl)) {
            s_obj = &ch_obj->streams[i];
            break;
        }
    }
    return s_obj;
}

/*===========================================================================
 * FUNCTION   : mm_channel_dispatch_super_buf
 *
 * DESCRIPTION: dispatch super buffer of bundle to registered user
 *
 * PARAMETERS :
 *   @cmd_cb  : ptr storing matched super buf information
 *   @userdata: user data ptr
 *
 * RETURN     : none
 *==========================================================================*/
static void mm_channel_dispatch_super_buf(mm_camera_cmdcb_t *cmd_cb,
                                          void* user_data)
{
    mm_channel_t * my_obj = (mm_channel_t *)user_data;

    if (NULL == my_obj) {
        return;
    }

    if (MM_CAMERA_CMD_TYPE_SUPER_BUF_DATA_CB != cmd_cb->cmd_type) {
        LOGE("Wrong cmd_type (%d) for super buf dataCB",
                    cmd_cb->cmd_type);
        return;
    }

    if (my_obj->bundle.super_buf_notify_cb) {
        my_obj->bundle.super_buf_notify_cb(&cmd_cb->u.superbuf, my_obj->bundle.user_data);
    }
}

/*===========================================================================
 * FUNCTION   : mm_channel_process_stream_buf
 *
 * DESCRIPTION: handle incoming buffer from stream in a bundle. In this function,
 *              matching logic will be performed on incoming stream frames.
 *              Will depends on the bundle attribute, either storing matched frames
 *              in the superbuf queue, or sending matched superbuf frames to upper
 *              layer through registered callback.
 *
 * PARAMETERS :
 *   @cmd_cb  : ptr storing matched super buf information
 *   @userdata: user data ptr
 *
 * RETURN     : none
 *==========================================================================*/
static void mm_channel_process_stream_buf(mm_camera_cmdcb_t * cmd_cb,
                                          void *user_data)
{
    mm_camera_super_buf_notify_mode_t notify_mode;
    mm_channel_queue_node_t *node = NULL;
    mm_channel_t *ch_obj = (mm_channel_t *)user_data;
    uint32_t i = 0;
    /* Set expected frame id to a future frame idx, large enough to wait
    * for good_frame_idx_range, and small enough to still capture an image */
    uint8_t needStartZSL = FALSE;

    if (NULL == ch_obj) {
        return;
    }
    if (MM_CAMERA_CMD_TYPE_DATA_CB  == cmd_cb->cmd_type) {
        /* comp_and_enqueue */
        mm_channel_superbuf_comp_and_enqueue(
                        ch_obj,
                        &ch_obj->bundle.superbuf_queue,
                        &cmd_cb->u.buf);
    } else if (MM_CAMERA_CMD_TYPE_REQ_DATA_CB  == cmd_cb->cmd_type) {
        /* skip frames if needed */
        ch_obj->pending_cnt = cmd_cb->u.req_buf.num_buf_requested;
        ch_obj->pending_retro_cnt = cmd_cb->u.req_buf.num_retro_buf_requested;
        ch_obj->req_type = cmd_cb->u.req_buf.type;
        ch_obj->bWaitForPrepSnapshotDone = 0;

        LOGH("pending cnt (%d), retro count (%d)"
                "req_type (%d) is_primary (%d)",
                 ch_obj->pending_cnt, ch_obj->pending_retro_cnt,
                ch_obj->req_type, cmd_cb->u.req_buf.primary_only);
        if (!ch_obj->pending_cnt || (ch_obj->pending_retro_cnt > ch_obj->pending_cnt)) {
          ch_obj->pending_retro_cnt = ch_obj->pending_cnt;
        }
        if (ch_obj->pending_retro_cnt > 0) {
          LOGL("Resetting need Led Flash!!!");
          ch_obj->needLEDFlash = 0;
        }
        ch_obj->stopZslSnapshot = 0;
        ch_obj->unLockAEC = 0;

        mm_channel_superbuf_skip(ch_obj, &ch_obj->bundle.superbuf_queue);

    } else if (MM_CAMERA_CMD_TYPE_START_ZSL == cmd_cb->cmd_type) {
            ch_obj->manualZSLSnapshot = TRUE;
            mm_camera_start_zsl_snapshot(ch_obj->cam_obj);
    } else if (MM_CAMERA_CMD_TYPE_STOP_ZSL == cmd_cb->cmd_type) {
            ch_obj->manualZSLSnapshot = FALSE;
            mm_camera_stop_zsl_snapshot(ch_obj->cam_obj);
    } else if (MM_CAMERA_CMD_TYPE_CONFIG_NOTIFY == cmd_cb->cmd_type) {
           ch_obj->bundle.superbuf_queue.attr.notify_mode = cmd_cb->u.notify_mode;
    } else if (MM_CAMERA_CMD_TYPE_FLUSH_QUEUE  == cmd_cb->cmd_type) {
        ch_obj->bundle.superbuf_queue.expected_frame_id = cmd_cb->u.flush_cmd.frame_idx;
        mm_channel_superbuf_flush(ch_obj,
                &ch_obj->bundle.superbuf_queue, cmd_cb->u.flush_cmd.stream_type);
        cam_sem_post(&(ch_obj->cmd_thread.sync_sem));
        return;
    } else if (MM_CAMERA_CMD_TYPE_GENERAL == cmd_cb->cmd_type) {
        LOGH("MM_CAMERA_CMD_TYPE_GENERAL");
        switch (cmd_cb->u.gen_cmd.type) {
            case MM_CAMERA_GENERIC_CMD_TYPE_AE_BRACKETING:
            case MM_CAMERA_GENERIC_CMD_TYPE_AF_BRACKETING: {
                uint32_t start = cmd_cb->u.gen_cmd.payload[0];
                LOGI("MM_CAMERA_GENERIC_CMDTYPE_AF_BRACKETING %u",
                      start);
                mm_channel_superbuf_flush(ch_obj,
                        &ch_obj->bundle.superbuf_queue, CAM_STREAM_TYPE_DEFAULT);

                if (start) {
                    LOGH("need AE bracketing, start zsl snapshot");
                    ch_obj->bracketingState = MM_CHANNEL_BRACKETING_STATE_WAIT_GOOD_FRAME_IDX;
                } else {
                    ch_obj->bracketingState = MM_CHANNEL_BRACKETING_STATE_OFF;
                }
            }
                break;
            case MM_CAMERA_GENERIC_CMD_TYPE_FLASH_BRACKETING: {
                uint32_t start = cmd_cb->u.gen_cmd.payload[0];
                LOGI("MM_CAMERA_GENERIC_CMDTYPE_FLASH_BRACKETING %u",
                      start);
                mm_channel_superbuf_flush(ch_obj,
                        &ch_obj->bundle.superbuf_queue, CAM_STREAM_TYPE_DEFAULT);

                if (start) {
                    LOGH("need flash bracketing");
                    ch_obj->isFlashBracketingEnabled = TRUE;
                } else {
                    ch_obj->isFlashBracketingEnabled = FALSE;
                }
            }
                break;
            case MM_CAMERA_GENERIC_CMD_TYPE_ZOOM_1X: {
                uint32_t start = cmd_cb->u.gen_cmd.payload[0];
                LOGI("MM_CAMERA_GENERIC_CMD_TYPE_ZOOM_1X %u",
                      start);
                mm_channel_superbuf_flush(ch_obj,
                        &ch_obj->bundle.superbuf_queue, CAM_STREAM_TYPE_DEFAULT);

                if (start) {
                    LOGH("need zoom 1x frame");
                    ch_obj->isZoom1xFrameRequested = TRUE;
                } else {
                    ch_obj->isZoom1xFrameRequested = FALSE;
                }
            }
                break;
            case MM_CAMERA_GENERIC_CMD_TYPE_CAPTURE_SETTING: {
                uint32_t start = cmd_cb->u.gen_cmd.payload[0];
                LOGI("MM_CAMERA_GENERIC_CMD_TYPE_CAPTURE_SETTING %u num_batch = %d",
                      start, cmd_cb->u.gen_cmd.frame_config.num_batch);

                if (start) {
                    memset(&ch_obj->frameConfig, 0, sizeof(cam_capture_frame_config_t));
                    for (i = 0; i < cmd_cb->u.gen_cmd.frame_config.num_batch; i++) {
                        if (cmd_cb->u.gen_cmd.frame_config.configs[i].type
                                != CAM_CAPTURE_RESET) {
                            ch_obj->frameConfig.configs[
                                    ch_obj->frameConfig.num_batch] =
                                    cmd_cb->u.gen_cmd.frame_config.configs[i];
                            ch_obj->frameConfig.num_batch++;
                            LOGH("capture setting frame = %d type = %d",
                                    i,ch_obj->frameConfig.configs[
                                    ch_obj->frameConfig.num_batch].type);
                        }
                    }
                    LOGD("Capture setting Batch Count %d",
                              ch_obj->frameConfig.num_batch);
                    ch_obj->isConfigCapture = TRUE;
                } else {
                    ch_obj->isConfigCapture = FALSE;
                    memset(&ch_obj->frameConfig, 0, sizeof(cam_capture_frame_config_t));
                }
                ch_obj->cur_capture_idx = 0;
                memset(ch_obj->capture_frame_id, 0, sizeof(uint8_t) * MAX_CAPTURE_BATCH_NUM);
                break;
            }
            default:
                LOGE("Error: Invalid command");
                break;
        }
    }
    notify_mode = ch_obj->bundle.superbuf_queue.attr.notify_mode;

    /*Handle use case which does not need start ZSL even in unified case*/
    if ((ch_obj->pending_cnt > 0)
            && (ch_obj->isConfigCapture)
            && (ch_obj->manualZSLSnapshot == FALSE)
            && (ch_obj->startZSlSnapshotCalled == FALSE)) {
        needStartZSL = TRUE;
        for (i = ch_obj->cur_capture_idx;
                i < ch_obj->frameConfig.num_batch;
                i++) {
            cam_capture_type type = ch_obj->frameConfig.configs[i].type;
            if (((type == CAM_CAPTURE_FLASH) && (!ch_obj->needLEDFlash))
                    || ((type == CAM_CAPTURE_LOW_LIGHT) && (!ch_obj->needLowLightZSL))) {
                /*For flash and low light capture, start ZSL is triggered only if needed*/
                needStartZSL = FALSE;
                break;
            }
        }
    }

    if ((ch_obj->isConfigCapture)
            && (needStartZSL)) {
        for (i = ch_obj->cur_capture_idx;
                i < ch_obj->frameConfig.num_batch;
                i++) {
            ch_obj->capture_frame_id[i] =
                    ch_obj->bundle.superbuf_queue.expected_frame_id
                    + MM_CAMERA_MAX_FUTURE_FRAME_WAIT;
        }

        /* Need to Flush the queue and trigger frame config */
        mm_channel_superbuf_flush(ch_obj,
                &ch_obj->bundle.superbuf_queue, CAM_STREAM_TYPE_DEFAULT);
        LOGI("TRIGGER Start ZSL");
        mm_camera_start_zsl_snapshot(ch_obj->cam_obj);
        ch_obj->startZSlSnapshotCalled = TRUE;
        ch_obj->burstSnapNum = ch_obj->pending_cnt;
        ch_obj->bWaitForPrepSnapshotDone = 0;
    } else if ((ch_obj->pending_cnt > 0)
        && ((ch_obj->needLEDFlash == TRUE) ||
        (MM_CHANNEL_BRACKETING_STATE_OFF != ch_obj->bracketingState))
        && (ch_obj->manualZSLSnapshot == FALSE)
        && ch_obj->startZSlSnapshotCalled == FALSE) {

        LOGI("TRIGGER Start ZSL for Flash");
        mm_camera_start_zsl_snapshot(ch_obj->cam_obj);
        ch_obj->startZSlSnapshotCalled = TRUE;
        ch_obj->burstSnapNum = ch_obj->pending_cnt;
        ch_obj->bWaitForPrepSnapshotDone = 0;
    } else if (((ch_obj->pending_cnt == 0) || (ch_obj->stopZslSnapshot == 1))
            && (ch_obj->manualZSLSnapshot == FALSE)
            && (ch_obj->startZSlSnapshotCalled == TRUE)) {
        LOGI("TRIGGER Stop ZSL for cancel picture");
        mm_camera_stop_zsl_snapshot(ch_obj->cam_obj);
        // Unlock AEC
        ch_obj->startZSlSnapshotCalled = FALSE;
        ch_obj->needLEDFlash = FALSE;
        ch_obj->burstSnapNum = 0;
        ch_obj->stopZslSnapshot = 0;
        ch_obj->bWaitForPrepSnapshotDone = 0;
        ch_obj->unLockAEC = 1;
        ch_obj->bracketingState = MM_CHANNEL_BRACKETING_STATE_OFF;
        ch_obj->isConfigCapture = FALSE;
    }
    /* bufdone for overflowed bufs */
    mm_channel_superbuf_bufdone_overflow(ch_obj, &ch_obj->bundle.superbuf_queue);

    LOGD("Super Buffer received, pending_cnt=%d queue cnt = %d expected = %d",
            ch_obj->pending_cnt, ch_obj->bundle.superbuf_queue.match_cnt,
            ch_obj->bundle.superbuf_queue.expected_frame_id);

    /* dispatch frame if pending_cnt>0 or is in continuous streaming mode */
    while (((ch_obj->pending_cnt > 0) ||
             (MM_CAMERA_SUPER_BUF_NOTIFY_CONTINUOUS == notify_mode)) &&
             (!ch_obj->bWaitForPrepSnapshotDone)) {

        /* dequeue */
        mm_channel_node_info_t info;
        memset(&info, 0x0, sizeof(info));

        if (ch_obj->req_type == MM_CAMERA_REQ_FRAME_SYNC_BUF) {
            // Lock the Queues
            mm_frame_sync_lock_queues();
            uint32_t match_frame = mm_frame_sync_find_matched(FALSE);
            if (match_frame) {
                uint8_t j = 0;
                for (j = 0; j < MAX_NUM_CAMERA_PER_BUNDLE; j++) {
                    if (fs.ch_obj[j]) {
                        mm_channel_queue_t *ch_queue =
                                &fs.ch_obj[j]->bundle.superbuf_queue;
                        if (ch_queue == NULL) {
                            LOGW("Channel queue is NULL");
                            break;
                        }
                        node = mm_channel_superbuf_dequeue_frame_internal(
                                ch_queue, match_frame);
                        if (node != NULL) {
                            info.ch_obj[info.num_nodes] = fs.ch_obj[j];
                            info.node[info.num_nodes] = node;
                            info.num_nodes++;
                            LOGH("Added ch(%p) to node ,num nodes %d",
                                     fs.ch_obj[j], info.num_nodes);
                        }
                    }
                }
                mm_frame_sync_remove(match_frame);
                LOGI("match frame %d", match_frame);
                if (info.num_nodes != fs.num_cam) {
                    LOGI("num node %d != num cam (%d) Debug this",
                             info.num_nodes, fs.num_cam);
                    uint8_t j = 0;
                    // free super buffers from various nodes
                    for (j = 0; j < info.num_nodes; j++) {
                        if (info.node[j]) {
                            mm_channel_node_qbuf(info.ch_obj[j], info.node[j]);
                            free(info.node[j]);
                        }
                    }
                    // we should not use it as matched dual camera frames
                    info.num_nodes = 0;
                }
            }
            mm_frame_sync_unlock_queues();
        } else {
           node = mm_channel_superbuf_dequeue(&ch_obj->bundle.superbuf_queue, ch_obj);
           if (node != NULL) {
               if (ch_obj->isConfigCapture &&
                       ((node->frame_idx <
                        ch_obj->capture_frame_id[ch_obj->cur_capture_idx]))) {
                   uint8_t i;
                   LOGD("Not expected super buffer. frameID = %d expected = %d",
                           node->frame_idx, ch_obj->capture_frame_id[ch_obj->cur_capture_idx]);
                   for (i = 0; i < node->num_of_bufs; i++) {
                       mm_channel_qbuf(ch_obj, node->super_buf[i].buf);
                   }
                   free(node);
               } else {
                   info.num_nodes = 1;
                   info.ch_obj[0] = ch_obj;
                   info.node[0] = node;
               }
            }
        }
        if (info.num_nodes > 0) {
            /* decrease pending_cnt */
            if (MM_CAMERA_SUPER_BUF_NOTIFY_BURST == notify_mode) {
                ch_obj->pending_cnt--;
                if (ch_obj->pending_retro_cnt > 0) {
                  if (ch_obj->pending_retro_cnt == 1) {
                    ch_obj->bWaitForPrepSnapshotDone = 1;
                  }
                  ch_obj->pending_retro_cnt--;
                }

                if (((ch_obj->pending_cnt == 0) ||
                      (ch_obj->stopZslSnapshot == 1)) &&
                      (ch_obj->manualZSLSnapshot == FALSE) &&
                       ch_obj->startZSlSnapshotCalled == TRUE) {
                    LOGI("TRIGGER Stop ZSL. All frame received");
                    mm_camera_stop_zsl_snapshot(ch_obj->cam_obj);
                    ch_obj->startZSlSnapshotCalled = FALSE;
                    ch_obj->burstSnapNum = 0;
                    ch_obj->stopZslSnapshot = 0;
                    ch_obj->unLockAEC = 1;
                    ch_obj->needLEDFlash = FALSE;
                    ch_obj->bracketingState = MM_CHANNEL_BRACKETING_STATE_OFF;
                    ch_obj->isConfigCapture = FALSE;
                }

                if (ch_obj->isConfigCapture) {
                    if (ch_obj->frameConfig.configs[ch_obj->cur_capture_idx].num_frames != 0) {
                        ch_obj->frameConfig.configs[ch_obj->cur_capture_idx].num_frames--;
                    } else {
                        LOGW("Invalid frame config batch index %d max batch = %d",
                                ch_obj->cur_capture_idx, ch_obj->frameConfig.num_batch);
                    }

                    if (ch_obj->frameConfig.configs[ch_obj->cur_capture_idx].num_frames == 0) {
                        //Received all frames for current batch
                        ch_obj->cur_capture_idx++;
                        ch_obj->bundle.superbuf_queue.expected_frame_id =
                                ch_obj->capture_frame_id[ch_obj->cur_capture_idx];
                    } else {
                        LOGH("Need %d frames more for batch %d",
                                ch_obj->frameConfig.configs[ch_obj->cur_capture_idx].num_frames,
                                ch_obj->cur_capture_idx);
                    }
                }
            }
            /* dispatch superbuf */
            mm_channel_send_super_buf(&info);
        } else {
            /* no superbuf avail, break the loop */
            break;
        }
    }
}

/*===========================================================================
 * FUNCTION   : mm_channel_send_super_buf
 *
 * DESCRIPTION: Send super buffers to HAL
 *
 * PARAMETERS :
 *   @info  : Info of super buffers to be sent in callback
 *
 * RETURN     : None
 *==========================================================================*/
void mm_channel_send_super_buf(mm_channel_node_info_t *info)
{
    if (!info || !info->num_nodes){
        LOGE("X Error!! Info invalid");
        return;
    }
    mm_channel_queue_node_t *node = NULL;

    LOGH("num nodes %d to send", info->num_nodes);
    uint32_t idx = 0;
    mm_channel_t *ch_obj = NULL;
    for (idx = 0; idx < info->num_nodes; idx++) {
        node = info->node[idx];
        ch_obj = info->ch_obj[idx];
        if ((ch_obj) && (NULL != ch_obj->bundle.super_buf_notify_cb) && node) {
            mm_camera_cmdcb_t* cb_node = NULL;
            LOGD("Send superbuf to HAL, pending_cnt=%d",
                     ch_obj->pending_cnt);
            /* send cam_sem_post to wake up cb thread to dispatch super buffer */
            cb_node = (mm_camera_cmdcb_t *)malloc(sizeof(mm_camera_cmdcb_t));
            if (NULL != cb_node) {
                memset(cb_node, 0, sizeof(mm_camera_cmdcb_t));
                cb_node->cmd_type = MM_CAMERA_CMD_TYPE_SUPER_BUF_DATA_CB;
                cb_node->u.superbuf.num_bufs = node->num_of_bufs;
                uint8_t i = 0;
                for (i = 0; i < node->num_of_bufs; i++) {
                    cb_node->u.superbuf.bufs[i] = node->super_buf[i].buf;
                }
                cb_node->u.superbuf.camera_handle = ch_obj->cam_obj->my_hdl;
                cb_node->u.superbuf.ch_id = ch_obj->my_hdl;
                cb_node->u.superbuf.bReadyForPrepareSnapshot =
                        ch_obj->bWaitForPrepSnapshotDone;
                if (ch_obj->unLockAEC == 1) {
                    cb_node->u.superbuf.bUnlockAEC = 1;
                    LOGH("Unlocking AEC");
                    ch_obj->unLockAEC = 0;
                }
                /* enqueue to cb thread */
                cam_queue_enq(&(ch_obj->cb_thread.cmd_queue), cb_node);
                /* wake up cb thread */
                cam_sem_post(&(ch_obj->cb_thread.cmd_sem));
                LOGH("Sent super buf for node[%d] ", idx);

            } else {
                LOGE("No memory for mm_camera_node_t");
                /* buf done with the unused super buf */
                uint8_t i = 0;
                for (i = 0; i < node->num_of_bufs; i++) {
                    mm_channel_qbuf(ch_obj, node->super_buf[i].buf);
                }
            }
            free(node);
        } else if ((ch_obj != NULL) && (node != NULL)) {
            /* buf done with the unused super buf */
            uint8_t i;
            for (i = 0; i < node->num_of_bufs; i++) {
                mm_channel_qbuf(ch_obj, node->super_buf[i].buf);
            }
            free(node);
        } else {
            LOGE("node is NULL, debug this");
        }
    }
}

/*===========================================================================
 * FUNCTION   : mm_channel_reg_stream_buf_cb
 *
 * DESCRIPTION: Register callback for stream buffer
 *
 * PARAMETERS :
 *   @my_obj     : Channel object
 *   @stream_id  : stream that will be linked
 *   @buf_cb     : special callback needs to be registered for stream buffer
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 --  failure
 *==========================================================================*/
int32_t mm_channel_reg_stream_buf_cb (mm_channel_t* my_obj,
        uint32_t stream_id, mm_stream_data_cb_t buf_cb)
{
    int32_t rc = -1;
    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj,
            stream_id);

    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* No op. on linked streams */
            return 0;
        }
        rc = mm_stream_reg_buf_cb(s_obj, buf_cb);
    }

    return rc;

}

/*===========================================================================
 * FUNCTION   : mm_channel_fsm_fn
 *
 * DESCRIPTION: channel finite state machine entry function. Depends on channel
 *              state, incoming event will be handled differently.
 *
 * PARAMETERS :
 *   @my_obj   : ptr to a channel object
 *   @evt      : channel event to be processed
 *   @in_val   : input event payload. Can be NULL if not needed.
 *   @out_val  : output payload, Can be NULL if not needed.
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_fsm_fn(mm_channel_t *my_obj,
                          mm_channel_evt_type_t evt,
                          void * in_val,
                          void * out_val)
{
    int32_t rc = -1;

    LOGD("E state = %d", my_obj->state);
    switch (my_obj->state) {
    case MM_CHANNEL_STATE_NOTUSED:
        rc = mm_channel_fsm_fn_notused(my_obj, evt, in_val, out_val);
        break;
    case MM_CHANNEL_STATE_STOPPED:
        rc = mm_channel_fsm_fn_stopped(my_obj, evt, in_val, out_val);
        break;
    case MM_CHANNEL_STATE_ACTIVE:
        rc = mm_channel_fsm_fn_active(my_obj, evt, in_val, out_val);
        break;
    case MM_CHANNEL_STATE_PAUSED:
        rc = mm_channel_fsm_fn_paused(my_obj, evt, in_val, out_val);
        break;
    default:
        LOGD("Not a valid state (%d)", my_obj->state);
        break;
    }

    /* unlock ch_lock */
    pthread_mutex_unlock(&my_obj->ch_lock);
    LOGD("X rc = %d", rc);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_fsm_fn_notused
 *
 * DESCRIPTION: channel finite state machine function to handle event
 *              in NOT_USED state.
 *
 * PARAMETERS :
 *   @my_obj   : ptr to a channel object
 *   @evt      : channel event to be processed
 *   @in_val   : input event payload. Can be NULL if not needed.
 *   @out_val  : output payload, Can be NULL if not needed.
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_fsm_fn_notused(mm_channel_t *my_obj,
                                  mm_channel_evt_type_t evt,
                                  void * in_val,
                                  void * out_val)
{
    int32_t rc = -1;

    switch (evt) {
    default:
        LOGE("invalid state (%d) for evt (%d), in(%p), out(%p)",
                    my_obj->state, evt, in_val, out_val);
        break;
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_fsm_fn_stopped
 *
 * DESCRIPTION: channel finite state machine function to handle event
 *              in STOPPED state.
 *
 * PARAMETERS :
 *   @my_obj   : ptr to a channel object
 *   @evt      : channel event to be processed
 *   @in_val   : input event payload. Can be NULL if not needed.
 *   @out_val  : output payload, Can be NULL if not needed.
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_fsm_fn_stopped(mm_channel_t *my_obj,
                                  mm_channel_evt_type_t evt,
                                  void * in_val,
                                  void * out_val)
{
    int32_t rc = 0;
    LOGD("E evt = %d", evt);
    switch (evt) {
    case MM_CHANNEL_EVT_ADD_STREAM:
        {
            uint32_t s_hdl = 0;
            s_hdl = mm_channel_add_stream(my_obj);
            *((uint32_t*)out_val) = s_hdl;
            rc = 0;
        }
        break;
    case MM_CHANNEL_EVT_LINK_STREAM:
        {
            mm_camera_stream_link_t *stream_link = NULL;
            uint32_t s_hdl = 0;
            stream_link = (mm_camera_stream_link_t *) in_val;
            s_hdl = mm_channel_link_stream(my_obj, stream_link);
            *((uint32_t*)out_val) = s_hdl;
            rc = 0;
        }
        break;
    case MM_CHANNEL_EVT_DEL_STREAM:
        {
            uint32_t s_id = *((uint32_t *)in_val);
            rc = mm_channel_del_stream(my_obj, s_id);
        }
        break;
    case MM_CHANNEL_EVT_START:
        {
            rc = mm_channel_start(my_obj);
            /* first stream started in stopped state
             * move to active state */
            if (0 == rc) {
                my_obj->state = MM_CHANNEL_STATE_ACTIVE;
            }
        }
        break;
    case MM_CHANNEL_EVT_CONFIG_STREAM:
        {
            mm_evt_paylod_config_stream_t *payload =
                (mm_evt_paylod_config_stream_t *)in_val;
            rc = mm_channel_config_stream(my_obj,
                                          payload->stream_id,
                                          payload->config);
        }
        break;
    case MM_CHANNEL_EVT_GET_BUNDLE_INFO:
        {
            cam_bundle_config_t *payload =
                (cam_bundle_config_t *)in_val;
            rc = mm_channel_get_bundle_info(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_DELETE:
        {
            mm_channel_release(my_obj);
            rc = 0;
        }
        break;
    case MM_CHANNEL_EVT_SET_STREAM_PARM:
        {
            mm_evt_paylod_set_get_stream_parms_t *payload =
                (mm_evt_paylod_set_get_stream_parms_t *)in_val;
            rc = mm_channel_set_stream_parm(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_GET_STREAM_QUEUED_BUF_COUNT:
        {
            uint32_t stream_id = *((uint32_t *)in_val);
            rc = mm_channel_get_queued_buf_count(my_obj, stream_id);
        }
        break;
    case MM_CHANNEL_EVT_GET_STREAM_PARM:
        {
            mm_evt_paylod_set_get_stream_parms_t *payload =
                (mm_evt_paylod_set_get_stream_parms_t *)in_val;
            rc = mm_channel_get_stream_parm(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_DO_STREAM_ACTION:
        {
            mm_evt_paylod_do_stream_action_t *payload =
                (mm_evt_paylod_do_stream_action_t *)in_val;
            rc = mm_channel_do_stream_action(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_MAP_STREAM_BUF:
        {
            cam_buf_map_type *payload =
                (cam_buf_map_type *)in_val;
            rc = mm_channel_map_stream_buf(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_MAP_STREAM_BUFS:
        {
            cam_buf_map_type_list *payload =
                (cam_buf_map_type_list *)in_val;
            rc = mm_channel_map_stream_bufs(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_UNMAP_STREAM_BUF:
        {
            cam_buf_unmap_type *payload =
                (cam_buf_unmap_type *)in_val;
            rc = mm_channel_unmap_stream_buf(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_REG_STREAM_BUF_CB:
        {
            mm_evt_paylod_reg_stream_buf_cb *payload =
                (mm_evt_paylod_reg_stream_buf_cb *)in_val;
            rc = mm_channel_reg_stream_buf_cb (my_obj,
                    payload->stream_id, payload->buf_cb);
        }
        break;
    default:
        LOGE("invalid state (%d) for evt (%d)",
                    my_obj->state, evt);
        break;
    }
    LOGD("E rc = %d", rc);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_fsm_fn_active
 *
 * DESCRIPTION: channel finite state machine function to handle event
 *              in ACTIVE state.
 *
 * PARAMETERS :
 *   @my_obj   : ptr to a channel object
 *   @evt      : channel event to be processed
 *   @in_val   : input event payload. Can be NULL if not needed.
 *   @out_val  : output payload, Can be NULL if not needed.
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_fsm_fn_active(mm_channel_t *my_obj,
                          mm_channel_evt_type_t evt,
                          void * in_val,
                          void * out_val)
{
    int32_t rc = 0;

    LOGD("E evt = %d", evt);
    switch (evt) {
    case MM_CHANNEL_EVT_STOP:
        {
            rc = mm_channel_stop(my_obj);
            my_obj->state = MM_CHANNEL_STATE_STOPPED;
        }
        break;
    case MM_CHANNEL_EVT_REQUEST_SUPER_BUF:
        {
            mm_camera_req_buf_t *payload =
                    (mm_camera_req_buf_t *)in_val;
            rc = mm_channel_request_super_buf(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_CANCEL_REQUEST_SUPER_BUF:
        {
            rc = mm_channel_cancel_super_buf_request(my_obj);
        }
        break;
    case MM_CHANNEL_EVT_FLUSH_SUPER_BUF_QUEUE:
        {
            uint32_t frame_idx = *((uint32_t *)in_val);
            rc = mm_channel_flush_super_buf_queue(my_obj, frame_idx, CAM_STREAM_TYPE_DEFAULT);
        }
        break;
    case MM_CHANNEL_EVT_START_ZSL_SNAPSHOT:
        {
            rc = mm_channel_start_zsl_snapshot(my_obj);
        }
        break;
    case MM_CHANNEL_EVT_STOP_ZSL_SNAPSHOT:
        {
            rc = mm_channel_stop_zsl_snapshot(my_obj);
        }
        break;
    case MM_CHANNEL_EVT_CONFIG_NOTIFY_MODE:
        {
            mm_camera_super_buf_notify_mode_t notify_mode =
                *((mm_camera_super_buf_notify_mode_t *)in_val);
            rc = mm_channel_config_notify_mode(my_obj, notify_mode);
        }
        break;
    case MM_CHANNEL_EVT_SET_STREAM_PARM:
        {
            mm_evt_paylod_set_get_stream_parms_t *payload =
                (mm_evt_paylod_set_get_stream_parms_t *)in_val;
            rc = mm_channel_set_stream_parm(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_GET_STREAM_QUEUED_BUF_COUNT:
        {
            uint32_t stream_id = *((uint32_t *)in_val);
            rc = mm_channel_get_queued_buf_count(my_obj, stream_id);
        }
        break;
    case MM_CHANNEL_EVT_GET_STREAM_PARM:
        {
            mm_evt_paylod_set_get_stream_parms_t *payload =
                (mm_evt_paylod_set_get_stream_parms_t *)in_val;
            rc = mm_channel_get_stream_parm(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_DO_STREAM_ACTION:
        {
            mm_evt_paylod_do_stream_action_t *payload =
                (mm_evt_paylod_do_stream_action_t *)in_val;
            rc = mm_channel_do_stream_action(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_MAP_STREAM_BUF:
        {
            cam_buf_map_type *payload =
                (cam_buf_map_type *)in_val;
            if (payload != NULL) {
                uint8_t type = payload->type;
                if ((type == CAM_MAPPING_BUF_TYPE_OFFLINE_INPUT_BUF) ||
                        (type == CAM_MAPPING_BUF_TYPE_OFFLINE_META_BUF)) {
                    rc = mm_channel_map_stream_buf(my_obj, payload);
                }
            } else {
                LOGE("cannot map regualr stream buf in active state");
            }
        }
        break;
    case MM_CHANNEL_EVT_MAP_STREAM_BUFS:
        {
            cam_buf_map_type_list *payload =
                (cam_buf_map_type_list *)in_val;
            if ((payload != NULL) && (payload->length > 0)) {
                uint8_t type = payload->buf_maps[0].type;
                if ((type == CAM_MAPPING_BUF_TYPE_OFFLINE_INPUT_BUF) ||
                        (type == CAM_MAPPING_BUF_TYPE_OFFLINE_META_BUF)) {
                    rc = mm_channel_map_stream_bufs(my_obj, payload);
                }
            } else {
                LOGE("cannot map regualr stream buf in active state");
            }
        }
        break;
    case MM_CHANNEL_EVT_UNMAP_STREAM_BUF:
        {
            cam_buf_unmap_type *payload =
                (cam_buf_unmap_type *)in_val;
            if (payload != NULL) {
                uint8_t type = payload->type;
                if ((type == CAM_MAPPING_BUF_TYPE_OFFLINE_INPUT_BUF) ||
                        (type == CAM_MAPPING_BUF_TYPE_OFFLINE_META_BUF)) {
                    rc = mm_channel_unmap_stream_buf(my_obj, payload);
                }
            } else {
                LOGE("cannot unmap regualr stream buf in active state");
            }
        }
        break;
    case MM_CHANNEL_EVT_AF_BRACKETING:
        {
            LOGH("MM_CHANNEL_EVT_AF_BRACKETING");
            uint32_t start_flag = *((uint32_t *)in_val);
            mm_camera_generic_cmd_t gen_cmd;
            gen_cmd.type = MM_CAMERA_GENERIC_CMD_TYPE_AF_BRACKETING;
            gen_cmd.payload[0] = start_flag;
            rc = mm_channel_proc_general_cmd(my_obj, &gen_cmd);
        }
        break;
    case MM_CHANNEL_EVT_AE_BRACKETING:
        {
            LOGH("MM_CHANNEL_EVT_AE_BRACKETING");
            uint32_t start_flag = *((uint32_t *)in_val);
            mm_camera_generic_cmd_t gen_cmd;
            gen_cmd.type = MM_CAMERA_GENERIC_CMD_TYPE_AE_BRACKETING;
            gen_cmd.payload[0] = start_flag;
            rc = mm_channel_proc_general_cmd(my_obj, &gen_cmd);
        }
        break;
    case MM_CHANNEL_EVT_FLASH_BRACKETING:
        {
            LOGH("MM_CHANNEL_EVT_FLASH_BRACKETING");
            uint32_t start_flag = *((uint32_t *)in_val);
            mm_camera_generic_cmd_t gen_cmd;
            gen_cmd.type = MM_CAMERA_GENERIC_CMD_TYPE_FLASH_BRACKETING;
            gen_cmd.payload[0] = start_flag;
            rc = mm_channel_proc_general_cmd(my_obj, &gen_cmd);
        }
        break;
    case MM_CHANNEL_EVT_ZOOM_1X:
        {
            LOGH("MM_CHANNEL_EVT_ZOOM_1X");
            uint32_t start_flag = *((uint32_t *)in_val);
            mm_camera_generic_cmd_t gen_cmd;
            gen_cmd.type = MM_CAMERA_GENERIC_CMD_TYPE_ZOOM_1X;
            gen_cmd.payload[0] = start_flag;
            rc = mm_channel_proc_general_cmd(my_obj, &gen_cmd);
        }
        break;
    case MM_CAMERA_EVT_CAPTURE_SETTING:
        {
            mm_camera_generic_cmd_t gen_cmd;
            cam_capture_frame_config_t *input;
            gen_cmd.type = MM_CAMERA_GENERIC_CMD_TYPE_CAPTURE_SETTING;
            LOGH("MM_CAMERA_EVT_CAPTURE_SETTING");
            if (in_val == NULL) {
                gen_cmd.payload[0] = 0;
                memset(&gen_cmd.frame_config, 0, sizeof(cam_capture_frame_config_t));
            } else {
                gen_cmd.payload[0] = 1;
                input = (cam_capture_frame_config_t *)in_val;
                gen_cmd.frame_config = *input;
            }
            rc = mm_channel_proc_general_cmd(my_obj, &gen_cmd);
        }
        break;
    case MM_CHANNEL_EVT_REG_STREAM_BUF_CB:
        {
            mm_evt_paylod_reg_stream_buf_cb *payload =
                (mm_evt_paylod_reg_stream_buf_cb *)in_val;
            rc = mm_channel_reg_stream_buf_cb (my_obj,
                    payload->stream_id, payload->buf_cb);
        }
        break;
     default:
        LOGE("invalid state (%d) for evt (%d), in(%p), out(%p)",
                    my_obj->state, evt, in_val, out_val);
        break;
    }
    LOGD("X rc = %d", rc);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_fsm_fn_paused
 *
 * DESCRIPTION: channel finite state machine function to handle event
 *              in PAUSED state.
 *
 * PARAMETERS :
 *   @my_obj   : ptr to a channel object
 *   @evt      : channel event to be processed
 *   @in_val   : input event payload. Can be NULL if not needed.
 *   @out_val  : output payload, Can be NULL if not needed.
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_fsm_fn_paused(mm_channel_t *my_obj,
                          mm_channel_evt_type_t evt,
                          void * in_val,
                          void * out_val)
{
    int32_t rc = 0;

    /* currently we are not supporting pause/resume channel */
    LOGE("invalid state (%d) for evt (%d), in(%p), out(%p)",
                my_obj->state, evt, in_val, out_val);

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_init
 *
 * DESCRIPTION: initialize a channel
 *
 * PARAMETERS :
 *   @my_obj       : channel object be to initialized
 *   @attr         : bundle attribute of the channel if needed
 *   @channel_cb   : callback function for bundle data notify
 *   @userdata     : user data ptr
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * 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.
 *==========================================================================*/
int32_t mm_channel_init(mm_channel_t *my_obj,
                        mm_camera_channel_attr_t *attr,
                        mm_camera_buf_notify_t channel_cb,
                        void *userdata)
{
    int32_t rc = 0;

    my_obj->bundle.super_buf_notify_cb = channel_cb;
    my_obj->bundle.user_data = userdata;
    if (NULL != attr) {
        my_obj->bundle.superbuf_queue.attr = *attr;
    }

    LOGD("Launch data poll thread in channel open");
    snprintf(my_obj->poll_thread[0].threadName, THREAD_NAME_SIZE, "CAM_dataPoll");
    mm_camera_poll_thread_launch(&my_obj->poll_thread[0],
                                 MM_CAMERA_POLL_TYPE_DATA);

    /* change state to stopped state */
    my_obj->state = MM_CHANNEL_STATE_STOPPED;
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_release
 *
 * DESCRIPTION: release a channel resource. Channel state will move to UNUSED
 *              state after this call.
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *
 * RETURN     : none
 *==========================================================================*/
void mm_channel_release(mm_channel_t *my_obj)
{
    /* stop data poll thread */
    mm_camera_poll_thread_release(&my_obj->poll_thread[0]);

    /* memset bundle info */
    memset(&my_obj->bundle, 0, sizeof(mm_channel_bundle_t));

    /* change state to notused state */
    my_obj->state = MM_CHANNEL_STATE_NOTUSED;
}

/*===========================================================================
 * FUNCTION   : mm_channel_link_stream
 *
 * DESCRIPTION: link a stream from external channel into this channel
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *   @stream_link  : channel and stream to be linked
 *
 * 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_channel_link_stream(mm_channel_t *my_obj,
        mm_camera_stream_link_t *stream_link)
{
    uint8_t idx = 0;
    uint32_t s_hdl = 0;
    mm_stream_t *stream_obj = NULL;
    mm_stream_t *stream = NULL;

    if (NULL == stream_link) {
        LOGE("Invalid stream link");
        return 0;
    }

    stream = mm_channel_util_get_stream_by_handler(stream_link->ch,
            stream_link->stream_id);
    if (NULL == stream) {
        return 0;
    }

    /* check available stream */
    for (idx = 0; idx < MAX_STREAM_NUM_IN_BUNDLE; idx++) {
        if (MM_STREAM_STATE_NOTUSED == my_obj->streams[idx].state) {
            stream_obj = &my_obj->streams[idx];
            break;
        }
    }
    if (NULL == stream_obj) {
        LOGE("streams reach max, no more stream allowed to add");
        return s_hdl;
    }

    /* initialize stream object */
    *stream_obj = *stream;
    stream_obj->linked_stream = stream;
    s_hdl = stream->my_hdl;

    LOGD("stream handle = %d", s_hdl);
    return s_hdl;
}

/*===========================================================================
 * FUNCTION   : mm_channel_add_stream
 *
 * DESCRIPTION: add a stream into the channel
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *
 * 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_channel_add_stream(mm_channel_t *my_obj)
{
    int32_t rc = 0;
    uint8_t idx = 0;
    uint32_t s_hdl = 0;
    mm_stream_t *stream_obj = NULL;

    LOGD("E");
    /* check available stream */
    for (idx = 0; idx < MAX_STREAM_NUM_IN_BUNDLE; idx++) {
        if (MM_STREAM_STATE_NOTUSED == my_obj->streams[idx].state) {
            stream_obj = &my_obj->streams[idx];
            break;
        }
    }
    if (NULL == stream_obj) {
        LOGE("streams reach max, no more stream allowed to add");
        return s_hdl;
    }

    /* initialize stream object */
    memset(stream_obj, 0, sizeof(mm_stream_t));
    stream_obj->fd = -1;
    stream_obj->my_hdl = mm_camera_util_generate_handler(idx);
    stream_obj->ch_obj = my_obj;
    pthread_mutex_init(&stream_obj->buf_lock, NULL);
    pthread_mutex_init(&stream_obj->cb_lock, NULL);
    pthread_mutex_init(&stream_obj->cmd_lock, NULL);
    pthread_cond_init(&stream_obj->buf_cond, NULL);
    memset(stream_obj->buf_status, 0,
            sizeof(stream_obj->buf_status));
    stream_obj->state = MM_STREAM_STATE_INITED;

    /* acquire stream */
    rc = mm_stream_fsm_fn(stream_obj, MM_STREAM_EVT_ACQUIRE, NULL, NULL);
    if (0 == rc) {
        s_hdl = stream_obj->my_hdl;
    } else {
        /* error during acquire, de-init */
        pthread_cond_destroy(&stream_obj->buf_cond);
        pthread_mutex_destroy(&stream_obj->buf_lock);
        pthread_mutex_destroy(&stream_obj->cb_lock);
        pthread_mutex_destroy(&stream_obj->cmd_lock);
        memset(stream_obj, 0, sizeof(mm_stream_t));
    }
    LOGD("stream handle = %d", s_hdl);
    return s_hdl;
}

/*===========================================================================
 * FUNCTION   : mm_channel_del_stream
 *
 * DESCRIPTION: delete a stream from the channel bu its handle
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @stream_id    : stream handle
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * NOTE       : assume steam is stooped before it can be deleted
 *==========================================================================*/
int32_t mm_channel_del_stream(mm_channel_t *my_obj,
                              uint32_t stream_id)
{
    int rc = -1;
    mm_stream_t * stream_obj = NULL;
    stream_obj = mm_channel_util_get_stream_by_handler(my_obj, stream_id);

    if (NULL == stream_obj) {
        LOGE("Invalid Stream Object for stream_id = %d", stream_id);
        return rc;
    }

    if (stream_obj->ch_obj != my_obj) {
        /* Only unlink stream */
        pthread_mutex_lock(&stream_obj->linked_stream->buf_lock);
        stream_obj->linked_stream->is_linked = 0;
        stream_obj->linked_stream->linked_obj = NULL;
        pthread_mutex_unlock(&stream_obj->linked_stream->buf_lock);
        memset(stream_obj, 0, sizeof(mm_stream_t));

        return 0;
    }

    rc = mm_stream_fsm_fn(stream_obj,
                          MM_STREAM_EVT_RELEASE,
                          NULL,
                          NULL);

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_config_stream
 *
 * DESCRIPTION: configure a stream
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @stream_id    : stream handle
 *   @config       : stream configuration
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_config_stream(mm_channel_t *my_obj,
                                   uint32_t stream_id,
                                   mm_camera_stream_config_t *config)
{
    int rc = -1;
    mm_stream_t * stream_obj = NULL;
    LOGD("E stream ID = %d", stream_id);
    stream_obj = mm_channel_util_get_stream_by_handler(my_obj, stream_id);

    if (NULL == stream_obj) {
        LOGE("Invalid Stream Object for stream_id = %d", stream_id);
        return rc;
    }

    if (stream_obj->ch_obj != my_obj) {
        /* No op. on linked streams */
        return 0;
    }

    /* set stream fmt */
    rc = mm_stream_fsm_fn(stream_obj,
                          MM_STREAM_EVT_SET_FMT,
                          (void *)config,
                          NULL);
    LOGD("X rc = %d",rc);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_get_bundle_info
 *
 * DESCRIPTION: query bundle info of the channel, which should include all
 *              streams within this channel
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @bundle_info  : bundle info to be filled in
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_get_bundle_info(mm_channel_t *my_obj,
                                   cam_bundle_config_t *bundle_info)
{
    int i;
    mm_stream_t *s_obj = NULL;
    cam_stream_type_t stream_type = CAM_STREAM_TYPE_DEFAULT;
    int32_t rc = 0;

    memset(bundle_info, 0, sizeof(cam_bundle_config_t));
    bundle_info->bundle_id = my_obj->my_hdl;
    bundle_info->num_of_streams = 0;
    for (i = 0; i < MAX_STREAM_NUM_IN_BUNDLE; i++) {
        if (my_obj->streams[i].my_hdl > 0) {
            s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                          my_obj->streams[i].my_hdl);
            if (NULL != s_obj) {
                stream_type = s_obj->stream_info->stream_type;
                if ((CAM_STREAM_TYPE_METADATA != stream_type) &&
                        (s_obj->ch_obj == my_obj)) {
                    bundle_info->stream_ids[bundle_info->num_of_streams++] =
                                                        s_obj->server_stream_id;
                }
            } else {
                LOGE("cannot find stream obj (%d) by handler (%d)",
                            i, my_obj->streams[i].my_hdl);
                rc = -1;
                break;
            }
        }
    }
    if (rc != 0) {
        /* error, reset to 0 */
        memset(bundle_info, 0, sizeof(cam_bundle_config_t));
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_start
 *
 * DESCRIPTION: start a channel, which will start all streams in the channel
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_start(mm_channel_t *my_obj)
{
    int32_t rc = 0;
    int i = 0, j = 0;
    mm_stream_t *s_objs[MAX_STREAM_NUM_IN_BUNDLE] = {NULL};
    uint8_t num_streams_to_start = 0;
    uint8_t num_streams_in_bundle_queue = 0;
    mm_stream_t *s_obj = NULL;
    int meta_stream_idx = 0;
    cam_stream_type_t stream_type = CAM_STREAM_TYPE_DEFAULT;

    for (i = 0; i < MAX_STREAM_NUM_IN_BUNDLE; i++) {
        if (my_obj->streams[i].my_hdl > 0) {
            s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                          my_obj->streams[i].my_hdl);
            if (NULL != s_obj) {
                stream_type = s_obj->stream_info->stream_type;
                /* remember meta data stream index */
                if ((stream_type == CAM_STREAM_TYPE_METADATA) &&
                        (s_obj->ch_obj == my_obj)) {
                    meta_stream_idx = num_streams_to_start;
                }
                s_objs[num_streams_to_start++] = s_obj;

                if (!s_obj->stream_info->noFrameExpected) {
                    num_streams_in_bundle_queue++;
                }
            }
        }
    }

    if (meta_stream_idx > 0 ) {
        /* always start meta data stream first, so switch the stream object with the first one */
        s_obj = s_objs[0];
        s_objs[0] = s_objs[meta_stream_idx];
        s_objs[meta_stream_idx] = s_obj;
    }

    if (NULL != my_obj->bundle.super_buf_notify_cb) {
        /* need to send up cb, therefore launch thread */
        /* init superbuf queue */
        mm_channel_superbuf_queue_init(&my_obj->bundle.superbuf_queue);
        my_obj->bundle.superbuf_queue.num_streams = num_streams_in_bundle_queue;
        my_obj->bundle.superbuf_queue.expected_frame_id =
                my_obj->bundle.superbuf_queue.attr.user_expected_frame_id;
        my_obj->bundle.superbuf_queue.expected_frame_id_without_led = 0;
        my_obj->bundle.superbuf_queue.led_off_start_frame_id = 0;
        my_obj->bundle.superbuf_queue.led_on_start_frame_id = 0;
        my_obj->bundle.superbuf_queue.led_on_num_frames = 0;

        for (i = 0; i < num_streams_to_start; i++) {
            /* Only bundle streams that belong to the channel */
            if(!(s_objs[i]->stream_info->noFrameExpected)) {
                if (s_objs[i]->ch_obj == my_obj) {
                    /* set bundled flag to streams */
                    s_objs[i]->is_bundled = 1;
                }
                my_obj->bundle.superbuf_queue.bundled_streams[j++] = s_objs[i]->my_hdl;
            }
        }

        /* launch cb thread for dispatching super buf through cb */
        snprintf(my_obj->cb_thread.threadName, THREAD_NAME_SIZE, "CAM_SuperBuf");
        mm_camera_cmd_thread_launch(&my_obj->cb_thread,
                                    mm_channel_dispatch_super_buf,
                                    (void*)my_obj);

        /* launch cmd thread for super buf dataCB */
        snprintf(my_obj->cmd_thread.threadName, THREAD_NAME_SIZE, "CAM_SuperBufCB");
        mm_camera_cmd_thread_launch(&my_obj->cmd_thread,
                                    mm_channel_process_stream_buf,
                                    (void*)my_obj);

        /* set flag to TRUE */
        my_obj->bundle.is_active = TRUE;
    }

    /* link any streams first before starting the rest of the streams */
    for (i = 0; i < num_streams_to_start; i++) {
        if (s_objs[i]->ch_obj != my_obj) {
            pthread_mutex_lock(&s_objs[i]->linked_stream->buf_lock);
            s_objs[i]->linked_stream->linked_obj = my_obj;
            s_objs[i]->linked_stream->is_linked = 1;
            pthread_mutex_unlock(&s_objs[i]->linked_stream->buf_lock);
            continue;
        }
    }

    for (i = 0; i < num_streams_to_start; i++) {
        if (s_objs[i]->ch_obj != my_obj) {
            continue;
        }
        /* all streams within a channel should be started at the same time */
        if (s_objs[i]->state == MM_STREAM_STATE_ACTIVE) {
            LOGE("stream already started idx(%d)", i);
            rc = -1;
            break;
        }

        /* allocate buf */
        rc = mm_stream_fsm_fn(s_objs[i],
                              MM_STREAM_EVT_GET_BUF,
                              NULL,
                              NULL);
        if (0 != rc) {
            LOGE("get buf failed at idx(%d)", i);
            break;
        }

        /* reg buf */
        rc = mm_stream_fsm_fn(s_objs[i],
                              MM_STREAM_EVT_REG_BUF,
                              NULL,
                              NULL);
        if (0 != rc) {
            LOGE("reg buf failed at idx(%d)", i);
            break;
        }

        /* start stream */
        rc = mm_stream_fsm_fn(s_objs[i],
                              MM_STREAM_EVT_START,
                              NULL,
                              NULL);
        if (0 != rc) {
            LOGE("start stream failed at idx(%d)", i);
            break;
        }
    }

    /* error handling */
    if (0 != rc) {
        /* unlink the streams first */
        for (j = 0; j < num_streams_to_start; j++) {
            if (s_objs[j]->ch_obj != my_obj) {
                pthread_mutex_lock(&s_objs[j]->linked_stream->buf_lock);
                s_objs[j]->linked_stream->is_linked = 0;
                s_objs[j]->linked_stream->linked_obj = NULL;
                pthread_mutex_unlock(&s_objs[j]->linked_stream->buf_lock);

                if (TRUE == my_obj->bundle.is_active) {
                    mm_channel_flush_super_buf_queue(my_obj, 0,
                            s_objs[i]->stream_info->stream_type);
                }
                memset(s_objs[j], 0, sizeof(mm_stream_t));
                continue;
            }
        }

        for (j = 0; j <= i; j++) {
            if ((NULL == s_objs[j]) || (s_objs[j]->ch_obj != my_obj)) {
                continue;
            }
            /* stop streams*/
            mm_stream_fsm_fn(s_objs[j],
                             MM_STREAM_EVT_STOP,
                             NULL,
                             NULL);

            /* unreg buf */
            mm_stream_fsm_fn(s_objs[j],
                             MM_STREAM_EVT_UNREG_BUF,
                             NULL,
                             NULL);

            /* put buf back */
            mm_stream_fsm_fn(s_objs[j],
                             MM_STREAM_EVT_PUT_BUF,
                             NULL,
                             NULL);
        }

        /* destroy super buf cmd thread */
        if (TRUE == my_obj->bundle.is_active) {
            /* first stop bundle thread */
            mm_camera_cmd_thread_release(&my_obj->cmd_thread);
            mm_camera_cmd_thread_release(&my_obj->cb_thread);

            /* deinit superbuf queue */
            mm_channel_superbuf_queue_deinit(&my_obj->bundle.superbuf_queue);

            /* memset super buffer queue info */
            my_obj->bundle.is_active = 0;
            memset(&my_obj->bundle.superbuf_queue, 0, sizeof(mm_channel_queue_t));
        }
    }
    my_obj->bWaitForPrepSnapshotDone = 0;
    if (my_obj->bundle.superbuf_queue.attr.enable_frame_sync) {
        LOGH("registering Channel obj %p", my_obj);
        mm_frame_sync_register_channel(my_obj);
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_stop
 *
 * DESCRIPTION: stop a channel, which will stop all streams in the channel
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_stop(mm_channel_t *my_obj)
{
    int32_t rc = 0;
    int i;
    mm_stream_t *s_objs[MAX_STREAM_NUM_IN_BUNDLE] = {NULL};
    uint8_t num_streams_to_stop = 0;
    mm_stream_t *s_obj = NULL;
    int meta_stream_idx = 0;
    cam_stream_type_t stream_type = CAM_STREAM_TYPE_DEFAULT;

    if (my_obj->bundle.superbuf_queue.attr.enable_frame_sync) {
        mm_frame_sync_unregister_channel(my_obj);
    }

    for (i = 0; i < MAX_STREAM_NUM_IN_BUNDLE; i++) {
        if (my_obj->streams[i].my_hdl > 0) {
            s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                          my_obj->streams[i].my_hdl);
            if (NULL != s_obj) {
                if (s_obj->ch_obj == my_obj) {
                    stream_type = s_obj->stream_info->stream_type;
                    /* remember meta data stream index */
                    if (stream_type == CAM_STREAM_TYPE_METADATA) {
                        meta_stream_idx = num_streams_to_stop;
                    }
                }
                s_objs[num_streams_to_stop++] = s_obj;
            }
        }
    }

    if (meta_stream_idx < num_streams_to_stop - 1 ) {
        /* always stop meta data stream last, so switch the stream object with the last one */
        s_obj = s_objs[num_streams_to_stop - 1];
        s_objs[num_streams_to_stop - 1] = s_objs[meta_stream_idx];
        s_objs[meta_stream_idx] = s_obj;
    }

    for (i = 0; i < num_streams_to_stop; i++) {
        /* stream that are linked to this channel should not be stopped */
        if (s_objs[i]->ch_obj != my_obj) {
            continue;
        }

        /* stream off */
        mm_stream_fsm_fn(s_objs[i],
                         MM_STREAM_EVT_STOP,
                         NULL,
                         NULL);

        /* unreg buf at kernel */
        mm_stream_fsm_fn(s_objs[i],
                         MM_STREAM_EVT_UNREG_BUF,
                         NULL,
                         NULL);
    }

    for (i = 0; i < num_streams_to_stop; i++) {
        if (s_objs[i]->ch_obj != my_obj) {
            /* Only unlink stream */
            pthread_mutex_lock(&s_objs[i]->linked_stream->buf_lock);
            s_objs[i]->linked_stream->is_linked = 0;
            s_objs[i]->linked_stream->linked_obj = NULL;
            pthread_mutex_unlock(&s_objs[i]->linked_stream->buf_lock);
        }
    }

    /* destroy super buf cmd thread */
    if (TRUE == my_obj->bundle.is_active) {
        mm_channel_flush_super_buf_queue(my_obj, 0, CAM_STREAM_TYPE_DEFAULT);
        /* first stop bundle thread */
        mm_camera_cmd_thread_release(&my_obj->cmd_thread);
        mm_camera_cmd_thread_release(&my_obj->cb_thread);

        /* deinit superbuf queue */
        mm_channel_superbuf_queue_deinit(&my_obj->bundle.superbuf_queue);

        /* reset few fields in the bundle info */
        my_obj->bundle.is_active = 0;
        my_obj->bundle.superbuf_queue.expected_frame_id = 0;
        my_obj->bundle.superbuf_queue.match_cnt = 0;
    }

    /* since all streams are stopped, we are safe to
     * release all buffers allocated in stream */
    for (i = 0; i < num_streams_to_stop; i++) {
        if (s_objs[i]->ch_obj != my_obj) {
            continue;
        }
        /* put buf back */
        mm_stream_fsm_fn(s_objs[i],
                         MM_STREAM_EVT_PUT_BUF,
                         NULL,
                         NULL);
    }

    for (i = 0; i < num_streams_to_stop; i++) {
        if (s_objs[i]->ch_obj != my_obj) {
            memset(s_objs[i], 0, sizeof(mm_stream_t));
        } else {
            continue;
        }
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_request_super_buf
 *
 * DESCRIPTION: for burst mode in bundle, reuqest certain amount of matched
 *              frames from superbuf queue
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @num_buf_requested : number of matched frames needed
 *   @num_retro_buf_requested : number of retro frames needed
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_request_super_buf(mm_channel_t *my_obj,
        mm_camera_req_buf_t *buf)
{
    int32_t rc = 0;
    mm_camera_cmdcb_t* node = NULL;

    if(!buf) {
        LOGE("Request info buf is NULL");
        return -1;
    }

    /* set pending_cnt
     * will trigger dispatching super frames if pending_cnt > 0 */
    /* send cam_sem_post to wake up cmd thread to dispatch super buffer */
    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_REQ_DATA_CB;
        node->u.req_buf = *buf;

        /* enqueue to cmd thread */
        cam_queue_enq(&(my_obj->cmd_thread.cmd_queue), node);

        /* wake up cmd thread */
        cam_sem_post(&(my_obj->cmd_thread.cmd_sem));
    } else {
        LOGE("No memory for mm_camera_node_t");
        rc = -1;
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_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       : channel object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_cancel_super_buf_request(mm_channel_t *my_obj)
{
    int32_t rc = 0;
    /* reset pending_cnt */
    mm_camera_req_buf_t buf;
    memset(&buf, 0x0, sizeof(buf));
    buf.type = MM_CAMERA_REQ_SUPER_BUF;
    buf.num_buf_requested = 0;
    rc = mm_channel_request_super_buf(my_obj, &buf);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_flush_super_buf_queue
 *
 * DESCRIPTION: flush superbuf queue
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *   @frame_idx : frame idx until which to flush all superbufs
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_flush_super_buf_queue(mm_channel_t *my_obj, uint32_t frame_idx,
                                                     cam_stream_type_t stream_type)
{
    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_FLUSH_QUEUE;
        node->u.flush_cmd.frame_idx = frame_idx;
        node->u.flush_cmd.stream_type = stream_type;

        /* enqueue to cmd thread */
        cam_queue_enq(&(my_obj->cmd_thread.cmd_queue), node);

        /* wake up cmd thread */
        cam_sem_post(&(my_obj->cmd_thread.cmd_sem));

        /* wait for ack from cmd thread */
        cam_sem_wait(&(my_obj->cmd_thread.sync_sem));
    } else {
        LOGE("No memory for mm_camera_node_t");
        rc = -1;
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_config_notify_mode
 *
 * DESCRIPTION: configure notification mode
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *   @notify_mode : notification mode
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_config_notify_mode(mm_channel_t *my_obj,
                                      mm_camera_super_buf_notify_mode_t notify_mode)
{
    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->u.notify_mode = notify_mode;
        node->cmd_type = MM_CAMERA_CMD_TYPE_CONFIG_NOTIFY;

        /* enqueue to cmd thread */
        cam_queue_enq(&(my_obj->cmd_thread.cmd_queue), node);

        /* wake up cmd thread */
        cam_sem_post(&(my_obj->cmd_thread.cmd_sem));
    } else {
        LOGE("No memory for mm_camera_node_t");
        rc = -1;
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_start_zsl_snapshot
 *
 * DESCRIPTION: start zsl snapshot
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_start_zsl_snapshot(mm_channel_t *my_obj)
{
    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_START_ZSL;

        /* enqueue to cmd thread */
        cam_queue_enq(&(my_obj->cmd_thread.cmd_queue), node);

        /* wake up cmd thread */
        cam_sem_post(&(my_obj->cmd_thread.cmd_sem));
    } else {
        LOGE("No memory for mm_camera_node_t");
        rc = -1;
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_stop_zsl_snapshot
 *
 * DESCRIPTION: stop zsl snapshot
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_stop_zsl_snapshot(mm_channel_t *my_obj)
{
    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_STOP_ZSL;

        /* enqueue to cmd thread */
        cam_queue_enq(&(my_obj->cmd_thread.cmd_queue), node);

        /* wake up cmd thread */
        cam_sem_post(&(my_obj->cmd_thread.cmd_sem));
    } else {
        LOGE("No memory for mm_camera_node_t");
        rc = -1;
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_qbuf
 *
 * DESCRIPTION: enqueue buffer back to kernel
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @buf          : buf ptr to be enqueued
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_qbuf(mm_channel_t *my_obj,
                        mm_camera_buf_def_t *buf)
{
    int32_t rc = -1;
    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj, buf->stream_id);

    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* Redirect to linked stream */
            rc = mm_stream_fsm_fn(s_obj->linked_stream,
                    MM_STREAM_EVT_QBUF,
                    (void *)buf,
                    NULL);
        } else {
            rc = mm_stream_fsm_fn(s_obj,
                    MM_STREAM_EVT_QBUF,
                    (void *)buf,
                    NULL);
        }
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_get_queued_buf_count
 *
 * DESCRIPTION: return queued buffer count
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @stream_id    : steam_id
 *
 * RETURN     : queued buffer count
 *==========================================================================*/
int32_t mm_channel_get_queued_buf_count(mm_channel_t *my_obj, uint32_t stream_id)
{
    int32_t rc = -1;
    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj, stream_id);

    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* Redirect to linked stream */
            rc = mm_stream_fsm_fn(s_obj->linked_stream,
                    MM_STREAM_EVT_GET_QUEUED_BUF_COUNT,
                    NULL,
                    NULL);
        } else {
            rc = mm_stream_fsm_fn(s_obj,
                    MM_STREAM_EVT_GET_QUEUED_BUF_COUNT,
                    NULL,
                    NULL);
        }
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_set_stream_parms
 *
 * DESCRIPTION: set parameters per stream
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @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_channel_set_stream_parm(mm_channel_t *my_obj,
                                   mm_evt_paylod_set_get_stream_parms_t *payload)
{
    int32_t rc = -1;
    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                               payload->stream_id);
    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* No op. on linked streams */
            return 0;
        }

        rc = mm_stream_fsm_fn(s_obj,
                              MM_STREAM_EVT_SET_PARM,
                              (void *)payload,
                              NULL);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_get_stream_parms
 *
 * DESCRIPTION: get parameters per stream
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @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_channel_get_stream_parm(mm_channel_t *my_obj,
                                   mm_evt_paylod_set_get_stream_parms_t *payload)
{
    int32_t rc = -1;
    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                               payload->stream_id);
    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* No op. on linked streams */
            return 0;
        }

        rc = mm_stream_fsm_fn(s_obj,
                              MM_STREAM_EVT_GET_PARM,
                              (void *)payload,
                              NULL);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_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       : channel object
 *   @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_channel_do_stream_action(mm_channel_t *my_obj,
                                   mm_evt_paylod_do_stream_action_t *payload)
{
    int32_t rc = -1;
    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                               payload->stream_id);
    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* No op. on linked streams */
            return 0;
        }

        rc = mm_stream_fsm_fn(s_obj,
                              MM_STREAM_EVT_DO_ACTION,
                              (void *)payload,
                              NULL);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_map_stream_buf
 *
 * DESCRIPTION: mapping stream buffer via domain socket to server
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @payload      : ptr to payload for mapping
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_map_stream_buf(mm_channel_t *my_obj,
                                  cam_buf_map_type *payload)
{
    int32_t rc = -1;
    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                               payload->stream_id);
    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* No op. on linked streams */
            return 0;
        }

        rc = mm_stream_map_buf(s_obj,
                               payload->type,
                               payload->frame_idx,
                               payload->plane_idx,
                               payload->fd,
                               payload->size);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_map_stream_bufs
 *
 * DESCRIPTION: mapping stream buffers via domain socket to server
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @payload      : ptr to payload for mapping
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_map_stream_bufs(mm_channel_t *my_obj,
                                   cam_buf_map_type_list *payload)
{
    int32_t rc = -1;
    if ((payload == NULL) || (payload->length == 0)) {
        return rc;
    }

    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                               payload->buf_maps[0].stream_id);
    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* No op. on linked streams */
            return 0;
        }

        rc = mm_stream_map_bufs(s_obj, payload);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_unmap_stream_buf
 *
 * DESCRIPTION: unmapping stream buffer via domain socket to server
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @payload      : ptr to unmap payload
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_unmap_stream_buf(mm_channel_t *my_obj,
                                    cam_buf_unmap_type *payload)
{
    int32_t rc = -1;
    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                               payload->stream_id);
    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* No op. on linked streams */
            return 0;
        }

        rc = mm_stream_unmap_buf(s_obj, payload->type,
                                 payload->frame_idx, payload->plane_idx);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_queue_init
 *
 * DESCRIPTION: initialize superbuf queue in the channel
 *
 * PARAMETERS :
 *   @queue   : ptr to superbuf queue to be initialized
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_superbuf_queue_init(mm_channel_queue_t * queue)
{
    return cam_queue_init(&queue->que);
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_queue_deinit
 *
 * DESCRIPTION: deinitialize superbuf queue in the channel
 *
 * PARAMETERS :
 *   @queue   : ptr to superbuf queue to be deinitialized
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_superbuf_queue_deinit(mm_channel_queue_t * queue)
{
    return cam_queue_deinit(&queue->que);
}

/*===========================================================================
 * FUNCTION   : mm_channel_util_seq_comp_w_rollover
 *
 * DESCRIPTION: utility function to handle sequence number comparison with rollover
 *
 * PARAMETERS :
 *   @v1      : first value to be compared
 *   @v2      : second value to be compared
 *
 * RETURN     : int8_t type of comparison result
 *              >0  -- v1 larger than v2
 *              =0  -- vi equal to v2
 *              <0  -- v1 smaller than v2
 *==========================================================================*/
int8_t mm_channel_util_seq_comp_w_rollover(uint32_t v1,
                                           uint32_t v2)
{
    int8_t ret = 0;

    /* TODO: need to handle the case if v2 roll over to 0 */
    if (v1 > v2) {
        ret = 1;
    } else if (v1 < v2) {
        ret = -1;
    }

    return ret;
}

/*===========================================================================
 * FUNCTION   : mm_channel_handle_metadata
 *
 * DESCRIPTION: Handle frame matching logic change due to metadata
 *
 * PARAMETERS :
 *   @ch_obj  : channel object
 *   @queue   : superbuf queue
 *   @buf_info: new buffer from stream
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_handle_metadata(
                        mm_channel_t* ch_obj,
                        mm_channel_queue_t * queue,
                        mm_camera_buf_info_t *buf_info)
{

    int rc = 0 ;
    mm_stream_t* stream_obj = NULL;
    stream_obj = mm_channel_util_get_stream_by_handler(ch_obj,
                buf_info->stream_id);
    uint8_t is_prep_snapshot_done_valid = 0;
    uint8_t is_good_frame_idx_range_valid = 0;
    int32_t prep_snapshot_done_state = 0;
    cam_frame_idx_range_t good_frame_idx_range;
    uint8_t is_crop_1x_found = 0;
    uint32_t snapshot_stream_id = 0;
    uint32_t i;
    /* Set expected frame id to a future frame idx, large enough to wait
    * for good_frame_idx_range, and small enough to still capture an image */
    const uint32_t max_future_frame_offset = MM_CAMERA_MAX_FUTURE_FRAME_WAIT;

    memset(&good_frame_idx_range, 0, sizeof(good_frame_idx_range));

    if (NULL == stream_obj) {
        LOGE("Invalid Stream Object for stream_id = %d",
                    buf_info->stream_id);
        rc = -1;
        goto end;
    }
    if (NULL == stream_obj->stream_info) {
        LOGE("NULL stream info for stream_id = %d",
                     buf_info->stream_id);
        rc = -1;
        goto end;
    }

    if ((CAM_STREAM_TYPE_METADATA == stream_obj->stream_info->stream_type) &&
            ((stream_obj->ch_obj == ch_obj) ||
            ((stream_obj->linked_stream != NULL) &&
            (stream_obj->linked_stream->linked_obj == ch_obj)))) {
        const metadata_buffer_t *metadata;
        metadata = (const metadata_buffer_t *)buf_info->buf->buffer;

        if (NULL == metadata) {
            LOGE("NULL metadata buffer for metadata stream");
            rc = -1;
            goto end;
        }
        LOGL("E , expected frame id: %d", queue->expected_frame_id);

        IF_META_AVAILABLE(const int32_t, p_prep_snapshot_done_state,
                CAM_INTF_META_PREP_SNAPSHOT_DONE, metadata) {
            prep_snapshot_done_state = *p_prep_snapshot_done_state;
            is_prep_snapshot_done_valid = 1;
            LOGH("prepare snapshot done valid ");
        }
        IF_META_AVAILABLE(const cam_frame_idx_range_t, p_good_frame_idx_range,
                CAM_INTF_META_GOOD_FRAME_IDX_RANGE, metadata) {
            good_frame_idx_range = *p_good_frame_idx_range;
            is_good_frame_idx_range_valid = 1;
            LOGH("good_frame_idx_range : min: %d, max: %d , num frames = %d",
                 good_frame_idx_range.min_frame_idx,
                good_frame_idx_range.max_frame_idx, good_frame_idx_range.num_led_on_frames);
        }
        IF_META_AVAILABLE(const cam_crop_data_t, p_crop_data,
                CAM_INTF_META_CROP_DATA, metadata) {
            cam_crop_data_t crop_data = *p_crop_data;

            for (i = 0; i < ARRAY_SIZE(ch_obj->streams); i++) {
                if (MM_STREAM_STATE_NOTUSED == ch_obj->streams[i].state) {
                    continue;
                }
                if (CAM_STREAM_TYPE_SNAPSHOT ==
                    ch_obj->streams[i].stream_info->stream_type) {
                    snapshot_stream_id = ch_obj->streams[i].server_stream_id;
                    break;
                }
            }

            for (i=0; i<crop_data.num_of_streams; i++) {
                if (snapshot_stream_id == crop_data.crop_info[i].stream_id) {
                    if (!crop_data.crop_info[i].crop.left &&
                            !crop_data.crop_info[i].crop.top) {
                        is_crop_1x_found = 1;
                        break;
                    }
                }
            }
        }

        IF_META_AVAILABLE(const cam_buf_divert_info_t, p_divert_info,
                CAM_INTF_BUF_DIVERT_INFO, metadata) {
            cam_buf_divert_info_t divert_info = *p_divert_info;
            if (divert_info.frame_id >= buf_info->frame_idx) {
                ch_obj->diverted_frame_id = divert_info.frame_id;
            } else {
                ch_obj->diverted_frame_id = 0;
            }
        }

        if (ch_obj->isZoom1xFrameRequested) {
            if (is_crop_1x_found) {
                ch_obj->isZoom1xFrameRequested = 0;
                queue->expected_frame_id = buf_info->frame_idx + 1;
            } else {
                queue->expected_frame_id += max_future_frame_offset;
                /* Flush unwanted frames */
                mm_channel_superbuf_flush_matched(ch_obj, queue);
            }
            goto end;
        }

        if (ch_obj->startZSlSnapshotCalled && is_good_frame_idx_range_valid) {
            LOGI("frameID = %d, expected = %d good_frame_idx = %d",
                    buf_info->frame_idx, queue->expected_frame_id,
                    good_frame_idx_range.min_frame_idx);
        }

        if (is_prep_snapshot_done_valid) {
            ch_obj->bWaitForPrepSnapshotDone = 0;
            if (prep_snapshot_done_state == NEED_FUTURE_FRAME) {
                queue->expected_frame_id += max_future_frame_offset;
                LOGI("PreFlash Done. Need Main Flash");

                mm_channel_superbuf_flush(ch_obj,
                        queue, CAM_STREAM_TYPE_DEFAULT);

                ch_obj->needLEDFlash = TRUE;
            } else {
                ch_obj->needLEDFlash = FALSE;
            }
        }
        if (is_good_frame_idx_range_valid) {
            queue->expected_frame_id =
                good_frame_idx_range.min_frame_idx;
             if((ch_obj->needLEDFlash == TRUE) && (ch_obj->burstSnapNum > 1)) {
                queue->led_on_start_frame_id =
                good_frame_idx_range.min_frame_idx;
                queue->led_off_start_frame_id =
                good_frame_idx_range.max_frame_idx;
                queue->once = 0;
                queue->led_on_num_frames =
                  good_frame_idx_range.num_led_on_frames;
                queue->frame_skip_count = good_frame_idx_range.frame_skip_count;
                LOGD("Need Flash, expected frame id = %d,"
                        " led_on start = %d, led off start = %d, led on frames = %d ",
                           queue->expected_frame_id, queue->led_on_start_frame_id,
                        queue->led_off_start_frame_id, queue->led_on_num_frames);
            } else {
                LOGD("No flash, expected frame id = %d ",
                         queue->expected_frame_id);
            }
        } else if ((MM_CHANNEL_BRACKETING_STATE_WAIT_GOOD_FRAME_IDX == ch_obj->bracketingState) &&
                !is_prep_snapshot_done_valid) {
            /* Flush unwanted frames */
            mm_channel_superbuf_flush_matched(ch_obj, queue);
            queue->expected_frame_id += max_future_frame_offset;
        }
        if (ch_obj->isFlashBracketingEnabled &&
            is_good_frame_idx_range_valid) {
            /* Flash bracketing needs two frames, with & without led flash.
            * in valid range min frame is with led flash and max frame is
            * without led flash */
            queue->expected_frame_id =
                good_frame_idx_range.min_frame_idx;
            /* max frame is without led flash */
            queue->expected_frame_id_without_led =
                good_frame_idx_range.max_frame_idx;

        } else if (is_good_frame_idx_range_valid) {
            queue->expected_frame_id =
                    good_frame_idx_range.min_frame_idx;

            ch_obj->bracketingState = MM_CHANNEL_BRACKETING_STATE_ACTIVE;
        }

        if (ch_obj->isConfigCapture && is_good_frame_idx_range_valid
                && (good_frame_idx_range.config_batch_idx < ch_obj->frameConfig.num_batch)) {

            LOGI("Frame Config: Expcted ID = %d batch index = %d",
                    good_frame_idx_range.min_frame_idx, good_frame_idx_range.config_batch_idx);
            ch_obj->capture_frame_id[good_frame_idx_range.config_batch_idx] =
                    good_frame_idx_range.min_frame_idx;

            if (ch_obj->cur_capture_idx == good_frame_idx_range.config_batch_idx) {
                queue->expected_frame_id =
                        good_frame_idx_range.min_frame_idx;
            } else {
                queue->expected_frame_id =
                        ch_obj->capture_frame_id[ch_obj->cur_capture_idx];
            }
        }

        if ((ch_obj->burstSnapNum > 1) && (ch_obj->needLEDFlash == TRUE)
            && !ch_obj->isFlashBracketingEnabled
            && (MM_CHANNEL_BRACKETING_STATE_OFF == ch_obj->bracketingState)
            && !ch_obj->isConfigCapture) {
            if((buf_info->frame_idx >= queue->led_off_start_frame_id)
                    &&  !queue->once) {
                LOGD("Burst snap num = %d ",
                         ch_obj->burstSnapNum);
                // Skip frames from LED OFF frame to get a good frame
                queue->expected_frame_id = queue->led_off_start_frame_id +
                        queue->frame_skip_count;
                queue->once = 1;
                ch_obj->stopZslSnapshot = 1;
                ch_obj->needLEDFlash = FALSE;
                LOGD("Reached max led on frames = %d , expected id = %d",
                         buf_info->frame_idx, queue->expected_frame_id);
         }
       }

        IF_META_AVAILABLE(const cam_low_light_mode_t, low_light_level,
            CAM_INTF_META_LOW_LIGHT, metadata) {
            ch_obj->needLowLightZSL = *low_light_level;
        }

        // For the instant capture case, if AEC settles before expected frame ID from user,
        // reset the expected frame ID to current frame index.
        if (queue->attr.user_expected_frame_id > 0) {
            if (queue->attr.user_expected_frame_id > buf_info->frame_idx) {
                IF_META_AVAILABLE(const cam_3a_params_t, ae_params,
                    CAM_INTF_META_AEC_INFO, metadata) {
                    if (ae_params->settled) {
                        queue->expected_frame_id = buf_info->frame_idx;
                        // Reset the expected frame ID from HAL to 0
                        queue->attr.user_expected_frame_id = 0;
                        LOGD("AEC settled, reset expected frame ID from user");
                    }
                }
            } else {
                 // Reset the expected frame ID from HAL to 0 after
                 // current frame index is greater than expected id.
                queue->attr.user_expected_frame_id = 0;
                LOGD("reset expected frame ID from user as it reached the bound");
            }
        }
    }
end:
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_comp_and_enqueue
 *
 * DESCRIPTION: implementation for matching logic for superbuf
 *
 * PARAMETERS :
 *   @ch_obj  : channel object
 *   @queue   : superbuf queue
 *   @buf_info: new buffer from stream
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_superbuf_comp_and_enqueue(
                        mm_channel_t* ch_obj,
                        mm_channel_queue_t *queue,
                        mm_camera_buf_info_t *buf_info)
{
    cam_node_t* node = NULL;
    struct cam_list *head = NULL;
    struct cam_list *pos = NULL;
    mm_channel_queue_node_t* super_buf = NULL;
    uint8_t buf_s_idx, i, found_super_buf, unmatched_bundles;
    struct cam_list *last_buf, *insert_before_buf, *last_buf_ptr;

    LOGD("E");

    for (buf_s_idx = 0; buf_s_idx < queue->num_streams; buf_s_idx++) {
        if (buf_info->stream_id == queue->bundled_streams[buf_s_idx]) {
            break;
        }
    }

    if (buf_s_idx == queue->num_streams) {
        LOGE("buf from stream (%d) not bundled", buf_info->stream_id);
        return -1;
    }

    if(buf_info->frame_idx == 0) {
        mm_channel_qbuf(ch_obj, buf_info->buf);
        return 0;
    }

    if (mm_channel_handle_metadata(ch_obj, queue, buf_info) < 0) {
        mm_channel_qbuf(ch_obj, buf_info->buf);
        return -1;
    }

    if (mm_channel_util_seq_comp_w_rollover(buf_info->frame_idx,
                                            queue->expected_frame_id) < 0) {
        LOGH("incoming buf id(%d) is older than expected buf id(%d), will discard it",
                 buf_info->frame_idx, queue->expected_frame_id);
        mm_channel_qbuf(ch_obj, buf_info->buf);
        return 0;
    }

    /* comp */
    pthread_mutex_lock(&queue->que.lock);
    head = &queue->que.head.list;
    /* get the last one in the queue which is possibly having no matching */
    pos = head->next;

    found_super_buf = 0;
    unmatched_bundles = 0;
    last_buf = NULL;
    insert_before_buf = NULL;
    last_buf_ptr = NULL;

    while (pos != head) {
        node = member_of(pos, cam_node_t, list);
        super_buf = (mm_channel_queue_node_t*)node->data;

        if (NULL != super_buf) {
            if (super_buf->matched) {
                /* find a matched super buf, move to next one */
                pos = pos->next;
                continue;
            } else if ( buf_info->frame_idx == super_buf->frame_idx
                    /*Pick metadata greater than available frameID*/
                    || ((queue->attr.priority == MM_CAMERA_SUPER_BUF_PRIORITY_LOW)
                    && (super_buf->super_buf[buf_s_idx].frame_idx == 0)
                    && (buf_info->buf->stream_type == CAM_STREAM_TYPE_METADATA)
                    && (super_buf->frame_idx < buf_info->frame_idx))
                    /*Pick available metadata closest to frameID*/
                    || ((queue->attr.priority == MM_CAMERA_SUPER_BUF_PRIORITY_LOW)
                    && (buf_info->buf->stream_type != CAM_STREAM_TYPE_METADATA)
                    && (super_buf->super_buf[buf_s_idx].frame_idx == 0)
                    && (super_buf->frame_idx > buf_info->frame_idx))){
                /*super buffer frame IDs matching OR In low priority bundling
                metadata frameID greater than avialbale super buffer frameID  OR
                metadata frame closest to incoming frameID will be bundled*/
                found_super_buf = 1;
                break;
            } else {
                unmatched_bundles++;
                if ( NULL == last_buf ) {
                    if ( super_buf->frame_idx < buf_info->frame_idx ) {
                        last_buf = pos;
                    }
                }
                if ( NULL == insert_before_buf ) {
                    if ( super_buf->frame_idx > buf_info->frame_idx ) {
                        insert_before_buf = pos;
                    }
                }
                pos = pos->next;
            }
        }
    }

    if ( found_super_buf ) {
        if(super_buf->super_buf[buf_s_idx].frame_idx != 0) {
            //This can cause frame drop. We are overwriting same memory.
            pthread_mutex_unlock(&queue->que.lock);
            LOGW("Warning: frame is already in camera ZSL queue");
            mm_channel_qbuf(ch_obj, buf_info->buf);
            return 0;
        }

        /*Insert incoming buffer to super buffer*/
        super_buf->super_buf[buf_s_idx] = *buf_info;

        /* check if superbuf is all matched */
        super_buf->matched = 1;
        for (i=0; i < super_buf->num_of_bufs; i++) {
            if (super_buf->super_buf[i].frame_idx == 0) {
                super_buf->matched = 0;
                break;
            }
        }

        if (super_buf->matched) {
            if(ch_obj->isFlashBracketingEnabled) {
               queue->expected_frame_id =
                   queue->expected_frame_id_without_led;
               if (buf_info->frame_idx >=
                       queue->expected_frame_id_without_led) {
                   ch_obj->isFlashBracketingEnabled = FALSE;
               }
            } else {
               queue->expected_frame_id = buf_info->frame_idx
                                          + queue->attr.post_frame_skip;
            }

            super_buf->expected = FALSE;

            LOGD("curr = %d, skip = %d , Expected Frame ID: %d",
                     buf_info->frame_idx,
                    queue->attr.post_frame_skip, queue->expected_frame_id);

            queue->match_cnt++;
            if (ch_obj->bundle.superbuf_queue.attr.enable_frame_sync) {
                pthread_mutex_lock(&fs_lock);
                mm_frame_sync_add(buf_info->frame_idx, ch_obj);
                pthread_mutex_unlock(&fs_lock);
            }
            /* Any older unmatched buffer need to be released */
            if ( last_buf ) {
                while ( last_buf != pos ) {
                    node = member_of(last_buf, cam_node_t, list);
                    super_buf = (mm_channel_queue_node_t*)node->data;
                    if (NULL != super_buf) {
                        for (i=0; i<super_buf->num_of_bufs; i++) {
                            if (super_buf->super_buf[i].frame_idx != 0) {
                                mm_channel_qbuf(ch_obj, super_buf->super_buf[i].buf);
                            }
                        }
                        queue->que.size--;
                        last_buf = last_buf->next;
                        cam_list_del_node(&node->list);
                        free(node);
                        free(super_buf);
                    } else {
                        LOGE("Invalid superbuf in queue!");
                        break;
                    }
                }
            }
        }else {
            if (ch_obj->diverted_frame_id == buf_info->frame_idx) {
                super_buf->expected = TRUE;
                ch_obj->diverted_frame_id = 0;
            }
        }
    } else {
        if ((queue->attr.max_unmatched_frames < unmatched_bundles)
                && ( NULL == last_buf )) {
            /* incoming frame is older than the last bundled one */
            mm_channel_qbuf(ch_obj, buf_info->buf);
        } else {
            last_buf_ptr = last_buf;

            /* Loop to remove unmatched frames */
            while ((queue->attr.max_unmatched_frames < unmatched_bundles)
                    && (last_buf_ptr != NULL && last_buf_ptr != pos)) {
                node = member_of(last_buf_ptr, cam_node_t, list);
                super_buf = (mm_channel_queue_node_t*)node->data;
                if (NULL != super_buf && super_buf->expected == FALSE
                        && (&node->list != insert_before_buf)) {
                    for (i=0; i<super_buf->num_of_bufs; i++) {
                        if (super_buf->super_buf[i].frame_idx != 0) {
                            mm_channel_qbuf(ch_obj, super_buf->super_buf[i].buf);
                        }
                    }
                    queue->que.size--;
                    cam_list_del_node(&node->list);
                    free(node);
                    free(super_buf);
                    unmatched_bundles--;
                }
                last_buf_ptr = last_buf_ptr->next;
            }

            if (queue->attr.max_unmatched_frames < unmatched_bundles) {
                node = member_of(last_buf, cam_node_t, list);
                super_buf = (mm_channel_queue_node_t*)node->data;
                for (i=0; i<super_buf->num_of_bufs; i++) {
                    if (super_buf->super_buf[i].frame_idx != 0) {
                        mm_channel_qbuf(ch_obj, super_buf->super_buf[i].buf);
                    }
                }
                queue->que.size--;
                cam_list_del_node(&node->list);
                free(node);
                free(super_buf);
            }

            /* insert the new frame at the appropriate position. */

            mm_channel_queue_node_t *new_buf = NULL;
            cam_node_t* new_node = NULL;

            new_buf = (mm_channel_queue_node_t*)malloc(sizeof(mm_channel_queue_node_t));
            new_node = (cam_node_t*)malloc(sizeof(cam_node_t));
            if (NULL != new_buf && NULL != new_node) {
                memset(new_buf, 0, sizeof(mm_channel_queue_node_t));
                memset(new_node, 0, sizeof(cam_node_t));
                new_node->data = (void *)new_buf;
                new_buf->num_of_bufs = queue->num_streams;
                new_buf->super_buf[buf_s_idx] = *buf_info;
                new_buf->frame_idx = buf_info->frame_idx;

                if (ch_obj->diverted_frame_id == buf_info->frame_idx) {
                    new_buf->expected = TRUE;
                    ch_obj->diverted_frame_id = 0;
                }

                /* enqueue */
                if ( insert_before_buf ) {
                    cam_list_insert_before_node(&new_node->list, insert_before_buf);
                } else {
                    cam_list_add_tail_node(&new_node->list, &queue->que.head.list);
                }
                queue->que.size++;

                if(queue->num_streams == 1) {
                    new_buf->matched = 1;
                    new_buf->expected = FALSE;
                    queue->expected_frame_id = buf_info->frame_idx + queue->attr.post_frame_skip;
                    queue->match_cnt++;
                    if (ch_obj->bundle.superbuf_queue.attr.enable_frame_sync) {
                        pthread_mutex_lock(&fs_lock);
                        mm_frame_sync_add(buf_info->frame_idx, ch_obj);
                        pthread_mutex_unlock(&fs_lock);
                    }
                }
            } else {
                /* No memory */
                if (NULL != new_buf) {
                    free(new_buf);
                }
                if (NULL != new_node) {
                    free(new_node);
                }
                /* qbuf the new buf since we cannot enqueue */
                mm_channel_qbuf(ch_obj, buf_info->buf);
            }
        }
    }

    pthread_mutex_unlock(&queue->que.lock);
    LOGD("X");
    return 0;
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_dequeue_internal
 *
 * DESCRIPTION: internal implementation for dequeue from the superbuf queue
 *
 * PARAMETERS :
 *   @queue   : superbuf queue
 *   @matched_only : if dequeued buf should be matched
 *   @ch_obj  : channel object
 *
 * RETURN     : ptr to a node from superbuf queue
 *==========================================================================*/
mm_channel_queue_node_t* mm_channel_superbuf_dequeue_internal(
        mm_channel_queue_t * queue,
        uint8_t matched_only, mm_channel_t *ch_obj)
{
    cam_node_t* node = NULL;
    struct cam_list *head = NULL;
    struct cam_list *pos = NULL;
    mm_channel_queue_node_t* super_buf = NULL;

    head = &queue->que.head.list;
    pos = head->next;
    if (pos != head) {
        /* get the first node */
        node = member_of(pos, cam_node_t, list);
        super_buf = (mm_channel_queue_node_t*)node->data;
        if ( (NULL != super_buf) &&
             (matched_only == TRUE) &&
             (super_buf->matched == FALSE) ) {
            /* require to dequeue matched frame only, but this superbuf is not matched,
               simply set return ptr to NULL */
            super_buf = NULL;
        }
        if (NULL != super_buf) {
            /* remove from the queue */
            cam_list_del_node(&node->list);
            queue->que.size--;
            if (super_buf->matched == TRUE) {
                queue->match_cnt--;
                if (ch_obj->bundle.superbuf_queue.attr.enable_frame_sync) {
                    pthread_mutex_lock(&fs_lock);
                    mm_frame_sync_remove(super_buf->frame_idx);
                    pthread_mutex_unlock(&fs_lock);
                }
            }
            free(node);
        }
    }

    return super_buf;
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_dequeue_frame_internal
 *
 * DESCRIPTION: internal implementation for dequeue based on frame index
 *                     from the superbuf queue
 *
 * PARAMETERS :
 *   @queue       : superbuf queue
 *   @frame_idx  : frame index to be dequeued
 *
 * RETURN     : ptr to a node from superbuf queue with matched frame index
 *                : NULL if not found
 *==========================================================================*/
mm_channel_queue_node_t* mm_channel_superbuf_dequeue_frame_internal(
        mm_channel_queue_t * queue, uint32_t frame_idx)
{
    cam_node_t* node = NULL;
    struct cam_list *head = NULL;
    struct cam_list *pos = NULL;
    mm_channel_queue_node_t* super_buf = NULL;

    if (!queue) {
        LOGE("queue is NULL");
        return NULL;
    }

    head = &queue->que.head.list;
    pos = head->next;
    LOGL("Searching for match frame %d", frame_idx);
    while ((pos != head) && (pos != NULL)) {
        /* get the first node */
        node = member_of(pos, cam_node_t, list);
        super_buf = (mm_channel_queue_node_t*)node->data;
        if (super_buf && super_buf->matched &&
                (super_buf->frame_idx == frame_idx)) {
            /* remove from the queue */
            cam_list_del_node(&node->list);
            queue->que.size--;
            queue->match_cnt--;
            LOGH("Found match frame %d", frame_idx);
            free(node);
            break;
        }
        else {
            LOGH("match frame not found %d", frame_idx);
            super_buf = NULL;
        }
        pos = pos->next;
    }
    return super_buf;
}


/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_dequeue
 *
 * DESCRIPTION: dequeue from the superbuf queue
 *
 * PARAMETERS :
 *   @queue   : superbuf queue
 *   @ch_obj  : channel object
 *
 * RETURN     : ptr to a node from superbuf queue
 *==========================================================================*/
mm_channel_queue_node_t* mm_channel_superbuf_dequeue(
        mm_channel_queue_t * queue, mm_channel_t *ch_obj)
{
    mm_channel_queue_node_t* super_buf = NULL;

    pthread_mutex_lock(&queue->que.lock);
    super_buf = mm_channel_superbuf_dequeue_internal(queue, TRUE, ch_obj);
    pthread_mutex_unlock(&queue->que.lock);

    return super_buf;
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_bufdone_overflow
 *
 * DESCRIPTION: keep superbuf queue no larger than watermark set by upper layer
 *              via channel attribute
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *   @queue   : superbuf queue
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_superbuf_bufdone_overflow(mm_channel_t* my_obj,
                                             mm_channel_queue_t * queue)
{
    int32_t rc = 0, i;
    mm_channel_queue_node_t* super_buf = NULL;
    if (MM_CAMERA_SUPER_BUF_NOTIFY_CONTINUOUS == queue->attr.notify_mode) {
        /* for continuous streaming mode, no overflow is needed */
        return 0;
    }

    LOGD("before match_cnt=%d, water_mark=%d",
          queue->match_cnt, queue->attr.water_mark);
    /* bufdone overflowed bufs */
    pthread_mutex_lock(&queue->que.lock);
    while (queue->match_cnt > queue->attr.water_mark) {
        super_buf = mm_channel_superbuf_dequeue_internal(queue, TRUE, my_obj);
        if (NULL != super_buf) {
            for (i=0; i<super_buf->num_of_bufs; i++) {
                if (NULL != super_buf->super_buf[i].buf) {
                    mm_channel_qbuf(my_obj, super_buf->super_buf[i].buf);
                }
            }
            free(super_buf);
        }
    }
    pthread_mutex_unlock(&queue->que.lock);
    LOGD("after match_cnt=%d, water_mark=%d",
          queue->match_cnt, queue->attr.water_mark);

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_skip
 *
 * DESCRIPTION: depends on the lookback configuration of the channel attribute,
 *              unwanted superbufs will be removed from the superbuf queue.
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *   @queue   : superbuf queue
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_superbuf_skip(mm_channel_t* my_obj,
                                 mm_channel_queue_t * queue)
{
    int32_t rc = 0, i;
    mm_channel_queue_node_t* super_buf = NULL;
    if (MM_CAMERA_SUPER_BUF_NOTIFY_CONTINUOUS == queue->attr.notify_mode) {
        /* for continuous streaming mode, no skip is needed */
        return 0;
    }

    /* bufdone overflowed bufs */
    pthread_mutex_lock(&queue->que.lock);
    while (queue->match_cnt > queue->attr.look_back) {
        super_buf = mm_channel_superbuf_dequeue_internal(queue, TRUE, my_obj);
        if (NULL != super_buf) {
            for (i=0; i<super_buf->num_of_bufs; i++) {
                if (NULL != super_buf->super_buf[i].buf) {
                    mm_channel_qbuf(my_obj, super_buf->super_buf[i].buf);
                }
            }
            free(super_buf);
        }
    }
    pthread_mutex_unlock(&queue->que.lock);

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_flush
 *
 * DESCRIPTION: flush the superbuf queue.
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *   @queue   : superbuf queue
 *   @cam_type: flush only particular type (default flushes all)
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_superbuf_flush(mm_channel_t* my_obj,
        mm_channel_queue_t * queue, cam_stream_type_t cam_type)
{
    int32_t rc = 0, i;
    mm_channel_queue_node_t* super_buf = NULL;
    cam_stream_type_t stream_type = CAM_STREAM_TYPE_DEFAULT;

    /* bufdone bufs */
    pthread_mutex_lock(&queue->que.lock);
    super_buf = mm_channel_superbuf_dequeue_internal(queue, FALSE, my_obj);
    while (super_buf != NULL) {
        for (i=0; i<super_buf->num_of_bufs; i++) {
            if (NULL != super_buf->super_buf[i].buf) {
                stream_type = super_buf->super_buf[i].buf->stream_type;
                if ((CAM_STREAM_TYPE_DEFAULT == cam_type) ||
                        (cam_type == stream_type)) {
                    mm_channel_qbuf(my_obj, super_buf->super_buf[i].buf);
                }
            }
        }
        free(super_buf);
        super_buf = mm_channel_superbuf_dequeue_internal(queue, FALSE, my_obj);
    }
    pthread_mutex_unlock(&queue->que.lock);

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_proc_general_cmd
 *
 * DESCRIPTION: process general command
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *   @notify_mode : notification mode
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_proc_general_cmd(mm_channel_t *my_obj,
                                      mm_camera_generic_cmd_t *p_gen_cmd)
{
    LOGD("E");
    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->u.gen_cmd = *p_gen_cmd;
        node->cmd_type = MM_CAMERA_CMD_TYPE_GENERAL;

        /* enqueue to cmd thread */
        cam_queue_enq(&(my_obj->cmd_thread.cmd_queue), node);

        /* wake up cmd thread */
        cam_sem_post(&(my_obj->cmd_thread.cmd_sem));
    } else {
        LOGE("No memory for mm_camera_node_t");
        rc = -1;
    }
    LOGD("X");

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_flush_matched
 *
 * DESCRIPTION: flush matched buffers from the superbuf queue.
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *   @queue   : superbuf queue
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_superbuf_flush_matched(mm_channel_t* my_obj,
                                  mm_channel_queue_t * queue)
{
    int32_t rc = 0, i;
    mm_channel_queue_node_t* super_buf = NULL;

    /* bufdone bufs */
    pthread_mutex_lock(&queue->que.lock);
    super_buf = mm_channel_superbuf_dequeue_internal(queue, TRUE, my_obj);
    while (super_buf != NULL) {
        for (i=0; i<super_buf->num_of_bufs; i++) {
            if (NULL != super_buf->super_buf[i].buf) {
                mm_channel_qbuf(my_obj, super_buf->super_buf[i].buf);
            }
        }
        free(super_buf);
        super_buf = mm_channel_superbuf_dequeue_internal(queue, TRUE, my_obj);
    }
    pthread_mutex_unlock(&queue->que.lock);

    return rc;
}


/*===========================================================================
 * FUNCTION   : mm_frame_sync_reset
 *
 * DESCRIPTION: Reset Frame sync info
 *
 * RETURN     : None
 *==========================================================================*/
void mm_frame_sync_reset() {
    memset(&fs, 0x0, sizeof(fs));
    LOGD("Reset Done");
}

/*===========================================================================
 * FUNCTION   : mm_frame_sync_register_channel
 *
 * DESCRIPTION: Register Channel for frame sync
 *
 * PARAMETERS :
 *   @ch_obj  : channel object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_frame_sync_register_channel(mm_channel_t *ch_obj) {
    // Lock frame sync info
    pthread_mutex_lock(&fs_lock);
    if ((fs.num_cam >= MAX_NUM_CAMERA_PER_BUNDLE) || (!ch_obj)) {
        LOGE("Error!! num cam(%d) is out of range ",
                 fs.num_cam);
        pthread_mutex_unlock(&fs_lock);
        return -1;
    }
    if (fs.num_cam == 0) {
        LOGH("First channel registering!!");
        mm_frame_sync_reset();
    }
    uint8_t i = 0;
    for (i = 0; i < MAX_NUM_CAMERA_PER_BUNDLE; i++) {
        if (fs.ch_obj[i] == NULL) {
            fs.ch_obj[i] = ch_obj;
            fs.cb[i] = ch_obj->bundle.super_buf_notify_cb;
            fs.num_cam++;
            LOGD("DBG_FS index %d", i);
            break;
        }
    }
    if (i >= MAX_NUM_CAMERA_PER_BUNDLE) {
        LOGH("X, DBG_FS Cannot register channel!!");
        pthread_mutex_unlock(&fs_lock);
        return -1;
    }
    LOGH("num_cam %d ", fs.num_cam);
    pthread_mutex_unlock(&fs_lock);
    return 0;
}

/*===========================================================================
 * FUNCTION   : mm_frame_sync_unregister_channel
 *
 * DESCRIPTION: un-register Channel for frame sync
 *
 * PARAMETERS :
 *   @ch_obj  : channel object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_frame_sync_unregister_channel(mm_channel_t *ch_obj) {
    uint8_t i = 0;
    // Lock frame sync info
    pthread_mutex_lock(&fs_lock);
    if (!fs.num_cam || !ch_obj) {
        LOGH("X, DBG_FS: channel not found  !!");
        // Lock frame sync info
        pthread_mutex_unlock(&fs_lock);
        return -1;
    }
    for (i = 0; i < MAX_NUM_CAMERA_PER_BUNDLE; i++) {
        if (fs.ch_obj[i] == ch_obj) {
            LOGD("found ch_obj at i (%d) ", i);
            break;
        }
    }
    if (i < MAX_NUM_CAMERA_PER_BUNDLE) {
        LOGD("remove channel info ");
        fs.ch_obj[i] = NULL;
        fs.cb[i] = NULL;
        fs.num_cam--;
    } else {
        LOGD("DBG_FS Channel not found ");
    }
    if (fs.num_cam == 0) {
        mm_frame_sync_reset();
    }
    LOGH("X, fs.num_cam %d", fs.num_cam);
    pthread_mutex_unlock(&fs_lock);
    return 0;
}


/*===========================================================================
 * FUNCTION   : mm_frame_sync_add
 *
 * DESCRIPTION: Add frame info into frame sync nodes
 *
 * PARAMETERS :
 *   @frame_id  : frame id to be added
 *   @ch_obj  : channel object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_frame_sync_add(uint32_t frame_id, mm_channel_t *ch_obj) {

    LOGD("E, frame id %d ch_obj %p", frame_id, ch_obj);
    if (!frame_id || !ch_obj) {
        LOGH("X : Error, cannot add sync frame !!");
        return -1;
    }

    int8_t ch_idx = -1;
    uint8_t i = 0;
    for (i = 0; i < MAX_NUM_CAMERA_PER_BUNDLE; i++) {
        if (fs.ch_obj[i] == ch_obj) {
            ch_idx = i;
            LOGD("ch id %d ", ch_idx);
            break;
        }
    }
    if (ch_idx < 0) {
        LOGH("X : DBG_FS ch not found!!");
        return -1;
    }
    int8_t index = mm_frame_sync_find_frame_index(frame_id);
    if ((index >= 0) && (index < MM_CAMERA_FRAME_SYNC_NODES)) {
        fs.node[index].frame_valid[ch_idx] = 1;
    } else if (index < 0) {
        if (fs.pos >= MM_CAMERA_FRAME_SYNC_NODES) {
            fs.pos = 0;
        }
        index = fs.pos;
        memset(&fs.node[index], 0x00, sizeof(mm_channel_sync_node_t));
        fs.pos++;
        fs.node[index].frame_idx = frame_id;
        fs.node[index].frame_valid[ch_idx] = 1;
        if (fs.num_cam == 1) {
            LOGD("Single camera frame %d , matched ", frame_id);
            fs.node[index].matched = 1;
        }
    }
    uint8_t frames_valid = 0;
    if (!fs.node[index].matched) {
        for (i = 0; i < MAX_NUM_CAMERA_PER_BUNDLE; i++) {
            if (fs.node[index].frame_valid[i]) {
                frames_valid++;
            }
        }
        if (frames_valid == fs.num_cam) {
            fs.node[index].matched = 1;
            LOGD("dual camera frame %d , matched ",
                     frame_id);
        }
    }
    return 0;
}

/*===========================================================================
 * FUNCTION   : mm_frame_sync_remove
 *
 * DESCRIPTION: Remove frame info from frame sync nodes
 *
 * PARAMETERS :
 *   @frame_id  : frame id to be removed
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_frame_sync_remove(uint32_t frame_id) {
    int8_t index = -1;

    LOGD("E, frame_id %d", frame_id);
    if (!frame_id) {
        LOGE("X, DBG_FS frame id invalid");
        return -1;
    }

    index = mm_frame_sync_find_frame_index(frame_id);
    if ((index >= 0) && (index < MM_CAMERA_FRAME_SYNC_NODES)) {
        LOGD("Removing sync frame %d", frame_id);
        memset(&fs.node[index], 0x00, sizeof(mm_channel_sync_node_t));
    }
    LOGD("X ");
    return 0;
}

/*===========================================================================
 * FUNCTION   : mm_frame_sync_find_matched
 *
 * DESCRIPTION: Find  a matched sync frame from the node array
 *
 * PARAMETERS :
 *   @oldest  : If enabled, find oldest matched frame.,
 *                  If not enabled, get the first matched frame found
 *
 * RETURN     : unt32_t type of status
 *              0  -- If no matched frames found
 *              frame index: inf matched frame found
 *==========================================================================*/
uint32_t mm_frame_sync_find_matched(uint8_t oldest) {
    LOGH("E, oldest %d ", oldest);
    uint8_t i = 0;
    uint32_t frame_idx = 0;
    uint32_t curr_frame_idx = 0;
    for (i = 0; i < MM_CAMERA_FRAME_SYNC_NODES; i++) {
        if (fs.node[i].matched) {
            curr_frame_idx = fs.node[i].frame_idx;
            if (!frame_idx) {
                frame_idx = curr_frame_idx;
            }
            if (!oldest) {
                break;
            } else if (frame_idx > curr_frame_idx) {
                frame_idx = curr_frame_idx;
            }
        }
    }
    LOGH("X, oldest %d frame idx %d", oldest, frame_idx);
    return frame_idx;
}

/*===========================================================================
 * FUNCTION   : mm_frame_sync_find_frame_index
 *
 * DESCRIPTION: Find sync frame index if present
 *
 * PARAMETERS :
 *   @frame_id  : frame id to be searched
 *
 * RETURN     : int8_t type of status
 *              -1  -- If desired frame not found
 *              index: node array index if frame is found
 *==========================================================================*/
int8_t mm_frame_sync_find_frame_index(uint32_t frame_id) {

    LOGD("E, frame_id %d", frame_id);
    int8_t index = -1, i = 0;
    for (i = 0; i < MM_CAMERA_FRAME_SYNC_NODES; i++) {
        if (fs.node[i].frame_idx == frame_id) {
            index = i;
            break;
        }
    }
    LOGD("X index :%d", index);
    return index;
}

/*===========================================================================
 * FUNCTION   : mm_frame_sync_lock_queues
 *
 * DESCRIPTION: Lock all channel queues present in node info
 *
 * RETURN     : None
 *==========================================================================*/
void mm_frame_sync_lock_queues() {
    uint8_t j = 0;
    LOGD("E ");
    for (j = 0; j < MAX_NUM_CAMERA_PER_BUNDLE; j++) {
        if (fs.ch_obj[j]) {
            mm_channel_queue_t *ch_queue =
                    &fs.ch_obj[j]->bundle.superbuf_queue;
            if (ch_queue) {
                pthread_mutex_lock(&ch_queue->que.lock);
                LOGL("Done locking fs.ch_obj[%d] ", j);
            }
        }
    }
    pthread_mutex_lock(&fs_lock);
    LOGD("X ");
}

/*===========================================================================
 * FUNCTION   : mm_frame_sync_unlock_queues
 *
 * DESCRIPTION: Unlock all channel queues
 *
 * RETURN     : None
 *==========================================================================*/
void mm_frame_sync_unlock_queues() {
    // Unlock all queues
    uint8_t j = 0;
    LOGD("E ");
    pthread_mutex_unlock(&fs_lock);
    LOGL("Done unlocking fs ");
    for (j = 0; j < MAX_NUM_CAMERA_PER_BUNDLE; j++) {
        if (fs.ch_obj[j]) {
            mm_channel_queue_t *ch_queue =
                    &fs.ch_obj[j]->bundle.superbuf_queue;
            if (ch_queue) {
                pthread_mutex_unlock(&ch_queue->que.lock);
                LOGL("Done unlocking fs.ch_obj[%d] ", j);
            }
        }
    }
    LOGD("X ");
}

/*===========================================================================
 * FUNCTION   : mm_channel_node_qbuf
 *
 * DESCRIPTION: qbuf all buffers in a node
 *
 * PARAMETERS :
 *   @ch_obj  : Channel info
 *   @node    : node to qbuf
 *
 * RETURN     : None
 *==========================================================================*/
void mm_channel_node_qbuf(mm_channel_t *ch_obj, mm_channel_queue_node_t *node) {
    uint8_t i;
    if (!ch_obj || !node) {
        return;
    }
    for (i = 0; i < node->num_of_bufs; i++) {
        mm_channel_qbuf(ch_obj, node->super_buf[i].buf);
    }
    return;
}