/*******************************************************************************
*  (c) synertronixx GmbH
*  Vahrenwalder Str. 7
*  D-30165 Hannover
*  Tel. : 0511 / 93 57 670
*  Fax. : 0511 / 93 57 679
*  Web  : www.synertronixx.de
*  eMail: info@synertronixx.de
* ----------------------------
* Name          : catest.c
* Version       : 1.02
* Datum         : 12.07.2010
* Author        : Blaschke
* Description   : Example for CAN2Web-Advanced for using synertronixx
*                 CAN2Web CAN driver and serial interface:
*                 Function: N CAN messages will be send at a baudrate of rate kBit/s 
*                 and all incomming messages will be displayed.
*                 When N is negativ program runs until -N Messages have been received
*                 and no messages will be send 
*                 Additional serial interface is opened and a start message will be send.
*                 Received characters are send back (echo).
* program call  : catest.exe N rate mode;
                    e.g.: catest.exe 23 100 0 (send 23 messages at 100 kBit/s in CAN2.0A mode)
                    e.g.: catest.exe 17 100 1 (send 17 messages at 100 kBit/s in CAN2.0B mode)
                    e.g.: catest.exe 34 100 2 (send 34 messages at 100 kBit/s in CAN2.0A/B mixed mode)
* *****************************************************************************/

//LINUX-Header
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>

// CAN2Web driver Header
#include "can_driver.h"


// CAN settings
int fd_can;                             // file descriptor for CAN kernel driver
unsigned int      can_baudrate = 100;   // baudrate 
unsigned long int can_mask=0x1FFFFFFFL; // CAN-mask-register (0x00..0x7FF) for CAN 2.0A
                                        // and (0x00000000..0x1FFFFFFF)    for CAN 2.0B
unsigned long int can_code=0x1FFFFFFFL; // CAN-Code-Register (0x00..0x7FF) for CAN 2.0A
                                        // and (0x00000000..0x1FFFFFFF)    for CAN 2.0B
int               can_extended=0;       // CAN mode: 0=CAN2.0A, 1=CAN2.0B, 2=CAN2.0A/CAN2.0B mixed mode
int               can_btr_on=0;         // 1=direct access to baudrate register
unsigned int      can_btr;              // value for baudrate register


char* appl_name="CAN2Web Advanced Test";  // Application name for debug messages

// for sending/receiving CAN-Messages
struct TCANMsg  can_out;                 // CAN-Message-stucture for sending data
struct TCANMsgT can_in;                  // CAN-Message-stucture for receiving data

unsigned char     can_error;             // Buffer for CAN error state
long int n=1;                            // Loop counter
long int N;                              // nr. of CAN messages to send
int rate;                                // baudrate for CAN bus
int i;                                   // counter
int m;                                   // counter
long int n_old;                          // counter
unsigned int nr_msgs;                    // nr. of messages in receive buffer 

// for serial interface COM1
int  fd_serial=-1;                                     // file descriptor for serial interface
unsigned long int com1_baudrate = 19200;               // baudrate 19200 bit/second
int               com1_databits = 8;                   // nr. of data bits
int               com1_stopbits = 1;                   // nr. of stop bits
char              com1_parity   = 'N';                 // parity = 'No'
char              com1_flowctrl = 'N';                 // flow contorl = 'No'
char              com1_message[]= "\n\nHello world!\nWaiting for data from serial interface ...\n";
//
#define MAX_CMD_BUFFER_SIZE 50                          // size of receive buffer
int               com1_rcv;                             // nr. of bytes received
char              com1_rcv_buffer[MAX_CMD_BUFFER_SIZE]; //receive buffer



