]> rtime.felk.cvut.cz Git - sojka/can-utils.git/commitdiff
can-utils: canfdtest, a full duplex test to find out-of-order messages
authorWolfgang Grandegger <wg@grandegger.com>
Wed, 25 Nov 2009 11:18:22 +0000 (11:18 +0000)
committerWolfgang Grandegger <wg@grandegger.com>
Wed, 25 Nov 2009 11:18:22 +0000 (11:18 +0000)
This patch is a pepped up version of Valdislav's canecho_gen and
canecho_dut test programs, which have prooved to be useful for
detecting out-of-order message transmisson and reception. Here
is a list of the changes and improvements:

- Both programs have been merged into on test program named
  canfdtest. Message generation can be selected via the command
  line option '-g'.
- The test loop count can be specified.
- A low and high verbosity level has been added.
- send/recv is used instead of write/read.
- The return code of send/recv is checked properly.
- Use Linux coding style.

Signed-off-by: Wolfgang Grandegger <wg@grandegger.com>
Makefile
canfdtest.c [new file with mode: 0644]

index 5f32ef4824e5e383d18745075c75aba78b3dfdf3..5db72c8890e066f1304788260deb4d43cbdfa9be 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -52,7 +52,7 @@ CFLAGS    = -O2 -Wall -Wno-parentheses -I$(KERNELDIR)/include \
 PROGRAMS = candump cansniffer cansend canplayer canlogserver cangen\
           canbusload log2long log2asc asc2log bcmserver\
           isotpdump isotprecv isotpsend isotpsniffer isotptun\
-          slcan_attach slcand slcanpty
+          slcan_attach slcand slcanpty canfdtest
 
 all: $(PROGRAMS)
 
