From 9d17b0d67114f89987d8ea4cd3a9c643bbd44903 Mon Sep 17 00:00:00 2001 From: Janez Date: Sat, 29 Feb 2020 18:09:29 +0100 Subject: [PATCH] socketCAN driver: - Always enable CANptr and timestamp info in CO_CANrx_t. - Update documentation --- CANopen.h | 2 +- Doxyfile | 2 +- doc/CHANGELOG.md | 1 + socketCAN/CO_Linux_threads.c | 4 +- socketCAN/CO_Linux_threads.h | 57 +++++++++------ socketCAN/CO_OD_storage.h | 4 +- socketCAN/CO_driver.c | 35 ++++----- socketCAN/CO_driver_target.h | 133 +++++++++++++++++++++-------------- socketCAN/CO_error.c | 1 + socketCAN/CO_error.h | 17 +++-- socketCAN/CO_notify_pipe.h | 4 +- 11 files changed, 148 insertions(+), 112 deletions(-) diff --git a/CANopen.h b/CANopen.h index 67b4335..11db15f 100644 --- a/CANopen.h +++ b/CANopen.h @@ -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 diff --git a/Doxyfile b/Doxyfile index 458bef7..37c12f2 100644 --- a/Doxyfile +++ b/Doxyfile @@ -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 diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index b437ab0..9a68822 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -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. diff --git a/socketCAN/CO_Linux_threads.c b/socketCAN/CO_Linux_threads.c index d6b84e0..8051a34 100644 --- a/socketCAN/CO_Linux_threads.c +++ b/socketCAN/CO_Linux_threads.c @@ -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) { diff --git a/socketCAN/CO_Linux_threads.h b/socketCAN/CO_Linux_threads.h index 7569527..f903c2c 100644 --- a/socketCAN/CO_Linux_threads.h +++ b/socketCAN/CO_Linux_threads.h @@ -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(); diff --git a/socketCAN/CO_OD_storage.h b/socketCAN/CO_OD_storage.h index 2d2c525..24b4f2a 100644 --- a/socketCAN/CO_OD_storage.h +++ b/socketCAN/CO_OD_storage.h @@ -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 * @{ * diff --git a/socketCAN/CO_driver.c b/socketCAN/CO_driver.c index f4b5b5f..451e009 100644 --- a/socketCAN/CO_driver.c +++ b/socketCAN/CO_driver.c @@ -32,7 +32,9 @@ #include #include #include +#include #include +#include #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, ×tamp); 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; } diff --git a/socketCAN/CO_driver_target.h b/socketCAN/CO_driver_target.h index 1141525..a1782b6 100644 --- a/socketCAN/CO_driver_target.h +++ b/socketCAN/CO_driver_target.h @@ -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 +#include +#include +#include +#include +#include +#include -/* - * @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 -#include -#include -#include -#include -#include -#include -#include - -#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 } diff --git a/socketCAN/CO_error.c b/socketCAN/CO_error.c index a7db938..3faf65c 100644 --- a/socketCAN/CO_error.c +++ b/socketCAN/CO_error.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "301/CO_driver.h" diff --git a/socketCAN/CO_error.h b/socketCAN/CO_error.h index 9a0b56a..3594ff7 100644 --- a/socketCAN/CO_error.h +++ b/socketCAN/CO_error.h @@ -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 diff --git a/socketCAN/CO_notify_pipe.h b/socketCAN/CO_notify_pipe.h index cf8dcb6..d1002d3 100644 --- a/socketCAN/CO_notify_pipe.h +++ b/socketCAN/CO_notify_pipe.h @@ -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 * @{ *