/******************************************************************************
Function:    CANPrintMsgT
Description: print a CAN msg. type TCANMsgT
Parameter:   can_msg: ptr to struct TCANMsg
Return:      none
Date:        12.07.2010
Changes:
******************************************************************************/
void
CANPrintMsgT(struct TCANMsgT* can_msg)
{
  int nr=0;
  char str[200], str2[20];

  sprintf(str,"%10ld.%06ld  ID:%08lX RTR:%d Len:%d ", can_msg->TIMES, can_msg->TIMEUS,
          can_msg->ID, can_msg->RTR, (can_msg->LEN&0x0F));

  if (can_msg->RTR==0)
  {
    for (nr=0; nr <= (can_msg->LEN&0x0F)-1; nr++)
    {
      sprintf(str2, " %02X", can_msg->DATA[nr]);
      strcat(str,str2);
    }
  }

  if(can_extended == 2)
  { // CAN2.0A/CAN2.0B mixed mode
    if(can_msg->LEN&0x40)
    {
      if(can_msg->LEN&0x20)
        strcat(str, " (CAN2.0B)");
      else
        strcat(str, " (CAN2.0A)");
    }
  }

  strcat(str,"\n");
  printf(str);

}

/******************************************************************************
Function:    CANSetParameter
Description: set parameter for CAN-bus
Parameter:   none
Return:      1, when setting was sucessfull
             -1, otherwise
Date:        19.10.2005
Changes:     
******************************************************************************/
int
CANSetParameter(void)
{ // Init CAN-Bus
  int ret_value;
  struct CANSettings cs;
 
  // copy data to CANSettings structure
  cs.mode            = (unsigned char)can_extended;              
  if(can_btr_on==1)
    cs.baudrate        = can_btr; 
  else
    cs.baudrate        = can_baudrate;          
  cs.baudrate_to_btr = (unsigned char)can_btr_on;  
  cs.mask            = can_mask;               
  cs.code            = can_code;                                                 

  // call kernel driver
  ret_value = ioctl(fd_can, CAN_SET_SETTINGS, &cs);

  return ret_value;
}

/******************************************************************************
Function:    CANSetOnOff
Description: switch CAN-bus on/off
Parameter:   state: 0 CAN off, 1 CAN on
Return:       1, when setting was sucessfull
             -1, otherwise
Date:        19.10.2005
Changes:     
******************************************************************************/
int
CANSetOnOff(int state)
{                                   
  return ioctl(fd_can, CAN_SET_ON_OFF, state);
}


/******************************************************************************
Function:    CANGetDriverInfo
Description: get some information from CAN driver
Parameter:   none
Return:       1, when reading was sucessfull
             -1, otherwise
Date:        19.10.2005
Changes:     
******************************************************************************/
int
CANGetDriverInfo(void)
{ 
  int ret_value;
  struct DriverInfo dinfo;

  ret_value = ioctl(fd_can, CAN_GET_DRIVER_INFO, &dinfo);
  usleep(500000);
  if(ret_value==1)
  { 
    printf("\nDriver info:\n");
    printf("Name: '%s'\n",  dinfo.version);
    printf("Received since last parameter setting :%lu\n",   dinfo.total_rcv);
    printf("Send since last parameter setting     :%lu\n",   dinfo.total_snd);
    printf("CAN-msg. Receive buffer size          :%lu\n",   dinfo.rcv_buffer_size);
    printf("CAN-msg. Send buffer size             :%lu\n\n", dinfo.snd_buffer_size);
  }
  else
    printf("CAN driver Info not available\n");
 usleep(1000000);
  return ret_value;
}

/******************************************************************************
Function:    CANGetSettings
Description: get the actual settings from CAN driver
Parameter:   none
Return:      1, when reading was sucessfull
             0, otherwise
Date:        01.09.2005
Changes:     
******************************************************************************/
int
CANGetSettings(void)
{ 
  int ret_value;
  struct CANSettings cs;

  ret_value = ioctl(fd_can, CAN_GET_SETTINGS, &cs);
  usleep(500000);
  if(ret_value==1)
    printf("Baudrate=%d BTR=%d mode=%d mask=0x%08X code=0x%08X\n", 
           cs.baudrate, cs.baudrate_to_btr, cs.mode, cs.mask, cs.code);
  else
    printf("CAN settings not available\n");
  usleep(500000);
  return ret_value;
}

