Module: xenomai-forge Branch: next Commit: 3e52ecb465f5e3e25fb42d600da50e66b5b0f17b URL: http://git.xenomai.org/?p=xenomai-forge.git;a=commit;h=3e52ecb465f5e3e25fb42d600da50e66b5b0f17b
Author: Philippe Gerum <r...@xenomai.org> Date: Fri Jun 13 10:21:52 2014 +0200 demo/posix: add CAN round-trip time measurement program --- demo/posix/Makefile.am | 24 +++ demo/posix/Makefile.in | 192 +++++++++++++++-- demo/posix/can-rtt.c | 427 +++++++++++++++++++++++++++++++++++++ doc/doxygen/manual-common.conf.in | 7 +- include/rtdm/can.h | 2 +- 5 files changed, 633 insertions(+), 19 deletions(-) diff --git a/demo/posix/Makefile.am b/demo/posix/Makefile.am index 7af79ba..dca7e90 100644 --- a/demo/posix/Makefile.am +++ b/demo/posix/Makefile.am @@ -1,4 +1,28 @@ +demodir = @XENO_DEMO_DIR@ + +CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC) +core_libs = if XENO_COBALT SUBDIRS = cobalt +core_libs += ../../lib/cobalt/libcobalt.la endif + +demo_PROGRAMS = \ + can-rtt + +cppflags = \ + $(XENO_USER_CFLAGS) \ + -I$(top_srcdir)/include + +ldflags = $(XENO_POSIX_WRAPPERS) + +ldadd = \ + $(core_libs) \ + @XENO_USER_LDADD@ \ + -lpthread -lrt + +can_rtt_SOURCES = can-rtt.c +can_rtt_CPPFLAGS = $(cppflags) +can_rtt_LDFLAGS = $(ldflags) +can_rtt_LDADD = $(ldadd) diff --git a/demo/posix/Makefile.in b/demo/posix/Makefile.in index 801ccc2..722b750 100644 --- a/demo/posix/Makefile.in +++ b/demo/posix/Makefile.in @@ -13,6 +13,7 @@ # PARTICULAR PURPOSE. @SET_MAKE@ + VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ @@ -78,8 +79,11 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ +@XENO_COBALT_TRUE@am__append_1 = ../../lib/cobalt/libcobalt.la +demo_PROGRAMS = can-rtt$(EXEEXT) subdir = demo/posix -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/config/depcomp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/config/ac_prog_cc_for_build.m4 \ $(top_srcdir)/config/libtool.m4 \ @@ -94,6 +98,19 @@ mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/xeno_config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(demodir)" +PROGRAMS = $(demo_PROGRAMS) +am_can_rtt_OBJECTS = can_rtt-can-rtt.$(OBJEXT) +can_rtt_OBJECTS = $(am_can_rtt_OBJECTS) +am__DEPENDENCIES_1 = $(core_libs) +can_rtt_DEPENDENCIES = $(am__DEPENDENCIES_1) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +can_rtt_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(can_rtt_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false @@ -106,8 +123,29 @@ AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = -SOURCES = -DIST_SOURCES = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/include +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(can_rtt_SOURCES) +DIST_SOURCES = $(can_rtt_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ @@ -338,10 +376,28 @@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ +demodir = @XENO_DEMO_DIR@ +CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC) +core_libs = $(am__append_1) @XENO_COBALT_TRUE@SUBDIRS = cobalt +cppflags = \ + $(XENO_USER_CFLAGS) \ + -I$(top_srcdir)/include + +ldflags = $(XENO_POSIX_WRAPPERS) +ldadd = \ + $(core_libs) \ + @XENO_USER_LDADD@ \ + -lpthread -lrt + +can_rtt_SOURCES = can-rtt.c +can_rtt_CPPFLAGS = $(cppflags) +can_rtt_LDFLAGS = $(ldflags) +can_rtt_LDADD = $(ldadd) all: all-recursive .SUFFIXES: +.SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -372,6 +428,102 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): +install-demoPROGRAMS: $(demo_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(demo_PROGRAMS)'; test -n "$(demodir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(demodir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(demodir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(demodir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(demodir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-demoPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(demo_PROGRAMS)'; test -n "$(demodir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(demodir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(demodir)" && rm -f $$files + +clean-demoPROGRAMS: + @list='$(demo_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +can-rtt$(EXEEXT): $(can_rtt_OBJECTS) $(can_rtt_DEPENDENCIES) $(EXTRA_can_rtt_DEPENDENCIES) + @rm -f can-rtt$(EXEEXT) + $(AM_V_CCLD)$(can_rtt_LINK) $(can_rtt_OBJECTS) $(can_rtt_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/can_rtt-can-rtt.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +can_rtt-can-rtt.o: can-rtt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(can_rtt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT can_rtt-can-rtt.o -MD -MP -MF $(DEPDIR)/can_rtt-can-rtt.Tpo -c -o can_rtt-can-rtt.o `test -f 'can-rtt.c' || echo '$(srcdir)/'`can-rtt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/can_rtt-can-rtt.Tpo $(DEPDIR)/can_rtt-can-rtt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='can-rtt.c' object='can_rtt-can-rtt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(can_rtt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o can_rtt-can-rtt.o `test -f 'can-rtt.c' || echo '$(srcdir)/'`can-rtt.c + +can_rtt-can-rtt.obj: can-rtt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(can_rtt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT can_rtt-can-rtt.obj -MD -MP -MF $(DEPDIR)/can_rtt-can-rtt.Tpo -c -o can_rtt-can-rtt.obj `if test -f 'can-rtt.c'; then $(CYGPATH_W) 'can-rtt.c'; else $(CYGPATH_W) '$(srcdir)/can-rtt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/can_rtt-can-rtt.Tpo $(DEPDIR)/can_rtt-can-rtt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='can-rtt.c' object='can_rtt-can-rtt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(can_rtt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o can_rtt-can-rtt.obj `if test -f 'can-rtt.c'; then $(CYGPATH_W) 'can-rtt.c'; else $(CYGPATH_W) '$(srcdir)/can-rtt.c'; fi` mostlyclean-libtool: -rm -f *.lo @@ -535,9 +687,12 @@ distdir: $(DISTFILES) done check-am: all-am check: check-recursive -all-am: Makefile +all-am: Makefile $(PROGRAMS) installdirs: installdirs-recursive installdirs-am: + for dir in "$(DESTDIR)$(demodir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive @@ -570,11 +725,14 @@ maintainer-clean-generic: @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive -clean-am: clean-generic clean-libtool mostlyclean-am +clean-am: clean-demoPROGRAMS clean-generic clean-libtool \ + mostlyclean-am distclean: distclean-recursive + -rm -rf ./$(DEPDIR) -rm -f Makefile -distclean-am: clean-am distclean-generic distclean-tags +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags dvi: dvi-recursive @@ -588,7 +746,7 @@ info: info-recursive info-am: -install-data-am: +install-data-am: install-demoPROGRAMS install-dvi: install-dvi-recursive @@ -617,12 +775,14 @@ install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive -mostlyclean-am: mostlyclean-generic mostlyclean-libtool +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf: pdf-recursive @@ -632,22 +792,24 @@ ps: ps-recursive ps-am: -uninstall-am: +uninstall-am: uninstall-demoPROGRAMS .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ - check-am clean clean-generic clean-libtool cscopelist-am ctags \ - ctags-am distclean distclean-generic distclean-libtool \ - distclean-tags distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am install-dvi \ + check-am clean clean-demoPROGRAMS clean-generic clean-libtool \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-demoPROGRAMS install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ - ps ps-am tags tags-am uninstall uninstall-am + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-demoPROGRAMS # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/demo/posix/can-rtt.c b/demo/posix/can-rtt.c new file mode 100644 index 0000000..d1e49f0 --- /dev/null +++ b/demo/posix/can-rtt.c @@ -0,0 +1,427 @@ +/* + * Round-Trip-Time Test - sends and receives messages and measures the + * time in between. + * + * Copyright (C) 2006 Wolfgang Grandegger <w...@grandegger.com> + * + * Based on RTnet's examples/xenomai/posix/rtt-sender.c. + * + * Copyright (C) 2002 Ulrich Marx <m...@kammer.uni-hannover.de> + * 2002 Marc Kleine-Budde <kleine-bu...@gmx.de> + * 2006 Jan Kiszka <jan.kis...@web.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * The program sends out CAN messages periodically and copies the current + * time-stamp to the payload. At reception, that time-stamp is compared + * with the current time to determine the round-trip time. The jitter + * values are printer out regularly. Concurrent tests can be carried out + * by starting the program with different message identifiers. It is also + * possible to use this program on a remote system as simple repeater to + * loopback messages. + */ + +#include <errno.h> +#include <mqueue.h> +#include <signal.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#include <getopt.h> +#include <netinet/in.h> +#include <net/if.h> +#include <sys/ioctl.h> +#include <sys/mman.h> + +#ifdef __XENO__ +#include <rtdm/can.h> +#else +#include <linux/can.h> +#include <linux/can/raw.h> +#endif + +#define NSEC_PER_SEC 1000000000 + +static unsigned int cycle = 10000; /* 10 ms */ +static canid_t can_id = 0x1; + +static pthread_t txthread, rxthread; +static int txsock, rxsock; +static mqd_t mq; +static int txcount, rxcount; +static int overruns; +static int repeater; + +struct rtt_stat { + long long rtt; + long long rtt_min; + long long rtt_max; + long long rtt_sum; + long long rtt_sum_last; + int counts_per_sec; +}; + +static void print_usage(char *prg) +{ + fprintf(stderr, + "Usage: %s [Options] <tx-can-interface> <rx-can-interface>\n" + "Options:\n" + " -h, --help This help\n" + " -r, --repeater Repeater, send back received messages\n" + " -i, --id=ID CAN Identifier (default = 0x1)\n" + " -c, --cycle Cycle time in us (default = 10000us)\n", + prg); +} + +static void *transmitter(void *arg) +{ + struct sched_param param = { .sched_priority = 80 }; + struct timespec next_period; + struct timespec time; + struct can_frame frame; + long long *rtt_time = (long long *)&frame.data; + + /* Pre-fill CAN frame */ + frame.can_id = can_id; + frame.can_dlc = sizeof(*rtt_time); + +#ifdef __XENO__ + pthread_set_name_np(pthread_self(), "rtcan_rtt_transmitter"); +#endif + pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m); + + clock_gettime(CLOCK_MONOTONIC, &next_period); + + while(1) { + next_period.tv_nsec += cycle * 1000; + while (next_period.tv_nsec >= NSEC_PER_SEC) { + next_period.tv_nsec -= NSEC_PER_SEC; + next_period.tv_sec++; + } + + clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_period, NULL); + + if (rxcount != txcount) { + overruns++; + continue; + } + + clock_gettime(CLOCK_MONOTONIC, &time); + *rtt_time = (long long)time.tv_sec * NSEC_PER_SEC + time.tv_nsec; + + /* Transmit the message containing the local time */ + if (send(txsock, (void *)&frame, sizeof(struct can_frame), 0) < 0) { + if (errno == EBADF) + printf("terminating transmitter thread\n"); + else + perror("send failed"); + return NULL; + } + txcount++; + } +} + + +static void *receiver(void *arg) +{ + struct sched_param param = { .sched_priority = 82 }; + struct timespec time; + struct can_frame frame; + long long *rtt_time = (long long *)frame.data; + struct rtt_stat rtt_stat = {0, 1000000000000000000LL, -1000000000000000000LL, + 0, 0, 0}; + +#ifdef __XENO__ + pthread_set_name_np(pthread_self(), "rtcan_rtt_receiver"); +#endif + pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m); + + rtt_stat.counts_per_sec = 1000000 / cycle; + + while (1) { + if (recv(rxsock, (void *)&frame, sizeof(struct can_frame), 0) < 0) { + if (errno == EBADF) + printf("terminating receiver thread\n"); + else + perror("recv failed"); + return NULL; + } + if (repeater) { + /* Transmit the message back as is */ + if (send(txsock, (void *)&frame, sizeof(struct can_frame), 0) < 0) { + if (errno == EBADF) + printf("terminating transmitter thread\n"); + else + perror("send failed"); + return NULL; + } + txcount++; + } else { + clock_gettime(CLOCK_MONOTONIC, &time); + if (rxcount > 0) { + rtt_stat.rtt = ((long long)time.tv_sec * 1000000000LL + + time.tv_nsec - *rtt_time); + rtt_stat.rtt_sum += rtt_stat.rtt; + if (rtt_stat.rtt < rtt_stat.rtt_min) + rtt_stat.rtt_min = rtt_stat.rtt; + if (rtt_stat.rtt > rtt_stat.rtt_max) + rtt_stat.rtt_max = rtt_stat.rtt; + } + } + rxcount++; + + if ((rxcount % rtt_stat.counts_per_sec) == 0) { + mq_send(mq, (char *)&rtt_stat, sizeof(rtt_stat), 0); + rtt_stat.rtt_sum_last = rtt_stat.rtt_sum; + } + } +} + +static void catch_signal(int sig) +{ + mq_close(mq); +} + + +int main(int argc, char *argv[]) +{ + struct sched_param param = { .sched_priority = 1 }; + pthread_attr_t thattr; + struct mq_attr mqattr; + struct sockaddr_can rxaddr, txaddr; + struct can_filter rxfilter[1]; + struct rtt_stat rtt_stat; + char mqname[32]; + char *txdev, *rxdev; + struct ifreq ifr; + int ret, opt; + + struct option long_options[] = { + { "id", required_argument, 0, 'i'}, + { "cycle", required_argument, 0, 'c'}, + { "repeater", no_argument, 0, 'r'}, + { "help", no_argument, 0, 'h'}, + { 0, 0, 0, 0}, + }; + + while ((opt = getopt_long(argc, argv, "hri:c:", + long_options, NULL)) != -1) { + switch (opt) { + case 'c': + cycle = atoi(optarg); + break; + + case 'i': + can_id = strtoul(optarg, NULL, 0); + break; + + case 'r': + repeater = 1; + break; + + default: + fprintf(stderr, "Unknown option %c\n", opt); + case 'h': + print_usage(argv[0]); + exit(-1); + } + } + + printf("%d %d\n", optind, argc); + if (optind + 2 != argc) { + print_usage(argv[0]); + exit(0); + } + + txdev = argv[optind]; + rxdev = argv[optind + 1]; + + /* Create and configure RX socket */ + if ((rxsock = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + perror("RX socket failed"); + return -1; + } + + strncpy(ifr.ifr_name, rxdev, IFNAMSIZ); + printf("RX rxsock=%d, ifr_name=%s\n", rxsock, ifr.ifr_name); + + if (ioctl(rxsock, SIOCGIFINDEX, &ifr) < 0) { + perror("RX ioctl SIOCGIFINDEX failed"); + goto failure1; + } + + /* We only want to receive our own messages */ + rxfilter[0].can_id = can_id; + rxfilter[0].can_mask = 0x3ff; + if (setsockopt(rxsock, SOL_CAN_RAW, CAN_RAW_FILTER, + &rxfilter, sizeof(struct can_filter)) < 0) { + perror("RX setsockopt CAN_RAW_FILTER failed"); + goto failure1; + } + memset(&rxaddr, 0, sizeof(rxaddr)); + rxaddr.can_ifindex = ifr.ifr_ifindex; + rxaddr.can_family = AF_CAN; + if (bind(rxsock, (struct sockaddr *)&rxaddr, sizeof(rxaddr)) < 0) { + perror("RX bind failed\n"); + goto failure1; + } + + /* Create and configure TX socket */ + + if (strcmp(rxdev, txdev) == 0) { + txsock = rxsock; + } else { + if ((txsock = socket(PF_CAN, SOCK_RAW, 0)) < 0) { + perror("TX socket failed"); + goto failure1; + } + + strncpy(ifr.ifr_name, txdev, IFNAMSIZ); + printf("TX txsock=%d, ifr_name=%s\n", txsock, ifr.ifr_name); + + if (ioctl(txsock, SIOCGIFINDEX, &ifr) < 0) { + perror("TX ioctl SIOCGIFINDEX failed"); + goto failure2; + } + + /* Suppress definiton of a default receive filter list */ + if (setsockopt(txsock, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0) < 0) { + perror("TX setsockopt CAN_RAW_FILTER failed"); + goto failure2; + } + + memset(&txaddr, 0, sizeof(txaddr)); + txaddr.can_ifindex = ifr.ifr_ifindex; + txaddr.can_family = AF_CAN; + + if (bind(txsock, (struct sockaddr *)&txaddr, sizeof(txaddr)) < 0) { + perror("TX bind failed\n"); + goto failure2; + } + } + + signal(SIGTERM, catch_signal); + signal(SIGINT, catch_signal); + signal(SIGHUP, catch_signal); + mlockall(MCL_CURRENT|MCL_FUTURE); + + printf("Round-Trip-Time test %s -> %s with CAN ID 0x%x\n", + argv[optind], argv[optind + 1], can_id); + printf("Cycle time: %d us\n", cycle); + printf("All RTT timing figures are in us.\n"); + + /* Create statistics message queue */ + snprintf(mqname, sizeof(mqname), "/rtcan_rtt-%d", getpid()); + mqattr.mq_flags = 0; + mqattr.mq_maxmsg = 100; + mqattr.mq_msgsize = sizeof(struct rtt_stat); + mq = mq_open(mqname, O_RDWR | O_CREAT | O_EXCL, 0600, &mqattr); + if (mq == (mqd_t)-1) { + perror("opening mqueue failed"); + goto failure2; + } + + /* Create receiver RT-thread */ + pthread_attr_init(&thattr); + pthread_attr_setdetachstate(&thattr, PTHREAD_CREATE_JOINABLE); + pthread_attr_setstacksize(&thattr, PTHREAD_STACK_MIN); + ret = pthread_create(&rxthread, &thattr, &receiver, NULL); + if (ret) { + fprintf(stderr, "%s: pthread_create(receiver) failed\n", + strerror(-ret)); + goto failure3; + } + + if (!repeater) { + /* Create transitter RT-thread */ + ret = pthread_create(&txthread, &thattr, &transmitter, NULL); + if (ret) { + fprintf(stderr, "%s: pthread_create(transmitter) failed\n", + strerror(-ret)); + goto failure4; + } + } + + pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m); + + if (repeater) + printf("Messages\n"); + else + printf("Messages RTTlast RTT_avg RTT_min RTT_max Overruns\n"); + + while (1) { + long long rtt_avg; + + ret = mq_receive(mq, (char *)&rtt_stat, sizeof(rtt_stat), NULL); + if (ret != sizeof(rtt_stat)) { + if (ret < 0) { + if (errno == EBADF) + printf("terminating mq_receive\n"); + else + perror("mq_receive failed"); + } else + fprintf(stderr, + "mq_receive returned invalid length %d\n", ret); + break; + } + + if (repeater) { + printf("%8d\n", rxcount); + } else { + rtt_avg = ((rtt_stat.rtt_sum - rtt_stat.rtt_sum_last) / + rtt_stat.counts_per_sec); + printf("%8d %7ld %7ld %7ld %7ld %8d\n", rxcount, + (long)(rtt_stat.rtt / 1000), (long)(rtt_avg / 1000), + (long)(rtt_stat.rtt_min / 1000), + (long)(rtt_stat.rtt_max / 1000), + overruns); + } + } + + /* This call also leaves primary mode, required for socket cleanup. */ + printf("shutting down\n"); + + /* Important: First close the sockets! */ + while ((close(rxsock) < 0) && (errno == EAGAIN)) { + printf("RX socket busy - waiting...\n"); + sleep(1); + } + while ((close(txsock) < 0) && (errno == EAGAIN)) { + printf("TX socket busy - waiting...\n"); + sleep(1); + } + + pthread_join(txthread, NULL); + pthread_kill(rxthread, SIGHUP); + pthread_join(rxthread, NULL); + + return 0; + + failure4: + pthread_kill(rxthread, SIGHUP); + pthread_join(rxthread, NULL); + failure3: + mq_close(mq); + failure2: + close(txsock); + failure1: + close(rxsock); + + return 1; +} diff --git a/doc/doxygen/manual-common.conf.in b/doc/doxygen/manual-common.conf.in index 7cbb1d1..370d867 100644 --- a/doc/doxygen/manual-common.conf.in +++ b/doc/doxygen/manual-common.conf.in @@ -869,9 +869,10 @@ INPUT = \ # directories that contain example code fragments that are included (see # the \include command). -EXAMPLE_PATH = \ - @top_srcdir@/demo/posix \ - @top_srcdir@/demo/alchemy +EXAMPLE_PATH = \ + @top_srcdir@/demo/posix \ + @top_srcdir@/demo/alchemy \ + @top_srcdir@/utils # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by diff --git a/include/rtdm/can.h b/include/rtdm/can.h index a597be8..d70b89e 100644 --- a/include/rtdm/can.h +++ b/include/rtdm/can.h @@ -231,7 +231,7 @@ * @example rtcanconfig.c * @example rtcansend.c * @example rtcanrecv.c - * @example rtcan_rtt.c + * @example can-rtt.c * @} * * @} _______________________________________________ Xenomai-git mailing list Xenomai-git@xenomai.org http://www.xenomai.org/mailman/listinfo/xenomai-git