/*
 $License:
   Copyright 2011 InvenSense, Inc.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
  $
 */

/******************************************************************************
 *
 * $Id: mldmp.c 5629 2011-06-11 03:13:08Z mcaramello $
 *
 *****************************************************************************/

/**
 * @addtogroup MLDMP
 *
 * @{
 *      @file     mldmp.c
 *      @brief    Shared functions between all the different DMP versions
**/

#include <stdio.h>

#include "mltypes.h"
#include "mlinclude.h"
#include "mltypes.h"
#include "ml.h"
#include "mldl_cfg.h"
#include "mldl.h"
#include "compass.h"
#include "mlSetGyroBias.h"
#include "mlsl.h"
#include "mlFIFO.h"
#include "mldmp.h"
#include "mlstates.h"
#include "dmpDefault.h"
#include "mlFIFOHW.h"
#include "mlsupervisor.h"

#include "log.h"
#undef MPL_LOG_TAG
#define MPL_LOG_TAG "MPL-dmp"

/**
 *  @brief  Open the default motion sensor engine.
 *          This function is used to open the default MPL engine, 
 *          featuring, for example, sensor fusion (6 axes and 9 axes), 
 *          sensor calibration, accelerometer data byte swapping, among 
 *          others.  
 *          Compare with the other provided engines.
 *
 *  @pre    inv_serial_start() must have been called to instantiate the serial 
 *          communication.
 *  
 *  Example:
 *  @code
 *    result = inv_dmp_open( );
 *    if (INV_SUCCESS != result) {
 *        // Handle the error case
 *    }
 *  @endcode
 *
 *  @return Zero on success; Error Code on any failure.
 *
 */
inv_error_t inv_dmp_open(void)
{
    INVENSENSE_FUNC_START;
    inv_error_t result;
    unsigned char state = inv_get_state();
    struct mldl_cfg *mldl_cfg;
    unsigned long requested_sensors;

    /*************************************************************
     * Common operations before calling DMPOpen
     ************************************************************/
    if (state == INV_STATE_DMP_OPENED)
        return INV_SUCCESS;

    if (state == INV_STATE_DMP_STARTED) {
        return inv_dmp_stop();
    }

    result = inv_state_transition(INV_STATE_DMP_OPENED);
    if (result) {
        LOG_RESULT_LOCATION(result);
        return result;
    }

    result = inv_dl_open(inv_get_serial_handle());
    if (result) {
        LOG_RESULT_LOCATION(result);
        return result;
    }
#ifdef ML_USE_DMP_SIM
    do {
        void setup_univ();
        setup_univ();           /* hijack the read and write paths 
                                   and re-direct them to the simulator */
    } while (0);
#endif

    result = inv_setup_dmp();
    if (result) {
        LOG_RESULT_LOCATION(result);
        return result;
    }

    // Init vars.
    inv_init_ml();

    result = inv_init_fifo_param();
    if (result) {
        LOG_RESULT_LOCATION(result);
        return result;
    }
    result = inv_enable_set_bias();
    if (result) {
        LOG_RESULT_LOCATION(result);
        return result;
    }
    inv_init_fifo_hardare();
    mldl_cfg = inv_get_dl_config();
    requested_sensors = INV_THREE_AXIS_GYRO;
    if (mldl_cfg->accel && mldl_cfg->accel->resume)
        requested_sensors |= INV_THREE_AXIS_ACCEL;

    if (mldl_cfg->compass && mldl_cfg->compass->resume)
        requested_sensors |= INV_THREE_AXIS_COMPASS;

    if (mldl_cfg->pressure && mldl_cfg->pressure->resume)
        requested_sensors |= INV_THREE_AXIS_PRESSURE;

    result = inv_init_requested_sensors(requested_sensors);
    if (result) {
        LOG_RESULT_LOCATION(result);
        return result;
    }
    result = inv_apply_calibration();
    if (result) {
        LOG_RESULT_LOCATION(result);
        return result;
    }
    result = inv_apply_endian_accel();

    return result;
}

/**
 *  @brief  Start the DMP.
 *
 *  @pre    inv_dmp_open() must have been called.
 * 
 *  @code
 *     result = inv_dmp_start();
 *     if (INV_SUCCESS != result) {
 *         // Handle the error case
 *     }
 *  @endcode
 *
 *  @return INV_SUCCESS if successful, or Non-zero error code otherwise.
 */
inv_error_t inv_dmp_start(void)
{
    INVENSENSE_FUNC_START;
    inv_error_t result;

    if (inv_get_state() == INV_STATE_DMP_STARTED)
        return INV_SUCCESS;

    result = inv_state_transition(INV_STATE_DMP_STARTED);
    if (result) {
        LOG_RESULT_LOCATION(result);
        return result;
    }
    inv_init_sensor_fusion_supervisor();
    result = inv_dl_start(inv_get_dl_config()->requested_sensors);
    if (result) {
        LOG_RESULT_LOCATION(result);
        return result;
    }
    /* This is done after the start since it will modify DMP memory, which 
     * will cause a full reset is most cases */
    result = inv_reset_motion();
    if (result) {
        LOG_RESULT_LOCATION(result);
        return result;
    }

    return result;
}

/**
 *  @brief  Stops the DMP and puts it in low power.
 *
 *  @pre    inv_dmp_start() must have been called.
 * 
 *  @return INV_SUCCESS, Non-zero error code otherwise.
 */
inv_error_t inv_dmp_stop(void)
{
    INVENSENSE_FUNC_START;
    inv_error_t result;

    if (inv_get_state() == INV_STATE_DMP_OPENED)
        return INV_SUCCESS;

    result = inv_state_transition(INV_STATE_DMP_OPENED);
    if (result) {
        LOG_RESULT_LOCATION(result);
        return result;
    }
    result = inv_dl_stop(INV_ALL_SENSORS);
    if (result) {
        LOG_RESULT_LOCATION(result);
        return result;
    }

    return result;
}

/**
 *  @brief  Closes the motion sensor engine.
 *          Does not close the serial communication. To do that,
 *          call inv_serial_stop().
 *          After calling inv_dmp_close() another DMP module can be
 *          loaded in the MPL with the corresponding necessary 
 *          intialization and configurations, via any of the 
 *          MLDmpXXXOpen functions.
 *
 *  @pre    inv_dmp_open() must have been called.
 * 
 *  @code
 *     result = inv_dmp_close();
 *     if (INV_SUCCESS != result) {
 *         // Handle the error case
 *     }
 *  @endcode
 *
 *  @return INV_SUCCESS, Non-zero error code otherwise.
 */
inv_error_t inv_dmp_close(void)
{
    INVENSENSE_FUNC_START;
    inv_error_t result;
    inv_error_t firstError = INV_SUCCESS;

    if (inv_get_state() <= INV_STATE_DMP_CLOSED)
        return INV_SUCCESS;

    result = inv_disable_set_bias();
    ERROR_CHECK_FIRST(firstError, result);

    result = inv_dl_stop(INV_ALL_SENSORS);
    ERROR_CHECK_FIRST(firstError, result);

    result = inv_close_fifo();
    ERROR_CHECK_FIRST(firstError, result);

    result = inv_dl_close();
    ERROR_CHECK_FIRST(firstError, result);

    result = inv_state_transition(INV_STATE_SERIAL_OPENED);
    ERROR_CHECK_FIRST(firstError, result);

    return result;
}

/**
 *  @}
 */