/* --COPYRIGHT--,BSD
 * Copyright (c) 2012, Texas Instruments Incorporated
 * 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 Texas Instruments Incorporated 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 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.
 * --/COPYRIGHT--*/
//
// Include files
//
#include "deviceSpecifics.h"
#include "i2cMaster.h"
#include "i2cBslHost.h"

//
// Local function Prototypes
//
static uint16_t CRC_CCITT(uint8_t * data, uint16_t length);
static void CRC8_ADD(uint8_t *crc, uint8_t new_data);
static int16_t i2cApp_Cmd(uint8_t *msg, uint8_t size);


/******************************************************************************
 * @brief   Calculates CRC16 CCITT of an array of bytes
 *
 * @param data      Pointer to the start of array
 * @param length    Number of bytes in array
 *
 * @return uint16_t calculated CRC
 *****************************************************************************/
// Calculate checksum of the BSL Core Command
static uint16_t CRC_CCITT(uint8_t * data, uint16_t length)
{
  uint16_t x;
  CRCINIRES = 0xFFFF;
  for( x = 0; x < length; x++)
    CRCDIRB_L = data[x];
  return CRCINIRES;
}

/******************************************************************************
 * @brief  Calculates CRC8 adding a new byte
 *
 * @param crc       Current CRC, updated with new CRC
 * @param new_data  New byte
 *
 * @return none
 *****************************************************************************/
#define CRC8_POLY   0x07
static void CRC8_ADD(uint8_t *crc, uint8_t new_data)
{
    uint8_t i;	// Counter for 8 shifts

    *crc ^= new_data;        // Initial XOR
    i = 8;
    do
    {
        if (*crc & 0x80)
        {
            *crc <<= 1;
            *crc ^= CRC8_POLY;
        }
        else
        {
            *crc <<= 1;
        }
    }
    while(--i);
}

//
//  Application Commands 
//
/******************************************************************************
 * @brief  Sends a command to target using Application protocol
 *
 * @param msg       Pointer to message being sent
 * @param size      Number of bytes in message
 *
 * @return BSL_PI_ERROR: Error in Peripheral Interface
 *         BSL_ERROR_OK: Command processed correctly
 *****************************************************************************/
static int16_t i2cApp_Cmd(uint8_t *msg, uint8_t size)
{
    int16_t ret;

     //if (i2cSendMessagenoTimeout(msg, size) < 0)
    if (i2cSendMessage(msg, size) < 0)
        ret = BSL_PI_ERROR;
    else
        ret = BSL_ERROR_OK;

    i2cSendStop();
    return ret;
}

/******************************************************************************
 * @brief  Application Command : Toggle LED1
 *
 * @return BSL_PI_ERROR: Error in Peripheral Interface
 *         BSL_ERROR_OK: Command processed correctly
 *****************************************************************************/
int16_t i2CApp_LED1(void)
{
    uint8_t msg[2];

    msg[0] = 0x01;
    return i2cApp_Cmd(msg, 1);
}

/******************************************************************************
 * @brief  Application Command : Toggle LED2
 *
 * @return BSL_PI_ERROR: Error in Peripheral Interface
 *         BSL_ERROR_OK: Command processed correctly
 *****************************************************************************/
int16_t i2CApp_LED2(void)
{
    uint8_t msg[2];

    msg[0] = 0x02;
    return i2cApp_Cmd(msg, 1);
}

/******************************************************************************
 * @brief  Application Command : Toggle both LEDs
 *
 * @return BSL_PI_ERROR: Error in Peripheral Interface
 *         BSL_ERROR_OK: Command processed correctly
 *****************************************************************************/
int i2CApp_LEDBoth(void)
{
    uint8_t msg[2];

    msg[0] = 0x55;
    return i2cApp_Cmd(msg, 1);
}

/******************************************************************************
 * @brief  Application Command : Force BSL mode
 *
 * @return BSL_PI_ERROR: Error in Peripheral Interface
 *         BSL_ERROR_OK: Command processed correctly
 *****************************************************************************/
int i2CApp_ForceBSL(void)
{
    unsigned char msg[2];

    msg[0] = 0xAA;
    return i2cApp_Cmd(msg, 1);
}


