]> rtime.felk.cvut.cz Git - canping.git/commitdiff
Second version of OCERA casestudies. This version fixes many errors, enhances the...
authorwentasah <wentasah>
Wed, 2 Mar 2005 17:56:29 +0000 (17:56 +0000)
committerwentasah <wentasah>
Wed, 2 Mar 2005 17:56:29 +0000 (17:56 +0000)
12 files changed:
Makefile [new file with mode: 0644]
Makefile.omk [new file with mode: 0644]
doc/canping.ent [new file with mode: 0644]
doc/canping.xml [new file with mode: 0644]
include/ul_itbase.h [new file with mode: 0644]
include/ul_list.h [new file with mode: 0644]
include/ul_listbase.h [new file with mode: 0644]
include/ul_utdefs.h [new file with mode: 0644]
src/Makefile [new file with mode: 0644]
src/Makefile.omk [new file with mode: 0644]
src/canping.c [new file with mode: 0644]
src/canping_rtl.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..f595272
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,14 @@
+# Generic directory or leaf node makefile for OCERA make framework
+
+ifndef MAKERULES_DIR
+MAKERULES_DIR := $(shell ( old_pwd="" ;  while [ ! -e Makefile.rules ] ; do if [ "$$old_pwd" == `pwd`  ] ; then exit 1 ; else old_pwd=`pwd` ; cd -L .. 2>/dev/null ; fi ; done ; pwd ) )
+endif
+
+ifeq ($(MAKERULES_DIR),)
+all : default
+.DEFAULT::
+       @echo -e "\nThe Makefile.rules has not been found in this or partent directory\n"
+else   
+include $(MAKERULES_DIR)/Makefile.rules
+endif
+
diff --git a/Makefile.omk b/Makefile.omk
new file mode 100644 (file)
index 0000000..9c2ad6d
--- /dev/null
@@ -0,0 +1,3 @@
+# -*- makefile -*-
+
+SUBDIRS = src doc
\ No newline at end of file
diff --git a/doc/canping.ent b/doc/canping.ent
new file mode 100644 (file)
index 0000000..343e87a
--- /dev/null
@@ -0,0 +1,5 @@
+<!-- -*- nxml -*- -->
+
+<!ENTITY canping SYSTEM "canping.xml">
+<!-- <!ENTITY canping-man SYSTEM "canping-man.xml"> -->
+
diff --git a/doc/canping.xml b/doc/canping.xml
new file mode 100644 (file)
index 0000000..1f1994d
--- /dev/null
@@ -0,0 +1,583 @@
+<?xml version="1.0" encoding="ISO-8859-2"?>
+<!-- <?xml version="1.0" encoding="ISO-8859-2"?> -->
+<!-- <!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" -->
+<!-- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [ -->
+<!-- <!ENTITY % canping_path "."> -->
+<!-- <!ENTITY % canping_entities SYSTEM "canping.ent"> -->
+<!-- %canping_entities; -->
+<!-- ]> -->
+<chapter>
+  <title>CANping &ndash; a simple LinCAN testing application</title>
+
+  <section>
+    <title>Introduction</title>
+
+    <para>This chapter describes a simple application called
+    <productname>canping</productname>. Its primary goal was to test the
+    LinCAN driver but it also nicely illustrates how to use LinCAN driver in
+    real applications. In addition, it shows some maliciousness of the LinCAN
+    driver.</para>
+
+    <para>Canping is a multithread application, which sends and receives
+    messages in parallel and thus can be used for stress testing of the LinCAN
+    driver. For basic usage, two running instances of canping, each one on a
+    different host, are needed. One instance sends messages and waits for
+    replies and the other instance waits for the sent messages and sends
+    replays with an ID incremented by one. Canping can also be run twice on
+    one host provided that we are either using a virtual CAN device or there
+    are multiple CAN cards/chips in the host.</para>
+  </section>
+
+  <section>
+    <title>CANping manual</title>
+
+    <refentry>
+      <refnamediv>
+        <refname>canping</refname>
+
+        <refpurpose>Multi-thread LinCAN testing utility</refpurpose>
+      </refnamediv>
+
+      <refsynopsisdiv>
+        <cmdsynopsis>
+          <command>canping</command>
+
+          <group>
+            <arg choice="req">-m <replaceable>num</replaceable></arg>
+
+            <arg choice="req">-s <replaceable>num</replaceable></arg>
+          </group>
+
+          <arg>-c <replaceable>count</replaceable></arg>
+
+          <arg>-d <replaceable>device</replaceable></arg>
+
+          <arg>-h</arg>
+
+          <arg>-i <replaceable>id</replaceable></arg>
+
+          <arg>-l <replaceable>length</replaceable></arg>
+
+          <arg>-o</arg>
+
+          <arg>-t <replaceable>timeout</replaceable></arg>
+
+          <arg>-y <replaceable>count</replaceable></arg>
+        </cmdsynopsis>
+      </refsynopsisdiv>
+
+      <refsect1>
+        <title>Options</title>
+
+        <variablelist>
+          <varlistentry>
+            <term><option>-m <replaceable>num</replaceable></option></term>
+
+            <listitem>
+              <para>Start in the master mode and run
+              <replaceable>num</replaceable> master threads in
+              parallel.</para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>-s <replaceable>num</replaceable></option></term>
+
+            <listitem>
+              <para>Start in the slave mode and run
+              <replaceable>num</replaceable> slave threads in parallel.</para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>-c <replaceable>count</replaceable></option></term>
+
+            <listitem>
+              <para>Every master thread will send only
+              <replaceable>count</replaceable> messages and then finishes.
+              Without this option messages are sent forever and CANping can be
+              terminated by a signal e.g. by pressing a <keycombo>
+                  <keysym>Ctrl+C</keysym>
+                </keycombo> key.</para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>-d <replaceable>device</replaceable></option></term>
+
+            <listitem>
+              <para>CAN device to be used. Default is
+              <filename>/dev/can0</filename>.</para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>-i <replaceable>id</replaceable></option></term>
+
+            <listitem>
+              <para>Select ID of the lowest generated or responded message.
+              Default is 1000. CANping in the master mode will generate
+              messages with IDs <replaceable>id</replaceable>,
+              <replaceable>id</replaceable>+2,
+              <replaceable>id</replaceable>+4, etc. Each master thread will
+              generate different ID. The slave threads will listen to these
+              IDs and will answer with an ID increased by one.</para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>-l <replaceable>len</replaceable></option></term>
+
+            <listitem>
+              <para>Specify a length of the messages. Default is 8 and
+              possible values are from 0 to 8.</para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>-t <replaceable>sec</replaceable></option></term>
+
+            <listitem>
+              <para>A timeout in seconds; this option specifies how long the
+              master thread will wait for the response message from a slave.
+              If the response won't arrive in <replaceable>SEC</replaceable>
+              seconds, the message is considered as loosed.</para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>-v</option></term>
+
+            <listitem>
+              <para>Increase verbosity. Without this option only summary
+              statistics are displayed just before the program finishes. One
+              -v means to display a global status (number of sent messages and
+              timeouts) during program execution. Two -v options display a
+              simple message for every packet in the format <emphasis
+              role="bold">ID:time</emphasis> where time is measured in
+              microseconds. Three -v options display more verbose information
+              about each packet.</para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>-w <replaceable>msec</replaceable></option></term>
+
+            <listitem>
+              <para>Wait time in milliseconds before the master thread sends a
+              next message. Default value is 1000 ms.</para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>-y</option></term>
+
+            <listitem>
+              <para>Synchronize the master threads before sending the first
+              message. When CANping is stared with the high number of master
+              threads, usually the first created thread will begin by sending
+              the messages before the last thread is created. Sometimes it may
+              be useful that every thread will wait before sending anything to
+              the other threads. Only when all threads are created and
+              prepared for sending, sending can start.</para>
+            </listitem>
+          </varlistentry>
+        </variablelist>
+      </refsect1>
+
+      <refsect1>
+        <title>Exit codes</title>
+
+        <para>The <productname>canping</productname> exit code depends on how
+        the program finishes. This feature can be used for (semi)automatic
+        testing of <productname>LinCAN</productname> driver. Exit codes are
+        listed in the table bellow.</para>
+
+        <informaltable frame="none" pgwide="0">
+          <tgroup cols="2">
+            <colspec align="center" colwidth="1cm" />
+
+            <tbody>
+              <row>
+                <entry>0</entry>
+
+                <entry>OK</entry>
+              </row>
+
+              <row>
+                <entry>1</entry>
+
+                <entry>Bad command line parameter</entry>
+              </row>
+
+              <row>
+                <entry>2</entry>
+
+                <entry>Problem with opening LinCAN driver</entry>
+              </row>
+
+              <row>
+                <entry>3</entry>
+
+                <entry>Problem with filter</entry>
+              </row>
+
+              <row>
+                <entry>4</entry>
+
+                <entry>Insufficient memory</entry>
+              </row>
+
+              <row>
+                <entry>5</entry>
+
+                <entry>Read syscall error</entry>
+              </row>
+
+              <row>
+                <entry>6</entry>
+
+                <entry>Write syscall error</entry>
+              </row>
+
+              <row>
+                <entry>7</entry>
+
+                <entry>Select syscall error</entry>
+              </row>
+
+              <row>
+                <entry>8</entry>
+
+                <entry>Flush (ioctl syscall) error</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </informaltable>
+      </refsect1>
+
+      <refsect1>
+        <title>Examples</title>
+
+        <para>On one computer one runs slave with 10 threads:</para>
+
+        <screen><prompt>root@pc104:~%</prompt> canping -d /dev/can1 -s 10</screen>
+
+        <para>and on another computer master is run with 10 threads
+        too:</para>
+
+        <screen><prompt>sojka@glab:~%</prompt> canping -d /dev/can4 -m 10 -v -c 100 -w 10
+Total count: 1000, Timeouts: 0
+Summary statistics:
+Id 1000: count = 100 mean = 8005.16 stddev = 691.01 min = 2022 max = 11133 [us] loss = 0% (0) 
+Id 1002: count = 100 mean = 8013.10 stddev = 641.19 min = 4013 max = 12885 [us] loss = 0% (0) 
+Id 1004: count = 100 mean = 8035.99 stddev = 575.36 min = 5996 max = 13081 [us] loss = 0% (0) 
+Id 1006: count = 100 mean = 8062.38 stddev = 626.46 min = 7005 max = 14061 [us] loss = 0% (0)
+Id 1008: count = 100 mean = 8082.63 stddev = 740.59 min = 5963 max = 15020 [us] loss = 0% (0)
+Id 1010: count = 100 mean = 8089.04 stddev = 831.64 min = 7018 max = 15970 [us] loss  = 0% (0)
+Id 1012: count = 100 mean = 8105.02 stddev = 952.95 min = 7011 max = 16970 [us] loss = 0% (0) 
+Id 1014: count = 100 mean = 8129.57 stddev = 1037.39 min = 7020 max = 17906 [us] loss = 0% (0) 
+Id 1016: count = 100 mean = 8144.46 stddev = 1154.17 min = 7019 max = 18870 [us] loss = 0% (0) 
+Id 1018: count = 100 mean = 8157.85 stddev = 1259.84 min = 5994 max = 19842 [us] loss = 0% (0)</screen>
+
+        <para>At the end of program execution, there are statistics for each
+        packet type (ID). These contain mean round trip times (RTT), standard
+        deviations of RTT, minimal and maximal RTT and packet losses
+        (percentage and absolute).</para>
+      </refsect1>
+    </refentry>
+  </section>
+
+  <section>
+    <title>Implementation details</title>
+
+    <para>The structure of <productname>canping</productname> is as follows.
+    After the initialization and parsing of command line options (function
+    <function>parse_optins()</function>), communicating threads are started.
+    The code of these threads is made up by
+    <function>master_thread()</function> and
+    <function>slave_thread()</function> functions which are described in the
+    following section.</para>
+
+    <para>After these threads are finished (either after pressing
+    <keycap>Ctrl-C</keycap> or due to <parameter>-c</parameter> switch),
+    summary statistics are written to stdout and the application
+    finishes.</para>
+
+    <section>
+      <title>Usage of the LinCAN driver</title>
+
+      <para>This section will cover <function>master_thread()</function>
+      function. Code of <function>slave_thread()</function> is very similar to
+      <function>master_thread()</function>, so it will not be covered here.
+      These functions represent typical usage of the LinCAN driver in
+      applications and if you are looking for examples how to use the LinCAN
+      driver, this is probably the most important section for you.</para>
+
+      <para>The <function>master_thread()</function> function is responsible
+      for opening the driver, sending message with a particular ID and waiting
+      for response message. Sending and receiving messages is done
+      periodically in a loop.</para>
+
+      <section>
+        <title>Opening the Driver</title>
+
+        <para>At the beginning of every access to the LinCAN driver, the
+        driver has to be opened. This is done by calling
+        <function>open()</function>.<programlisting>canfd = open(option_device, O_RDWR)</programlisting></para>
+
+        <para>The value of the <varname>option_device</varname> variable
+        specifies the name of CAN device e.g. <filename>/dev/can0</filename>
+        and the second parameter contains additional flags. We are using a
+        <constant>O_RDWR</constant> flag which means that we want to use the
+        driver for both reading and writing (messages). You can also use the
+        <constant>O_NOBLOCK</constant> flag in addition. For further details
+        regarding this flag see the LinCAN documentation.</para>
+
+        <para>If the opening operation succeeds, the <varname>canfd</varname>
+        variable contains a valid file descriptor, which is used for further
+        communication with the driver. In the case of an error, we use
+        <function>perror()</function> function to print the reason of the
+        error and then we exit the program with appropriate exit code<footnote>
+            <para>The exit code can be used by an automatic regression tests
+            to detect the reason of failure.</para>
+          </footnote>.</para>
+      </section>
+
+      <section>
+        <title>Setting Filters</title>
+
+        <para>After the device is open, the second step is to create a filter
+        for message receiving. The filter assures this thread only receives
+        messages it is waiting for.</para>
+
+        <programlisting>/* setup filtering of received messages */
+memset(&amp;canfilt, 0, sizeof(canfilt));
+canfilt.mask = 0xfffffff;
+canfilt.id = pong_id; /* pong responces with increased id */
+ret = ioctl(canfd, CANQUE_FILTER, &amp;canfilt);
+</programlisting>
+
+        <para>The filter is created by filling the <type>canfilt_t</type>
+        structure and submitting it to the <constant>CANQUE_FILTER</constant>
+        ioctl. Filters allow us to filter messages by various criterions. We
+        use only filtering by an ID. The <varname>mask</varname> member tells
+        which bits of message ID are relevant for filtering and the member
+        <varname>id</varname> contains desired ID bit values. Since we need
+        only to receive messages with one ID <varname>mask</varname> is set to
+        all ones and <varname>id</varname> contains the ID.</para>
+
+        <para>After the filter is set up it is necessary to flush driver
+        queues. The reason for this is that in the time between driver opening
+        and filter setup, there can be some received messages in the queue,
+        which doesn't match the filter criteria. Flushing the queue is done by
+        calling <constant>CANQUE_FLUSH</constant> ioctl.</para>
+
+        <programlisting>ret = ioctl(canfd, CANQUE_FLUSH, NULL);
+</programlisting>
+      </section>
+
+      <section>
+        <title>Reading and Writing</title>
+
+        <para>As soon as these operations are done, everything is ready for
+        message sending and reception. The message is sent by calling the
+        <function>write()</function> function with <type>canmsg_t</type>
+        structure as the second parameter. The <type>canmsg_t</type> structure
+        should be filled according to the message we wish to send. This
+        comprises of a message ID, message data bytes, the length of message
+        etc. The structure for the ping message is filled by the following
+        commands:</para>
+
+        <programlisting>pingmsg.flags=0;
+pingmsg.id=ping_id;
+pingmsg.length = option_length;
+for (i=0; i &lt; option_length; i++) pingmsg.data[i] = i;
+</programlisting>
+
+        <para>Later, the message is sent by:</para>
+
+        <programlisting>ret = write(canfd, &amp;pingmsg, sizeof(pingmsg));</programlisting>
+
+        <para>After the message is sent, the program starts waiting for the
+        response message. The <function>select()</function> system call is
+        used for this purpose because it allows us to wait with timeout. If
+        there is a received message, we can read it from the driver by calling
+        <function>read()</function> function.</para>
+
+        <para>Before doing that, it is important to zero
+        <varname>flags</varname> field of <type>canmsg_t</type> structure.
+        This is due to a feature of the driver. Pavel Pí¹a describes this as
+        follows:</para>
+
+        <blockquote>
+          <para>Adding "<function>msg.flags=0;</function>" before
+          "<function>read()</function>" call is required, because random value
+          could trigger RTR read patch in the driver. This obsolete driver
+          read mode should be moved to its own IOCTL in future.</para>
+        </blockquote>
+
+        <programlisting>/* Read the message */
+pongmsg.flags=0;
+ret = read(canfd, &amp;pongmsg, sizeof(pongmsg));</programlisting>
+      </section>
+
+      <section>
+        <title>Closing</title>
+
+        <para>At the end, when it is not needed to work with the driver, it
+        should be closed in order to remove all driver resources associated
+        with our application. The driver is closed simply by calling
+        <function>close()</function>.</para>
+
+        <programlisting>close(canfd);</programlisting>
+      </section>
+    </section>
+
+    <section>
+      <title>Other parts of the application</title>
+
+      <section>
+        <title>Lists</title>
+
+        <para>For managing the list of executing threads and their statistics
+        a list implementation from Pavel Pí¹a's <productname>uLan
+        utils</productname> (ulut) package was used. This framework allows us
+        very simple and efficient list handling.</para>
+
+        <para>First, list head should be declared:<programlisting>typedef struct threads {
+        ul_list_head_t head;
+} threads_t;
+</programlisting>Since we don't need any additional data, our list head has
+        only one field of the type <type>ul_list_head_t</type>. Next we
+        declare a list element:<programlisting>typedef struct thread_data {
+        pthread_t tid;
+        long int canid;
+
+        int count;
+        double mean;            /* mean value of responses */
+        double moment2nd;       /* used to compute variance of
+                                 * responses */
+        int min, max;           /* min/max response times */
+        int timeout;            /* number of timeouts */
+
+        ul_list_node_t node;
+} thread_data_t;
+</programlisting>This structure is used for storing data for every executed
+        thread. In the next step we declare functions for list manipulation.
+        These functions are created automatically by the
+        <function>UL_LIST_CUST_DEC</function> macro.<programlisting>UL_LIST_CUST_DEC(thread_list, threads_t, thread_data_t, head, node);</programlisting>This
+        declares some functions whose names start with the
+        <constant>thread_list_</constant> prefix. We use two of these, namely
+        <function>thread_list_init_head()</function> for list initialization
+        and <function>thread_list_ins_tail()</function> for adding elements to
+        the list.</para>
+
+        <para>We also use a macro <function>ul_list_for_each()</function>
+        (declared in <filename>ul_list.h</filename>) which traverses through
+        all the list elements in a loop.</para>
+      </section>
+
+      <section>
+        <title>Waiting for the thread completion</title>
+
+        <para>After the main thread starts all the communication threads, it
+        is necessary to wait for their completion. This is done in
+        <function>wait_for_threads()</function>. In the simplest case, waiting
+        runs in a while loop and in every iteration is is waited for one
+        thread using a <varname>finish_sem</varname> semaphore. After the
+        <varname>thread_count</varname> iterations, we are sure all the
+        threads finished. Whenever any thread finishes, it increments this
+        semaphore by calling <function>sem_post()</function> and this is why
+        we can wait for a particular number of threads to finish.</para>
+
+        <para>The simplest case of waiting code looks as
+        follows:<programlisting>while (thread_count &gt; 0) {
+        ret = sem_wait(&amp;finish_sem);
+        if (ret == 0) thread_count--;
+}
+</programlisting>In the <productname>canping</productname> application we use
+        more difficult code because of printing of progress messages during
+        waiting.</para>
+
+        <para>Finally, when all the threads are finished, we print summary
+        statistics for each thread and free list elements:<programlisting>ul_list_for_each_cut(thread_list, &amp;master_threads, td) {
+        print_stats(td);
+        free(td);
+}
+</programlisting></para>
+      </section>
+
+      <section>
+        <title>Signal handling</title>
+
+        <para>In order to exit <productname>canping</productname> by pressing
+        <keycap>Ctrl-C</keycap> (<constant>SIGINT</constant>) or by sending
+        other signal such as <constant>SIGTERM</constant> it is necessary to
+        write and register a signal handler. The handler is registered
+        by:<programlisting>siginterrupt(SIGINT, 1);
+signal(SIGINT, term_handler);
+siginterrupt(SIGTERM, 1);
+signal(SIGTERM, term_handler);
+</programlisting>This registers the <function>term_handler</function> function
+        as the handler for the <constant>SIGINT</constant> and
+        <constant>SIGTERM</constant> signals. The call to the
+        <function>siginterrupt()</function> function tells the OS that an
+        arrived signal should interrupt currently executed system
+        call<footnote>
+            <para>The behaviour of signals is different in Linux 2.4 and Linux
+            2.6 with NPTL. The call to <function>siginterrupt()</function> is
+            necessary in order to get the same behaviour for both 2.4 and
+            2.6.</para>
+          </footnote>. This is necessary because the master and slave threads
+        executes <function>select()</function> or <function>read()</function>
+        system call, which cause waiting for external event, which can never
+        happen and we want the program to exit even if there is no
+        event.</para>
+
+        <para>The signal handler looks as follows:<programlisting>#define NOT_INTERRUPTED_SYSCALL (errno != EINTR &amp;&amp; errno != ERESTART)
+#define IS_FINISH_FLAG() (finish_flag)
+
+void term_handler(int signum)
+{
+        if (!IS_FINISH_FLAG()) {
+                finish_flag = 1;
+                kill_all_threads(signum);
+        }
+}
+</programlisting>Whenever the main thread receives a signal it sets
+        <varname>finish_flag</varname> to prevent recursive call to the
+        handler and then sends the same signal to all other threads. The other
+        threads receive the signal and execute the handler. Because the
+        <varname>finish_flag</varname> is set the handler does nothing. The
+        only result of the signal is interruption of currently executed system
+        call. As a consequence, the thread exits the send-receive loop and
+        executes exit code.</para>
+
+        <para>You can notice the macro <varname>IS_FINISH_FLAG</varname>. This
+        macro is defined only for debugging purposes so don't be confused by
+        it.</para>
+      </section>
+    </section>
+  </section>
+
+  <section>
+    <title>Assignment</title>
+
+    <orderedlist>
+      <listitem>
+        <para>Create a simple chat application that will send messages over
+        the CAN bus. The application will read the characters typed on
+        keyboard and send them to the bus. In addition, it will receive CAN
+        messages from other nodes on the bus and print then on screen.</para>
+      </listitem>
+
+      <listitem>
+        <para>Extend the chat application by displaying messages in color
+        depending on the ID of sender. For color and screen management use for
+        example the <productname>ncurses</productname> library (<ulink
+        url="http://www.tldp.org/HOWTO/NCURSES-Programming-HOWTO/">http://www.tldp.org/HOWTO/NCURSES-Programming-HOWTO/</ulink>).</para>
+      </listitem>
+    </orderedlist>
+  </section>
+</chapter>
\ No newline at end of file
diff --git a/include/ul_itbase.h b/include/ul_itbase.h
new file mode 100644 (file)
index 0000000..f7dc81c
--- /dev/null
@@ -0,0 +1,97 @@
+#ifndef _UL_ITBASE_H
+#define _UL_ITBASE_H
+
+#include "ul_utdefs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UL_ITBASE_UL_DEC(cust_prefix, cust_container_t, cust_item_t)           \
+typedef struct { cust_container_t *container; cust_item_t *item;}              \
+        cust_prefix##_it_t;                                                    \
+static inline cust_item_t *                                                    \
+cust_prefix##_it2item(const cust_prefix##_it_t *it)                            \
+{                                                                              \
+  return it->item;                                                             \
+}                                                                              \
+static inline void                                                             \
+cust_prefix##_first_it(cust_container_t *container, cust_prefix##_it_t *it)    \
+{                                                                              \
+  it->container=container;                                                     \
+  it->item=cust_prefix##_first(container);                                     \
+}                                                                              \
+static inline void                                                             \
+cust_prefix##_last_it(cust_container_t *container, cust_prefix##_it_t *it)     \
+{                                                                              \
+  it->container=container;                                                     \
+  it->item=cust_prefix##_last(container);                                      \
+}                                                                              \
+static inline void                                                             \
+cust_prefix##_next_it(cust_prefix##_it_t *it)                                  \
+{                                                                              \
+  if(it->item) it->item=cust_prefix##_next(it->container,it->item);            \
+  else it->item=cust_prefix##_first(it->container);                            \
+}                                                                              \
+static inline void                                                             \
+cust_prefix##_prev_it(cust_prefix##_it_t *it)                                  \
+{                                                                              \
+  if(it->item) it->item=cust_prefix##_prev(it->container,it->item);            \
+  else it->item=cust_prefix##_last(it->container);                             \
+}                                                                              \
+static inline int                                                              \
+cust_prefix##_is_end_it(cust_prefix##_it_t *it)                                        \
+{                                                                              \
+  return !it->item;                                                            \
+}                                                                              \
+static inline void                                                             \
+cust_prefix##_delete_it(cust_prefix##_it_t *it)                                        \
+{                                                                              \
+  cust_item_t *p;                                                              \
+  if(!(p=it->item)) return;                                                    \
+  it->item=cust_prefix##_next(it->container,it->item);                         \
+  cust_prefix##_delete(it->container,p);                                       \
+}
+
+#define UL_ITBASE_SORT_DEC(cust_prefix, cust_container_t, cust_item_t, cust_key_t)                     \
+UL_ITBASE_UL_DEC(cust_prefix, cust_container_t, cust_item_t)                                           \
+static inline int                                                                                      \
+cust_prefix##_find_it(cust_container_t *container, cust_key_t *key, cust_prefix##_it_t *it)            \
+{                                                                                                      \
+  it->container=container;                                                                             \
+  return (it->item=cust_prefix##_find(container, key))!=0;                                             \
+}                                                                                                      \
+static inline int                                                                                      \
+cust_prefix##_find_first_it(cust_container_t *container, cust_key_t *key, cust_prefix##_it_t *it)      \
+{                                                                                                      \
+  it->container=container;                                                                             \
+  return (it->item=cust_prefix##_find_first(container, key))!=0;                                       \
+}                                                                                                      \
+static inline int                                                                                      \
+cust_prefix##_find_after_it(cust_container_t *container, cust_key_t *key, cust_prefix##_it_t *it)      \
+{                                                                                                      \
+  it->container=container;                                                                             \
+  return (it->item=cust_prefix##_find_after(container, key))!=0;                                       \
+}
+
+#define ul_for_each_it(cust_prefix, root, it)                          \
+       for(cust_prefix##_first_it(root,&it);                           \
+       !cust_prefix##_is_end_it(&it);cust_prefix##_next_it(&it))
+
+#define ul_for_each_rev_it(cust_prefix, root, it)                      \
+       for(cust_prefix##_last_it(root,&it);                            \
+       !cust_prefix##_is_end_it(&it);cust_prefix##_prev_it(&it))
+
+#define ul_for_each_from_it(cust_prefix, root, key, it)                        \
+       for(cust_prefix##_find_first_it(root, key, &it);                \
+       !cust_prefix##_is_end_it(&it);cust_prefix##_next_it(&it))
+
+#define ul_for_each_after_it(cust_prefix, root, key, it)               \
+       for(cust_prefix##_find_after_it(root, key, &it);                \
+       !cust_prefix##_is_end_it(&it);cust_prefix##_next_it(&it))
+
+#ifdef __cplusplus
+} /* extern "C"*/
+#endif
+
+#endif /* _UL_ITBASE_H */
diff --git a/include/ul_list.h b/include/ul_list.h
new file mode 100644 (file)
index 0000000..89b6836
--- /dev/null
@@ -0,0 +1,110 @@
+#ifndef _UL_LISTS_H
+#define _UL_LISTS_H
+
+#include "ul_utdefs.h"
+#include "ul_listbase.h"
+#include "ul_itbase.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct list_head ul_list_node_t;
+typedef struct list_head ul_list_head_t;
+
+#define UL_LIST_CUST_DEC(cust_prefix, cust_head_t, cust_item_t,                        \
+               cust_head_field, cust_node_field)                               \
+                                                                               \
+static inline cust_item_t *                                                    \
+cust_prefix##_node2item(const cust_head_t *head, const ul_list_node_t *node)   \
+  {return UL_CONTAINEROF(node, cust_item_t, cust_node_field);}                 \
+                                                                               \
+static inline void                                                             \
+cust_prefix##_init_head(cust_head_t *head)                                     \
+{                                                                              \
+  INIT_LIST_HEAD(&head->cust_head_field);                                      \
+}                                                                              \
+static inline void                                                             \
+cust_prefix##_init_detached(cust_item_t *item){                                        \
+  INIT_LIST_HEAD(&item->cust_node_field);                                      \
+}                                                                              \
+static inline cust_item_t *                                                    \
+cust_prefix##_first(const cust_head_t *head)                                   \
+{                                                                              \
+  ul_list_node_t *n=head->cust_head_field.next;                                        \
+  return (n!=&head->cust_head_field)?cust_prefix##_node2item(head,n):NULL;     \
+}                                                                              \
+static inline cust_item_t *                                                    \
+cust_prefix##_last(const cust_head_t *head)                                    \
+{                                                                              \
+  ul_list_node_t *n=head->cust_head_field.prev;                                        \
+  return (n!=&head->cust_head_field)?cust_prefix##_node2item(head,n):NULL;     \
+}                                                                              \
+static inline cust_item_t *                                                    \
+cust_prefix##_next(const cust_head_t *head, const cust_item_t *item)           \
+{                                                                              \
+  ul_list_node_t *n=item->cust_node_field.next;                                        \
+  return (n!=&head->cust_head_field)?cust_prefix##_node2item(head,n):NULL;     \
+}                                                                              \
+static inline cust_item_t *                                                    \
+cust_prefix##_prev(const cust_head_t *head, const cust_item_t *item)           \
+{                                                                              \
+  ul_list_node_t *n=item->cust_node_field.prev;                                        \
+  return (n!=&head->cust_head_field)?cust_prefix##_node2item(head,n):NULL;     \
+}                                                                              \
+static inline int                                                              \
+cust_prefix##_is_empty(const cust_head_t *head)                                        \
+{                                                                              \
+  return head->cust_head_field.next==&head->cust_head_field;                   \
+}                                                                              \
+static inline void                                                             \
+cust_prefix##_ins_head(cust_head_t *head, cust_item_t *item)                   \
+{                                                                              \
+  list_add(&item->cust_node_field, &head->cust_head_field);                    \
+}                                                                              \
+static inline void                                                             \
+cust_prefix##_ins_tail(cust_head_t *head, cust_item_t *item)                   \
+{                                                                              \
+  list_add_tail(&item->cust_node_field, &head->cust_head_field);               \
+}                                                                              \
+static inline void                                                             \
+cust_prefix##_insert(cust_head_t *head, cust_item_t *item)                     \
+{                                                                              \
+  cust_prefix##_ins_tail(head, item);                                          \
+}                                                                              \
+static inline void                                                             \
+cust_prefix##_delete(cust_head_t *head, cust_item_t *item)                     \
+{                                                                              \
+  list_del_init(&item->cust_node_field);                                       \
+}                                                                              \
+static inline void                                                             \
+cust_prefix##_del_item(cust_item_t *item)                                      \
+{                                                                              \
+  list_del_init(&item->cust_node_field);                                       \
+}                                                                              \
+static inline cust_item_t *                                                    \
+cust_prefix##_cut_first(cust_head_t *head)                                     \
+{                                                                              \
+  ul_list_node_t *n=head->cust_head_field.next;                                        \
+  if(n==&head->cust_head_field) return NULL;                                   \
+  list_del_init(n);                                                            \
+  return cust_prefix##_node2item(head,n);                                      \
+}                                                                              \
+/*** Iterators ***/                                                            \
+UL_ITBASE_UL_DEC(cust_prefix, cust_head_t, cust_item_t)
+
+
+#define ul_list_for_each(cust_prefix, head, ptr)                                       \
+       for(ptr=cust_prefix##_first(head);ptr;ptr=cust_prefix##_next((head),ptr))
+
+#define ul_list_for_each_rev(cust_prefix, head, ptr)                                   \
+       for(ptr=cust_prefix##_last(head);ptr;ptr=cust_prefix##_prev((head),ptr))
+
+#define ul_list_for_each_cut(cust_prefix, head, ptr)   \
+       for(;(ptr=cust_prefix##_cut_first(head));)
+
+#ifdef __cplusplus
+} /* extern "C"*/
+#endif
+
+#endif /* _UL_LISTS_H */
diff --git a/include/ul_listbase.h b/include/ul_listbase.h
new file mode 100644 (file)
index 0000000..1c900ef
--- /dev/null
@@ -0,0 +1,287 @@
+#ifndef _UL_LISTBASE_H
+#define _UL_LISTBASE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __KERNEL__
+
+#define LIST_POISON1  ((void *) 0)
+#define LIST_POISON2  ((void *) 0)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+       struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+       (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries. 
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+                             struct list_head *prev,
+                             struct list_head *next)
+{
+       next->prev = new;
+       new->next = next;
+       new->prev = prev;
+       prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       entry->next = LIST_POISON1;
+       entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       INIT_LIST_HEAD(entry); 
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+        __list_del(list->prev, list->next);
+        list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+                                 struct list_head *head)
+{
+        __list_del(list->prev, list->next);
+        list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(struct list_head *head)
+{
+       return head->next == head;
+}
+
+static inline void __list_splice(struct list_head *list,
+                                struct list_head *head)
+{
+       struct list_head *first = list->next;
+       struct list_head *last = list->prev;
+       struct list_head *at = head->next;
+
+       first->prev = head;
+       head->next = first;
+
+       last->next = at;
+       at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+       if (!list_empty(list))
+               __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+                                   struct list_head *head)
+{
+       if (!list_empty(list)) {
+               __list_splice(list, head);
+               INIT_LIST_HEAD(list);
+       }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:       the &struct list_head pointer.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+       container_of(ptr, type, member)
+
+/**
+ * list_for_each       -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ */
+#define list_for_each(pos, head) \
+       for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+               pos = pos->next, prefetch(pos->next))
+
+/**
+ * __list_for_each     -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+       for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev  -       iterate over a list backwards
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+       for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+               pos = pos->prev, prefetch(pos->prev))
+               
+/**
+ * list_for_each_safe  -       iterate over a list safe against removal of list entry
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @n:         another &struct list_head to use as temporary storage
+ * @head:      the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+       for (pos = (head)->next, n = pos->next; pos != (head); \
+               pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry -       iterate over list of given type
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)                         \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+                    prefetch(pos->member.next);                        \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.next, typeof(*pos), member),  \
+                    prefetch(pos->member.next))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)                 \
+       for (pos = list_entry((head)->prev, typeof(*pos), member),      \
+                    prefetch(pos->member.prev);                        \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.prev, typeof(*pos), member),  \
+                    prefetch(pos->member.prev))
+
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:       the type * to use as a loop counter.
+ * @n:         another type * to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)                 \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+               n = list_entry(pos->member.next, typeof(*pos), member); \
+            &pos->member != (head);                                    \
+            pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+#else /*__KERNEL__*/
+
+#include <linux/list.h>
+
+#endif /*__KERNEL__*/
+
+#ifdef __cplusplus
+} /* extern "C"*/
+#endif
+
+#endif /* _UL_LISTBASE_H */
diff --git a/include/ul_utdefs.h b/include/ul_utdefs.h
new file mode 100644 (file)
index 0000000..0e872fd
--- /dev/null
@@ -0,0 +1,43 @@
+/*******************************************************************
+  uLan Communication - C interface library
+
+  ul_utdefs.h  - common defines used in uLan utilities library
+
+ *******************************************************************/
+
+
+#ifndef _UL_UTDEFS_H
+#define _UL_UTDEFS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(_WIN32)&&defined(_MSC_VER)&&!defined(inline)
+#define inline _inline
+#endif
+
+#ifndef UL_OFFSETOF
+/* offset of structure field */
+#define UL_OFFSETOF(_type,_member) \
+                ((int)&(((_type*)0)->_member))
+#endif /*UL_OFFSET*/
+
+#ifndef UL_CONTAINEROF
+#ifdef  __GNUC__
+#define UL_CONTAINEROF(_ptr, _type, _member) ({ \
+        const typeof( ((_type *)0)->_member ) *__mptr = (_ptr); \
+        (_type *)( (char *)__mptr - UL_OFFSETOF(_type,_member) );})
+#else /*!__GNUC__*/
+#define UL_CONTAINEROF(_ptr, _type, _member) \
+        ((_type *)( (char *)_ptr - UL_OFFSETOF(_type,_member)))
+#endif /*__GNUC__*/
+#endif /*UL_CONTAINEROF*/
+
+
+
+#ifdef __cplusplus
+} /* extern "C"*/
+#endif
+
+#endif /* _UL_UTDEFS_H */
diff --git a/src/Makefile b/src/Makefile
new file mode 100644 (file)
index 0000000..f595272
--- /dev/null
@@ -0,0 +1,14 @@
+# Generic directory or leaf node makefile for OCERA make framework
+
+ifndef MAKERULES_DIR
+MAKERULES_DIR := $(shell ( old_pwd="" ;  while [ ! -e Makefile.rules ] ; do if [ "$$old_pwd" == `pwd`  ] ; then exit 1 ; else old_pwd=`pwd` ; cd -L .. 2>/dev/null ; fi ; done ; pwd ) )
+endif
+
+ifeq ($(MAKERULES_DIR),)
+all : default
+.DEFAULT::
+       @echo -e "\nThe Makefile.rules has not been found in this or partent directory\n"
+else   
+include $(MAKERULES_DIR)/Makefile.rules
+endif
+
diff --git a/src/Makefile.omk b/src/Makefile.omk
new file mode 100644 (file)
index 0000000..b902cd4
--- /dev/null
@@ -0,0 +1,14 @@
+# -*- makefile -*-
+
+bin_PROGRAMS = canping
+
+canping_SOURCES = canping.c
+canping_LIBS = pthread m
+INCLUDE = -D_REENTRANT -ggdb
+
+INCLUDES = -I $(srcdir)/../include
+kernel_INCLUDES = -I $(srcdir)/../include
+
+
+#rtlinux_MODULES = canping_rtl
+canping_rtl_SOURCES = canping_rtl.c
diff --git a/src/canping.c b/src/canping.c
new file mode 100644 (file)
index 0000000..10c605e
--- /dev/null
@@ -0,0 +1,641 @@
+#include <string.h>
+#include <math.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <pthread.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <ul_list.h>
+#include <errno.h>
+#include <semaphore.h>
+
+/* TODO: Handle the case where there are more canping slaves running
+ * on one CAN bus. */
+
+#include <can.h>
+
+//#define DEBUG 1
+//#define DEBUG 2
+
+#ifndef DEBUG
+#define dbg(level, fmt, arg...) do {} while (0)
+#else
+#define dbg(level, fmt, arg...) do { if (level <= DEBUG) { printf("candping: " fmt, ## arg); } } while (0)
+#endif
+
+#define NOT_INTERRUPTED_SYSCALL (errno != EINTR && errno != ERESTART)
+#define IS_FINISH_FLAG() (finish_flag)
+
+/* Exit codes */
+#define EXIT_OK 0
+#define EXIT_BAD_PARAM 1
+#define EXIT_CANNOT_OPEN 2
+#define EXIT_FILTER_ERROR 3
+#define EXIT_NO_MEM 4
+#define EXIT_READ_ERROR 5
+#define EXIT_WRITE_ERROR 6
+#define EXIT_SELECT_ERROR 7
+#define EXIT_FLUSH_ERROR 8
+
+/* Global variables */
+sig_atomic_t finish_flag = 0;  /* Threads should terminate. */
+sem_t finish_sem;              /* Thread signals a termination */
+int global_canfd;
+
+int total_count = 0;
+int total_timeout = 0;
+
+pthread_mutex_t mut_start = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t cond_start = PTHREAD_COND_INITIALIZER;
+int start_flag = 0;
+sem_t ready_sem;               /* Thread is ready for execution */
+
+
+/* Command line options */
+char *option_device = "/dev/can0";
+int option_masters = 0;
+long int option_first_id = 1000;
+int option_slaves = 0;
+int option_verbose = 0;                /* 0 - nothing, 1 - only global
+                                * statistics, 2 - simple times, 3 -
+                                * verbose times */
+int option_length = 8;
+int option_count = 0;
+int option_wait_ms = 1000;
+int option_timeout = 4;
+int option_open_once = 0;
+int option_synch_start = 0;
+
+/* Lists */
+typedef struct threads {
+       ul_list_head_t head;
+} threads_t;
+
+typedef struct thread_data {
+       pthread_t tid;
+       long int canid;
+
+       int count;
+       double mean;            /* mean value of responses */
+       double moment2nd;       /* used to compute variance of
+                                * responses */
+       int min, max;           /* min/max response times */
+       int timeout;            /* number of timeouts */
+
+       ul_list_node_t node;
+} thread_data_t;
+
+UL_LIST_CUST_DEC(thread_list, threads_t, thread_data_t, head, node);
+
+threads_t master_threads;
+threads_t slave_threads;
+
+/* Subtract the `struct timeval' values X and Y, storing the result in
+   RESULT.  Return 1 if the difference is negative, otherwise 0.  */
+     
+int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y)
+{
+       /* Perform the carry for the later subtraction by updating Y. */
+       if (x->tv_usec < y->tv_usec) {
+               int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
+               y->tv_usec -= 1000000 * nsec;
+               y->tv_sec += nsec;
+       }
+       if (x->tv_usec - y->tv_usec > 1000000) {
+               int nsec = (x->tv_usec - y->tv_usec) / 1000000;
+               y->tv_usec += 1000000 * nsec;
+               y->tv_sec -= nsec;
+       }
+     
+       /* Compute the time remaining to wait.
+          `tv_usec' is certainly positive. */
+       result->tv_sec = x->tv_sec - y->tv_sec;
+       result->tv_usec = x->tv_usec - y->tv_usec;
+     
+       /* Return 1 if result is negative. */
+       return x->tv_sec < y->tv_sec;
+}
+
+void dbg_print_timeval(char *msg, struct timeval *tv)
+{
+
+       printf("%s sec=%ld usec=%ld\n", msg, tv->tv_sec, tv->tv_usec);
+}
+void kill_all_threads(int signum)
+{
+       thread_data_t *td;
+               
+       ul_list_for_each(thread_list, &master_threads, td) {
+               pthread_kill(td->tid, signum);
+       }
+       ul_list_for_each(thread_list, &slave_threads, td) {
+               pthread_kill(td->tid, signum);
+       }
+}
+void term_handler(int signum)
+{
+       dbg(1, "Thread %p got a signal %d\n", (void *)pthread_self(), signum);
+       if (!IS_FINISH_FLAG()) {
+               dbg(1, "Terminating threads\n");
+               finish_flag = 1;
+
+               kill_all_threads(signum);
+       }
+}
+
+void *master_thread(void *arg)
+{
+       thread_data_t *td = (thread_data_t *)arg;
+       int ping_id = td->canid;
+       int pong_id = ping_id + 1;
+       struct canmsg_t pingmsg, pongmsg;
+       int ping_count = 0;
+       int ret = 0;
+       int canfd;              /* File descriptor of CAN driver. */
+       int i;
+       fd_set rdset;           /* read set for select syscall */
+       struct timeval timeout, pingtime, pongtime;
+       struct canfilt_t canfilt; /* filter for received messages */
+
+       if (!option_open_once) {
+               /* Open can driver */
+               if ((canfd = open(option_device, O_RDWR)) < 0) {
+                       perror("open");
+                       fprintf(stderr, "Error opening %s (for id %d)\n", option_device, ping_id);
+                       exit(EXIT_CANNOT_OPEN); 
+               }
+       } else {
+               canfd = global_canfd;
+       }
+       
+       /* setup filtering of received messages */
+       memset(&canfilt, 0, sizeof(canfilt));
+       canfilt.mask = 0xfffffff;
+       canfilt.id = pong_id;   /* pong responces with increased id */
+       ret = ioctl(canfd, CANQUE_FILTER, &canfilt);
+       if(ret<0) {
+               perror("ioctl CANQUE_FILTER");
+               exit(EXIT_FILTER_ERROR);
+       }
+       ret = ioctl(canfd, CANQUE_FLUSH, NULL);
+       if(ret<0) {
+               perror("ioctl CANQUE_QUEUE_FLUSH");
+               exit(EXIT_FLUSH_ERROR);
+       }
+       
+       /* Prepare data structures for the select syscall. */
+       FD_ZERO (&rdset);
+
+       /* Signal that I'm ready */
+       sem_post(&ready_sem);
+
+       if (option_synch_start) {
+               /* Wait for other threads to initialize */
+               pthread_mutex_lock(&mut_start);
+               while (!start_flag) pthread_cond_wait(&cond_start, &mut_start);
+               pthread_mutex_unlock(&mut_start);
+       }
+
+       while (!IS_FINISH_FLAG() && (option_count == 0 || ping_count++ < option_count)) {
+               /* Send a ping message */
+               pingmsg.flags=0;
+               pingmsg.id=ping_id;
+               pingmsg.length = option_length;
+               for (i=0; i < option_length; i++) pingmsg.data[i] = i;
+               gettimeofday(&pingtime, NULL);
+
+               ret = write(canfd, &pingmsg, sizeof(pingmsg));
+               if (ret < 0) {
+                       if (NOT_INTERRUPTED_SYSCALL) {
+                               perror("write");
+                               exit(EXIT_WRITE_ERROR);
+                       }
+                        else continue;
+               }
+               
+               /* Wait for a pong responce */
+               FD_SET (canfd, &rdset);
+     
+               timeout.tv_sec = option_timeout;
+               timeout.tv_usec = 0;
+     
+               ret = select(FD_SETSIZE, &rdset, NULL, NULL, &timeout);
+               if (ret > 0) {
+                       /* Read the message */
+                       pongmsg.flags=0;
+                       ret = read(canfd, &pongmsg, sizeof(pongmsg));
+                       if (ret < 0) {
+                               if (NOT_INTERRUPTED_SYSCALL) {
+                                       perror("read");
+                                       exit(EXIT_READ_ERROR);
+                               }
+                                else continue;
+                       }
+                       else {  /* A pong message received */
+                               long int time;
+                               gettimeofday(&pongtime, NULL);
+                               if (ret == 0) {
+                                       fprintf(stderr, "read returned zero\n");
+                                       exit(EXIT_READ_ERROR);
+                               }
+                               if (pongmsg.id != pong_id) {
+                                       fprintf(stderr, "Filter error (expected: %d, received %ld)\n", pong_id, pongmsg.id);
+                                       exit(EXIT_FILTER_ERROR);
+                               }
+                               timeval_subtract(&pongtime, &pongtime, &pingtime);
+                               time = pongtime.tv_sec*1000000 + pongtime.tv_usec;
+                               switch (option_verbose) {
+                               case 2:
+                                       printf("%d:%ld\n", ping_id, time); 
+                                       break;
+                               case 3:
+                                       printf("Pong response for id %d received in %ld us\n", ping_id, time);
+                                       break;
+                               }
+                               /* Update statistics */
+                               td->count++;
+                               td->mean = 
+                                       td->mean * ((double)(td->count - 1) / td->count) + 
+                                       (double)time / td->count;
+                               td->moment2nd = 
+                                       td->moment2nd * ((double)(td->count - 1) / td->count) + 
+                                       (double)time*time  / td->count;
+                               if (time > td->max) td->max = time;
+                               if (time < td->min) td->min = time;
+                               total_count++;
+                       } /* read */
+               } 
+                else if (ret == 0) { /* select */
+                       if (option_verbose >= 2) 
+                               printf("Timeout encountered (id %d)\n", ping_id);
+                       td->timeout++;
+                       total_timeout++;
+               } 
+                else {
+                       if (NOT_INTERRUPTED_SYSCALL) {
+                               perror("select");
+                               exit(EXIT_SELECT_ERROR);
+                       }
+                        else continue;
+               }
+               usleep(option_wait_ms * 1000);
+       }
+
+       if (!option_open_once) {
+               /* Close the can driver */
+               close(canfd);
+       }
+
+       sem_post(&finish_sem);
+       return (void *)ret;
+}
+
+void start_masters(int masters, int first_id)
+{
+       int id = first_id;
+       int i;
+
+       dbg(1, "Starting %d master threads\n", masters);
+       
+       for (i = 0; i < masters; i++, id += 2) {
+               thread_data_t *td;
+               
+               td = malloc(sizeof(*td));
+               if (!td) {
+                       printf("Can't allocate memory");
+                       exit(EXIT_NO_MEM);
+               }
+               memset(td, 0, sizeof(*td));
+               td->canid = id;
+               td->min = 0x7fffffff;
+               /* TODO use mutexes and signal blocking */
+               thread_list_ins_tail(&master_threads, td);
+               pthread_create(&td->tid, NULL, master_thread, (void *)td);
+               dbg(2, "Master thread: %p\n", (void *)td->tid);
+       }
+
+
+       /* Wait for all threads beeing ready */
+       for (i = 0; i < masters; i++) sem_wait(&ready_sem);
+
+       /* Start threads */
+       pthread_mutex_lock(&mut_start);
+       start_flag = 1;
+       pthread_cond_broadcast(&cond_start);
+       pthread_mutex_unlock(&mut_start);
+}
+
+void *slave_thread(void *arg)
+{
+       thread_data_t *td = (thread_data_t *)arg;
+       int ping_id = td->canid;
+       int pong_id = ping_id + 1;
+       struct canmsg_t pingmsg, pongmsg;
+       int ret = 0;
+       int canfd;              /* File descriptor of CAN driver. */
+       int i;
+       struct canfilt_t canfilt; /* filter for received messages */
+
+       if (!option_open_once) {
+               /* Open the CAN driver */
+               if ((canfd = open(option_device, O_RDWR)) < 0) {
+                       perror("open");
+                       printf("Error opening %s (for id %d)\n", option_device, ping_id);
+                       exit(EXIT_CANNOT_OPEN); 
+               }
+       } else {
+               canfd = global_canfd;
+       }
+       
+       /* setup filtering of received messages */
+       memset(&canfilt, 0, sizeof(canfilt));
+       canfilt.mask = 0xfffffff;
+       canfilt.id = ping_id;   /* receive only our ping messages */
+       ret = ioctl(canfd, CANQUE_FILTER, &canfilt);
+       if (ret < 0) {
+               perror("ioctl CANQUE_FILTER");
+               exit(EXIT_FILTER_ERROR);
+       }
+       /* If there are some messages already, delete them. These may
+        * not processed by our filter and thus may have a wrong
+        * ID. */
+       ret = ioctl(canfd, CANQUE_FLUSH, NULL);
+       if (ret < 0) {
+               perror("ioctl CANQUE_QUEUE_FLUSH");
+               exit(EXIT_FLUSH_ERROR);
+       }
+       
+       /* Prepare a pong message */
+       pongmsg.flags = 0;
+       pongmsg.id = pong_id;
+       pongmsg.length = option_length;
+       for (i=0; i < option_length; i++) pongmsg.data[i] = i;
+
+       while (!IS_FINISH_FLAG()) {
+               /* Receive a ping message */
+               pingmsg.flags=0;
+               ret = read(canfd, &pingmsg, sizeof(pingmsg));
+               if (ret < 0) {
+                       if (NOT_INTERRUPTED_SYSCALL) {
+                               printf("%d\n", errno);
+                               perror("read");
+                               exit(EXIT_READ_ERROR);
+                       }
+               }
+               if (NOT_INTERRUPTED_SYSCALL && pingmsg.id != ping_id) {
+                       fprintf(stderr, "Filter error (expected: %d, received %ld)\n", ping_id, pingmsg.id);
+                       exit(EXIT_FILTER_ERROR);
+               }
+               /* Answer immendiately with a pong message */
+               if (NOT_INTERRUPTED_SYSCALL) {
+                       ret = write(canfd, &pongmsg, sizeof(pongmsg));
+                       if (ret < 0) {
+                               if (NOT_INTERRUPTED_SYSCALL) {
+                                       perror("write");
+                                       exit(EXIT_WRITE_ERROR);
+                               }
+                       }
+               }
+
+               if (ret >= 0) total_count++;
+
+               if (option_verbose >= 2)
+                       /* This drasticly slows down the pong
+                        * response. Why??? */
+                       printf("Replying to ping id %lu\n", pingmsg.id);
+
+       }
+
+       if (!option_open_once) {
+               /* Close can driver */
+               close(canfd);
+       }
+
+        dbg(2, "Slave thread for id %d is going to finish\n", ping_id);
+
+       sem_post(&finish_sem);
+       return (void *)ret;
+}
+     
+void start_slaves(int slaves, int first_id)
+{
+       int id = first_id;
+       int i;
+
+       dbg(1, "Starting %d slave threads\n", slaves);
+       
+       for (i = 0; i < slaves; i++, id += 2) {
+               thread_data_t *td;
+               
+               td = malloc(sizeof(*td));
+               if (!td) {
+                       printf("Can't allocate memory");
+                       exit(EXIT_NO_MEM);
+               }
+               memset(td, 0, sizeof(*td));
+               td->canid = id;
+               /* TODO use mutexes and signal blocking */
+               thread_list_ins_tail(&slave_threads, td);
+               pthread_create(&td->tid, NULL, slave_thread, (void *)td);
+               dbg(2, "Slave thread: %p\n", (void *)td->tid);
+       }
+}
+
+void print_help(void)
+{
+       printf("Usage: canping -m <master threads> [other options]\n"
+              "       canping -s <slave threads> [other options]\n\n"
+              "Other options:\n"
+              "  -c count      how many messages each master sends\n"
+              "  -d dev        device (e.g. /dev/can1)\n"
+              "  -h            print this help\n"
+              "  -i id         id of first master message\n"
+              "  -l length     length of the messages (0..8)\n"
+              "  -o            open a device only once for all threads (doesn't work)\n" /* due to filters */
+              "  -t timeout    timeout in seconds (default 4 s)\n"
+              "  -v            be verbose (use more than once to increase verbosity)\n"
+              "  -w ms         wait ms miliseconds between sending pings\n"
+              "  -y            synchronize threads before start (doesn't test race conditions\n"
+              "                between open and read/write)\n"
+              "\n"
+              "Example: canping -m 10 -vv\n"
+               );
+}
+     
+int parse_options(int argc, char *argv[])
+{
+       int c;
+       
+       opterr = 0;
+       while ((c = getopt (argc, argv, "c:d:hi:l:m:os:t:vw:y")) != -1)
+               switch (c)
+               {
+               case 'c':
+                       option_count = atoi(optarg);
+                       break;
+               case 'd':
+                       option_device = optarg;
+                       break;
+               case 'h':
+                       print_help();
+                       exit(EXIT_OK);
+                       break;
+               case 'i':
+                       option_first_id = atoi(optarg);
+                       break;
+               case 'l':
+                       option_length = atoi(optarg);
+                        if (option_length > 8) option_length = 8;
+                        if (option_length < 0) option_length = 0;
+                       break;
+               case 'm':
+                       option_masters = atoi(optarg);
+                       break;
+               case 'o':
+                       option_open_once = 1;
+                       break;
+               case 's':
+                       option_slaves = atoi(optarg);
+                       break;
+               case 't':
+                       option_timeout = atoi(optarg);
+                       break;
+               case 'v':
+                       option_verbose++;
+                       break;
+               case 'w':
+                       option_wait_ms = atoi(optarg);
+                       break;
+               case 'y':
+                       option_synch_start = 1;
+                       break;
+               case '?':
+                       if (isprint (optopt))
+                               fprintf (stderr, "Unknown option `-%c'.\n", optopt);
+                       else
+                               fprintf (stderr,
+                                        "Unknown option character `\\x%x'.\n",
+                                        optopt);
+                       return 1;
+               default:
+                       exit(EXIT_BAD_PARAM);
+               }
+       if (!(option_masters || option_slaves) || (option_masters && option_slaves))
+               exit(EXIT_BAD_PARAM);
+       return 0;
+}
+
+void print_stats(thread_data_t *td)
+{
+       char std[20];
+       int count = td->count + td->timeout;
+       
+       if (td->count >= 2) {
+                snprintf(std, 19, "%7.2f", sqrt((td->count/(td->count - 1)) * 
+                                                (td->moment2nd - td->mean*td->mean)));
+       } else {
+               strncpy(std, "N/A", 19);
+       }
+       printf("Id %4ld: count = %5d"
+              " mean = %7.2f stddev = %s"
+              " min = %5d max = %5d [us]"
+              " loss = %d%% (%d)\n",
+              td->canid, count, td->mean, 
+              std,
+              td->min, td->max,
+              (count > 0) ? 100 * td->timeout / count : 0, td->timeout
+               );
+}
+
+void wait_for_threads(void)
+{
+       thread_data_t *td;
+       void *thread_ret;
+       int thread_count = option_slaves + option_masters;
+        int ret;
+
+       while (thread_count > 0) {
+               if (option_verbose != 1) {
+                       ret = sem_wait(&finish_sem);
+                        dbg(2, "Main thread sem_wait() exited with ret=%d.\n", ret);
+
+                        /* If the sem_wait is successful i.e. not
+                         * interrupted by a signal, decrease the
+                         * thread_count*/
+                       if (ret == 0) thread_count--; 
+               } else {
+                       if (!IS_FINISH_FLAG()) {
+                               printf("Total count: %6d, Timeouts: %6d\r", total_count, total_timeout);
+                               fflush(stdout);
+                               usleep(1000000);
+                       }
+                       while (sem_trywait(&finish_sem) == 0) thread_count--;
+               }
+       }
+       if (option_verbose == 1) {
+               printf("\n");
+       }
+
+       ul_list_for_each(thread_list, &master_threads, td) {
+               pthread_join(td->tid, &thread_ret);
+               dbg(2, "Master thread for id %ld finished; exit code %d\n", td->canid, (int)thread_ret);
+       }
+
+       ul_list_for_each(thread_list, &slave_threads, td) {
+               pthread_join(td->tid, &thread_ret);
+               dbg(2, "Slave thread for id %ld finished; exit code %d\n", td->canid, (int)thread_ret);
+       }
+
+       if (option_masters) printf("Summary statistics:\n");
+
+       ul_list_for_each_cut(thread_list, &master_threads, td) {
+               print_stats(td);
+               free(td);
+       }
+       
+
+       ul_list_for_each_cut(thread_list, &slave_threads, td) 
+               free(td);
+}
+
+int main(int argc, char *argv[])
+{
+       parse_options(argc, argv);
+
+       thread_list_init_head(&master_threads);
+       thread_list_init_head(&slave_threads);
+
+       sem_init(&finish_sem, 0, 0);
+       sem_init(&ready_sem, 0, 0);
+
+       siginterrupt(SIGINT, 1);
+       signal(SIGINT, term_handler);
+       siginterrupt(SIGTERM, 1);
+       signal(SIGTERM, term_handler);
+
+       dbg(2, "Main thread: %p\n", (void *)pthread_self());
+
+       if (option_open_once) {
+               /* Open the CAN driver */
+               if ((global_canfd = open(option_device, O_RDWR)) < 0) {
+                       perror("open");
+                       printf("Error opening %s\n", option_device);
+                       exit(EXIT_CANNOT_OPEN); 
+               }
+       }
+
+       if (option_masters) start_masters(option_masters, option_first_id);
+       if (option_slaves) start_slaves(option_slaves, option_first_id);
+
+       wait_for_threads();
+
+       if (option_open_once) {
+               close(global_canfd);
+       }
+
+       return 0;
+}
diff --git a/src/canping_rtl.c b/src/canping_rtl.c
new file mode 100644 (file)
index 0000000..004431c
--- /dev/null
@@ -0,0 +1,620 @@
+#include <posix/unistd.h>
+#include <linux/module.h>
+#include <asm/atomic.h>
+#include <pthread.h>
+#include <ul_list.h>
+#include <semaphore.h>
+#include <linux/time.h>
+
+#include <can.h>
+
+/* TODO osetrit vicero pongu */
+
+/* Defines for better compatibility with user-space version */
+#define option_device device
+#define option_masters masters
+#define option_first_id first_id
+#define option_slaves slaves
+#define option_verbose verbose
+#define option_length length
+#define option_count count
+#define option_wait_ms wait_ms
+#define option_timeout timeout
+#define option_open_once open_once
+#define option_synch_start synch_start
+
+#define printf rtl_printf
+#define fprintf(file, fmt, arg...) printf(fmt, ## arg)
+#define perror(s) printf("%s: error %d\n", s, errno);
+#define exit(retval) pthread_exit((void *)retval)
+#define select(NFDS, READFDS, WRITEFDS, EXCEPTFDS, TIMEOUT) (1)
+#define malloc(size) kmalloc(size, GFP_KERNEL)
+#define free(what) kfree(what)
+#define sqrt(val) (0.0)
+#define fflush(fd)
+#define signal(a,b)
+#define siginterrupt(a, b)
+#define gettimeofday(a, b) do {;} while (0) //do_gettimeofday(a)
+
+#ifndef DEBUG
+#define dbg(level, fmt, arg...) do {} while (0)
+#else
+#define dbg(level, fmt, arg...) do { if (level <= DEBUG) { printf("candping: " fmt, ## arg); } } while (0)
+#endif
+
+#define NOT_INTERRUPTED_SYSCALL (errno != EINTR && errno != ERESTART)
+#define IS_FINISH_FLAG() (atomic_read(&finish_flag) != 0)
+
+/* Exit codes */
+#define EXIT_OK 0
+#define EXIT_BAD_PARAM 1
+#define EXIT_CANNOT_OPEN 2
+#define EXIT_FILTER_ERROR 3
+#define EXIT_NO_MEM 4
+#define EXIT_READ_ERROR 5
+#define EXIT_WRITE_ERROR 6
+#define EXIT_SELECT_ERROR 7
+#define EXIT_FLUSH_ERROR 8
+
+/* Global variables */
+atomic_t finish_flag = ATOMIC_INIT(0); /* Threads should terminate. */
+sem_t finish_sem;              /* Thread signals a termination */
+int global_canfd;
+
+int total_count = 0;
+int total_timeout = 0;
+
+pthread_mutex_t mut_start = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t cond_start = PTHREAD_COND_INITIALIZER;
+int start_flag = 0;
+sem_t ready_sem;               /* Thread is ready for execution */
+
+
+/* Command line options */
+char *option_device = "/dev/can0";
+int option_masters = 0;
+long int option_first_id = 1000;
+int option_slaves = 0;
+int option_verbose = 0;                /* 0 - nothing, 1 - only global
+                                * statistics, 2 - simple times, 3 -
+                                * verbose times */
+int option_length = 8;
+int option_count = 0;
+int option_wait_ms = 1000;
+int option_timeout = 4;
+int option_open_once = 0;
+int option_synch_start = 0;
+
+MODULE_PARM_DESC(option_device,"name of CAN device [/dev/can0]");
+MODULE_PARM(option_device,"1s");
+
+MODULE_PARM_DESC(option_first_id,"lowest ID of generated messages [1000]");
+MODULE_PARM(option_first_id,"1i");
+
+MODULE_PARM_DESC(option_masters,"how many master threads to start");
+MODULE_PARM(option_masters,"1i");
+
+MODULE_PARM_DESC(option_slaves,"how many slave threads to start");
+MODULE_PARM(option_slaves,"1i");
+
+MODULE_PARM_DESC(option_verbose,"verbosity level");
+MODULE_PARM(option_verbose,"1i");
+
+MODULE_PARM_DESC(option_count,"number of messages to send");
+MODULE_PARM(option_count,"1i");
+
+MODULE_PARM_DESC(option_wait_ms,"miliseconds to wait between sent messages");
+MODULE_PARM(option_wait_ms,"1i");
+
+MODULE_PARM_DESC(option_synch_start,"synchronize threads before start");
+MODULE_PARM(option_synch_start,"1i");
+
+
+MODULE_SUPPORTED_DEVICE("canping");
+MODULE_AUTHOR("Michal Sojka <sojkam1@fel.cvut.cz>");
+MODULE_DESCRIPTION("canping: RT-Linux LinCAN test application");
+MODULE_LICENSE("GPL");
+
+/* Lists */
+typedef struct threads {
+       ul_list_head_t head;
+} threads_t;
+
+typedef struct thread_data {
+       pthread_t tid;
+       long int canid;
+
+       int count;
+       double mean;            /* mean value of responses */
+       double moment2nd;       /* used to compute variance of
+                                * responses */
+       int min, max;           /* min/max response times */
+       int timeout;            /* number of timeouts */
+
+       ul_list_node_t node;
+} thread_data_t;
+
+UL_LIST_CUST_DEC(thread_list, threads_t, thread_data_t, head, node);
+
+threads_t master_threads;
+threads_t slave_threads;
+
+/* Subtract the `struct timeval' values X and Y, storing the result in
+   RESULT.  Return 1 if the difference is negative, otherwise 0.  */
+     
+int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y)
+{
+       /* Perform the carry for the later subtraction by updating Y. */
+       if (x->tv_usec < y->tv_usec) {
+               int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
+               y->tv_usec -= 1000000 * nsec;
+               y->tv_sec += nsec;
+       }
+       if (x->tv_usec - y->tv_usec > 1000000) {
+               int nsec = (x->tv_usec - y->tv_usec) / 1000000;
+               y->tv_usec += 1000000 * nsec;
+               y->tv_sec -= nsec;
+       }
+     
+       /* Compute the time remaining to wait.
+          `tv_usec' is certainly positive. */
+       result->tv_sec = x->tv_sec - y->tv_sec;
+       result->tv_usec = x->tv_usec - y->tv_usec;
+     
+       /* Return 1 if result is negative. */
+       return x->tv_sec < y->tv_sec;
+}
+
+void dbg_print_timeval(char *msg, struct timeval *tv)
+{
+
+       printf("%s sec=%ld usec=%ld\n", msg, tv->tv_sec, tv->tv_usec);
+}
+void kill_all_threads(int signum)
+{
+       thread_data_t *td;
+               
+       ul_list_for_each(thread_list, &master_threads, td) {
+               pthread_kill(td->tid, signum);
+       }
+       ul_list_for_each(thread_list, &slave_threads, td) {
+               pthread_kill(td->tid, signum);
+       }
+}
+void term_handler(int signum)
+{
+       dbg(1, "Thread %p got a signal %d\n", (void *)pthread_self(), signum);
+       if (!IS_FINISH_FLAG()) {
+               dbg(1, "Terminating threads\n");
+               atomic_set(&finish_flag, 1);
+
+               kill_all_threads(signum);
+       }
+}
+
+void *master_thread(void *arg)
+{
+       thread_data_t *td = (thread_data_t *)arg;
+       int ping_id = td->canid;
+       int pong_id = ping_id + 1;
+       struct canmsg_t pingmsg, pongmsg;
+       int ping_count = 0;
+       int ret = 0;
+       int canfd;              /* File descriptor of CAN driver. */
+       int i;
+       fd_set rdset;           /* read set for select syscall */
+       struct timeval to, pingtime, pongtime;
+       struct canfilt_t canfilt; /* filter for received messages */
+
+       if (!option_open_once) {
+               /* Open can driver */
+               if ((canfd = open(option_device, O_RDWR)) < 0) {
+                       perror("open");
+                       fprintf(stderr, "Error opening %s (for id %d)\n", option_device, ping_id);
+                       exit(EXIT_CANNOT_OPEN); 
+               }
+       } else {
+               canfd = global_canfd;
+       }
+       
+       /* setup filtering of received messages */
+       memset(&canfilt, 0, sizeof(canfilt));
+       canfilt.mask = 0xfffffff;
+       canfilt.id = pong_id;   /* pong responces with increased id */
+       ret = ioctl(canfd, CANQUE_FILTER, &canfilt);
+       if(ret<0) {
+               perror("ioctl CANQUE_FILTER");
+               exit(EXIT_FILTER_ERROR);
+       }
+       ret = ioctl(canfd, CANQUE_FLUSH, NULL);
+       if(ret<0) {
+               perror("ioctl CANQUE_QUEUE_FLUSH");
+               exit(EXIT_FLUSH_ERROR);
+       }
+       
+       /* Prepare data structures for the select syscall. */
+       FD_ZERO (&rdset);
+
+       /* Signal that I'm ready */
+       sem_post(&ready_sem);
+
+       if (option_synch_start) {
+               /* Wait for other threads to initialize */
+               pthread_mutex_lock(&mut_start);
+               while (!start_flag) pthread_cond_wait(&cond_start, &mut_start);
+               pthread_mutex_unlock(&mut_start);
+       }
+
+       while (!IS_FINISH_FLAG() && (option_count == 0 || ping_count++ < option_count)) {
+               /* Send a ping message */
+               pingmsg.flags=0;
+               pingmsg.id=ping_id;
+               pingmsg.length = option_length;
+               for (i=0; i < option_length; i++) pingmsg.data[i] = i;
+               gettimeofday(&pingtime, NULL);
+
+               ret = write(canfd, &pingmsg, sizeof(pingmsg));
+               if (ret < 0) {
+                       if (NOT_INTERRUPTED_SYSCALL) {
+                               perror("write");
+                               exit(EXIT_WRITE_ERROR);
+                       }
+               }
+               
+               /* Wait for a pong responce */
+               FD_SET (canfd, &rdset);
+     
+               to.tv_sec = option_timeout;
+               to.tv_usec = 0;
+     
+               ret = select(FD_SETSIZE, &rdset, NULL, NULL, &to);
+               if (ret > 0) {
+                       /* Read the message */
+                       pongmsg.flags=0;
+                       ret = read(canfd, &pongmsg, sizeof(pongmsg));
+                       if (ret < 0) {
+                               if (NOT_INTERRUPTED_SYSCALL) {
+                                       perror("read");
+                                       exit(EXIT_READ_ERROR);
+                               }
+                       }
+                       else {  /* A pong message received */
+                               long int time;
+                               gettimeofday(&pongtime, NULL);
+                               if (ret == 0) {
+                                       fprintf(stderr, "read returned zero\n");
+                                       exit(EXIT_READ_ERROR);
+                               }
+                               if (pongmsg.id != pong_id) {
+                                       fprintf(stderr, "Filter error (expected: %d, received %ld)\n", pong_id, pongmsg.id);
+                                       exit(EXIT_FILTER_ERROR);
+                               }
+                               timeval_subtract(&pongtime, &pongtime, &pingtime);
+                               time = pongtime.tv_sec*1000000 + pongtime.tv_usec;
+                               switch (option_verbose) {
+                               case 2:
+                                       printf("%d:%ld\n", ping_id, time); 
+                                       break;
+                               case 3:
+                                       printf("Pong response for id %d received in %ld us\n", ping_id, time);
+                                       break;
+                               }
+                               /* Update statistics */
+                               td->count++;
+                               td->mean = 
+                                       td->mean * ((double)(td->count - 1) / td->count) + 
+                                       (double)time / td->count;
+                               td->moment2nd = 
+                                       td->moment2nd * ((double)(td->count - 1) / td->count) + 
+                                       (double)time*time  / td->count;
+                               if (time > td->max) td->max = time;
+                               if (time < td->min) td->min = time;
+                               total_count++;
+                       } /* read */
+               } else if (ret == 0) { /* select */
+                       if (option_verbose >= 2) 
+                               printf("Timeout encountered (id %d)\n", ping_id);
+                       td->timeout++;
+                       total_timeout++;
+               } else {
+                       if (NOT_INTERRUPTED_SYSCALL) {
+                               perror("select");
+                               exit(EXIT_SELECT_ERROR);
+                       }
+               }
+               usleep(option_wait_ms * 1000);
+       }
+
+       if (!option_open_once) {
+               /* Close the can driver */
+               close(canfd);
+       }
+
+       sem_post(&finish_sem);
+       return (void *)ret;
+}
+
+void start_masters(int masters, int first_id)
+{
+       int id = first_id;
+       int i;
+
+       dbg(1, "Starting %d master threads\n", masters);
+       
+       for (i = 0; i < masters; i++, id += 2) {
+               thread_data_t *td;
+               
+               td = malloc(sizeof(*td));
+               if (!td) {
+                       printf("Can't allocate memory");
+                       exit(EXIT_NO_MEM);
+               }
+               memset(td, 0, sizeof(*td));
+               td->canid = id;
+               td->min = 0x7fffffff;
+               /* TODO use mutexes and signal blocking */
+               thread_list_ins_tail(&master_threads, td);
+               pthread_create(&td->tid, NULL, master_thread, (void *)td);
+               dbg(2, "Master thread: %p\n", (void *)td->tid);
+       }
+
+
+       /* Wait for all threads beeing ready */
+       for (i = 0; i < masters; i++) sem_wait(&ready_sem);
+
+       /* Start threads */
+       pthread_mutex_lock(&mut_start);
+       start_flag = 1;
+       pthread_cond_broadcast(&cond_start);
+       pthread_mutex_unlock(&mut_start);
+}
+
+void *slave_thread(void *arg)
+{
+       thread_data_t *td = (thread_data_t *)arg;
+       int ping_id = td->canid;
+       int pong_id = ping_id + 1;
+       struct canmsg_t pingmsg, pongmsg;
+       int ret = 0;
+       int canfd;              /* File descriptor of CAN driver. */
+       int i;
+       struct canfilt_t canfilt; /* filter for received messages */
+
+       if (!option_open_once) {
+               /* Open the CAN driver */
+               if ((canfd = open(option_device, O_RDWR)) < 0) {
+                       perror("open");
+                       printf("Error opening %s (for id %d)\n", option_device, ping_id);
+                       exit(EXIT_CANNOT_OPEN); 
+               }
+       } else {
+               canfd = global_canfd;
+       }
+       
+       /* setup filtering of received messages */
+       memset(&canfilt, 0, sizeof(canfilt));
+       canfilt.mask = 0xfffffff;
+       canfilt.id = ping_id;   /* receive only our ping messages */
+       ret = ioctl(canfd, CANQUE_FILTER, &canfilt);
+       if (ret < 0) {
+               perror("ioctl CANQUE_FILTER");
+               exit(EXIT_FILTER_ERROR);
+       }
+       /* If there are some messages already, delete them. These may
+        * not processed by our filter and thus may have a wrong
+        * ID. */
+       ret = ioctl(canfd, CANQUE_FLUSH, NULL);
+       if (ret < 0) {
+               perror("ioctl CANQUE_QUEUE_FLUSH");
+               exit(EXIT_FLUSH_ERROR);
+       }
+       
+       /* Prepare a pong message */
+       pongmsg.flags = 0;
+       pongmsg.id = pong_id;
+       pongmsg.length = option_length;
+       for (i=0; i < option_length; i++) pongmsg.data[i] = i;
+
+       while (!IS_FINISH_FLAG()) {
+               /* Receive a ping message */
+               pingmsg.flags=0;
+               ret = read(canfd, &pingmsg, sizeof(pingmsg));
+               if (ret < 0) {
+                       if (NOT_INTERRUPTED_SYSCALL) {
+                               printf("%d\n", errno);
+                               perror("read");
+                               exit(EXIT_READ_ERROR);
+                       }
+               }
+               if (NOT_INTERRUPTED_SYSCALL && pingmsg.id != ping_id) {
+                       fprintf(stderr, "Filter error (expected: %d, received %ld)\n", ping_id, pingmsg.id);
+                       exit(EXIT_FILTER_ERROR);
+               }
+               /* Answer immendiately with a pong message */
+               if (NOT_INTERRUPTED_SYSCALL) {
+                       ret = write(canfd, &pongmsg, sizeof(pongmsg));
+                       if (ret < 0) {
+                               if (NOT_INTERRUPTED_SYSCALL) {
+                                       perror("write");
+                                       exit(EXIT_WRITE_ERROR);
+                               }
+                       }
+               }
+
+               if (ret >= 0) total_count++;
+
+               if (option_verbose >= 2)
+                       /* This drasticly slows down the pong
+                        * response. Why??? */
+                       printf("Replying to ping id %lu\n", pingmsg.id);
+
+       }
+
+       if (!option_open_once) {
+               /* Close can driver */
+               close(canfd);
+       }
+
+       sem_post(&finish_sem);
+       return (void *)ret;
+}
+     
+void start_slaves(int slaves, int first_id)
+{
+       int id = first_id;
+       int i;
+
+       dbg(1, "Starting %d slave threads\n", slaves);
+       
+       for (i = 0; i < slaves; i++, id += 2) {
+               thread_data_t *td;
+               
+               td = malloc(sizeof(*td));
+               if (!td) {
+                       printf("Can't allocate memory");
+                       exit(EXIT_NO_MEM);
+               }
+               memset(td, 0, sizeof(*td));
+               td->canid = id;
+               /* TODO use mutexes and signal blocking */
+               thread_list_ins_tail(&slave_threads, td);
+               pthread_create(&td->tid, NULL, slave_thread, (void *)td);
+               dbg(2, "Slave thread: %p\n", (void *)td->tid);
+       }
+}
+
+void print_help(void)
+{
+       printf("Usage: canping -m <master threads> [other options]\n"
+              "       canping -s <slave threads> [other options]\n\n"
+              "Other options:\n"
+              "  -c count      how many messages each master sends\n"
+              "  -d dev        device (e.g. /dev/can1)\n"
+              "  -h            print this help\n"
+              "  -i id         id of first master message\n"
+              "  -l length     length of the messages (0..8)\n"
+              "  -o            open a device only once for all threads (doesn't work)\n" /* due to filters */
+              "  -t timeout    timeout in seconds (default 4 s)\n"
+              "  -v            be verbose (use more than once to increase verbosity)\n"
+              "  -w ms         wait ms miliseconds between sending pings\n"
+              "  -y            synchronize threads before start (doesn't test race conditions\n"
+              "                between open and read/write)\n"
+              "\n"
+              "Example: canping -m 10 -vv\n"
+               );
+}
+     
+void print_stats(thread_data_t *td)
+{
+       char std[20];
+       int count = td->count + td->timeout;
+       
+       if (td->count >= 2) {
+                snprintf(std, 19, "%7.2f", sqrt((td->count/(td->count - 1)) * 
+                                                (td->moment2nd - td->mean*td->mean)));
+       } else {
+               strncpy(std, "N/A", 19);
+       }
+       printf("Id %4ld: count = %5d"
+              " mean = %7.2f stddev = %s"
+              " min = %5d max = %5d [us]"
+              " loss = %d%% (%d)\n",
+              td->canid, count, td->mean, 
+              std,
+              td->min, td->max,
+              (count > 0) ? 100 * td->timeout / count : 0, td->timeout
+               );
+}
+
+void wait_for_threads(void)
+{
+       thread_data_t *td;
+       void *thread_ret;
+       int thread_count = option_slaves + option_masters;
+       while (thread_count > 0) {
+               if (option_verbose != 1) {
+                       sem_wait(&finish_sem);
+                       thread_count--;
+               } else {
+                       if (!IS_FINISH_FLAG()) {
+                               printf("Total count: %6d, Timeouts: %6d\r", total_count, total_timeout);
+                               fflush(stdout);
+                               usleep(1000000);
+                       }
+                       while (sem_trywait(&finish_sem) == 0) thread_count--;
+               }
+       }
+       if (option_verbose == 1) {
+               printf("\n");
+       }
+
+       ul_list_for_each(thread_list, &master_threads, td) {
+               pthread_join(td->tid, &thread_ret);
+               dbg(2, "Master thread for id %ld finished; exit code %d\n", td->canid, (int)thread_ret);
+       }
+
+       ul_list_for_each(thread_list, &slave_threads, td) {
+               pthread_join(td->tid, &thread_ret);
+               dbg(2, "Slave thread for id %ld finished; exit code %d\n", td->canid, (int)thread_ret);
+       }
+
+       if (option_masters) printf("Summary statistics:\n");
+
+       ul_list_for_each_cut(thread_list, &master_threads, td) {
+               print_stats(td);
+               free(td);
+       }
+       
+
+       ul_list_for_each_cut(thread_list, &slave_threads, td) 
+               free(td);
+}
+
+int main(int argc, char *argv[])
+{
+       thread_list_init_head(&master_threads);
+       thread_list_init_head(&slave_threads);
+
+       sem_init(&finish_sem, 0, 0);
+       sem_init(&ready_sem, 0, 0);
+
+       siginterrupt(SIGINT, 1);
+       signal(SIGINT, term_handler);
+       siginterrupt(SIGTERM, 1);
+       signal(SIGTERM, term_handler);
+
+       dbg(2, "Main thread: %p\n", (void *)pthread_self());
+
+       if (option_open_once) {
+               /* Open the CAN driver */
+               if ((global_canfd = open(option_device, O_RDWR)) < 0) {
+                       perror("open");
+                       printf("Error opening %s\n", option_device);
+                       exit(EXIT_CANNOT_OPEN); 
+               }
+       }
+
+       if (option_masters) start_masters(option_masters, option_first_id);
+       if (option_slaves) start_slaves(option_slaves, option_first_id);
+
+/*     wait_for_threads(); */
+
+/*     if (option_open_once) { */
+/*             close(global_canfd); */
+/*     } */
+
+       return 0;
+}
+
+int init_module(void)
+{
+        rtl_printf("Loading canping_rtl module...\n");
+        return main(0, NULL);
+}
+
+void cleanup_module(void) 
+{
+       term_handler(0);
+        wait_for_threads();
+        rtl_printf("Module canping_rtl finished.\n");
+}