Hello everybody,
I have been looking into adding can-fd driver support to can festival
and would like feedback/suggestion so we together can make the
solution together.
Sorry that it is a loot to read.
- What need to change?
The current driver API need to be extended to be able to do the following:
* Enable can-FD mode
* Check if driver support can-FD
* Set data-baudtrate
* Receive/send normal and can-fd messages
* switch from can-fd bosh and can-fd iso (possible on some hw)
So when one is to change the drivers anyway could this be the time to
add more functionality to the driver API?
* hw/kernel can filter support
* get can state (active,warning,passive, off)
* start/stop/reset driver
* get rx,tx error counter
* listen only mode. (For autobaudrate usage)
I think so.
But if it ends up with heavy rewriting of all the drivers then no.
As a test i have written a (almost) complete socketcan driver using
the suggested new API.
>From what i have learned to takes some work to rewrite drivers to
suggested new API so the current and new API should be able to live
along each other.
- Here is a high level view of the current and suggested new API
Current driver has 5 public functions:
* CAN_HANDLE open(s_BOARD* (name, baudrate)
* close(CAN_HANDLE)
* setbaudrate(CAN_HANDLE, baudrate)
* send(canmsg)
* canmsg recive
The new API has only one public function
* driverStruct openEx(const char name).
The open function does fill the driverStructure with function pointers
and variable, do some basic init stuff procedure.
The function does not set baud rate or start the device.
driverStruct
{
void* pointer to driver internal struct. Like current CAN_HANDLE
long int supported CtrlMode. bit-field indicating what function mode
the driver supports (fd, fd non-is, listen only etc.)
setBaudrate (function pointer) to a function that set main and
databaudrate individually..... maybe better to set them at the same
time?
send (function pointer) same function as current API
recive (function pointer) same function as current API
setState (function pointer) start/stop the driver
//The functions pointer below are optional and may be set to NULL
getCanState //active, warning passive etc.
getErrorCounters //get tx/rx counter
setMode (turns on and off multiple modes at the same time)
}
Using the new API to init the driver one must call multiple functions
to replace the current open function
1. openEx to get the driver structure
2. setBaudrate to set baud rate
3. setState to start the device
- Why use struck and more functions:
The reason for splitting up open into multiple stages is to enable
function like autobaudrate.
There are multiple reason for using.
1. One can add optional functions and test for support in runtime by
checking if the function pointer is NULL
2. One can create a layered structure by replacing send/receive
function with one that does something with the message before it is
sent or receive.
To make a software filter:
1. Get the struct from the driver
2. Make a copy of the struct and change the receive function to
filter function that when call calles the driver receive function and
checks the message and then optional passes it on.
Implementing CiA 302 (redundancy) can also be a lot simpler to
create a layer between the driver and the normal caneopen stack
- How to support new and current API at the same time?
As far as i can see it is during startup and shutdown things need to
take different directions
When using dynamic loading (.so/.dll)
If openEx is found use the new way of initing the driver. If it is
not found init the driver to current way.
This can be done in runtime
On close check for openEx again and close using different ways
When static linking
As i have not found a way to detect if a function exist i compiler
time it must be solved using preprocessor
When init is done, make a driver structure and copy send&receive
function into it.
>From now on one can use a common interface
Thats it and thanks for reading.
I will attach can_socketFD, my mostly complete implementation. Missing
start/stop, setMode and real testing....so it does have bugs
can_driver.h includes new structures and enums needed by the new API
So thats it and thanks for reading it all.
Do please comment on my suggestions and ask questions
Best Regards
Lars
/*
This file is part of CanFestival, a library implementing CanOpen Stack.
Copyright (C): Edouard TISSERANT and Francis DUPIN
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
*/
#ifndef __can_driver_h__
#define __can_driver_h__
struct struct_s_BOARD;
typedef struct struct_s_BOARD s_BOARD;
typedef struct struct_s_driverHandle CAN_HANDLE_Ex;
#include "applicfg.h"
#include "can.h"
/**
* @brief The CAN board configuration
* @ingroup can
*/
//struct struct_s_BOARD {
// char busname[100]; /**< The bus name on which the CAN board is connected */
// char baudrate[4]; /**< The board baudrate */
//};
struct struct_s_BOARD {
char * busname; /**< The bus name on which the CAN board is connected */
char * baudrate; /**< The board baudrate */
};
enum CanDriverExtentions
{
cfDRIVER_BAUDRATE_CHANGE = 0x0001,
cfDRIVER_BERR_REPORTING = 0x0002,
cfDRIVER_CAN_STATE = 0x0004,
cfDRIVER_FD = 0x0008,
cfDRIVER_FD_NON_ISO = 0x0010,
cfDRIVER_LOOPBACK = 0x0020,
cfDRIVER_LISTENONLY = 0x0040,
cfDRIVER_3_SAMPLES = 0x0080,
cfDRIVER_ONE_SHOT = 0x0100,
cfDRIVER_PRESUME_ACK = 0x0200,
cfDRIVER_FILTER = 0x0400,
cfDRIVER_ERROR_COUNTER = 0x0800,
};
enum cfCanDriverState
{
cfCAN_STATE_FAILED_READING,
cfCAN_STATE_ERROR_ACTIVE,
cfCAN_STATE_ERROR_WARNING,
cfCAN_STATE_ERROR_PASSIVE,
cfCAN_STATE_BUS_OFF,
cfCAN_STATE_STOPPED,
cfCAN_STATE_SLEEPING
};
enum BaudrateType
{
MAIN_BAUDRATE,
DATA_BAUDRATE
};
struct cfCAN_berr_counter {
int txerr;
int rxerr;
};
struct struct_s_driverHandle {
void* internalDriverHandle; /**< pointer to driver internal data */
long int ctrlMode; /**< Control mods the can device support */
UNS8 (*receive) (CAN_HANDLE_Ex* fd, Message * m);
UNS8 (*send) (CAN_HANDLE_Ex* fd, Message const* m);
UNS8 (*setBaudrate) (CAN_HANDLE_Ex*, enum BaudrateType, int);
UNS8 (*getState) (CAN_HANDLE_Ex*, enum cfCanDriverState*);
UNS8 (*getErrorCounters)(CAN_HANDLE_Ex*, struct cfCAN_berr_counter*);
UNS8 (*setMode) (CAN_HANDLE_Ex*, long, long);
};
#ifndef DLL_CALL
#if !defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
#define LIBAPI
#define DLL_CALL(funcname) funcname##_driver
#else
#define LIBAPI __stdcall
//Windows was missing the definition of the calling convention
#define DLL_CALL(funcname) LIBAPI funcname##_driver
#endif
#endif //DLL_CALL
#define LIBPUBLIC
#ifndef FCT_PTR_INIT
#define FCT_PTR_INIT
#endif
UNS8 DLL_CALL(canReceive)(CAN_HANDLE, Message *)FCT_PTR_INIT;
UNS8 DLL_CALL(canSend)(CAN_HANDLE, Message const *)FCT_PTR_INIT;
CAN_HANDLE DLL_CALL(canOpen)(s_BOARD *)FCT_PTR_INIT;
int DLL_CALL(canClose)(CAN_HANDLE)FCT_PTR_INIT;
UNS8 DLL_CALL(canChangeBaudRate)(CAN_HANDLE, char *)FCT_PTR_INIT;
#if defined DEBUG_MSG_CONSOLE_ON || defined NEED_PRINT_MESSAGE
#include "def.h"
#define _P(fc) case fc: MSG(#fc" ");break;
static inline void print_message(Message const *m)
{
int i;
UNS8 fc;
MSG("id:%02x ", m->cob_id & 0x7F);
fc = m->cob_id >> 7;
switch(fc)
{
case SYNC:
if(m->cob_id == 0x080)
MSG("SYNC ");
else
MSG("EMCY ");
break;
#ifdef CO_ENABLE_LSS
case LSS:
if(m->cob_id == 0x7E5)
MSG("MLSS ");
else
MSG("SLSS ");
break;
#endif
_P(TIME_STAMP)
_P(PDO1tx)
_P(PDO1rx)
_P(PDO2tx)
_P(PDO2rx)
_P(PDO3tx)
_P(PDO3rx)
_P(PDO4tx)
_P(PDO4rx)
_P(SDOtx)
_P(SDOrx)
_P(NODE_GUARD)
_P(NMT)
}
if( fc == SDOtx)
{
switch(m->data[0] >> 5)
{
/* scs: server command specifier */
_P(UPLOAD_SEGMENT_RESPONSE)
_P(DOWNLOAD_SEGMENT_RESPONSE)
_P(INITIATE_DOWNLOAD_RESPONSE)
_P(INITIATE_UPLOAD_RESPONSE)
_P(ABORT_TRANSFER_REQUEST)
}
}else if( fc == SDOrx)
{
switch(m->data[0] >> 5)
{
/* ccs: client command specifier */
_P(DOWNLOAD_SEGMENT_REQUEST)
_P(INITIATE_DOWNLOAD_REQUEST)
_P(INITIATE_UPLOAD_REQUEST)
_P(UPLOAD_SEGMENT_REQUEST)
_P(ABORT_TRANSFER_REQUEST)
}
}
MSG(" rtr:%d", m->rtr);
MSG(" len:%d", m->len);
for (i = 0 ; i < m->len ; i++)
MSG(" %02x", m->data[i]);
MSG("\n");
}
#endif
#endif
/*
This file is part of CanFestival, a library implementing CanOpen Stack.
Copyright (C): Edouard TISSERANT and Francis DUPIN
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
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h> /* for NULL */
#include <errno.h>
#include "can_netlink.h"
#include "config.h"
#include "libsocketcan.h"
#ifdef RTCAN_SOCKET
#include "rtdm/rtcan.h"
#define CAN_IFNAME "rtcan%s"
#define CAN_SOCKET rt_dev_socket
#define CAN_CLOSE rt_dev_close
#define CAN_RECV rt_dev_recv
#define CAN_SEND rt_dev_send
#define CAN_BIND rt_dev_bind
#define CAN_IOCTL rt_dev_ioctl
#define CAN_SETSOCKOPT rt_dev_setsockopt
#define CAN_ERRNO(err) (-err)
#else
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include "linux/can.h"
#include "linux/can/raw.h"
#include "net/if.h"
#ifndef PF_CAN
#define PF_CAN 29
#endif
#ifndef AF_CAN
#define AF_CAN PF_CAN
#endif
//#include "af_can.h"
#define CAN_IFNAME "can%s"
#define CAN_SOCKET socket
#define CAN_CLOSE close
#define CAN_RECV recv
#define CAN_SEND send
#define CAN_BIND bind
#define CAN_IOCTL ioctl
#define CAN_ERRNO(err) errno
#define CAN_SETSOCKOPT setsockopt
#endif
#ifdef ENABLE_CANFD
#define CAN_FRAME canfd_frame
#else
#define CAN_FRAME can_frame
#endif
#include "can_driver.h"
const int socketCanBinding[][2] = {
{CAN_CTRLMODE_LOOPBACK, cfDRIVER_LOOPBACK},
{CAN_CTRLMODE_LISTENONLY, cfDRIVER_LISTENONLY},
{CAN_CTRLMODE_3_SAMPLES, cfDRIVER_3_SAMPLES},
{CAN_CTRLMODE_ONE_SHOT, cfDRIVER_ONE_SHOT},
{CAN_CTRLMODE_BERR_REPORTING, cfDRIVER_BERR_REPORTING},
{CAN_CTRLMODE_FD, cfDRIVER_FD},
{CAN_CTRLMODE_PRESUME_ACK, cfDRIVER_PRESUME_ACK},
{CAN_CTRLMODE_FD_NON_ISO, cfDRIVER_FD_NON_ISO}
};
UNS32 SocketFD_probe(const char* deviceName);
struct socketFDInternal
{
char devName[20];
int socket;
};
/**
* /ingroup socketCan driver
* SocketFD_changeBaudRate Changes buadrate, main or data
*
* /param fd pointer to driver structure
* /param ds pointer to driverstate that will be filled with the driver state (on success)
*
* /return 0 on success
* /return 1 on failed
*/
UNS8 SocketFD_getState(CAN_HANDLE_Ex* fd,enum cfCanDriverState* ds)
{
struct socketFDInternal* iHandle = (struct socketFDInternal*)fd->internalDriverHandle;
int state;
if( can_get_state(iHandle->devName,&state) == 0)
{
switch(state)
{
case CAN_STATE_ERROR_ACTIVE:
*ds = cfCAN_STATE_ERROR_ACTIVE;
break;
case CAN_STATE_ERROR_WARNING:
*ds = cfCAN_STATE_ERROR_WARNING;
break;
case CAN_STATE_ERROR_PASSIVE:
*ds = cfCAN_STATE_ERROR_PASSIVE;
break;
case CAN_STATE_BUS_OFF:
*ds = cfCAN_STATE_BUS_OFF;
break;
case CAN_STATE_STOPPED:
*ds = cfCAN_STATE_STOPPED;
break;
case CAN_STATE_SLEEPING:
*ds = cfCAN_STATE_SLEEPING;
break;
}
return 0;
}
else
{
return 1;
}
}
/**
* /ingroup socketCan driver
* SocketFD_changeBaudRate Changes buadrate, main or data XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
*
* /param fd pointer to driver structure
*
* /return NULL on failed
* /return !=NULL on success
*/
UNS8 SocketFD_setMode(CAN_HANDLE_Ex* fd, long mask, long flag)
{
struct socketFDInternal* iHandle = (struct socketFDInternal*)fd->internalDriverHandle;
switch(mask)
{
case cfDRIVER_FD:
{
int canFD = 1;
int err = CAN_SETSOCKOPT(iHandle->socket, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &canFD, sizeof(canFD));
if (err)
{
fprintf(stderr, "dev_setsockopt: %s\n", strerror (CAN_ERRNO (err)));
return 1;
}
return 0;
}
break;
}
}
/**
* /ingroup socketCan driver
* SocketFD_berr_counter Reads out tx and rx counters
*
* /param fd pointer to driver structure
* /param bc pointer to error counter struct that will be filled with the error counters value (on success)
*
* /return 0 on success
* /return 1 on failed
*/
UNS8 SocketFD_berr_counter(CAN_HANDLE_Ex* fd, struct cfCAN_berr_counter* bc)
{
struct socketFDInternal* iHandle = (struct socketFDInternal*)fd->internalDriverHandle;
struct can_berr_counter errCount;
if( can_get_berr_counter(iHandle->devName,&errCount) == 0)
{
bc->txerr = errCount.txerr;
bc->rxerr = errCount.rxerr;
return 0;
}
else
{
return 1;
}
}
/**
* /ingroup socketCan driver
* SocketFD_receive Try to receive a can package
*
* /param fd pointer to driver structure
* /param m pointer to message struct that will be filled with the received message (on success)
*
* /return 0 on success
* /return 1 on failed
*/
UNS8 SocketFD_receive(CAN_HANDLE_Ex* fd, Message * m)
{
int res;
struct CAN_FRAME frame;
struct socketFDInternal* iHandle = (struct socketFDInternal*)fd->internalDriverHandle;
res = CAN_RECV (iHandle->socket, &frame, sizeof (frame), 0);
if (res < 0)
{
fprintf (stderr, "Recv failed: %s\n", strerror (CAN_ERRNO (res)));
return 1;
}
m->cob_id = frame.can_id & CAN_EFF_MASK;
m->len = frame.can_dlc;
m->rtr = (frame.can_id & CAN_RTR_FLAG) != 0;
memcpy (m->data, frame.data, sizeof(frame.data));
return 0;
}
/**
* /ingroup socketCan driver
* SocketFD_send Try to add a message to send queue
*
* /param fd pointer to driver structure
* /param m pointer to message struct that will be added to the send queue
*
* /return 0 on success
* /return 1 on failed
*/
UNS8 SocketFD_send(CAN_HANDLE_Ex* fd, Message const * m)
{
int res;
struct CAN_FRAME frame;
struct socketFDInternal* iHandle = (struct socketFDInternal*)fd->internalDriverHandle;
frame.can_id = m->cob_id;
if (frame.can_id >= 0x800)
frame.can_id |= CAN_EFF_FLAG;
frame.can_dlc = m->len;
if (m->rtr)
frame.can_id |= CAN_RTR_FLAG;
else
memcpy (frame.data, m->data, sizeof(m->data));
res = CAN_SEND (iHandle->socket, &frame, sizeof (frame), 0);
if (res < 0)
{
fprintf (stderr, "Send failed: %s\n", strerror (CAN_ERRNO (res)));
return 1;
}
return 0;
}
/**
* /ingroup socketCan driver
* SocketFD_changeBaudRate Changes baudrate, main or data
*
* /param fd pointer to driver structure
* /param brType What type of baudrate to change main or data
* /param int bit/sec
*
* MAIN_BAUDRATE can be a value between 1000(1kbit/s) and 1000000(1000kbit/s).
* DATA_BAUDRATE can be a value between 1000(1kbit/s) and ?????
*
* /return 0 on success
* /return 1 on failed
*/
UNS8 SocketFD_changeBaudRate(CAN_HANDLE_Ex* fd, enum BaudrateType brType, int baudrate)
{
struct socketFDInternal* iHandle = (struct socketFDInternal*)fd->internalDriverHandle;
if( brType == MAIN_BAUDRATE)
{
//libsocketcan function returns -1 on error
return can_set_bitrate(iHandle->devName,baudrate) != 0;
}
else
{
//libsocketcan function returns -1 on error
return can_set_data_bitrate(iHandle->devName,baudrate) != 0;
}
}
/**
* /ingroup socketCan driver
* canOpen_driverEx Creates a driver structure and returns it.
* Does not start up anything like "old" canfestival driver
*
* /param name full device name like "can0" or "can1"
*
* /return NULL on failed
* /return !=NULL on success
*/
CAN_HANDLE_Ex* canOpen_driverEx(const char* name)
{
struct ifreq ifr;
struct sockaddr_can addr;
int err;
if(!name)
{
return NULL;
}
CAN_HANDLE_Ex* fd = malloc (sizeof (struct struct_s_driverHandle));
memset(fd,0,sizeof(struct struct_s_driverHandle));
struct socketFDInternal* iHandle = malloc (sizeof (struct socketFDInternal));
fd->internalDriverHandle = iHandle;
iHandle->socket = CAN_SOCKET (PF_CAN, SOCK_RAW, CAN_RAW);
if (iHandle->socket < 0)
{
fprintf (stderr, "Socket creation failed: %s\n",
strerror (CAN_ERRNO (*(int *) fd0)));
goto error_ret;
}
strncpy (ifr.ifr_name, name, IFNAMSIZ);
err = CAN_IOCTL (iHandle->socket, SIOCGIFINDEX, &ifr);
if (err)
{
fprintf (stderr, "Getting IF index for %s failed: %s\n",
ifr.ifr_name, strerror (CAN_ERRNO (err)));
goto error_close;
}
{
int loopback = 1;
err = CAN_SETSOCKOPT(iHandle->socket, SOL_CAN_RAW, CAN_RAW_LOOPBACK,
&loopback, sizeof(loopback));
if (err)
{
fprintf(stderr, "rt_dev_setsockopt: %s\n", strerror (CAN_ERRNO (err)));
goto error_close;
}
}
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
err = CAN_BIND (iHandle->socket, (struct sockaddr *) &addr, sizeof (addr));
if (err)
{
fprintf (stderr, "Binding failed: %s\n", strerror (CAN_ERRNO (err)));
goto error_close;
}
fd->ctrlMode = SocketFD_probe(name);
fd->send = &SocketFD_send;
fd->receive = &SocketFD_receive;
fd->setBaudrate = &SocketFD_changeBaudRate;
fd->getState = &SocketFD_getState;
fd->setMode = &SocketFD_setMode;
if(fd->ctrlMode & cfDRIVER_ERROR_COUNTER)
{
fd->getErrorCounters = &SocketFD_berr_counter;
}
return fd;
error_close:
CAN_CLOSE (iHandle->socket);
error_ret:
free (iHandle);
free (fd);
return NULL;
}
/**
* /ingroup drivers
* SocketFD_close Closes the socket and frees up memory
*
* /param fd pointer to driver structure
*
* /return 0 if success
* /return -1 if failed (not in use)
*/
int SocketFD_close(CAN_HANDLE_Ex* fd)
{
struct socketFDInternal* iHandle = (struct socketFDInternal*)fd->internalDriverHandle;
if (iHandle->socket)
{
CAN_CLOSE (iHandle->socket);
free(fd);
free(iHandle);
return 0;
}
}
/**
* /ingroup drivers
* SocketFD_probe (driver internal) Probes the device for modes it supports
*
* /param fd pointer to driver structure
*
* /return 0 if success
* /return -1 if failed (not in use)
*/
UNS32 SocketFD_probe(const char* devName)
{
int result;
int i;
struct can_ctrlmode cm;
memset(&cm, 0, sizeof(cm));
int numModesToTest = (sizeof(socketCanBinding) / sizeof(socketCanBinding[0])) / 2;
for (int i = 0; i < numModesToTest; i++)
{
cm.mask = socketCanBinding[i][0];
cm.flags = socketCanBinding[i][0];
if(can_set_ctrlmode(devName,&cm) == 0)
{
result |= socketCanBinding[i][1];
}
}
return result;
}
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Canfestival-devel mailing list
Canfestival-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/canfestival-devel