//
// Simple Protocol commands
//
/******************************************************************************
 * @brief  Simple Protocol Command : Download image
 *
 * @param  programCode      Pointer to start of target's application image
 * @param  length           Lenght of target's application image
 * 
 * @return BSL_PI_ERROR: Error in Peripheral Interface
 *         BSL_ERROR_OK: Command processed correctly
 *****************************************************************************/
int16_t i2CSimple_DownloadtoTarget(const uint8_t *programCode, uint16_t length)
{
    uint16_t i;
    uint8_t sync;

    sync = 0x55;

    if (i2cApp_Cmd(&sync, 1) < 0)
        return BSL_PI_ERROR;

    // Add delay after the mass erase
    // MSPBoot configures flash clockgen from 333Khz-470Khz and a flash segment
    // takes 4819cycles, so
    // A device with 16KB would take from 32*14ms(462ms) to 32*10ms(328ms)
    __delay_cycles(16000000/2); //500ms delay
    
    for (i=0; i < length; i+=16)
    {
        if (i2cApp_Cmd((unsigned char *) &programCode[i], 16) < 0)
            return BSL_PI_ERROR;
    }
    return  BSL_ERROR_OK;
}


//
// BSL-based Protocol commands
//
/******************************************************************************
 * @brief  Send command using BSL-based Protocol 
 *
 * @param  pCmd         Pointer to Stucture with BSL command
 * @param  pResponse    Pointer to Structure where response will be stored
 * 
 * @return BSL_PI_ERROR: Error in Peripheral Interface
 *         BSL_ERROR_OK: Command processed correctly
 *****************************************************************************/
int16_t i2cBsl_ProcessCmd(I2cBslCmd* pCmd, I2cBslResponse* pResponse)
{
    uint8_t message[256];
    uint16_t x;
    uint32_t retry =0;

    message[0] = pCmd->header;
    message[1] = pCmd->NL;
    for(x = 0; x < pCmd->NL; x++)    // BSL Core Command
        message[2 + x]    = pCmd->bslCoreCommand[x];
    message[2 + x]      = (unsigned char) (pCmd->checksum & 0xFF);
    message[2 + x + 1]  = (unsigned char) (pCmd->checksum >> 8);

    if (i2cSendMessage(message, 2 + x + 2) < 0)
    {
        i2cInit();
        return BSL_PI_ERROR;
    }

    if (pCmd->bslResponseMsgLen == 0)
    {
        //i2cSendRestart();
        i2cSendStop();
        return BSL_ERROR_OK;
    }
    else
    {
        // __delay_cycles(16000000/20000); //50us
        retry = 0;
        while ((i2cReceiveMessage(message, pCmd->bslResponseMsgLen)<0)  && (retry++ < pCmd->retries))
        {
            __delay_cycles(16000000/10000); //100us delay
        }

        if (retry >= pCmd->retries)
        {
            i2cInit();
            return BSL_PI_ERROR;
        }
    }
    pResponse->i2cBslPiMessage = message[0];
    return BSL_ERROR_OK;
}

/******************************************************************************
 * @brief  BSL-based Protocol Command : Download image
 *
 * @param  programCode      Pointer to start of target's application image
 * @param  startaddr        Start address of application image 
 * @param  length           Lenght of target's application image
 * 
 * @return BSL_PI_ERROR: Error in Peripheral Interface
 *         BSL_RESPONSE_ERROR: Error in response from target
 *         BSL_ERROR_OK: Command processed correctly
 *****************************************************************************/
int16_t i2cBsl_DownloadtoTarget(const uint8_t *programCode, uint16_t startaddr, 
                                uint16_t length)
{
    I2cBslCmd Cmd;
    I2cBslResponse Response;
    uint16_t i;

    i2cBsl_CmdMassErase(&Cmd);
    if ((i2cBsl_ProcessCmd(&Cmd, &Response) < 0))
        return BSL_PI_ERROR;
    if (Response.i2cBslPiMessage != 0x00)
        return BSL_RESPONSE_ERROR;  // Error erasing device

    for (i=0; i < length; i+=16)
    {
        i2cBsl_CmdRxDataBlock(&Cmd, startaddr, 16, (unsigned char *) &programCode[i]);
        startaddr += 16;
        if ((i2cBsl_ProcessCmd(&Cmd, &Response) < 0))
            return BSL_PI_ERROR;
        if (Response.i2cBslPiMessage != 0x00)
            return BSL_RESPONSE_ERROR;  // Error sending package
    }

    i2cBsl_CmdJmptoApp(&Cmd);
    i2cBsl_ProcessCmd(&Cmd, &Response);

    return BSL_ERROR_OK;
}