/******************************************************************************
Function:    COM1SetParameter
Description: set parameter for serial interface COM1
Parameter:   none
Return:      return value from tcsetattr()
Date:        28.09.2009
Changes:     
******************************************************************************/
int
COM1SetParameter(void)
{
  int ret_value=0;
  struct termios options;
  speed_t speed;
  
  tcgetattr(fd_serial, &options);        // Get the current options for the port

  // setting baudrate
  switch (com1_baudrate)
  {
    case    300: speed =    B300; break;
    case    600: speed =    B600; break;
    case   1200: speed =   B1200; break;
    case   2400: speed =   B2400; break;
    case   4800: speed =   B4800; break;
    case   9600: speed =   B9600; break;
    case  19200: speed =  B19200; break;
    case  38400: speed =  B38400; break;
    case  57600: speed =  B57600; break;
    case 115200: speed = B115200; break;
    default:     speed = B19200;  break;
  } 

  ret_value = cfsetispeed(&options, speed);   // Set the in baud rate 
  ret_value = cfsetospeed(&options, speed);   // Set the out baud rate 
  if (ret_value==-1)
    printf("%s: COM1 setting baudrate failed, errno=%d %s\n", appl_name, errno, strerror(errno));
  else
  {

    // setting character size
    switch (com1_databits)
    {
      case 7: // 7 databits 
        options.c_cflag &= ~CSIZE;     // Mask the character size bits 
        options.c_cflag |= CS7;        // Select size
        break;
      case 8: // 8 databits 
      default: options.c_cflag &= ~CSIZE;     // Mask the character size bits 
        options.c_cflag |= CS8;        // Select size
        break;
    }

#define CSTOPB2 CSTOPB
    // set stop bits
    switch (com1_stopbits)
    {
      case 1: // 1 stop bit
        options.c_cflag &=~ CSTOPB2; break; 
      case 2: // 2 stop bits
        options.c_cflag |=  CSTOPB2; break; 
      
      default:// 1 stop bit
        options.c_cflag &=~ CSTOPB2; break;  
    }

    // setting parity
    switch (com1_parity)
    {
      case 'N':
      case 'n': // No parity
        options.c_cflag &= ~PARENB;  
        break;   

      case 'O':
      case 'o': // Odd
        options.c_cflag |= PARENB;   
        options.c_cflag |= PARODD;
        break;   

      case 'E':
      case 'e': // Even
        options.c_cflag |= PARENB;   
        options.c_cflag &= ~PARODD;
        break;  

      case 'M':
      case 'm': /* ?? */ break;   // Mark

      case 'S':
      case 's': // Space parity is setup the same as no parity 
        options.c_cflag &= ~PARENB; 
        break;   

      default : // No parity 
        options.c_cflag &= ~PARENB; 
        break;
    }

#define CNEW_RTSCTS CRTSCTS
    //  Flowcontrol einstellen
    switch (com1_flowctrl)
    {
      case 'N':
      case 'n': // None
        options.c_cflag &= ~CNEW_RTSCTS; break;  
      case 'R':
      case 'r': // RTS CTS
        options.c_cflag |= CNEW_RTSCTS; break;  
      case 'X':
      case 'x': // XON XOFF: not available
        options.c_cflag &= ~CNEW_RTSCTS; break;  
      default : // default is off
        options.c_cflag &= ~CNEW_RTSCTS; break;  
    }

    // Setting Software Flow Control: Software flow control is enabled using the IXON, IXOFF, and IXANY constants:
    // options.c_iflag |= (IXON | IXOFF | IXANY);
    // To disable software flow control simply mask those bits:
    // options.c_iflag &= ~(IXON | IXOFF | IXANY);
    // The XON (start data) and XOFF (stop data) characters are defined in the c_cc array described below.


    // Choosing Raw Input: Raw input is unprocessed. Input characters are passed through exactly as 
    // they are received, when they are received. Generally you'll deselect the ICANON, ECHO, ECHOE, and ISIG options when using raw input:
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); 
    options.c_cc[VMIN] = 0;
    options.c_cc[VTIME] = 0;

    // set new settings now
    options.c_cflag |= (CLOCAL | CREAD);

    ret_value = tcsetattr(fd_serial, TCSANOW, &options); // Set the new options for the port
                                                         // TCSANOW: Make changes now without waiting for data to complete
    if (ret_value==-1)
      printf("%s: COM1 setting flags failed, errno=%d %s\n", appl_name, errno, strerror(errno));
    else
      printf("%s: COM1 Init. baudrate=%lu, databits=%d, parity=%c, flowcontrol=%c\n", appl_name, com1_baudrate, com1_databits,
             com1_parity, com1_flowctrl);
  }

  return ret_value;
}

