/*
 * Copyright (c) 2013 - 2014, Freescale Semiconductor, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o 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.
 *
 * o Neither the name of Freescale Semiconductor, Inc. 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
 */

#include "i2c_dac_slave.h"

/*******************************************************************************
 * Global Variables
 ******************************************************************************/
/* @param i2c0_rxData Buffer for received data from I2C bus*/
uint8_t i2c0_rxData[RX_DEPTH] = {0,0,0,0,0,0,0,0,0,0,0};
/* @param i2c0_txData Buffer for transmitted data to I2C bus*/
static uint8_t i2c0_txData[1] = {0x3E};        

static volatile int rxIndex = 0;
static volatile int txIndex = 0;

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/******************************************************************************/
/*!*/
/*! @brief Callback used by slave IRQ handler to put byte it received.*/
/*!*/
/*! This callback function is used by the I2C slave IRQ handler to deliver the*/
/*! byte it received to the application. If there is no room in the buffer, the*/
/*! callback function will report kStatus_OutOfRange.*/
/*!*/
/*! @param [in] rxData The byte of data from the driver to store in the applications buffer.*/
/*!*/
/*! @return kStatus_Success or kStatus_OutOfRange*/
/*!*/
/******************************************************************************/
static i2c_status_t data_sink(uint8_t rxData)
{
    if ( rxIndex < sizeof(i2c0_rxData ))
    {
        i2c0_rxData[rxIndex] = rxData;
        ++rxIndex;

    }
    else
    {
       return kStatus_I2C_OutOfRange;
    }

    return kStatus_I2C_Success;
}

/******************************************************************************/
/*!*/
/*! @brief Callback used by slave IRQ handler to get byte to transmit.*/
/*!*/
/*! This callback function is used by the I2C slave IRQ handler to supply the*/
/*! byte it transmitted by the application to the I2C driver. If there are no more*/
/*! bytes in the buffer, the callback function will report kStatus_OutOfRange.*/
/*!*/
/*! @param [out] *txData The byte of data from the applications buffer to*/
/*! return to the driver.*/
/*!*/
/*! @return kStatus_Success or kStatus_OutOfRange*/
/*!*/
/******************************************************************************/
static i2c_status_t data_source(uint8_t * txData)
{
    if ( txIndex < sizeof(i2c0_txData) )
    {
        *txData = i2c0_txData[txIndex];
        ++txIndex;
    }
    else
    {
        return kStatus_I2C_OutOfRange;}

    return kStatus_I2C_Success;
}


/******************************************************************************/
/*!*/
/*! @brief Callback used by slave IRQ handler to report an error condition.*/
/*!*/
/*! This callback function is used by the I2C slave IRQ handler to report an error*/
/*! condition. If the error is an OutOfRange error, the appropriate buffer index is*/
/*! reset.*/
/*!*/
/*! @param [in] error The error reported by the driver.*/
/*!*/
/*! @return nothing*/
/*!*/
/******************************************************************************/
static void on_error(i2c_status_t error)
{
    switch (error)
    {
        case kStatus_I2C_SlaveTxUnderrun:
          txIndex = 0;
          printf("Slave TX Underrun occurred!\n\n\r");
          while(1)
          {}
            break;

        case kStatus_I2C_SlaveRxOverrun:
          rxIndex = 0;
          printf("Slave RX Overrun occurred!\n\n\r");
          /*while(1);*/
            break;

        case kStatus_I2C_AribtrationLost:
          printf("Arbitration lost!\n\n\r");
          while(1)
          {}
            break;

        default:
            break;
    }
}


