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

// PROTOCOL:
// This module implements the frame check level and the aplication level of the host communication protocol. See documentation for details.

// COMMUNICATION INTERFACE:
// - This module allows using both the UART0 and the FTDI_FastSerial physical interfaces.
//   Well-designed communication protocol as well as properly organized code ensure that both interfaces CAN be used simultaneously ;-).

// NOTES:
// - This module uses directly the RX and TX communication buffers (UART0 and FTDI_FastSerial). Be careful.
// - The host communication is active when there is data in the reception buffer that has not been processed OR if there is unsent data in the transmission buffer.
//   Data in the transmission buffer blocks processing of the next bulk of commands because it is rewritten during processing.
//   Thus full-duplex communication is possible only after processing, during the transmission of the response.

/** S12X derivative information */ 
#include "mc9s12xf512.h"  
#include "typedefs.h"
#include "GPIO_macros.h"
#include "SCI0.h"
#include "CAN.h"
#include "HostComm.h"
#include "Host.h"

#pragma DATA_SEG HOSTCOMM_DATA
#pragma CODE_SEG HOSTCOMM_CODE

/* Static Functions */

/* Diagnostic LEDs */
#define DIAG_HOSTCOMM_START    LED_ON(LED7);       // host communication start
#define DIAG_HOSTCOMM_END      LED_OFF(LED7);      // host communication end

#define DIAG_CAN_HOSTCOMM_START    LED_ON(LED6);       // host communication start
#define DIAG_CAN_HOSTCOMM_END      LED_OFF(LED6);      // host communication end

/******************/
/* Initialization */
/******************/

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

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

/* Host communication frame processing: UART. */
void hostcomm_process_SCI0 (void) {
  byte checksum = 0;           // the checksum byte
  byte node_id;
  if (!SCI0_txactive())       // CANnot allow processing of the next frame until the TX buffer is empty
    if (SCI0_received()) {   
      /* COMMAND */
      // 1. Frame-Check Layer.
      checksum = hostcomm_framecheck (SCI0_rxdata, SCI0_rxlen - 1);
      if (SCI0_rxdata[SCI0_rxlen - 1] != checksum) { // frames with wrong checksum are ignored
        /* Reset SCI0 buffer registers. Resyncronize frame reception*/
        SCI0_OnError();
        return;   
      }
      SCI0_txlen = 0; /* reset transmit buffer */
      
      node_id = SCI0_rxdata[0];
      
      if (node_id == 1){ // continue with application layer 
        // 2. Application layer.
        hostcomm_application (SCI0_rxdata, --SCI0_rxlen, SCI0_txdata, &SCI0_txlen);
      }else if (node_id == 0){
        hostcomm_CAN_command (SCI0_rxdata,   SCI0_rxlen, SCI0_txdata, &SCI0_txlen);
        hostcomm_application (SCI0_rxdata, --SCI0_rxlen, SCI0_txdata, &SCI0_txlen);
      }
      else { // send CAN command to another node
        hostcomm_CAN_command (SCI0_rxdata,   SCI0_rxlen, SCI0_txdata, &SCI0_txlen);
      }
      
      // Frame processed, allow reception. The host should still wait for the response.
      SCI0_rxprocessed();
      /* RESPONSE */
     
      if (SCI0_txlen == 0){  /* No data receiver from CAN backbone */
        SCI0_txdata[0] = 1;
        SCI0_txdata[1] = SCI0_rxdata[1];
        SCI0_txdata[2] = SCI0_rxdata[2] ^ CMD_ACK;
        SCI0_txdata[3] = 0; /* dummy data */ 
        SCI0_txdata[4] = 0; /* dummy data */ 
        SCI0_txlen = 5;
      }
     
      // 1. Frame-Check Layer.
      checksum = hostcomm_framecheck (SCI0_txdata, SCI0_txlen);
      SCI0_txdata[SCI0_txlen++] = checksum;
      // Request to transmit the response.
      SCI0_transmit ();
    }
}

/* Host communication frame processing: CAN. */
void hostcomm_process_CAN (void) {
  byte checksum = 0;           // the checksum byte
  byte node_id;
  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 = 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;   
      }
      
      node_id = CAN_rxdata[0];
      if ((node_id == 0) || (node_id == CAN_node_ID)){ // continue with application layer 
        DIAG_CAN_HOSTCOMM_START
        // 2. Application layer.
        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 && (node_id != 0)) {
          // 1. Frame-Check Layer.
          checksum = hostcomm_framecheck (CAN_txdata, CAN_txlen);
          CAN_txdata[CAN_txlen++] = checksum;
          // Request to transmit the response.
          CAN_transmit ();
        }
        DIAG_CAN_HOSTCOMM_END 
      }else CAN_on_error();
    }
}


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

/* Computation of checksum for the frame. */
byte 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 hostcomm_application (TBUFFER_DATA *prxdata, byte rxlength, TBUFFER_DATA *ptxdata, byte *txlength) {
  byte node_id;        // addressing node_id byte (0 = to all nodes)
  byte group;          // command group byte (0 = no frame)
  byte command;        // command byte
  
  if (rxlength < 3) {  // safety precaution
    *txlength = 0;
    return;
  }
  
  DIAG_HOSTCOMM_START
  
  node_id = *prxdata++;                // header
  group = *prxdata++;                  // header
  command = *prxdata++;                // header
  *ptxdata++ = node_id;                // header
  *ptxdata++ = group;                  // header
  *ptxdata++ = command ^ CMD_ACK;      // header              
  // Command processing.
  host_prxdata = prxdata;
  host_rxlength = rxlength - 3;
  host_ptxdata = ptxdata;
  host_command [group] (command);
  *txlength = (byte *) host_ptxdata - (byte *) ptxdata + 3;
  DIAG_HOSTCOMM_END
}


/* Send frame to another node and wait for answer if required. */
void hostcomm_CAN_command (TBUFFER_DATA *prxdata, byte rxlength, TBUFFER_DATA *ptxdata, byte *txlength){
  word count;
  byte i;
  
  CAN_txlen = rxlength;
  if (CAN_txlen > SIZE_COMM_BUFFERS) CAN_txlen = SIZE_COMM_BUFFERS;
  
  for (i = 0; i < CAN_txlen; i++){
    CAN_txdata[i] = *prxdata++;
  }
  CAN_transmit();
  
  while (++count < 65535){
    if (CAN_received()){
      if (CAN_rxlen > SIZE_COMM_BUFFERS) CAN_rxlen = SIZE_COMM_BUFFERS;
      *txlength = CAN_rxlen - 1; // without checksum byte
      for (i = 0; i < CAN_rxlen; i++){
        *ptxdata++ = CAN_rxdata[i];
      }
      break;
    }  
  }
}







#pragma CODE_SEG DEFAULT
