1
0
Fork 0

socketCAN driver:

- Always enable CANptr and timestamp info in CO_CANrx_t.
- Update documentation
This commit is contained in:
Janez 2020-02-29 18:09:29 +01:00
parent f21cde9ab0
commit 9d17b0d671
11 changed files with 148 additions and 112 deletions

View file

@ -129,7 +129,7 @@ extern "C" {
#ifdef CO_DOXYGEN
/**
* @defgroup CO_NO_OBJ Number of CANopenNode communication objects.
* @defgroup CO_NO_OBJ Configuration
*
* Definitions specify, which and how many CANopenNode communication objects
* will be used in current configuration. Usage of some objects is mandatory and

View file

@ -2057,7 +2057,7 @@ PREDEFINED = CO_DOXYGEN \
CO_NO_SDO_CLIENT=1 \
CO_NO_NMT_MASTER=1 \
CO_NO_LSS_SERVER \
CO_NO_LSS_CLIENT \
CO_NO_LSS_CLIENT=1 \
CO_NO_TRACE=1
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this

View file

@ -37,6 +37,7 @@ Change Log
- NMT self start functionality (OD object 1F80) implemented to strictly folow standard. Default value for object 1F80 have to be updated in OD editor. See README.md.
### Fixed
- Various fixes.
- neuberger-socketCAN fixed.
### Added
- CANopen TIME protocol added.

View file

@ -111,9 +111,9 @@ void CANrx_threadTmr_close(void)
void CANrx_threadTmr_process(void)
{
int32_t result;
int32_t i;
uint64_t i;
bool_t syncWas;
unsigned long long missed;
uint64_t missed;
result = CO_CANrxWait(CO->CANmodule[0], threadRT.interval_fd, NULL);
if (result < 0) {

View file

@ -33,75 +33,90 @@
extern "C" {
#endif
/**
* @defgroup CO_socketCAN socketCAN
* @{
*
* Linux specific interface to CANopenNode
*
* CANopenNode runs in two threads: timer based realtime thread for CAN receive,
* SYNC and PDO and mainline thread for other processing.
* CANopenNode runs in two threads:
* - timer based real-time thread for CAN receive, SYNC and PDO, see
* CANrx_threadTmr_process()
* - mainline thread for other processing, see threadMain_process()
*
* The "threads" specified here do not fork threads themselves, but require
* that two threads are provided by the calling application.
* It uses the global CO object and has one thread-local struct for variables.
*/
/**
* Initialize mainline thread.
*
* threadMain is non-realtime thread for CANopenNode processing. It is nonblocking
* and should be called cyclically in 50 ms intervals or less if necessary. This
* is indicated by the callback function.
* This thread processes CO_process() function from CANopen.c file.
*
* @param callback this function is called to indicate #threadMain_process() has
* work to do
* @param object this pointer is given to _callback()_
*/
extern void threadMain_init(void (*callback)(void*), void *object);
/**
* Cleanup mainline thread.
*/
extern void threadMain_close(void);
/**
* Process mainline thread.
*
* Function must be called cyclically and after callback
* threadMain is non-realtime thread for CANopenNode processing. It is
* initialized by threadMain_init(). There is no configuration for CANopen
* objects. There is also no configuration for epool or interval timer or notify
* pipe. These must be specified externally.
*
* threadMain_process() calls CO_process() function for processing mainline
* CANopen objects. It is non-blocking and should be called cyclically in 50 ms
* intervals (typically). Function must also be called immediately after
* callback provided in threadMain_init() is called.
*
* @param reset return value from CO_process() function.
*/
extern void threadMain_process(CO_NMT_reset_cmd_t *reset);
/**
* Initialize realtime thread.
*
* CANrx_threadTmr is realtime thread for CANopenNode processing. It is nonblocking
* and must be executed at CAN message receive or periodically in 1ms (or something)
* intervals. Inside interval is processed CANopen SYNC message, RPDOs(inputs)
* and TPDOs(outputs).
* CANrx_threadTmr uses CAN socket from CO_driver.c
*
* @remark If realtime is required, this thread must be registred as such in the Linux
* kernel.
*
* @param interval_us Interval of periodic timer in microseconds, recommended
* value for realtime response: 1000 us
*/
extern void CANrx_threadTmr_init(uint32_t interval_us);
/**
* Terminate realtime thread.
*/
extern void CANrx_threadTmr_close(void);
/**
* Process realtime thread.
* Process real-time thread.
*
* This function must be called inside an infinite loop. It blocks until either
* some event happens or a timer runs out.
* CANrx_threadTmr is realtime thread for CANopenNode processing. It is
* initialized by CANrx_threadTmr_init(). There is no configuration for CANopen
* objects. But configuration for epool event notification facility is included
* in CO_CANmodule_init() from CO_driver.c. Epool is configured to monitor the
* following file descriptors: notify pipe, CANrx sockets from all interfaces
* and interval timer.
*
* CANrx_threadTmr_process() blocks on epoll_wait(). This is implemented inside
* CO_CANrxWait() from CO_driver.c. New CAN message is processed in
* CANrx_threadTmr_process() function, which calls CO_process_SYNC(),
* CO_process_RPDO() and CO_process_TPDO() functions for each expired timer
* interval. This function must be called inside an infinite loop.
*
* @remark If realtime is required, this thread must be registered as such in
* the Linux kernel.
*/
extern void CANrx_threadTmr_process();

View file

@ -2,7 +2,7 @@
* CANopen Object Dictionary storage object for Linux SocketCAN.
*
* @file CO_OD_storage.h
* @ingroup CO_socketCAN
* @ingroup CO_socketCAN_OD_storage
* @author Janez Paternoster
* @copyright 2015 - 2020 Janez Paternoster
*
@ -38,7 +38,7 @@ extern "C" {
#endif
/**
* @defgroup CO_OD_storage OD_storage
* @defgroup CO_socketCAN_OD_storage OD storage
* @ingroup CO_socketCAN
* @{
*

View file

@ -32,7 +32,9 @@
#include <linux/can/error.h>
#include <linux/net_tstamp.h>
#include <sys/socket.h>
#include <asm/socket.h>
#include <sys/epoll.h>
#include <time.h>
#include "301/CO_driver.h"
#include "CO_error.h"
@ -268,11 +270,9 @@ CO_ReturnError_t CO_CANmodule_init(
rxArray[i].mask = 0xFFFFFFFFU;
rxArray[i].object = NULL;
rxArray[i].CANrx_callback = NULL;
#ifdef CO_DRIVER_MULTI_INTERFACE
rxArray[i].CANptr = NULL;
rxArray[i].timestamp.tv_sec = 0;
rxArray[i].timestamp.tv_nsec = 0;
#endif
}
#ifndef CO_DRIVER_MULTI_INTERFACE
@ -344,7 +344,7 @@ CO_ReturnError_t CO_CANmodule_addInterface(
log_printf(LOG_DEBUG, DBG_ERRNO, "setsockopt(ovfl)");
return CO_ERROR_SYSCALL;
}
#ifdef CO_DRIVER_MULTI_INTERFACE
/* enable software time stamp mode (hardware timestamps do not work properly
* on all devices)*/
tmp = (SOF_TIMESTAMPING_SOFTWARE |
@ -354,7 +354,6 @@ CO_ReturnError_t CO_CANmodule_addInterface(
log_printf(LOG_DEBUG, DBG_ERRNO, "setsockopt(timestamping)");
return CO_ERROR_SYSCALL;
}
#endif
//todo - modify rx buffer size? first one needs root
//ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, (void *)&bytes, sLen);
@ -501,11 +500,9 @@ CO_ReturnError_t CO_CANrxBufferInit(
/* Configure object variables */
buffer->object = object;
buffer->CANrx_callback = CANrx_callback;
#ifdef CO_DRIVER_MULTI_INTERFACE
buffer->CANptr = NULL;
buffer->timestamp.tv_nsec = 0;
buffer->timestamp.tv_sec = 0;
#endif
/* CAN identifier and CAN mask, bit aligned with CAN module */
buffer->ident = ident & CAN_SFF_MASK;
@ -626,7 +623,7 @@ CO_ReturnError_t CO_CANtxBuffer_setInterface(
#endif /* CO_DRIVER_MULTI_INTERFACE */
/******************************************************************************/
/* send CAN message ***********************************************************/
static CO_ReturnError_t CO_CANCheckSendInterface(
CO_CANmodule_t *CANmodule,
CO_CANtx_t *buffer,
@ -773,12 +770,12 @@ void CO_CANverifyErrors(CO_CANmodule_t *CANmodule)
}
/******************************************************************************/
/* Read CAN message from socket and verify some errors ************************/
static CO_ReturnError_t CO_CANread(
CO_CANmodule_t *CANmodule,
CO_CANinterface_t *interface,
struct can_frame *msg,
struct timespec *timestamp)
struct can_frame *msg, /* CAN message, return value */
struct timespec *timestamp) /* timestamp of CAN message, return value */
{
int32_t n;
uint32_t dropped;
@ -838,10 +835,11 @@ static CO_ReturnError_t CO_CANread(
}
static int32_t CO_CANrxMsg(
/* find msg inside rxArray and call corresponding CANrx_callback **************/
static int32_t CO_CANrxMsg( /* return index of received message in rxArray or -1 */
CO_CANmodule_t *CANmodule,
struct can_frame *msg,
CO_CANrxMsg_t *buffer)
struct can_frame *msg, /* CAN message input */
CO_CANrxMsg_t *buffer) /* If not NULL, msg will be copied to buffer */
{
int32_t retval;
const CO_CANrxMsg_t *rcvMsg; /* pointer to received message in CAN module */
@ -888,9 +886,7 @@ int32_t CO_CANrxWait(CO_CANmodule_t *CANmodule, int fdTimer, CO_CANrxMsg_t *buff
{
int32_t retval;
int32_t ret;
#ifdef CO_DRIVER_MULTI_INTERFACE
const void *CANptr;
#endif
CO_ReturnError_t err;
CO_CANinterface_t *interface = NULL;
struct epoll_event ev[1];
@ -938,7 +934,7 @@ int32_t CO_CANrxWait(CO_CANmodule_t *CANmodule, int fdTimer, CO_CANrxMsg_t *buff
/* one of the sockets is ready */
if ((ev[0].data.fd == CO_NotifyPipeGetFd(CANmodule->pipe)) ||
(ev[0].data.fd == fdTimer)) {
/* timer/pipe socket */
/* timer or notify pipe */
return -1;
}
else {
@ -949,10 +945,8 @@ int32_t CO_CANrxWait(CO_CANmodule_t *CANmodule, int fdTimer, CO_CANrxMsg_t *buff
interface = &CANmodule->CANinterfaces[i];
if (ev[0].data.fd == interface->fd) {
#ifdef CO_DRIVER_MULTI_INTERFACE
/* get interface handle */
CANdriverState = interface->CANptr;
#endif
CANptr = interface->CANptr;
/* get message */
err = CO_CANread(CANmodule, interface, &msg, &timestamp);
if (err != CO_ERROR_NO) {
@ -983,16 +977,15 @@ int32_t CO_CANrxWait(CO_CANmodule_t *CANmodule, int fdTimer, CO_CANrxMsg_t *buff
int32_t msgIndex;
#ifdef CO_DRIVER_ERROR_REPORTING
/* clear listenOnly and noackCounter if necessary */
CO_CANerror_rxMsg(&interface->errorhandler);
#endif
msgIndex = CO_CANrxMsg(CANmodule, &msg, buffer);
if (msgIndex > -1) {
#ifdef CO_DRIVER_MULTI_INTERFACE
/* Store message info */
CANmodule->rxArray[msgIndex].timestamp = timestamp;
CANmodule->rxArray[msgIndex].CANptr = CANptr;
#endif
}
retval = msgIndex;
}

View file

@ -1,7 +1,8 @@
/*
/**
* Linux socketCAN specific definitions for CANopenNode.
*
* @file CO_driver_target.h
* @ingroup CO_socketCAN_driver_target
* @author Janez Paternoster
* @author Martin Wagner
* @copyright 2004 - 2020 Janez Paternoster
@ -25,15 +26,41 @@
*/
/* This file contains device and application specific definitions.
* It is included from CO_driver.h, which contains documentation
* for common definitions below. */
#ifndef CO_DRIVER_TARGET
#define CO_DRIVER_TARGET
/* This file contains device and application specific definitions.
* It is included from CO_driver.h, which contains documentation
* for definitions below. */
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <endian.h>
#include <pthread.h>
#include <linux/can.h>
#include <net/if.h>
/*
* @name multi interface support
#if __has_include("CO_driver_custom.h")
#include "CO_driver_custom.h"
#endif
#include "CO_notify_pipe.h"
#include "CO_error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup CO_socketCAN_driver_target CO_driver_target.h
* @ingroup CO_socketCAN
* @{
*
* Linux socketCAN specific @ref CO_driver definitions for CANopenNode.
*/
/**
* Multi interface support
*
* Enable this to use interface combining at driver level. This
* adds functions to broadcast/selective transmit messages on the
@ -51,40 +78,32 @@
* This is not intended to realize interface redundancy!!!
*/
/* #define CO_DRIVER_MULTI_INTERFACE */
#ifdef CO_DOXYGEN
#define CO_DRIVER_MULTI_INTERFACE
#endif
/*
* @name CAN bus error reporting
/**
* CAN bus error reporting
*
* CO_DRIVER_ERROR_REPORTING enabled adds support for socketCAN error detection
* and handling functions inside the driver. This is needed when you have
* CANopen with "0" connected nodes as a use case, as this is normally
* forbidden in CAN.
*
* you need to enable error reporting in your kernel driver using
* "ip link set canX type can berr-reporting on". Of course, the kernel
* driver for your hardware needs this functionality to be implemented...
* you need to enable error reporting in your kernel driver using:
* @code{.sh}
* ip link set canX type can berr-reporting on
* @endcode
* Of course, the kernel driver for your hardware needs this functionality to be
* implemented...
*/
#ifndef CO_DRIVER_ERROR_REPORTING_DISABLE
/* #define CO_DRIVER_ERROR_REPORTING */
#ifdef CO_DOXYGEN
#define CO_DRIVER_ERROR_REPORTING
#endif
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/time.h>
#include <endian.h>
#include <pthread.h>
#include <linux/can.h>
#include <net/if.h>
#include "CO_notify_pipe.h"
#ifdef CO_DRIVER_ERROR_REPORTING
#include "CO_error.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* skip this section for Doxygen, because it is documented in CO_driver.h */
#ifndef CO_DOXYGEN
/* Basic definitions */
#ifdef __BYTE_ORDER
@ -135,11 +154,9 @@ typedef struct {
uint32_t mask;
void *object;
void (*CANrx_callback)(void *object, void *message);
#ifdef CO_DRIVER_MULTI_INTERFACE
/* info about last received message */
const void *CANptr; /* CAN Interface identifier */
struct timespec timestamp; /* time of reception */
#endif
const void *CANptr; /* CAN Interface identifier from last
message */
struct timespec timestamp; /* time of reception of last message */
} CO_CANrx_t;
/* Transmit message object as aligned in socketCAN. */
@ -182,7 +199,8 @@ typedef struct {
volatile bool_t CANnormal;
void *em;
CO_NotifyPipe_t *pipe; /* Notification Pipe */
int fdEpoll; /* epoll FD */
int fdEpoll; /* epoll FD for pipe, CANrx sockets in all
interfaces and fdTimerRead */
int fdTimerRead; /* timer handle from CANrxWait() */
#ifdef CO_DRIVER_MULTI_INTERFACE
/* Lookup tables Cob ID to rx/tx array index.
@ -221,9 +239,11 @@ static inline void CO_UNLOCK_OD() {
#define CO_FLAG_SET(rxNew) {CO_MemoryBarrier(); rxNew = (void*)1L;}
#define CO_FLAG_CLEAR(rxNew) {CO_MemoryBarrier(); rxNew = NULL;}
#endif /* CO_DOXYGEN */
#ifdef CO_DRIVER_MULTI_INTERFACE
/*
/**
* Add socketCAN interface to can driver
*
* Function must be called after CO_CANmodule_init.
@ -236,7 +256,7 @@ static inline void CO_UNLOCK_OD() {
CO_ReturnError_t CO_CANmodule_addInterface(CO_CANmodule_t *CANmodule,
const void *CANptr);
/*
/**
* Check on which interface the last message for one message buffer was received
*
* It is in the responsibility of the user to check that this information is
@ -256,7 +276,7 @@ bool_t CO_CANrxBuffer_getInterface(CO_CANmodule_t *CANmodule,
const void **const CANptrRx,
struct timespec *timestamp);
/*
/**
* Set which interface should be used for message buffer transmission
*
* It is in the responsibility of the user to ensure that the correct interface
@ -277,26 +297,33 @@ CO_ReturnError_t CO_CANtxBuffer_setInterface(CO_CANmodule_t *CANmodule,
#endif /* CO_DRIVER_MULTI_INTERFACE */
/*
* Functions receives CAN messages. It is blocking.
/**
* Functions receives CAN messages (blocking)
*
* This function can be used in two ways
* - automatic mode (call callback that is set by #CO_CANrxBufferInit() function)
* - manual mode (evaluate message filters, return received message)
* This function waits for received CAN message, CAN error frame, notification
* pipe or fdTimer expiration. In case of CAN message it searches _rxArray_ from
* CO_CANmodule_t and if matched it calls the corresponding CANrx_callback,
* optionally copies received CAN message to _buffer_ and returns index of
* matched _rxArray_.
*
* Both modes can be combined.
* This function can be used in two ways, which can be combined:
* - automatic mode: If CANrx_callback is specified for matched _rxArray_, then
* calls its callback.
* - manual mode: evaluate message filters, return received message
*
* @param CANmodule This object.
* @param fdTimer file descriptor with activated timeout. fd is not read after
* expiring! -1 if not used.
* @param buffer [out] storage for received message or _NULL_
* @retval >= 0 index of received message in array set by #CO_CANmodule_init()
* _rxArray_, copy available in _buffer_
* @retval -1 no message received
* @param fdTimer File descriptor with activated timeout. If set to -1, then
* timer will not be used. File descriptor must be read
* externally if retval == -1! Read must be nonblocking and
* provides number of timer expirations since last read.
* @param [out] buffer Storage for received message or _NULL_ if not used.
* @retval >= 0 index of received message in array from CO_CANmodule_t
* _rxArray_, copy of CAN message is available in _buffer_.
* @retval -1 no message received (timer expired or notification pipe or error)
*/
int32_t CO_CANrxWait(CO_CANmodule_t *CANmodule,
int fdTimer,
CO_CANrxMsg_t *buffer);
int32_t CO_CANrxWait(CO_CANmodule_t* CANmodule, int fdTimer, CO_CANrxMsg_t* buffer);
/** @} */
#ifdef __cplusplus
}

View file

@ -26,6 +26,7 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <linux/can/error.h>
#include "301/CO_driver.h"

View file

@ -2,7 +2,7 @@
* CANopenNode Linux socketCAN Error handling.
*
* @file CO_error.h
* @ingroup CO_socketCAN
* @ingroup CO_socketCAN_ERROR
* @author Martin Wagner
* @copyright 2018 - 2020 Neuberger Gebaeudeautomation GmbH
*
@ -41,11 +41,11 @@ extern "C" {
#endif
/**
* @defgroup CO_socketCAN_ERROR socketCAN_ERROR
* @defgroup CO_socketCAN_ERROR CAN errors & Log
* @ingroup CO_socketCAN
* @{
*
* CANopen Errors and logging of messages
* CANopen Errors and System message log
*/
/**
@ -108,9 +108,7 @@ typedef enum {
typedef struct {
int fd; /**< interface FD */
char ifName[IFNAMSIZ]; /**< interface name as string */
uint32_t noackCounter;
uint32_t noackCounter; /**< counts no ACK on CAN transmission */
volatile unsigned char listenOnly; /**< set to listen only mode */
struct timespec timestamp; /**< listen only mode started at this time */
} CO_CANinterfaceErrorhandler_t;
@ -143,7 +141,8 @@ void CO_CANerror_disable(
/**
* Message received event
*
* when a message is received at least one other CAN module is connected
* When a message is received at least one other CAN module is connected.
* Function clears listenOnly and noackCounter error flags.
*
* @param CANerrorhandler CAN error object.
*/
@ -154,7 +153,7 @@ void CO_CANerror_rxMsg(
/**
* Check if interface is ready for message transmission
*
* message musn't be transmitted if not ready
* Message mustn't be transmitted if not ready.
*
* @param CANerrorhandler CAN error object.
* @return CO_INTERFACE_ACTIVE message transmission ready
@ -166,7 +165,7 @@ CO_CANinterfaceState_t CO_CANerror_txMsg(
/**
* Error message received event
*
* this handles all received error messages
* This handles all received error messages.
*
* @param CANerrorhandler CAN error object.
* @param msg received error message

View file

@ -2,7 +2,7 @@
* Notify pipe for Linux threads.
*
* @file CO_notify_pipe.h
* @ingroup CO_socketCAN
* @ingroup CO_socketCAN_notify_pipe
* @author Martin Wagner
* @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH
*
@ -32,7 +32,7 @@ extern "C" {
#endif
/**
* @defgroup CO_pipe Pipe
* @defgroup CO_socketCAN_notify_pipe Notify pipe
* @ingroup CO_socketCAN
* @{
*