/******************************************************************************
 * @brief  BSL-based Protocol Command : Writes bytes to target's memory
 *
 * @param  pCmd     Pointer to BSL command structure, updated with formatted parameters
 * @param  addr     Target's Start address where data will be written
 * @param  len      Number of bytes to write
 * @param  pData    Start of Array with data being written
 * 
 * @return none
 *****************************************************************************/
void i2cBsl_CmdRxDataBlock(I2cBslCmd* pCmd, uint32_t addr, uint16_t len, uint8_t* pData)
{
  uint16_t x;
  uint8_t * pCode;
  pCode = (unsigned char*)pData;

  pCmd->header            = SYNC_CHAR;      // Header is always SYNC_CHAR
  pCmd->NL                = (char)( (len+3) & 0xFF); // Number of bytes in BSL Core Packet
  pCmd->bslCoreCommand[0] = BSL_COMMAND_RX_DATA_BLOCK;           // BSL command RX Data Block
  pCmd->bslCoreCommand[1] = (char) (addr & 0xFF);       // AL
  pCmd->bslCoreCommand[2] = (char) ((addr >> 8)& 0xFF); // AM

  for(x = 0; x < len; x++)
    pCmd->bslCoreCommand[3 + x] = pCode[x] ;           // Copy data

  pCmd->checksum          = CRC_CCITT(pCmd->bslCoreCommand,  (int)pCmd->NL );
  pCmd->retries = 500;      // 50s
  pCmd->bslResponseMsgLen = 2;  //

}

/******************************************************************************
 * @brief  BSL-based Protocol Command : Erases the target's App Area
 *
 * @param  pCmd     Pointer to BSL command structure, updated with formatted parameters
 * 
 * @return none
 *****************************************************************************/
void i2cBsl_CmdMassErase(I2cBslCmd* pCmd)
{
  pCmd->header            = SYNC_CHAR;      // Header is always SYNC_CHAR
  pCmd->NL                = 1;              // Number of bytes in BSL Core Packet
  pCmd->bslCoreCommand[0] = BSL_COMMAND_MASS_ERASE;          // BSL command Mass Erase
  pCmd->checksum          = CRC_CCITT(pCmd->bslCoreCommand,(int)pCmd->NL  );
  pCmd->retries = 5000;     // 500ms
  pCmd->bslResponseMsgLen = 2;  //
}

/******************************************************************************
 * @brief  BSL-based Protocol Command : Erases a target's memory segment
 *
 * @param  pCmd     Pointer to BSL command structure, updated with formatted parameters
 * @param  addr     Target's address being erased
 * 
 * @return none
 *****************************************************************************/
void i2cBsl_CmdEraseSeg(I2cBslCmd* pCmd, unsigned long addr)
{
  pCmd->header            = SYNC_CHAR;      // Header is always SYNC_CHAR
  pCmd->NL                = 3;              // Number of bytes in BSL Core Packet
  pCmd->bslCoreCommand[0] = BSL_COMMAND_ERASE_SEGMENT;          // BSL command Segment Erase
  pCmd->bslCoreCommand[1] = (char) (addr & 0xFF);       // AL
  pCmd->bslCoreCommand[2] = (char) ((addr >> 8)& 0xFF); // AM
  pCmd->checksum          = CRC_CCITT(pCmd->bslCoreCommand,(int)pCmd->NL  );
  pCmd->retries = 5000;     // 500ms
  pCmd->bslResponseMsgLen = 2;  //
}

/******************************************************************************
 * @brief  BSL-based Protocol Command : Forces a jump to Applicatoin
 *
 * @param  pCmd     Pointer to BSL command structure, updated with formatted parameters
 * 
 * @return none
 *****************************************************************************/
void i2cBsl_CmdJmptoApp(I2cBslCmd* pCmd)
{
  pCmd->header            = SYNC_CHAR;      // Header is always SYNC_CHAR
  pCmd->NL                = 1;              // Number of bytes in BSL Core Packet
  pCmd->bslCoreCommand[0] = BSL_COMMAND_JUMP2APP;          // BSL command Mass Erase
  pCmd->checksum          = CRC_CCITT(pCmd->bslCoreCommand,(int)pCmd->NL  );
  pCmd->retries = 0;
  pCmd->bslResponseMsgLen = 0;  //
}

