├── .clang_complete ├── .gitignore ├── COPYING ├── MANIFEST ├── Makefile ├── Makefile.opensource ├── README.md ├── bin └── canopen-ls ├── build.sh ├── canopen2.xml ├── etc └── canopen.cfg ├── example-driver.c ├── inc ├── CanIOHandlerInterface.h ├── CanMasterInterface.h ├── Driver.h ├── DriverManager.h ├── arc.h ├── can-tcp.h ├── canopen-driver.h ├── canopen.h ├── canopen │ ├── byteorder.h │ ├── cia302-error-codes.h │ ├── cia402-error-codes.h │ ├── dump.h │ ├── eds.h │ ├── emcy.h │ ├── error.h │ ├── heartbeat.h │ ├── master.h │ ├── network.h │ ├── nmt.h │ ├── sdo-dict.h │ ├── sdo.h │ ├── sdo_async.h │ ├── sdo_req.h │ ├── sdo_req_enums.h │ ├── sdo_srv.h │ ├── sdo_sync.h │ └── types.h ├── canopen_info.h ├── cfg.h ├── co_atomic.h ├── compat │ ├── atomic_compat.h │ ├── mloop.h │ ├── plog.h │ ├── prioq.h │ └── thread-utils.h ├── conversions.h ├── fff.h ├── http.h ├── ini_parser.h ├── legacy-driver.h ├── net-util.h ├── profiling.h ├── rest.h ├── sdo-rest.h ├── sock.h ├── socketcan.h ├── stream.h ├── string-utils.h ├── sys │ ├── queue.h │ └── tree.h ├── time-utils.h ├── trace-buffer.h ├── tst.h ├── type-macros.h ├── userdata.h ├── vector.h └── vnode.h ├── packaging ├── src ├── Driver.cpp ├── DriverManager.cpp ├── byteorder.c ├── can-tcp.c ├── canbridge.c ├── canopen-dump.c ├── canopen-master.c ├── canopen-vnode.c ├── canopen.c ├── canopen_info.c ├── cfg.c ├── conversions.c ├── driver.c ├── dump.c ├── eds.c ├── error.c ├── hexdump.c ├── http.c ├── ini_parser.c ├── legacy-driver.cpp ├── master-main.c ├── master.c ├── mloop.c ├── net-util.c ├── network.c ├── prioq.c ├── profiling.c ├── rest.c ├── sdo-dict.c ├── sdo-rest.c ├── sdo_async.c ├── sdo_common.c ├── sdo_req.c ├── sdo_srv.c ├── sdo_sync.c ├── sock.c ├── socketcan.c ├── stream.c ├── string-utils.c ├── strlcpy.c ├── trace-buffer.c ├── types.c ├── userdata.c └── vnode.c ├── target.postinst ├── target.prerm ├── test ├── sdo_async_fuzz_test.c ├── unit_arc.c ├── unit_cfg.c ├── unit_conversions.c ├── unit_error.c ├── unit_http.c ├── unit_init_parser.c ├── unit_network.c ├── unit_rest.c ├── unit_sdo-dict.c ├── unit_sdo_async.c ├── unit_sdo_req.c ├── unit_string-utils.c ├── unit_trace-buffer.c ├── unit_types.c └── unit_vector.c ├── valgrind.supp └── vnodes ├── mcs816.ini ├── mcv14.ini └── mws2.ini /.clang_complete: -------------------------------------------------------------------------------- 1 | -Iinc/ -I/usr/include/lua5.1 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.so* 2 | *.o 3 | *.swp 4 | tst/test_* 5 | tst/fuzz_test_* 6 | fakenode 7 | mcv14.lua 8 | build-* 9 | *.deb 10 | .sconsign.dblite 11 | *_version.c 12 | lib.map 13 | debian_* 14 | *.doxy 15 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2016, Marel 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose 4 | with or without fee is hereby granted, provided that the above copyright notice 5 | and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 9 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 11 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 12 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 13 | THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | This document contains short descriptions for most source and header files in 2 | the project. The ones that have been left out either aren't used or have an 3 | apropriately named source/header counter-part that has already been described. 4 | 5 | src: 6 | byteorder.c Utilities for converting between host and network byte 7 | order. 8 | canbridge.c A small program that forwards traffic between CAN 9 | interfaces over TCP. 10 | canopen.c Functions to classify CANopen frames based on COB-IDs 11 | canopen-dump.c A small program that interprets CANopen messages on the 12 | bus as simple text messages. 13 | canopen_info.c Shared memory map with node information. 14 | canopen-vnode.c Main function for vnode.c. 15 | can-tcp.c Implementation of canbridge. 16 | conversions.c Functions to convert object dictionary entries to/from 17 | strings. 18 | driver.c New driver API. 19 | Driver.cpp Old CANopen master driver code. 20 | DriverManager.cpp Same as above. 21 | dump.c Implementation of canopen-dump. 22 | eds.c Contains functions to read EDS files and access the data 23 | quickly after it has been loaded. 24 | hexdump.c A simple hexdumper. 25 | http.c HTTP request parser. 26 | ini_parser.c INI file parser. 27 | legacy-driver.c A C wrapper around the old C++ driver code. 28 | master.c The master program. 29 | master-main.c The main function for the master program. 30 | network.c Utility functions for networking. 31 | profiling.c Instrumentation for profiling execution time. 32 | rest.c REST service. 33 | sdo_async.c SDO client code. An sdo_async module is a machine that 34 | eats CAN frames and spits out fully formed messages. 35 | sdo_common.c Common SDO client/server utility functions. 36 | sdo-dict.c Map between indices/subindices, types and dictionary entry 37 | names. 38 | sdo_req.c Request-reply abstraction on top of sdo_async. 39 | sdo-rest.c SDO REST service (mostly for configuring Lenze Inverters). 40 | sdo_sync.c Synchronous (blocking) SDO functions. 41 | sdo_srv.c SDO server code. Used in vnode. 42 | sock.c A layer to make the rest of the code socket type agnostic. 43 | Can be a socketcan socket or a TCP socket. 44 | socketcan.c SocketCAN utilites. 45 | stream.c A blocking stdio stream class. 46 | string-utils.c String manipulation utilities. 47 | strlcpy.c BSD's strlcpy() (contrib). 48 | types.c Utilities and definitions that identify and describe 49 | CANopen object dictionary types. 50 | vnode.c Virtual CANopen nodes. This is used for testing and 51 | profiling. 52 | 53 | inc: 54 | arc.h Atomic reference counting macros. 55 | CanIOHandlerInterface.h Old CANopen master driver code. 56 | CanMasterInterface.h Same as above. 57 | Driver.h Same as above. 58 | DriverManager.h Same as above. 59 | canopen.h Description of CANopen message types. 60 | co_atomic.h Compatibility layer for atomic operations. 61 | fff.h Fake function framework (contrib). 62 | string-utils.h String manipulation utilities. 63 | time-utils.h Common time conversion utilities. 64 | tst.h Minimal unit-testing framework. 65 | vector.h Dynamic buffers. 66 | type-macros.h Contains useful macros such as container_of(). 67 | 68 | inc/canopen: 69 | emcy.h EMCY message utility functions. 70 | heartbeat.h Heartbeat message utility functions. 71 | master.h Shared data in the main program. 72 | nmt.h NMT message utility functions. 73 | sdo.h SDO message utility functions. 74 | types.h Description of CANopen object dictionary types. 75 | 76 | inc/sys: 77 | queue.h BSD linked lists. 78 | tree.h BSD red-black and splay trees. 79 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PRJ_NAME := canopen2 2 | PRJ_TYPE := SOLIB 3 | 4 | ADD_CFLAGS := -std=gnu99 -std=gnu++0x -D_GNU_SOURCE -Wextra -fexceptions \ 5 | -fvisibility=hidden -pthread 6 | 7 | ADD_LIBS := mloop appbase dl sharedmalloc plog plutopst digitaliopin 8 | ADD_LFLAGS := -pthread -Wl,-rpath=/usr/lib/mloop 9 | 10 | #ifeq ($(shell marel_getcompilerprefix powerpc),powerpc-marel-linux-gnu) 11 | # ADD_CFLAGS += -flto 12 | # ADD_LFLAGS += -flto 13 | # TEST_LDFLAGS += -flto=n 14 | #endif 15 | 16 | MAIN_SRC := \ 17 | canopen-master.c \ 18 | canbridge.c \ 19 | canopen-dump.c \ 20 | canopen-vnode.c 21 | 22 | SRC := \ 23 | master.c \ 24 | sdo_common.c \ 25 | sdo_req.c \ 26 | byteorder.c \ 27 | network.c \ 28 | canopen.c \ 29 | sdo_async.c \ 30 | socketcan.c \ 31 | legacy-driver.cpp \ 32 | DriverManager.cpp \ 33 | Driver.cpp \ 34 | rest.c \ 35 | http.c \ 36 | eds.c \ 37 | ini_parser.c \ 38 | types.c \ 39 | sdo-rest.c \ 40 | conversions.c \ 41 | strlcpy.c \ 42 | canopen_info.c \ 43 | profiling.c \ 44 | sdo_sync.c \ 45 | sdo_srv.c \ 46 | driver.c \ 47 | net-util.c \ 48 | sock.c \ 49 | stream.c \ 50 | dump.c \ 51 | vnode.c \ 52 | sdo-dict.c \ 53 | hexdump.c \ 54 | string-utils.c \ 55 | can-tcp.c \ 56 | cfg.c \ 57 | error.c \ 58 | trace-buffer.c \ 59 | userdata.c \ 60 | 61 | TEST_SRC := \ 62 | unit_arc.c \ 63 | unit_conversions.c \ 64 | unit_http.c \ 65 | unit_init_parser.c \ 66 | unit_network.c \ 67 | unit_sdo_req.c \ 68 | unit_string-utils.c \ 69 | unit_vector.c \ 70 | unit_sdo_async.c \ 71 | unit_rest.c \ 72 | unit_types.c \ 73 | unit_sdo-dict.c \ 74 | sdo_async_fuzz_test.c \ 75 | unit_cfg.c \ 76 | unit_error.c \ 77 | unit_trace-buffer.c \ 78 | 79 | include $(MDEV)/make/make.main 80 | 81 | # vi: noet sw=8 ts=8 tw=80 82 | 83 | -------------------------------------------------------------------------------- /Makefile.opensource: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014-2018, Marel 2 | # 3 | # Permission to use, copy, modify, and/or distribute this software for any 4 | # purpose with or without fee is hereby granted, provided that the above 5 | # copyright notice and this permission notice appear in all copies. 6 | # 7 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | # PERFORMANCE OF THIS SOFTWARE. 14 | 15 | CC ?= gcc 16 | LD ?= ld 17 | DEBUG ?= 0 18 | BUILDDIR ?= build 19 | PREFIX ?= /usr/local 20 | DESTDIR ?= 21 | EDS_PATH ?= /var/canopen/eds 22 | DRIVER_PATH ?= /usr/lib/canopen 23 | 24 | COMMON_CFLAGS = -std=gnu99 -D_GNU_SOURCE -Iinc/ -Iinc/compat -Wextra \ 25 | -fvisibility=hidden -pthread -fPIC -DNO_MAREL_CODE \ 26 | -DEDS_PATH=\"$(EDS_PATH)\" -DDRIVER_PATH=\"$(DRIVER_PATH)\" 27 | RELEASE_CFLAGS = -O2 -DNDEBUG -flto 28 | DEBUG_CFLAGS = -O0 -g 29 | CFLAGS += $(COMMON_CFLAGS) 30 | LDFLAGS += -ldl -lrt -pthread -flto 31 | 32 | BIN_LDFLAGS = -L$(BUILDDIR)/lib -Wl,--rpath=$(BUILDDIR)/lib -lcanopen2 -pthread 33 | 34 | ifneq ($(DEBUG),0) 35 | CFLAGS += $(DEBUG_CFLAGS) 36 | else 37 | CFLAGS += $(RELEASE_CFLAGS) 38 | endif 39 | 40 | LIBDEPS = \ 41 | master \ 42 | sdo_common \ 43 | sdo_req \ 44 | byteorder \ 45 | network \ 46 | canopen \ 47 | sdo_async \ 48 | socketcan \ 49 | rest \ 50 | http \ 51 | eds \ 52 | ini_parser \ 53 | types \ 54 | sdo-rest \ 55 | conversions \ 56 | strlcpy \ 57 | profiling \ 58 | sdo_sync \ 59 | sdo_srv \ 60 | driver \ 61 | net-util \ 62 | sock \ 63 | stream \ 64 | dump \ 65 | vnode \ 66 | sdo-dict \ 67 | hexdump \ 68 | string-utils \ 69 | can-tcp \ 70 | mloop \ 71 | prioq \ 72 | cfg \ 73 | error \ 74 | trace-buffer \ 75 | 76 | LIBOBJS = $(foreach dep,$(LIBDEPS),$(BUILDDIR)/obj/$(dep).o) 77 | 78 | BINS = \ 79 | canopen-master \ 80 | canbridge \ 81 | canopen-dump \ 82 | canopen-vnode \ 83 | 84 | LIBBUILD = $(BUILDDIR)/lib/libcanopen2.so 85 | BINBUILDS = $(foreach bin,$(BINS),$(BUILDDIR)/bin/$(bin)) 86 | 87 | INSTALLDEPS = $(LIBBUILD) $(BINBUILDS) 88 | 89 | .PHONY: all 90 | all: $(LIBBUILD) $(BINBUILDS) 91 | 92 | $(BUILDDIR)/stamp: 93 | mkdir $(@D) && touch $@ 94 | 95 | $(BUILDDIR)/bin/stamp: $(BUILDDIR)/stamp 96 | mkdir $(@D) && touch $@ 97 | 98 | $(BUILDDIR)/lib/stamp: $(BUILDDIR)/stamp 99 | mkdir $(@D) && touch $@ 100 | 101 | $(BUILDDIR)/obj/stamp: $(BUILDDIR)/stamp 102 | mkdir $(@D) && touch $@ 103 | 104 | $(BUILDDIR)/lib/libcanopen2.so: $(BUILDDIR)/lib/stamp $(LIBOBJS) 105 | $(CC) -o $@ -shared $(LIBOBJS) $(LDFLAGS) 106 | 107 | $(BUILDDIR)/bin/%: $(BUILDDIR)/obj/%.o $(BUILDDIR)/bin/stamp \ 108 | $(BUILDDIR)/lib/libcanopen2.so 109 | $(CC) -o $@ $< $(BIN_LDFLAGS) 110 | 111 | $(BUILDDIR)/obj/%.o: src/%.c $(BUILDDIR)/obj/stamp 112 | $(CC) -c $(CFLAGS) -o $@ $< -MMD -MP -MF $@.deps 113 | 114 | .PHONY: install 115 | install: $(INSTALLDEPS) 116 | mkdir -p $(DESTDIR)$(PREFIX)/lib 117 | cp -r $(LIBBUILD) $(DESTDIR)$(PREFIX)/lib 118 | mkdir -p $(DESTDIR)$(PREFIX)/bin 119 | cp -r $(BINBUILDS) $(DESTDIR)$(PREFIX)/bin 120 | 121 | .PHONY: clean 122 | clean: 123 | rm -rf $(BUILDDIR) 124 | 125 | -include $(BUILDDIR)/obj/*.deps 126 | 127 | .SUFFIXES: 128 | .SECONDARY: 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # openCANopen 2 | ## Features 3 | * CANopen master 4 | * Network management 5 | * Automatic discovery of nodes on network 6 | * Dynamically loadable drivers 7 | * SDO client with expediated and segmented mode transfer 8 | * EDS reader 9 | * REST interface for accessing object-dictionary based on EDS 10 | * Virtual CANopen nodes for testing 11 | * SDO server with expediated and segmented mode transfer 12 | * Virtual nodes are configurable via ini files 13 | * CAN-TCP bridge 14 | * Transfers raw CAN frames over TCP 15 | * Traffic monitoring 16 | * A tool is included to monitor and interpret CANopen traffic 17 | * Supports Linux Socket CAN 18 | * Can be ported to other platforms with little effort 19 | 20 | ## Architecture 21 | The system is mostly asynchronous but some things are implemented synchronously via worker threads. 22 | 23 | Non-blocking tasks are handled in a single main loop. The main loop sends blocking tasks to a single work queue. The work queue is a stable priority queue implemented using a heap. When a worker is ready (which is most of the time), it grabs a job from the queue. When it's done, it sends the result back to the main loop. 24 | 25 | ## Using the Code 26 | Currently, the internals of the CANopen master are not available as a library, but they are fairly independent of each other so they can be used in isolation. Some components require a main loop library. The one that is provided with the project can be run within another main loop using `mloop_get_pollfd(struct mloop*)` and `mloop_run_once(struct mloop*)`. Mloop is implemented using epoll, so it would have to be implemented using a different method when porting to other platforms. 27 | 28 | All components can be executed in an asynchronous fashion. Some components, such as SDO requests, can be executed synchronously. 29 | 30 | ## Writing Drivers 31 | The API can be found in inc/canopen-driver.h. It lacks documentation but the names should be quite revealing. 32 | 33 | A driver's DSO file should be named co_drv_name.so where "name" is the lower case name of the node according to the object dictionary. Place the DSO under /usr/lib/canopen. 34 | 35 | The driver needs to implement the function `int co_drv_init(struct co_drv*)`. The function shall return a number less than 0 on error and 0 on success. Use the init function to initialise event handlers for PDO, EMCY, etc. and set up the node via SDO. 36 | 37 | The SDO interface is asynchronous and follows a request-reply pattern. A callback function must be set for each request. 38 | 39 | See example-driver.c 40 | 41 | #### Building 42 | `# make -f Makefile.opensource` 43 | #### Installing 44 | `# make install -f Makefile.opensource` 45 | ### Running 46 | `# canopen-master can0` 47 | -------------------------------------------------------------------------------- /bin/canopen-ls: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | local iface = arg[1] 4 | 5 | if not iface then 6 | print "ERROR: Missing interface argument" 7 | print "Usage: canopen-ls " 8 | return 1 9 | end 10 | 11 | local sm = require 'pluto.sharedmemory' 12 | 13 | local m = sm:read('canopen2.' .. iface) 14 | if not m then 15 | print "Could not read shared memory." 16 | return 1 17 | end 18 | 19 | local nodes = m.nodes 20 | 21 | function get_status_text(is_active) 22 | if is_active then 23 | return "online" 24 | else 25 | return "offline" 26 | end 27 | end 28 | 29 | print "ID\tNAME\tHWVER\tSWVER\tERROR\tSTATUS" 30 | for i=0,#nodes do 31 | local n = nodes[i] 32 | if (n.is_active or n.last_seen ~= 0) then 33 | local id = i + 1 34 | local row = { 35 | id, 36 | n.name, 37 | n.hw_version, 38 | n.sw_version, 39 | n.error_register, 40 | get_status_text(n.is_active) 41 | } 42 | print(table.concat(row, "\t")) 43 | end 44 | end 45 | 46 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | make_control() 6 | { 7 | local package=$1 8 | local arch=$2 9 | local version=$3 10 | local depends=$4 11 | 12 | cat < 21 | Depends: $depends 22 | Description: CANopen master version 2. 23 | EOF 24 | } 25 | 26 | make_target_control() 27 | { 28 | local name=$1 29 | local arch=$2 30 | local version=$3 31 | 32 | make_control $name $arch $version "libmloop, appbase" 33 | } 34 | 35 | make_host_control() 36 | { 37 | local version=$1 38 | make_control canopen2-crossdevelopment i386 $version \ 39 | "libmloop-crossdevelopment, appbase-crossdevelopment" 40 | } 41 | 42 | build_package() 43 | { 44 | local arch=$1 45 | local path=$2 46 | local name=$3 47 | local version=$4 48 | 49 | mkdir -p $path/DEBIAN 50 | if [ "$arch" == "host" ]; then 51 | arch=i386 52 | make_host_control $version >$path/DEBIAN/control 53 | else 54 | make_target_control $name $arch $version >$path/DEBIAN/control 55 | fi 56 | 57 | fakeroot dpkg-deb -b -Zgzip $path $name\_$version\_$arch\.deb 58 | 59 | rm -rf $path/DEBIAN 60 | } 61 | 62 | build_target_release() 63 | { 64 | local arch=$1 65 | 66 | local path=build-$arch-release 67 | local rootfs=build-host$(marel_getrootprefix $arch) 68 | 69 | marel_make.sh $arch -j8 RELEASE=yes 70 | make install RELEASE=yes PREFIX=/usr DESTDIR=$path 71 | make install RELEASE=yes PREFIX=/usr DESTDIR=$rootfs 72 | make clean 73 | } 74 | 75 | build_target_debug() 76 | { 77 | local arch=$1 78 | 79 | local path=build-$arch-debug 80 | local rootfs=build-host$(marel_getrootprefix $arch) 81 | 82 | marel_make.sh $arch -j8 83 | make install PREFIX=/usr DESTDIR=$path 84 | make install PREFIX=/usr DESTDIR=$rootfs 85 | make clean 86 | } 87 | 88 | build_target() 89 | { 90 | local arch=$1 91 | 92 | build_target_release $arch && build_target_debug $arch 93 | } 94 | 95 | build_host_release() 96 | { 97 | local path=build-host 98 | 99 | make -j8 RELEASE=yes 100 | make install RELEASE=yes PREFIX=/usr DESTDIR=$path 101 | make clean 102 | } 103 | 104 | build_host_debug() 105 | { 106 | local path=build-host 107 | 108 | make -j8 109 | make install PREFIX=/usr DESTDIR=$path 110 | make clean 111 | } 112 | 113 | build_host() 114 | { 115 | build_host_release && build_host_debug 116 | } 117 | 118 | build_all() 119 | { 120 | for arch in $(marel_supportedtargets --deb); do 121 | build_target $arch 122 | done 123 | 124 | build_host 125 | } 126 | 127 | build_all_packages() 128 | { 129 | local name=$1 130 | local version=$2 131 | 132 | for arch in $(marel_supportedtargets --deb); do 133 | build_package $arch build-$arch-release $name $version 134 | build_package $arch build-$arch-debug $name-dbg $version 135 | done 136 | 137 | build_package host build-host $name-crossdevelopment $version 138 | } 139 | 140 | get_version() 141 | { 142 | echo $(git describe --tags --dirty)-pluto$(pluto_version) 143 | } 144 | 145 | make distclean 146 | build_all 147 | build_all_packages canopen2 $(get_version) 148 | 149 | -------------------------------------------------------------------------------- /canopen2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /etc/canopen.cfg: -------------------------------------------------------------------------------- 1 | [master] 2 | 3 | [all] 4 | ignore_sdo_multiplexer=yes 5 | send_full_sdo_frame=yes 6 | 7 | [=MWS2] 8 | has_zero_guard_status=yes 9 | 10 | [=MWS2-F1] 11 | has_zero_guard_status=yes 12 | -------------------------------------------------------------------------------- /example-driver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define EXPORT __attribute__((visibility("default"))) 7 | #define MIN(a, b) (a) < (b) ? (a) : (b) 8 | #define NAME_SIZE 256 9 | 10 | struct example { 11 | char name[NAME_SIZE]; 12 | }; 13 | 14 | void on_read_name(struct co_drv* drv, struct co_sdo_req* req) 15 | { 16 | struct example* self = co_get_context(drv); 17 | 18 | if (co_sdo_req_get_status(req) != CO_SDO_REQ_OK) 19 | return; 20 | 21 | const char* data = co_sdo_req_get_data(req); 22 | size_t size = MIN(co_sdo_req_get_size(req), NAME_SIZE - 1); 23 | 24 | memset(self->name, 0, NAME_SIZE); 25 | memcpy(self->name, data, size); 26 | } 27 | 28 | int start_read_name(struct co_drv* drv, struct example* self) 29 | { 30 | struct co_sdo_req* req = co_sdo_req_new(drv); 31 | if (!req) 32 | return -1; 33 | 34 | co_sdo_req_set_type(req, CO_SDO_UPLOAD); 35 | co_sdo_req_set_indices(req, 0x1008, 0); 36 | co_sdo_req_set_done_fn(req, on_read_name); 37 | 38 | int r = co_sdo_req_start(req); 39 | co_sdo_req_unref(req); 40 | return r; 41 | } 42 | 43 | void on_pdo1(struct co_drv* drv, const void* data, size_t size) 44 | { 45 | struct example* self = co_get_context(drv); 46 | 47 | uint64_t value = 0; 48 | co_byteorder(&value, data, sizeof(value), size); 49 | 50 | printf("%s: %llx\n", self->name, value); 51 | } 52 | 53 | EXPORT 54 | int co_drv_init(struct co_drv* drv) 55 | { 56 | struct example* self = malloc(sizeof(*self)); 57 | if (!self) 58 | return -1; 59 | 60 | memset(self, 0, sizeof(*self)); 61 | 62 | co_set_context(drv, self, (co_free_fn)free); 63 | 64 | co_set_pdo1_fn(drv, on_pdo1); 65 | 66 | start_read_name(drv, self); 67 | 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /inc/CanMasterInterface.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef _can_master_interface__h 17 | #define _can_master_interface__h 18 | 19 | #include 20 | /** 21 | * @class CanIoMapDriver CanMasterInteface.h 22 | */ 23 | namespace CanIOMapDriver 24 | { 25 | 26 | class CanMasterInterface 27 | { 28 | protected: 29 | int nodeId; 30 | 31 | public: 32 | CanMasterInterface(){}; 33 | virtual ~CanMasterInterface(){}; 34 | virtual int sendPdo1(unsigned char *data, size_t size) = 0; 35 | virtual int sendPdo2(unsigned char *data, size_t size) = 0; 36 | virtual int sendPdo3(unsigned char *data, size_t size) = 0; 37 | virtual int sendPdo4(unsigned char *data, size_t size) = 0; 38 | virtual int requestPdo1() = 0; 39 | virtual int requestPdo2() = 0; 40 | virtual int requestPdo3() = 0; 41 | virtual int requestPdo4() = 0; 42 | virtual int sendSdo(int index, int subindex, unsigned char *data, size_t size) = 0; 43 | virtual int requestSdo(int index, int subindex) = 0; 44 | virtual int setNodeState(int state) = 0; 45 | int getNodeId() { return nodeId; } 46 | }; 47 | 48 | } /* End of namespace */ 49 | 50 | #endif /* _can_master_interface__h */ 51 | 52 | -------------------------------------------------------------------------------- /inc/Driver.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2017, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef __Driver_h__ 17 | #define __Driver_h__ 18 | #include 19 | #include 20 | #include 21 | #include "CanIOHandlerInterface.h" 22 | #include "StateFactory.h" 23 | 24 | class Driver 25 | { 26 | static int inst; 27 | StateManagerInterface *state; 28 | int profilePos; 29 | int *candriver_profile; 30 | int *candriver_version; 31 | candriver_set_parent_t* candriver_set_parent; 32 | candriver_create_handler_t* candriver_create_handler; 33 | candriver_delete_handler_t* candriver_delete_handler; 34 | candriver_delete_all_handlers_t* candriver_delete_all_handlers; 35 | candriver_close_driver_t* candriver_close_driver; 36 | void *handle; 37 | std::string fileName; 38 | 39 | template T tryLoadSymbol(void* handle, const char* name) 40 | { 41 | T sym = reinterpret_cast(dlsym(handle, name)); 42 | const char* err = dlerror(); 43 | if (err) 44 | throw std::runtime_error("Could not load symbol '" 45 | + std::string(name) + "': " + std::string(err)); 46 | return sym; 47 | } 48 | 49 | public: 50 | Driver(const char *filename); 51 | ~Driver(); 52 | int createHandler(const char* nodeName, int32_t profileNr, 53 | CanIOMapDriver::CanMasterInterface *cmi, 54 | CanIOMapDriver::CanIOHandlerInterface** chi); 55 | int deleteHandler(CanIOMapDriver::CanIOHandlerInterface* chi ); 56 | int getFirstProfile(); 57 | int getNextProfile(); 58 | std::string getFilename() { return fileName; } 59 | }; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /inc/DriverManager.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef __driverManager_h__ 17 | #define __driverManager_h__ 18 | 19 | #include 20 | #include 21 | #include 22 | #include "CanMasterInterface.h" 23 | #include "CanIOHandlerInterface.h" 24 | 25 | class Driver; 26 | 27 | typedef std::map driver_t; 28 | typedef std::map chi2driver_t; 29 | 30 | class DriverManager { 31 | private: 32 | driver_t drivers; 33 | chi2driver_t chi2driver; 34 | Driver *defaultDriver; 35 | std::string _driverPath; 36 | 37 | int getdir(const std::string& dir, std::vector &files); 38 | std::string& getDriverPath(); 39 | DriverManager(const DriverManager &); 40 | DriverManager & operator=(const DriverManager&); 41 | 42 | public: 43 | DriverManager(); 44 | virtual ~DriverManager(); 45 | int createHandler(const char* nodeName, int profileNr, 46 | CanIOMapDriver::CanMasterInterface *cmi, 47 | CanIOMapDriver::CanIOHandlerInterface** chi); 48 | int deleteHandler(int profileNr, CanIOMapDriver::CanIOHandlerInterface* chi); 49 | 50 | 51 | }; 52 | #endif 53 | -------------------------------------------------------------------------------- /inc/arc.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef ARC_H_ 17 | #define ARC_H_ 18 | 19 | #include "co_atomic.h" 20 | 21 | #ifdef ARC_DEBUG 22 | #include 23 | #define ARC_PRINT_DEBUG(name, type) \ 24 | printf(ARC_STR(name) "_" type "(%p) = %d\n", self, ref); 25 | #else 26 | #define ARC_PRINT_DEBUG(...) 27 | #endif 28 | 29 | #define ARC_STR(x) #x 30 | 31 | #define ARC_GENERATE_REF(name) \ 32 | int name ## _ref(struct name* self) \ 33 | { \ 34 | int ref = co_atomic_add_fetch(&self->ref, 1); \ 35 | ARC_PRINT_DEBUG(name, "ref") \ 36 | return ref; \ 37 | } 38 | 39 | #define ARC_GENERATE_UNREF(name, free_fn) \ 40 | int name ## _unref(struct name* self) \ 41 | { \ 42 | int ref = co_atomic_sub_fetch(&self->ref, 1); \ 43 | ARC_PRINT_DEBUG(name, "unref") \ 44 | if (ref == 0) \ 45 | free_fn(self); \ 46 | return ref; \ 47 | } 48 | 49 | #define ARC_GENERATE(name, free_fn) \ 50 | ARC_GENERATE_REF(name) \ 51 | ARC_GENERATE_UNREF(name, free_fn) 52 | 53 | #define ARC_PROTOTYPE(name) \ 54 | int name ## _ref(struct name* self); \ 55 | int name ## _unref(struct name* self); 56 | 57 | #endif /* ARC_H_ */ 58 | -------------------------------------------------------------------------------- /inc/can-tcp.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef CAN_TCP_H_ 17 | #define CAN_TCP_H_ 18 | 19 | int can_tcp_open(const char* addr, int port); 20 | int can_tcp_bridge_server(const char* can, int port); 21 | int can_tcp_bridge_client(const char* can, const char* address, int port); 22 | 23 | #endif /* CAN_TCP_H_ */ 24 | 25 | -------------------------------------------------------------------------------- /inc/canopen.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef _CANOPEN_H 17 | #define _CANOPEN_H 18 | 19 | #define CANOPEN_NODEID_MIN 1 20 | #define CANOPEN_NODEID_MAX 127 21 | 22 | struct can_frame; 23 | 24 | enum canopen_range { 25 | R_NMT = 0, 26 | R_SYNC = 0x80, 27 | R_EMCY = 0x80, EMCY_LOW = R_EMCY + 1, EMCY_HIGH = R_EMCY + 0x7f, 28 | R_TIMESTAMP = 0x100, 29 | R_TPDO1 = 0x180, TPDO1_LOW = R_TPDO1, TPDO1_HIGH = R_TPDO1 + 0x7f, 30 | R_RPDO1 = 0x200, RPDO1_LOW = R_RPDO1, RPDO1_HIGH = R_RPDO1 + 0x7f, 31 | R_TPDO2 = 0x280, TPDO2_LOW = R_TPDO2, TPDO2_HIGH = R_TPDO2 + 0x7f, 32 | R_RPDO2 = 0x300, RPDO2_LOW = R_RPDO2, RPDO2_HIGH = R_RPDO2 + 0x7f, 33 | R_TPDO3 = 0x380, TPDO3_LOW = R_TPDO3, TPDO3_HIGH = R_TPDO3 + 0x7f, 34 | R_RPDO3 = 0x400, RPDO3_LOW = R_RPDO3, RPDO3_HIGH = R_RPDO3 + 0x7f, 35 | R_TPDO4 = 0x480, TPDO4_LOW = R_TPDO4, TPDO4_HIGH = R_TPDO4 + 0x7f, 36 | R_RPDO4 = 0x500, RPDO4_LOW = R_RPDO4, RPDO4_HIGH = R_RPDO4 + 0x7f, 37 | R_TSDO = 0x580, TSDO_LOW = R_TSDO , TSDO_HIGH = R_TSDO + 0x7f, 38 | R_RSDO = 0x600, RSDO_LOW = R_RSDO , RSDO_HIGH = R_RSDO + 0x7f, 39 | R_HEARTBEAT = 0x700, HEARTBEAT_LOW = R_HEARTBEAT, 40 | HEARTBEAT_HIGH = R_HEARTBEAT + 0x7f, 41 | }; 42 | 43 | enum canopen_object { 44 | CANOPEN_UNSPEC = 0, 45 | CANOPEN_NONE = 0, 46 | CANOPEN_NMT = 1 << 0, 47 | CANOPEN_SYNC = 1 << 1, 48 | CANOPEN_EMCY = 1 << 2, 49 | CANOPEN_TIMESTAMP = 1 << 3, 50 | CANOPEN_TPDO1 = 1 << 4, 51 | CANOPEN_RPDO1 = 1 << 5, 52 | CANOPEN_TPDO2 = 1 << 6, 53 | CANOPEN_RPDO2 = 1 << 7, 54 | CANOPEN_TPDO3 = 1 << 8, 55 | CANOPEN_RPDO3 = 1 << 9, 56 | CANOPEN_TPDO4 = 1 << 10, 57 | CANOPEN_RPDO4 = 1 << 11, 58 | CANOPEN_TSDO = 1 << 12, 59 | CANOPEN_RSDO = 1 << 13, 60 | CANOPEN_HEARTBEAT = 1 << 14, 61 | CANOPEN_MASK = (CANOPEN_HEARTBEAT << 1) - 1 62 | }; 63 | 64 | struct canopen_msg { 65 | int id; 66 | enum canopen_object object; 67 | }; 68 | 69 | int canopen_get_object_type(struct canopen_msg* msg, 70 | const struct can_frame* frame); 71 | 72 | #endif /* _CANOPEN_H */ 73 | 74 | -------------------------------------------------------------------------------- /inc/canopen/byteorder.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef _CANOPEN_BYTEORDER_H 17 | #define _CANOPEN_BYTEORDER_H 18 | 19 | #include 20 | 21 | void byteorder(void* dst, const void* src, size_t size); 22 | void byteorder2(void* dst, const void* src, size_t dst_size, size_t src_size); 23 | 24 | #define BYTEORDER(dst, value) \ 25 | ({ \ 26 | __typeof__(value) _value = value; \ 27 | byteorder(dst, &_value, sizeof(_value)); \ 28 | }) 29 | 30 | 31 | #endif /* _CANOPEN_BYTEORDER_H */ 32 | 33 | -------------------------------------------------------------------------------- /inc/canopen/cia302-error-codes.h: -------------------------------------------------------------------------------- 1 | X(0x0000, "Error reset or no error") \ 2 | X(0x1000, "Generic error") \ 3 | X(0x2000, "Current") \ 4 | X(0x2100, "Input") \ 5 | X(0x2200, "Internal") \ 6 | X(0x2300, "Output") \ 7 | X(0x3000, "Voltage") \ 8 | X(0x3100, "Mains") \ 9 | X(0x3200, "Internal") \ 10 | X(0x3300, "Output") \ 11 | X(0x4000, "Temperature") \ 12 | X(0x4100, "Ambient") \ 13 | X(0x4200, "Device") \ 14 | X(0x5000, "Hardware") \ 15 | X(0x6000, "Software") \ 16 | X(0x6100, "Internal") \ 17 | X(0x6200, "User") \ 18 | X(0x6300, "Data set") \ 19 | X(0x7000, "Additional modules") \ 20 | X(0x8000, "Monitoring") \ 21 | X(0x8100, "Communication") \ 22 | X(0x8110, "CAN overrrun (object lost)") \ 23 | X(0x8120, "CAN in error passive mode") \ 24 | X(0x8130, "Life guard error or heartbeat error") \ 25 | X(0x8140, "Recovered from bus off") \ 26 | X(0x8150, "CAN-ID collision") \ 27 | X(0x8200, "Protocol error") \ 28 | X(0x8210, "PDO not processed due to length error") \ 29 | X(0x8211, "PDO1") \ 30 | X(0x8212, "PDO2") \ 31 | X(0x8213, "PDO3") \ 32 | X(0x8214, "PDO4") \ 33 | X(0x8220, "PDO length exceeded") \ 34 | X(0x8221, "PDO1") \ 35 | X(0x8222, "PDO2") \ 36 | X(0x8223, "PDO3") \ 37 | X(0x8224, "PDO4") \ 38 | X(0x8230, "DAM MPDO not processed, destination object not available") \ 39 | X(0x8240, "Unexpected SYNC data length") \ 40 | X(0x8250, "RPDO timeout") \ 41 | X(0x8251, "PDO1") \ 42 | X(0x8252, "PDO2") \ 43 | X(0x8253, "PDO3") \ 44 | X(0x8254, "PDO4") \ 45 | X(0x9000, "External error") \ 46 | X(0xF000, "Additional functions") \ 47 | X(0xFF00, "CANopen device specific") \ 48 | 49 | -------------------------------------------------------------------------------- /inc/canopen/dump.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2017, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef CANOPEN_DUMP_H_ 17 | #define CANOPEN_DUMP_H_ 18 | 19 | #define CO_DUMP_FILTER_SHIFT 8 20 | #define CO_DUMP_PDO_FILTER_SHIFT 16 21 | #define CO_DUMP_FILTER_MASK 0x00ffff00 22 | 23 | enum co_dump_options { 24 | CO_DUMP_TCP = 1, 25 | CO_DUMP_TIMESTAMP = 1 << 1, 26 | CO_DUMP_FILE = 1 << 2, 27 | 28 | CO_DUMP_FILTER_NMT = 1 << (CO_DUMP_FILTER_SHIFT + 0), 29 | CO_DUMP_FILTER_SYNC = 1 << (CO_DUMP_FILTER_SHIFT + 1), 30 | CO_DUMP_FILTER_TIMESTAMP = 1 << (CO_DUMP_FILTER_SHIFT + 2), 31 | CO_DUMP_FILTER_EMCY = 1 << (CO_DUMP_FILTER_SHIFT + 3), 32 | CO_DUMP_FILTER_SDO = 1 << (CO_DUMP_FILTER_SHIFT + 4), 33 | CO_DUMP_FILTER_HEARTBEAT = 1 << (CO_DUMP_FILTER_SHIFT + 5), 34 | 35 | CO_DUMP_FILTER_PDO1 = 1 << (CO_DUMP_PDO_FILTER_SHIFT + 0), 36 | CO_DUMP_FILTER_PDO2 = 1 << (CO_DUMP_PDO_FILTER_SHIFT + 1), 37 | CO_DUMP_FILTER_PDO3 = 1 << (CO_DUMP_PDO_FILTER_SHIFT + 2), 38 | CO_DUMP_FILTER_PDO4 = 1 << (CO_DUMP_PDO_FILTER_SHIFT + 3), 39 | 40 | CO_DUMP_FILTER_PDO = CO_DUMP_FILTER_PDO1 | CO_DUMP_FILTER_PDO2 41 | | CO_DUMP_FILTER_PDO3 | CO_DUMP_FILTER_PDO4, 42 | }; 43 | 44 | int co_dump(const char* addr, enum co_dump_options options); 45 | 46 | #endif /* CANOPEN_DUMP_H_ */ 47 | -------------------------------------------------------------------------------- /inc/canopen/eds.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef CANOPEN_EDS_H_ 17 | #define CANOPEN_EDS_H_ 18 | 19 | #include "canopen/types.h" 20 | #include "sys/tree.h" 21 | 22 | enum eds_obj_access { 23 | EDS_OBJ_R = 1, 24 | EDS_OBJ_W = 2, 25 | EDS_OBJ_RW = 3, 26 | EDS_OBJ_CONST = 4, 27 | }; 28 | 29 | struct eds_obj { 30 | uint32_t key; 31 | enum canopen_type type; 32 | enum eds_obj_access access; 33 | const char* name; 34 | const char* default_value; 35 | const char* low_limit; 36 | const char* high_limit; 37 | const char* unit; 38 | const char* scaling; 39 | }; 40 | 41 | struct eds_obj_node; 42 | 43 | RB_HEAD(eds_obj_tree, eds_obj_node); 44 | 45 | struct canopen_eds { 46 | uint32_t vendor; 47 | uint32_t product; 48 | uint32_t revision; 49 | char name[256]; 50 | 51 | struct eds_obj_tree obj_tree; 52 | }; 53 | 54 | int eds_db_load(void); 55 | void eds_db_unload(void); 56 | 57 | const struct canopen_eds* eds_db_find(int vendor, int product, int revision); 58 | const struct canopen_eds* eds_db_find_by_name(const char* name); 59 | 60 | const struct eds_obj* eds_obj_find(const struct canopen_eds* eds, 61 | int index, int subindex); 62 | 63 | static inline int eds_obj_index(const struct eds_obj* obj) 64 | { 65 | return obj->key >> 8; 66 | } 67 | 68 | static inline int eds_obj_subindex(const struct eds_obj* obj) 69 | { 70 | return obj->key & 0xff; 71 | } 72 | 73 | struct canopen_eds* eds_db_get(int index); 74 | size_t eds_db_length(void); 75 | 76 | const struct eds_obj* eds_obj_first(const struct canopen_eds* eds); 77 | const struct eds_obj* eds_obj_next(const struct canopen_eds* eds, 78 | const struct eds_obj* obj); 79 | 80 | #endif /* CANOPEN_EDS_H_ */ 81 | -------------------------------------------------------------------------------- /inc/canopen/emcy.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef CANOPEN_EMCY_H_ 17 | #define CANOPEN_EMCY_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | #include "canopen/byteorder.h" 23 | 24 | static inline unsigned int emcy_get_code(const struct can_frame* frame) 25 | { 26 | uint16_t result = 0; 27 | byteorder(&result, frame->data, sizeof(result)); 28 | return result; 29 | } 30 | 31 | static inline unsigned int emcy_get_register(const struct can_frame* frame) 32 | { 33 | uint8_t result = 0; 34 | byteorder(&result, &frame->data[2], sizeof(result)); 35 | return result; 36 | } 37 | 38 | static inline uint64_t emcy_get_manufacturer_error(const struct can_frame* frame) 39 | { 40 | uint64_t tmp = 0; 41 | uint64_t result = 0; 42 | 43 | memcpy(&tmp, &frame->data[4], 5); 44 | 45 | byteorder(&result, &tmp, sizeof(result)); 46 | 47 | return result; 48 | } 49 | 50 | #endif /* CANOPEN_EMCY_H_ */ 51 | 52 | -------------------------------------------------------------------------------- /inc/canopen/error.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | const char* error_code_to_string(uint16_t code, int profile); 6 | 7 | -------------------------------------------------------------------------------- /inc/canopen/heartbeat.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef _CANOPEN_HEARTBEAT_H 17 | #define _CANOPEN_HEARTBEAT_H 18 | 19 | #include 20 | #include "canopen/nmt.h" 21 | 22 | static inline int heartbeat_is_valid(const struct can_frame* frame) 23 | { 24 | return frame->can_dlc == 1; 25 | } 26 | 27 | static inline enum nmt_state heartbeat_get_state(const struct can_frame* frame) 28 | { 29 | return frame->data[0] & 0x7f; 30 | } 31 | 32 | static inline int heartbeat_is_bootup(const struct can_frame* frame) 33 | { 34 | return heartbeat_get_state(frame) == NMT_STATE_BOOTUP; 35 | } 36 | 37 | static inline void heartbeat_set_state(struct can_frame* frame, 38 | enum nmt_state state) 39 | { 40 | frame->data[0] = state & 0x7f; 41 | } 42 | 43 | #endif /* _CANOPEN_HEARTBEAT_H */ 44 | 45 | -------------------------------------------------------------------------------- /inc/canopen/master.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef CANOPEN_MASTER_H_ 17 | #define CANOPEN_MASTER_H_ 18 | 19 | #include 20 | #include 21 | #include "canopen.h" 22 | #include "canopen-driver.h" 23 | #include "type-macros.h" 24 | 25 | enum co_master_driver_type { 26 | CO_MASTER_DRIVER_NONE = 0, 27 | CO_MASTER_DRIVER_LEGACY, 28 | CO_MASTER_DRIVER_NEW 29 | }; 30 | 31 | typedef int (*co_drv_init_fn)(struct co_drv*); 32 | 33 | struct co_drv { 34 | void* dso; 35 | co_drv_init_fn init_fn; 36 | 37 | struct sdo_req_queue* sdo_queue; 38 | 39 | void* context; 40 | co_free_fn free_fn; 41 | 42 | co_pdo_fn pdo1_fn, pdo2_fn, pdo3_fn, pdo4_fn; 43 | co_emcy_fn emcy_fn; 44 | co_start_fn start_fn; 45 | 46 | enum co_options options; 47 | 48 | char iface[256]; 49 | }; 50 | 51 | struct co_master_node { 52 | enum co_master_driver_type driver_type; 53 | 54 | void* driver; 55 | void* master_iface; 56 | 57 | struct co_drv ndrv; 58 | 59 | uint32_t device_type; 60 | int is_heartbeat_supported; 61 | 62 | uint32_t vendor_id, product_code, revision_number; 63 | 64 | struct mloop_timer* heartbeat_timer; 65 | struct mloop_timer* ping_timer; 66 | 67 | char name[64]; 68 | char hw_version[64]; 69 | char sw_version[64]; 70 | 71 | int is_loading; 72 | int is_initialized; 73 | 74 | uint32_t ntimeouts; 75 | }; 76 | 77 | extern struct co_master_node co_master_node_[]; 78 | 79 | static inline int co_master_get_node_id(const struct co_master_node* node) 80 | { 81 | return ((char*)node - (char*)co_master_node_) 82 | / sizeof(struct co_master_node); 83 | } 84 | 85 | static inline struct co_master_node* co_master_get_node(int nodeid) 86 | { 87 | assert(CANOPEN_NODEID_MIN <= nodeid && nodeid <= CANOPEN_NODEID_MAX); 88 | return &co_master_node_[nodeid]; 89 | } 90 | 91 | int co_master_run(void); 92 | 93 | int co_drv_load(struct co_drv* drv, const char* name); 94 | int co_drv_init(struct co_drv* drv); 95 | void co_drv_unload(struct co_drv* drv); 96 | 97 | int co__rpdox(int nodeid, int type, const void* data, size_t size); 98 | int co__start(int nodeid); 99 | 100 | static inline struct co_master_node* co_drv_node(const struct co_drv* drv) 101 | { 102 | return container_of(drv, struct co_master_node, ndrv); 103 | } 104 | 105 | static inline uint16_t 106 | co_master_get_device_profile(const struct co_master_node* node) 107 | { 108 | return node->device_type & 0xffff; 109 | } 110 | 111 | #endif /* CANOPEN_MASTER_H_ */ 112 | -------------------------------------------------------------------------------- /inc/canopen/network.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef CANOPEN_NETWORK_H_ 17 | #define CANOPEN_NETWORK_H_ 18 | 19 | #include 20 | 21 | struct can_frame; 22 | struct sock; 23 | 24 | /* Reset the network and see which nodes respond to the reset signal. 25 | * 26 | * nodes_seen must be an array of length 128; prior values are not cleared. 27 | * timeout is in ms. 28 | */ 29 | int co_net_reset(const struct sock* sock, char* nodes_seen, int timeout); 30 | 31 | /* Reset each node individually and see if it responds to the reset signal. 32 | * 33 | * nodes_seen must be an array of length 128; prior values are not cleared. 34 | * start/stop is an inclusive range of node ids to probe 35 | * timeout is in ms. 36 | */ 37 | int co_net_reset_range(const struct sock* sock, char* nodes_seen, int start, 38 | int end, int timeout); 39 | 40 | /* Probe the network for nodes. 41 | * 42 | * nodes_seen must be an array of length 128; prior values are not cleared. 43 | * start/stop is an inclusive range of node ids to probe 44 | * timeout is in ms. 45 | */ 46 | int co_net_probe(const struct sock* sock, char* nodes_seen, int start, int end, 47 | int timeout); 48 | 49 | int co_net_probe_sdo(const struct sock* sock, char* nodes_seen, int start, 50 | int end, int timeout); 51 | 52 | int co_net_send_nmt(const struct sock* sock, int cs, int nodeid); 53 | int co_net__request_device_type(const struct sock* sock, int nodeid); 54 | 55 | int co_net__wait_for_bootup(const struct sock* sock, char* nodes_seen, 56 | int start, int end, int timeout); 57 | 58 | int co_net__wait_for_sdo(const struct sock* sock, char* nodes_seen, int start, 59 | int end, int timeout); 60 | 61 | #endif /* CANOPEN_NETWORK_H_ */ 62 | 63 | -------------------------------------------------------------------------------- /inc/canopen/nmt.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef _CANOPEN_NMT_H 17 | #define _CANOPEN_NMT_H 18 | 19 | #include 20 | #include 21 | 22 | #define NMT_ALL_NODES 0 23 | 24 | enum nmt_state { 25 | NMT_STATE_BOOTUP = 0, 26 | NMT_STATE_STOPPED = 4, 27 | NMT_STATE_OPERATIONAL = 5, 28 | NMT_STATE_PREOPERATIONAL = 127, 29 | }; 30 | 31 | enum nmt_cs { 32 | NMT_CS_START = 1, 33 | NMT_CS_STOP = 2, 34 | NMT_CS_ENTER_PREOPERATIONAL = 128, 35 | NMT_CS_RESET_NODE = 129, 36 | NMT_CS_RESET_COMMUNICATION = 130, 37 | }; 38 | 39 | struct nmt_slave { 40 | enum nmt_state state; 41 | }; 42 | 43 | static inline int nmt_is_valid(const struct can_frame* frame) 44 | { 45 | return frame->can_dlc == 2; 46 | } 47 | 48 | static inline enum nmt_cs nmt_get_cs(const struct can_frame* frame) 49 | { 50 | return (enum nmt_cs)frame->data[0]; 51 | } 52 | 53 | static inline void nmt_set_cs(struct can_frame* frame, enum nmt_cs cs) 54 | { 55 | frame->data[0] = cs; 56 | } 57 | 58 | static inline int nmt_get_nodeid(const struct can_frame* frame) 59 | { 60 | return frame->data[1]; 61 | } 62 | 63 | static inline void nmt_set_nodeid(struct can_frame* frame, int nodeid) 64 | { 65 | frame->data[1] = nodeid; 66 | } 67 | 68 | #endif /* _CANOPEN_NMT_H */ 69 | 70 | -------------------------------------------------------------------------------- /inc/canopen/sdo-dict.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef CANOPEN_SDO_DICT_H_ 17 | #define CANOPEN_SDO_DICT_H_ 18 | 19 | #include "canopen/types.h" 20 | #include 21 | 22 | #define SDO_MUX(index, subindex) ((index << 16) | subindex) 23 | 24 | #define SDO_DICTIONARY \ 25 | X(0x1000, 0, UNSIGNED32, DEVICE_TYPE) \ 26 | X(0x1001, 0, UNSIGNED8, ERROR_REGISTER) \ 27 | X(0x1002, 0, UNSIGNED32, MANUFACTURER_STATUS_REGISTER) \ 28 | X(0x1003, 0, UNSIGNED32, PREDEFINED_ERROR_FIELD_COUNT) \ 29 | X(0x1005, 0, UNSIGNED32, SYNC_COB_ID) \ 30 | X(0x1006, 0, UNSIGNED32, COMMUNICATION_CYCLE_PERIOD) \ 31 | X(0x1007, 0, UNSIGNED32, SYNCHRONOUS_WINDOW_LENGTH) \ 32 | X(0x1008, 0, VISIBLE_STRING, MANUFACTURER_DEVICE_NAME) \ 33 | X(0x1009, 0, VISIBLE_STRING, MANUFACTURER_HARDWARE_VERSION) \ 34 | X(0x100A, 0, VISIBLE_STRING, MANUFACTURER_SOFTWARE_VERSION) \ 35 | X(0x100C, 0, UNSIGNED16, GUARD_TIME) \ 36 | X(0x100D, 0, UNSIGNED8, LIFE_TIME_FACTOR) \ 37 | X(0x1010, 0, UNSIGNED32, STORE_PARAMETERS) \ 38 | X(0x1011, 0, UNSIGNED32, RESTORE_DEFAULT_PARAMETERS) \ 39 | X(0x1012, 0, UNSIGNED32, TIME_COB_ID) \ 40 | X(0x1013, 0, UNSIGNED32, HIGH_RESULUTION_TIME_STAMP) \ 41 | X(0x1014, 0, UNSIGNED32, EMERGENCY_COB_ID) \ 42 | X(0x1015, 0, UNSIGNED16, EMERGENCY_INHIBIT_TIME) \ 43 | X(0x1016, 0, UNSIGNED32, CONSUMER_HEARTBEAT_TIME) \ 44 | X(0x1017, 0, UNSIGNED16, PRODUCER_HEARTBEAT_TIME) \ 45 | X(0x1018, 0, UNSIGNED8, IDENTITY_COUNT) \ 46 | X(0x1018, 1, UNSIGNED32, VENDOR_ID) \ 47 | X(0x1018, 2, UNSIGNED32, PRODUCT_CODE) \ 48 | X(0x1018, 3, UNSIGNED32, REVISION_NUMBER) \ 49 | X(0x1020, 0, UNSIGNED32, VERIFY_CONFIGURATION) \ 50 | X(0x1021, 0, DOMAIN, STORE_EDS) \ 51 | X(0x1022, 0, UNSIGNED16, STORAGE_FORMAT) 52 | 53 | enum { 54 | #define X(index, subindex, type, name) dict_ ## name = SDO_MUX(index, subindex), 55 | DICTIONARY 56 | #undef X 57 | }; 58 | 59 | enum canopen_type sdo_dict_type(uint32_t mux); 60 | const char* sdo_dict_tostring(uint32_t mux); 61 | uint32_t sdo_dict_fromstring(const char* str); 62 | 63 | #endif /* CANOPEN_SDO_DICT_H_ */ 64 | -------------------------------------------------------------------------------- /inc/canopen/sdo_async.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef SDO_ASYNC_H_ 17 | #define SDO_ASYNC_H_ 18 | 19 | #include 20 | #include "vector.h" 21 | #include "canopen/sdo_req_enums.h" 22 | #include "canopen/sdo.h" 23 | #include "sock.h" 24 | 25 | struct sdo_async; 26 | struct can_frame; 27 | 28 | typedef void (*sdo_async_fn)(struct sdo_async* async); 29 | typedef void (*sdo_async_free_fn)(void* ptr); 30 | 31 | enum sdo_async_comm_state { 32 | SDO_ASYNC_COMM_START = 0, 33 | SDO_ASYNC_COMM_INIT_RESPONSE, 34 | SDO_ASYNC_COMM_SEG_RESPONSE, 35 | }; 36 | 37 | enum sdo_async_quirks_flags { 38 | SDO_ASYNC_QUIRK_NONE = 0, 39 | SDO_ASYNC_QUIRK_IGNORE_MULTIPLEXER = 1, 40 | SDO_ASYNC_QUIRK_NEEDS_FULL_FRAME = 2, 41 | SDO_ASYNC_QUIRK_ALL = 0xff, 42 | }; 43 | 44 | struct sdo_async { 45 | struct sock sock; 46 | unsigned int nodeid; 47 | enum sdo_req_type type; 48 | int is_running; 49 | enum sdo_async_comm_state comm_state; 50 | struct mloop_timer* timer; 51 | struct vector buffer; 52 | sdo_async_fn on_done; 53 | int index, subindex; 54 | int is_toggled; 55 | size_t pos; 56 | enum sdo_req_status status; 57 | enum sdo_abort_code abort_code; 58 | enum sdo_async_quirks_flags quirks; 59 | void* context; 60 | sdo_async_free_fn free_fn; 61 | int is_size_indicated; 62 | }; 63 | 64 | struct sdo_async_info { 65 | enum sdo_req_type type; 66 | int index, subindex; 67 | unsigned long timeout; 68 | const void* data; 69 | size_t size; 70 | sdo_async_fn on_done; 71 | void* context; 72 | sdo_async_free_fn free_fn; 73 | }; 74 | 75 | int sdo_async_init(struct sdo_async* self, const struct sock* sock, int nodeid); 76 | void sdo_async_destroy(struct sdo_async* self); 77 | 78 | int sdo_async_start(struct sdo_async* self, const struct sdo_async_info* info); 79 | int sdo_async_stop(struct sdo_async* self); 80 | 81 | int sdo_async_feed(struct sdo_async* self, const struct can_frame* frame); 82 | 83 | #endif /* SDO_ASYNC_H_ */ 84 | 85 | -------------------------------------------------------------------------------- /inc/canopen/sdo_req.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef SDO_REQ_H_ 17 | #define SDO_REQ_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | #include "vector.h" 23 | #include "canopen/sdo.h" 24 | #include "arc.h" 25 | 26 | #include "canopen/sdo_async.h" 27 | #include "canopen/sdo_req_enums.h" 28 | #include "type-macros.h" 29 | 30 | struct sdo_req; 31 | struct sock; 32 | 33 | typedef void (*sdo_req_fn)(struct sdo_req*); 34 | typedef void (*sdo_req_free_fn)(void*); 35 | 36 | struct sdo_req_info { 37 | enum sdo_req_type type; 38 | int index, subindex; 39 | sdo_req_fn on_done; 40 | const void* dl_data; 41 | size_t dl_size; 42 | void* context; 43 | }; 44 | 45 | struct sdo_req_queue; 46 | 47 | struct sdo_req { 48 | int ref; 49 | TAILQ_ENTRY(sdo_req) links; 50 | enum sdo_req_type type; 51 | int index, subindex; 52 | struct vector data; 53 | enum sdo_req_status status; 54 | enum sdo_abort_code abort_code; 55 | sdo_req_fn on_done; 56 | struct sdo_req_queue* parent; 57 | void* context; 58 | sdo_req_free_fn context_free_fn; 59 | int is_size_indicated; 60 | }; 61 | 62 | TAILQ_HEAD(sdo_req_list, sdo_req); 63 | 64 | struct sdo_req_queue { 65 | pthread_mutex_t mutex; 66 | size_t size; 67 | size_t limit; 68 | struct sdo_req_list list; 69 | struct sdo_async sdo_client; 70 | struct mloop_idle* idle; 71 | int nodeid; 72 | }; 73 | 74 | int sdo_req__queue_init(struct sdo_req_queue* self, const struct sock* sock, 75 | int nodeid, size_t, enum sdo_async_quirks_flags quirks); 76 | void sdo_req__queue_destroy(struct sdo_req_queue* self); 77 | 78 | int sdo_req_queues_init(const struct sock* sock, size_t limit, 79 | enum sdo_async_quirks_flags quirks); 80 | void sdo_req_queues_cleanup(); 81 | 82 | struct sdo_req_queue* sdo_req_queue_get(int nodeid); 83 | void sdo_req_queue_flush(struct sdo_req_queue* self); 84 | 85 | struct sdo_req* sdo_req_new(struct sdo_req_info* info); 86 | void sdo_req_free(struct sdo_req* self); 87 | 88 | int sdo_req_start(struct sdo_req* self, struct sdo_req_queue* queue); 89 | void sdo_req_wait(struct sdo_req* self); 90 | 91 | int sdo_req_queue__enqueue(struct sdo_req_queue* self, struct sdo_req* req); 92 | struct sdo_req* sdo_req_queue__dequeue(struct sdo_req_queue* self); 93 | 94 | static inline 95 | struct sdo_req_queue* sdo_req_queue__from_async(const struct sdo_async* async) 96 | { 97 | return container_of(async, struct sdo_req_queue, sdo_client); 98 | } 99 | 100 | ARC_PROTOTYPE(sdo_req) 101 | 102 | #endif /* SDO_REQ_H_ */ 103 | 104 | -------------------------------------------------------------------------------- /inc/canopen/sdo_req_enums.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef SDO_REQ_ENUMS_H_ 17 | #define SDO_REQ_ENUMS_H_ 18 | 19 | enum sdo_req_type { 20 | SDO_REQ_UPLOAD = 1, 21 | SDO_REQ_DOWNLOAD 22 | }; 23 | 24 | enum sdo_req_status { 25 | SDO_REQ_PENDING = 0, 26 | SDO_REQ_OK, 27 | SDO_REQ_LOCAL_ABORT, 28 | SDO_REQ_REMOTE_ABORT, 29 | SDO_REQ_CANCELLED, 30 | SDO_REQ_NOMEM, 31 | }; 32 | 33 | #endif /* SDO_REQ_ENUMS_H_ */ 34 | -------------------------------------------------------------------------------- /inc/canopen/sdo_srv.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef SDO_SRV_H_ 17 | #define SDO_SRV_H_ 18 | 19 | #include "canopen/sdo.h" 20 | #include "sock.h" 21 | #include "vector.h" 22 | #include "canopen/sdo_req_enums.h" 23 | 24 | struct sdo_srv; 25 | struct can_frame; 26 | 27 | enum sdo_srv_comm_state { 28 | SDO_SRV_COMM_INIT_REQ = 0, 29 | SDO_SRV_COMM_DL_SEG_REQ, 30 | SDO_SRV_COMM_UL_SEG_REQ 31 | }; 32 | 33 | typedef int (*sdo_srv_fn)(struct sdo_srv* srv); 34 | 35 | struct sdo_srv { 36 | struct sock sock; 37 | unsigned int nodeid; 38 | enum sdo_req_type req_type; 39 | enum sdo_srv_comm_state comm_state; 40 | struct vector buffer; 41 | size_t pos; 42 | sdo_srv_fn on_init; 43 | sdo_srv_fn on_done; 44 | int index, subindex; 45 | int is_toggled; 46 | enum sdo_req_status status; 47 | enum sdo_abort_code abort_code; 48 | }; 49 | 50 | int sdo_srv_init(struct sdo_srv* self, const struct sock* sock, int nodeid, 51 | sdo_srv_fn on_init, sdo_srv_fn on_done); 52 | 53 | int sdo_srv_feed(struct sdo_srv* self, const struct can_frame* cf); 54 | void sdo_srv_destroy(struct sdo_srv* self); 55 | 56 | int sdo_srv_abort(struct sdo_srv* self, enum sdo_abort_code code); 57 | 58 | #endif /* SDO_SRV_H_ */ 59 | -------------------------------------------------------------------------------- /inc/canopen/sdo_sync.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef SDO_SYNC_H_ 17 | #define SDO_SYNC_H_ 18 | 19 | #include "sdo_req.h" 20 | 21 | struct sdo_req* sdo_sync_read(int nodeid, int index, int subindex); 22 | int sdo_sync_write(int nodeid, struct sdo_req_info* info); 23 | 24 | int64_t sdo_sync_read_i64(int nodeid, int index, int subindex); 25 | uint64_t sdo_sync_read_u64(int nodeid, int index, int subindex); 26 | int32_t sdo_sync_read_i32(int nodeid, int index, int subindex); 27 | uint32_t sdo_sync_read_u32(int nodeid, int index, int subindex); 28 | int16_t sdo_sync_read_i16(int nodeid, int index, int subindex); 29 | uint16_t sdo_sync_read_u16(int nodeid, int index, int subindex); 30 | int8_t sdo_sync_read_i8(int nodeid, int index, int subindex); 31 | uint8_t sdo_sync_read_u8(int nodeid, int index, int subindex); 32 | 33 | int sdo_sync_write_i64(int nodeid, struct sdo_req_info* info, int64_t value); 34 | int sdo_sync_write_u64(int nodeid, struct sdo_req_info* info, uint64_t value); 35 | int sdo_sync_write_i32(int nodeid, struct sdo_req_info* info, int32_t value); 36 | int sdo_sync_write_u32(int nodeid, struct sdo_req_info* info, uint32_t value); 37 | int sdo_sync_write_i16(int nodeid, struct sdo_req_info* info, int16_t value); 38 | int sdo_sync_write_u16(int nodeid, struct sdo_req_info* info, uint16_t value); 39 | int sdo_sync_write_i8(int nodeid, struct sdo_req_info* info, int8_t value); 40 | int sdo_sync_write_u8(int nodeid, struct sdo_req_info* info, uint8_t value); 41 | 42 | #endif /* SDO_SYNC_H_ */ 43 | -------------------------------------------------------------------------------- /inc/canopen/types.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef _CANOPEN_TYPES_H 17 | #define _CANOPEN_TYPES_H 18 | 19 | #include 20 | #include 21 | #include "canopen/byteorder.h" 22 | 23 | #define CANOPEN_TYPES \ 24 | X(UNKNOWN, 0x00, 0) \ 25 | X(BOOLEAN, 0x01, 1) \ 26 | X(INTEGER8, 0x02, 1) \ 27 | X(INTEGER16, 0x03, 2) \ 28 | X(INTEGER32, 0x04, 4) \ 29 | X(UNSIGNED8, 0x05, 1) \ 30 | X(UNSIGNED16, 0x06, 2) \ 31 | X(UNSIGNED32, 0x07, 4) \ 32 | X(REAL32, 0x08, 4) \ 33 | X(VISIBLE_STRING, 0x09, 0) \ 34 | X(OCTET_STRING, 0x0A, 0) \ 35 | X(UNICODE_STRING, 0x0B, 0) \ 36 | X(TIME_OF_DAY, 0x0C, 6) \ 37 | X(TIME_DIFFERENCE, 0x0D, 6) \ 38 | X(DOMAIN, 0x0F, 0) \ 39 | X(INTEGER24, 0x10, 3) \ 40 | X(REAL64, 0x11, 8) \ 41 | X(INTEGER40, 0x12, 5) \ 42 | X(INTEGER48, 0x13, 6) \ 43 | X(INTEGER56, 0x14, 7) \ 44 | X(INTEGER64, 0x15, 8) \ 45 | X(UNSIGNED24, 0x16, 3) \ 46 | X(UNSIGNED40, 0x18, 5) \ 47 | X(UNSIGNED48, 0x19, 6) \ 48 | X(UNSIGNED56, 0x1A, 7) \ 49 | X(UNSIGNED64, 0x1B, 8) \ 50 | X(PDO_COMMUNICATION_PARAMETER, 0x20, 0) \ 51 | X(PDO_MAPPING, 0x21, 0) \ 52 | X(SDO_PARAMETER, 0x22, 0) \ 53 | X(IDENTITY, 0x23, 0) 54 | 55 | enum canopen_type { 56 | #define X(name, number, ...) CANOPEN_ ## name = number, 57 | CANOPEN_TYPES 58 | #undef X 59 | }; 60 | 61 | size_t canopen_type_size(enum canopen_type type); 62 | 63 | int canopen_type_is_signed_integer(enum canopen_type type); 64 | int canopen_type_is_unsigned_integer(enum canopen_type type); 65 | 66 | static inline int canopen_type_is_real(enum canopen_type type) 67 | { 68 | return type == CANOPEN_REAL32 || type == CANOPEN_REAL64; 69 | } 70 | 71 | static inline int canopen_type_is_integer(enum canopen_type type) 72 | { 73 | return canopen_type_is_signed_integer(type) 74 | || canopen_type_is_unsigned_integer(type); 75 | } 76 | 77 | static inline int canopen_type_is_number(enum canopen_type type) 78 | { 79 | return canopen_type_is_integer(type) 80 | || canopen_type_is_real(type); 81 | } 82 | 83 | static inline int canopen_type_is_string(enum canopen_type type) 84 | { 85 | return type == CANOPEN_VISIBLE_STRING 86 | || type == CANOPEN_OCTET_STRING 87 | || type == CANOPEN_UNICODE_STRING; 88 | } 89 | 90 | const char* canopen_type_to_string(enum canopen_type type); 91 | enum canopen_type canopen_type_from_string(const char* str); 92 | 93 | #endif /* _CANOPEN_TYPES_H */ 94 | -------------------------------------------------------------------------------- /inc/canopen_info.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef CANOPEN_INFO_H_ 17 | #define CANOPEN_INFO_H_ 18 | 19 | #include 20 | #include 21 | 22 | struct canopen_info { 23 | uint32_t is_active; 24 | uint32_t device_type; 25 | uint32_t last_seen; 26 | uint32_t error_register; 27 | uint32_t skipped_heartbeats; 28 | char name[64]; 29 | char hw_version[64]; 30 | char sw_version[64]; 31 | }; 32 | 33 | extern struct canopen_info* canopen_info_; 34 | 35 | static inline struct canopen_info* canopen_info_get(int nodeid) 36 | { 37 | assert(1 <= nodeid && nodeid <= 127); 38 | return &canopen_info_[nodeid - 1]; 39 | } 40 | 41 | int canopen_info_init(const char* iface); 42 | void canopen_info_cleanup(void); 43 | 44 | #endif /* CANOPEN_INFO_H_ */ 45 | -------------------------------------------------------------------------------- /inc/cfg.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2018, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef CFG_H_ 17 | #define CFG_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #define CFG__PARAMETERS \ 24 | X(string, iface, "") \ 25 | X(uint, n_workers, 4) \ 26 | X(uint, worker_stack_size, 0) \ 27 | X(uint, job_queue_length, 256) \ 28 | X(uint, sdo_queue_length, 1024) \ 29 | X(uint, rest_port, 9191) \ 30 | X(bool, be_strict, 0) \ 31 | X(bool, use_tcp, 0) \ 32 | X(uint, heartbeat_period, 0 /* ms */) \ 33 | X(uint, heartbeat_timeout, 0 /* ms */) \ 34 | X(uint, n_timeouts_max, 2) \ 35 | X(uint, range_start, 0) \ 36 | X(uint, range_stop, 0) \ 37 | X(uint, sync_interval, 0 /* us */) \ 38 | X(uint, trace_buffer_size, 0) \ 39 | X(string, trace_dump_path, "/var/log/canopen") \ 40 | X(bool, enable_bootup_trace, 0) \ 41 | X(bool, enable_incident_trace, 0) \ 42 | 43 | #define CFG__NODE_PARAMETERS \ 44 | X(bool, has_zero_guard_status, 0) \ 45 | X(bool, ignore_sdo_multiplexer, 1) \ 46 | X(bool, send_full_sdo_frame, 0) \ 47 | X(uint, heartbeat_period, 10000 /* ms */) \ 48 | X(uint, heartbeat_timeout, 1000 /* ms */) \ 49 | X(uint, n_timeouts_max, 0) \ 50 | X(bool, enable_node_guarding, 1) \ 51 | 52 | #define CFG__DEFINE_bool(name) int name 53 | #define CFG__DEFINE_uint(name) uint64_t name 54 | #define CFG__DEFINE_int(name) int64_t name 55 | #define CFG__DEFINE_string(name) char name[256] 56 | #define CFG__DEFINE_(type, ...) CFG__DEFINE_ ## type(__VA_ARGS__) 57 | 58 | #define CFG__SET_bool(name, value) name = value 59 | #define CFG__SET_uint(name, value) name = value 60 | #define CFG__SET_int(name, value) name = value 61 | #define CFG__SET_string(name, value) strlcpy(name, value, 256) 62 | #define CFG__SET_(type, ...) CFG__SET_ ## type(__VA_ARGS__) 63 | 64 | #define CFG__STRTO_bool(value) (strcmp(value, "yes") == 0) 65 | #define CFG__STRTO_uint(value) strtoll(value, NULL, 0) 66 | #define CFG__STRTO_int(value) strtoull(value, NULL, 0) 67 | #define CFG__STRTO_string(value) value 68 | #define CFG__STRTO_(type, ...) CFG__STRTO_ ## type(__VA_ARGS__) 69 | 70 | struct cfg { 71 | #define X(type, name, default_) CFG__DEFINE_(type, name); 72 | CFG__PARAMETERS 73 | #undef X 74 | 75 | struct { 76 | #define X(type, name, default_) CFG__DEFINE_(type, name); 77 | CFG__NODE_PARAMETERS 78 | #undef X 79 | } node[128]; 80 | }; 81 | 82 | extern struct cfg cfg; 83 | 84 | void cfg_load_defaults(void); 85 | void cfg_load_globals(void); 86 | 87 | int cfg_load_file(const char* path); 88 | void cfg_unload_file(void); 89 | 90 | void cfg_load_node(int id); 91 | 92 | const char* cfg__file_read(int nodeid, const char* key); 93 | 94 | #endif /* CFG_H_ */ 95 | -------------------------------------------------------------------------------- /inc/co_atomic.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef CO_ATOMIC_H_ 17 | #define CO_ATOMIC_H_ 18 | 19 | #ifdef __ATOMIC_SEQ_CST 20 | #define HAVE_NEW_ATOMICS 21 | #endif 22 | 23 | #ifdef HAVE_NEW_ATOMICS 24 | 25 | #define co_atomic_cas(ptr, expected, desired) \ 26 | ({ \ 27 | __typeof__(expected) expected_ = expected; \ 28 | __atomic_compare_exchange_n(ptr, &expected_, desired, 0, \ 29 | __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); \ 30 | }) 31 | 32 | #define co_atomic_load(ptr) __atomic_load_n(ptr, __ATOMIC_SEQ_CST) 33 | 34 | #define co_atomic_store(ptr, value) \ 35 | __atomic_store_n(ptr, value, __ATOMIC_SEQ_CST) 36 | 37 | #define co_atomic_sub_fetch(ptr, value) \ 38 | __atomic_sub_fetch(ptr, value, __ATOMIC_SEQ_CST) 39 | 40 | #define co_atomic_add_fetch(ptr, value) \ 41 | __atomic_add_fetch(ptr, value, __ATOMIC_SEQ_CST) 42 | 43 | #else 44 | 45 | #define co_atomic_cas(ptr, expected, desired) \ 46 | __sync_bool_compare_and_swap(ptr, expected, desired) 47 | 48 | #define co_atomic_load(ptr) \ 49 | ({ \ 50 | __sync_synchronize(); \ 51 | __typeof__(*ptr) result = (*ptr); \ 52 | __sync_synchronize(); \ 53 | result; \ 54 | }) 55 | 56 | #define co_atomic_store(ptr, value) \ 57 | ({ \ 58 | __sync_synchronize(); \ 59 | *ptr = value; \ 60 | __sync_synchronize(); \ 61 | }) 62 | 63 | #define co_atomic_sub_fetch(ptr, value) __sync_sub_and_fetch(ptr, value) 64 | #define co_atomic_add_fetch(ptr, value) __sync_add_and_fetch(ptr, value) 65 | 66 | #endif /* HAVE_NEW_ATOMICS */ 67 | 68 | #undef HAVE_NEW_ATOMICS 69 | 70 | #endif /* CO_ATOMIC_H_ */ 71 | -------------------------------------------------------------------------------- /inc/compat/atomic_compat.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef ATOMIC_COMPAT_H_ 17 | #define ATOMIC_COMPAT_H_ 18 | 19 | #ifndef __ATOMIC_SEQ_CST 20 | #define __ATOMIC_SEQ_CST 21 | 22 | #define __atomic_compare_exchange_n(ptr, expected_ptr, desired, ...) \ 23 | __sync_bool_compare_and_swap(ptr, *(expected_ptr), desired) 24 | 25 | #define __atomic_load_n(ptr, ...) \ 26 | ({ \ 27 | __sync_synchronize(); \ 28 | __typeof__(*ptr) result = *(ptr); \ 29 | __sync_synchronize(); \ 30 | result; \ 31 | }) 32 | 33 | #define __atomic_store_n(ptr, val, ...) \ 34 | ({ \ 35 | __sync_synchronize(); \ 36 | *(ptr) = val; \ 37 | __sync_synchronize(); \ 38 | }) 39 | 40 | 41 | #define __atomic_sub_fetch(ptr, value, ...) \ 42 | __sync_sub_and_fetch(ptr, value) 43 | 44 | #define __atomic_add_fetch(ptr, value, ...) \ 45 | __sync_add_and_fetch(ptr, value) 46 | #endif 47 | 48 | #endif /* ATOMIC_COMPAT_H_ */ 49 | -------------------------------------------------------------------------------- /inc/compat/plog.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef PLOG_H_ 17 | #define PLOG_H_ 18 | 19 | #include 20 | 21 | #define LOG_ERROR LOG_ERR 22 | #define plog(prio, fmt, ...) syslog(prio, fmt, ## __VA_ARGS__) 23 | 24 | #endif /* PLOG_H_ */ 25 | -------------------------------------------------------------------------------- /inc/compat/prioq.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef PRIOQ_H_ 17 | #define PRIOQ_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | struct prioq_elem { 25 | unsigned long priority; 26 | unsigned long sequence_; 27 | void* data; 28 | }; 29 | 30 | struct prioq { 31 | size_t size; 32 | unsigned long sequence; 33 | unsigned long index; 34 | pthread_mutex_t mutex; 35 | pthread_cond_t suspend_cond; 36 | struct prioq_elem* head; 37 | }; 38 | 39 | int prioq_init(struct prioq* self, size_t size); 40 | 41 | void prioq_destroy(struct prioq* self); 42 | 43 | void prioq_clear(struct prioq* self); 44 | 45 | int prioq_copy(struct prioq* dst, struct prioq* src); 46 | int prioq_move(struct prioq* dst, struct prioq* src); 47 | 48 | int prioq_grow(struct prioq* self, size_t size); 49 | 50 | int prioq_insert(struct prioq* self, unsigned long priority, void* data); 51 | 52 | int prioq_pop(struct prioq* self, struct prioq_elem* elem, int timeout); 53 | 54 | static inline unsigned long prioq__parent(unsigned long index) 55 | { 56 | return (index - 1) >> 1; 57 | } 58 | 59 | static inline unsigned long prioq__lchild(unsigned long index) 60 | { 61 | return (index << 1) + 1; 62 | } 63 | 64 | static inline unsigned long prioq__rchild(unsigned long index) 65 | { 66 | return (index << 1) + 2; 67 | } 68 | 69 | static inline void prioq__swap(struct prioq_elem* a, struct prioq_elem* b) 70 | { 71 | struct prioq_elem tmp = *a; 72 | *a = *b; 73 | *b = tmp; 74 | } 75 | 76 | static inline void prioq__lock(struct prioq* self) 77 | { 78 | pthread_mutex_lock(&self->mutex); 79 | } 80 | 81 | static inline void prioq__unlock(struct prioq* self) 82 | { 83 | pthread_mutex_unlock(&self->mutex); 84 | } 85 | 86 | int prioq__is_seq_lt(unsigned long a, unsigned long b); 87 | 88 | unsigned long prioq__get_smaller_child(struct prioq* self, unsigned long index); 89 | void prioq__bubble_up(struct prioq* self, unsigned long index); 90 | void prioq__sink_down(struct prioq* self, unsigned long index); 91 | 92 | #endif /* PRIOQ_H_ */ 93 | 94 | -------------------------------------------------------------------------------- /inc/compat/thread-utils.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef THREAD_UTILS_H_ 17 | #define THREAD_UTILS_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "time-utils.h" 24 | 25 | #define block_thread_while_empty(cond, mutex, timeout, cmp) \ 26 | ({ \ 27 | struct timespec deadline_; \ 28 | int rc_ = 0; \ 29 | if ((timeout) == 0) { \ 30 | if (cmp) { \ 31 | errno = EAGAIN; \ 32 | rc_ = -1; \ 33 | } \ 34 | } else if ((timeout) < 0) { \ 35 | while (cmp) \ 36 | pthread_cond_wait((cond), (mutex)); \ 37 | } else { \ 38 | while (cmp) { \ 39 | clock_gettime(CLOCK_REALTIME, &deadline_); \ 40 | add_to_timespec(&deadline_, msec_to_nsec(timeout)); \ 41 | if (pthread_cond_timedwait((cond), (mutex), \ 42 | &deadline_) == ETIMEDOUT) \ 43 | if (cmp) { \ 44 | errno = ETIMEDOUT; \ 45 | rc_ = -1; \ 46 | break; \ 47 | } \ 48 | } \ 49 | } \ 50 | rc_; \ 51 | }) 52 | 53 | #endif /* THREAD_UTILS_H_ */ 54 | 55 | -------------------------------------------------------------------------------- /inc/conversions.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef CONVERSIONS_H_ 17 | #define CONVERSIONS_H_ 18 | 19 | #include 20 | #include "canopen/types.h" 21 | 22 | struct canopen_data { 23 | enum canopen_type type; 24 | void* data; 25 | size_t size; 26 | uint64_t value; 27 | int is_size_unknown; 28 | }; 29 | 30 | char* canopen_data_tostring(char* dst, size_t size, struct canopen_data* src); 31 | int canopen_data_fromstring(struct canopen_data* dst, 32 | enum canopen_type expected_type, const char* str); 33 | 34 | #endif /* CONVERSIONS_H_ */ 35 | -------------------------------------------------------------------------------- /inc/http.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef CANOPEN_HTTP_H_ 17 | #define CANOPEN_HTTP_H_ 18 | 19 | #define URL_INDEX_MAX 32 20 | #define URL_QUERY_INDEX_MAX 32 21 | 22 | #include 23 | 24 | enum http_method { 25 | HTTP_GET = 1, 26 | HTTP_PUT = 2, 27 | HTTP_OPTIONS = 4, 28 | }; 29 | 30 | struct http_url_query { 31 | char* key; 32 | char* value; 33 | }; 34 | 35 | struct http_req { 36 | enum http_method method; 37 | size_t header_length; 38 | size_t content_length; 39 | char* content_type; 40 | size_t url_index; 41 | char* url[URL_INDEX_MAX]; 42 | size_t url_query_index; 43 | struct http_url_query url_query[URL_QUERY_INDEX_MAX]; 44 | }; 45 | 46 | int http_req_parse(struct http_req* req, const char* head); 47 | void http_req_free(struct http_req* req); 48 | 49 | const char* http_req_query(struct http_req* req, const char* key); 50 | 51 | #endif /* CANOPEN_HTTP_H_ */ 52 | -------------------------------------------------------------------------------- /inc/ini_parser.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef INI_PARSER_H_INCLUDED_ 17 | #define INI_PARSER_H_INCLUDED_ 18 | 19 | #include 20 | #include "vector.h" 21 | 22 | struct ini_key_value { 23 | char* value; 24 | char key[0]; 25 | }; 26 | 27 | struct ini_section { 28 | struct vector kv; 29 | char section[0]; 30 | }; 31 | 32 | struct ini_file { 33 | struct vector section; 34 | }; 35 | 36 | int ini_parse(struct ini_file* file, FILE* stream); 37 | void ini_destroy(struct ini_file* file); 38 | 39 | const char* ini_find(const struct ini_file* file, const char* section, 40 | const char* key); 41 | const char* ini_find_key(const struct ini_section* file, const char* key); 42 | 43 | const struct ini_section* ini_find_section(const struct ini_file* file, 44 | const char* section); 45 | 46 | static inline size_t ini_get_length(const struct ini_file* ini) 47 | { 48 | return ini->section.index / sizeof(void*); 49 | } 50 | 51 | static inline const struct ini_section* 52 | ini_get_section(const struct ini_file* ini, size_t index) 53 | { 54 | struct ini_section** section = ini->section.data; 55 | return section[index]; 56 | } 57 | 58 | static inline size_t ini_get_section_length(const struct ini_section* section) 59 | { 60 | return section->kv.index / sizeof(void*); 61 | } 62 | 63 | #endif /* INI_PARSER_H_INCLUDED_ */ 64 | 65 | -------------------------------------------------------------------------------- /inc/legacy-driver.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef LEGACY_DRIVER_H_ 17 | #define LEGACY_DRIVER_H_ 18 | 19 | #include 20 | #include 21 | 22 | struct legacy_master_iface { 23 | int nodeid; 24 | int (*send_pdo)(int nodeid, int n, unsigned char* data, size_t size); 25 | int (*send_sdo)(int nodeid, int index, int subindex, 26 | unsigned char* data, size_t size); 27 | int (*request_sdo)(int nodeid, int index, int subindex); 28 | int (*set_node_state)(int nodeid, int state); 29 | }; 30 | 31 | void* legacy_master_iface_new(struct legacy_master_iface*); 32 | void legacy_master_iface_delete(void*); 33 | 34 | void* legacy_driver_manager_new(); 35 | void legacy_driver_manager_delete(void*); 36 | 37 | int legacy_driver_manager_create_handler(void* obj, const char* name, 38 | int profile_number, 39 | void* master_iface, 40 | void** driver_iface); 41 | 42 | int legacy_driver_delete_handler(void* obj, int profile_number, 43 | void* driver_interface); 44 | 45 | int legacy_driver_iface_initialize(void* iface); 46 | 47 | int legacy_driver_iface_process_emr(void* iface, int code, int reg, 48 | uint64_t manufacturer_error); 49 | 50 | int legacy_driver_iface_process_sdo(void* obj, int index, int subindex, 51 | unsigned char* data, size_t size); 52 | 53 | int legacy_driver_iface_process_pdo(void* obj, int n, const unsigned char* data, 54 | size_t size); 55 | 56 | int legacy_driver_iface_process_node_state(void* obj, int state); 57 | 58 | #endif /* LEGACY_DRIVER_H_ */ 59 | 60 | -------------------------------------------------------------------------------- /inc/net-util.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef NET_UTIL_H_ 17 | #define NET_UTIL_H_ 18 | 19 | #include 20 | #include 21 | 22 | struct can_frame; 23 | 24 | /* A wrapper around 'write' with ms timeout 25 | */ 26 | ssize_t net_write(int fd, const void* src, size_t size, int timeout); 27 | 28 | /* A wrapper around 'read' with ms timeout 29 | */ 30 | ssize_t net_read(int fd, void* dst, size_t size, int timeout); 31 | 32 | ssize_t net_write_frame(int fd, const struct can_frame* cf, int timeout); 33 | ssize_t net_read_frame(int fd, struct can_frame* cf, int timeout); 34 | ssize_t net_filtered_read_frame(int fd, struct can_frame* cf, int timeout, 35 | uint32_t can_id); 36 | 37 | /* Make socket non-blocking 38 | */ 39 | int net_dont_block(int fd); 40 | int net_fix_sndbuf(int fd); 41 | int net_reuse_addr(int fd); 42 | int net_dont_delay(int fd); 43 | 44 | #endif /* NET_UTIL_H_ */ 45 | -------------------------------------------------------------------------------- /inc/profiling.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef PROFILING_H_ 17 | #define PROFILING_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | extern int profiling_is_active_; 24 | extern uint64_t profiling_start_time_; 25 | 26 | int profiling_getenv(void); 27 | 28 | static inline int profiling_is_active(void) 29 | { 30 | if (profiling_is_active_ < 0) 31 | profiling_is_active_ = profiling_getenv(); 32 | return profiling_is_active_; 33 | } 34 | 35 | static inline void profiling_reset(void) 36 | { 37 | profiling_start_time_ = gettime_us(CLOCK_MONOTONIC); 38 | } 39 | 40 | #define tprintf(fmt, ...) \ 41 | printf("%07"PRIu64"\t" fmt, gettime_us(CLOCK_MONOTONIC) - profiling_start_time_, \ 42 | ## __VA_ARGS__) 43 | 44 | #define profile(fmt, ...) \ 45 | if (profiling_is_active()) tprintf(fmt, ## __VA_ARGS__) 46 | 47 | #endif /* PROFILING_H_ */ 48 | -------------------------------------------------------------------------------- /inc/rest.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef CANOPEN_REST_H_ 17 | #define CANOPEN_REST_H_ 18 | 19 | #include 20 | #include 21 | #include "http.h" 22 | #include "vector.h" 23 | 24 | struct rest_reply_data { 25 | const char* status_code; 26 | const char* content_type; 27 | ssize_t content_length; 28 | const void* content; 29 | }; 30 | 31 | enum rest_client_state { 32 | REST_CLIENT_START = 0, 33 | REST_CLIENT_CONTENT, 34 | REST_CLIENT_SERVICING, 35 | REST_CLIENT_DISCONNECTED, 36 | REST_CLIENT_DONE 37 | }; 38 | 39 | struct rest_client { 40 | int ref; 41 | enum rest_client_state state; 42 | struct vector buffer; 43 | struct http_req req; 44 | FILE* output; 45 | }; 46 | 47 | typedef void (*rest_fn)(struct rest_client* client, const void* content); 48 | 49 | struct rest_service { 50 | SLIST_ENTRY(rest_service) links; 51 | enum http_method method; 52 | const char* path; 53 | rest_fn fn; 54 | }; 55 | 56 | int rest_init(int port); 57 | void rest_cleanup(); 58 | 59 | int rest_register_service(enum http_method method, const char* path, 60 | rest_fn fn); 61 | void rest_reply(FILE* output, struct rest_reply_data* data); 62 | void rest_reply_header(FILE* output, struct rest_reply_data* data); 63 | 64 | void rest_client_ref(struct rest_client* self); 65 | int rest_client_unref(struct rest_client* self); 66 | 67 | int rest__service_is_match(const struct rest_service* service, 68 | const struct http_req* req); 69 | struct rest_service* rest__find_service(const struct http_req* req); 70 | void rest__init_service_list(void); 71 | 72 | int rest__open_server(int port); 73 | int rest__read(struct vector* buffer, int fd); 74 | 75 | #endif /* CANOPEN_REST_H_ */ 76 | -------------------------------------------------------------------------------- /inc/sdo-rest.h: -------------------------------------------------------------------------------- 1 | #ifndef SDO_REST_H_ 2 | #define SDO_REST_H_ 3 | 4 | void sdo_rest_service(struct rest_client* client, const void* content); 5 | 6 | #endif /* SDO_REST_H_ */ 7 | -------------------------------------------------------------------------------- /inc/sock.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef CAN_SOCK_H_ 17 | #define CAN_SOCK_H_ 18 | 19 | #include 20 | 21 | struct can_frame; 22 | struct tracebuffer; 23 | 24 | enum sock_type { 25 | SOCK_TYPE_UNSPEC = 0, 26 | SOCK_TYPE_CAN = 1, 27 | SOCK_TYPE_TCP = 2, 28 | }; 29 | 30 | struct sock { 31 | enum sock_type type; 32 | int fd; 33 | struct tracebuffer* tb; 34 | }; 35 | 36 | static inline void sock_init(struct sock* sock, enum sock_type type, int fd, 37 | struct tracebuffer* tb) 38 | { 39 | sock->type = type; 40 | sock->fd = fd; 41 | sock->tb = tb; 42 | } 43 | 44 | int sock_open(struct sock* sock, enum sock_type type, const char* addr, 45 | struct tracebuffer* tb); 46 | 47 | ssize_t sock_send(const struct sock* sock, struct can_frame* cf, int flags); 48 | int sock_timed_send(const struct sock* sock, struct can_frame* cf, int timeout); 49 | 50 | ssize_t sock_recv(const struct sock* sock, struct can_frame* cf, int flags); 51 | int sock_timed_recv(const struct sock* sock, struct can_frame* cf, int timeout); 52 | 53 | static inline int sock_close(struct sock* sock) 54 | { 55 | return close(sock->fd); 56 | } 57 | 58 | #endif /* CAN_SOCK_H_ */ 59 | -------------------------------------------------------------------------------- /inc/socketcan.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef _CANOPEN_SOCKETCAN_H 17 | #define _CANOPEN_SOCKETCAN_H 18 | 19 | #include 20 | #include 21 | 22 | #define CANOPEN_SLAVE_FILTER_LENGTH 9 23 | #define CANOPEN_MASTER_FILTER_LENGTH 10 24 | 25 | void socketcan_make_slave_filters(struct can_filter* filters, int nodeid); 26 | void socketcan_make_master_filters(struct can_filter* filters, int nodeid); 27 | int socketcan_open(const char* iface); 28 | int socketcan_apply_filters(int fd, struct can_filter* filters, int n); 29 | 30 | int socketcan_open_slave(const char* iface, int nodeid); 31 | int socketcan_open_master(const char* iface, int nodeid); 32 | 33 | #endif /* _CANOPEN_SOCKETCAN_H */ 34 | 35 | -------------------------------------------------------------------------------- /inc/stream.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef STREAM_H_ 17 | #define STREAM_H_ 18 | 19 | #include 20 | 21 | FILE* stream_open(int fd, const char* mode); 22 | 23 | #endif /* STREAM_H_ */ 24 | 25 | -------------------------------------------------------------------------------- /inc/string-utils.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef STRING_UTILS_H_ 17 | #define STRING_UTILS_H_ 18 | 19 | #include 20 | #include 21 | 22 | #define SPACE_CHARACTER " \f\n\r\t\v" 23 | 24 | static inline char* string_trim_left(char* str) 25 | { 26 | return str + strspn(str, SPACE_CHARACTER); 27 | } 28 | 29 | char* string_trim_right(char* str); 30 | 31 | static inline char* string_trim(char* str) 32 | { 33 | return string_trim_right(string_trim_left(str)); 34 | } 35 | 36 | static inline int string_is_empty(const char* str) 37 | { 38 | return *str == '\0'; 39 | } 40 | 41 | char* string_keep_if(int (*cond)(int c), char* str); 42 | 43 | static inline int string_begins_with(const char* cmp, const char* str) 44 | { 45 | return strncmp(str, cmp, strlen(cmp)) == 0; 46 | } 47 | 48 | int string_ends_with(const char* cmp, const char* str); 49 | 50 | char* string_tolower(char* str); 51 | 52 | char* string_replace_char(char m, char r, char* str); 53 | 54 | #ifndef IN_STRING_UTILS_C_ 55 | #undef SPACE_CHARACTER 56 | #endif 57 | 58 | #endif /* STRING_UTILS_H_ */ 59 | -------------------------------------------------------------------------------- /inc/time-utils.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef TIME_UTILS_H_ 17 | #define TIME_UTILS_H_ 18 | 19 | #include 20 | #include 21 | 22 | #define nSEC_IN_SEC 1000000000ULL 23 | 24 | #define msec_to_nsec(t) ((t) * 1000000ULL) 25 | 26 | static inline uint64_t timespec_to_ns(struct timespec* ts) 27 | { 28 | return (uint64_t)ts->tv_sec * nSEC_IN_SEC + (uint64_t)ts->tv_nsec; 29 | } 30 | 31 | static inline struct timespec ns_to_timespec(uint64_t ns) 32 | { 33 | struct timespec ts; 34 | ts.tv_nsec = ns % nSEC_IN_SEC; 35 | ts.tv_sec = ns / nSEC_IN_SEC; 36 | return ts; 37 | } 38 | 39 | static inline void add_to_timespec(struct timespec* ts, uint64_t addition) 40 | { 41 | *ts = ns_to_timespec(timespec_to_ns(ts) + addition); 42 | } 43 | 44 | static inline uint64_t gettime_ns(clockid_t clk_id) 45 | { 46 | struct timespec ts = { 0 }; 47 | clock_gettime(clk_id, &ts); 48 | return timespec_to_ns(&ts); 49 | } 50 | 51 | static inline uint64_t gettime_us(clockid_t clk_id) 52 | { 53 | return gettime_ns(clk_id) / 1000ULL; 54 | } 55 | 56 | static inline uint64_t gettime_ms(clockid_t clk_id) 57 | { 58 | return gettime_us(clk_id) / 1000ULL; 59 | } 60 | 61 | static inline uint64_t gettime_s(clockid_t clk_id) 62 | { 63 | return gettime_ms(clk_id) / 1000ULL; 64 | } 65 | 66 | #endif /* TIME_UTILS_H_ */ 67 | -------------------------------------------------------------------------------- /inc/trace-buffer.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2018, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef _TRACE_BUFFER_H 17 | #define _TRACE_BUFFER_H 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "socketcan.h" 24 | 25 | struct tb_frame { 26 | uint64_t timestamp; 27 | struct can_frame cf; 28 | }; 29 | 30 | struct tracebuffer { 31 | size_t length; 32 | size_t index; 33 | size_t count; 34 | int is_blocked; 35 | struct tb_frame* data; 36 | }; 37 | 38 | int tb_init(struct tracebuffer* self, size_t size); 39 | void tb_destroy(struct tracebuffer* self); 40 | void tb_append(struct tracebuffer* self, const struct can_frame* frame); 41 | void tb_dump(struct tracebuffer* self, FILE* stream); 42 | 43 | #endif /* _TRACE_BUFFER_H */ 44 | -------------------------------------------------------------------------------- /inc/type-macros.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef TYPE_MACROS_H_ 17 | #define TYPE_MACROS_H_ 18 | 19 | #include 20 | 21 | #define container_of(ptr, type, member) \ 22 | ({ \ 23 | const typeof( ((type *)0)->member ) *__mptr = (ptr); \ 24 | (type *)( (char *)__mptr - offsetof(type,member) ); \ 25 | }) 26 | 27 | #endif /* TYPE_MACROS_H_ */ 28 | -------------------------------------------------------------------------------- /inc/userdata.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2018, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef CANOPEN_USERDATA_ 17 | #define CANOPEN_USERDATA_ 18 | 19 | #ifdef NO_MAREL_CODE 20 | struct userdata { }; 21 | #define userdata_init(...) (0) 22 | #define userdata_destroy(...) 23 | #define userdata_reload(...) 24 | #define userdata_set_missing(...) 25 | #define userdata_clear_missing(...) 26 | #define userdata_check_missing(...) 27 | #else 28 | #include 29 | 30 | struct pst_set; 31 | 32 | struct userdata { 33 | struct pst_set* pst; 34 | int pin; 35 | uint64_t required[2]; 36 | uint64_t missing[2]; 37 | }; 38 | 39 | int userdata_init(struct userdata* self); 40 | void userdata_destroy(struct userdata* self); 41 | 42 | void userdata_reload(struct userdata* self); 43 | 44 | void userdata_set_missing(struct userdata* self, unsigned int id); 45 | void userdata_clear_missing(struct userdata* self, unsigned int id); 46 | 47 | void userdata_check_missing(struct userdata* self); 48 | #endif 49 | 50 | #endif /* CANOPEN_USERDATA_ */ 51 | -------------------------------------------------------------------------------- /inc/vector.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef _VECTOR_H_INCLUDED 17 | #define _VECTOR_H_INCLUDED 18 | 19 | #include 20 | #include 21 | 22 | struct vector { 23 | void* data; 24 | size_t index; 25 | size_t size; 26 | }; 27 | 28 | static inline int vector_init(struct vector* self, size_t size) 29 | { 30 | memset(self, 0, sizeof(*self)); 31 | self->size = size; 32 | self->data = malloc(size); 33 | return self->data ? 0 : -1; 34 | } 35 | 36 | static inline void vector_destroy(struct vector* self) 37 | { 38 | free(self->data); 39 | self->data = NULL; 40 | } 41 | 42 | static inline int vector__grow(struct vector* self, size_t size) 43 | { 44 | void* data = realloc(self->data, size); 45 | if (!data) 46 | return -1; 47 | self->data = data; 48 | self->size = size; 49 | return 1; 50 | } 51 | 52 | static inline int vector_reserve(struct vector* self, size_t size) 53 | { 54 | return self->size < size ? vector__grow(self, size * 2) : 0; 55 | } 56 | 57 | static inline int vector_append(struct vector* self, const void* data, 58 | size_t size) 59 | { 60 | if (vector_reserve(self, self->index + size) < 0) 61 | return -1; 62 | memcpy((char*)self->data + self->index, data, size); 63 | self->index += size; 64 | return 0; 65 | } 66 | 67 | static inline void vector_clear(struct vector* self) 68 | { 69 | self->index = 0; 70 | } 71 | 72 | static inline int vector_assign(struct vector* self, const void* data, 73 | size_t size) 74 | { 75 | vector_clear(self); 76 | if (vector_reserve(self, size) < 0) 77 | return -1; 78 | memcpy(self->data, data, size); 79 | self->index = size; 80 | return 0; 81 | } 82 | 83 | static inline int vector_fill(struct vector* self, int c, size_t size) 84 | { 85 | vector_clear(self); 86 | if (vector_reserve(self, size) < 0) 87 | return -1; 88 | memset(self->data, c, size); 89 | self->index = size; 90 | return 0; 91 | } 92 | 93 | static inline int vector_copy(struct vector* dst, const struct vector* src) 94 | { 95 | return vector_assign(dst, src->data, src->index); 96 | } 97 | 98 | #endif /* _VECTOR_H_INCLUDED */ 99 | -------------------------------------------------------------------------------- /inc/vnode.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef CANOPEN_VNODE_H_ 17 | #define CANOPEN_VNODE_H_ 18 | 19 | #include "sock.h" 20 | 21 | struct vnode; 22 | 23 | struct vnode* co_vnode_new(enum sock_type type, const char* iface, 24 | const char* config_path, int nodeid); 25 | void co_vnode_destroy(struct vnode* self); 26 | 27 | #endif /* CANOPEN_VNODE_H_ */ 28 | -------------------------------------------------------------------------------- /packaging: -------------------------------------------------------------------------------- 1 | Package: canopen2 2 | Version: 0.0.0 3 | Description: CANopen master version 2. 4 | 5 | Destination: target 6 | #BuildDepends: marel-env-host, libmloop-crossdevelopment, libmloop-eloop-crossdevelopment, appbase-crossdevelopment, libplog-crossdevelopment 7 | Depends: libmloop, libmloop-eloop, appbase, libplog (>= 0.5.0), luasharedmemory, pst, digitaliopin 8 | 9 | Type: release 10 | Install: ##BUILDRELEASE##lib/libcanopen2.so usr/lib 11 | Install: ##BUILDRELEASE##bin/canopen-master usr/bin 12 | Install: ##BUILDRELEASE##bin/canbridge usr/bin 13 | Install: ##BUILDRELEASE##bin/canopen-dump usr/bin 14 | Install: ##BUILDRELEASE##bin/canopen-vnode usr/bin 15 | Install: canopen2.xml /var/marel/sharedmalloc/keys 16 | Install: vnodes /usr/share/canopen2 17 | Install: bin /usr 18 | Install: etc / 19 | 20 | PostInst: target.postinst 21 | PreRm: target.prerm 22 | 23 | Type: debug 24 | Install: ##BUILDDEBUG##lib/libcanopen2.so usr/lib/debug 25 | Install: ##BUILDDEBUG##bin/canopen-master usr/bin/debug 26 | Install: ##BUILDDEBUG##bin/canbridge usr/bin/debug 27 | Install: ##BUILDDEBUG##bin/canopen-dump usr/bin/debug 28 | Install: ##BUILDDEBUG##bin/canopen-vnode usr/bin/debug 29 | 30 | Destination: host 31 | #BuildDepends: marel-env-host, libmloop-crossdevelopment, appbase-crossdevelopment, libplog-crossdevelopment 32 | Depends: libmloop-crossdevelopment, libmloop-eloop-crossdevelopment, appbase-crossdevelopment, libplog-crossdevelopment (>= 0.5.0), luasharedmemory-crossdevelopment, pst-crossdevelopment, digitaliopin-crossdevelopment 33 | Type: development 34 | 35 | Install: ##BUILDRELEASE##lib/libcanopen2.so usr/lib 36 | Install: ##BUILDDEBUG##lib/libcanopen2.so usr/lib/debug 37 | Install: ##BUILDRELEASE##bin/canopen-master usr/bin 38 | Install: ##BUILDDEBUG##bin/canopen-master usr/bin/debug 39 | Install: ##BUILDRELEASE##bin/canbridge usr/bin 40 | Install: ##BUILDDEBUG##bin/canbridge usr/bin/debug 41 | Install: ##BUILDRELEASE##bin/canopen-dump usr/bin 42 | Install: ##BUILDDEBUG##bin/canopen-dump usr/bin/debug 43 | Install: ##BUILDRELEASE##bin/canopen-vnode usr/bin 44 | Install: ##BUILDDEBUG##bin/canopen-vnode usr/bin/debug 45 | Install: inc/canopen-driver.h usr/include 46 | Install: canopen2.xml /var/marel/sharedmalloc/keys 47 | Install: vnodes /usr/share/canopen2 48 | Install: bin /usr 49 | Install: etc / 50 | 51 | InstallTarget: ##BUILDRELEASE##lib/libcanopen2.so usr/lib 52 | InstallTarget: ##BUILDDEBUG##lib/libcanopen2.so usr/lib/debug 53 | InstallTarget: ##BUILDRELEASE##bin/canopen-master usr/bin 54 | InstallTarget: ##BUILDDEBUG##bin/canopen-master usr/bin/debug 55 | InstallTarget: ##BUILDRELEASE##bin/canbridge usr/bin 56 | InstallTarget: ##BUILDDEBUG##bin/canbridge usr/bin/debug 57 | InstallTarget: ##BUILDRELEASE##bin/canopen-dump usr/bin 58 | InstallTarget: ##BUILDDEBUG##bin/canopen-dump usr/bin/debug 59 | InstallTarget: ##BUILDRELEASE##bin/canopen-vnode usr/bin 60 | InstallTarget: ##BUILDDEBUG##bin/canopen-vnode usr/bin/debug 61 | InstallTarget: inc/canopen-driver.h usr/include 62 | InstallTarget: canopen2.xml /var/marel/sharedmalloc/keys 63 | InstallTarget: vnodes /usr/share/canopen2 64 | InstallTarget: bin /usr 65 | InstallTarget: etc / 66 | 67 | # vi: tw=0 68 | -------------------------------------------------------------------------------- /src/Driver.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2017, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "Driver.h" 21 | #include "CanIOHandlerInterface.h" 22 | 23 | #include 24 | 25 | using namespace std; 26 | 27 | int Driver::inst = 0; 28 | 29 | int 30 | Driver::getFirstProfile() 31 | { 32 | profilePos = 0; 33 | if (candriver_profile) 34 | return candriver_profile[0]; 35 | 36 | return -1; 37 | } 38 | 39 | int 40 | Driver::getNextProfile() 41 | { 42 | if (candriver_profile) 43 | { 44 | if (candriver_profile[profilePos] == 0) 45 | return -1; 46 | 47 | profilePos++; 48 | return candriver_profile[profilePos]; 49 | } 50 | return -1; 51 | } 52 | 53 | Driver::Driver( const char *filename ) 54 | { 55 | profilePos = 0; 56 | const char* dlsym_error; 57 | string tmp = filename; 58 | 59 | unsigned int pos = tmp.find_last_of('/') + 1; 60 | if (pos == tmp.npos) 61 | pos = 0; 62 | fileName = tmp.substr(pos); 63 | 64 | candriver_profile = NULL; 65 | candriver_version = NULL; 66 | candriver_set_parent = NULL; 67 | candriver_create_handler = NULL; 68 | candriver_delete_handler = NULL; 69 | candriver_delete_all_handlers = NULL; 70 | candriver_close_driver = NULL; 71 | 72 | handle = dlopen( filename, RTLD_NOW); 73 | dlsym_error = dlerror(); 74 | if (handle==NULL) 75 | throw std::runtime_error("Cannot load driver " + string(filename) 76 | + ": " + dlsym_error); 77 | 78 | candriver_profile = tryLoadSymbol(handle, "candriver_profile"); 79 | candriver_version = tryLoadSymbol(handle, "candriver_version"); 80 | candriver_set_parent = tryLoadSymbol(handle, "candriver_set_parent"); 81 | candriver_create_handler = tryLoadSymbol(handle, "candriver_create_handler"); 82 | candriver_delete_handler = tryLoadSymbol(handle, "candriver_delete_handler"); 83 | candriver_delete_all_handlers = tryLoadSymbol(handle, "candriver_delete_all_handlers"); 84 | 85 | candriver_close_driver = tryLoadSymbol(handle, "candriver_close_driver"); 86 | 87 | candriver_set_parent("canopen", appbase_get_instance()); 88 | } 89 | 90 | int 91 | Driver::createHandler( const char* nodeName, int32_t profileNr, 92 | CanIOMapDriver::CanMasterInterface *cmi, 93 | CanIOMapDriver::CanIOHandlerInterface** chi ) 94 | { 95 | if (candriver_create_handler) 96 | return candriver_create_handler( nodeName, profileNr, cmi, chi ); 97 | 98 | return -1; 99 | } 100 | 101 | int Driver::deleteHandler(CanIOMapDriver::CanIOHandlerInterface* chi ) 102 | { 103 | if (candriver_delete_handler) 104 | return candriver_delete_handler( chi ); 105 | 106 | return -1; 107 | } 108 | 109 | Driver::~Driver() 110 | { 111 | candriver_delete_all_handlers(); 112 | candriver_close_driver(); 113 | dlclose( handle ); 114 | fileName = ""; 115 | } 116 | -------------------------------------------------------------------------------- /src/byteorder.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "canopen/byteorder.h" 22 | 23 | #ifdef __powerpc__ 24 | 25 | void byteorder(void* dst, const void* src, size_t size) 26 | { 27 | uint8_t* d = (uint8_t*)dst; 28 | uint8_t* s = (uint8_t*)src; 29 | int i = (int)size; 30 | 31 | switch(size) 32 | { 33 | case 8: d[i - 1] = s[size - i]; --i; 34 | d[i - 1] = s[size - i]; --i; 35 | d[i - 1] = s[size - i]; --i; 36 | d[i - 1] = s[size - i]; --i; 37 | case 4: d[i - 1] = s[size - i]; --i; 38 | d[i - 1] = s[size - i]; --i; 39 | case 2: d[i - 1] = s[size - i]; --i; 40 | case 1: d[i - 1] = s[size - i]; 41 | case 0: break; 42 | default: 43 | abort(); 44 | } 45 | } 46 | 47 | void byteorder2(void* dst, const void* src, size_t dst_size, size_t src_size) 48 | { 49 | uint8_t* d = (uint8_t*)dst; 50 | uint8_t* s = (uint8_t*)src; 51 | int d_i = dst_size; 52 | int s_i = 0; 53 | 54 | assert(dst_size >= src_size); 55 | 56 | switch(src_size) 57 | { 58 | case 8: d[--d_i] = s[s_i++]; 59 | case 7: d[--d_i] = s[s_i++]; 60 | case 6: d[--d_i] = s[s_i++]; 61 | case 5: d[--d_i] = s[s_i++]; 62 | case 4: d[--d_i] = s[s_i++]; 63 | case 3: d[--d_i] = s[s_i++]; 64 | case 2: d[--d_i] = s[s_i++]; 65 | case 1: d[--d_i] = s[s_i++]; 66 | case 0: break; 67 | default: 68 | abort(); 69 | } 70 | } 71 | 72 | #else 73 | 74 | void byteorder(void* dst, const void* src, size_t size) 75 | { 76 | memcpy(dst, src, size); 77 | } 78 | 79 | void byteorder2(void* dst, const void* src, size_t dst_size, size_t src_size) 80 | { 81 | assert(dst_size >= src_size); 82 | memcpy(dst, src, src_size); 83 | } 84 | 85 | #endif 86 | 87 | -------------------------------------------------------------------------------- /src/canbridge.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "can-tcp.h" 23 | 24 | const char usage_[] = 25 | "Usage: canbridge [options] [interface]\n" 26 | "\n" 27 | "Options:\n" 28 | " -h, --help Get help.\n" 29 | " -L, --listen[=port] Listen on TCP port. Default 5555.\n" 30 | " -c, --connect=host[:port] Connect to TCP server. Default 5555.\n" 31 | " -C, --create Try to create a virtual CAN interface.\n" 32 | "\n" 33 | "Examples:\n" 34 | " $ canbridge can0 --listen=1234\n" 35 | " $ canbridge vcan0 --connect=127.0.0.1:1234\n" 36 | "\n"; 37 | 38 | static inline int print_usage(FILE* output, int status) 39 | { 40 | fprintf(output, "%s", usage_); 41 | return status; 42 | } 43 | 44 | 45 | static int try_create(const char* iface) 46 | { 47 | char buffer[256]; 48 | snprintf(buffer, sizeof(buffer), "ip link add dev %s type vcan", iface); 49 | buffer[sizeof(buffer) - 1] = '\0'; 50 | return system(buffer); 51 | } 52 | 53 | static int try_setup(const char* iface) 54 | { 55 | char buffer[256]; 56 | snprintf(buffer, sizeof(buffer), "ip link set %s up", iface); 57 | buffer[sizeof(buffer) - 1] = '\0'; 58 | return system(buffer); 59 | } 60 | 61 | static void on_signal_event(struct mloop_signal* sig, int signo) 62 | { 63 | (void)sig; 64 | (void)signo; 65 | mloop_exit(mloop_default()); 66 | } 67 | 68 | int main(int argc, char* argv[]) 69 | { 70 | static const struct option long_options[] = { 71 | { "help", no_argument, 0, 'h' }, 72 | { "listen", optional_argument, 0, 'L' }, 73 | { "connect", required_argument, 0, 'c' }, 74 | { "create", no_argument, 0, 'C' }, 75 | { 0, 0, 0, 0 } 76 | }; 77 | 78 | const char* listen_ = NULL; 79 | const char* connect_ = NULL; 80 | int create = 0; 81 | 82 | while (1) { 83 | int c = getopt_long(argc, argv, "hL::c:C", long_options, NULL); 84 | if (c < 0) 85 | break; 86 | 87 | switch (c) { 88 | case 'h': return print_usage(stdout, 0); 89 | case 'L': listen_ = optarg ? optarg : "5555"; break; 90 | case 'c': connect_ = optarg; break; 91 | case 'C': create = 1; break; 92 | } 93 | } 94 | 95 | int nargs = argc - optind; 96 | char** args = &argv[optind]; 97 | 98 | const char* iface = args[0]; 99 | 100 | if (listen_ && connect_) { 101 | fprintf(stderr, "Can't have both listen and connect arguments\n"); 102 | return print_usage(stderr, 1); 103 | } 104 | 105 | if (!listen_ && !connect_) { 106 | fprintf(stderr, "You must either listen or connect\n"); 107 | return print_usage(stderr, 1); 108 | } 109 | 110 | if (create) { 111 | if (!iface) { 112 | perror("Cannot create an interface unless you name it\n"); 113 | return 1; 114 | } 115 | 116 | if (try_create(iface) < 0) { 117 | perror("Could not create interface"); 118 | return 1; 119 | } 120 | 121 | if (try_setup(iface) < 0) { 122 | perror("Could not set up interface"); 123 | return 1; 124 | } 125 | } 126 | 127 | struct mloop* mloop = mloop_default(); 128 | mloop_ref(mloop); 129 | 130 | if (listen_) { 131 | int port = atoi(listen_); 132 | if (can_tcp_bridge_server(iface, port) < 0) { 133 | perror("Could not create interface bridge"); 134 | goto failure; 135 | } 136 | } else { 137 | int port = 5555; 138 | char* portptr = strchr(connect_, ':'); 139 | if (portptr) { 140 | *portptr++ = '\0'; 141 | port = atoi(portptr); 142 | } 143 | 144 | if (can_tcp_bridge_client(iface, connect_, port) < 0) { 145 | perror("Could not create interface bridge"); 146 | goto failure; 147 | } 148 | } 149 | 150 | sigset_t sigset; 151 | sigemptyset(&sigset); 152 | sigaddset(&sigset, SIGINT); 153 | sigaddset(&sigset, SIGTERM); 154 | sigaddset(&sigset, SIGHUP); 155 | 156 | pthread_sigmask(SIG_BLOCK, &sigset, NULL); 157 | 158 | struct mloop_signal* sig = mloop_signal_new(mloop_default()); 159 | if (!sig) { 160 | perror("Could not allocate signal handler"); 161 | return 1; 162 | } 163 | 164 | mloop_signal_set_signals(sig, &sigset); 165 | mloop_signal_set_callback(sig, on_signal_event); 166 | mloop_signal_start(sig); 167 | mloop_signal_unref(sig); 168 | 169 | mloop_run(mloop); 170 | 171 | failure: 172 | mloop_unref(mloop); 173 | return 1; 174 | } 175 | -------------------------------------------------------------------------------- /src/canopen-dump.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2017, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "canopen/dump.h" 23 | 24 | const char usage_[] = 25 | "Usage: canopen-dump [options] \n" 26 | "\n" 27 | "Options:\n" 28 | " -h, --help Get help.\n" 29 | " -u, --time Show time of arrival.\n" 30 | " -T, --tcp Connect via TCP.\n" 31 | " -f, --file Dump from trace buffer file.\n" 32 | " -n, --nmt Show NMT.\n" 33 | " -S, --sync Show SYNC.\n" 34 | " -e, --emcy Show EMCY.\n" 35 | " -p, --pdo[=mask] Show PDO.\n" 36 | " -s, --sdo Show SDO.\n" 37 | " -H, --heartbeat Show heartbeat.\n" 38 | "\n" 39 | "Examples:\n" 40 | " $ canopen-dump can0\n" 41 | " $ canopen-dump -T 127.0.0.1\n" 42 | "\n"; 43 | 44 | static inline int print_usage(FILE* output, int status) 45 | { 46 | fprintf(output, "%s", usage_); 47 | return status; 48 | } 49 | 50 | unsigned int apply_pdo_option(const char* arg) 51 | { 52 | return arg 53 | ? (strtoul(arg, NULL, 0) << CO_DUMP_PDO_FILTER_SHIFT) & CO_DUMP_FILTER_PDO 54 | : CO_DUMP_FILTER_PDO; 55 | } 56 | 57 | int main(int argc, char* argv[]) 58 | { 59 | static const struct option long_options[] = { 60 | { "help", no_argument, 0, 'h' }, 61 | { "time", no_argument, 0, 'u' }, 62 | { "tcp", no_argument, 0, 'T' }, 63 | { "file", no_argument, 0, 'f' }, 64 | { "nmt", no_argument, 0, 'n' }, 65 | { "sync", no_argument, 0, 'S' }, 66 | { "emcy", no_argument, 0, 'e' }, 67 | { "pdo", optional_argument, 0, 'p' }, 68 | { "sdo", no_argument, 0, 's' }, 69 | { "heartbeat", no_argument, 0, 'H' }, 70 | { 0, 0, 0, 0 } 71 | }; 72 | 73 | enum co_dump_options opt = 0; 74 | 75 | while (1) { 76 | int c = getopt_long(argc, argv, "huTfnSepsiH", long_options, NULL); 77 | if (c < 0) 78 | break; 79 | 80 | switch (c) { 81 | case 'h': return print_usage(stdout, 0); 82 | case 'u': opt |= CO_DUMP_TIMESTAMP; break; 83 | case 'T': opt |= CO_DUMP_TCP; break; 84 | case 'f': opt |= CO_DUMP_FILE; break; 85 | case 'n': opt |= CO_DUMP_FILTER_NMT; break; 86 | case 'S': opt |= CO_DUMP_FILTER_SYNC; break; 87 | case 'e': opt |= CO_DUMP_FILTER_EMCY; break; 88 | case 'p': opt |= apply_pdo_option(optarg); break; 89 | case 's': opt |= CO_DUMP_FILTER_SDO; break; 90 | case 'H': opt |= CO_DUMP_FILTER_HEARTBEAT; break; 91 | default: return print_usage(stderr, 1); 92 | } 93 | } 94 | 95 | int nargs = argc - optind; 96 | char** args = &argv[optind]; 97 | 98 | if (nargs < 1) 99 | return print_usage(stderr, 1); 100 | 101 | const char* iface = args[0]; 102 | 103 | setvbuf(stdout, NULL, _IOLBF, 0); 104 | 105 | return co_dump(iface, opt); 106 | } 107 | -------------------------------------------------------------------------------- /src/canopen-master.c: -------------------------------------------------------------------------------- 1 | master-main.c -------------------------------------------------------------------------------- /src/canopen-vnode.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "vnode.h" 23 | 24 | const char usage_[] = 25 | "Usage: canopen-vnode [options] [nodeid] [...]\n" 26 | "\n" 27 | "Options:\n" 28 | " -h, --help Get help.\n" 29 | " -T, --tcp Connect via TCP.\n" 30 | " -c, --config Set path to config file.\n" 31 | "\n" 32 | "Examples:\n" 33 | " $ canopen-vnode can0\n" 34 | " $ canopen-vnode -T 127.0.0.1\n" 35 | "\n"; 36 | 37 | static struct vnode* node[127]; 38 | 39 | static inline int print_usage(FILE* output, int status) 40 | { 41 | fprintf(output, "%s", usage_); 42 | return status; 43 | } 44 | 45 | int init_nodes(enum sock_type type, const char* config, const char* iface, 46 | char* ids[], int n_ids) 47 | { 48 | memset(node, 0, sizeof(node)); 49 | 50 | int i; 51 | for (i = 0; i < n_ids; ++i) { 52 | int nodeid = atoi(ids[i]); 53 | 54 | struct vnode* vnode = co_vnode_new(type, iface, config, nodeid); 55 | if (!vnode) 56 | goto failure; 57 | 58 | node[nodeid - 1] = vnode; 59 | } 60 | 61 | return 0; 62 | 63 | failure: 64 | for (--i; i >= 0; --i) { 65 | int nodeid = atoi(ids[i]); 66 | co_vnode_destroy(node[nodeid - 1]); 67 | } 68 | 69 | return -1; 70 | } 71 | 72 | void destroy_nodes(void) 73 | { 74 | for (int i = 0; i < 127; ++i) 75 | if (node[i]) 76 | co_vnode_destroy(node[i]); 77 | } 78 | 79 | int main(int argc, char* argv[]) 80 | { 81 | static const struct option long_options[] = { 82 | { "help", no_argument, 0, 'h' }, 83 | { "tcp", no_argument, 0, 'T' }, 84 | { "config", required_argument, 0, 'c' }, 85 | { 0, 0, 0, 0 } 86 | }; 87 | 88 | int use_tcp = 0; 89 | const char* config = NULL; 90 | 91 | while (1) { 92 | int c = getopt_long(argc, argv, "hTc:", long_options, NULL); 93 | if (c < 0) 94 | break; 95 | 96 | switch (c) { 97 | case 'h': return print_usage(stdout, 0); 98 | case 'T': use_tcp = 1; break; 99 | case 'c': config = optarg; break; 100 | } 101 | } 102 | 103 | int nargs = argc - optind; 104 | char** args = &argv[optind]; 105 | 106 | if (nargs < 2) 107 | return print_usage(stderr, 1); 108 | 109 | const char* iface = args[0]; 110 | 111 | enum sock_type type = use_tcp ? SOCK_TYPE_TCP : SOCK_TYPE_CAN; 112 | 113 | struct mloop* mloop = mloop_default(); 114 | mloop_ref(mloop); 115 | 116 | if (init_nodes(type, config, iface, &args[1], nargs - 1) < 0) 117 | return 1; 118 | 119 | mloop_run(mloop); 120 | 121 | destroy_nodes(); 122 | mloop_unref(mloop); 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /src/canopen.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "canopen.h" 22 | 23 | int canopen_get_object_type(struct canopen_msg* msg, 24 | const struct can_frame* frame) 25 | { 26 | uint32_t cob = frame->can_id & CAN_SFF_MASK; 27 | enum canopen_range id = (enum canopen_range)cob; 28 | 29 | switch (id) { 30 | case R_NMT: 31 | msg->id = 0; 32 | msg->object = CANOPEN_NMT; 33 | return 0; 34 | case R_SYNC: 35 | msg->id = 0; 36 | msg->object = CANOPEN_SYNC; 37 | return 0; 38 | case R_TIMESTAMP: 39 | msg->id = 0; 40 | msg->object = CANOPEN_TIMESTAMP; 41 | return 0; 42 | case EMCY_LOW ... EMCY_HIGH: 43 | msg->id = id - R_EMCY; 44 | msg->object = CANOPEN_EMCY; 45 | return 0; 46 | case TPDO1_LOW ... TPDO1_HIGH: 47 | msg->id = id - R_TPDO1; 48 | msg->object = CANOPEN_TPDO1; 49 | return 0; 50 | case RPDO1_LOW ... RPDO1_HIGH: 51 | msg->id = id - R_RPDO1; 52 | msg->object = CANOPEN_RPDO1; 53 | return 0; 54 | case TPDO2_LOW ... TPDO2_HIGH: 55 | msg->id = id - R_TPDO2; 56 | msg->object = CANOPEN_TPDO2; 57 | return 0; 58 | case RPDO2_LOW ... RPDO2_HIGH: 59 | msg->id = id - R_RPDO2; 60 | msg->object = CANOPEN_RPDO2; 61 | return 0; 62 | case TPDO3_LOW ... TPDO3_HIGH: 63 | msg->id = id - R_TPDO3; 64 | msg->object = CANOPEN_TPDO3; 65 | return 0; 66 | case RPDO3_LOW ... RPDO3_HIGH: 67 | msg->id = id - R_RPDO3; 68 | msg->object = CANOPEN_RPDO3; 69 | return 0; 70 | case TPDO4_LOW ... TPDO4_HIGH: 71 | msg->id = id - R_TPDO4; 72 | msg->object = CANOPEN_TPDO4; 73 | return 0; 74 | case RPDO4_LOW ... RPDO4_HIGH: 75 | msg->id = id - R_RPDO4; 76 | msg->object = CANOPEN_RPDO4; 77 | return 0; 78 | case TSDO_LOW ... TSDO_HIGH: 79 | msg->id = id - R_TSDO; 80 | msg->object = CANOPEN_TSDO; 81 | return 0; 82 | case RSDO_LOW ... RSDO_HIGH: 83 | msg->id = id - R_RSDO; 84 | msg->object = CANOPEN_RSDO; 85 | return 0; 86 | case HEARTBEAT_LOW ... HEARTBEAT_HIGH: 87 | msg->id = id - R_HEARTBEAT; 88 | msg->object = CANOPEN_HEARTBEAT; 89 | return 0; 90 | default: 91 | msg->id = id; 92 | msg->object = CANOPEN_UNSPEC; 93 | return -1; 94 | } 95 | } 96 | 97 | int canopen_get_object_type_simple(struct canopen_msg* msg, 98 | struct can_frame* frame, unsigned int nodeid) 99 | { 100 | enum canopen_range cob = (enum canopen_range)frame->can_id; 101 | 102 | assert(nodeid < 128); 103 | 104 | msg->id = nodeid; 105 | msg->object = nodeid > cob ? cob - nodeid : (enum canopen_object)cob; 106 | 107 | return 0; 108 | } 109 | 110 | const char* canopen_object_type_to_string(enum canopen_object obj) 111 | { 112 | switch (obj) { 113 | case CANOPEN_NMT: return "NMT"; 114 | case CANOPEN_SYNC: return "SYNC"; 115 | case CANOPEN_EMCY: return "EMCY"; 116 | case CANOPEN_TIMESTAMP: return "TIMESTAMP"; 117 | case CANOPEN_TPDO1 ... CANOPEN_RPDO4: 118 | return "PDO"; 119 | case CANOPEN_TSDO ... CANOPEN_RSDO: 120 | return "SDO"; 121 | case CANOPEN_HEARTBEAT: return "HEARTBEAT"; 122 | default: return "UNKNOWN"; 123 | } 124 | } 125 | 126 | const char* canopen_object_type_to_string_exact(enum canopen_object obj) 127 | { 128 | switch (obj) { 129 | case CANOPEN_NMT: return "NMT"; 130 | case CANOPEN_SYNC: return "SYNC"; 131 | case CANOPEN_EMCY: return "EMCY"; 132 | case CANOPEN_TIMESTAMP: return "TIMESTAMP"; 133 | case CANOPEN_TPDO1: return "TPDO1"; 134 | case CANOPEN_RPDO1: return "RPDO1"; 135 | case CANOPEN_TPDO2: return "TPDO2"; 136 | case CANOPEN_RPDO2: return "RPDO2"; 137 | case CANOPEN_TPDO3: return "TPDO3"; 138 | case CANOPEN_RPDO3: return "RPDO3"; 139 | case CANOPEN_TPDO4: return "TPDO4"; 140 | case CANOPEN_RPDO4: return "RPDO4"; 141 | case CANOPEN_TSDO: return "TSDO"; 142 | case CANOPEN_RSDO: return "RSDO"; 143 | case CANOPEN_HEARTBEAT: return "HEARTBEAT"; 144 | default: return "UNKNOWN"; 145 | } 146 | } 147 | 148 | -------------------------------------------------------------------------------- /src/canopen_info.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include 18 | #include "canopen_info.h" 19 | 20 | struct canopen_info* canopen_info_ = NULL; 21 | 22 | static const char canopen_info_name[] = "canopen2"; 23 | static const char canopen_info_description[] = "canopen2.xml"; 24 | 25 | int canopen_info_init(const char* iface) 26 | { 27 | char buffer[256]; 28 | snprintf(buffer, sizeof(buffer), "%s.%s", canopen_info_name, iface); 29 | buffer[sizeof(buffer) - 1] = '\0'; 30 | 31 | canopen_info_ = s_malloc(sizeof(struct canopen_info) * 127, buffer, 32 | canopen_info_description); 33 | if (!canopen_info_) 34 | return -1; 35 | 36 | for (size_t i = 0; i < 127; ++i) 37 | canopen_info_[i].is_active = 0; 38 | 39 | return 0; 40 | } 41 | 42 | void canopen_info_cleanup(void) 43 | { 44 | s_free(canopen_info_); 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/cfg.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include "cfg.h" 17 | #include "ini_parser.h" 18 | #include "canopen/master.h" 19 | 20 | #define EXPORT __attribute__((visibility("default"))) 21 | 22 | #define XSTR(s) STR(s) 23 | #define STR(s) #s 24 | 25 | static int cfg__is_initialised = 0; 26 | static struct ini_file ini; 27 | 28 | size_t strlcpy(char*, const char*, size_t); 29 | 30 | EXPORT 31 | struct cfg cfg; 32 | 33 | EXPORT 34 | void cfg_load_defaults(void) 35 | { 36 | #define X(type, name, default_) CFG__SET_(type, cfg.name, default_); 37 | CFG__PARAMETERS 38 | #undef X 39 | 40 | } 41 | 42 | void cfg__load_node_defaults(int id) 43 | { 44 | #define X(type, name, default_) CFG__SET_(type, cfg.node[id].name, default_); 45 | CFG__NODE_PARAMETERS 46 | #undef X 47 | } 48 | 49 | void cfg__load_node_config(int id) 50 | { 51 | const char* v; 52 | #define X(type, name, default_) \ 53 | v = cfg__file_read(id, XSTR(name)); \ 54 | if (v) { \ 55 | CFG__SET_(type, cfg.node[id].name, CFG__STRTO_(type, v)); \ 56 | } 57 | 58 | CFG__NODE_PARAMETERS 59 | #undef X 60 | } 61 | 62 | void cfg_load_node(int id) 63 | { 64 | cfg__load_node_defaults(id); 65 | cfg__load_node_config(id); 66 | 67 | if (cfg.be_strict) 68 | cfg.node[id].ignore_sdo_multiplexer = 0; 69 | 70 | if (cfg.heartbeat_period) 71 | cfg.node[id].heartbeat_period = cfg.heartbeat_period; 72 | 73 | if (cfg.heartbeat_timeout) 74 | cfg.node[id].heartbeat_timeout = cfg.heartbeat_timeout; 75 | 76 | if (cfg.n_timeouts_max) 77 | cfg.node[id].n_timeouts_max = cfg.n_timeouts_max; 78 | } 79 | 80 | EXPORT 81 | void cfg_load_globals(void) 82 | { 83 | const char* v; 84 | 85 | const struct ini_section* section = ini_find_section(&ini, "master"); 86 | if (!section) 87 | return; 88 | 89 | #define X(type, name, default_) \ 90 | v = ini_find_key(section, XSTR(name)); \ 91 | if (v) { \ 92 | CFG__SET_(type, cfg.name, CFG__STRTO_(type, v)); \ 93 | } 94 | 95 | CFG__PARAMETERS 96 | #undef X 97 | } 98 | 99 | int cfg__load_stream(FILE* stream) 100 | { 101 | int r = ini_parse(&ini, stream); 102 | if (r >= 0) 103 | cfg__is_initialised = 1; 104 | return r; 105 | } 106 | 107 | EXPORT 108 | int cfg_load_file(const char* path) 109 | { 110 | int r = -1; 111 | 112 | FILE* stream = fopen(path, "r"); 113 | if (!stream) 114 | return -1; 115 | 116 | if (cfg__load_stream(stream) < 0) 117 | goto failure; 118 | 119 | r = 0; 120 | failure: 121 | fclose(stream); 122 | return r; 123 | } 124 | 125 | EXPORT 126 | void cfg_unload_file(void) 127 | { 128 | if (!cfg__is_initialised) 129 | return; 130 | 131 | ini_destroy(&ini); 132 | cfg__is_initialised = 0; 133 | } 134 | 135 | const char* cfg__get_by_nodeid(int nodeid, const char* key) 136 | { 137 | char section[256]; 138 | snprintf(section, sizeof(section) - 1, "#%d", nodeid); 139 | section[sizeof(section) - 1] = '\0'; 140 | 141 | return ini_find(&ini, section, key); 142 | } 143 | 144 | const char* cfg__get_by_name(int nodeid, const char* key) 145 | { 146 | struct co_master_node* node = co_master_get_node(nodeid); 147 | if (!node || !*node->name) 148 | return NULL; 149 | 150 | char section[256]; 151 | snprintf(section, sizeof(section) - 1, "=%s", node->name); 152 | section[sizeof(section) - 1] = '\0'; 153 | 154 | return ini_find(&ini, section, key); 155 | } 156 | 157 | const char* cfg__file_read(int nodeid, const char* key) 158 | { 159 | if (!cfg__is_initialised) 160 | return NULL; 161 | 162 | const char* result = NULL; 163 | 164 | result = cfg__get_by_nodeid(nodeid, key); 165 | if (result) goto done; 166 | 167 | result = cfg__get_by_name(nodeid, key); 168 | if (result) goto done; 169 | 170 | result = ini_find(&ini, "all", key); 171 | if (result) goto done; 172 | 173 | done: 174 | return result; 175 | } 176 | 177 | -------------------------------------------------------------------------------- /src/error.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "canopen/error.h" 6 | 7 | static const char* cia302_lookup(uint16_t code) 8 | { 9 | switch (code) { 10 | #define X(c, str) \ 11 | case c: return str; 12 | #include "canopen/cia302-error-codes.h" 13 | #undef X 14 | } 15 | 16 | return NULL; 17 | } 18 | 19 | static const char* cia402_lookup(uint16_t code) 20 | { 21 | switch (code) { 22 | #define X(c, str) \ 23 | case c: return str; 24 | #include "canopen/cia402-error-codes.h" 25 | #undef X 26 | } 27 | 28 | return cia302_lookup(code); 29 | } 30 | 31 | static inline void 32 | append_code(char* dst, uint16_t code, const char* (*lookup)(uint16_t)) 33 | { 34 | const char* s = lookup(code); 35 | 36 | if (s) { 37 | if (dst[0]) strcat(dst, ": "); 38 | strcat(dst, s); 39 | } 40 | } 41 | 42 | static const char* 43 | convert_to_string(uint16_t code, const char* (*lookup)(uint16_t)) 44 | { 45 | static char buf[256]; 46 | uint16_t current_code, last_code = 0; 47 | 48 | if (code == 0) 49 | return lookup(0); 50 | 51 | memset(buf, 0, sizeof(buf)); 52 | 53 | #define X(mask) \ 54 | current_code = code & mask; \ 55 | if (current_code != last_code) \ 56 | append_code(buf, current_code, lookup); \ 57 | last_code = current_code; 58 | 59 | X(0xf000) 60 | X(0xff00) 61 | X(0xfff0) 62 | X(0xffff) 63 | 64 | #undef X 65 | 66 | return buf; 67 | } 68 | 69 | const char* error_code_to_string(uint16_t code, int profile) 70 | { 71 | switch (profile) { 72 | case 402: return convert_to_string(code, cia402_lookup); 73 | case 302: 74 | default: return convert_to_string(code, cia302_lookup); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/hexdump.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include 18 | 19 | static inline char nibble_to_char(int nibble) 20 | { 21 | static char map[] = { 22 | '0', '1', '2', '3', 23 | '4', '5', '6', '7', 24 | '8', '9', 'A', 'B', 25 | 'C', 'D', 'E', 'F' 26 | }; 27 | 28 | return map[nibble]; 29 | } 30 | 31 | static inline uint8_t get_low_nibble(uint8_t data) 32 | { 33 | return data & 0x0f; 34 | } 35 | 36 | static inline uint8_t get_high_nibble(uint8_t data) 37 | { 38 | return (data & 0xf0) >> 4; 39 | } 40 | 41 | const char* hexdump(const void* data, size_t size) 42 | { 43 | const char* p = data; 44 | static __thread char buffer[256]; 45 | 46 | size_t k = 0; 47 | for(size_t i = 0; i < size && k + 2 < sizeof(buffer); ++i) { 48 | buffer[k++] = nibble_to_char(get_high_nibble(p[i])); 49 | buffer[k++] = nibble_to_char(get_low_nibble(p[i])); 50 | } 51 | buffer[k] = '\0'; 52 | 53 | return buffer; 54 | } 55 | -------------------------------------------------------------------------------- /src/net-util.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "plog.h" 24 | #include 25 | #include 26 | #include 27 | 28 | #include "socketcan.h" 29 | #include "net-util.h" 30 | #include "time-utils.h" 31 | 32 | static ssize_t net__write(int fd, const void* src, size_t size, int timeout) 33 | { 34 | struct pollfd pollfd = { .fd = fd, .events = POLLOUT, .revents = 0 }; 35 | if (poll(&pollfd, 1, timeout) == 1) 36 | return write(fd, src, size); 37 | return -1; 38 | } 39 | 40 | ssize_t net_write(int fd, const void* src, size_t size, int timeout) 41 | { 42 | errno = 0; 43 | ssize_t rc = net__write(fd, src, size, timeout); 44 | if (rc < 0 && errno && errno != EAGAIN && errno != EWOULDBLOCK) 45 | plog(LOG_ERROR, "Failed to write to CAN bus: %s", 46 | strerror(errno)); 47 | return rc; 48 | } 49 | 50 | ssize_t net_write_frame(int fd, const struct can_frame* cf, int timeout) 51 | { 52 | return net_write(fd, cf, sizeof(*cf), timeout); 53 | } 54 | 55 | static ssize_t net__read(int fd, void* dst, size_t size, int timeout) 56 | { 57 | struct pollfd pollfd = { .fd = fd, .events = POLLIN, .revents = 0 }; 58 | if (poll(&pollfd, 1, timeout) == 1) 59 | return read(fd, dst, size); 60 | return -1; 61 | } 62 | 63 | ssize_t net_read(int fd, void* dst, size_t size, int timeout) 64 | { 65 | errno = 0; 66 | ssize_t rc = net__read(fd, dst, size, timeout); 67 | if (rc < 0 && errno && errno != EAGAIN && errno != EWOULDBLOCK) 68 | plog(LOG_ERROR, "Failed to read from CAN bus: %s", 69 | strerror(errno)); 70 | return rc; 71 | } 72 | 73 | ssize_t net_read_frame(int fd, struct can_frame* cf, int timeout) 74 | { 75 | return net_read(fd, cf, sizeof(*cf), timeout); 76 | } 77 | 78 | ssize_t net_filtered_read_frame(int fd, struct can_frame* cf, int timeout, 79 | uint32_t can_id) 80 | { 81 | int t = gettime_ms(CLOCK_MONOTONIC); 82 | int t_end = t + timeout; 83 | ssize_t size; 84 | 85 | while (1) { 86 | size = net_read_frame(fd, cf, t_end - t); 87 | if (size < 0) 88 | return -1; 89 | 90 | if (cf->can_id == can_id) 91 | return size; 92 | 93 | t = gettime_ms(CLOCK_MONOTONIC); 94 | } 95 | 96 | return -1; 97 | } 98 | 99 | int net_dont_block(int fd) 100 | { 101 | return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); 102 | } 103 | 104 | int net_fix_sndbuf(int fd) 105 | { 106 | int sndbuf = 0; 107 | return setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)); 108 | } 109 | 110 | int net_reuse_addr(int fd) 111 | { 112 | int one = 1; 113 | return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); 114 | } 115 | 116 | int net_dont_delay(int fd) 117 | { 118 | int one = 1; 119 | return setsockopt(fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one)); 120 | } 121 | -------------------------------------------------------------------------------- /src/network.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "socketcan.h" 24 | #include "canopen.h" 25 | #include "canopen/nmt.h" 26 | #include "canopen/heartbeat.h" 27 | #include "canopen/network.h" 28 | #include "canopen/sdo.h" 29 | #include "net-util.h" 30 | #include "time-utils.h" 31 | #include "sock.h" 32 | 33 | #define MAX(a,b) ((a) > (b) ? (a) : (b)) 34 | 35 | int co_net_send_nmt(const struct sock* sock, int cs, int nodeid) 36 | { 37 | struct can_frame cf = { 0 }; 38 | cf.can_id = 0; 39 | cf.can_dlc = 2; 40 | nmt_set_cs(&cf, cs); 41 | nmt_set_nodeid(&cf, nodeid); 42 | return sock_send(sock, &cf, 0); 43 | } 44 | 45 | int co_net__request_heartbeat(const struct sock* sock, int nodeid) 46 | { 47 | struct can_frame cf = { 0 }; 48 | cf.can_id = R_HEARTBEAT + nodeid; 49 | cf.can_id |= CAN_RTR_FLAG; 50 | return sock_send(sock, &cf, 0); 51 | } 52 | 53 | int co_net__request_sdo(const struct sock* sock, int nodeid) 54 | { 55 | struct can_frame cf; 56 | sdo_clear_frame(&cf); 57 | cf.can_id = R_RSDO + nodeid; 58 | cf.can_dlc = 4; 59 | sdo_set_index(&cf, 0x1000); 60 | sdo_set_subindex(&cf, 0); 61 | sdo_set_cs(&cf, SDO_CCS_UL_INIT_REQ); 62 | return sock_send(sock, &cf, 0); 63 | } 64 | 65 | int co_net__wait_for_bootup(const struct sock* sock, char* nodes_seen, 66 | int start, int end, int timeout) 67 | { 68 | struct can_frame cf; 69 | struct canopen_msg msg; 70 | 71 | int t = gettime_ms(CLOCK_MONOTONIC); 72 | int t_end = t + timeout; 73 | 74 | while (sock_timed_recv(sock, &cf, MAX(0, t_end - t)) > 0) { 75 | t = gettime_ms(CLOCK_MONOTONIC); 76 | 77 | canopen_get_object_type(&msg, &cf); 78 | 79 | if (!(start <= msg.id && msg.id <= end)) 80 | continue; 81 | 82 | if (msg.object != CANOPEN_HEARTBEAT) 83 | continue; 84 | 85 | nodes_seen[msg.id] = 1; 86 | t_end = t + timeout; 87 | } 88 | 89 | return 0; 90 | } 91 | 92 | int co_net__wait_for_sdo(const struct sock* sock, char* nodes_seen, 93 | int start, int end, int timeout) 94 | { 95 | struct can_frame cf; 96 | struct canopen_msg msg; 97 | 98 | int t = gettime_ms(CLOCK_MONOTONIC); 99 | int t_end = t + timeout; 100 | 101 | while (sock_timed_recv(sock, &cf, MAX(0, t_end - t)) > 0) { 102 | t = gettime_ms(CLOCK_MONOTONIC); 103 | 104 | canopen_get_object_type(&msg, &cf); 105 | 106 | if (!(start <= msg.id && msg.id <= end)) 107 | continue; 108 | 109 | if (msg.object != CANOPEN_TSDO) 110 | continue; 111 | 112 | nodes_seen[msg.id] = 1; 113 | t_end = t + timeout; 114 | } 115 | 116 | return 0; 117 | } 118 | 119 | int co_net_reset(const struct sock* sock, char* nodes_seen, int timeout) 120 | { 121 | co_net_send_nmt(sock, NMT_CS_RESET_COMMUNICATION, 0); 122 | return co_net__wait_for_bootup(sock, nodes_seen, 0, 127, timeout); 123 | } 124 | 125 | int co_net_reset_range(const struct sock* sock, char* nodes_seen, int start, 126 | int end, int timeout) 127 | { 128 | for (int i = start; i <= end; ++i) 129 | co_net_send_nmt(sock, NMT_CS_RESET_COMMUNICATION, i); 130 | 131 | return co_net__wait_for_bootup(sock, nodes_seen, start, end, timeout); 132 | } 133 | 134 | int co_net_probe(const struct sock* sock, char* nodes_seen, int start, int end, 135 | int timeout) 136 | { 137 | for (int i = start; i <= end; ++i) 138 | if (!nodes_seen[i]) 139 | co_net__request_heartbeat(sock, i); 140 | 141 | return co_net__wait_for_bootup(sock, nodes_seen, start, end, timeout); 142 | } 143 | 144 | int co_net_probe_sdo(const struct sock* sock, char* nodes_seen, int start, 145 | int end, int timeout) 146 | { 147 | for (int i = start; i <= end; ++i) 148 | if (!nodes_seen[i]) 149 | co_net__request_sdo(sock, i); 150 | 151 | return co_net__wait_for_sdo(sock, nodes_seen, start, end, timeout); 152 | } 153 | -------------------------------------------------------------------------------- /src/profiling.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include "profiling.h" 18 | 19 | int profiling_is_active_ = -1; 20 | uint64_t profiling_start_time_ = 0; 21 | 22 | int profiling_getenv(void) 23 | { 24 | return getenv("CANOPEN_PROFILE") != NULL; 25 | } 26 | -------------------------------------------------------------------------------- /src/sdo-dict.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include "canopen/sdo-dict.h" 17 | #include "string-utils.h" 18 | 19 | #include 20 | 21 | #define XSTR(s) STR(s) 22 | #define STR(s) #s 23 | 24 | size_t strlcpy(char* dst, const char* src, size_t size); 25 | 26 | static const char* str_dict__constname(uint32_t mux) 27 | { 28 | switch (mux) { 29 | #define X(idx, subidx, type, name) \ 30 | case SDO_MUX(idx, subidx): return XSTR(name); 31 | 32 | SDO_DICTIONARY 33 | #undef X 34 | default: return "UNKNOWN"; 35 | } 36 | } 37 | 38 | enum canopen_type sdo_dict_type(uint32_t mux) 39 | { 40 | switch (mux) { 41 | #define X(idx, subidx, type, ...) \ 42 | case SDO_MUX(idx, subidx): return CANOPEN_ ## type; 43 | 44 | SDO_DICTIONARY 45 | #undef X 46 | default: return CANOPEN_UNKNOWN; 47 | } 48 | } 49 | 50 | static inline char* sdo_dict__constname_to_string(char* constname) 51 | { 52 | return string_tolower(string_replace_char('_', '-', constname)); 53 | } 54 | 55 | const char* sdo_dict_tostring(uint32_t mux) 56 | { 57 | static __thread char buffer[64]; 58 | strlcpy(buffer, str_dict__constname(mux), sizeof(buffer)); 59 | return sdo_dict__constname_to_string(buffer); 60 | } 61 | 62 | uint32_t sdo_dict_fromstring(const char* str) 63 | { 64 | static __thread char buffer[64]; 65 | strlcpy(buffer, str, sizeof(buffer)); 66 | string_replace_char('-', '_', buffer); 67 | 68 | #define X(idx, subidx, type, name) \ 69 | if (0 == strcasecmp(buffer, XSTR(name))) return SDO_MUX(idx, subidx); 70 | 71 | SDO_DICTIONARY 72 | #undef X 73 | 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /src/sdo_common.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include "canopen/sdo.h" 17 | 18 | #ifndef CAN_MAX_DLC 19 | #define CAN_MAX_DLC 8 20 | #endif 21 | 22 | void sdo_abort(struct can_frame* frame, enum sdo_abort_code code, int index, 23 | int subindex) 24 | { 25 | sdo_set_cs(frame, SDO_SCS_ABORT); 26 | sdo_set_abort_code(frame, code); 27 | 28 | sdo_set_index(frame, index); 29 | sdo_set_subindex(frame, subindex); 30 | 31 | frame->can_dlc = CAN_MAX_DLC; 32 | } 33 | 34 | const char* sdo_strerror(enum sdo_abort_code code) 35 | { 36 | switch (code) { 37 | case SDO_ABORT_TOGGLE: 38 | return "Toggle bit not alternated"; 39 | case SDO_ABORT_TIMEOUT: 40 | return "SDO protocol timed out"; 41 | case SDO_ABORT_INVALID_CS: 42 | return "Client/server command specifier not valid or unknown"; 43 | case SDO_ABORT_BLOCKSZ: 44 | return "Invalid block size"; 45 | case SDO_ABORT_SEQNR: 46 | return "Invalid sequence number"; 47 | case SDO_ABORT_CRCERR: 48 | return "CRC error"; 49 | case SDO_ABORT_NOMEM: 50 | return "Out of memory"; 51 | case SDO_ABORT_ACCESS: 52 | return "Unsupported access"; 53 | case SDO_ABORT_RO: 54 | return "Attempt to write a read only object"; 55 | case SDO_ABORT_WO: 56 | return "Attempt to read a write only object"; 57 | case SDO_ABORT_NEXIST: 58 | return "Object does not exist in the object dictionary"; 59 | case SDO_ABORT_NOPDO: 60 | return "Object cannot be mapped to PDO"; 61 | case SDO_ABORT_PDOSZ: 62 | return "The number and length of objects to be mapped would exceed PDO length"; 63 | case SDO_ABORT_PARCOMPAT: 64 | return "General parameter incompatibility error"; 65 | case SDO_ABORT_DEVCOMPAT: 66 | return "General internal incompatibility in device"; 67 | case SDO_ABORT_HWERROR: 68 | return "Access failed due to a hardware error"; 69 | case SDO_ABORT_SIZE: 70 | return "Data type does not match, length of service parameter does not match"; 71 | case SDO_ABORT_TOO_LONG: 72 | return "Data type does not match, length of service parameter too high"; 73 | case SDO_ABORT_TOO_SHORT: 74 | return "Data type does not match, length of service parameter too low"; 75 | case SDO_ABORT_SUBNEXIST: 76 | return "Sub-index does not exist"; 77 | case SDO_ABORT_NVAL: 78 | return "Invalid value for parameter"; 79 | case SDO_ABORT_GENERAL: 80 | return "General error"; 81 | } 82 | return "UNKNOWN"; 83 | } 84 | 85 | -------------------------------------------------------------------------------- /src/sdo_sync.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include "canopen/byteorder.h" 18 | #include "canopen/sdo_sync.h" 19 | 20 | struct sdo_req* sdo_sync_read(int nodeid, int index, int subindex) 21 | { 22 | struct sdo_req_info info = { 23 | .type = SDO_REQ_UPLOAD, 24 | .index = index, 25 | .subindex = subindex 26 | }; 27 | 28 | struct sdo_req* req = sdo_req_new(&info); 29 | if (!req) 30 | return NULL; 31 | 32 | if (sdo_req_start(req, sdo_req_queue_get(nodeid)) < 0) 33 | goto done; 34 | 35 | sdo_req_wait(req); 36 | 37 | if (req->status != SDO_REQ_OK) { 38 | errno = ENOENT; 39 | goto done; 40 | } 41 | 42 | return req; 43 | 44 | done: 45 | sdo_req_unref(req); 46 | return NULL; 47 | } 48 | 49 | #define DECLARE_SDO_READ(type, name) \ 50 | type sdo_sync_read_ ## name(int nodeid, int index, int subindex) \ 51 | { \ 52 | type value = 0; \ 53 | struct sdo_req* req = sdo_sync_read(nodeid, index, subindex); \ 54 | if (!req) \ 55 | return 0; \ 56 | if (req->data.index > sizeof(value)) { \ 57 | errno = ERANGE; \ 58 | goto done; \ 59 | } \ 60 | byteorder2(&value, req->data.data, sizeof(value), req->data.index); \ 61 | done: \ 62 | sdo_req_unref(req); \ 63 | return value; \ 64 | } 65 | 66 | DECLARE_SDO_READ(int64_t, i64) 67 | DECLARE_SDO_READ(uint64_t, u64) 68 | DECLARE_SDO_READ(int32_t, i32) 69 | DECLARE_SDO_READ(uint32_t, u32) 70 | DECLARE_SDO_READ(int16_t, i16) 71 | DECLARE_SDO_READ(uint16_t, u16) 72 | DECLARE_SDO_READ(int8_t, i8) 73 | DECLARE_SDO_READ(uint8_t, u8) 74 | 75 | int sdo_sync_write(int nodeid, struct sdo_req_info* info) 76 | { 77 | int rc = -1; 78 | info->type = SDO_REQ_DOWNLOAD; 79 | 80 | struct sdo_req* req = sdo_req_new(info); 81 | if (!req) 82 | return -1; 83 | 84 | if (sdo_req_start(req, sdo_req_queue_get(nodeid)) < 0) 85 | goto failure; 86 | 87 | sdo_req_wait(req); 88 | 89 | if (req->status != SDO_REQ_OK) 90 | goto failure; 91 | 92 | rc = 0; 93 | failure: 94 | sdo_req_unref(req); 95 | return rc; 96 | } 97 | 98 | #define DECLARE_SDO_WRITE(type, name) \ 99 | int sdo_sync_write_ ## name(int nodeid, struct sdo_req_info* info, type value) \ 100 | { \ 101 | type network_order = 0; \ 102 | byteorder(&network_order, &value, sizeof(network_order)); \ 103 | info->dl_data = &network_order; \ 104 | info->dl_size = sizeof(network_order); \ 105 | return sdo_sync_write(nodeid, info); \ 106 | } 107 | 108 | DECLARE_SDO_WRITE(int64_t, i64) 109 | DECLARE_SDO_WRITE(uint64_t, u64) 110 | DECLARE_SDO_WRITE(int32_t, i32) 111 | DECLARE_SDO_WRITE(uint32_t, u32) 112 | DECLARE_SDO_WRITE(int16_t, i16) 113 | DECLARE_SDO_WRITE(uint16_t, u16) 114 | DECLARE_SDO_WRITE(int8_t, i8) 115 | DECLARE_SDO_WRITE(uint8_t, u8) 116 | -------------------------------------------------------------------------------- /src/sock.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "sock.h" 24 | #include "socketcan.h" 25 | #include "net-util.h" 26 | #include "can-tcp.h" 27 | #include "trace-buffer.h" 28 | 29 | size_t strlcpy(char* dst, const char* src, size_t size); 30 | 31 | static int sock__open_tcp(const char* addr) 32 | { 33 | char buffer[256]; 34 | strlcpy(buffer, addr, sizeof(buffer)); 35 | char* portptr = strchr(buffer, ':'); 36 | int port = 5555; 37 | if (portptr) { 38 | *portptr++ = '\0'; 39 | port = atoi(portptr); 40 | } 41 | return can_tcp_open(buffer, port); 42 | } 43 | 44 | int sock_open(struct sock* sock, enum sock_type type, const char* addr, 45 | struct tracebuffer* tb) 46 | { 47 | int fd = -1; 48 | switch (type) { 49 | case SOCK_TYPE_CAN: fd = socketcan_open(addr); break; 50 | case SOCK_TYPE_TCP: fd = sock__open_tcp(addr); break; 51 | default: abort(); 52 | } 53 | sock_init(sock, type, fd, tb); 54 | return fd; 55 | } 56 | 57 | static inline struct can_frame* 58 | sock__frame_htonl(const struct sock* sock, struct can_frame* cf) 59 | { 60 | if (sock->type != SOCK_TYPE_CAN) 61 | cf->can_id = htonl(cf->can_id); 62 | return cf; 63 | } 64 | 65 | static inline struct can_frame* 66 | sock__frame_ntohl(const struct sock* sock, struct can_frame* cf) 67 | { 68 | if (sock->type != SOCK_TYPE_CAN) 69 | cf->can_id = ntohl(cf->can_id); 70 | return cf; 71 | } 72 | 73 | ssize_t sock_send(const struct sock* sock, struct can_frame* cf, int flags) 74 | { 75 | if (sock->tb) 76 | tb_append(sock->tb, cf); 77 | 78 | return send(sock->fd, sock__frame_htonl(sock, cf), sizeof(*cf), flags); 79 | } 80 | 81 | int sock_timed_send(const struct sock* sock, struct can_frame* cf, int timeout) 82 | { 83 | if (sock->tb) 84 | tb_append(sock->tb, cf); 85 | 86 | return net_write_frame(sock->fd, sock__frame_htonl(sock, cf), timeout); 87 | } 88 | 89 | ssize_t sock_recv(const struct sock* sock, struct can_frame* cf, int flags) 90 | { 91 | ssize_t rsize = recv(sock->fd, cf, sizeof(*cf), flags); 92 | if (rsize <= 0) 93 | return rsize; 94 | 95 | if (sock->tb) 96 | tb_append(sock->tb, cf); 97 | 98 | sock__frame_ntohl(sock, cf); 99 | return rsize; 100 | } 101 | 102 | int sock_timed_recv(const struct sock* sock, struct can_frame* cf, int timeout) 103 | { 104 | int rc = net_read_frame(sock->fd, cf, timeout); 105 | 106 | if (rc >= 0 && sock->tb) 107 | tb_append(sock->tb, cf); 108 | 109 | sock__frame_ntohl(sock, cf); 110 | return rc; 111 | } 112 | 113 | -------------------------------------------------------------------------------- /src/socketcan.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "canopen.h" 27 | #include "socketcan.h" 28 | 29 | static void set_sff_mask(struct can_filter* f, int len) 30 | { 31 | int i; 32 | for (i = 0; i < len; ++i) 33 | f[i].can_mask = CAN_SFF_MASK; 34 | } 35 | 36 | void socketcan_make_slave_filters(struct can_filter* f, int nodeid) 37 | { 38 | assert(nodeid < 128); 39 | 40 | set_sff_mask(f, CANOPEN_SLAVE_FILTER_LENGTH); 41 | 42 | f[0].can_id = R_NMT; 43 | f[1].can_id = R_SYNC; 44 | f[2].can_id = R_TIMESTAMP; 45 | f[3].can_id = R_RPDO1 + nodeid; 46 | f[4].can_id = R_RPDO2 + nodeid; 47 | f[5].can_id = R_RPDO3 + nodeid; 48 | f[6].can_id = R_RPDO4 + nodeid; 49 | f[7].can_id = R_RSDO + nodeid; 50 | f[8].can_id = R_HEARTBEAT + nodeid; 51 | } 52 | 53 | void socketcan_make_master_filters(struct can_filter* f, int nodeid) 54 | { 55 | assert(nodeid < 128); 56 | 57 | set_sff_mask(f, CANOPEN_MASTER_FILTER_LENGTH); 58 | 59 | f[0].can_id = R_NMT; 60 | f[1].can_id = R_SYNC; 61 | f[2].can_id = R_EMCY + nodeid; 62 | f[3].can_id = R_TIMESTAMP; 63 | f[4].can_id = R_TPDO1 + nodeid; 64 | f[5].can_id = R_TPDO2 + nodeid; 65 | f[6].can_id = R_TPDO3 + nodeid; 66 | f[7].can_id = R_TPDO4 + nodeid; 67 | f[8].can_id = R_TSDO + nodeid; 68 | f[9].can_id = R_HEARTBEAT + nodeid; 69 | } 70 | 71 | int socketcan_open(const char* iface) 72 | { 73 | int fd; 74 | struct sockaddr_can addr; 75 | struct ifreq ifr; 76 | 77 | fd = socket(PF_CAN, SOCK_RAW, CAN_RAW); 78 | if (fd < 0) 79 | return -1; 80 | 81 | memset(&ifr, 0, sizeof(ifr)); 82 | strcpy(ifr.ifr_name, iface); 83 | if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) 84 | goto error; 85 | 86 | memset(&addr, 0, sizeof(addr)); 87 | addr.can_family = AF_CAN; 88 | addr.can_ifindex = ifr.ifr_ifindex; 89 | if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) 90 | goto error; 91 | 92 | return fd; 93 | 94 | error: 95 | close(fd); 96 | return -1; 97 | } 98 | 99 | int socketcan_apply_filters(int fd, struct can_filter* filters, int n) 100 | { 101 | return setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FILTER, filters, 102 | n*sizeof(struct can_filter)); 103 | } 104 | 105 | int socketcan_open_slave(const char* iface, int nodeid) 106 | { 107 | struct can_filter filters[CANOPEN_SLAVE_FILTER_LENGTH]; 108 | 109 | int fd = socketcan_open(iface); 110 | if (fd < 0) 111 | return -1; 112 | 113 | socketcan_make_slave_filters(filters, nodeid); 114 | 115 | if (socketcan_apply_filters(fd, filters, 116 | CANOPEN_SLAVE_FILTER_LENGTH) < 0) { 117 | close(fd); 118 | return -1; 119 | } 120 | 121 | return fd; 122 | } 123 | 124 | int socketcan_open_master(const char* iface, int nodeid) 125 | { 126 | struct can_filter filters[CANOPEN_MASTER_FILTER_LENGTH]; 127 | 128 | int fd = socketcan_open(iface); 129 | if (fd < 0) 130 | return -1; 131 | 132 | socketcan_make_master_filters(filters, nodeid); 133 | 134 | if (socketcan_apply_filters(fd, filters, 135 | CANOPEN_MASTER_FILTER_LENGTH) < 0) { 136 | close(fd); 137 | return -1; 138 | } 139 | 140 | return fd; 141 | } 142 | 143 | -------------------------------------------------------------------------------- /src/stream.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #pragma GCC diagnostic ignored "-Wpointer-to-int-cast" 17 | #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "stream.h" 24 | #include "net-util.h" 25 | 26 | static ssize_t stream__write(void* cookie, const char* buf, size_t size) 27 | { 28 | ssize_t wsize = -1; 29 | ssize_t total = 0; 30 | 31 | while (total < (ssize_t)size) { 32 | wsize = net_write((int)cookie, buf + total, size - total, -1); 33 | if (wsize < 0) 34 | break; 35 | 36 | total += wsize; 37 | } 38 | 39 | return total > 0 || wsize >= 0 ? total : -1; 40 | } 41 | 42 | static ssize_t stream__read(void* cookie, char* buf, size_t size) 43 | { 44 | return net_read((int)cookie, buf, size, -1); 45 | } 46 | 47 | static int stream__seek(void* cookie, off64_t* offset, int whence) 48 | { 49 | *offset = lseek64((int)cookie, *offset, whence); 50 | return (int)*offset; 51 | } 52 | 53 | static int stream__close(void* cookie) 54 | { 55 | return close((int)cookie); 56 | } 57 | 58 | static cookie_io_functions_t stream_funcs_ = { 59 | .read = stream__read, 60 | .write = stream__write, 61 | .seek = stream__seek, 62 | .close = stream__close, 63 | }; 64 | 65 | FILE* stream_open(int fd, const char* mode) 66 | { 67 | return fopencookie((void*)fd, mode, stream_funcs_); 68 | } 69 | -------------------------------------------------------------------------------- /src/string-utils.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #define IN_STRING_UTILS_C_ 17 | #include "string-utils.h" 18 | 19 | char* string_trim_right(char* str) 20 | { 21 | char* end = str + strlen(str) - 1; 22 | while (str < end && isspace(*end)) 23 | *end-- = '\0'; 24 | return str; 25 | } 26 | 27 | char* string_keep_if(int (*cond)(int c), char* str) 28 | { 29 | size_t i, k = 0; 30 | for (i = 0; str[i]; ++i) { 31 | int c = str[i]; 32 | if (cond(c)) 33 | str[k++] = c; 34 | } 35 | str[k] = '\0'; 36 | return str; 37 | } 38 | 39 | int string_ends_with(const char* cmp, const char* str) 40 | { 41 | size_t cmp_len = strlen(cmp); 42 | size_t str_len = strlen(str); 43 | 44 | if (cmp_len > str_len) 45 | return 0; 46 | 47 | return strcmp(str + str_len - cmp_len, cmp) == 0; 48 | } 49 | 50 | char* string_tolower(char* str) 51 | { 52 | char* ptr = str; 53 | while (*ptr) { 54 | *ptr = tolower(*ptr); 55 | ++ptr; 56 | } 57 | return str; 58 | } 59 | 60 | char* string_replace_char(char m, char r, char* str) 61 | { 62 | char* ptr = str; 63 | for (; *ptr; ++ptr) 64 | if (*ptr == m) 65 | *ptr = r; 66 | return str; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /src/strlcpy.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strlcpy.c,v 1.13 2015/08/31 02:53:57 guenther Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1998, 2015 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | /* 23 | * Copy string src to buffer dst of size dsize. At most dsize-1 24 | * chars will be copied. Always NUL terminates (unless dsize == 0). 25 | * Returns strlen(src); if retval >= dsize, truncation occurred. 26 | */ 27 | 28 | __attribute__((visibility("default"))) 29 | size_t 30 | strlcpy(char *dst, const char *src, size_t dsize) 31 | { 32 | const char *osrc = src; 33 | size_t nleft = dsize; 34 | 35 | /* Copy as many bytes as will fit. */ 36 | if (nleft != 0) { 37 | while (--nleft != 0) { 38 | if ((*dst++ = *src++) == '\0') 39 | break; 40 | } 41 | } 42 | 43 | /* Not enough room in dst, add NUL and traverse rest of src. */ 44 | if (nleft == 0) { 45 | if (dsize != 0) 46 | *dst = '\0'; /* NUL-terminate dst */ 47 | while (*src++) 48 | ; 49 | } 50 | 51 | return(src - osrc - 1); /* count does not include NUL */ 52 | } 53 | -------------------------------------------------------------------------------- /src/trace-buffer.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2018, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include "trace-buffer.h" 17 | 18 | #include "socketcan.h" 19 | #include "co_atomic.h" 20 | #include "time-utils.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | static inline unsigned long clzl(unsigned long x) 29 | { 30 | return __builtin_clzl(x); 31 | } 32 | 33 | static inline unsigned long round_up_to_power_of_2(unsigned long x) 34 | { 35 | return 1UL << ((sizeof(x) << 3) - clzl(x - 1UL)); 36 | } 37 | 38 | static inline int tb_try_block(struct tracebuffer* self) 39 | { 40 | return co_atomic_cas(&self->is_blocked, 0, 1); 41 | } 42 | 43 | static inline void tb_unblock(struct tracebuffer* self) 44 | { 45 | co_atomic_store(&self->is_blocked, 0); 46 | } 47 | 48 | static inline int tb_is_blocked(const struct tracebuffer* self) 49 | { 50 | return co_atomic_load(&self->is_blocked); 51 | } 52 | 53 | int tb_init(struct tracebuffer* self, size_t size) 54 | { 55 | memset(self, 0, sizeof(*self)); 56 | 57 | self->length = round_up_to_power_of_2(size / sizeof(self->data[0])); 58 | self->data = malloc(self->length * sizeof(self->data[0])); 59 | 60 | return self->data ? 0 : -1; 61 | } 62 | 63 | void tb_destroy(struct tracebuffer* self) 64 | { 65 | free(self->data); 66 | } 67 | 68 | void tb_append(struct tracebuffer* self, const struct can_frame* frame) 69 | { 70 | if (tb_is_blocked(self)) 71 | return; 72 | 73 | struct tb_frame tb_frame = { 74 | .timestamp = gettime_us(CLOCK_REALTIME), 75 | .cf = *frame, 76 | }; 77 | 78 | self->data[self->index++] = tb_frame; 79 | self->index &= (self->length - 1); 80 | 81 | if (self->count < self->length) 82 | self->count++; 83 | } 84 | 85 | void tb_dump(struct tracebuffer* self, FILE* stream) 86 | { 87 | if (!tb_try_block(self)) 88 | return; 89 | 90 | if (self->count < self->length) { 91 | fwrite(self->data, sizeof(self->data[0]), self->count, stream); 92 | } else { 93 | fwrite(&self->data[self->index], sizeof(self->data[0]), 94 | self->length - self->index, stream); 95 | 96 | fwrite(self->data, sizeof(self->data[0]), self->index, stream); 97 | } 98 | 99 | tb_unblock(self); 100 | 101 | fflush(stream); 102 | } 103 | -------------------------------------------------------------------------------- /src/types.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include "canopen/types.h" 18 | 19 | #define XSTR(s) STR(s) 20 | #define STR(s) #s 21 | 22 | size_t canopen_type_size(enum canopen_type type) 23 | { 24 | switch (type) { 25 | #define X(name, number, size) case CANOPEN_ ## name: return size; 26 | CANOPEN_TYPES 27 | #undef X 28 | } 29 | 30 | return 0; 31 | } 32 | 33 | int canopen_type_is_signed_integer(enum canopen_type type) 34 | { 35 | switch (type) { 36 | case CANOPEN_INTEGER8: 37 | case CANOPEN_INTEGER16: 38 | case CANOPEN_INTEGER24: 39 | case CANOPEN_INTEGER32: 40 | case CANOPEN_INTEGER40: 41 | case CANOPEN_INTEGER48: 42 | case CANOPEN_INTEGER56: 43 | case CANOPEN_INTEGER64: 44 | return 1; 45 | default: 46 | return 0; 47 | } 48 | } 49 | 50 | int canopen_type_is_unsigned_integer(enum canopen_type type) 51 | { 52 | switch (type) { 53 | case CANOPEN_UNSIGNED8: 54 | case CANOPEN_UNSIGNED16: 55 | case CANOPEN_UNSIGNED24: 56 | case CANOPEN_UNSIGNED32: 57 | case CANOPEN_UNSIGNED40: 58 | case CANOPEN_UNSIGNED48: 59 | case CANOPEN_UNSIGNED56: 60 | case CANOPEN_UNSIGNED64: 61 | return 1; 62 | default: 63 | return 0; 64 | } 65 | } 66 | 67 | const char* canopen_type_to_string(enum canopen_type type) 68 | { 69 | switch (type) { 70 | #define X(name, ...) case CANOPEN_ ## name: return XSTR(name); 71 | CANOPEN_TYPES 72 | #undef X 73 | } 74 | 75 | return NULL; 76 | } 77 | 78 | enum canopen_type canopen_type_from_string(const char* str) 79 | { 80 | #define X(name, ...) \ 81 | if (strcasecmp(str, XSTR(name)) == 0) \ 82 | return CANOPEN_ ## name; 83 | 84 | CANOPEN_TYPES 85 | #undef X 86 | 87 | return CANOPEN_UNKNOWN; 88 | } 89 | -------------------------------------------------------------------------------- /target.postinst: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TYPE=$1 4 | 5 | configure() 6 | { 7 | pluto_init_add -r -c canopen-master -a can0 -n canopen-master-can0 \ 8 | >/dev/null 9 | pluto_init_add -r -c canopen-master -a "can1 -i1 -R9192" \ 10 | -n canopen-master-can1 \ 11 | >/dev/null 12 | } 13 | 14 | case $TYPE in 15 | configure) 16 | configure 17 | ;; 18 | *) 19 | ;; 20 | esac 21 | 22 | exit 0 23 | -------------------------------------------------------------------------------- /target.prerm: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TYPE=$1 4 | 5 | remove() 6 | { 7 | pluto_init_remove canopen-master-can0 8 | pluto_init_remove canopen-master-can1 9 | } 10 | 11 | case $TYPE in 12 | remove) 13 | remove 14 | ;; 15 | *) 16 | ;; 17 | esac 18 | 19 | exit 0 20 | -------------------------------------------------------------------------------- /test/sdo_async_fuzz_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "canopen/sdo_async.h" 6 | #include "net-util.h" 7 | #include "canopen.h" 8 | #include "fff.h" 9 | 10 | DEFINE_FFF_GLOBALS; 11 | 12 | struct mloop; 13 | 14 | struct mloop_timer { 15 | int dummy; 16 | }; 17 | 18 | FAKE_VALUE_FUNC(struct mloop*, mloop_default); 19 | FAKE_VALUE_FUNC(struct mloop_timer*, mloop_timer_new, struct mloop*); 20 | FAKE_VALUE_FUNC(int, mloop_timer_start, struct mloop_timer*); 21 | FAKE_VALUE_FUNC(int, mloop_timer_stop, struct mloop_timer*); 22 | FAKE_VALUE_FUNC(int, mloop_timer_unref, struct mloop_timer*); 23 | FAKE_VOID_FUNC(mloop_timer_set_time, struct mloop_timer*, uint64_t); 24 | FAKE_VOID_FUNC(mloop_timer_set_context, struct mloop_timer*, void*, 25 | mloop_free_fn); 26 | FAKE_VOID_FUNC(mloop_timer_set_callback, struct mloop_timer*, mloop_timer_fn); 27 | FAKE_VALUE_FUNC(void*, mloop_timer_get_context, const struct mloop_timer*); 28 | FAKE_VOID_FUNC(on_done, struct sdo_async*); 29 | FAKE_VALUE_FUNC(ssize_t, net_write_frame, int, const struct can_frame*, int); 30 | 31 | struct mloop_timer timer; 32 | 33 | static struct sdo_async client; 34 | 35 | static void initialize() 36 | { 37 | static struct sock sock = { .fd = 10, .type = SOCK_TYPE_CAN }; 38 | mloop_timer_new_fake.return_val = &timer; 39 | sdo_async_init(&client, &sock, 42); 40 | } 41 | 42 | static void cleanup() 43 | { 44 | sdo_async_destroy(&client); 45 | } 46 | 47 | int upload_fuzz() 48 | { 49 | struct sdo_async_info info = { 50 | .type = SDO_REQ_UPLOAD, 51 | .index = 0x1234, 52 | .subindex = 42, 53 | .timeout = 1000, 54 | }; 55 | 56 | assert(sdo_async_start(&client, &info) == 0); 57 | 58 | while (client.is_running) { 59 | struct can_frame cf; 60 | sdo_clear_frame(&cf); 61 | cf.can_id = R_TSDO + 42; 62 | cf.can_dlc = rand() % 8; 63 | *(int*)cf.data = rand(); 64 | int cs = rand() % 5; 65 | if (cs == SDO_SCS_UL_INIT_RES) { 66 | sdo_set_index(&cf, 0x1234); 67 | sdo_set_subindex(&cf, 42); 68 | } 69 | sdo_set_cs(&cf, cs); 70 | sdo_async_feed(&client, &cf); 71 | } 72 | 73 | return 0; 74 | } 75 | 76 | static char dl_data[4096] = { 0 }; 77 | 78 | int download_fuzz() 79 | { 80 | struct sdo_async_info info = { 81 | .type = SDO_REQ_DOWNLOAD, 82 | .index = 0x1234, 83 | .subindex = 42, 84 | .timeout = 1000, 85 | .data = dl_data, 86 | .size = sizeof(dl_data) 87 | }; 88 | 89 | assert(sdo_async_start(&client, &info) == 0); 90 | 91 | while (client.is_running) { 92 | struct can_frame cf; 93 | sdo_clear_frame(&cf); 94 | cf.can_id = R_TSDO + 42; 95 | cf.can_dlc = rand() % 8; 96 | *(int*)cf.data = rand(); 97 | int cs = rand() % 5; 98 | if (cs == SDO_SCS_DL_INIT_RES) { 99 | sdo_set_index(&cf, 0x1234); 100 | sdo_set_subindex(&cf, 42); 101 | } 102 | sdo_set_cs(&cf, cs); 103 | sdo_async_feed(&client, &cf); 104 | } 105 | 106 | return 0; 107 | } 108 | 109 | int main() 110 | { 111 | initialize(); 112 | 113 | for (int i = 0; i < 100000; ++i) 114 | upload_fuzz(); 115 | 116 | for (int i = 0; i < 100000; ++i) 117 | download_fuzz(); 118 | 119 | cleanup(); 120 | return 0; 121 | } 122 | -------------------------------------------------------------------------------- /test/unit_arc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "tst.h" 4 | #include "arc.h" 5 | 6 | struct arc_test { 7 | int ref; 8 | int dummy; 9 | }; 10 | 11 | static inline struct arc_test* arc_test_new(void) 12 | { 13 | struct arc_test* self = malloc(sizeof(*self)); 14 | if (!self) 15 | return NULL; 16 | 17 | memset(self, 0, sizeof(*self)); 18 | 19 | self->ref = 1; 20 | 21 | return self; 22 | } 23 | 24 | static inline void arc_test_free_fn(struct arc_test* self) 25 | { 26 | free(self); 27 | } 28 | 29 | ARC_GENERATE(arc_test, arc_test_free_fn); 30 | 31 | static int test_ref_unref(void) 32 | { 33 | struct arc_test* test = arc_test_new(); 34 | ASSERT_TRUE(test); 35 | 36 | ASSERT_INT_EQ(1, test->ref); 37 | 38 | ASSERT_INT_EQ(2, arc_test_ref(test)); 39 | ASSERT_INT_EQ(2, test->ref); 40 | 41 | ASSERT_INT_EQ(1, arc_test_unref(test)); 42 | ASSERT_INT_EQ(1, test->ref); 43 | 44 | ASSERT_INT_EQ(0, arc_test_unref(test)); 45 | 46 | return 0; 47 | } 48 | 49 | int main() 50 | { 51 | int r = 0; 52 | RUN_TEST(test_ref_unref); 53 | return r; 54 | } 55 | 56 | -------------------------------------------------------------------------------- /test/unit_cfg.c: -------------------------------------------------------------------------------- 1 | #include "tst.h" 2 | #include "cfg.h" 3 | #include "canopen/master.h" 4 | 5 | int cfg__load_stream(FILE* stream); 6 | 7 | static int test_priority_order(void) 8 | { 9 | const char* text = 10 | "[all]\n" 11 | "a=1\n" 12 | "b=2\n" 13 | "c=3\n" 14 | "[=mynode]\n" 15 | "a=4\n" 16 | "b=5\n" 17 | "[#42]\n" 18 | "a=6\n" 19 | ""; 20 | 21 | struct co_master_node* node; 22 | 23 | node = co_master_get_node(23); 24 | strcpy(node->name, "foobar"); 25 | 26 | node = co_master_get_node(42); 27 | strcpy(node->name, "mynode"); 28 | 29 | FILE* stream = fmemopen((void*)text, strlen(text) + 1, "r"); 30 | cfg__load_stream(stream); 31 | fclose(stream); 32 | 33 | ASSERT_STR_EQ("1", cfg__file_read(23, "a")); 34 | ASSERT_STR_EQ("2", cfg__file_read(23, "b")); 35 | ASSERT_STR_EQ("3", cfg__file_read(23, "c")); 36 | 37 | ASSERT_STR_EQ("6", cfg__file_read(42, "a")); 38 | ASSERT_STR_EQ("5", cfg__file_read(42, "b")); 39 | ASSERT_STR_EQ("3", cfg__file_read(42, "c")); 40 | 41 | cfg_unload_file(); 42 | return 0; 43 | } 44 | 45 | int main() 46 | { 47 | int r = 0; 48 | RUN_TEST(test_priority_order); 49 | return r; 50 | } 51 | -------------------------------------------------------------------------------- /test/unit_error.c: -------------------------------------------------------------------------------- 1 | #include "tst.h" 2 | 3 | #include "canopen/error.h" 4 | 5 | static int test_cia402_monitoring_torque_too_high() 6 | { 7 | ASSERT_STR_EQ("Monitoring: Torque: Too high", 8 | error_code_to_string(0x8311, 402)); 9 | ASSERT_STR_EQ("Monitoring: Torque", 10 | error_code_to_string(0x8300, 402)); 11 | ASSERT_STR_EQ("Monitoring", 12 | error_code_to_string(0x8000, 402)); 13 | return 0; 14 | } 15 | 16 | static int test_cia302_monitoring_rpdo_timeout() 17 | { 18 | ASSERT_STR_EQ("Monitoring: Protocol error: RPDO timeout: PDO1", 19 | error_code_to_string(0x8251, 302)); 20 | ASSERT_STR_EQ("Monitoring: Protocol error: RPDO timeout", 21 | error_code_to_string(0x8250, 302)); 22 | ASSERT_STR_EQ("Monitoring: Protocol error", 23 | error_code_to_string(0x8200, 302)); 24 | ASSERT_STR_EQ("Monitoring", 25 | error_code_to_string(0x8000, 302)); 26 | return 0; 27 | } 28 | 29 | static int test_cia302_reset() 30 | { 31 | ASSERT_STR_EQ("Error reset or no error", error_code_to_string(0, 302)); 32 | return 0; 33 | } 34 | 35 | int main() 36 | { 37 | int r = 0; 38 | RUN_TEST(test_cia402_monitoring_torque_too_high); 39 | RUN_TEST(test_cia302_monitoring_rpdo_timeout); 40 | RUN_TEST(test_cia302_reset); 41 | return r; 42 | } 43 | -------------------------------------------------------------------------------- /test/unit_http.c: -------------------------------------------------------------------------------- 1 | #include "tst.h" 2 | #include "http.h" 3 | 4 | int test_get_empty_path() 5 | { 6 | const char* text = "GET / HTTP/1.1\r\n\r\nasdf"; 7 | 8 | struct http_req req; 9 | ASSERT_INT_EQ(0, http_req_parse(&req, text)); 10 | 11 | ASSERT_INT_EQ(HTTP_GET, req.method); 12 | ASSERT_INT_EQ(0, req.url_index); 13 | ASSERT_UINT_EQ(strlen(text)-4, req.header_length); 14 | 15 | http_req_free(&req); 16 | return 0; 17 | } 18 | 19 | int test_get_one_elem_path() 20 | { 21 | const char* text = "GET /foo HTTP/1.1\r\n\r\nasdf"; 22 | 23 | struct http_req req; 24 | ASSERT_INT_EQ(0, http_req_parse(&req, text)); 25 | 26 | ASSERT_INT_EQ(HTTP_GET, req.method); 27 | ASSERT_INT_EQ(1, req.url_index); 28 | ASSERT_STR_EQ("foo", req.url[0]); 29 | ASSERT_UINT_EQ(strlen(text)-4, req.header_length); 30 | 31 | http_req_free(&req); 32 | return 0; 33 | } 34 | 35 | int test_get_two_elem_path() 36 | { 37 | const char* text = "GET /foo/bar HTTP/1.1\r\n\r\nasdf"; 38 | 39 | struct http_req req; 40 | ASSERT_INT_EQ(0, http_req_parse(&req, text)); 41 | 42 | ASSERT_INT_EQ(HTTP_GET, req.method); 43 | ASSERT_INT_EQ(2, req.url_index); 44 | ASSERT_STR_EQ("foo", req.url[0]); 45 | ASSERT_STR_EQ("bar", req.url[1]); 46 | ASSERT_UINT_EQ(strlen(text)-4, req.header_length); 47 | 48 | http_req_free(&req); 49 | return 0; 50 | } 51 | 52 | int test_put_empty_path() 53 | { 54 | const char* text = "PUT / HTTP/1.1\r\n\r\nasdf"; 55 | 56 | struct http_req req; 57 | ASSERT_INT_EQ(0, http_req_parse(&req, text)); 58 | 59 | ASSERT_INT_EQ(HTTP_PUT, req.method); 60 | ASSERT_INT_EQ(0, req.url_index); 61 | ASSERT_UINT_EQ(strlen(text)-4, req.header_length); 62 | 63 | http_req_free(&req); 64 | return 0; 65 | } 66 | 67 | int test_put_with_content_length() 68 | { 69 | const char* text = 70 | "PUT / HTTP/1.1\r\n" 71 | "Content-Length: 42\r\n" 72 | "\r\n"; 73 | 74 | struct http_req req; 75 | ASSERT_INT_EQ(0, http_req_parse(&req, text)); 76 | 77 | ASSERT_INT_EQ(HTTP_PUT, req.method); 78 | ASSERT_INT_EQ(0, req.url_index); 79 | ASSERT_UINT_EQ(42, req.content_length); 80 | ASSERT_UINT_EQ(strlen(text), req.header_length); 81 | 82 | http_req_free(&req); 83 | return 0; 84 | } 85 | 86 | int test_get_with_content_type() 87 | { 88 | const char* text = 89 | "GET / HTTP/1.1\r\n" 90 | "Content-Type: foo/bar\r\n" 91 | "\r\n"; 92 | 93 | struct http_req req; 94 | ASSERT_INT_EQ(0, http_req_parse(&req, text)); 95 | 96 | ASSERT_INT_EQ(HTTP_GET, req.method); 97 | ASSERT_INT_EQ(0, req.url_index); 98 | ASSERT_STR_EQ("foo/bar", req.content_type); 99 | ASSERT_UINT_EQ(strlen(text), req.header_length); 100 | 101 | http_req_free(&req); 102 | return 0; 103 | } 104 | 105 | int test_get_with_single_query() 106 | { 107 | const char* text = "GET /path?key=value HTTP/1.1\r\n\r\nasdf"; 108 | 109 | struct http_req req; 110 | ASSERT_INT_EQ(0, http_req_parse(&req, text)); 111 | 112 | ASSERT_INT_EQ(HTTP_GET, req.method); 113 | ASSERT_INT_EQ(1, req.url_index); 114 | ASSERT_STR_EQ("path", req.url[0]); 115 | ASSERT_UINT_EQ(strlen(text)-4, req.header_length); 116 | 117 | ASSERT_INT_EQ(1, req.url_query_index); 118 | ASSERT_STR_EQ("key", req.url_query[0].key); 119 | ASSERT_STR_EQ("value", req.url_query[0].value); 120 | 121 | http_req_free(&req); 122 | return 0; 123 | } 124 | 125 | int test_get_with_two_query_pairs() 126 | { 127 | const char* text = "GET /path?foo=bar&asdf=xyz HTTP/1.1\r\n\r\nasdf"; 128 | 129 | struct http_req req; 130 | ASSERT_INT_EQ(0, http_req_parse(&req, text)); 131 | 132 | ASSERT_INT_EQ(HTTP_GET, req.method); 133 | ASSERT_INT_EQ(1, req.url_index); 134 | ASSERT_STR_EQ("path", req.url[0]); 135 | ASSERT_UINT_EQ(strlen(text)-4, req.header_length); 136 | 137 | ASSERT_INT_EQ(2, req.url_query_index); 138 | ASSERT_STR_EQ("foo", req.url_query[0].key); 139 | ASSERT_STR_EQ("bar", req.url_query[0].value); 140 | 141 | ASSERT_STR_EQ("asdf", req.url_query[1].key); 142 | ASSERT_STR_EQ("xyz", req.url_query[1].value); 143 | 144 | http_req_free(&req); 145 | return 0; 146 | } 147 | 148 | int main() 149 | { 150 | int r = 0; 151 | RUN_TEST(test_get_empty_path); 152 | RUN_TEST(test_get_one_elem_path); 153 | RUN_TEST(test_get_two_elem_path); 154 | RUN_TEST(test_put_empty_path); 155 | RUN_TEST(test_put_with_content_length); 156 | RUN_TEST(test_get_with_content_type); 157 | RUN_TEST(test_get_with_single_query); 158 | RUN_TEST(test_get_with_two_query_pairs); 159 | return r; 160 | } 161 | -------------------------------------------------------------------------------- /test/unit_init_parser.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "tst.h" 6 | #include "ini_parser.h" 7 | 8 | FILE* rstream; 9 | FILE* wstream; 10 | 11 | static void setup() 12 | { 13 | int fds[2]; 14 | pipe(fds); 15 | fcntl(fds[0], F_SETFL, fcntl(fds[0], F_GETFL, 0) | O_NONBLOCK); 16 | fcntl(fds[1], F_SETFL, fcntl(fds[1], F_GETFL, 0) | O_NONBLOCK); 17 | rstream = fdopen(fds[0], "r"); 18 | wstream = fdopen(fds[1], "w"); 19 | setvbuf(rstream, NULL, _IONBF, 0); 20 | setvbuf(wstream, NULL, _IONBF, 0); 21 | } 22 | 23 | static void cleanup() 24 | { 25 | fclose(rstream); 26 | fclose(wstream); 27 | } 28 | 29 | static int test_simple_file() 30 | { 31 | const char* text = 32 | "; a simple test file\n" 33 | "\n" 34 | "[Section]\n" 35 | "Foo=bar\n" 36 | "x=y"; 37 | 38 | fprintf(wstream, "%s", text); 39 | fflush(wstream); 40 | 41 | struct ini_file file; 42 | ASSERT_INT_EQ(0, ini_parse(&file, rstream)); 43 | 44 | ASSERT_STR_EQ("bar", ini_find(&file, "section", "foo")); 45 | ASSERT_STR_EQ("y", ini_find(&file, "section", "x")); 46 | 47 | ini_destroy(&file); 48 | 49 | return 0; 50 | } 51 | 52 | int main() 53 | { 54 | int r = 0; 55 | setup(); 56 | 57 | RUN_TEST(test_simple_file); 58 | 59 | cleanup(); 60 | return r; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /test/unit_network.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "tst.h" 6 | #include "fff.h" 7 | #include "canopen/network.h" 8 | #include "net-util.h" 9 | #include "canopen/nmt.h" 10 | #include "canopen/sdo.h" 11 | #include "canopen/heartbeat.h" 12 | #include "socketcan.h" 13 | #include "canopen.h" 14 | #include "sock.h" 15 | 16 | DEFINE_FFF_GLOBALS; 17 | 18 | FAKE_VALUE_FUNC(int, poll, struct pollfd*, nfds_t, int); 19 | FAKE_VALUE_FUNC(ssize_t, read, int, void*, size_t); 20 | FAKE_VALUE_FUNC(ssize_t, write, int, const void*, size_t); 21 | FAKE_VALUE_FUNC(int, clock_gettime, clockid_t, struct timespec*); 22 | 23 | int test_net_write() 24 | { 25 | RESET_FAKE(poll); 26 | RESET_FAKE(write); 27 | 28 | poll_fake.return_val = 1; 29 | write_fake.return_val = 5; 30 | 31 | ASSERT_INT_EQ(5, net_write(42, (const void*)0xdeadbeef, 5, 1337)); 32 | 33 | ASSERT_INT_EQ(42, write_fake.arg0_val); 34 | ASSERT_PTR_EQ((void*)0xdeadbeef, (void*)write_fake.arg1_val); 35 | ASSERT_INT_EQ(5, write_fake.arg2_val); 36 | 37 | ASSERT_INT_EQ(1, poll_fake.arg1_val); 38 | ASSERT_INT_EQ(1337, poll_fake.arg2_val); 39 | 40 | return 0; 41 | } 42 | 43 | int test_net_read() 44 | { 45 | RESET_FAKE(poll); 46 | RESET_FAKE(read); 47 | 48 | poll_fake.return_val = 1; 49 | read_fake.return_val = 5; 50 | 51 | ASSERT_INT_EQ(5, net_read(42, (void*)0xdeadbeef, 5, 1337)); 52 | 53 | ASSERT_INT_EQ(42, read_fake.arg0_val); 54 | ASSERT_PTR_EQ((void*)0xdeadbeef, read_fake.arg1_val); 55 | ASSERT_INT_EQ(5, read_fake.arg2_val); 56 | 57 | ASSERT_INT_EQ(1, poll_fake.arg1_val); 58 | ASSERT_INT_EQ(1337, poll_fake.arg2_val); 59 | 60 | return 0; 61 | } 62 | 63 | struct can_frame saved_can_frame_; 64 | 65 | ssize_t save_can_frame(int fd, const void* addr, size_t size) 66 | { 67 | (void)fd; 68 | (void)size; 69 | 70 | memcpy(&saved_can_frame_, addr, sizeof(saved_can_frame_)); 71 | 72 | return sizeof(saved_can_frame_); 73 | } 74 | 75 | int test_net__send_nmt() 76 | { 77 | RESET_FAKE(poll); 78 | RESET_FAKE(write); 79 | memset(&saved_can_frame_, 0, sizeof(saved_can_frame_)); 80 | 81 | write_fake.custom_fake = save_can_frame; 82 | 83 | poll_fake.return_val = 1; 84 | write_fake.return_val = sizeof(struct can_frame); 85 | 86 | struct sock sock = { .fd = 42, .type = SOCK_TYPE_CAN }; 87 | 88 | ASSERT_INT_GE(0, co_net_send_nmt(&sock, 7, 11)); 89 | 90 | ASSERT_INT_EQ(42, write_fake.arg0_val); 91 | ASSERT_INT_EQ(sizeof(struct can_frame), write_fake.arg2_val); 92 | 93 | ASSERT_INT_EQ(0, saved_can_frame_.can_id); 94 | ASSERT_INT_EQ(2, saved_can_frame_.can_dlc); 95 | 96 | ASSERT_INT_EQ(7, nmt_get_cs(&saved_can_frame_)); 97 | ASSERT_INT_EQ(11, nmt_get_nodeid(&saved_can_frame_)); 98 | 99 | return 0; 100 | } 101 | 102 | static struct timespec gettime_timespec; 103 | 104 | int custom_gettime(clockid_t id, struct timespec* ts) 105 | { 106 | (void)id; 107 | *ts = gettime_timespec; 108 | return 0; 109 | } 110 | 111 | static char enabled_nodes[128]; 112 | int enabled_nodes_index; 113 | 114 | static ssize_t read_bootup(int fd, void* dst, size_t size) 115 | { 116 | (void)fd; 117 | (void)size; 118 | 119 | struct can_frame* cf = dst; 120 | 121 | while (enabled_nodes_index < 128 && !enabled_nodes[enabled_nodes_index]) 122 | ++enabled_nodes_index; 123 | 124 | if (enabled_nodes_index > 127) 125 | return -1; 126 | 127 | cf->can_id = R_HEARTBEAT + enabled_nodes_index; 128 | heartbeat_set_state(cf, NMT_STATE_BOOTUP); 129 | 130 | ++enabled_nodes_index; 131 | 132 | return sizeof(struct can_frame); 133 | } 134 | 135 | int test_net__wait_for_bootup() 136 | { 137 | RESET_FAKE(poll); 138 | RESET_FAKE(read); 139 | RESET_FAKE(clock_gettime); 140 | 141 | read_fake.custom_fake = read_bootup; 142 | poll_fake.return_val = 1; 143 | 144 | memset(enabled_nodes, 0, sizeof(enabled_nodes)); 145 | enabled_nodes_index = 0; 146 | 147 | enabled_nodes[0] = 1; 148 | enabled_nodes[7] = 1; 149 | enabled_nodes[127] = 1; 150 | 151 | struct sock sock = { .fd = 42, .type = SOCK_TYPE_CAN }; 152 | 153 | char nodes_seen[128]; 154 | memset(nodes_seen, 0, sizeof(nodes_seen)); 155 | ASSERT_INT_EQ(0, co_net__wait_for_bootup(&sock, nodes_seen, 0, 127, 156 | 1000)); 157 | 158 | ASSERT_INT_EQ(4, poll_fake.call_count); 159 | ASSERT_INT_EQ(4, read_fake.call_count); 160 | ASSERT_INT_EQ(42, read_fake.arg0_val); 161 | 162 | ASSERT_TRUE(nodes_seen[0]); 163 | ASSERT_FALSE(nodes_seen[1]); 164 | ASSERT_FALSE(nodes_seen[2]); 165 | ASSERT_FALSE(nodes_seen[6]); 166 | ASSERT_TRUE(nodes_seen[7]); 167 | ASSERT_FALSE(nodes_seen[8]); 168 | ASSERT_FALSE(nodes_seen[126]); 169 | ASSERT_TRUE(nodes_seen[127]); 170 | 171 | return 0; 172 | } 173 | 174 | int main() 175 | { 176 | int r = 0; 177 | RUN_TEST(test_net_write); 178 | RUN_TEST(test_net_read); 179 | // RUN_TEST(test_net__send_nmt); 180 | // RUN_TEST(test_net__wait_for_bootup); 181 | return r; 182 | } 183 | 184 | -------------------------------------------------------------------------------- /test/unit_sdo-dict.c: -------------------------------------------------------------------------------- 1 | #include "tst.h" 2 | 3 | #include "canopen/sdo-dict.h" 4 | 5 | static int test_sdo_dict_tostring(void) 6 | { 7 | ASSERT_STR_EQ("device-type", sdo_dict_tostring(SDO_MUX(0x1000, 0))); 8 | ASSERT_STR_EQ("time-cob-id", sdo_dict_tostring(SDO_MUX(0x1012, 0))); 9 | ASSERT_STR_EQ("product-code", sdo_dict_tostring(SDO_MUX(0x1018, 2))); 10 | return 0; 11 | } 12 | 13 | static int test_sdo_dict_fromstring(void) 14 | { 15 | ASSERT_UINT_EQ(SDO_MUX(0x1001, 0), sdo_dict_fromstring("error-register")); 16 | ASSERT_UINT_EQ(SDO_MUX(0x1018, 1), sdo_dict_fromstring("vendor-id")); 17 | return 0; 18 | } 19 | 20 | static int test_sdo_dict_type(void) 21 | { 22 | ASSERT_UINT_EQ(CANOPEN_UNSIGNED32, sdo_dict_type(SDO_MUX(0x1000, 0))); 23 | ASSERT_UINT_EQ(CANOPEN_VISIBLE_STRING, sdo_dict_type(SDO_MUX(0x1008, 0))); 24 | return 0; 25 | } 26 | 27 | int main() 28 | { 29 | int r = 0; 30 | RUN_TEST(test_sdo_dict_tostring); 31 | RUN_TEST(test_sdo_dict_fromstring); 32 | RUN_TEST(test_sdo_dict_type); 33 | return r; 34 | } 35 | 36 | -------------------------------------------------------------------------------- /test/unit_sdo_req.c: -------------------------------------------------------------------------------- 1 | #include "tst.h" 2 | #include "fff.h" 3 | #include "canopen/sdo_req.h" 4 | #include "sock.h" 5 | 6 | DEFINE_FFF_GLOBALS; 7 | 8 | FAKE_VOID_FUNC(mloop_iterate, struct mloop*); 9 | FAKE_VALUE_FUNC(struct mloop*, mloop_default); 10 | FAKE_VALUE_FUNC(struct mloop_idle*, mloop_idle_new, struct mloop*); 11 | FAKE_VALUE_FUNC(int, mloop_idle_start, struct mloop_idle*); 12 | FAKE_VALUE_FUNC(int, mloop_idle_unref, struct mloop_idle*); 13 | FAKE_VOID_FUNC(mloop_idle_set_context, struct mloop_idle*, void*, 14 | mloop_free_fn); 15 | FAKE_VALUE_FUNC(void*, mloop_idle_get_context, const struct mloop_idle*); 16 | FAKE_VOID_FUNC(mloop_idle_set_idle_fn, struct mloop_idle*, mloop_idle_fn); 17 | FAKE_VOID_FUNC(mloop_idle_set_cond_fn, struct mloop_idle*, mloop_idle_cond_fn); 18 | FAKE_VOID_FUNC(mloop_idle_set_priority, struct mloop_idle*, unsigned long); 19 | FAKE_VALUE_FUNC(int, sdo_async_init, struct sdo_async*, const struct sock*, 20 | int); 21 | FAKE_VALUE_FUNC(int, sdo_async_stop, struct sdo_async*); 22 | FAKE_VOID_FUNC(sdo_async_destroy, struct sdo_async*); 23 | FAKE_VALUE_FUNC(int, sdo_async_start, struct sdo_async*, 24 | const struct sdo_async_info*); 25 | 26 | static int test_req_new_free() 27 | { 28 | int dl_data = 1337; 29 | 30 | struct sdo_req_info info = { 31 | .type = SDO_REQ_DOWNLOAD, 32 | .index = 0x1234, 33 | .subindex = 42, 34 | .on_done = (sdo_req_fn)0xdeadbeef, 35 | .dl_data = &dl_data, 36 | .dl_size = sizeof(int) 37 | }; 38 | 39 | struct sdo_req* req = sdo_req_new(&info); 40 | 41 | ASSERT_INT_EQ(SDO_REQ_DOWNLOAD, req->type); 42 | ASSERT_INT_EQ(0x1234, req->index); 43 | ASSERT_INT_EQ(42, req->subindex); 44 | ASSERT_PTR_EQ((void*)0xdeadbeef, req->on_done); 45 | 46 | ASSERT_INT_EQ(1337, *(int*)req->data.data); 47 | ASSERT_INT_EQ(sizeof(int), req->data.index); 48 | 49 | sdo_req_free(req); 50 | return 0; 51 | } 52 | 53 | static int test_req_queue_init_destroy() 54 | { 55 | RESET_FAKE(sdo_async_init); 56 | sdo_async_init_fake.return_val = 0; 57 | 58 | struct mloop_idle* idle = (void*)0xdeadbeef; 59 | 60 | RESET_FAKE(mloop_idle_new); 61 | mloop_idle_new_fake.return_val = idle; 62 | 63 | struct sock sock = { .fd = 4, .type = SOCK_TYPE_CAN }; 64 | 65 | struct sdo_req_queue queue; 66 | ASSERT_INT_EQ(0, sdo_req__queue_init(&queue, &sock, 42, 10, 0)); 67 | 68 | ASSERT_INT_EQ(4, sdo_async_init_fake.arg1_val->fd); 69 | ASSERT_INT_EQ(SOCK_TYPE_CAN, sdo_async_init_fake.arg1_val->type); 70 | ASSERT_INT_EQ(42, sdo_async_init_fake.arg2_val); 71 | ASSERT_INT_EQ(10, queue.limit); 72 | 73 | sdo_req__queue_destroy(&queue); 74 | 75 | return 0; 76 | } 77 | 78 | static int test_req_queue_enqueue_dequeue() 79 | { 80 | RESET_FAKE(sdo_async_init); 81 | sdo_async_init_fake.return_val = 0; 82 | 83 | struct sdo_req_queue queue; 84 | sdo_req__queue_init(&queue, 0, 0, 3, 0); 85 | 86 | struct sdo_req req[4]; 87 | memset(req, 0, sizeof(req)); 88 | 89 | ASSERT_INT_EQ(0, sdo_req_queue__enqueue(&queue, &req[0])); 90 | ASSERT_INT_EQ(0, sdo_req_queue__enqueue(&queue, &req[1])); 91 | ASSERT_INT_EQ(0, sdo_req_queue__enqueue(&queue, &req[2])); 92 | ASSERT_INT_LT(0, sdo_req_queue__enqueue(&queue, &req[3])); 93 | 94 | ASSERT_PTR_EQ(&queue, req[0].parent); 95 | ASSERT_PTR_EQ(&queue, req[1].parent); 96 | ASSERT_PTR_EQ(&queue, req[2].parent); 97 | ASSERT_PTR_EQ(NULL, req[3].parent); 98 | 99 | ASSERT_PTR_EQ(&req[0], sdo_req_queue__dequeue(&queue)); 100 | ASSERT_PTR_EQ(&req[1], sdo_req_queue__dequeue(&queue)); 101 | ASSERT_PTR_EQ(&req[2], sdo_req_queue__dequeue(&queue)); 102 | ASSERT_PTR_EQ(NULL, sdo_req_queue__dequeue(&queue)); 103 | 104 | sdo_req__queue_destroy(&queue); 105 | return 0; 106 | } 107 | 108 | static int test_req_queue_from_async() 109 | { 110 | struct sdo_req_queue queue; 111 | ASSERT_PTR_EQ(&queue, sdo_req_queue__from_async(&queue.sdo_client)); 112 | return 0; 113 | } 114 | 115 | int main() 116 | { 117 | int r = 0; 118 | RUN_TEST(test_req_new_free); 119 | RUN_TEST(test_req_queue_init_destroy); 120 | RUN_TEST(test_req_queue_enqueue_dequeue); 121 | RUN_TEST(test_req_queue_from_async); 122 | return r; 123 | } 124 | -------------------------------------------------------------------------------- /test/unit_string-utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "string-utils.h" 3 | #include "tst.h" 4 | 5 | static char* string(const char* str) 6 | { 7 | static char buffer[256]; 8 | strncpy(buffer, str, sizeof(buffer)); 9 | return buffer; 10 | } 11 | 12 | static int test_trim_left() 13 | { 14 | ASSERT_STR_EQ("foo bar", string_trim_left(string("foo bar"))); 15 | ASSERT_STR_EQ("foo bar", string_trim_left(string("\t foo bar"))); 16 | ASSERT_STR_EQ("foo bar ", string_trim_left(string("\t foo bar "))); 17 | return 0; 18 | } 19 | 20 | static int test_trim_right() 21 | { 22 | ASSERT_STR_EQ("foo bar", string_trim_right(string("foo bar"))); 23 | ASSERT_STR_EQ("foo bar", string_trim_right(string("foo bar \t"))); 24 | ASSERT_STR_EQ(" foo bar", string_trim_right(string(" foo bar \t"))); 25 | return 0; 26 | } 27 | 28 | static int test_trim() 29 | { 30 | ASSERT_STR_EQ("foo bar", string_trim(string("foo bar"))); 31 | ASSERT_STR_EQ("foo bar", string_trim(string("foo bar \t"))); 32 | ASSERT_STR_EQ("foo bar", string_trim(string(" \tfoo bar \t"))); 33 | return 0; 34 | } 35 | 36 | static int test_is_empty() 37 | { 38 | ASSERT_TRUE(string_is_empty("")); 39 | ASSERT_FALSE(string_is_empty("asdf")); 40 | return 0; 41 | } 42 | 43 | static int test_keep_if_alpha() 44 | { 45 | ASSERT_STR_EQ("foobar", string_keep_if(isalpha, string(".f.o.o.b.a.r."))); 46 | ASSERT_STR_EQ("", string_keep_if(isalpha, string("12345"))); 47 | return 0; 48 | } 49 | 50 | static int test_string_begins_with() 51 | { 52 | ASSERT_FALSE(string_begins_with("foo", "")); 53 | ASSERT_FALSE(string_begins_with("foo", "f")); 54 | ASSERT_FALSE(string_begins_with("foo", "fo")); 55 | ASSERT_TRUE(string_begins_with("foo", "foo")); 56 | ASSERT_TRUE(string_begins_with("foo", "foobar")); 57 | return 0; 58 | } 59 | 60 | static int test_string_ends_with() 61 | { 62 | ASSERT_FALSE(string_ends_with("bar", "foo")); 63 | ASSERT_FALSE(string_ends_with("bar", "foor")); 64 | ASSERT_FALSE(string_ends_with("bar", "fooar")); 65 | ASSERT_TRUE(string_ends_with("bar", "foobar")); 66 | ASSERT_TRUE(string_ends_with("bar", "bar")); 67 | ASSERT_TRUE(string_ends_with("r", "foobar")); 68 | return 0; 69 | } 70 | 71 | static int test_string_replace_char() 72 | { 73 | ASSERT_STR_EQ("a.b.c.d", string_replace_char(',', '.', string("a,b,c,d"))); 74 | return 0; 75 | } 76 | 77 | int main() 78 | { 79 | int r = 0; 80 | RUN_TEST(test_trim_left); 81 | RUN_TEST(test_trim_right); 82 | RUN_TEST(test_trim); 83 | RUN_TEST(test_is_empty); 84 | RUN_TEST(test_keep_if_alpha); 85 | RUN_TEST(test_string_begins_with); 86 | RUN_TEST(test_string_ends_with); 87 | RUN_TEST(test_string_replace_char); 88 | return r; 89 | } 90 | -------------------------------------------------------------------------------- /test/unit_trace-buffer.c: -------------------------------------------------------------------------------- 1 | #include "tst.h" 2 | #include "trace-buffer.h" 3 | 4 | #include "socketcan.h" 5 | 6 | #include 7 | 8 | int test_incomplete_buffer(void) 9 | { 10 | struct tracebuffer tb; 11 | 12 | ASSERT_INT_GE(0, tb_init(&tb, 3 * sizeof(struct tb_frame))); 13 | ASSERT_INT_EQ(4, tb.length); 14 | 15 | struct can_frame cf = { 0 }; 16 | cf.can_id = 1; 17 | tb_append(&tb, &cf); 18 | cf.can_id = 2; 19 | tb_append(&tb, &cf); 20 | 21 | struct tb_frame* buffer; 22 | size_t size; 23 | FILE* stream = open_memstream((char**)&buffer, &size); 24 | 25 | tb_dump(&tb, stream); 26 | 27 | ASSERT_INT_EQ(1, buffer[0].cf.can_id); 28 | ASSERT_INT_EQ(2, buffer[1].cf.can_id); 29 | 30 | fclose(stream); 31 | free(buffer); 32 | tb_destroy(&tb); 33 | return 0; 34 | } 35 | 36 | int test_full_buffer(void) 37 | { 38 | struct tracebuffer tb; 39 | 40 | ASSERT_INT_GE(0, tb_init(&tb, 3 * sizeof(struct tb_frame))); 41 | ASSERT_INT_EQ(4, tb.length); 42 | 43 | struct can_frame cf = { 0 }; 44 | 45 | for (int i = 0; i < 6; ++i) 46 | tb_append(&tb, &cf); 47 | 48 | for (int i = 0; i < 4; ++i) { 49 | cf.can_id = i + 1; 50 | tb_append(&tb, &cf); 51 | } 52 | 53 | struct tb_frame* buffer; 54 | size_t size; 55 | FILE* stream = open_memstream((char**)&buffer, &size); 56 | 57 | tb_dump(&tb, stream); 58 | 59 | ASSERT_INT_EQ(1, buffer[0].cf.can_id); 60 | ASSERT_INT_EQ(2, buffer[1].cf.can_id); 61 | ASSERT_INT_EQ(3, buffer[2].cf.can_id); 62 | 63 | fclose(stream); 64 | free(buffer); 65 | tb_destroy(&tb); 66 | return 0; 67 | } 68 | 69 | int main() 70 | { 71 | int r = 0; 72 | RUN_TEST(test_incomplete_buffer); 73 | RUN_TEST(test_full_buffer); 74 | return r; 75 | } 76 | -------------------------------------------------------------------------------- /test/unit_types.c: -------------------------------------------------------------------------------- 1 | #include "tst.h" 2 | 3 | #include "canopen/types.h" 4 | 5 | static int test_type_size(void) 6 | { 7 | ASSERT_UINT_EQ(1, canopen_type_size(CANOPEN_BOOLEAN)); 8 | ASSERT_UINT_EQ(1, canopen_type_size(CANOPEN_INTEGER8)); 9 | ASSERT_UINT_EQ(2, canopen_type_size(CANOPEN_INTEGER16)); 10 | ASSERT_UINT_EQ(3, canopen_type_size(CANOPEN_INTEGER24)); 11 | ASSERT_UINT_EQ(4, canopen_type_size(CANOPEN_INTEGER32)); 12 | ASSERT_UINT_EQ(5, canopen_type_size(CANOPEN_INTEGER40)); 13 | ASSERT_UINT_EQ(6, canopen_type_size(CANOPEN_INTEGER48)); 14 | ASSERT_UINT_EQ(7, canopen_type_size(CANOPEN_INTEGER56)); 15 | ASSERT_UINT_EQ(8, canopen_type_size(CANOPEN_INTEGER64)); 16 | return 0; 17 | } 18 | 19 | static int test_type_to_string(void) 20 | { 21 | ASSERT_STR_EQ("REAL32", canopen_type_to_string(CANOPEN_REAL32)); 22 | ASSERT_STR_EQ("BOOLEAN", canopen_type_to_string(CANOPEN_BOOLEAN)); 23 | ASSERT_FALSE(canopen_type_to_string(1337)); 24 | return 0; 25 | } 26 | 27 | static int test_type_from_string(void) 28 | { 29 | ASSERT_UINT_EQ(CANOPEN_REAL32, canopen_type_from_string("REAL32")); 30 | ASSERT_UINT_EQ(CANOPEN_BOOLEAN, canopen_type_from_string("boolean")); 31 | ASSERT_UINT_EQ(CANOPEN_UNKNOWN, canopen_type_from_string("foobar")); 32 | return 0; 33 | } 34 | 35 | int main() 36 | { 37 | int r = 0; 38 | RUN_TEST(test_type_size); 39 | RUN_TEST(test_type_to_string); 40 | RUN_TEST(test_type_from_string); 41 | return r; 42 | } 43 | -------------------------------------------------------------------------------- /test/unit_vector.c: -------------------------------------------------------------------------------- 1 | #include "tst.h" 2 | #include "vector.h" 3 | 4 | static int test_init_destroy() 5 | { 6 | struct vector vector; 7 | ASSERT_INT_EQ(0, vector_init(&vector, 42)); 8 | ASSERT_UINT_EQ(0, vector.index); 9 | ASSERT_UINT_EQ(42, vector.size); 10 | ASSERT_TRUE(vector.data); 11 | vector_destroy(&vector); 12 | return 0; 13 | } 14 | 15 | static int test_reserve_less() 16 | { 17 | struct vector vector; 18 | vector_init(&vector, 42); 19 | ASSERT_UINT_EQ(42, vector.size); 20 | vector_reserve(&vector, 5); 21 | ASSERT_UINT_EQ(42, vector.size); 22 | vector_reserve(&vector, 42); 23 | ASSERT_UINT_EQ(42, vector.size); 24 | vector_destroy(&vector); 25 | return 0; 26 | } 27 | 28 | static int test_reserve_more() 29 | { 30 | struct vector vector; 31 | vector_init(&vector, 42); 32 | ASSERT_UINT_EQ(42, vector.size); 33 | vector_reserve(&vector, 43); 34 | ASSERT_UINT_GE(43, vector.size); 35 | vector_destroy(&vector); 36 | return 0; 37 | } 38 | 39 | static int test_append_nothing() 40 | { 41 | struct vector vector; 42 | vector_init(&vector, 42); 43 | vector_append(&vector, "", 0); 44 | ASSERT_UINT_EQ(0, vector.index); 45 | ASSERT_UINT_EQ(42, vector.size); 46 | vector_destroy(&vector); 47 | return 0; 48 | } 49 | 50 | static int test_append_something() 51 | { 52 | struct vector vector; 53 | vector_init(&vector, 42); 54 | vector_append(&vector, "a", 1); 55 | ASSERT_UINT_EQ(1, vector.index); 56 | ASSERT_UINT_EQ(42, vector.size); 57 | ASSERT_INT_EQ('a', *(char*)vector.data); 58 | vector_destroy(&vector); 59 | return 0; 60 | } 61 | 62 | static int test_vector_clear() 63 | { 64 | struct vector vector; 65 | vector.index = 42; 66 | vector_clear(&vector); 67 | ASSERT_UINT_EQ(0, vector.index); 68 | return 0; 69 | } 70 | 71 | static int test_vector_assign_once() 72 | { 73 | struct vector vector; 74 | vector_init(&vector, 1); 75 | char* str = "foobar"; 76 | size_t size = strlen(str) + 1; 77 | vector_assign(&vector, str, size); 78 | ASSERT_UINT_GE(size, vector.size); 79 | ASSERT_UINT_EQ(size, vector.index); 80 | ASSERT_STR_EQ("foobar", vector.data); 81 | vector_destroy(&vector); 82 | return 0; 83 | } 84 | 85 | static int test_vector_assign_twice() 86 | { 87 | struct vector vector; 88 | vector_init(&vector, 1); 89 | char* str1 = "foo"; 90 | char* str2 = "bar"; 91 | size_t size1 = strlen(str1) + 1; 92 | size_t size2 = strlen(str2) + 1; 93 | vector_assign(&vector, str1, size1); 94 | ASSERT_STR_EQ("foo", vector.data); 95 | ASSERT_INT_EQ(4, vector.index); 96 | vector_assign(&vector, str2, size2); 97 | ASSERT_STR_EQ("bar", vector.data); 98 | ASSERT_INT_EQ(4, vector.index); 99 | vector_destroy(&vector); 100 | return 0; 101 | } 102 | 103 | static int test_vector_fill() 104 | { 105 | struct vector vector; 106 | vector_init(&vector, 1); 107 | vector_fill(&vector, 0, 4); 108 | vector_fill(&vector, 'a', 3); 109 | ASSERT_STR_EQ("aaa", vector.data); 110 | ASSERT_INT_EQ(3, vector.index); 111 | vector_destroy(&vector); 112 | return 0; 113 | } 114 | 115 | int main() 116 | { 117 | int r = 0; 118 | RUN_TEST(test_init_destroy); 119 | RUN_TEST(test_reserve_less); 120 | RUN_TEST(test_reserve_more); 121 | RUN_TEST(test_append_nothing); 122 | RUN_TEST(test_append_something); 123 | RUN_TEST(test_vector_clear); 124 | RUN_TEST(test_vector_assign_once); 125 | RUN_TEST(test_vector_assign_twice); 126 | RUN_TEST(test_vector_fill); 127 | return r; 128 | } 129 | -------------------------------------------------------------------------------- /valgrind.supp: -------------------------------------------------------------------------------- 1 | { 2 | dlopen bug. 3 | Memcheck:Leak 4 | ... 5 | fun:_dlerror_run 6 | ... 7 | } 8 | { 9 | Piece of shit log4c crap. 10 | Memcheck:Leak 11 | ... 12 | fun:sd_*alloc 13 | ... 14 | } 15 | { 16 | Ignore leak due to missing dlclose. We cannot call dlclose when using valgrind. 17 | Memcheck:Leak 18 | ... 19 | fun:_dl_open 20 | ... 21 | } 22 | -------------------------------------------------------------------------------- /vnodes/mcs816.ini: -------------------------------------------------------------------------------- 1 | [device] 2 | node_guarding=yes 3 | heartbeat=no 4 | bootup=legacy 5 | 6 | ; Device type 7 | [1000sub0] 8 | type=UNSIGNED32 9 | value=0x401 10 | 11 | ; Device name 12 | [1008sub0] 13 | type=VISIBLE_STRING 14 | value=MCS816 15 | 16 | ; HW version 17 | [1009sub0] 18 | type=VISIBLE_STRING 19 | value=canopen-vnode 20 | 21 | ; SW version 22 | [100Asub0] 23 | type=VISIBLE_STRING 24 | value=canopen-vnode 25 | 26 | -------------------------------------------------------------------------------- /vnodes/mcv14.ini: -------------------------------------------------------------------------------- 1 | [device] 2 | node_guarding=yes 3 | heartbeat=yes 4 | bootup=both 5 | 6 | ; Device type 7 | [1000sub0] 8 | type=UNSIGNED32 9 | value=0x401 10 | 11 | ; Device name 12 | [1008sub0] 13 | type=VISIBLE_STRING 14 | value=MCV14 15 | 16 | ; HW version 17 | [1009sub0] 18 | type=VISIBLE_STRING 19 | value=canopen-vnode 20 | 21 | ; SW version 22 | [100asub0] 23 | type=VISIBLE_STRING 24 | value=canopen-vnode 25 | 26 | -------------------------------------------------------------------------------- /vnodes/mws2.ini: -------------------------------------------------------------------------------- 1 | [device] 2 | node_guarding=yes 3 | heartbeat=no 4 | bootup=legacy 5 | guard_status_bug=yes 6 | 7 | ; Device type 8 | [1000sub0] 9 | type=UNSIGNED32 10 | value=0x401 11 | 12 | ; Device name 13 | [1008sub0] 14 | type=VISIBLE_STRING 15 | value=MWS2 16 | 17 | ; HW version 18 | [1009sub0] 19 | type=VISIBLE_STRING 20 | value=canopen-vnode 21 | 22 | ; SW version 23 | [100asub0] 24 | type=VISIBLE_STRING 25 | value=canopen-vnode 26 | 27 | --------------------------------------------------------------------------------