drivers/can_ixxat_win32/ixxat.cpp
author fbeaulier
Tue, 16 Aug 2011 14:15:52 +0200
changeset 663 70fc3603e36f
parent 655 ea1ddcc77acf
child 683 a3ff0e3e9829
permissions -rw-r--r--
timers_unix.c : remove sigint and sigterm catch
sdo : Allow multiple servers
The sdo transfer struct is not anymore referenced by server's node id but by
client or server number in the OD. Node id is not relevant in SDO transfert.
/*
This file is part of CanFestival, a library implementing CanOpen Stack.

CanFestival Copyright (C): Edouard TISSERANT and Francis DUPIN
CanFestival Win32 port Copyright (C) 2007 Leonid Tochinski, ChattenAssociates, Inc.

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
*/
// pragma based message
// http://www.codeproject.com/macro/location_pragma.asp
#define __STR2__(x) #x
#define __STR1__(x) __STR2__(x)
#define __LOC2__ __FILE__ "("__STR1__(__LINE__)") : "


#pragma message("*********************************************************************************")
#pragma message("  NOTE: IXXAT Win32 drivers and API should be installed in order to build this project!")
#pragma message(__LOC2__ "See IXXAT.Cpp header for details.")
#pragma message("*********************************************************************************")


// IXXAT adapter driver for CanFestival-3 Win32 port
//
// Notes
//--------------------------------------------
// For building of this project you will need 
// the following IXXAT API files
// Vci2.h
// Vci11un6.lib
//
// IXXAT Win32 drivers and API can be downloaded from
// http://www.ixxat.com/download_vci_en,7547,5873.html
//
// Copy Vci2.h & Vci11un6.lib files to can_ixxat_win32 folder of add path to them in Project settings.


#include <stdio.h>
extern "C" {
#include "applicfg.h"
#include "can_driver.h"
#include "def.h"
}
#include "VCI2.h"
#include "async_access_que.h"

#define CAN_NUM 0

class IXXAT
   {
   public:
      class error
        {
        };
      IXXAT(s_BOARD *board);
      ~IXXAT();
      bool send(const Message *m);
      bool receive(Message *m);
   private:
      bool open(const char* board_name, int board_number, const char* baud_rate);
      bool close();                             
      void receive_queuedata(UINT16 que_hdl, UINT16 count, VCI_CAN_OBJ* p_obj);
      // VCI2 handler      
      static void VCI_CALLBACKATTR message_handler(char *msg_str);
      static void VCI_CALLBACKATTR receive_queuedata_handler(UINT16 que_hdl, UINT16 count, VCI_CAN_OBJ* p_obj);
      static void VCI_CALLBACKATTR exception_handler(VCI_FUNC_NUM func_num, INT32 err_code, UINT16 ext_err, char* err_str);
      
      static void CALLBACK canBusWatchdog(HWND hwnd, UINT msg, UINT_PTR idEvent, DWORD dwTime);
      void watchdog();
   private:
      UINT16 m_BoardHdl;
      UINT16 m_TxQueHdl;
      UINT16 m_RxQueHdl;
      async_access_que<VCI_CAN_OBJ> m_RX_Que;
      static IXXAT* m_callbackPtr;
      static UINT_PTR m_watchdogTimerId;
      static const unsigned int CAN_BUS_WATCHDOG_INTERVAL_MSEC = 10000;

      /** Bitmask inside sts from VCI_ReadCanStatus() that defines the can bus off state.*/
      static const unsigned char STS_CAN_BUS_OFF = 0x80;

      /** Bitmask inside sts from VCI_ReadCanStatus() that defines the can data overrun state.*/
      static const unsigned char STS_CAN_DATA_OVERRUN = 0x20;

      /** Bitmask inside sts from VCI_ReadCanStatus() that defines the remote queue overrun state.*/
      static const unsigned char STS_REMOTE_QUEUE_OVERRUN = 0x04;
   };

IXXAT *IXXAT::m_callbackPtr = NULL;

UINT_PTR IXXAT::m_watchdogTimerId = 0;

IXXAT::IXXAT(s_BOARD *board) : m_BoardHdl(0xFFFF),
                               m_TxQueHdl(0xFFFF),
                               m_RxQueHdl(0xFFFF)
                               
   {
   char busname[100];
   ::strcpy(busname,board->busname);
   char board_name[100];      
   long board_number = 0;   
   char *ptr = ::strrchr(busname,':');
   if (ptr != 0)
      {
      *ptr = 0;
      ::strcpy(board_name,busname);
      if (++ptr - busname < (int)::strlen(board->busname))
         board_number = ::atoi(ptr);
      }
   if (!open(board_name,board_number,board->baudrate))
      {
      close();
      throw error();
      }
   m_callbackPtr = this;
   }

IXXAT::~IXXAT()
   {
   close();
   m_callbackPtr = 0;
   }

