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 <wg@grandegger.com>pull/7/head
parent
0b4d143113
commit
af4ef62646
2
Makefile
2
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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Reference in New Issue