/********************* THIS FILE IS THE IMPLEMENTATION OF THE PROTOCOL ************/

#include "XTWR-MCF51JE.h"
#include "sci.h"
#include "ascii.h"
#include "CFV1_Flash.h"
#include "com_protocol.h"

/*************** DECLARATION OF THE NON-VOLATILE TABLES ******************/
/*This is used to locate Non-volatile Tables in Flash memory*/

#pragma define_section my_tables ".romsymbols"  /*start of pragma*/
/*following code will be located in memory section called .romsymbols */
 
#pragma section my_tables begin

UINT8 Table1[TABLE1_SIZE] = 
{
 'T',
 'A',
 'B',
 'L',
 'E',
 ' ',
 'N',
 'U',
 'M',
 'B',
 'E',
 'R',
 ' ',
 '1',
 ' ',
 'I',
 'S',
 ' ',
 'S',
 'A',
 'V',
 'E',
 'D',
 ' ',
 'I',
 'N',
 ' ',
 'F',
 'L',
 'A',
 'S',
 'H'
};
                                                
#pragma section my_tables end                   /*end of pragma*/



/*************** DECLARATION OF THE VOLATILE TABLES ******************/
UINT8 Table0[TABLE0_SIZE];            /*Buffer used by Table 0*/
UINT8 Table2[TABLE2_SIZE];            /*Buffer used by Table 2*/
UINT8 Table3[TABLE3_SIZE];            /*Buffer used by Table 3*/

UINT8 temp_Table[HEX_BUFFER_SIZE];    /*Backup table used in flash write*/

/****************** STRUCT FOR TABLES PROPERTIES *********************/
struct Table_Description Table_list[TABLES_NUMBER] = 
  {
   {&Table0[0],TABLE0_SIZE,TABLE0_TYPE,TABLE0_SECURITY,"0.- Measurements"},
   {&Table1[0],TABLE1_SIZE,TABLE1_TYPE,TABLE1_SECURITY,"1.- Calibration"},
   {&Table2[0],TABLE2_SIZE,TABLE2_TYPE,TABLE2_SECURITY,"2.- Settings"},
   {&Table3[0],TABLE3_SIZE,TABLE3_TYPE,TABLE3_SECURITY,"3.- Data logger"},
  }; 

struct Table_Description *p_table_list;



/******************* GENERAL VARIABLES USED ****************/

UINT8 RX_Buffer[RX_BUFFER_SIZE];      /*Buffer used for SCI RX*/
UINT8 Hex_Buffer[HEX_BUFFER_SIZE];    /*Buffer used for converted ASCII to HEX values*/

UINT16 *p_size;                       /*Variable used to detect table size*/
UINT16 tableID;                       /*Variable used to save the Table ID*/
UINT8  u8cksum;                       /*Variable used to check the calue of Checksum*/
UINT8  u8write_status_flag;           /*Variable used to check Write command status*/
UINT16 i;                             /*Variable used for diferent routines*/


/*******************************************************************/
/******************** START OF THE FUNCTIONS ***********************/
/********************************************************************/

void communication_protocol(void)
{
 UINT8  code_service;                       /*Variable to capture the service*/

 if (RX_status != COMMAND_RECEIVED)         /*Wait for a command complete in RX_Buffer (in ASCII)*/
 {
   return;
 }

 else 
 {
   buffer_conversion();                     /*Converts the RX_Buffer to HEX value in the HexBuffer*/
                                       
   if (conversion_status == COMMAND_ERROR)  /*Check if the received ASCII data is valid */
   {
    Hex_Buffer[0] = COMMAND_ERROR;          /*Invalidates the buffer*/
    conversion_status = COMMAND_WAITING;    /*If non-ASCII data is received, returns an error*/
   }
   
   /*Starts of the command analisys*/
   
   code_service = Hex_Buffer[0];            /*If not ASCII errors, code service will be decoded*/
  
   #if (IR_ACTIVE)
   Disable_RX(USED_PORT);                   /*If IR is active, disable RX*/
   #endif
  
   switch (code_service)
   {
     case COMMAND_FULL_READ:                /*Full read is 0x30 command*/
     {
      full_read();
     }break;
     
     case COMMAND_OFFSET_READ:              /*Offset read is 0x3F command*/
     {
      offset_read();
     }break;
     
     case COMMAND_FULL_WRITE:               /*Full write is 0x40 command*/
     {
      full_write();       
     }break;
      
     case COMMAND_OFFSET_WRITE:             /*Offset write is 0x4F command*/
     {
      offset_write();      
     }break;
     
     
     default: 
     {
      sendNOK();                            /*If any other command is received, returns error*/
     }break;
   }

   RX_status = COMMAND_WAITING;             /*bufer analysis completed.*/

   #if (IR_ACTIVE)
   Enable_RX(USED_PORT);                    /*If IR is active, enables RX again*/
   #endif

 }
}


/*----------------------------------------------------------------------------------------------*/