bool IXXAT::send(const Message *m)
   {
   if (m_BoardHdl == 0xFFFF)
      return true; // true -> NOT OK
   long res = VCI_ERR;
   if (m->rtr == NOT_A_REQUEST)
      res = VCI_TransmitObj(m_BoardHdl, m_TxQueHdl, m->cob_id, m->len, const_cast<unsigned char*>(m->data));
   else
      res = VCI_RequestObj(m_BoardHdl, m_TxQueHdl, m->cob_id, m->len);

   return (res == VCI_OK);
   }


bool IXXAT::receive(Message *m)
   {
   if (m_BoardHdl == 0xFFFF)
      return false;
   VCI_CAN_OBJ obj;
   if (m_RX_Que.extract_top(obj))
      {
      m->cob_id = static_cast<UNS16>(obj.id); //valid for 11Bit ids
      m->len = obj.len;
      m->rtr = (obj.rtr == VCI_RX_BUF) ? NOT_A_REQUEST : REQUEST;
      if (m->rtr == NOT_A_REQUEST)
         ::memcpy(m->data, obj.a_data, m->len);
      return true;
      }
   return false;
   }

bool IXXAT::open(const char* board_name, int board_number, const char* baud_rate)
   {
   // check, if baudrate is supported
   struct IXXAT_baud_rate_param 
     { 
     UINT8  bt0; 
     UINT8  bt1;
     };
   struct IXXAT_look_up_table
     {
     char baud_rate[20];
     IXXAT_baud_rate_param bt;
     };
   static const IXXAT_look_up_table br_lut[] = {
               {"10K",{VCI_10KB}},
               {"20K",{VCI_20KB}},
               {"50K",{VCI_50KB}},
               {"100K",{VCI_100KB}},
               {"125K",{VCI_125KB}},
               {"250K",{VCI_250KB}},
               {"500K",{VCI_500KB}},
               {"800K",{VCI_800KB}},
               {"1M",{VCI_1000KB}}
               };
   static const long br_lut_size = sizeof (br_lut)/sizeof(IXXAT_look_up_table);
   int index;
   for (index = 0; index < br_lut_size; ++index)
       {
       if (::strcmp(br_lut[index].baud_rate,baud_rate)==0)
          break;
       }
   if (index == br_lut_size)    
      return false;
   // close existing board   
   close();
   // init IXXAT board
   unsigned long board_type = VCI_GetBrdTypeByName(const_cast<char*>(board_name));
   long res = VCI2_PrepareBoard( board_type,                  // board type
                                   board_number,              // unique board index
                                   NULL,                      // pointer to buffer for additional info 
                                   0,                         // length of additional info buffer
                                   message_handler,           // pointer to msg-callbackhandler
                                   receive_queuedata_handler, // pointer to receive-callbackhandler
                                   exception_handler);        // pointer to exception-callbackhandler
   if (res < 0)
      return false;
   m_BoardHdl = (UINT16)res;

   VCI_ResetBoard(m_BoardHdl);
   
   // init CAN parameters
   
   // initialize CAN-Controller
   res = VCI_InitCan(m_BoardHdl, CAN_NUM, br_lut[index].bt.bt0,br_lut[index].bt.bt1, VCI_11B);
   
   //  definition of Acceptance-Mask (define to receive all IDs)
   res = VCI_SetAccMask(m_BoardHdl, CAN_NUM, 0x0UL, 0x0UL);

   // definition of Transmit Queue
   res = VCI_ConfigQueue(m_BoardHdl, CAN_NUM, VCI_TX_QUE, 100 , 0, 0, 0,  &m_TxQueHdl);
   
   //  definition of Receive Queue (interrupt mode)
   res = VCI_ConfigQueue(m_BoardHdl, CAN_NUM, VCI_RX_QUE, 500, 1, 0, 100,  &m_RxQueHdl);

   //  assign the all IDs to the Receive Queue
   res = VCI_AssignRxQueObj(m_BoardHdl, m_RxQueHdl ,VCI_ACCEPT, 0, 0) ;

   //  And now start the CAN
   res = VCI_StartCan(m_BoardHdl, CAN_NUM);

   //Start CAN Bus-Off watchdog
   m_watchdogTimerId = SetTimer(NULL, NULL, IXXAT::CAN_BUS_WATCHDOG_INTERVAL_MSEC, IXXAT::canBusWatchdog);
   
   return true;
   }

bool IXXAT::close()
   {
   if (m_BoardHdl == 0xFFFF)
      return true;
   VCI_ResetBoard(m_BoardHdl);   
   VCI_CancelBoard(m_BoardHdl);
   m_BoardHdl = 
   m_TxQueHdl = 
   m_RxQueHdl = 0xFFFF;
   return true;
   }

void IXXAT::receive_queuedata(UINT16 que_hdl, UINT16 count, VCI_CAN_OBJ *p_obj)
   {
   for (int i = 0; i < count; ++i)
       m_RX_Que.append(p_obj[i]); // can packet
   }

void VCI_CALLBACKATTR IXXAT::receive_queuedata_handler(UINT16 que_hdl, UINT16 count, VCI_CAN_OBJ *p_obj)
  {
   if (m_callbackPtr != NULL)
      m_callbackPtr->receive_queuedata(que_hdl, count, p_obj);
  }

