--- /dev/null
+# 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
+
--- /dev/null
+# -*- makefile -*-
+
+SUBDIRS = src doc
\ No newline at end of file
--- /dev/null
+<!-- -*- nxml -*- -->
+
+<!ENTITY canping SYSTEM "canping.xml">
+<!-- <!ENTITY canping-man SYSTEM "canping-man.xml"> -->
+
--- /dev/null
+<?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 – 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(&canfilt, 0, sizeof(canfilt));
+canfilt.mask = 0xfffffff;
+canfilt.id = pong_id; /* pong responces with increased id */
+ret = ioctl(canfd, CANQUE_FILTER, &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 < option_length; i++) pingmsg.data[i] = i;
+</programlisting>
+
+ <para>Later, the message is sent by:</para>
+
+ <programlisting>ret = write(canfd, &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, &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 > 0) {
+ ret = sem_wait(&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, &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 && 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
--- /dev/null
+#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 */
--- /dev/null
+#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 */
--- /dev/null
+#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 */
--- /dev/null
+/*******************************************************************
+ 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 */
--- /dev/null
+# 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
+
--- /dev/null
+# -*- 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
--- /dev/null
+#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;
+}
--- /dev/null
+#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");
+}