/********************************************************************/
int main (void)
{
    /* General Variables */
    char rValue = 0, gValue = 0, bValue = 0;
    char i2cSlaveVar = 0, i = 0;
    //i2c_slave_user_config_t Slave0Info;

    /* I2C Slave configuration structure */
    i2c_slave_user_config_t Slave0Info =
    {
        .data_source = data_source,
        .data_sink = data_sink,
        .on_error = on_error,
        .slaveAddress = SLAVE_BASE
    };
    
    /* SMC Power mode protection configurations */
    smc_power_mode_protection_config_t smc_power_protection_config = 
    {
        .vlpProt = true,
        .llsProt = false,
        .vllsProt = false
    };
    
    /* Configure the power mode protection */
    smc_hal_config_power_mode_protection(&smc_power_protection_config);
    
    /* SMC Power mode configuration variables */
    smc_power_mode_config_t smc_config_data = 
    {
        .powerModeName = kPowerModeRun,
        .stopSubMode = kSmcStopSub0,
        .lpwuiOption = false,
        .lpwuiOptionValue = false,
        .porOption = true,
        .porOptionValue = false
    };
    
    /* Initialize hardware */
    hardware_init();
    /* Init uart driver for stdio. */
    dbg_uart_init(); 
    
    printf("**************************************************\n\r");
    printf("* Starting I2C Demo...\n\r");
    printf("**************************************************\n\n\r");

    /* Initialize the GPIO pins */
    printf("Initializing GPIO...");
    
    gpio_init(i2cAddressPins, ledPins);
    
    printf(" complete.\n\n\r");

    /* Sample the GPIO pins to determine the I2C address */
    printf("Sampling input pins and setting slave address...");
    i2cSlaveVar = ((gpio_read_pin_input(kGpioI2Caddr1)) 
                   | (gpio_read_pin_input(kGpioI2Caddr2) << 1));
     
    printf(" complete.\n\n\r");
    
    /* Set new slave address */
    Slave0Info.slaveAddress = SLAVE_BASE | i2cSlaveVar;
    
    /* Inform user of the slave address */
    printf("Current Slave address:  0x%X\n\n\r", Slave0Info.slaveAddress);
    
    printf("Please ensure that you have made the connections specified\n\r");
    printf("in the I2C_DAC user guide before sending commands from the\n\r");
    printf("master.\n\n\r");
            
    /***************************************************************************
     * Init I2C driver.
     **************************************************************************/
    i2c_slave_init(BOARD_DAC_I2C_INSTANCE, &Slave0Info);
    
    /* Enable the General Call functionality of the I2C module */
    i2c_hal_set_general_call_enable(BOARD_DAC_I2C_INSTANCE, true);
    
    /* Configure the I2C module to wakeup the device on address match */
    i2c_hal_set_wakeup_enable(BOARD_DAC_I2C_INSTANCE, true);
    
    i2c0_txData[0] = Slave0Info.slaveAddress;
    
	while(1)
	{   
            while (rxIndex == 0)
            {
                /* Wait to receive data from the master */
            }
            
            switch(i2c0_rxData[0]) {
              case 1:
                /* Wait for 3 data bytes to be received before continuing */
                while(rxIndex < 4)
                {}
                
                /* This command should set the RBG value variables */
                rValue = i2c0_rxData[1];
                gValue = i2c0_rxData[2];
                bValue = i2c0_rxData[3];
                
                printf("**************************************************\n\r");
                printf("New RGB value received:  \n\r");
                printf("        R:  %d\n\r", rValue);
                printf("        G:  %d\n\r", gValue);
                printf("        B:  %d\n\n\r", bValue);
                
                break;
              case 2:
                /* This command should set the output of the software DAC.   */
                
                /* Wait for 1 data byte to be received before continuing */
                while(rxIndex < 1)
                {}
                
                /* Print confirmation that the command was received */
                printf("**************************************************\n\r");
                printf("Command Received:  %d\n\r", i2c0_rxData[0]);
                printf("Latching data. \n\n\r");
                
                /* Check the R value.  If > 0, turn on the Red LED */
                if(rValue > 0)
                {
                  gpio_clear_pin_output(BOARD_GPIO_LED_RED);
                }
                else
                {
                  gpio_set_pin_output(BOARD_GPIO_LED_RED);
                }
                /* Check the G value.  If > 0, turn on the Green LED */
                if(gValue > 0)
                {
                  gpio_clear_pin_output(BOARD_GPIO_LED_GREEN);
                } 
                else
                {
                  gpio_set_pin_output(BOARD_GPIO_LED_GREEN);
                }
                    
                /* Check the B value.  If > 0, turn on the Blue LED */
                if(bValue > 0)
                {
                  gpio_clear_pin_output(BOARD_GPIO_LED_BLUE);
                }
                else
                {
                  gpio_set_pin_output(BOARD_GPIO_LED_BLUE);
                }
                    
                
                break;
              case 4:
                /* Wait for data byte to be received before continuing */
                while(rxIndex < 1)
                {}
                
                /* This command should sample the input pins and set the 
                 *    address accordingly.  */
                i2cSlaveVar = ((gpio_read_pin_input(kGpioI2Caddr1)) 
                               | (gpio_read_pin_input(kGpioI2Caddr2) << 1));
    
                /* Set slave address */
                Slave0Info.slaveAddress = (SLAVE_BASE | i2cSlaveVar);
                
                /* Re-initialize the I2C slave */
                i2c_slave_init(BOARD_DAC_I2C_INSTANCE, &Slave0Info);
                
                rxIndex = 0;
                
                /* Print confirmation that the command was received */
                printf("**************************************************\n\r");
                printf("Received General Call Command\n\r");
                printf("Resampling I2C address pins without resetting. \n\n\r");
                
                break;
              case 5:
                /* Wait for 3 data bytes to be received before continuing */
                while(rxIndex < 1)
                {}
                
                /* Print confirmation that the command was received */
                printf("**************************************************\n\r");
                printf("Received sleep command!!\n\n\r");
                
                rxIndex = 0;
                
                /* Set the power mode configuration structure for VLPS */
                smc_config_data.powerModeName = kPowerModeVlps;
                
                /* Enter VLPS mode */
                smc_set_power_mode(&smc_config_data);
                
                break;
              case 0xAA:
                /* Wait for 3 data bytes to be received before continuing */
                while(rxIndex < 1)
                {}
                
                /* Print confirmation that the command was received */
                printf("**************************************************\n\r");
                printf("Received Ping from master.\n\n\r");
                
                break;
              default:
                /* This is the default case.  It should reset the state machine
                  and RX buffer and inform the user.   */
                printf("**************************************************\n\r");
                printf("Invalid command.  Resetting RX buffer.\n\n\r");
                
                break;
            }
            
            /* Reset the RX buffer after every reception.  */
            for(i = rxIndex; i > 0; i--)
            {
                i2c0_rxData[i] = 0;
            }
            
            rxIndex = 0;
            txIndex = 0;
            
	} 
}
/********************************************************************/
/********************************************************************/
