/** * Helper functions for Linux epoll interface to CANopenNode. * * @file CO_epoll_interface.h * @ingroup CO_epoll_interface * @author Janez Paternoster * @author Martin Wagner * @copyright 2004 - 2020 Janez Paternoster * @copyright 2018 - 2020 Neuberger Gebaeudeautomation GmbH * * * This file is part of , a CANopen Stack. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the License. */ #ifndef CO_EPOLL_INTERFACE_H #define CO_EPOLL_INTERFACE_H #include "CANopen.h" #include #include #include #ifdef __cplusplus extern "C" { #endif /** * @defgroup CO_socketCAN socketCAN * Linux specific interface to CANopenNode. * * @{ * Linux includes CAN interface inside its kernel, so called SocketCAN. It operates as a network device. For more * information on Linux SocketCAN see https://www.kernel.org/doc/html/latest/networking/can.html * * Linux specific files for interfacing with Linux SocketCAN are located inside "CANopenNode/socketCAN" directory. * * CANopenNode runs as a set of non-blocking functions. It can run in single or multiple threads. Best approach for RT * IO device can be with two threads: * - timer based real-time thread for CAN receive, SYNC and PDO, see @ref CO_epoll_processRT() * - mainline thread for other processing, see @ref CO_epoll_processMain() * * Main references for Linux functions used here are Linux man pages and the book: The Linux Programming Interface by * Michael Kerrisk. * @} */ /** * @defgroup CO_epoll_interface Epoll interface * Linux epoll interface to CANopenNode. * * @ingroup CO_socketCAN * @{ * The Linux epoll API performs a monitoring multiple file descriptors to see if I/O is possible on any of them. * * CANopenNode uses epoll interface to provide an event based mechanism. Epoll waits for multiple different events, such * as: interval timer event, notification event, CAN receive event or socket based event for gateway. CANopenNode * non-blocking functions are processed after each event. * * CANopenNode itself offers functionality for calculation of time, when next interval timer event should trigger the * processing. It can also trigger notification events in case of multi-thread operation. */ /** * Object for epoll, timer and event API. */ typedef struct { int epoll_fd; /**< Epoll file descriptor */ int event_fd; /**< Notification event file descriptor */ int timer_fd; /**< Interval timer file descriptor */ uint32_t timerInterval_us; /**< Interval of the timer in microseconds, from @ref CO_epoll_create() */ uint32_t timeDifference_us; /**< Time difference since last @ref CO_epoll_wait() execution in microseconds */ uint32_t timerNext_us; /**< Timer value in microseconds, which can be changed by application and can shorten time of next @ref CO_epoll_wait() execution */ bool_t timerEvent; /**< True,if timer event is inside @ref CO_epoll_wait() */ uint64_t previousTime_us; /**< time value from the last process call in microseconds */ struct itimerspec tm; /**< Structure for timerfd */ struct epoll_event ev; /**< Structure for epoll_wait */ bool_t epoll_new; /**< true, if new epoll event is necessary to process */ } CO_epoll_t; /** * Create Linux epoll, timerfd and eventfd * * Create and configure multiple Linux notification facilities, which trigger execution of the task. Epoll blocks and * monitors multiple file descriptors, timerfd triggers in constant timer intervals and eventfd triggers on external * signal. * * @param ep This object * @param timerInterval_us Timer interval in microseconds * * @return @ref CO_ReturnError_t CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT or CO_ERROR_SYSCALL. */ CO_ReturnError_t CO_epoll_create(CO_epoll_t* ep, uint32_t timerInterval_us); /** * Close epoll, timerfd and eventfd * * @param ep This object */ void CO_epoll_close(CO_epoll_t* ep); /** * Wait for an epoll event * * This function blocks until event registered on epoll: timerfd, eventfd, or application specified event. Function also * calculates timeDifference_us since last call and prepares timerNext_us. * * @param ep This object */ void CO_epoll_wait(CO_epoll_t* ep); /** * Closing function for an epoll event * * This function must be called after @ref CO_epoll_wait(). Between them should be application specified processing * functions, which can check for own events and do own processing. Application may also lower timerNext_us variable. If * lowered, then interval timer will be reconfigured and @ref CO_epoll_wait() will be triggered earlier. * * @param ep This object */ void CO_epoll_processLast(CO_epoll_t* ep); /** * Initialization of functions in CANopen reset-communication section * * Configure callbacks for CANopen objects. * * @param ep This object * @param co CANopen object */ void CO_epoll_initCANopenMain(CO_epoll_t* ep, CO_t* co); /** * Process CANopen mainline functions * * This function calls @ref CO_process(). It is non-blocking and should execute cyclically. It should be between @ref * CO_epoll_wait() and @ref CO_epoll_processLast() functions. * * @param ep This object * @param co CANopen object * @param enableGateway If true, gateway to external world will be enabled. * @param [out] reset Return from @ref CO_process(). */ void CO_epoll_processMain(CO_epoll_t* ep, CO_t* co, bool_t enableGateway, CO_NMT_reset_cmd_t* reset); /** * Process CAN receive and realtime functions * * This function checks epoll for CAN receive event and processes CANopen realtime functions: @ref CO_process_SYNC(), * @ref CO_process_RPDO() and @ref CO_process_TPDO(). It is non-blocking and should execute cyclically. It should be * between @ref CO_epoll_wait() and @ref CO_epoll_processLast() functions. * * Function can be used in the mainline thread or in own realtime thread. * * Processing of CANopen realtime functions is protected with @ref CO_LOCK_OD. Also Node-Id must be configured and * CANmodule must be in CANnormal for processing. * * @param ep Pointer to @ref CO_epoll_t object. * @param co CANopen object * @param realtime Set to true, if function is called from the own realtime thread, and is executed at short constant * interval. */ void CO_epoll_processRT(CO_epoll_t* ep, CO_t* co, bool_t realtime); #if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII) || defined CO_DOXYGEN /** * Command interface type for gateway-ascii */ typedef enum { CO_COMMAND_IF_DISABLED = -100, CO_COMMAND_IF_STDIO = -2, CO_COMMAND_IF_LOCAL_SOCKET = -1, CO_COMMAND_IF_TCP_SOCKET_MIN = 0, CO_COMMAND_IF_TCP_SOCKET_MAX = 0xFFFF } CO_commandInterface_t; /** * Object for gateway */ typedef struct { int epoll_fd; /**< Epoll file descriptor, from @ref CO_epoll_createGtw() */ int32_t commandInterface; /**< Command interface type or tcp port number, see @ref CO_commandInterface_t */ uint32_t socketTimeout_us; /**< Socket timeout in microseconds */ uint32_t socketTimeoutTmr_us; /**< Socket timeout timer in microseconds */ char* localSocketPath; /**< Path in case of local socket */ int gtwa_fdSocket; /**< Gateway socket file descriptor */ int gtwa_fd; /**< Gateway io stream file descriptor */ bool_t freshCommand; /**< Indication of fresh command */ } CO_epoll_gtw_t; /** * Create socket for gateway-ascii command interface and add it to epoll * * Depending on arguments function configures stdio interface or local socket or IP socket. * * @param epGtw This object * @param epoll_fd Already configured epoll file descriptor * @param commandInterface Command interface type from CO_commandInterface_t * @param socketTimeout_ms Timeout for established socket connection in [ms] * @param localSocketPath File path, if commandInterface is local socket * * @return @ref CO_ReturnError_t CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT or CO_ERROR_SYSCALL. */ CO_ReturnError_t CO_epoll_createGtw(CO_epoll_gtw_t* epGtw, int epoll_fd, int32_t commandInterface, uint32_t socketTimeout_ms, char* localSocketPath); /** * Close gateway-ascii sockets * * @param epGtw This object */ void CO_epoll_closeGtw(CO_epoll_gtw_t* epGtw); /** * Initialization of gateway functions in CANopen reset-communication section * * @param epGtw This object * @param co CANopen object */ void CO_epoll_initCANopenGtw(CO_epoll_gtw_t* epGtw, CO_t* co); /** * Process CANopen gateway functions * * This function checks for epoll events and verifies socket connection timeout. It is non-blocking and should execute * cyclically. It should be between @ref CO_epoll_wait() and @ref CO_epoll_processLast() functions. * * @param epGtw This object * @param co CANopen object * @param ep Pointer to @ref CO_epoll_t object. */ void CO_epoll_processGtw(CO_epoll_gtw_t* epGtw, CO_t* co, CO_epoll_t* ep); #endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII */ /** @} */ #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* CO_EPOLL_INTERFACE_H */