From: Wolfgang Grandegger Date: Wed, 25 Nov 2009 11:18:22 +0000 (+0000) Subject: can-utils: canfdtest, a full duplex test to find out-of-order messages X-Git-Url: https://rtime.felk.cvut.cz/gitweb/sojka/can-utils.git/commitdiff_plain/af4ef626466bcc0800c49aa9fe576eebd16fc387 can-utils: canfdtest, a full duplex test to find out-of-order messages 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 --- diff --git a/Makefile b/Makefile index 5f32ef4..5db72c8 100644 --- 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 index 0000000..6752d1f --- /dev/null +++ b/canfdtest.c @@ -0,0 +1,379 @@ +/* + * canfdtest.c - Full-duplex test program (DUT and host part) + * + * (C) 2009 by Vladislav Gribov, IXXAT Automation GmbH, + * (C) 2009 Wolfgang Grandegger + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#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] \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 , otherwise all messages received on the\n" + " 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; +}