void full_read(void)
{
  UINT8  *p_read;
  
  if (hex_counter != FULL_READ_SIZE)      
  /*If the number of data received is different to the length of the code, returns an error*/
  {
   sendNOK();
   return;
  }
  
  tableID = (UINT16)( ((UINT16)Hex_Buffer[1]<<8) | ((UINT16)Hex_Buffer[2]) );
  /*captures the Table ID from the reception Buffer*/
  
  if ( (tableID >= TABLES_NUMBER) || (Table_list[tableID].type == W_ONLY) )
  /*if the TableID does not exist, returns an error*/
  {
   sendNOK();
  }
  
  else
  {
   p_read = Table_list[tableID].address;      /*loads the addres of the current Table*/      
   p_size = &Table_list[tableID].size;        /*loads the size of the current Table*/      
   sendOK();                                  /*send a OK response*/    
  
   hextoascii ( (UINT8)(*p_size>>8) );        /*send first and second nibbles in ASCII format*/
   hextoascii ( (UINT8)(*p_size>>0) );        /*send third and fourth nibbles in ASCII format*/
   
   u8cksum = 0;                               /*clears the variable used to verify the ckecksum*/
   
   for(i=0; i<Table_list[tableID].size; i++)
   /*addition of all values of the received data*/
      {
        hextoascii (*p_read);                 /*used to send table data in ascii format*/
        u8cksum += *p_read;
        p_read++;
      }
   hextoascii ( (UINT8)(0 - u8cksum) );       /*Twos complement of the ckecksum*/
   sendCR();                                  /*Send a carry return and line feed*/
  }
}


/*----------------------------------------------------------------------------------------------*/


void offset_read(void)
{
  UINT8  *p_read;           /*Variable for capture the origin address*/
  UINT16 u16Table_offset;   /*Offset in the current table*/
  UINT16 u16octet_count;    /*Number of data received*/
  
  if (hex_counter != OFFSET_READ_SIZE)
  /*If the number of data received is different to the length of the code, returns an error*/
  {
   sendNOK();
   return;
  }
    
  tableID = (UINT16)(((UINT16)Hex_Buffer[1]<<8) | (UINT16)Hex_Buffer[2]);
  /*captures the Table ID from the reception Buffer*/
  u16Table_offset = (UINT16)(((UINT16)Hex_Buffer[4]<<8) | (UINT16)Hex_Buffer[5]);
  /*captures the offset to use from the reception Buffer*/
  u16octet_count = (UINT16)(((UINT16)(Hex_Buffer[6])<<8) | (UINT16)Hex_Buffer[7]);
  /*captures the number of data to use from the reception Buffer*/
        
  if (   (tableID >= TABLES_NUMBER) 
      || (u16Table_offset >= Table_list[tableID].size)
      || ((u16Table_offset + u16octet_count) > Table_list[tableID].size)
      || (!u16octet_count)
      || (Hex_Buffer[3])
      || (Table_list[tableID].type == W_ONLY) 
     )
  {
   sendNOK();                                                         
  }
    
  else
  {
   p_read = Table_list[tableID].address + u16Table_offset; 
   p_size = &u16octet_count;
   sendOK();
    
   hextoascii ( (UINT8)(*p_size>>8) );        /*send first and second nibbles in ASCII format*/
   hextoascii ( (UINT8)(*p_size>>0) );        /*send third and fourth nibbles in ASCII format*/
     
   u8cksum = 0;                               /*clears the variable used to verify the ckecksum*/
   
   for(i=0; i<u16octet_count; i++)
   /*addition of all values of the received data*/
      {
       hextoascii (*p_read);                  /*used to send table data in ascii format*/
       u8cksum += *p_read;
       p_read++;
      }
     hextoascii ((UINT8)(0 - u8cksum));       /*Twos complement of the ckecksum*/
     sendCR();                                /*Send a carry return and line feed*/
  }
}


/*----------------------------------------------------------------------------------------------*/