void VCI_CALLBACKATTR IXXAT::message_handler(char *msg_str)
  {
  char buf[200];
  ::sprintf(buf,"IXXAT Message: [%s]\n", msg_str);
  ::OutputDebugString(buf);
  }

void VCI_CALLBACKATTR IXXAT::exception_handler(VCI_FUNC_NUM func_num, INT32 err_code, UINT16 ext_err, char* err_str)
  {
  static const char* Num2Function[] =
    {
    "VCI_Init",
    "VCI_Searchboard",
    "VCI_Prepareboard",
    "VCI_Cancel_board",
    "VCI_Testboard",
    "VCI_ReadBoardInfo",
    "VCI_ReadBoardStatus",
    "VCI_Resetboard",
    "VCI_ReadCANInfo",
    "VCI_ReadCANStatus",
    "VCI_InitCAN",
    "VCI_SetAccMask",
    "VCI_ResetCAN",
    "VCI_StartCAN",
    "VCI_ResetTimeStamps",
    "VCI_ConfigQueue",
    "VCI_AssignRxQueObj",
    "VCI_ConfigBuffer",
    "VCI_ReconfigBuffer",
    "VCI_ConfigTimer",
    "VCI_ReadQueStatus",
    "VCI_ReadQueObj",
    "VCI_ReadBufStatus",
    "VCI_ReadBufData",
    "VCI_TransmitObj",
    "VCI_RequestObj",
    "VCI_UpdateBufObj",
    "VCI_CciReqData"
    };
  char buf[200];
  ::sprintf(buf, "IXXAT Exception: %s (%i / %u) [%s]\n", Num2Function[func_num], err_code, ext_err, err_str);
  ::OutputDebugString(buf);
  }

  void IXXAT::watchdog()
  {
    VCI_CAN_STS sts;
    long res = VCI_ReadCanStatus(m_BoardHdl, CAN_NUM, &sts);

    if (res < 0)
    {
      char buf[200];
      ::sprintf(buf, "\nIXXAT canBusWatchdog: ERROR: Reading the can state failed!\n");
      ::OutputDebugString(buf);
    }
    else
    {
      if (sts.sts & (STS_CAN_BUS_OFF | STS_CAN_DATA_OVERRUN | STS_REMOTE_QUEUE_OVERRUN))
      {
        if (sts.sts & STS_CAN_BUS_OFF)
        {
          ::OutputDebugString("\nIXXAT canBusWatchdog: CAN bus off detected!\n");
        }
        if (sts.sts & STS_CAN_DATA_OVERRUN)
        {
          ::OutputDebugString("\nIXXAT canBusWatchdog: CAN data overrun detected!\n");
        }
        if (sts.sts & STS_REMOTE_QUEUE_OVERRUN)
        {
          ::OutputDebugString("\nIXXAT canBusWatchdog: Remote queue overrun detected!\n");
        }

        if (VCI_ResetCan(m_BoardHdl, CAN_NUM) < 0)
        {
          ::OutputDebugString("\nIXXAT canBusWatchdog: ERROR: Resetting the can module failed!\n");
        }

        if (VCI_StartCan(m_BoardHdl, CAN_NUM) < 0)
        {
          ::OutputDebugString("\nIXXAT canBusWatchdog: ERROR: Restaring the can module failed!\n");
        }
      }
    }

    if (SetTimer(NULL, m_watchdogTimerId, IXXAT::CAN_BUS_WATCHDOG_INTERVAL_MSEC, IXXAT::canBusWatchdog) == 0)
    {
      char buf[200];
      ::sprintf(buf, "\nIXXAT canBusWatchdog: ERROR: Creation of the watchdog timer failed!\n");
      ::OutputDebugString(buf);
    }
  }

void CALLBACK IXXAT::canBusWatchdog(HWND hwnd, UINT msg, UINT_PTR idEvent, DWORD dwTime)
{
   if (m_callbackPtr != NULL)
      m_callbackPtr->watchdog();
}

//------------------------------------------------------------------------
extern "C"
   UNS8 __stdcall canReceive_driver(CAN_HANDLE inst, Message *m)
   {
     return reinterpret_cast<IXXAT*>(inst)->receive(m) ? 0 : 1;
   }
                            
extern "C"
   UNS8 __stdcall canSend_driver(CAN_HANDLE inst, Message const *m)
   {
     return reinterpret_cast<IXXAT*>(inst)->send(m) ? 0 : 1;
   }

extern "C"
   CAN_HANDLE __stdcall canOpen_driver(s_BOARD *board)
   {
   try
      {
      return new IXXAT(board);
      }
   catch (IXXAT::error&)
      {
      return 0;
      }
   }

extern "C"
   int __stdcall canClose_driver(CAN_HANDLE inst)
   {
   delete reinterpret_cast<IXXAT*>(inst);
   return 1;
   }
   
extern "C"
   UNS8 __stdcall canChangeBaudRate_driver( CAN_HANDLE fd, char* baud)
	{
	//printf("canChangeBaudRate not yet supported by this driver\n");
	return 0;
	}