/*********************************************************/
/* ON Semiconductor                                      */
/* NCV7383 Evaluation KIT                                */
/*                                                       */
/* HOSTCOMM.C                                            */
/* Host communication module                             */
/*                                                       */
/* Originally created by: Jozef Polak                    */
/* Modified by: Filip Brtan                              */
/* Date: 30.07.2012                                      */
/*********************************************************/


/** S12X derivative information */ 
#include "mc9s12xf512.h"  
#include "typedefs.h"
#include "GPIO_macros.h"
#include "CAN.h"
#include "CAN_HostComm.h"
#include "CAN_Host.h"


#pragma DATA_SEG CAN_HOSTCOMM_DATA
#pragma CODE_SEG CAN_HOSTCOMM_CODE

/* Static Functions */
static byte CAN_hostcomm_framecheck(TBUFFER_DATA *buffer, byte length);      // computation of checksum for the frame
static void CAN_hostcomm_application(TBUFFER_DATA *rxdata, byte rxlength, 
                                  TBUFFER_DATA *txdata, byte *txlength);  // application-level processing of the frame
/******************/
/* Initialization */
/******************/

/* Initialization of the host communication. */
void CAN_hostcomm_initialize (void) {
  // No initialization neccessary.
}

/**********************/
/* Host Communication */
/**********************/

/* Send Command - Master Node  */
void CAN_send_command(byte group, byte command, byte *data, byte data_length){
  byte checksum = 0;
  byte i;
  CAN_txdata[0] = group;  
  CAN_txdata[1] = command;
  for (i = 0; i < data_length; i++){
    CAN_txdata[i+2] = *data++;  
  }
  CAN_txlen = data_length + 2; 
  // 1. Frame-Check Layer.
  checksum = CAN_hostcomm_framecheck (CAN_txdata, CAN_txlen); 
  CAN_txdata[CAN_txlen++] = checksum;  
  // Request to transmit the response.
  CAN_transmit (); 
}

/* Master Host communication frame processing: CAN. */
byte CAN_master_hostcomm_process (byte *group, byte *command, byte *data, byte *data_length) {
  byte checksum = 0;           // the checksum byte
  byte i;
  byte *CAN_prxdata;
  if (!CAN_txactive()){  // cannot allow processing of the next frame until the TX buffer is empty
    if (CAN_received()) {   
      /* COMMAND */
      // 1. Frame-Check Layer.
      checksum = CAN_hostcomm_framecheck (CAN_rxdata, CAN_rxlen - 1);
      if (CAN_rxdata[CAN_rxlen - 1] != checksum) { // frames with wrong checksum are ignored
        /* Reset can buffer registers. Resyncronize frame reception*/
        CAN_on_error();
        return ERR_FAULT;   
      }
      // 2. Application layer.
      CAN_prxdata = &CAN_rxdata[0];
      if (CAN_rxlen > 3) *data_length = CAN_rxlen - 3;
      else *data_length = 0;
      *group = *CAN_prxdata++;
      *command = *CAN_prxdata++;
      for (i = 0; i < *data_length; i++){
        *data++ = *CAN_prxdata++;
      }
      return ERR_OK; 
    }
    return ERR_RXEMPTY; 
  }
  return ERR_TXFULL;    
}


/* Host communication frame processing: CAN. */
void CAN_hostcomm_process (void) {
  byte checksum = 0;           // the checksum byte
  if (!CAN_txactive()){  // cannot allow processing of the next frame until the TX buffer is empty
    if (CAN_received()) {   
      /* COMMAND */
      // 1. Frame-Check Layer.
      checksum = CAN_hostcomm_framecheck (CAN_rxdata, CAN_rxlen - 1);
      if (CAN_rxdata[CAN_rxlen - 1] != checksum) { // frames with wrong checksum are ignored
        /* Reset can buffer registers. Resyncronize frame reception*/
        CAN_on_error();
      }
      // 2. Application layer.
      CAN_hostcomm_application (CAN_rxdata, --CAN_rxlen, CAN_txdata, &CAN_txlen);
      // Frame processed, allow reception. The host should still wait for the response.
      CAN_rxprocessed();
      
      /* RESPONSE */ 
      if (CAN_txlen) {
        // 1. Frame-Check Layer.
        checksum = CAN_hostcomm_framecheck (CAN_txdata, CAN_txlen);
        CAN_txdata[CAN_txlen++] = checksum;
        // Request to transmit the response.
        CAN_transmit ();
      }
      
    }
  }   
}

/*** Frame-Check Layer ***/

/* Computation of checksum for the frame. */
static byte CAN_hostcomm_framecheck (TBUFFER_DATA *buffer, byte length) {
  byte cnt;
  TBUFFER_DATA *pbuf = buffer;
  byte checksum;

  checksum = 0;
  for (cnt = 0; cnt < length; cnt++)
    checksum += *pbuf++;
  return (0-checksum);  
}

/*** Application Layer ***/

/* Application-level processing of the frame. */
static void CAN_hostcomm_application (TBUFFER_DATA *prxdata, byte rxlength, TBUFFER_DATA *ptxdata, byte *txlength) {
  byte group;          // command group byte (0 = no frame)
  byte command;        // command byte

  if (rxlength < 2) {  // safety precaution
    *txlength = 0;
    return;
  }
  group = *prxdata++;                  // header
  command = *prxdata++;                // header
  *ptxdata++ = group;                  // header
  *ptxdata++ = command ^ CAN_CMD_ACK;  // header
  // Command processing.
  CAN_host_prxdata = prxdata;
  CAN_host_rxlength = rxlength - 2;
  CAN_host_ptxdata = ptxdata;
  CAN_host_command [group] (command);
  *txlength = (byte *) CAN_host_ptxdata - (byte *) ptxdata + 2;

}

#pragma CODE_SEG DEFAULT