/*******************************************************************************
* M A I N
*******************************************************************************/

int main(int argc, char *argv[])
{
  int result, i;

  printf("\n\n%s: start of test program for CAN bus!\n", appl_name);

  // get nr. of CAN-Messages to send and baudrate from arguments
  if (argc==4)
  {
    if ( (sscanf(argv[1],"%ld",&N)==1)     &&
         (sscanf(argv[2],"%d", &rate)==1)  &&
         (sscanf(argv[3],"%d", &can_extended)==1) )
    {
      if (N>=0)
        printf("%s: Sending %d CAN-msgs. at %d kBits/s\n", appl_name, N, rate);
      else
        printf("Waiting for %d CAN-msgs. at %d kBits/s to receive\n", -N, rate);

      if (N<0)
        n=2*N+1;
    }
  }
  else
  {
    printf("arguments error! call: catest.exe 'nr.' 'baudrate' 'mode'\nexit program!\n");
    exit(1);
  }
  can_baudrate = rate;
  usleep(500000);

   // open CAN devices   
  fd_can = open("/dev/can0", O_RDWR);
  if (fd_can<0)
  { // Error
    printf("can't open CAN device, errno=%d %s, Exit program\n", errno, strerror(errno));
    close(fd_can);
    exit(1);
  }
  else
    CANGetDriverInfo(); // all fine, so read driver info

  CANSetOnOff(0);       // CAN-Bus off

  // Enable/disable CAN driver debug messages, when enable driver will print messages like:
  // "synertronixx CAN2Web CAN driver V1.02.3 25.09.2009 15:50: Debug mode enabled" or
  // "synertronixx CAN2Web CAN driver V1.02.3 25.09.2009 15:50: 'ioctl' baudrate=100, btr=0, mode=1, mask=1FFFFFFF, code=1FFFFFFF"

  //ioctl(fd_can, CAN_SET_DEBUGMODE, 0); // disable debug messages from CAN driver
  ioctl(fd_can, CAN_SET_DEBUGMODE, 1); // enable debug messages from CAN driver

  // set CAN parameter
  result = CANSetParameter(); // the the can parameter
  usleep(1000000);
  printf("%s: Set CAN parameter result=%d\n", appl_name, result);
  if(result!=1)
  {
    printf("%s: can't set CAN parameter, Exit program\n", appl_name);
    close(fd_can);
    exit(1);
  }
  
  CANGetSettings(); // just read and print the settings again

  CANSetOnOff(1);   // CAN-Bus on again

  ioctl(fd_can,  CAN_SET_HIGHSPEED, 0); 



  //--------------------------------------------------------------------------------------
  // open serial device
  //--------------------------------------------------------------------------------------
  fd_serial = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
  if (fd_serial<0)
  { // Error
    printf("%s: can't open COM device driver '/dev/ttyS0', errno=%d %s, Exit program\n", appl_name, errno, strerror(errno));
    close(fd_serial);
    exit(1);
  }
  else
  { // found serial interface
    COM1SetParameter();                                   // set interface parameter
    write(fd_serial, com1_message, strlen(com1_message)); // send a start message
  }


  printf("%s: is running\n", appl_name);
 
  // main loop
  do
  {
    usleep(50000); // wait 50 ms

    // read and print new CAN-message from CAN receive buffer
    do
    {
      nr_msgs=0;
      if (ioctl(fd_can, CAN_GET_RECEIVE_MSG, &nr_msgs)==1)
      { // ioctl returned valid value
        if (nr_msgs>0)
        { // there are still CAN messages
          if ( read(fd_can,(void*)&can_in, sizeof(can_in)) == sizeof(can_in) ) // read msg
          {
            CANPrintMsgT(&can_in);                                             // print message
            if (N<0)
              n++;
          }
        }
      }
    }
    while (nr_msgs>0); 
     

    // create and send a new CAN-messages
    if (N>0)
    { // create message
      can_out.ID  = n; 
      can_out.RTR = 0;
      can_out.LEN = 8;

      if(can_extended==2)
      { // CAN2.0A/CAN2.0B mixed mode
        if(n & 0x01)
          can_out.LEN |= 0x60; // CAN2.0A
        else
          can_out.LEN |= 0x40; // CAN2.0B
      }

      for (i=0; i <= (can_out.LEN&0x0F)-1; i++)
        can_out.DATA[i]=(unsigned char) n;

      // send messages
      if ( write(fd_can,(void*)&can_out, sizeof(can_out) ) == -1) 
        printf("CAN transmit buffer full!\n");

      ioctl(fd_can, CAN_SET_LED, (unsigned char) n);
    }

    
    // get CAN error information
    if (ioctl(fd_can, CAN_GET_STATE, &can_error)==1)// read CAN state
    {
      if ( (can_error&0xFC) != CAN_ERR_OK)
      {
        printf("CAN-Error %02X: ", can_error);
        can_error&=0xFC;
        if (can_error&CAN_ERR_OVERRUN)              printf(" OVERRUN");
        if (can_error&CAN_ERR_BUSERROR)             printf(" BUSERROR");
        if (can_error&CAN_ERR_BUSOFF)               printf(" BUSOFF");
        if (can_error&CAN_ERR_RECEIVEBUF_OVERFLOW)  printf(" RECEIVEBUF_OVERFLOW");
        if (can_error&CAN_ERR_TRANSMITBUF_OVERFLOW)
        {
          n--;
          printf(" TRANSMITBUF_OVERFLOW");
        }
        printf("\n");
      }
    }

    if (N>0)
      n++;


    // check serial interface
    com1_rcv = read(fd_serial, com1_rcv_buffer, MAX_CMD_BUFFER_SIZE);
    if (com1_rcv>0)
    {  // received data -> just echo it for test
      write(fd_serial, com1_rcv_buffer, com1_rcv);
    }
  }
  while(n<=N);


  // Wait until transmit buffer is empty when sending is possible or
  // max. 10 loops when not
  m=10;
  n_old=0; 
  do
  {
    ioctl(fd_can, CAN_GET_TRANSMIT_MSG, &n); // read CAN state
    if (n<n_old)
    {
      m++;
      n_old=n;
    }
    m--;
    printf("Messages in transmit buffer:%d\n\r",n);
    usleep(100000);  // wait 100ms
  }
  while( (n!=0) && (m>0) ); 
  

  // shutdown all interfaces
  printf("\n%s: shutdown program\n", appl_name);

  CANGetDriverInfo();       // see internal driver information
  CANSetOnOff(0);           // CAN-Bus off

  close(fd_can);            // shutdown CAN stream
  close(fd_serial);         // shutdown serial stream

  printf("%s: end of program\n", appl_name);

  return 0;                 // bye, bye, ....
}


// end of file






