// can_copcican_win32.cpp : Defines the exported functions for the DLL application.
//


#include "stdafx.h"

/*
This file is part of CanFestival, a library implementing CanOpen Stack.

Copyright (C): Cosateq GmbH & Co.KG
               http://www.cosateq.com/
               http://www.scale-rt.com/

See COPYING file for copyrights details.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*
	CAN driver interface for CO-PCICAN card.
*/

#include <stdio.h>
//#include <unistd.h>
#include <fcntl.h>

//#define NEED_PRINT_MESSAGE
static int initdone = 0;

inline void * maskDevID(int devId){	return ((void*)( (devId & 0x0000000F) | 0xC05A0000)); }
inline int unmaskDevID(void * Ptr){	return ((int)((int)Ptr & 0x0000000F)); }

/* at the moment not threadsafe :-( */
static unsigned char selectedChannelRx = 1, selectedChannelTx = 1;

static cosateq::devices::can::co_baudrate_t TranslateBaudRate( char* optarg )
{
  /* values see documentation of CO-PCICAN */
	if( !strcmp( optarg, "1M"    ) ) return cosateq::devices::can::CO_BAUD_1M;
	if( !strcmp( optarg, "800K"  ) ) return cosateq::devices::can::CO_BAUD_800k;
	if( !strcmp( optarg, "500K"  ) ) return cosateq::devices::can::CO_BAUD_500k;
  if( !strcmp( optarg, "250K"  ) ) return cosateq::devices::can::CO_BAUD_250k;
  if( !strcmp( optarg, "125K"  ) ) return cosateq::devices::can::CO_BAUD_125k;
  if( !strcmp( optarg, "100K"  ) ) return cosateq::devices::can::CO_BAUD_100k;
  if( !strcmp( optarg, "83.3K" ) ) return cosateq::devices::can::CO_BAUD_83k;
  if( !strcmp( optarg, "10K"   ) ) return cosateq::devices::can::CO_BAUD_10k;

  return (cosateq::devices::can::co_baudrate_t)-1;
}

int co_pcican_select_channel( const unsigned char channel, const unsigned int direction )
{
  if( channel >= CHANNELS )
  {
    MSG("can_copcican_linux: co_pcican_select_channel(): invalid channel\n");
    return -1;
  }

  /* at the moment not threadsafe :-( */
  switch( direction )
  {
    case cosateq::devices::can::CO_CAN_RX: selectedChannelRx = channel;
             break;
    case cosateq::devices::can::CO_CAN_TX: selectedChannelTx = channel;
             break;
    default: return -1;
  }

  return 0;
}

int co_pcican_configure_selected_channel( const int fd, s_BOARD *board, const unsigned int direction )
{
  unsigned int selectedChannel;
  int ret = 0;

  if( fd < 0 )
  {
    MSG("can_copcican_linux: co_pcican_configure_selected_channel(): invalid file descriptor\n");
    return -1;
  }

  if( board == NULL )
  {
    MSG("can_copcican_linux: co_pcican_configure_selected_channel(): board is NULL\n");
    return -1;
  }

  if( board->baudrate == NULL )
  {
    MSG("can_copcican_linux: co_pcican_configure_selected_channel(): baudrate is NULL\n");
    return -1;
  }

  switch( direction )
  {
    case cosateq::devices::can::CO_CAN_RX: selectedChannel = selectedChannelRx;
             break;
    case cosateq::devices::can::CO_CAN_TX: selectedChannel = selectedChannelTx;
             break;
    default: selectedChannel = 0xff;
  }

  if( selectedChannel >= CHANNELS )
  {
    MSG("can_copcican_linux: co_pcican_configure_selected_channel(): invalid channel selected\n");
    return -1;
  }

  ret = can_setChannelConfig( TranslateBaudRate( board->baudrate ),0x0,unmaskDevID((void*)fd),selectedChannel);
  if ( ret != 0 ) {
	  can_enterRunMode(unmaskDevID((void*)fd),selectedChannel/2);
	  return -ret;
  }else{
	  ret = can_setIrqMode(unmaskDevID((void*)fd),selectedChannel);
	  can_enterRunMode(unmaskDevID((void*)fd),selectedChannel/2);
  }
  return ret;
}