diff --git a/canfdtest.c b/canfdtest.c
new file mode 100644 (file)
index 0000000..6752d1f
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * canfdtest.c - Full-duplex test program (DUT and host part)
+ *
+ * (C) 2009 by Vladislav Gribov, IXXAT Automation GmbH, <gribov@ixxat.de>
+ * (C) 2009 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <libgen.h>
+#include <getopt.h>
+#include <time.h>
+#include <sched.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+
+#include <linux/can.h>
+#include <linux/can/raw.h>
+
+#define CAN_MSG_ID     0x77
+#define CAN_MSG_LEN    8
+#define CAN_MSG_COUNT  50
+#define CAN_MSG_WAIT   27
+
+static int running = 1;
+static int verbose;
+static int sockfd;
+static int test_loops;
+
+static void print_usage(char *prg)
+{
+       fprintf(stderr,
+               "Usage: %s [options] <can-interface>\n"
+               "\n"
+               "Options: -v       (low verbosity)\n"
+               "         -vv      (high verbosity)\n"
+               "         -g       (generate messages)\n"
+               "         -l COUNT (test loop count)\n"
+               "\n"
+               "With the option '-g' CAN messages are generated and checked\n"
+               "on <can-interface>, otherwise all messages received on the\n"
+                "<can-interface> are sent back incrementing the CAN id and\n"
+               "all data bytes. The program can be aborted with ^C.\n"
+               "\n"
+               "Example:\n"
+               "\ton DUT : %s -v can0\n"
+               "\ton Host: %s -g -v can2\n",
+               prg, prg, prg);
+
+       exit(1);
+}
+
+static void print_frame(struct can_frame *frame)
+{
+       int i;
+
+       printf("%04x: ", frame->can_id);
+       if (frame->can_id & CAN_RTR_FLAG) {
+               printf("remote request");
+       } else {
+               printf("[%d]", frame->can_dlc);
+               for (i = 0; i < frame->can_dlc; i++)
+                       printf(" %02x", frame->data[i]);
+       }
+       printf("\n");
+}
+
+static void print_compare(struct can_frame *exp, struct can_frame *rec)
+{
+       printf("expected: ");
+       print_frame(exp);
+       printf("received: ");
+       print_frame(rec);
+}
+
+static void compare_frame(struct can_frame *exp, struct can_frame *rec)
+{
+       int i;
+
+       if (rec->can_id != exp->can_id) {
+               printf("Message ID mismatch!\n");
+               print_compare(exp, rec);
+               running = 0;
+       } else if (rec->can_dlc != exp->can_dlc) {
+               printf("Message length mismatch!\n");
+               print_compare(exp, rec);
+               running = 0;
+       } else {
+               for (i = 0; i < rec->can_dlc; i++) {
+                       if (rec->data[i] != exp->data[i]) {
+                               printf("Databyte %x mismatch !\n", i);
+                               print_compare(exp,
+                                             rec);
+                               running = 0;
+                       }
+               }
+       }
+}
+
+static void millisleep(int msecs)
+{
+       if (msecs <= 0) {
+               sched_yield();
+       } else {
+               struct timespec rqtp, rmtp;
+
+               /* sleep in ms */
+               rqtp.tv_sec = msecs / 1000;
+               rqtp.tv_nsec = (msecs % 1000) * 1000000;
+               while (nanosleep(&rqtp, &rmtp)) {
+                       if (errno != EINTR) {
+                               printf("t\n");
+                               break;
+                       }
+                       rqtp = rmtp;
+               }
+       }
+}
+
+static void echo_progress(unsigned char data)
+{
+       if (data == 0xff) {
+               printf(".");
+               fflush(stdout);
+       }
+}
+
+static void signal_handler(int signo)
+{
+       close(sockfd);
+       running = 0;
+}
+
+static int recv_frame(struct can_frame *frame)
+{
+       int ret;
+
+       ret = recv(sockfd, frame, sizeof(*frame), 0);
+       if (ret != sizeof(*frame)) {
+               if (ret < 0)
+                       perror("recv failed");
+               else
+                       fprintf(stderr, "recv returned %d", ret);
+               return -1;
+       }
+       return 0;
+}
+
+static int send_frame(struct can_frame *frame)
+{
+       int ret;
+
+       while ((ret = send(sockfd, frame, sizeof(*frame), 0))
+              != sizeof(*frame)) {
+               if (ret < 0) {
+                       if (errno != ENOBUFS) {
+                               perror("send failed");
+                               return -1;
+                       } else {
+                               if (verbose) {
+                                       printf("N");
+                                       fflush(stdout);
+                               }
+                       }
+               } else {
+                       fprintf(stderr, "send returned %d", ret);
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+static int can_echo_dut(void)
+{
+       unsigned int frame_count = 0;
+       struct timeval tvn, tv_stop;
+       struct can_frame frame;
+       int i;
+
+       while (running) {
+               if (recv_frame(&frame))
+                       return -1;
+               frame_count++;
+               if (verbose == 1) {
+                       echo_progress(frame.data[0]);
+               } else if (verbose > 1) {
+                       printf("%04x: ", frame.can_id);
+                       if (frame.can_id & CAN_RTR_FLAG) {
+                               printf("remote request");
+                       } else {
+                               printf("[%d]", frame.can_dlc);
+                               for (i = 0; i < frame.can_dlc; i++)
+                                       printf(" %02x", frame.data[i]);
+                       }
+                       printf("\n");
+               }
+               frame.can_id++;
+               for (i = 0; i < frame.can_dlc; i++)
+                       frame.data[i]++;
+               if (send_frame(&frame))
+                       return -1;
+
+               /*
+                * to force a interlacing of the frames send by DUT and PC
+                * test tool a waiting time is injected
+                */
+               if (frame_count == CAN_MSG_WAIT) {
+                       frame_count = 0;
+                       if (gettimeofday(&tv_stop, NULL)) {
+                               perror("gettimeofday failed\n");
+                               return -1;
+                       } else {
+                               tv_stop.tv_usec += 3000;
+                               if (tv_stop.tv_usec > 999999) {
+                                       tv_stop.tv_sec++;
+                                       tv_stop.tv_usec =
+                                               tv_stop.tv_usec % 1000000;
+                               }
+                               gettimeofday(&tvn, NULL);
+                               while ((tv_stop.tv_sec > tvn.tv_sec) ||
+                                      ((tv_stop.tv_sec = tvn.tv_sec) &&
+                                       (tv_stop.tv_usec >= tvn.tv_usec)))
+                                       gettimeofday(&tvn, NULL);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int can_echo_gen(void)
+{
+       struct can_frame tx_frames[CAN_MSG_COUNT];
+       struct can_frame rx_frame;
+       unsigned char counter = 0;
+       int send_pos = 0, recv_pos = 0, unprocessed = 0, loops = 0;
+       int i;
+
+       while (running) {
+               if (unprocessed < CAN_MSG_COUNT) {
+                       /* still send messages */
+                       tx_frames[send_pos].can_dlc = CAN_MSG_LEN;
+                       tx_frames[send_pos].can_id = CAN_MSG_ID;
+                       for (i = 0; i < CAN_MSG_LEN; i++)
+                               tx_frames[send_pos].data[i] = counter + i;
+                       if (send_frame(&tx_frames[send_pos]))
+                               return -1;
+
+                       /* increment to be equal to expected */
+                       tx_frames[send_pos].can_id++;
+                       for (i = 0; i < CAN_MSG_LEN; i++)
+                               tx_frames[send_pos].data[i]++;
+
+                       send_pos++;
+                       if (send_pos == CAN_MSG_COUNT)
+                               send_pos = 0;
+                       unprocessed++;
+                       if (verbose == 1)
+                               echo_progress(counter);
+                       counter++;
+
+                       if ((counter % 33) == 0)
+                               millisleep(3);
+                       else
+                               millisleep(1);
+               } else {
+                       if (recv_frame(&rx_frame))
+                               return -1;
+
+                       if (verbose > 1)
+                               print_frame(&rx_frame);
+
+                       /* compare with expected */
+                       compare_frame(&tx_frames[recv_pos], &rx_frame);
+
+                       loops++;
+                       if (test_loops && loops >= test_loops)
+                               break;
+
+                       recv_pos++;
+                       if (recv_pos == CAN_MSG_COUNT)
+                               recv_pos = 0;
+                       unprocessed--;
+               }
+       }
+
+       printf("\nTest messages sent and received: %d\n", loops);
+
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       struct ifreq ifr;
+       struct sockaddr_can addr;
+       char *intf_name;
+       int family = PF_CAN, type = SOCK_RAW, proto = CAN_RAW;
+       int echo_gen = 0;
+       int opt, err;
+
+       signal(SIGTERM, signal_handler);
+       signal(SIGHUP, signal_handler);
+       signal(SIGINT, signal_handler);
+
+       while ((opt = getopt(argc, argv, "gl:v")) != -1) {
+               switch (opt) {
+               case 'v':
+                       verbose++;
+                       break;
+
+               case 'l':
+                       test_loops = atoi(optarg);;
+                       break;
+
+               case 'g':
+                       echo_gen = 1;
+                       break;
+
+               default:
+                       print_usage(basename(argv[0]));
+                       break;
+               }
+       }
+
+       if ((argc - optind) != 1)
+               print_usage(basename(argv[0]));
+       intf_name = argv[optind];
+
+       printf("interface = %s, family = %d, type = %d, proto = %d\n",
+              intf_name, family, type, proto);
+
+       if ((sockfd = socket(family, type, proto)) < 0) {
+               perror("socket");
+               return 1;
+       }
+
+       addr.can_family = family;
+       strcpy(ifr.ifr_name, intf_name);
+       ioctl(sockfd, SIOCGIFINDEX, &ifr);
+       addr.can_ifindex = ifr.ifr_ifindex;
+
+       if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+               perror("bind");
+               close(sockfd);
+               return 1;
+       }
+
+       if (echo_gen)
+               err = can_echo_gen();
+       else
+               err = can_echo_dut();
+
+       if (verbose)
+               printf("Exiting...\n");
+
+       close(sockfd);
+       return err;
+}