void full_write(void)
{
  UINT8  *p_write;
  UINT16 u16octet_count;
  UINT8  u8received_cksum;       /************ |VARIABLE FOR CAPTURE THE CHECKSUM| ************/
  
    
  if (hex_counter < FULL_WRITE_SIZE)
  {
   sendNOK();
   return;
  }  
  
  tableID = (UINT16)(((UINT16)Hex_Buffer[1]<<8) | (UINT16)Hex_Buffer[2]);
  u16octet_count = (UINT16)(((UINT16)Hex_Buffer[3]<<8) | (UINT16)Hex_Buffer[4]);
  
  if (    (tableID >= TABLES_NUMBER) 
       || (u16octet_count > Table_list[tableID].size) 
       || (u16octet_count + 6 != hex_counter)           /*total packet size = data_count*/
       || (Table_list[tableID].type == R_ONLY)          /* + 1-byte command, + 2-bytes TableID*/
     )                                                  /* + 2-byte data_count + 1-byte checksum */
  {                                                     
   sendNOK();
   return;
  }
  
  u8received_cksum = Hex_Buffer[u16octet_count + FULL_WRITE_SIZE];  
  /*Position of the checksum*/
   
  u8cksum = 0;
  p_write = &Hex_Buffer[FULL_WRITE_SIZE];
   
  for(i=0; i<u16octet_count; i++)
  {
   u8cksum += *p_write;
   p_write++;
  }
   
  u8cksum = (UINT8)(0 - u8cksum);            /*Calculates the checksum of the received values*/
   
  if (/*0*/ u8cksum != u8received_cksum)     /*0 for disable Checksum validation*/
  {                                          /*uncomment condition for activate Checksum validation*/
   sendNOK();
   return;
  }
  
  for(i=0; i<Table_list[tableID].size; i++)
  {
    temp_Table[i] = *(Table_list[tableID].address + i);
  }
  
  p_write = &temp_Table[0];       
  
  for(i=0; i<u16octet_count; i++)
  {
    *p_write = Hex_Buffer[i + FULL_WRITE_SIZE];  
    p_write++;
  }
  
  u8write_status_flag = 
  write_verify(Table_list[tableID].address, Table_list[tableID].size, &temp_Table[0]);
  
  if(u8write_status_flag == WRITE_ERROR)
  {
    sendNOK();
    return;
  }
    
  if (u8write_status_flag == WRITE_OK)
  {
    sendOK();
  }
  sendCR();
}


/*----------------------------------------------------------------------------------------------*/


void offset_write(void)
{
  UINT8  *p_write;
  UINT16 u16Table_offset;
  UINT16 u16octet_count;
  UINT8  u8received_cksum;       /************ |VARIABLE FOR CAPTURE THE CHECKSUM| ************/

  if (hex_counter < OFFSET_WRITE_SIZE)
  {
   sendNOK();
   return;
  }  
  
  tableID = (UINT16)(((UINT16)Hex_Buffer[1]<<8) | (UINT16)Hex_Buffer[2]);
  u16Table_offset = (UINT16)(((UINT16)Hex_Buffer[4]<<8) | (UINT16)Hex_Buffer[5]);
  u16octet_count = (UINT16)(((UINT16)(Hex_Buffer[6])<<8) | (UINT16)Hex_Buffer[7]);
 
  if (   (tableID >= TABLES_NUMBER) 
      || (u16Table_offset >= Table_list[tableID].size)
      || ((u16Table_offset + u16octet_count) > Table_list[tableID].size)
      || (u16octet_count + 9 != hex_counter) 
      || (!u16octet_count)
      || (Hex_Buffer[3])
      || (Table_list[tableID].type == R_ONLY)
     )
  {
   sendNOK();
   return;
  }        
  
  u8received_cksum = Hex_Buffer[u16octet_count + OFFSET_WRITE_SIZE];    
  /*Position of the checksum*/
    
  u8cksum = 0;
  p_write = &Hex_Buffer[8];
    
  for(i=0; i<u16octet_count; i++)
   {
    u8cksum += *p_write;
    p_write++;
   }
    
  u8cksum =(UINT8)(0 - u8cksum);             /*Calculates the checksum of the received values*/
    
  if (/*0*/ u8cksum != u8received_cksum)     /*0 for disable Checksum validation*/
   {                                         /*uncomment condition for activate Checksum validation*/
    sendNOK();
    return;
   }
    
  
  for(i=0; i<Table_list[tableID].size; i++)
  {
    temp_Table[i] = *(Table_list[tableID].address + i);
  }
  
  p_write = ( &temp_Table[0] + u16Table_offset );       
  
  for(i=0; i<u16octet_count; i++)
  {
    *p_write = Hex_Buffer[i + OFFSET_WRITE_SIZE];  
    p_write++;
  }
  
  u8write_status_flag = 
  write_verify(Table_list[tableID].address, Table_list[tableID].size, &temp_Table[0]);
  
  if(u8write_status_flag == WRITE_ERROR)
  {
    sendNOK();
    return;
  }
    
  if (u8write_status_flag == WRITE_OK)
  {
    sendOK();
  }
  sendCR();
}


/*----------------------------------------------------------------------------------------------*/


UINT8 write_verify(UINT8 *p_memory_address, UINT16 u16element_count, UINT8 *p_data_pointer) 
{
  UINT8 u8error_detect = 0;
  
  if( (p_memory_address >= (UINT8 *)RAM_START_ADDRESS) && 
      (p_memory_address <= (UINT8 *)RAM_END_ADDRESS))
  {
    
    for(i=0; i<u16element_count; i++)
    {
      *p_memory_address = *p_data_pointer;  
      p_memory_address++;
      p_data_pointer++;
    }
            
    return WRITE_OK;
  }
  
  else if( (p_memory_address >= (UINT8 *)FLASH2_START_ADDRESS) && 
           (p_memory_address <= (UINT8 *)FLASH2_END_ADDRESS))
  {
    /*FLASH ERASE*/
    Flash_Erase(p_memory_address);
    u8error_detect = Flash_Burst(p_memory_address, u16element_count, p_data_pointer);
    return u8error_detect;
  }
  
  else 
  {
    return WRITE_ERROR;
  }
}