/******************************************************************************
 * @brief  BSL-based Protocol Command : Requests BSL version
 *
 * @param  pCmd     Pointer to BSL command structure, updated with formatted parameters
 * 
 * @return none
 *****************************************************************************/
void i2cBsl_CmdVersion(I2cBslCmd* pCmd)
{
  pCmd->header            = SYNC_CHAR;      // Header is always SYNC_CHAR
  pCmd->NL                = 1 ;             // Number of bytes in BSL Core Packet
  pCmd->bslCoreCommand[0] = BSL_COMMAND_BSL_VERSION;           // BSL command BSL Version
  pCmd->checksum          = CRC_CCITT(pCmd->bslCoreCommand,  (int)pCmd->NL  );
  pCmd->retries = 10;
  pCmd->bslResponseMsgLen   = 2 ;  // Expect BSL to return CRC
}


/******************************************************************************
 * @brief  BSL-based Protocol Command : Requests BSL version with incorrect CRC
 *
 * @param  pCmd     Pointer to BSL command structure, updated with formatted parameters
 * 
 * @return none
 *****************************************************************************/
void i2cBsl_CmdBadVersion(I2cBslCmd* pCmd)
{
  pCmd->header            = SYNC_CHAR;      // Header is always SYNC_CHAR
  pCmd->NL                = 1 ;             // Number of bytes in BSL Core Packet
  pCmd->bslCoreCommand[0] = BSL_COMMAND_BSL_VERSION;           // BSL command BSL Version
  // Checksum is incorrect on purpose
  pCmd->checksum          = CRC_CCITT(pCmd->bslCoreCommand,  (int)pCmd->NL  ) +1;
  pCmd->retries = 10;
  pCmd->bslResponseMsgLen   = 2 ;  // Expect BSL to return CRC

}

//
// SMBus-based Protocol commands
//
/******************************************************************************
 * @brief  Send command using SMBus-based Protocol 
 *
 * @param  pCmd         Pointer to Stucture with SMBus command
 * @param  pResponse    Pointer to Structure where response will be stored
 * 
 * @return BSL_PI_ERROR: Error in Peripheral Interface
 *         BSL_ERROR_OK: Command processed correctly
 *****************************************************************************/
int16_t i2cSMB_ProcessCmd(I2cSMBCmd* pCmd, I2cSMBCmd* pResponse)
{
    uint32_t retry =0;

    while ((i2cSendMessage(pCmd->buf, pCmd->len) < 0)  && (retry++ < 3))
        ;
    if (retry >= 3)
        return BSL_PI_ERROR;

    i2cSendStop();
  if (pCmd->resp_len == 0)
  {
    return BSL_ERROR_OK;
  }
  else
    {
        while (UCB1CTL1 & UCTXSTP);
            ;
        retry = 0;
         while ((i2cReceiveMessage(pResponse->buf, pCmd->resp_len)<0)  && (retry++ < pCmd->retries))
         {
            __delay_cycles(16000000/10000); //100us delay
         }
            if (retry >= pCmd->retries)
                return BSL_PI_ERROR;
            else
                pResponse->len = pCmd->resp_len;
    }
    return BSL_ERROR_OK;
}

/******************************************************************************
 * @brief  SMBus-based Protocol Command : Requests bootloader 
 *
 * @param  pCmd     Pointer to SMBus command structure, updated with formatted parameters
 * 
 * @return none
 *****************************************************************************/
void i2cSMB_CmdVersion(I2cSMBCmd * pCmd)
{
    pCmd->buf[0] = 0x01;
    pCmd->len = 1;
    pCmd->resp_len = 2;
    pCmd->retries = 2;
}

/******************************************************************************
 * @brief  SMBus-based Protocol Command : Erases the Application Area
 *
 * @param  pCmd     Pointer to SMBus command structure, updated with formatted parameters
 * 
 * @return none
 *****************************************************************************/
void i2cSMB_CmdAppErasePEC(I2cSMBCmd * pCmd)
{
    pCmd->buf[0] = 0x82;
    pCmd->buf[1] = 0x31;
    pCmd->len = 2;
    pCmd->resp_len = 2;
    pCmd->retries = 5000;   // 500ms
}

/******************************************************************************
 * @brief  SMBus-based Protocol Command : Requests erase with incorrect PEC
 *
 * @param  pCmd     Pointer to SMBus command structure, updated with formatted parameters
 * 
 * @return none
 *****************************************************************************/