/*********functions which permit to communicate with the board****************/

UNS8 __stdcall canReceive_driver( CAN_HANDLE fd0, Message *m )
{
  cosateq::devices::can::co_can_packet_t p;
  int ret = 0;

  if ( !m )
	  return 0;

  memset(&p,0,sizeof(cosateq::devices::can::co_can_packet_t));
  ret = can_receive(&p,unmaskDevID(fd0),selectedChannelRx);
  //printf("rec: %d\n",ret);
  if ( ret != 0 )
	  return 1;

  //printf("rec2: %d\n",ret);
  if ( p.size > 8 )
	  p.size = 8;

  if ( !(p.type & MSG_RTR ) )
    memcpy(m->data,p.data,p.size);

  m->cob_id = p.id;
  m->len = p.size;
  m->rtr = p.type & MSG_RTR;
  
  return 0;
}

/***************************************************************************/

UNS8 __stdcall canSend_driver( CAN_HANDLE fd0, Message *m )
{
  cosateq::devices::can::co_can_packet_t p;
  
  if ( !m )
	  return 0;
  memset(&p,0,sizeof(cosateq::devices::can::co_can_packet_t));
  p.id = m->cob_id;
  p.size = (m->len > 8) ? 8 : m->len;
  p.type = ( m->rtr ) ? 0x2:0x0;
  if ( p.id > 0x800 )
	  p.type |= MSG_EXT;
  memcpy(p.data,m->data,p.size);

  return can_send(&p,unmaskDevID(fd0),selectedChannelTx);
}



/***************************************************************************/

UNS8 __stdcall canChangeBaudRate_driver( CAN_HANDLE fd0, char* baud )
{
  UNS8 ret = 0;

  ret = can_enterCfgMode(unmaskDevID(fd0),0);
  if ( ret != 0)
    MSG("Enter config mode for Channelpair 0 failed in Function %s! Error: %d\n",__FUNCTION__,ret);
  ret = can_enterCfgMode(unmaskDevID(fd0),1);
  if ( ret != 0)
    MSG("Enter config mode for Channelpair 1 failed in Function %s! Error: %d\n",__FUNCTION__,ret);

  for ( int i = 0; i < 4; i++ ) {
      ret = can_setChannelConfig(TranslateBaudRate(baud),0x0,unmaskDevID(fd0),i);
      if ( ret != 0)
        MSG("Set config for channel %d failed in Function %s! Error: %d\n",i,__FUNCTION__,ret);
	  can_setIrqMode(unmaskDevID(fd0),i);
  }
  ret = can_enterRunMode(unmaskDevID(fd0),0);
  if ( ret != 0)
    MSG("Enter run mode for Channelpair 1 failed in Function %s! Error: %d\n",__FUNCTION__,ret);

  ret = can_enterRunMode(unmaskDevID(fd0),1);
  if ( ret != 0)
    MSG("Enter run mode for Channelpair 1 failed in Function %s! Error: %d\n",__FUNCTION__,ret);

  return ret;
}

CAN_HANDLE __stdcall canOpen_driver( s_BOARD *board )
{
	int ret = 0;
	int devId = 0;
	if ( !board )
		return NULL;
	if ( !board->busname )
		return NULL;
	//TODO find out how boardname resolves

	//printf("BOARD: 0x%x %s\n",board,board->busname);
	//sscanf_s(board->busname,"%d",&devId);

	if ( devId < 0 || devId > 15 )
		return NULL;

	//return (can_HANDLE)NULL;
	if ( !initdone ) {
		can_init();
		initdone = 1;
	}

	ret = can_open(devId);
	if ( ret < 0 )
		return (CAN_HANDLE)NULL;
	
	canChangeBaudRate_driver( maskDevID(devId),board->baudrate );
	
	return maskDevID(devId);
}

int __stdcall canClose_driver(CAN_HANDLE fd0 )
{
	return can_close(unmaskDevID(fd0));
}