void i2cSMB_CmdBadAppErasePEC(I2cSMBCmd * pCmd)
{
    pCmd->buf[0] = 0x82;
    pCmd->buf[1] = 0x30;    // PEC is incorrect
    pCmd->len = 2;
    pCmd->resp_len = 2;
    pCmd->retries = 5000;
}

/******************************************************************************
 * @brief  SMBus-based Protocol Command : Forces target to jump to Application
 *
 * @param  pCmd     Pointer to SMBus command structure, updated with formatted parameters
 * 
 * @return none
 *****************************************************************************/
void i2cSMB_CmdJumptoAppPEC(I2cSMBCmd * pCmd)
{
    pCmd->buf[0] = 0x85;
    pCmd->buf[1] = 0x24;
    pCmd->len = 2;
    pCmd->resp_len = 0;
    pCmd->retries = 0;
}

/******************************************************************************
 * @brief  SMBus-based Protocol Command : Writes bytes to target's memory
 *
 * @param  pCmd     Pointer to SMBus command structure, updated with formatted parameters
 * @param  addr     Target's Start address where data will be written
 * @param  len      Number of bytes to write
 * @param  pData    Start of Array with data being written
 * 
 * @return none
 *****************************************************************************/
void i2cSMB_CmdRxDataBlock(I2cSMBCmd* pCmd, uint32_t addr, uint16_t len, uint8_t* pData)
{
    uint16_t i;
    uint8_t crc;

    crc = 0x00;
    CRC8_ADD(&crc, 0x80);   // Addr
    pCmd->buf[0] = 0x84;
    pCmd->buf[1] = len+2;  // 16 Data + 2 Addr
    pCmd->buf[2] = (addr & 0xFF);             // AL
    pCmd->buf[3] = (char) ((addr >> 8)& 0xFF);     // AM

    for(i = 0; i < len; i++)
        pCmd->buf[4 + i] = pData[i] ;           // Copy data

    for(i = 0; i < len+4; i++)
       CRC8_ADD(&crc, pCmd->buf[i]);   // Add to crc

    pCmd->buf[len+4]    = crc;

    pCmd->len = len+5;
    pCmd->retries = 500; // 5ms

    pCmd->resp_len = 2;  //
}

/******************************************************************************
 * @brief  SMBus-based Protocol Command : Download image
 *
 * @param  programCode      Pointer to start of target's application image
 * @param  startaddr        Start address of application image 
 * @param  length           Lenght of target's application image
 * 
 * @return BSL_PI_ERROR: Error in Peripheral Interface
 *         BSL_RESPONSE_ERROR: Error in response from target
 *         BSL_ERROR_OK: Command processed correctly
 *****************************************************************************/
int16_t i2CSMB_DownloadtoTarget(const uint8_t *programCode, uint16_t startaddr, uint16_t length)
{
    I2cSMBCmd SMB_Cmd, SMB_Resp;
    unsigned short i;
    unsigned char size;

    i2cSMB_CmdAppErasePEC(&SMB_Cmd);
    if ((i2cSMB_ProcessCmd(&SMB_Cmd, &SMB_Resp) < 0))
        return BSL_PI_ERROR;
    if (SMB_Resp.buf[0] != 0x00)
        return BSL_RESPONSE_ERROR;  // Error erasing device

#define MAX_SMB_DATA_SIZE 30 // Maximum size for SMBus is 32 (30 data + 2 address)
    i=0;
    while (i<length)
    {
        if (i+MAX_SMB_DATA_SIZE > length)
            size = length-i;
        else
            size = MAX_SMB_DATA_SIZE;
        i2cSMB_CmdRxDataBlock(&SMB_Cmd, startaddr, size, (unsigned char *) &programCode[i]);
        startaddr += size;
        i+= size;
        if ((i2cSMB_ProcessCmd(&SMB_Cmd, &SMB_Resp) < 0))
            return BSL_PI_ERROR;
        if (SMB_Resp.buf[0] != 0x00)
            return BSL_RESPONSE_ERROR;  // Error sending package
    }

    i2cSMB_CmdJumptoAppPEC(&SMB_Cmd);
    i2cSMB_ProcessCmd(&SMB_Cmd, &SMB_Resp);

    return BSL_ERROR_OK;
}

